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.2 1999/07/28 00:18:31 roberts 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 isFastCGI = FALSE;
108 static int isCGI = FALSE;
109 static int listenType = FD_UNUSED;
110 static HANDLE hListen = INVALID_HANDLE_VALUE;
111 static int libInitialized = 0;
115 *--------------------------------------------------------------
117 * Win32NewDescriptor --
119 * Set up for I/O descriptor masquerading.
122 * Returns "fake id" which masquerades as a UNIX-style "small
123 * non-negative integer" file/socket descriptor.
124 * Win32_* routine below will "do the right thing" based on the
125 * descriptor's actual type. -1 indicates failure.
128 * Entry in fdTable is reserved to represent the socket/file.
130 *--------------------------------------------------------------
132 static int Win32NewDescriptor(FILE_TYPE type, int fd, int desiredFd)
137 * If the "desiredFd" is not -1, try to get this entry for our
138 * pseudo file descriptor. If this is not available, return -1
139 * as the caller wanted to get this mapping. This is typically
140 * only used for mapping stdio handles.
142 if ((desiredFd >= 0) &&
143 (desiredFd < WIN32_OPEN_MAX)) {
145 if(fdTable[desiredFd].type == FD_UNUSED) {
155 * Next see if the entry that matches "fd" is available.
158 (fd < WIN32_OPEN_MAX) && (fdTable[fd].type == FD_UNUSED)) {
164 * Scan entries for one we can use. Start at 1 (0 fake id fails
165 * in some cases). -K*
167 for (index = 1; index < WIN32_OPEN_MAX; index++)
168 if (fdTable[index].type == FD_UNUSED)
171 /* If no table entries are available, return error. */
172 if (index == WIN32_OPEN_MAX) {
173 SetLastError(WSAEMFILE);
179 fdTable[index].fid.value = fd;
180 fdTable[index].type = type;
181 fdTable[index].path = NULL;
182 fdTable[index].Errno = NO_ERROR;
183 fdTable[index].status = 0;
184 fdTable[index].offset = -1;
185 fdTable[index].offsetHighPtr = fdTable[index].offsetLowPtr = NULL;
186 fdTable[index].hMapMutex = NULL;
187 fdTable[index].ovList = NULL;
193 *--------------------------------------------------------------
197 * This thread performs I/O on stadard input. It is needed
198 * because you can't guarantee that all applications will
199 * create standard input with sufficient access to perform
200 * asynchronous I/O. Since we don't want to block the app
201 * reading from stdin we make it look like it's using I/O
202 * completion ports to perform async I/O.
205 * Data is read from stdin and posted to the io completion
211 *--------------------------------------------------------------
213 static void StdinThread(LPDWORD startup){
218 POVERLAPPED_REQUEST pOv;
222 * Block until a request to read from stdin comes in or a
223 * request to terminate the thread arrives (fd = -1).
225 if (!GetQueuedCompletionStatus(hStdinCompPort, &bytesRead, &fd,
226 (LPOVERLAPPED *)&pOv, (DWORD)-1) && !pOv) {
231 ASSERT((fd == STDIN_FILENO) || (fd == -1));
236 ASSERT(pOv->clientData1 != NULL);
238 if(ReadFile(stdioHandles[STDIN_FILENO], pOv->clientData1, bytesRead,
240 PostQueuedCompletionStatus(hIoCompPort, bytesRead,
241 STDIN_FILENO, (LPOVERLAPPED)pOv);
253 *--------------------------------------------------------------
257 * Set up the OS library for use.
260 * Returns 0 if success, -1 if not.
263 * Sockets initialized, pseudo file descriptors setup, etc.
265 *--------------------------------------------------------------
267 int OS_LibInit(int stdioFds[3])
275 char *cLenPtr = NULL;
276 char *mutexPtr = NULL;
282 * Initialize windows sockets library.
284 wVersion = MAKEWORD(1,1);
285 err = WSAStartup( wVersion, &wsaData );
287 fprintf(stderr, "Error starting Windows Sockets. Error: %d",
293 * Create the I/O completion port to be used for our I/O queue.
295 if (hIoCompPort == INVALID_HANDLE_VALUE) {
296 hIoCompPort = CreateIoCompletionPort (INVALID_HANDLE_VALUE, NULL,
298 if(hIoCompPort == INVALID_HANDLE_VALUE) {
299 printf("<H2>OS_LibInit Failed CreateIoCompletionPort! ERROR: %d</H2>\r\n\r\n",
306 * Determine if this library is being used to listen for FastCGI
307 * connections. This is communicated by STDIN containing a
308 * valid handle to a listener object. In this case, both the
309 * "stdout" and "stderr" handles will be INVALID (ie. closed) by
310 * the starting process.
312 * The trick is determining if this is a pipe or a socket...
314 * XXX: Add the async accept test to determine socket or handle to a
317 if((GetStdHandle(STD_OUTPUT_HANDLE) == INVALID_HANDLE_VALUE) &&
318 (GetStdHandle(STD_ERROR_HANDLE) == INVALID_HANDLE_VALUE) &&
319 (GetStdHandle(STD_INPUT_HANDLE) != INVALID_HANDLE_VALUE) ) {
321 hListen = GetStdHandle(STD_INPUT_HANDLE);
325 * Set the pipe handle state so that it operates in wait mode.
327 * NOTE: The listenFd is not mapped to a pseudo file descriptor
328 * as all work done on it is contained to the OS library.
330 * XXX: Initial assumption is that SetNamedPipeHandleState will
331 * fail if this is an IP socket...
333 pipeMode = PIPE_READMODE_BYTE | PIPE_WAIT;
334 if(SetNamedPipeHandleState(hListen, &pipeMode, NULL, NULL)) {
335 listenType = FD_PIPE_SYNC;
337 * Lookup the mutex. If one is found, save it and
338 * remove it from the env table if it's not already
341 mutexPtr = getenv(MUTEX_VARNAME);
342 if(mutexPtr != NULL) {
343 hPipeMutex = (HANDLE)atoi(mutexPtr);
344 putenv(MUTEX_VARNAME"=");
347 listenType = FD_SOCKET_SYNC;
352 * If there are no stdioFds passed in, we're done.
354 if(stdioFds == NULL) {
360 * Setup standard input asynchronous I/O. There is actually a separate
361 * thread spawned for this purpose. The reason for this is that some
362 * web servers use anonymous pipes for the connection between itself
363 * and a CGI application. Anonymous pipes can't perform asynchronous
364 * I/O or use I/O completion ports. Therefore in order to present a
365 * consistent I/O dispatch model to an application we emulate I/O
366 * completion port behavior by having the standard input thread posting
367 * messages to the hIoCompPort which look like a complete overlapped
368 * I/O structure. This keeps the event dispatching simple from the
369 * application perspective.
371 stdioHandles[STDIN_FILENO] = GetStdHandle(STD_INPUT_HANDLE);
373 if(!SetHandleInformation(stdioHandles[STDIN_FILENO],
374 HANDLE_FLAG_INHERIT, 0)) {
376 * XXX: Causes error when run from command line. Check KB
377 err = GetLastError();
383 if ((fakeFd = Win32NewDescriptor(FD_PIPE_SYNC,
384 (int)stdioHandles[STDIN_FILENO],
385 STDIN_FILENO)) == -1) {
389 * Set stdin equal to our pseudo FD and create the I/O completion
390 * port to be used for async I/O.
392 stdioFds[STDIN_FILENO] = fakeFd;
396 * Create the I/O completion port to be used for communicating with
397 * the thread doing I/O on standard in. This port will carry read
398 * and possibly thread termination requests to the StdinThread.
400 if (hStdinCompPort == INVALID_HANDLE_VALUE) {
401 hStdinCompPort = CreateIoCompletionPort (INVALID_HANDLE_VALUE, NULL,
403 if(hStdinCompPort == INVALID_HANDLE_VALUE) {
404 printf("<H2>OS_LibInit Failed CreateIoCompletionPort: STDIN! ERROR: %d</H2>\r\n\r\n",
411 * Create the thread that will read stdin if the CONTENT_LENGTH
414 if((cLenPtr = getenv("CONTENT_LENGTH")) != NULL &&
416 hStdinThread = CreateThread(NULL, 8192,
417 (LPTHREAD_START_ROUTINE)&StdinThread,
419 if (hStdinThread == NULL) {
420 printf("<H2>OS_LibInit Failed to create STDIN thread! ERROR: %d</H2>\r\n\r\n",
427 * STDOUT will be used synchronously.
429 * XXX: May want to convert this so that it could be used for OVERLAPPED
430 * I/O later. If so, model it after the Stdin I/O as stdout is
431 * also incapable of async I/O on some servers.
433 stdioHandles[STDOUT_FILENO] = GetStdHandle(STD_OUTPUT_HANDLE);
434 if(!SetHandleInformation(stdioHandles[STDOUT_FILENO],
435 HANDLE_FLAG_INHERIT, FALSE)) {
440 if ((fakeFd = Win32NewDescriptor(FD_PIPE_SYNC,
441 (int)stdioHandles[STDOUT_FILENO],
442 STDOUT_FILENO)) == -1) {
446 * Set stdout equal to our pseudo FD
448 stdioFds[STDOUT_FILENO] = fakeFd;
451 stdioHandles[STDERR_FILENO] = GetStdHandle(STD_ERROR_HANDLE);
452 if(!SetHandleInformation(stdioHandles[STDERR_FILENO],
453 HANDLE_FLAG_INHERIT, FALSE)) {
457 if ((fakeFd = Win32NewDescriptor(FD_PIPE_SYNC,
458 (int)stdioHandles[STDERR_FILENO],
459 STDERR_FILENO)) == -1) {
463 * Set stderr equal to our pseudo FD
465 stdioFds[STDERR_FILENO] = fakeFd;
473 *--------------------------------------------------------------
477 * Shutdown the OS library.
483 * Memory freed, handles closed.
485 *--------------------------------------------------------------
487 void OS_LibShutdown()
490 if(hIoCompPort != INVALID_HANDLE_VALUE) {
491 CloseHandle(hIoCompPort);
492 hIoCompPort = INVALID_HANDLE_VALUE;
495 if(hStdinCompPort != INVALID_HANDLE_VALUE) {
496 CloseHandle(hStdinCompPort);
497 hStdinCompPort = INVALID_HANDLE_VALUE;
501 * Shutdown the socket library.
509 *--------------------------------------------------------------
511 * Win32FreeDescriptor --
513 * Free I/O descriptor entry in fdTable.
516 * Frees I/O descriptor entry in fdTable.
521 *--------------------------------------------------------------
523 static void Win32FreeDescriptor(int fd)
525 /* Catch it if fd is a bogus value */
526 ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX));
527 ASSERT(fdTable[fd].type != FD_UNUSED);
529 switch (fdTable[fd].type) {
532 /* Free file path string */
533 ASSERT(fdTable[fd].path != NULL);
534 free(fdTable[fd].path);
535 fdTable[fd].path = NULL;
539 * Break through to generic fdTable free-descriptor code
544 ASSERT(fdTable[fd].path == NULL);
545 fdTable[fd].type = FD_UNUSED;
546 fdTable[fd].path = NULL;
547 fdTable[fd].Errno = NO_ERROR;
548 fdTable[fd].offsetHighPtr = fdTable[fd].offsetLowPtr = NULL;
549 if (fdTable[fd].hMapMutex != NULL) {
550 CloseHandle(fdTable[fd].hMapMutex);
551 fdTable[fd].hMapMutex = NULL;
558 * OS_CreateLocalIpcFd --
560 * This procedure is responsible for creating the listener pipe
561 * on Windows NT for local process communication. It will create a
562 * named pipe and return a file descriptor to it to the caller.
565 * Listener pipe created. This call returns either a valid
566 * pseudo file descriptor or -1 on error.
569 * Listener pipe and IPC address are stored in the FCGI info
571 * 'errno' will set on errors (-1 is returned).
573 *----------------------------------------------------------------------
575 int OS_CreateLocalIpcFd(char *bindPath)
578 SECURITY_ATTRIBUTES sa;
579 HANDLE hListenPipe = INVALID_HANDLE_VALUE;
584 struct sockaddr_in sockAddr;
591 strcpy(host, bindPath);
592 if((tp = strchr(host, ':')) != 0) {
594 if((port = atoi(tp)) == 0) {
600 if(tcp && (*host && strcmp(host, "localhost") != 0)) {
601 fprintf(stderr, "To start a service on a TCP port can not "
602 "specify a host name.\n"
603 "You should either use \"localhost:<port>\" or "
604 " just use \":<port>.\"\n");
609 listenSock = socket(AF_INET, SOCK_STREAM, 0);
610 if(listenSock == SOCKET_ERROR) {
614 * Bind the listening socket.
616 memset((char *) &sockAddr, 0, sizeof(sockAddr));
617 sockAddr.sin_family = AF_INET;
618 sockAddr.sin_addr.s_addr = htonl(INADDR_ANY);
619 sockAddr.sin_port = htons(port);
620 servLen = sizeof(sockAddr);
622 if(bind(listenSock, (struct sockaddr *) &sockAddr, servLen) < 0
623 || listen(listenSock, 5) < 0) {
624 perror("bind/listen");
628 retFd = Win32NewDescriptor(FD_SOCKET_SYNC, (int)listenSock, -1);
634 * Initialize the SECURITY_ATTRIUBTES structure.
636 sa.nLength = sizeof(sa);
637 sa.lpSecurityDescriptor = NULL;
638 sa.bInheritHandle = TRUE; /* This will be inherited by the
643 * Create a mutex to be used to synchronize access to accepting a
644 * connection on a named pipe. We don't want to own this at creation
645 * time but would rather let the first process that goes for it
646 * be able to acquire it.
648 hPipeMutex = CreateMutex(NULL, FALSE, NULL);
649 if(hPipeMutex == NULL) {
652 if(!SetHandleInformation(hPipeMutex, HANDLE_FLAG_INHERIT,
656 sprintf(pipeMutexEnv, "%s=%d", MUTEX_VARNAME, (int)hPipeMutex);
657 putenv(pipeMutexEnv);
660 * Create a unique name to be used for the socket bind path.
661 * Make sure that this name is unique and that there's no process
664 * Named Pipe Pathname: \\.\pipe\FastCGI\OM_WS.pid.N
665 * Where: N is the pipe instance on the machine.
668 bpLen = (int)strlen(bindPathPrefix);
669 bpLen += strlen(bindPath);
670 localPath = malloc(bpLen+2);
671 strcpy(localPath, bindPathPrefix);
672 strcat(localPath, bindPath);
675 * Create and setup the named pipe to be used by the fcgi server.
677 hListenPipe = CreateNamedPipe(localPath, /* name of pipe */
678 PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
679 PIPE_TYPE_BYTE | PIPE_WAIT |
680 PIPE_READMODE_BYTE, /* pipe IO type */
681 PIPE_UNLIMITED_INSTANCES, /* number of instances */
682 4096, /* size of outbuf (0 == allocate as necessary) */
683 4096, /* size of inbuf */
684 0, /*1000,*/ /* default time-out value */
685 &sa); /* security attributes */
688 * Can't create an instance of the pipe, fail...
690 if (hListenPipe == INVALID_HANDLE_VALUE) {
694 retFd = Win32NewDescriptor(FD_PIPE_SYNC, (int)hListenPipe, -1);
700 *----------------------------------------------------------------------
704 * Create the pipe pathname connect to the remote application if
708 * -1 if fail or a valid handle if connection succeeds.
711 * Remote connection established.
713 *----------------------------------------------------------------------
715 int OS_FcgiConnect(char *bindPath)
717 char *pipePath = NULL;
721 struct sockaddr_in sockAddr;
722 int servLen, resultSock;
729 strcpy(host, bindPath);
730 if((tp = strchr(host, ':')) != 0) {
732 if((port = atoi(tp)) == 0) {
740 if((hp = gethostbyname((*host ? host : "localhost"))) == NULL) {
741 fprintf(stderr, "Unknown host: %s\n", bindPath);
744 sockAddr.sin_family = AF_INET;
745 memcpy(&sockAddr.sin_addr, hp->h_addr, hp->h_length);
746 sockAddr.sin_port = htons(port);
747 servLen = sizeof(sockAddr);
748 resultSock = socket(AF_INET, SOCK_STREAM, 0);
750 assert(resultSock >= 0);
751 connectStatus = connect(resultSock, (struct sockaddr *)
753 if(connectStatus < 0) {
755 * Most likely (errno == ENOENT || errno == ECONNREFUSED)
756 * and no FCGI application server is running.
758 closesocket(resultSock);
761 pseudoFd = Win32NewDescriptor(FD_SOCKET_SYNC, resultSock, -1);
763 closesocket(resultSock);
769 * Not a TCP connection, create and connect to a named pipe.
771 pipePath = malloc((size_t)(strlen(bindPathPrefix) + strlen(bindPath) + 2));
772 if(pipePath == NULL) {
775 strcpy(pipePath, bindPathPrefix);
776 strcat(pipePath, bindPath);
778 hPipe = CreateFile (pipePath,
779 /* Generic access, read/write. */
780 GENERIC_WRITE | GENERIC_READ,
781 /* Share both read and write. */
782 FILE_SHARE_READ | FILE_SHARE_WRITE ,
783 NULL, /* No security.*/
784 OPEN_EXISTING, /* Fail if not existing. */
785 FILE_FLAG_OVERLAPPED, /* Use overlap. */
786 NULL); /* No template. */
789 if(hPipe == INVALID_HANDLE_VALUE) {
793 if ((pseudoFd = Win32NewDescriptor(FD_PIPE_ASYNC, (int)hPipe, -1)) == -1) {
798 * Set stdin equal to our pseudo FD and create the I/O completion
799 * port to be used for async I/O.
801 if (!CreateIoCompletionPort(hPipe, hIoCompPort, pseudoFd, 1)) {
802 err = GetLastError();
803 Win32FreeDescriptor(pseudoFd);
813 *--------------------------------------------------------------
817 * Pass through to the appropriate NT read function.
820 * Returns number of byes read. Mimics unix read:.
821 * n bytes read, 0 or -1 failure: errno contains actual error
826 *--------------------------------------------------------------
828 int OS_Read(int fd, char * buf, size_t len)
834 * Catch any bogus fd values
836 ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX));
838 switch (fdTable[fd].type) {
845 * ReadFile returns: TRUE success, FALSE failure
847 if (!ReadFile(fdTable[fd].fid.fileHandle, buf, len, &bytesRead,
849 fdTable[fd].Errno = GetLastError();
855 case FD_SOCKET_ASYNC:
856 /* winsock recv returns n bytes recv'ed, SOCKET_ERROR failure */
858 * XXX: Test this with ReadFile. If it works, remove this code
859 * to simplify the routine.
861 if ((ret = recv(fdTable[fd].fid.sock, buf, len, 0)) ==
863 fdTable[fd].Errno = WSAGetLastError();
873 *--------------------------------------------------------------
877 * Perform a synchronous OS write.
880 * Returns number of bytes written. Mimics unix write:
881 * n bytes written, 0 or -1 failure (??? couldn't find man page).
886 *--------------------------------------------------------------
888 int OS_Write(int fd, char * buf, size_t len)
894 * Catch any bogus fd values
896 ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX));
897 ASSERT((fdTable[fd].type > FD_UNUSED) &&
898 (fdTable[fd].type <= FD_PIPE_ASYNC));
900 switch (fdTable[fd].type) {
907 * WriteFile returns: TRUE success, FALSE failure
909 if (!WriteFile(fdTable[fd].fid.fileHandle, buf, len,
910 &bytesWritten, NULL)) {
911 fdTable[fd].Errno = GetLastError();
916 case FD_SOCKET_ASYNC:
917 /* winsock send returns n bytes written, SOCKET_ERROR failure */
919 * XXX: Test this with WriteFile. If it works, remove this code
920 * to simplify the routine.
922 if ((ret = send(fdTable[fd].fid.sock, buf, len, 0)) ==
924 fdTable[fd].Errno = WSAGetLastError();
935 *----------------------------------------------------------------------
939 * Spawns a new server listener process, and stores the information
940 * relating to the child in the supplied record. A wait handler is
941 * registered on the child's completion. This involves creating
942 * a process on NT and preparing a command line with the required
943 * state (currently a -childproc flag and the server socket to use
944 * for accepting connections).
947 * 0 if success, -1 if error.
950 * Child process spawned.
952 *----------------------------------------------------------------------
954 int OS_SpawnChild(char *execPath, int listenFd)
956 STARTUPINFO StartupInfo;
957 PROCESS_INFORMATION pInfo;
960 memset((void *)&StartupInfo, 0, sizeof(STARTUPINFO));
961 StartupInfo.cb = sizeof (STARTUPINFO);
962 StartupInfo.lpReserved = NULL;
963 StartupInfo.lpReserved2 = NULL;
964 StartupInfo.cbReserved2 = 0;
965 StartupInfo.lpDesktop = NULL;
968 * FastCGI on NT will set the listener pipe HANDLE in the stdin of
969 * the new process. The fact that there is a stdin and NULL handles
970 * for stdout and stderr tells the FastCGI process that this is a
971 * FastCGI process and not a CGI process.
973 StartupInfo.dwFlags = STARTF_USESTDHANDLES;
975 * XXX: Do I have to dup the handle before spawning the process or is
976 * it sufficient to use the handle as it's reference counted
979 StartupInfo.hStdInput = fdTable[listenFd].fid.fileHandle;
980 StartupInfo.hStdOutput = INVALID_HANDLE_VALUE;
981 StartupInfo.hStdError = INVALID_HANDLE_VALUE;
984 * Make the listener socket inheritable.
986 success = SetHandleInformation(StartupInfo.hStdInput, HANDLE_FLAG_INHERIT,
993 * XXX: Might want to apply some specific security attributes to the
996 success = CreateProcess(execPath, /* LPCSTR address of module name */
997 NULL, /* LPCSTR address of command line */
998 NULL, /* Process security attributes */
999 NULL, /* Thread security attributes */
1000 TRUE, /* Inheritable Handes inherited. */
1001 0, /* DWORD creation flags */
1002 NULL, /* Use parent environment block */
1003 NULL, /* Address of current directory name */
1004 &StartupInfo, /* Address of STARTUPINFO */
1005 &pInfo); /* Address of PROCESS_INFORMATION */
1015 *--------------------------------------------------------------
1017 * OS_AsyncReadStdin --
1019 * This initiates an asynchronous read on the standard
1020 * input handle. This handle is not guaranteed to be
1021 * capable of performing asynchronous I/O so we send a
1022 * message to the StdinThread to do the synchronous read.
1025 * -1 if error, 0 otherwise.
1028 * Asynchronous message is queued to the StdinThread and an
1029 * overlapped structure is allocated/initialized.
1031 *--------------------------------------------------------------
1033 int OS_AsyncReadStdin(void *buf, int len, OS_AsyncProc procPtr,
1034 ClientData clientData)
1036 POVERLAPPED_REQUEST pOv;
1038 ASSERT(fdTable[STDIN_FILENO].type != FD_UNUSED);
1040 pOv = (POVERLAPPED_REQUEST)malloc(sizeof(struct OVERLAPPED_REQUEST));
1042 memset((void *)pOv, 0, sizeof(struct OVERLAPPED_REQUEST));
1043 pOv->clientData1 = (ClientData)buf;
1044 pOv->instance = fdTable[STDIN_FILENO].instance;
1045 pOv->procPtr = procPtr;
1046 pOv->clientData = clientData;
1048 PostQueuedCompletionStatus(hStdinCompPort, len, STDIN_FILENO,
1055 *--------------------------------------------------------------
1059 * This initiates an asynchronous read on the file
1060 * handle which may be a socket or named pipe.
1062 * We also must save the ProcPtr and ClientData, so later
1063 * when the io completes, we know who to call.
1065 * We don't look at any results here (the ReadFile may
1066 * return data if it is cached) but do all completion
1067 * processing in OS_Select when we get the io completion
1068 * port done notifications. Then we call the callback.
1071 * -1 if error, 0 otherwise.
1074 * Asynchronous I/O operation is queued for completion.
1076 *--------------------------------------------------------------
1078 int OS_AsyncRead(int fd, int offset, void *buf, int len,
1079 OS_AsyncProc procPtr, ClientData clientData)
1082 POVERLAPPED_REQUEST pOv;
1085 * Catch any bogus fd values
1087 ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX));
1089 * Confirm that this is an async fd
1091 ASSERT(fdTable[fd].type != FD_UNUSED);
1092 ASSERT(fdTable[fd].type != FD_FILE_SYNC);
1093 ASSERT(fdTable[fd].type != FD_PIPE_SYNC);
1094 ASSERT(fdTable[fd].type != FD_SOCKET_SYNC);
1096 pOv = (POVERLAPPED_REQUEST)malloc(sizeof(struct OVERLAPPED_REQUEST));
1098 memset((void *)pOv, 0, sizeof(struct OVERLAPPED_REQUEST));
1100 * Only file offsets should be non-zero, but make sure.
1102 if (fdTable[fd].type == FD_FILE_ASYNC)
1103 if (fdTable[fd].offset >= 0)
1104 pOv->overlapped.Offset = fdTable[fd].offset;
1106 pOv->overlapped.Offset = offset;
1107 pOv->instance = fdTable[fd].instance;
1108 pOv->procPtr = procPtr;
1109 pOv->clientData = clientData;
1112 * ReadFile returns: TRUE success, FALSE failure
1114 if (!ReadFile(fdTable[fd].fid.fileHandle, buf, len, &bytesRead,
1115 (LPOVERLAPPED)pOv)) {
1116 fdTable[fd].Errno = GetLastError();
1117 if(fdTable[fd].Errno == ERROR_NO_DATA ||
1118 fdTable[fd].Errno == ERROR_PIPE_NOT_CONNECTED) {
1119 PostQueuedCompletionStatus(hIoCompPort, 0, fd, (LPOVERLAPPED)pOv);
1122 if(fdTable[fd].Errno != ERROR_IO_PENDING) {
1123 PostQueuedCompletionStatus(hIoCompPort, 0, fd, (LPOVERLAPPED)pOv);
1126 fdTable[fd].Errno = 0;
1132 *--------------------------------------------------------------
1136 * This initiates an asynchronous write on the "fake" file
1137 * descriptor (which may be a file, socket, or named pipe).
1138 * We also must save the ProcPtr and ClientData, so later
1139 * when the io completes, we know who to call.
1141 * We don't look at any results here (the WriteFile generally
1142 * completes immediately) but do all completion processing
1143 * in OS_DoIo when we get the io completion port done
1144 * notifications. Then we call the callback.
1147 * -1 if error, 0 otherwise.
1150 * Asynchronous I/O operation is queued for completion.
1152 *--------------------------------------------------------------
1154 int OS_AsyncWrite(int fd, int offset, void *buf, int len,
1155 OS_AsyncProc procPtr, ClientData clientData)
1158 POVERLAPPED_REQUEST pOv;
1161 * Catch any bogus fd values
1163 ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX));
1165 * Confirm that this is an async fd
1167 ASSERT(fdTable[fd].type != FD_UNUSED);
1168 ASSERT(fdTable[fd].type != FD_FILE_SYNC);
1169 ASSERT(fdTable[fd].type != FD_PIPE_SYNC);
1170 ASSERT(fdTable[fd].type != FD_SOCKET_SYNC);
1172 pOv = (POVERLAPPED_REQUEST)malloc(sizeof(struct OVERLAPPED_REQUEST));
1174 memset((void *)pOv, 0, sizeof(struct OVERLAPPED_REQUEST));
1176 * Only file offsets should be non-zero, but make sure.
1178 if (fdTable[fd].type == FD_FILE_ASYNC)
1180 * Only file opened via OS_AsyncWrite with
1181 * O_APPEND will have an offset != -1.
1183 if (fdTable[fd].offset >= 0)
1185 * If the descriptor has a memory mapped file
1186 * handle, take the offsets from there.
1188 if (fdTable[fd].hMapMutex != NULL) {
1190 * Wait infinitely; this *should* not cause problems.
1192 WaitForSingleObject(fdTable[fd].hMapMutex, INFINITE);
1195 * Retrieve the shared offset values.
1197 pOv->overlapped.OffsetHigh = *(fdTable[fd].offsetHighPtr);
1198 pOv->overlapped.Offset = *(fdTable[fd].offsetLowPtr);
1201 * Update the shared offset values for the next write
1203 *(fdTable[fd].offsetHighPtr) += 0; /* XXX How do I handle overflow */
1204 *(fdTable[fd].offsetLowPtr) += len;
1206 ReleaseMutex(fdTable[fd].hMapMutex);
1208 pOv->overlapped.Offset = fdTable[fd].offset;
1210 pOv->overlapped.Offset = offset;
1211 pOv->instance = fdTable[fd].instance;
1212 pOv->procPtr = procPtr;
1213 pOv->clientData = clientData;
1216 * WriteFile returns: TRUE success, FALSE failure
1218 if (!WriteFile(fdTable[fd].fid.fileHandle, buf, len, &bytesWritten,
1219 (LPOVERLAPPED)pOv)) {
1220 fdTable[fd].Errno = GetLastError();
1221 if(fdTable[fd].Errno != ERROR_IO_PENDING) {
1222 PostQueuedCompletionStatus(hIoCompPort, 0, fd, (LPOVERLAPPED)pOv);
1225 fdTable[fd].Errno = 0;
1227 if (fdTable[fd].offset >= 0)
1228 fdTable[fd].offset += len;
1234 *--------------------------------------------------------------
1238 * Closes the descriptor with routine appropriate for
1239 * descriptor's type.
1242 * Socket or file is closed. Return values mimic Unix close:
1243 * 0 success, -1 failure
1246 * Entry in fdTable is marked as free.
1248 *--------------------------------------------------------------
1250 int OS_Close(int fd)
1255 * Catch it if fd is a bogus value
1257 ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX));
1258 ASSERT(fdTable[fd].type != FD_UNUSED);
1260 switch (fdTable[fd].type) {
1266 * CloseHandle returns: TRUE success, 0 failure
1268 if (CloseHandle(fdTable[fd].fid.fileHandle) == FALSE)
1271 case FD_SOCKET_SYNC:
1272 case FD_SOCKET_ASYNC:
1274 * Closing a socket that has an async read outstanding causes a
1275 * tcp reset and possible data loss. The shutdown call seems to
1278 shutdown(fdTable[fd].fid.sock, 2);
1280 * closesocket returns: 0 success, SOCKET_ERROR failure
1282 if (closesocket(fdTable[fd].fid.sock) == SOCKET_ERROR)
1286 return -1; /* fake failure */
1289 Win32FreeDescriptor(fd);
1294 *--------------------------------------------------------------
1298 * Cancel outstanding asynchronous reads and prevent subsequent
1299 * reads from completing.
1302 * Socket or file is shutdown. Return values mimic Unix shutdown:
1303 * 0 success, -1 failure
1305 *--------------------------------------------------------------
1307 int OS_CloseRead(int fd)
1312 * Catch it if fd is a bogus value
1314 ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX));
1315 ASSERT(fdTable[fd].type == FD_SOCKET_ASYNC
1316 || fdTable[fd].type == FD_SOCKET_SYNC);
1318 if (shutdown(fdTable[fd].fid.sock,0) == SOCKET_ERROR)
1324 *--------------------------------------------------------------
1328 * This function was formerly OS_Select. It's purpose is
1329 * to pull I/O completion events off the queue and dispatch
1330 * them to the appropriate place.
1336 * Handlers are called.
1338 *--------------------------------------------------------------
1340 int OS_DoIo(struct timeval *tmo)
1344 POVERLAPPED_REQUEST pOv;
1351 * We can loop in here, but not too long, as wait handlers
1353 * For cgi stdin, apparently select returns when io completion
1354 * ports don't, so don't wait the full timeout.
1357 ms = (tmo->tv_sec*1000 + tmo->tv_usec/1000) / 2;
1361 ms_last = tb.time*1000 + tb.millitm;
1363 if(tmo && (ms = tmo->tv_sec*1000 + tmo->tv_usec/1000)> 100)
1365 if (!GetQueuedCompletionStatus(hIoCompPort, &bytes, &fd,
1366 (LPOVERLAPPED *)&pOv, ms) && !pOv) {
1367 err = WSAGetLastError();
1368 return 0; /* timeout */
1371 ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX));
1372 /* call callback if descriptor still valid */
1374 if(pOv->instance == fdTable[fd].instance)
1375 (*pOv->procPtr)(pOv->clientData, bytes);
1379 ms -= (tb.time*1000 + tb.millitm - ms_last);
1380 ms_last = tb.time*1000 + tb.millitm;
1387 *----------------------------------------------------------------------
1389 * OS_FcgiIpcAccept --
1391 * Accepts a new FastCGI connection. This routine knows whether
1392 * we're dealing with TCP based sockets or NT Named Pipes for IPC.
1395 * -1 if the operation fails, otherwise this is a valid IPC fd.
1398 * New IPC connection is accepted.
1400 *----------------------------------------------------------------------
1402 int OS_FcgiIpcAccept(char *serverHostList)
1404 struct sockaddr_in sa;
1405 int isNewConnection;
1410 int clilen = sizeof(sa);
1411 DWORD waitForStatus;
1413 switch(listenType) {
1416 waitForStatus = WaitForSingleObject(hPipeMutex,INFINITE);
1417 switch(waitForStatus) {
1419 case WAIT_ABANDONED:
1428 * We have the mutex, go for the connection.
1430 pConnected = ConnectNamedPipe(hListen, NULL) ?
1431 TRUE : (GetLastError() == ERROR_PIPE_CONNECTED);
1433 ReleaseMutex(hPipeMutex);
1438 if (!DuplicateHandle(GetCurrentProcess(), hListen,
1439 GetCurrentProcess(), &hDup, 0,
1440 TRUE, /* allow inheritance */
1441 DUPLICATE_SAME_ACCESS)) {
1444 ipcFd = Win32NewDescriptor(FD_PIPE_SYNC, (int)hDup, -1);
1446 DisconnectNamedPipe(hListen);
1455 case FD_SOCKET_SYNC:
1456 hSock = accept((int)hListen, (struct sockaddr *) &sa, &clilen);
1459 } else if (sa.sin_family != AF_INET) { /* What are we? */
1466 if (serverHostList == NULL)
1467 isNewConnection = TRUE;
1469 tp1 = (char *) malloc(strlen(serverHostList)+1);
1470 ASSERT(tp1 != NULL);
1471 strcpy(tp1, serverHostList);
1473 if ((tp2 = strchr(tp1, ',')) != NULL)
1476 if (inet_addr(tp1) == sa.sin_addr.s_addr) {
1484 isNewConnection = TRUE;
1493 ipcFd = Win32NewDescriptor(FD_SOCKET_SYNC, hSock, -1);
1509 *----------------------------------------------------------------------
1513 * OS IPC routine to close an IPC connection.
1519 * IPC connection is closed.
1521 *----------------------------------------------------------------------
1523 int OS_IpcClose(int ipcFd)
1527 * Catch it if fd is a bogus value
1529 ASSERT((ipcFd >= 0) && (ipcFd < WIN32_OPEN_MAX));
1530 ASSERT(fdTable[ipcFd].type != FD_UNUSED);
1532 switch(listenType) {
1536 * Make sure that the client (ie. a Web Server in this case) has
1537 * read all data from the pipe before we disconnect.
1539 if(!FlushFileBuffers(fdTable[ipcFd].fid.fileHandle))
1541 if(DisconnectNamedPipe(fdTable[ipcFd].fid.fileHandle)) {
1549 case FD_SOCKET_SYNC:
1563 *----------------------------------------------------------------------
1567 * Determines whether this process is a FastCGI process or not.
1570 * Returns 1 if FastCGI, 0 if not.
1575 *----------------------------------------------------------------------
1579 if(listenType == FD_UNUSED) {
1590 *----------------------------------------------------------------------
1594 * Sets selected flag bits in an open file descriptor. Currently
1595 * this is only to put a SOCKET into non-blocking mode.
1597 *----------------------------------------------------------------------
1599 void OS_SetFlags(int fd, int flags)
1601 long int pLong = 1L;
1604 if(fdTable[fd].type == FD_SOCKET_SYNC && flags == O_NONBLOCK) {
1605 if (ioctlsocket(fdTable[fd].fid.sock, FIONBIO, &pLong) ==
1607 exit(WSAGetLastError());
1609 if (!CreateIoCompletionPort((HANDLE)fdTable[fd].fid.sock,
1610 hIoCompPort, fd, 1)) {
1611 err = GetLastError();
1615 fdTable[fd].type = FD_SOCKET_ASYNC;