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.6 2000/08/02 12:37:33 robs Exp $";
23 #include "fcgi_config.h"
25 #define DLLAPI __declspec(dllexport)
29 #include <sys/timeb.h>
36 #define WIN32_OPEN_MAX 32 /* XXX: Small hack */
37 #define MUTEX_VARNAME "_FCGI_MUTEX_"
40 static HANDLE hIoCompPort = INVALID_HANDLE_VALUE;
41 static HANDLE hStdinCompPort = INVALID_HANDLE_VALUE;
42 static HANDLE hStdinThread = INVALID_HANDLE_VALUE;
44 static HANDLE stdioHandles[3] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE,
45 INVALID_HANDLE_VALUE};
47 static HANDLE hPipeMutex = INVALID_HANDLE_VALUE;;
48 static char pipeMutexEnv[80] = "";
51 * An enumeration of the file types
52 * supported by the FD_TABLE structure.
54 * XXX: Not all currently supported. This allows for future
74 * Structure used to map file handle and socket handle
75 * values into values that can be used to create unix-like
76 * select bitmaps, read/write for both sockets/files.
83 unsigned long instance;
85 int offset; /* only valid for async file writes */
86 LPDWORD offsetHighPtr; /* pointers to offset high and low words */
87 LPDWORD offsetLowPtr; /* only valid for async file writes (logs) */
88 HANDLE hMapMutex; /* mutex handle for multi-proc offset update */
89 LPVOID ovList; /* List of associated OVERLAPPED_REQUESTs */
92 typedef struct FD_TABLE *PFD_TABLE;
94 static struct FD_TABLE fdTable[WIN32_OPEN_MAX];
96 struct OVERLAPPED_REQUEST {
97 OVERLAPPED overlapped;
98 unsigned long instance; /* file instance (won't match after a close) */
99 OS_AsyncProc procPtr; /* callback routine */
100 ClientData clientData; /* callback argument */
101 ClientData clientData1; /* additional clientData */
103 typedef struct OVERLAPPED_REQUEST *POVERLAPPED_REQUEST;
105 static const char *bindPathPrefix = "\\\\.\\pipe\\FastCGI\\";
107 static int listenType = FD_UNUSED;
108 static HANDLE hListen = INVALID_HANDLE_VALUE;
109 static int libInitialized = 0;
113 *--------------------------------------------------------------
115 * Win32NewDescriptor --
117 * Set up for I/O descriptor masquerading.
120 * Returns "fake id" which masquerades as a UNIX-style "small
121 * non-negative integer" file/socket descriptor.
122 * Win32_* routine below will "do the right thing" based on the
123 * descriptor's actual type. -1 indicates failure.
126 * Entry in fdTable is reserved to represent the socket/file.
128 *--------------------------------------------------------------
130 static int Win32NewDescriptor(FILE_TYPE type, int fd, int desiredFd)
135 * If the "desiredFd" is not -1, try to get this entry for our
136 * pseudo file descriptor. If this is not available, return -1
137 * as the caller wanted to get this mapping. This is typically
138 * only used for mapping stdio handles.
140 if ((desiredFd >= 0) &&
141 (desiredFd < WIN32_OPEN_MAX)) {
143 if(fdTable[desiredFd].type == FD_UNUSED) {
153 * Next see if the entry that matches "fd" is available.
156 (fd < WIN32_OPEN_MAX) && (fdTable[fd].type == FD_UNUSED)) {
162 * Scan entries for one we can use. Start at 1 (0 fake id fails
163 * in some cases). -K*
165 for (index = 1; index < WIN32_OPEN_MAX; index++)
166 if (fdTable[index].type == FD_UNUSED)
169 /* If no table entries are available, return error. */
170 if (index == WIN32_OPEN_MAX) {
171 SetLastError(WSAEMFILE);
177 fdTable[index].fid.value = fd;
178 fdTable[index].type = type;
179 fdTable[index].path = NULL;
180 fdTable[index].Errno = NO_ERROR;
181 fdTable[index].status = 0;
182 fdTable[index].offset = -1;
183 fdTable[index].offsetHighPtr = fdTable[index].offsetLowPtr = NULL;
184 fdTable[index].hMapMutex = NULL;
185 fdTable[index].ovList = NULL;
191 *--------------------------------------------------------------
195 * This thread performs I/O on stadard input. It is needed
196 * because you can't guarantee that all applications will
197 * create standard input with sufficient access to perform
198 * asynchronous I/O. Since we don't want to block the app
199 * reading from stdin we make it look like it's using I/O
200 * completion ports to perform async I/O.
203 * Data is read from stdin and posted to the io completion
209 *--------------------------------------------------------------
211 static void StdinThread(LPDWORD startup){
216 POVERLAPPED_REQUEST pOv;
220 * Block until a request to read from stdin comes in or a
221 * request to terminate the thread arrives (fd = -1).
223 if (!GetQueuedCompletionStatus(hStdinCompPort, &bytesRead, &fd,
224 (LPOVERLAPPED *)&pOv, (DWORD)-1) && !pOv) {
229 ASSERT((fd == STDIN_FILENO) || (fd == -1));
234 ASSERT(pOv->clientData1 != NULL);
236 if(ReadFile(stdioHandles[STDIN_FILENO], pOv->clientData1, bytesRead,
238 PostQueuedCompletionStatus(hIoCompPort, bytesRead,
239 STDIN_FILENO, (LPOVERLAPPED)pOv);
251 *--------------------------------------------------------------
255 * Set up the OS library for use.
258 * Returns 0 if success, -1 if not.
261 * Sockets initialized, pseudo file descriptors setup, etc.
263 *--------------------------------------------------------------
265 int OS_LibInit(int stdioFds[3])
273 char *cLenPtr = NULL;
274 char *mutexPtr = NULL;
280 * Initialize windows sockets library.
282 wVersion = MAKEWORD(1,1);
283 err = WSAStartup( wVersion, &wsaData );
285 fprintf(stderr, "Error starting Windows Sockets. Error: %d",
291 * Create the I/O completion port to be used for our I/O queue.
293 if (hIoCompPort == INVALID_HANDLE_VALUE) {
294 hIoCompPort = CreateIoCompletionPort (INVALID_HANDLE_VALUE, NULL,
296 if(hIoCompPort == INVALID_HANDLE_VALUE) {
297 printf("<H2>OS_LibInit Failed CreateIoCompletionPort! ERROR: %d</H2>\r\n\r\n",
304 * Determine if this library is being used to listen for FastCGI
305 * connections. This is communicated by STDIN containing a
306 * valid handle to a listener object. In this case, both the
307 * "stdout" and "stderr" handles will be INVALID (ie. closed) by
308 * the starting process.
310 * The trick is determining if this is a pipe or a socket...
312 * XXX: Add the async accept test to determine socket or handle to a
315 if((GetStdHandle(STD_OUTPUT_HANDLE) == INVALID_HANDLE_VALUE) &&
316 (GetStdHandle(STD_ERROR_HANDLE) == INVALID_HANDLE_VALUE) &&
317 (GetStdHandle(STD_INPUT_HANDLE) != INVALID_HANDLE_VALUE) ) {
319 hListen = GetStdHandle(STD_INPUT_HANDLE);
322 * Set the pipe handle state so that it operates in wait mode.
324 * NOTE: The listenFd is not mapped to a pseudo file descriptor
325 * as all work done on it is contained to the OS library.
327 * XXX: Initial assumption is that SetNamedPipeHandleState will
328 * fail if this is an IP socket...
330 pipeMode = PIPE_READMODE_BYTE | PIPE_WAIT;
331 if(SetNamedPipeHandleState(hListen, &pipeMode, NULL, NULL)) {
332 listenType = FD_PIPE_SYNC;
334 * Lookup the mutex. If one is found, save it and
335 * remove it from the env table if it's not already
338 mutexPtr = getenv(MUTEX_VARNAME);
339 if(mutexPtr != NULL) {
340 hPipeMutex = (HANDLE)atoi(mutexPtr);
341 putenv(MUTEX_VARNAME"=");
344 listenType = FD_SOCKET_SYNC;
349 * If there are no stdioFds passed in, we're done.
351 if(stdioFds == NULL) {
357 * Setup standard input asynchronous I/O. There is actually a separate
358 * thread spawned for this purpose. The reason for this is that some
359 * web servers use anonymous pipes for the connection between itself
360 * and a CGI application. Anonymous pipes can't perform asynchronous
361 * I/O or use I/O completion ports. Therefore in order to present a
362 * consistent I/O dispatch model to an application we emulate I/O
363 * completion port behavior by having the standard input thread posting
364 * messages to the hIoCompPort which look like a complete overlapped
365 * I/O structure. This keeps the event dispatching simple from the
366 * application perspective.
368 stdioHandles[STDIN_FILENO] = GetStdHandle(STD_INPUT_HANDLE);
370 if(!SetHandleInformation(stdioHandles[STDIN_FILENO],
371 HANDLE_FLAG_INHERIT, 0)) {
373 * XXX: Causes error when run from command line. Check KB
374 err = GetLastError();
380 if ((fakeFd = Win32NewDescriptor(FD_PIPE_SYNC,
381 (int)stdioHandles[STDIN_FILENO],
382 STDIN_FILENO)) == -1) {
386 * Set stdin equal to our pseudo FD and create the I/O completion
387 * port to be used for async I/O.
389 stdioFds[STDIN_FILENO] = fakeFd;
393 * Create the I/O completion port to be used for communicating with
394 * the thread doing I/O on standard in. This port will carry read
395 * and possibly thread termination requests to the StdinThread.
397 if (hStdinCompPort == INVALID_HANDLE_VALUE) {
398 hStdinCompPort = CreateIoCompletionPort (INVALID_HANDLE_VALUE, NULL,
400 if(hStdinCompPort == INVALID_HANDLE_VALUE) {
401 printf("<H2>OS_LibInit Failed CreateIoCompletionPort: STDIN! ERROR: %d</H2>\r\n\r\n",
408 * Create the thread that will read stdin if the CONTENT_LENGTH
411 if((cLenPtr = getenv("CONTENT_LENGTH")) != NULL &&
413 hStdinThread = CreateThread(NULL, 8192,
414 (LPTHREAD_START_ROUTINE)&StdinThread,
416 if (hStdinThread == NULL) {
417 printf("<H2>OS_LibInit Failed to create STDIN thread! ERROR: %d</H2>\r\n\r\n",
424 * STDOUT will be used synchronously.
426 * XXX: May want to convert this so that it could be used for OVERLAPPED
427 * I/O later. If so, model it after the Stdin I/O as stdout is
428 * also incapable of async I/O on some servers.
430 stdioHandles[STDOUT_FILENO] = GetStdHandle(STD_OUTPUT_HANDLE);
431 if(!SetHandleInformation(stdioHandles[STDOUT_FILENO],
432 HANDLE_FLAG_INHERIT, FALSE)) {
437 if ((fakeFd = Win32NewDescriptor(FD_PIPE_SYNC,
438 (int)stdioHandles[STDOUT_FILENO],
439 STDOUT_FILENO)) == -1) {
443 * Set stdout equal to our pseudo FD
445 stdioFds[STDOUT_FILENO] = fakeFd;
448 stdioHandles[STDERR_FILENO] = GetStdHandle(STD_ERROR_HANDLE);
449 if(!SetHandleInformation(stdioHandles[STDERR_FILENO],
450 HANDLE_FLAG_INHERIT, FALSE)) {
454 if ((fakeFd = Win32NewDescriptor(FD_PIPE_SYNC,
455 (int)stdioHandles[STDERR_FILENO],
456 STDERR_FILENO)) == -1) {
460 * Set stderr equal to our pseudo FD
462 stdioFds[STDERR_FILENO] = fakeFd;
470 *--------------------------------------------------------------
474 * Shutdown the OS library.
480 * Memory freed, handles closed.
482 *--------------------------------------------------------------
484 void OS_LibShutdown()
487 if(hIoCompPort != INVALID_HANDLE_VALUE) {
488 CloseHandle(hIoCompPort);
489 hIoCompPort = INVALID_HANDLE_VALUE;
492 if(hStdinCompPort != INVALID_HANDLE_VALUE) {
493 CloseHandle(hStdinCompPort);
494 hStdinCompPort = INVALID_HANDLE_VALUE;
498 * Shutdown the socket library.
506 *--------------------------------------------------------------
508 * Win32FreeDescriptor --
510 * Free I/O descriptor entry in fdTable.
513 * Frees I/O descriptor entry in fdTable.
518 *--------------------------------------------------------------
520 static void Win32FreeDescriptor(int fd)
522 /* Catch it if fd is a bogus value */
523 ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX));
524 ASSERT(fdTable[fd].type != FD_UNUSED);
526 switch (fdTable[fd].type) {
529 /* Free file path string */
530 ASSERT(fdTable[fd].path != NULL);
531 free(fdTable[fd].path);
532 fdTable[fd].path = NULL;
536 * Break through to generic fdTable free-descriptor code
541 ASSERT(fdTable[fd].path == NULL);
542 fdTable[fd].type = FD_UNUSED;
543 fdTable[fd].path = NULL;
544 fdTable[fd].Errno = NO_ERROR;
545 fdTable[fd].offsetHighPtr = fdTable[fd].offsetLowPtr = NULL;
546 if (fdTable[fd].hMapMutex != NULL) {
547 CloseHandle(fdTable[fd].hMapMutex);
548 fdTable[fd].hMapMutex = NULL;
555 * OS_CreateLocalIpcFd --
557 * This procedure is responsible for creating the listener pipe
558 * on Windows NT for local process communication. It will create a
559 * named pipe and return a file descriptor to it to the caller.
562 * Listener pipe created. This call returns either a valid
563 * pseudo file descriptor or -1 on error.
566 * Listener pipe and IPC address are stored in the FCGI info
568 * 'errno' will set on errors (-1 is returned).
570 *----------------------------------------------------------------------
572 int OS_CreateLocalIpcFd(const char *bindPath, int backlog)
575 SECURITY_ATTRIBUTES sa;
576 HANDLE hListenPipe = INVALID_HANDLE_VALUE;
581 struct sockaddr_in sockAddr;
588 strcpy(host, bindPath);
589 if((tp = strchr(host, ':')) != 0) {
591 if((port = atoi(tp)) == 0) {
597 if(tcp && (*host && strcmp(host, "localhost") != 0)) {
598 fprintf(stderr, "To start a service on a TCP port can not "
599 "specify a host name.\n"
600 "You should either use \"localhost:<port>\" or "
601 " just use \":<port>.\"\n");
606 listenSock = socket(AF_INET, SOCK_STREAM, 0);
607 if(listenSock == SOCKET_ERROR) {
611 * Bind the listening socket.
613 memset((char *) &sockAddr, 0, sizeof(sockAddr));
614 sockAddr.sin_family = AF_INET;
615 sockAddr.sin_addr.s_addr = htonl(INADDR_ANY);
616 sockAddr.sin_port = htons(port);
617 servLen = sizeof(sockAddr);
619 if(bind(listenSock, (struct sockaddr *) &sockAddr, servLen) < 0
620 || listen(listenSock, backlog) < 0) {
621 perror("bind/listen");
625 retFd = Win32NewDescriptor(FD_SOCKET_SYNC, (int)listenSock, -1);
631 * Initialize the SECURITY_ATTRIUBTES structure.
633 sa.nLength = sizeof(sa);
634 sa.lpSecurityDescriptor = NULL;
635 sa.bInheritHandle = TRUE; /* This will be inherited by the
640 * Create a mutex to be used to synchronize access to accepting a
641 * connection on a named pipe. We don't want to own this at creation
642 * time but would rather let the first process that goes for it
643 * be able to acquire it.
645 hPipeMutex = CreateMutex(NULL, FALSE, NULL);
646 if(hPipeMutex == NULL) {
649 if(!SetHandleInformation(hPipeMutex, HANDLE_FLAG_INHERIT,
653 sprintf(pipeMutexEnv, "%s=%d", MUTEX_VARNAME, (int)hPipeMutex);
654 putenv(pipeMutexEnv);
657 * Create a unique name to be used for the socket bind path.
658 * Make sure that this name is unique and that there's no process
661 * Named Pipe Pathname: \\.\pipe\FastCGI\OM_WS.pid.N
662 * Where: N is the pipe instance on the machine.
665 bpLen = (int)strlen(bindPathPrefix);
666 bpLen += strlen(bindPath);
667 localPath = malloc(bpLen+2);
668 strcpy(localPath, bindPathPrefix);
669 strcat(localPath, bindPath);
672 * Create and setup the named pipe to be used by the fcgi server.
674 hListenPipe = CreateNamedPipe(localPath, /* name of pipe */
675 PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
676 PIPE_TYPE_BYTE | PIPE_WAIT |
677 PIPE_READMODE_BYTE, /* pipe IO type */
678 PIPE_UNLIMITED_INSTANCES, /* number of instances */
679 4096, /* size of outbuf (0 == allocate as necessary) */
680 4096, /* size of inbuf */
681 0, /*1000,*/ /* default time-out value */
682 &sa); /* security attributes */
685 * Can't create an instance of the pipe, fail...
687 if (hListenPipe == INVALID_HANDLE_VALUE) {
691 retFd = Win32NewDescriptor(FD_PIPE_SYNC, (int)hListenPipe, -1);
697 *----------------------------------------------------------------------
701 * Create the pipe pathname connect to the remote application if
705 * -1 if fail or a valid handle if connection succeeds.
708 * Remote connection established.
710 *----------------------------------------------------------------------
712 int OS_FcgiConnect(char *bindPath)
714 char *pipePath = NULL;
718 struct sockaddr_in sockAddr;
719 int servLen, resultSock;
726 strcpy(host, bindPath);
727 if((tp = strchr(host, ':')) != 0) {
729 if((port = atoi(tp)) == 0) {
737 if((hp = gethostbyname((*host ? host : "localhost"))) == NULL) {
738 fprintf(stderr, "Unknown host: %s\n", bindPath);
741 sockAddr.sin_family = AF_INET;
742 memcpy(&sockAddr.sin_addr, hp->h_addr, hp->h_length);
743 sockAddr.sin_port = htons(port);
744 servLen = sizeof(sockAddr);
745 resultSock = socket(AF_INET, SOCK_STREAM, 0);
747 ASSERT(resultSock >= 0);
748 connectStatus = connect(resultSock, (struct sockaddr *)
750 if(connectStatus < 0) {
752 * Most likely (errno == ENOENT || errno == ECONNREFUSED)
753 * and no FCGI application server is running.
755 closesocket(resultSock);
758 pseudoFd = Win32NewDescriptor(FD_SOCKET_SYNC, resultSock, -1);
760 closesocket(resultSock);
766 * Not a TCP connection, create and connect to a named pipe.
768 pipePath = malloc((size_t)(strlen(bindPathPrefix) + strlen(bindPath) + 2));
769 if(pipePath == NULL) {
772 strcpy(pipePath, bindPathPrefix);
773 strcat(pipePath, bindPath);
775 hPipe = CreateFile (pipePath,
776 /* Generic access, read/write. */
777 GENERIC_WRITE | GENERIC_READ,
778 /* Share both read and write. */
779 FILE_SHARE_READ | FILE_SHARE_WRITE ,
780 NULL, /* No security.*/
781 OPEN_EXISTING, /* Fail if not existing. */
782 FILE_FLAG_OVERLAPPED, /* Use overlap. */
783 NULL); /* No template. */
786 if(hPipe == INVALID_HANDLE_VALUE) {
790 if ((pseudoFd = Win32NewDescriptor(FD_PIPE_ASYNC, (int)hPipe, -1)) == -1) {
795 * Set stdin equal to our pseudo FD and create the I/O completion
796 * port to be used for async I/O.
798 if (!CreateIoCompletionPort(hPipe, hIoCompPort, pseudoFd, 1)) {
799 err = GetLastError();
800 Win32FreeDescriptor(pseudoFd);
810 *--------------------------------------------------------------
814 * Pass through to the appropriate NT read function.
817 * Returns number of byes read. Mimics unix read:.
818 * n bytes read, 0 or -1 failure: errno contains actual error
823 *--------------------------------------------------------------
825 int OS_Read(int fd, char * buf, size_t len)
831 * Catch any bogus fd values
833 ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX));
835 switch (fdTable[fd].type) {
842 * ReadFile returns: TRUE success, FALSE failure
844 if (!ReadFile(fdTable[fd].fid.fileHandle, buf, len, &bytesRead,
846 fdTable[fd].Errno = GetLastError();
852 case FD_SOCKET_ASYNC:
853 /* winsock recv returns n bytes recv'ed, SOCKET_ERROR failure */
855 * XXX: Test this with ReadFile. If it works, remove this code
856 * to simplify the routine.
858 if ((ret = recv(fdTable[fd].fid.sock, buf, len, 0)) ==
860 fdTable[fd].Errno = WSAGetLastError();
870 *--------------------------------------------------------------
874 * Perform a synchronous OS write.
877 * Returns number of bytes written. Mimics unix write:
878 * n bytes written, 0 or -1 failure (??? couldn't find man page).
883 *--------------------------------------------------------------
885 int OS_Write(int fd, char * buf, size_t len)
891 * Catch any bogus fd values
893 ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX));
894 ASSERT((fdTable[fd].type > FD_UNUSED) &&
895 (fdTable[fd].type <= FD_PIPE_ASYNC));
897 switch (fdTable[fd].type) {
904 * WriteFile returns: TRUE success, FALSE failure
906 if (!WriteFile(fdTable[fd].fid.fileHandle, buf, len,
907 &bytesWritten, NULL)) {
908 fdTable[fd].Errno = GetLastError();
913 case FD_SOCKET_ASYNC:
914 /* winsock send returns n bytes written, SOCKET_ERROR failure */
916 * XXX: Test this with WriteFile. If it works, remove this code
917 * to simplify the routine.
919 if ((ret = send(fdTable[fd].fid.sock, buf, len, 0)) ==
921 fdTable[fd].Errno = WSAGetLastError();
932 *----------------------------------------------------------------------
936 * Spawns a new server listener process, and stores the information
937 * relating to the child in the supplied record. A wait handler is
938 * registered on the child's completion. This involves creating
939 * a process on NT and preparing a command line with the required
940 * state (currently a -childproc flag and the server socket to use
941 * for accepting connections).
944 * 0 if success, -1 if error.
947 * Child process spawned.
949 *----------------------------------------------------------------------
951 int OS_SpawnChild(char *execPath, int listenFd)
953 STARTUPINFO StartupInfo;
954 PROCESS_INFORMATION pInfo;
957 memset((void *)&StartupInfo, 0, sizeof(STARTUPINFO));
958 StartupInfo.cb = sizeof (STARTUPINFO);
959 StartupInfo.lpReserved = NULL;
960 StartupInfo.lpReserved2 = NULL;
961 StartupInfo.cbReserved2 = 0;
962 StartupInfo.lpDesktop = NULL;
965 * FastCGI on NT will set the listener pipe HANDLE in the stdin of
966 * the new process. The fact that there is a stdin and NULL handles
967 * for stdout and stderr tells the FastCGI process that this is a
968 * FastCGI process and not a CGI process.
970 StartupInfo.dwFlags = STARTF_USESTDHANDLES;
972 * XXX: Do I have to dup the handle before spawning the process or is
973 * it sufficient to use the handle as it's reference counted
976 StartupInfo.hStdInput = fdTable[listenFd].fid.fileHandle;
977 StartupInfo.hStdOutput = INVALID_HANDLE_VALUE;
978 StartupInfo.hStdError = INVALID_HANDLE_VALUE;
981 * Make the listener socket inheritable.
983 success = SetHandleInformation(StartupInfo.hStdInput, HANDLE_FLAG_INHERIT,
990 * XXX: Might want to apply some specific security attributes to the
993 success = CreateProcess(execPath, /* LPCSTR address of module name */
994 NULL, /* LPCSTR address of command line */
995 NULL, /* Process security attributes */
996 NULL, /* Thread security attributes */
997 TRUE, /* Inheritable Handes inherited. */
998 0, /* DWORD creation flags */
999 NULL, /* Use parent environment block */
1000 NULL, /* Address of current directory name */
1001 &StartupInfo, /* Address of STARTUPINFO */
1002 &pInfo); /* Address of PROCESS_INFORMATION */
1012 *--------------------------------------------------------------
1014 * OS_AsyncReadStdin --
1016 * This initiates an asynchronous read on the standard
1017 * input handle. This handle is not guaranteed to be
1018 * capable of performing asynchronous I/O so we send a
1019 * message to the StdinThread to do the synchronous read.
1022 * -1 if error, 0 otherwise.
1025 * Asynchronous message is queued to the StdinThread and an
1026 * overlapped structure is allocated/initialized.
1028 *--------------------------------------------------------------
1030 int OS_AsyncReadStdin(void *buf, int len, OS_AsyncProc procPtr,
1031 ClientData clientData)
1033 POVERLAPPED_REQUEST pOv;
1035 ASSERT(fdTable[STDIN_FILENO].type != FD_UNUSED);
1037 pOv = (POVERLAPPED_REQUEST)malloc(sizeof(struct OVERLAPPED_REQUEST));
1039 memset((void *)pOv, 0, sizeof(struct OVERLAPPED_REQUEST));
1040 pOv->clientData1 = (ClientData)buf;
1041 pOv->instance = fdTable[STDIN_FILENO].instance;
1042 pOv->procPtr = procPtr;
1043 pOv->clientData = clientData;
1045 PostQueuedCompletionStatus(hStdinCompPort, len, STDIN_FILENO,
1052 *--------------------------------------------------------------
1056 * This initiates an asynchronous read on the file
1057 * handle which may be a socket or named pipe.
1059 * We also must save the ProcPtr and ClientData, so later
1060 * when the io completes, we know who to call.
1062 * We don't look at any results here (the ReadFile may
1063 * return data if it is cached) but do all completion
1064 * processing in OS_Select when we get the io completion
1065 * port done notifications. Then we call the callback.
1068 * -1 if error, 0 otherwise.
1071 * Asynchronous I/O operation is queued for completion.
1073 *--------------------------------------------------------------
1075 int OS_AsyncRead(int fd, int offset, void *buf, int len,
1076 OS_AsyncProc procPtr, ClientData clientData)
1079 POVERLAPPED_REQUEST pOv;
1082 * Catch any bogus fd values
1084 ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX));
1086 * Confirm that this is an async fd
1088 ASSERT(fdTable[fd].type != FD_UNUSED);
1089 ASSERT(fdTable[fd].type != FD_FILE_SYNC);
1090 ASSERT(fdTable[fd].type != FD_PIPE_SYNC);
1091 ASSERT(fdTable[fd].type != FD_SOCKET_SYNC);
1093 pOv = (POVERLAPPED_REQUEST)malloc(sizeof(struct OVERLAPPED_REQUEST));
1095 memset((void *)pOv, 0, sizeof(struct OVERLAPPED_REQUEST));
1097 * Only file offsets should be non-zero, but make sure.
1099 if (fdTable[fd].type == FD_FILE_ASYNC)
1100 if (fdTable[fd].offset >= 0)
1101 pOv->overlapped.Offset = fdTable[fd].offset;
1103 pOv->overlapped.Offset = offset;
1104 pOv->instance = fdTable[fd].instance;
1105 pOv->procPtr = procPtr;
1106 pOv->clientData = clientData;
1109 * ReadFile returns: TRUE success, FALSE failure
1111 if (!ReadFile(fdTable[fd].fid.fileHandle, buf, len, &bytesRead,
1112 (LPOVERLAPPED)pOv)) {
1113 fdTable[fd].Errno = GetLastError();
1114 if(fdTable[fd].Errno == ERROR_NO_DATA ||
1115 fdTable[fd].Errno == ERROR_PIPE_NOT_CONNECTED) {
1116 PostQueuedCompletionStatus(hIoCompPort, 0, fd, (LPOVERLAPPED)pOv);
1119 if(fdTable[fd].Errno != ERROR_IO_PENDING) {
1120 PostQueuedCompletionStatus(hIoCompPort, 0, fd, (LPOVERLAPPED)pOv);
1123 fdTable[fd].Errno = 0;
1129 *--------------------------------------------------------------
1133 * This initiates an asynchronous write on the "fake" file
1134 * descriptor (which may be a file, socket, or named pipe).
1135 * We also must save the ProcPtr and ClientData, so later
1136 * when the io completes, we know who to call.
1138 * We don't look at any results here (the WriteFile generally
1139 * completes immediately) but do all completion processing
1140 * in OS_DoIo when we get the io completion port done
1141 * notifications. Then we call the callback.
1144 * -1 if error, 0 otherwise.
1147 * Asynchronous I/O operation is queued for completion.
1149 *--------------------------------------------------------------
1151 int OS_AsyncWrite(int fd, int offset, void *buf, int len,
1152 OS_AsyncProc procPtr, ClientData clientData)
1155 POVERLAPPED_REQUEST pOv;
1158 * Catch any bogus fd values
1160 ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX));
1162 * Confirm that this is an async fd
1164 ASSERT(fdTable[fd].type != FD_UNUSED);
1165 ASSERT(fdTable[fd].type != FD_FILE_SYNC);
1166 ASSERT(fdTable[fd].type != FD_PIPE_SYNC);
1167 ASSERT(fdTable[fd].type != FD_SOCKET_SYNC);
1169 pOv = (POVERLAPPED_REQUEST)malloc(sizeof(struct OVERLAPPED_REQUEST));
1171 memset((void *)pOv, 0, sizeof(struct OVERLAPPED_REQUEST));
1173 * Only file offsets should be non-zero, but make sure.
1175 if (fdTable[fd].type == FD_FILE_ASYNC)
1177 * Only file opened via OS_AsyncWrite with
1178 * O_APPEND will have an offset != -1.
1180 if (fdTable[fd].offset >= 0)
1182 * If the descriptor has a memory mapped file
1183 * handle, take the offsets from there.
1185 if (fdTable[fd].hMapMutex != NULL) {
1187 * Wait infinitely; this *should* not cause problems.
1189 WaitForSingleObject(fdTable[fd].hMapMutex, INFINITE);
1192 * Retrieve the shared offset values.
1194 pOv->overlapped.OffsetHigh = *(fdTable[fd].offsetHighPtr);
1195 pOv->overlapped.Offset = *(fdTable[fd].offsetLowPtr);
1198 * Update the shared offset values for the next write
1200 *(fdTable[fd].offsetHighPtr) += 0; /* XXX How do I handle overflow */
1201 *(fdTable[fd].offsetLowPtr) += len;
1203 ReleaseMutex(fdTable[fd].hMapMutex);
1205 pOv->overlapped.Offset = fdTable[fd].offset;
1207 pOv->overlapped.Offset = offset;
1208 pOv->instance = fdTable[fd].instance;
1209 pOv->procPtr = procPtr;
1210 pOv->clientData = clientData;
1213 * WriteFile returns: TRUE success, FALSE failure
1215 if (!WriteFile(fdTable[fd].fid.fileHandle, buf, len, &bytesWritten,
1216 (LPOVERLAPPED)pOv)) {
1217 fdTable[fd].Errno = GetLastError();
1218 if(fdTable[fd].Errno != ERROR_IO_PENDING) {
1219 PostQueuedCompletionStatus(hIoCompPort, 0, fd, (LPOVERLAPPED)pOv);
1222 fdTable[fd].Errno = 0;
1224 if (fdTable[fd].offset >= 0)
1225 fdTable[fd].offset += len;
1231 *--------------------------------------------------------------
1235 * Closes the descriptor with routine appropriate for
1236 * descriptor's type.
1239 * Socket or file is closed. Return values mimic Unix close:
1240 * 0 success, -1 failure
1243 * Entry in fdTable is marked as free.
1245 *--------------------------------------------------------------
1247 int OS_Close(int fd)
1252 * Catch it if fd is a bogus value
1254 ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX));
1255 ASSERT(fdTable[fd].type != FD_UNUSED);
1257 switch (fdTable[fd].type) {
1263 * CloseHandle returns: TRUE success, 0 failure
1265 if (CloseHandle(fdTable[fd].fid.fileHandle) == FALSE)
1268 case FD_SOCKET_SYNC:
1269 case FD_SOCKET_ASYNC:
1271 * Closing a socket that has an async read outstanding causes a
1272 * tcp reset and possible data loss. The shutdown call seems to
1275 shutdown(fdTable[fd].fid.sock, 2);
1277 * closesocket returns: 0 success, SOCKET_ERROR failure
1279 if (closesocket(fdTable[fd].fid.sock) == SOCKET_ERROR)
1283 return -1; /* fake failure */
1286 Win32FreeDescriptor(fd);
1291 *--------------------------------------------------------------
1295 * Cancel outstanding asynchronous reads and prevent subsequent
1296 * reads from completing.
1299 * Socket or file is shutdown. Return values mimic Unix shutdown:
1300 * 0 success, -1 failure
1302 *--------------------------------------------------------------
1304 int OS_CloseRead(int fd)
1309 * Catch it if fd is a bogus value
1311 ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX));
1312 ASSERT(fdTable[fd].type == FD_SOCKET_ASYNC
1313 || fdTable[fd].type == FD_SOCKET_SYNC);
1315 if (shutdown(fdTable[fd].fid.sock,0) == SOCKET_ERROR)
1321 *--------------------------------------------------------------
1325 * This function was formerly OS_Select. It's purpose is
1326 * to pull I/O completion events off the queue and dispatch
1327 * them to the appropriate place.
1333 * Handlers are called.
1335 *--------------------------------------------------------------
1337 int OS_DoIo(struct timeval *tmo)
1341 POVERLAPPED_REQUEST pOv;
1348 * We can loop in here, but not too long, as wait handlers
1350 * For cgi stdin, apparently select returns when io completion
1351 * ports don't, so don't wait the full timeout.
1354 ms = (tmo->tv_sec*1000 + tmo->tv_usec/1000) / 2;
1358 ms_last = tb.time*1000 + tb.millitm;
1360 if(tmo && (ms = tmo->tv_sec*1000 + tmo->tv_usec/1000)> 100)
1362 if (!GetQueuedCompletionStatus(hIoCompPort, &bytes, &fd,
1363 (LPOVERLAPPED *)&pOv, ms) && !pOv) {
1364 err = WSAGetLastError();
1365 return 0; /* timeout */
1368 ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX));
1369 /* call callback if descriptor still valid */
1371 if(pOv->instance == fdTable[fd].instance)
1372 (*pOv->procPtr)(pOv->clientData, bytes);
1376 ms -= (tb.time*1000 + tb.millitm - ms_last);
1377 ms_last = tb.time*1000 + tb.millitm;
1384 *----------------------------------------------------------------------
1388 * Accepts a new FastCGI connection. This routine knows whether
1389 * we're dealing with TCP based sockets or NT Named Pipes for IPC.
1392 * -1 if the operation fails, otherwise this is a valid IPC fd.
1395 * New IPC connection is accepted.
1397 *----------------------------------------------------------------------
1399 int OS_Accept(int listen_sock, int fail_on_intr, const char *webServerAddrs)
1401 /* XXX This is broken for listen_sock & fail_on_intr */
1402 struct sockaddr_in sa;
1403 int isNewConnection;
1408 int clilen = sizeof(sa);
1409 DWORD waitForStatus;
1411 switch(listenType) {
1414 waitForStatus = WaitForSingleObject(hPipeMutex,INFINITE);
1415 switch(waitForStatus) {
1417 case WAIT_ABANDONED:
1426 * We have the mutex, go for the connection.
1428 pConnected = ConnectNamedPipe(hListen, NULL) ?
1429 TRUE : (GetLastError() == ERROR_PIPE_CONNECTED);
1431 ReleaseMutex(hPipeMutex);
1436 if (!DuplicateHandle(GetCurrentProcess(), hListen,
1437 GetCurrentProcess(), &hDup, 0,
1438 TRUE, /* allow inheritance */
1439 DUPLICATE_SAME_ACCESS)) {
1442 ipcFd = Win32NewDescriptor(FD_PIPE_SYNC, (int)hDup, -1);
1444 DisconnectNamedPipe(hListen);
1453 case FD_SOCKET_SYNC:
1454 hSock = accept((int)hListen, (struct sockaddr *) &sa, &clilen);
1457 } else if (sa.sin_family != AF_INET) { /* What are we? */
1464 if (webServerAddrs == NULL)
1465 isNewConnection = TRUE;
1467 tp1 = (char *) malloc(strlen(webServerAddrs)+1);
1468 ASSERT(tp1 != NULL);
1469 strcpy(tp1, webServerAddrs);
1471 if ((tp2 = strchr(tp1, ',')) != NULL)
1474 if (inet_addr(tp1) == sa.sin_addr.s_addr) {
1482 isNewConnection = TRUE;
1491 ipcFd = Win32NewDescriptor(FD_SOCKET_SYNC, hSock, -1);
1507 *----------------------------------------------------------------------
1511 * OS IPC routine to close an IPC connection.
1517 * IPC connection is closed.
1519 *----------------------------------------------------------------------
1521 int OS_IpcClose(int ipcFd)
1524 * Catch it if fd is a bogus value
1526 ASSERT((ipcFd >= 0) && (ipcFd < WIN32_OPEN_MAX));
1527 ASSERT(fdTable[ipcFd].type != FD_UNUSED);
1529 switch(listenType) {
1533 * Make sure that the client (ie. a Web Server in this case) has
1534 * read all data from the pipe before we disconnect.
1536 if(!FlushFileBuffers(fdTable[ipcFd].fid.fileHandle))
1538 if(DisconnectNamedPipe(fdTable[ipcFd].fid.fileHandle)) {
1546 case FD_SOCKET_SYNC:
1560 *----------------------------------------------------------------------
1564 * Determines whether this process is a FastCGI process or not.
1567 * Returns 1 if FastCGI, 0 if not.
1572 *----------------------------------------------------------------------
1574 int OS_IsFcgi(int sock)
1576 /* XXX This is broken for sock */
1577 if(listenType == FD_UNUSED) {
1586 *----------------------------------------------------------------------
1590 * Sets selected flag bits in an open file descriptor. Currently
1591 * this is only to put a SOCKET into non-blocking mode.
1593 *----------------------------------------------------------------------
1595 void OS_SetFlags(int fd, int flags)
1597 unsigned long pLong = 1L;
1600 if (fdTable[fd].type == FD_SOCKET_SYNC && flags == O_NONBLOCK) {
1601 if (ioctlsocket(fdTable[fd].fid.sock, FIONBIO, &pLong) ==
1603 exit(WSAGetLastError());
1605 if (!CreateIoCompletionPort((HANDLE)fdTable[fd].fid.sock,
1606 hIoCompPort, fd, 1)) {
1607 err = GetLastError();
1611 fdTable[fd].type = FD_SOCKET_ASYNC;