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.8 2000/11/05 17:09:35 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;
95 * XXX Note there is no dyanmic sizing of this table, so if the
96 * number of open file descriptors exceeds WIN32_OPEN_MAX the
99 static struct FD_TABLE fdTable[WIN32_OPEN_MAX];
101 struct OVERLAPPED_REQUEST {
102 OVERLAPPED overlapped;
103 unsigned long instance; /* file instance (won't match after a close) */
104 OS_AsyncProc procPtr; /* callback routine */
105 ClientData clientData; /* callback argument */
106 ClientData clientData1; /* additional clientData */
108 typedef struct OVERLAPPED_REQUEST *POVERLAPPED_REQUEST;
110 static const char *bindPathPrefix = "\\\\.\\pipe\\FastCGI\\";
112 static int listenType = FD_UNUSED;
113 static HANDLE hListen = INVALID_HANDLE_VALUE;
114 static int libInitialized = 0;
118 *--------------------------------------------------------------
120 * Win32NewDescriptor --
122 * Set up for I/O descriptor masquerading.
125 * Returns "fake id" which masquerades as a UNIX-style "small
126 * non-negative integer" file/socket descriptor.
127 * Win32_* routine below will "do the right thing" based on the
128 * descriptor's actual type. -1 indicates failure.
131 * Entry in fdTable is reserved to represent the socket/file.
133 *--------------------------------------------------------------
135 static int Win32NewDescriptor(FILE_TYPE type, int fd, int desiredFd)
140 * If the "desiredFd" is not -1, try to get this entry for our
141 * pseudo file descriptor. If this is not available, return -1
142 * as the caller wanted to get this mapping. This is typically
143 * only used for mapping stdio handles.
145 if ((desiredFd >= 0) &&
146 (desiredFd < WIN32_OPEN_MAX)) {
148 if(fdTable[desiredFd].type == FD_UNUSED) {
158 * Next see if the entry that matches "fd" is available.
161 (fd < WIN32_OPEN_MAX) && (fdTable[fd].type == FD_UNUSED)) {
167 * Scan entries for one we can use. Start at 1 (0 fake id fails
168 * in some cases). -K*
170 for (index = 1; index < WIN32_OPEN_MAX; index++)
171 if (fdTable[index].type == FD_UNUSED)
174 /* If no table entries are available, return error. */
175 if (index == WIN32_OPEN_MAX) {
176 SetLastError(WSAEMFILE);
182 fdTable[index].fid.value = fd;
183 fdTable[index].type = type;
184 fdTable[index].path = NULL;
185 fdTable[index].Errno = NO_ERROR;
186 fdTable[index].status = 0;
187 fdTable[index].offset = -1;
188 fdTable[index].offsetHighPtr = fdTable[index].offsetLowPtr = NULL;
189 fdTable[index].hMapMutex = NULL;
190 fdTable[index].ovList = NULL;
196 *--------------------------------------------------------------
200 * This thread performs I/O on stadard input. It is needed
201 * because you can't guarantee that all applications will
202 * create standard input with sufficient access to perform
203 * asynchronous I/O. Since we don't want to block the app
204 * reading from stdin we make it look like it's using I/O
205 * completion ports to perform async I/O.
208 * Data is read from stdin and posted to the io completion
214 *--------------------------------------------------------------
216 static void StdinThread(LPDWORD startup){
221 POVERLAPPED_REQUEST pOv;
225 * Block until a request to read from stdin comes in or a
226 * request to terminate the thread arrives (fd = -1).
228 if (!GetQueuedCompletionStatus(hStdinCompPort, &bytesRead, &fd,
229 (LPOVERLAPPED *)&pOv, (DWORD)-1) && !pOv) {
234 ASSERT((fd == STDIN_FILENO) || (fd == -1));
239 ASSERT(pOv->clientData1 != NULL);
241 if(ReadFile(stdioHandles[STDIN_FILENO], pOv->clientData1, bytesRead,
243 PostQueuedCompletionStatus(hIoCompPort, bytesRead,
244 STDIN_FILENO, (LPOVERLAPPED)pOv);
256 *--------------------------------------------------------------
260 * Set up the OS library for use.
263 * Returns 0 if success, -1 if not.
266 * Sockets initialized, pseudo file descriptors setup, etc.
268 *--------------------------------------------------------------
270 int OS_LibInit(int stdioFds[3])
278 char *cLenPtr = NULL;
279 char *mutexPtr = NULL;
285 * Initialize windows sockets library.
287 wVersion = MAKEWORD(1,1);
288 err = WSAStartup( wVersion, &wsaData );
290 fprintf(stderr, "Error starting Windows Sockets. Error: %d",
296 * Create the I/O completion port to be used for our I/O queue.
298 if (hIoCompPort == INVALID_HANDLE_VALUE) {
299 hIoCompPort = CreateIoCompletionPort (INVALID_HANDLE_VALUE, NULL,
301 if(hIoCompPort == INVALID_HANDLE_VALUE) {
302 printf("<H2>OS_LibInit Failed CreateIoCompletionPort! ERROR: %d</H2>\r\n\r\n",
309 * Determine if this library is being used to listen for FastCGI
310 * connections. This is communicated by STDIN containing a
311 * valid handle to a listener object. In this case, both the
312 * "stdout" and "stderr" handles will be INVALID (ie. closed) by
313 * the starting process.
315 * The trick is determining if this is a pipe or a socket...
317 * XXX: Add the async accept test to determine socket or handle to a
320 if((GetStdHandle(STD_OUTPUT_HANDLE) == INVALID_HANDLE_VALUE) &&
321 (GetStdHandle(STD_ERROR_HANDLE) == INVALID_HANDLE_VALUE) &&
322 (GetStdHandle(STD_INPUT_HANDLE) != INVALID_HANDLE_VALUE) ) {
324 hListen = GetStdHandle(STD_INPUT_HANDLE);
327 * Set the pipe handle state so that it operates in wait mode.
329 * NOTE: The listenFd is not mapped to a pseudo file descriptor
330 * as all work done on it is contained to the OS library.
332 * XXX: Initial assumption is that SetNamedPipeHandleState will
333 * fail if this is an IP socket...
335 pipeMode = PIPE_READMODE_BYTE | PIPE_WAIT;
336 if(SetNamedPipeHandleState(hListen, &pipeMode, NULL, NULL)) {
337 listenType = FD_PIPE_SYNC;
339 * Lookup the mutex. If one is found, save it and
340 * remove it from the env table if it's not already
343 mutexPtr = getenv(MUTEX_VARNAME);
344 if(mutexPtr != NULL) {
345 hPipeMutex = (HANDLE)atoi(mutexPtr);
346 putenv(MUTEX_VARNAME"=");
349 listenType = FD_SOCKET_SYNC;
354 * If there are no stdioFds passed in, we're done.
356 if(stdioFds == NULL) {
362 * Setup standard input asynchronous I/O. There is actually a separate
363 * thread spawned for this purpose. The reason for this is that some
364 * web servers use anonymous pipes for the connection between itself
365 * and a CGI application. Anonymous pipes can't perform asynchronous
366 * I/O or use I/O completion ports. Therefore in order to present a
367 * consistent I/O dispatch model to an application we emulate I/O
368 * completion port behavior by having the standard input thread posting
369 * messages to the hIoCompPort which look like a complete overlapped
370 * I/O structure. This keeps the event dispatching simple from the
371 * application perspective.
373 stdioHandles[STDIN_FILENO] = GetStdHandle(STD_INPUT_HANDLE);
375 if(!SetHandleInformation(stdioHandles[STDIN_FILENO],
376 HANDLE_FLAG_INHERIT, 0)) {
378 * XXX: Causes error when run from command line. Check KB
379 err = GetLastError();
385 if ((fakeFd = Win32NewDescriptor(FD_PIPE_SYNC,
386 (int)stdioHandles[STDIN_FILENO],
387 STDIN_FILENO)) == -1) {
391 * Set stdin equal to our pseudo FD and create the I/O completion
392 * port to be used for async I/O.
394 stdioFds[STDIN_FILENO] = fakeFd;
398 * Create the I/O completion port to be used for communicating with
399 * the thread doing I/O on standard in. This port will carry read
400 * and possibly thread termination requests to the StdinThread.
402 if (hStdinCompPort == INVALID_HANDLE_VALUE) {
403 hStdinCompPort = CreateIoCompletionPort (INVALID_HANDLE_VALUE, NULL,
405 if(hStdinCompPort == INVALID_HANDLE_VALUE) {
406 printf("<H2>OS_LibInit Failed CreateIoCompletionPort: STDIN! ERROR: %d</H2>\r\n\r\n",
413 * Create the thread that will read stdin if the CONTENT_LENGTH
416 if((cLenPtr = getenv("CONTENT_LENGTH")) != NULL &&
418 hStdinThread = CreateThread(NULL, 8192,
419 (LPTHREAD_START_ROUTINE)&StdinThread,
421 if (hStdinThread == NULL) {
422 printf("<H2>OS_LibInit Failed to create STDIN thread! ERROR: %d</H2>\r\n\r\n",
429 * STDOUT will be used synchronously.
431 * XXX: May want to convert this so that it could be used for OVERLAPPED
432 * I/O later. If so, model it after the Stdin I/O as stdout is
433 * also incapable of async I/O on some servers.
435 stdioHandles[STDOUT_FILENO] = GetStdHandle(STD_OUTPUT_HANDLE);
436 if(!SetHandleInformation(stdioHandles[STDOUT_FILENO],
437 HANDLE_FLAG_INHERIT, FALSE)) {
442 if ((fakeFd = Win32NewDescriptor(FD_PIPE_SYNC,
443 (int)stdioHandles[STDOUT_FILENO],
444 STDOUT_FILENO)) == -1) {
448 * Set stdout equal to our pseudo FD
450 stdioFds[STDOUT_FILENO] = fakeFd;
453 stdioHandles[STDERR_FILENO] = GetStdHandle(STD_ERROR_HANDLE);
454 if(!SetHandleInformation(stdioHandles[STDERR_FILENO],
455 HANDLE_FLAG_INHERIT, FALSE)) {
459 if ((fakeFd = Win32NewDescriptor(FD_PIPE_SYNC,
460 (int)stdioHandles[STDERR_FILENO],
461 STDERR_FILENO)) == -1) {
465 * Set stderr equal to our pseudo FD
467 stdioFds[STDERR_FILENO] = fakeFd;
475 *--------------------------------------------------------------
479 * Shutdown the OS library.
485 * Memory freed, handles closed.
487 *--------------------------------------------------------------
489 void OS_LibShutdown()
492 if(hIoCompPort != INVALID_HANDLE_VALUE) {
493 CloseHandle(hIoCompPort);
494 hIoCompPort = INVALID_HANDLE_VALUE;
497 if(hStdinCompPort != INVALID_HANDLE_VALUE) {
498 CloseHandle(hStdinCompPort);
499 hStdinCompPort = INVALID_HANDLE_VALUE;
503 * Shutdown the socket library.
511 *--------------------------------------------------------------
513 * Win32FreeDescriptor --
515 * Free I/O descriptor entry in fdTable.
518 * Frees I/O descriptor entry in fdTable.
523 *--------------------------------------------------------------
525 static void Win32FreeDescriptor(int fd)
527 /* Catch it if fd is a bogus value */
528 ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX));
529 ASSERT(fdTable[fd].type != FD_UNUSED);
531 switch (fdTable[fd].type) {
534 /* Free file path string */
535 ASSERT(fdTable[fd].path != NULL);
536 free(fdTable[fd].path);
537 fdTable[fd].path = NULL;
541 * Break through to generic fdTable free-descriptor code
546 ASSERT(fdTable[fd].path == NULL);
547 fdTable[fd].type = FD_UNUSED;
548 fdTable[fd].path = NULL;
549 fdTable[fd].Errno = NO_ERROR;
550 fdTable[fd].offsetHighPtr = fdTable[fd].offsetLowPtr = NULL;
551 if (fdTable[fd].hMapMutex != NULL) {
552 CloseHandle(fdTable[fd].hMapMutex);
553 fdTable[fd].hMapMutex = NULL;
560 * OS_CreateLocalIpcFd --
562 * This procedure is responsible for creating the listener pipe
563 * on Windows NT for local process communication. It will create a
564 * named pipe and return a file descriptor to it to the caller.
567 * Listener pipe created. This call returns either a valid
568 * pseudo file descriptor or -1 on error.
571 * Listener pipe and IPC address are stored in the FCGI info
573 * 'errno' will set on errors (-1 is returned).
575 *----------------------------------------------------------------------
577 int OS_CreateLocalIpcFd(const char *bindPath, int backlog)
580 SECURITY_ATTRIBUTES sa;
581 HANDLE hListenPipe = INVALID_HANDLE_VALUE;
586 struct sockaddr_in sockAddr;
593 strcpy(host, bindPath);
594 if((tp = strchr(host, ':')) != 0) {
596 if((port = atoi(tp)) == 0) {
602 if(tcp && (*host && strcmp(host, "localhost") != 0)) {
603 fprintf(stderr, "To start a service on a TCP port can not "
604 "specify a host name.\n"
605 "You should either use \"localhost:<port>\" or "
606 " just use \":<port>.\"\n");
611 listenSock = socket(AF_INET, SOCK_STREAM, 0);
612 if(listenSock == SOCKET_ERROR) {
616 * Bind the listening socket.
618 memset((char *) &sockAddr, 0, sizeof(sockAddr));
619 sockAddr.sin_family = AF_INET;
620 sockAddr.sin_addr.s_addr = htonl(INADDR_ANY);
621 sockAddr.sin_port = htons(port);
622 servLen = sizeof(sockAddr);
624 if(bind(listenSock, (struct sockaddr *) &sockAddr, servLen) < 0
625 || listen(listenSock, backlog) < 0) {
626 perror("bind/listen");
630 retFd = Win32NewDescriptor(FD_SOCKET_SYNC, (int)listenSock, -1);
636 * Initialize the SECURITY_ATTRIUBTES structure.
638 sa.nLength = sizeof(sa);
639 sa.lpSecurityDescriptor = NULL;
640 sa.bInheritHandle = TRUE; /* This will be inherited by the
645 * Create a mutex to be used to synchronize access to accepting a
646 * connection on a named pipe. We don't want to own this at creation
647 * time but would rather let the first process that goes for it
648 * be able to acquire it.
650 hPipeMutex = CreateMutex(NULL, FALSE, NULL);
651 if(hPipeMutex == NULL) {
654 if(!SetHandleInformation(hPipeMutex, HANDLE_FLAG_INHERIT,
658 sprintf(pipeMutexEnv, "%s=%d", MUTEX_VARNAME, (int)hPipeMutex);
659 putenv(pipeMutexEnv);
662 * Create a unique name to be used for the socket bind path.
663 * Make sure that this name is unique and that there's no process
666 * Named Pipe Pathname: \\.\pipe\FastCGI\OM_WS.pid.N
667 * Where: N is the pipe instance on the machine.
670 bpLen = (int)strlen(bindPathPrefix);
671 bpLen += strlen(bindPath);
672 localPath = malloc(bpLen+2);
673 strcpy(localPath, bindPathPrefix);
674 strcat(localPath, bindPath);
677 * Create and setup the named pipe to be used by the fcgi server.
679 hListenPipe = CreateNamedPipe(localPath, /* name of pipe */
680 PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
681 PIPE_TYPE_BYTE | PIPE_WAIT |
682 PIPE_READMODE_BYTE, /* pipe IO type */
683 PIPE_UNLIMITED_INSTANCES, /* number of instances */
684 4096, /* size of outbuf (0 == allocate as necessary) */
685 4096, /* size of inbuf */
686 0, /*1000,*/ /* default time-out value */
687 &sa); /* security attributes */
690 * Can't create an instance of the pipe, fail...
692 if (hListenPipe == INVALID_HANDLE_VALUE) {
696 retFd = Win32NewDescriptor(FD_PIPE_SYNC, (int)hListenPipe, -1);
702 *----------------------------------------------------------------------
706 * Create the pipe pathname connect to the remote application if
710 * -1 if fail or a valid handle if connection succeeds.
713 * Remote connection established.
715 *----------------------------------------------------------------------
717 int OS_FcgiConnect(char *bindPath)
719 char *pipePath = NULL;
723 struct sockaddr_in sockAddr;
724 int servLen, resultSock;
731 strcpy(host, bindPath);
732 if((tp = strchr(host, ':')) != 0) {
734 if((port = atoi(tp)) == 0) {
742 if((hp = gethostbyname((*host ? host : "localhost"))) == NULL) {
743 fprintf(stderr, "Unknown host: %s\n", bindPath);
746 sockAddr.sin_family = AF_INET;
747 memcpy(&sockAddr.sin_addr, hp->h_addr, hp->h_length);
748 sockAddr.sin_port = htons(port);
749 servLen = sizeof(sockAddr);
750 resultSock = socket(AF_INET, SOCK_STREAM, 0);
752 ASSERT(resultSock >= 0);
753 connectStatus = connect(resultSock, (struct sockaddr *)
755 if(connectStatus < 0) {
757 * Most likely (errno == ENOENT || errno == ECONNREFUSED)
758 * and no FCGI application server is running.
760 closesocket(resultSock);
763 pseudoFd = Win32NewDescriptor(FD_SOCKET_SYNC, resultSock, -1);
765 closesocket(resultSock);
771 * Not a TCP connection, create and connect to a named pipe.
773 pipePath = malloc((size_t)(strlen(bindPathPrefix) + strlen(bindPath) + 2));
774 if(pipePath == NULL) {
777 strcpy(pipePath, bindPathPrefix);
778 strcat(pipePath, bindPath);
780 hPipe = CreateFile (pipePath,
781 /* Generic access, read/write. */
782 GENERIC_WRITE | GENERIC_READ,
783 /* Share both read and write. */
784 FILE_SHARE_READ | FILE_SHARE_WRITE ,
785 NULL, /* No security.*/
786 OPEN_EXISTING, /* Fail if not existing. */
787 FILE_FLAG_OVERLAPPED, /* Use overlap. */
788 NULL); /* No template. */
791 if(hPipe == INVALID_HANDLE_VALUE) {
795 if ((pseudoFd = Win32NewDescriptor(FD_PIPE_ASYNC, (int)hPipe, -1)) == -1) {
800 * Set stdin equal to our pseudo FD and create the I/O completion
801 * port to be used for async I/O.
803 if (!CreateIoCompletionPort(hPipe, hIoCompPort, pseudoFd, 1)) {
804 err = GetLastError();
805 Win32FreeDescriptor(pseudoFd);
815 *--------------------------------------------------------------
819 * Pass through to the appropriate NT read function.
822 * Returns number of byes read. Mimics unix read:.
823 * n bytes read, 0 or -1 failure: errno contains actual error
828 *--------------------------------------------------------------
830 int OS_Read(int fd, char * buf, size_t len)
836 * Catch any bogus fd values
838 ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX));
840 switch (fdTable[fd].type) {
847 * ReadFile returns: TRUE success, FALSE failure
849 if (!ReadFile(fdTable[fd].fid.fileHandle, buf, len, &bytesRead,
851 fdTable[fd].Errno = GetLastError();
857 case FD_SOCKET_ASYNC:
858 /* winsock recv returns n bytes recv'ed, SOCKET_ERROR failure */
860 * XXX: Test this with ReadFile. If it works, remove this code
861 * to simplify the routine.
863 if ((ret = recv(fdTable[fd].fid.sock, buf, len, 0)) ==
865 fdTable[fd].Errno = WSAGetLastError();
875 *--------------------------------------------------------------
879 * Perform a synchronous OS write.
882 * Returns number of bytes written. Mimics unix write:
883 * n bytes written, 0 or -1 failure (??? couldn't find man page).
888 *--------------------------------------------------------------
890 int OS_Write(int fd, char * buf, size_t len)
896 * Catch any bogus fd values
898 ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX));
899 ASSERT((fdTable[fd].type > FD_UNUSED) &&
900 (fdTable[fd].type <= FD_PIPE_ASYNC));
902 switch (fdTable[fd].type) {
909 * WriteFile returns: TRUE success, FALSE failure
911 if (!WriteFile(fdTable[fd].fid.fileHandle, buf, len,
912 &bytesWritten, NULL)) {
913 fdTable[fd].Errno = GetLastError();
918 case FD_SOCKET_ASYNC:
919 /* winsock send returns n bytes written, SOCKET_ERROR failure */
921 * XXX: Test this with WriteFile. If it works, remove this code
922 * to simplify the routine.
924 if ((ret = send(fdTable[fd].fid.sock, buf, len, 0)) ==
926 fdTable[fd].Errno = WSAGetLastError();
937 *----------------------------------------------------------------------
941 * Spawns a new server listener process, and stores the information
942 * relating to the child in the supplied record. A wait handler is
943 * registered on the child's completion. This involves creating
944 * a process on NT and preparing a command line with the required
945 * state (currently a -childproc flag and the server socket to use
946 * for accepting connections).
949 * 0 if success, -1 if error.
952 * Child process spawned.
954 *----------------------------------------------------------------------
956 int OS_SpawnChild(char *execPath, int listenFd)
958 STARTUPINFO StartupInfo;
959 PROCESS_INFORMATION pInfo;
962 memset((void *)&StartupInfo, 0, sizeof(STARTUPINFO));
963 StartupInfo.cb = sizeof (STARTUPINFO);
964 StartupInfo.lpReserved = NULL;
965 StartupInfo.lpReserved2 = NULL;
966 StartupInfo.cbReserved2 = 0;
967 StartupInfo.lpDesktop = NULL;
970 * FastCGI on NT will set the listener pipe HANDLE in the stdin of
971 * the new process. The fact that there is a stdin and NULL handles
972 * for stdout and stderr tells the FastCGI process that this is a
973 * FastCGI process and not a CGI process.
975 StartupInfo.dwFlags = STARTF_USESTDHANDLES;
977 * XXX: Do I have to dup the handle before spawning the process or is
978 * it sufficient to use the handle as it's reference counted
981 StartupInfo.hStdInput = fdTable[listenFd].fid.fileHandle;
982 StartupInfo.hStdOutput = INVALID_HANDLE_VALUE;
983 StartupInfo.hStdError = INVALID_HANDLE_VALUE;
986 * Make the listener socket inheritable.
988 success = SetHandleInformation(StartupInfo.hStdInput, HANDLE_FLAG_INHERIT,
995 * XXX: Might want to apply some specific security attributes to the
998 success = CreateProcess(execPath, /* LPCSTR address of module name */
999 NULL, /* LPCSTR address of command line */
1000 NULL, /* Process security attributes */
1001 NULL, /* Thread security attributes */
1002 TRUE, /* Inheritable Handes inherited. */
1003 0, /* DWORD creation flags */
1004 NULL, /* Use parent environment block */
1005 NULL, /* Address of current directory name */
1006 &StartupInfo, /* Address of STARTUPINFO */
1007 &pInfo); /* Address of PROCESS_INFORMATION */
1017 *--------------------------------------------------------------
1019 * OS_AsyncReadStdin --
1021 * This initiates an asynchronous read on the standard
1022 * input handle. This handle is not guaranteed to be
1023 * capable of performing asynchronous I/O so we send a
1024 * message to the StdinThread to do the synchronous read.
1027 * -1 if error, 0 otherwise.
1030 * Asynchronous message is queued to the StdinThread and an
1031 * overlapped structure is allocated/initialized.
1033 *--------------------------------------------------------------
1035 int OS_AsyncReadStdin(void *buf, int len, OS_AsyncProc procPtr,
1036 ClientData clientData)
1038 POVERLAPPED_REQUEST pOv;
1040 ASSERT(fdTable[STDIN_FILENO].type != FD_UNUSED);
1042 pOv = (POVERLAPPED_REQUEST)malloc(sizeof(struct OVERLAPPED_REQUEST));
1044 memset((void *)pOv, 0, sizeof(struct OVERLAPPED_REQUEST));
1045 pOv->clientData1 = (ClientData)buf;
1046 pOv->instance = fdTable[STDIN_FILENO].instance;
1047 pOv->procPtr = procPtr;
1048 pOv->clientData = clientData;
1050 PostQueuedCompletionStatus(hStdinCompPort, len, STDIN_FILENO,
1057 *--------------------------------------------------------------
1061 * This initiates an asynchronous read on the file
1062 * handle which may be a socket or named pipe.
1064 * We also must save the ProcPtr and ClientData, so later
1065 * when the io completes, we know who to call.
1067 * We don't look at any results here (the ReadFile may
1068 * return data if it is cached) but do all completion
1069 * processing in OS_Select when we get the io completion
1070 * port done notifications. Then we call the callback.
1073 * -1 if error, 0 otherwise.
1076 * Asynchronous I/O operation is queued for completion.
1078 *--------------------------------------------------------------
1080 int OS_AsyncRead(int fd, int offset, void *buf, int len,
1081 OS_AsyncProc procPtr, ClientData clientData)
1084 POVERLAPPED_REQUEST pOv;
1087 * Catch any bogus fd values
1089 ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX));
1091 * Confirm that this is an async fd
1093 ASSERT(fdTable[fd].type != FD_UNUSED);
1094 ASSERT(fdTable[fd].type != FD_FILE_SYNC);
1095 ASSERT(fdTable[fd].type != FD_PIPE_SYNC);
1096 ASSERT(fdTable[fd].type != FD_SOCKET_SYNC);
1098 pOv = (POVERLAPPED_REQUEST)malloc(sizeof(struct OVERLAPPED_REQUEST));
1100 memset((void *)pOv, 0, sizeof(struct OVERLAPPED_REQUEST));
1102 * Only file offsets should be non-zero, but make sure.
1104 if (fdTable[fd].type == FD_FILE_ASYNC)
1105 if (fdTable[fd].offset >= 0)
1106 pOv->overlapped.Offset = fdTable[fd].offset;
1108 pOv->overlapped.Offset = offset;
1109 pOv->instance = fdTable[fd].instance;
1110 pOv->procPtr = procPtr;
1111 pOv->clientData = clientData;
1114 * ReadFile returns: TRUE success, FALSE failure
1116 if (!ReadFile(fdTable[fd].fid.fileHandle, buf, len, &bytesRead,
1117 (LPOVERLAPPED)pOv)) {
1118 fdTable[fd].Errno = GetLastError();
1119 if(fdTable[fd].Errno == ERROR_NO_DATA ||
1120 fdTable[fd].Errno == ERROR_PIPE_NOT_CONNECTED) {
1121 PostQueuedCompletionStatus(hIoCompPort, 0, fd, (LPOVERLAPPED)pOv);
1124 if(fdTable[fd].Errno != ERROR_IO_PENDING) {
1125 PostQueuedCompletionStatus(hIoCompPort, 0, fd, (LPOVERLAPPED)pOv);
1128 fdTable[fd].Errno = 0;
1134 *--------------------------------------------------------------
1138 * This initiates an asynchronous write on the "fake" file
1139 * descriptor (which may be a file, socket, or named pipe).
1140 * We also must save the ProcPtr and ClientData, so later
1141 * when the io completes, we know who to call.
1143 * We don't look at any results here (the WriteFile generally
1144 * completes immediately) but do all completion processing
1145 * in OS_DoIo when we get the io completion port done
1146 * notifications. Then we call the callback.
1149 * -1 if error, 0 otherwise.
1152 * Asynchronous I/O operation is queued for completion.
1154 *--------------------------------------------------------------
1156 int OS_AsyncWrite(int fd, int offset, void *buf, int len,
1157 OS_AsyncProc procPtr, ClientData clientData)
1160 POVERLAPPED_REQUEST pOv;
1163 * Catch any bogus fd values
1165 ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX));
1167 * Confirm that this is an async fd
1169 ASSERT(fdTable[fd].type != FD_UNUSED);
1170 ASSERT(fdTable[fd].type != FD_FILE_SYNC);
1171 ASSERT(fdTable[fd].type != FD_PIPE_SYNC);
1172 ASSERT(fdTable[fd].type != FD_SOCKET_SYNC);
1174 pOv = (POVERLAPPED_REQUEST)malloc(sizeof(struct OVERLAPPED_REQUEST));
1176 memset((void *)pOv, 0, sizeof(struct OVERLAPPED_REQUEST));
1178 * Only file offsets should be non-zero, but make sure.
1180 if (fdTable[fd].type == FD_FILE_ASYNC)
1182 * Only file opened via OS_AsyncWrite with
1183 * O_APPEND will have an offset != -1.
1185 if (fdTable[fd].offset >= 0)
1187 * If the descriptor has a memory mapped file
1188 * handle, take the offsets from there.
1190 if (fdTable[fd].hMapMutex != NULL) {
1192 * Wait infinitely; this *should* not cause problems.
1194 WaitForSingleObject(fdTable[fd].hMapMutex, INFINITE);
1197 * Retrieve the shared offset values.
1199 pOv->overlapped.OffsetHigh = *(fdTable[fd].offsetHighPtr);
1200 pOv->overlapped.Offset = *(fdTable[fd].offsetLowPtr);
1203 * Update the shared offset values for the next write
1205 *(fdTable[fd].offsetHighPtr) += 0; /* XXX How do I handle overflow */
1206 *(fdTable[fd].offsetLowPtr) += len;
1208 ReleaseMutex(fdTable[fd].hMapMutex);
1210 pOv->overlapped.Offset = fdTable[fd].offset;
1212 pOv->overlapped.Offset = offset;
1213 pOv->instance = fdTable[fd].instance;
1214 pOv->procPtr = procPtr;
1215 pOv->clientData = clientData;
1218 * WriteFile returns: TRUE success, FALSE failure
1220 if (!WriteFile(fdTable[fd].fid.fileHandle, buf, len, &bytesWritten,
1221 (LPOVERLAPPED)pOv)) {
1222 fdTable[fd].Errno = GetLastError();
1223 if(fdTable[fd].Errno != ERROR_IO_PENDING) {
1224 PostQueuedCompletionStatus(hIoCompPort, 0, fd, (LPOVERLAPPED)pOv);
1227 fdTable[fd].Errno = 0;
1229 if (fdTable[fd].offset >= 0)
1230 fdTable[fd].offset += len;
1236 *--------------------------------------------------------------
1240 * Closes the descriptor with routine appropriate for
1241 * descriptor's type.
1244 * Socket or file is closed. Return values mimic Unix close:
1245 * 0 success, -1 failure
1248 * Entry in fdTable is marked as free.
1250 *--------------------------------------------------------------
1252 int OS_Close(int fd)
1257 * Catch it if fd is a bogus value
1259 ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX));
1260 ASSERT(fdTable[fd].type != FD_UNUSED);
1262 switch (fdTable[fd].type) {
1268 * CloseHandle returns: TRUE success, 0 failure
1270 if (CloseHandle(fdTable[fd].fid.fileHandle) == FALSE)
1273 case FD_SOCKET_SYNC:
1274 case FD_SOCKET_ASYNC:
1276 * Closing a socket that has an async read outstanding causes a
1277 * tcp reset and possible data loss. The shutdown call seems to
1280 shutdown(fdTable[fd].fid.sock, 2);
1282 * closesocket returns: 0 success, SOCKET_ERROR failure
1284 if (closesocket(fdTable[fd].fid.sock) == SOCKET_ERROR)
1288 return -1; /* fake failure */
1291 Win32FreeDescriptor(fd);
1296 *--------------------------------------------------------------
1300 * Cancel outstanding asynchronous reads and prevent subsequent
1301 * reads from completing.
1304 * Socket or file is shutdown. Return values mimic Unix shutdown:
1305 * 0 success, -1 failure
1307 *--------------------------------------------------------------
1309 int OS_CloseRead(int fd)
1314 * Catch it if fd is a bogus value
1316 ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX));
1317 ASSERT(fdTable[fd].type == FD_SOCKET_ASYNC
1318 || fdTable[fd].type == FD_SOCKET_SYNC);
1320 if (shutdown(fdTable[fd].fid.sock,0) == SOCKET_ERROR)
1326 *--------------------------------------------------------------
1330 * This function was formerly OS_Select. It's purpose is
1331 * to pull I/O completion events off the queue and dispatch
1332 * them to the appropriate place.
1338 * Handlers are called.
1340 *--------------------------------------------------------------
1342 int OS_DoIo(struct timeval *tmo)
1346 POVERLAPPED_REQUEST pOv;
1353 * We can loop in here, but not too long, as wait handlers
1355 * For cgi stdin, apparently select returns when io completion
1356 * ports don't, so don't wait the full timeout.
1359 ms = (tmo->tv_sec*1000 + tmo->tv_usec/1000) / 2;
1363 ms_last = tb.time*1000 + tb.millitm;
1365 if(tmo && (ms = tmo->tv_sec*1000 + tmo->tv_usec/1000)> 100)
1367 if (!GetQueuedCompletionStatus(hIoCompPort, &bytes, &fd,
1368 (LPOVERLAPPED *)&pOv, ms) && !pOv) {
1369 err = WSAGetLastError();
1370 return 0; /* timeout */
1373 ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX));
1374 /* call callback if descriptor still valid */
1376 if(pOv->instance == fdTable[fd].instance)
1377 (*pOv->procPtr)(pOv->clientData, bytes);
1381 ms -= (tb.time*1000 + tb.millitm - ms_last);
1382 ms_last = tb.time*1000 + tb.millitm;
1389 *----------------------------------------------------------------------
1393 * Accepts a new FastCGI connection. This routine knows whether
1394 * we're dealing with TCP based sockets or NT Named Pipes for IPC.
1397 * -1 if the operation fails, otherwise this is a valid IPC fd.
1400 * New IPC connection is accepted.
1402 *----------------------------------------------------------------------
1404 int OS_Accept(int listen_sock, int fail_on_intr, const char *webServerAddrs)
1406 /* XXX This is broken for listen_sock & fail_on_intr */
1407 struct sockaddr_in sa;
1408 int isNewConnection;
1413 int clilen = sizeof(sa);
1414 DWORD waitForStatus;
1416 switch(listenType) {
1419 waitForStatus = WaitForSingleObject(hPipeMutex,INFINITE);
1420 switch(waitForStatus) {
1422 case WAIT_ABANDONED:
1431 * We have the mutex, go for the connection.
1433 pConnected = ConnectNamedPipe(hListen, NULL) ?
1434 TRUE : (GetLastError() == ERROR_PIPE_CONNECTED);
1436 ReleaseMutex(hPipeMutex);
1441 if (!DuplicateHandle(GetCurrentProcess(), hListen,
1442 GetCurrentProcess(), &hDup, 0,
1443 TRUE, /* allow inheritance */
1444 DUPLICATE_SAME_ACCESS)) {
1447 ipcFd = Win32NewDescriptor(FD_PIPE_SYNC, (int)hDup, -1);
1449 DisconnectNamedPipe(hListen);
1458 case FD_SOCKET_SYNC:
1459 hSock = accept((int)hListen, (struct sockaddr *) &sa, &clilen);
1462 } else if (sa.sin_family != AF_INET) { /* What are we? */
1469 if (webServerAddrs == NULL)
1470 isNewConnection = TRUE;
1472 tp1 = (char *) malloc(strlen(webServerAddrs)+1);
1473 ASSERT(tp1 != NULL);
1474 strcpy(tp1, webServerAddrs);
1476 if ((tp2 = strchr(tp1, ',')) != NULL)
1479 if (inet_addr(tp1) == sa.sin_addr.s_addr) {
1487 isNewConnection = TRUE;
1496 ipcFd = Win32NewDescriptor(FD_SOCKET_SYNC, hSock, -1);
1512 *----------------------------------------------------------------------
1516 * OS IPC routine to close an IPC connection.
1522 * IPC connection is closed.
1524 *----------------------------------------------------------------------
1526 int OS_IpcClose(int ipcFd)
1532 * Catch it if fd is a bogus value
1534 ASSERT((ipcFd >= 0) && (ipcFd < WIN32_OPEN_MAX));
1535 ASSERT(fdTable[ipcFd].type != FD_UNUSED);
1537 switch(listenType) {
1541 * Make sure that the client (ie. a Web Server in this case) has
1542 * read all data from the pipe before we disconnect.
1544 if(!FlushFileBuffers(fdTable[ipcFd].fid.fileHandle))
1546 if(DisconnectNamedPipe(fdTable[ipcFd].fid.fileHandle)) {
1554 case FD_SOCKET_SYNC:
1568 *----------------------------------------------------------------------
1572 * Determines whether this process is a FastCGI process or not.
1575 * Returns 1 if FastCGI, 0 if not.
1580 *----------------------------------------------------------------------
1582 int OS_IsFcgi(int sock)
1584 /* XXX This is broken for sock */
1585 if(listenType == FD_UNUSED) {
1594 *----------------------------------------------------------------------
1598 * Sets selected flag bits in an open file descriptor. Currently
1599 * this is only to put a SOCKET into non-blocking mode.
1601 *----------------------------------------------------------------------
1603 void OS_SetFlags(int fd, int flags)
1605 unsigned long pLong = 1L;
1608 if (fdTable[fd].type == FD_SOCKET_SYNC && flags == O_NONBLOCK) {
1609 if (ioctlsocket(fdTable[fd].fid.sock, FIONBIO, &pLong) ==
1611 exit(WSAGetLastError());
1613 if (!CreateIoCompletionPort((HANDLE)fdTable[fd].fid.sock,
1614 hIoCompPort, fd, 1)) {
1615 err = GetLastError();
1619 fdTable[fd].type = FD_SOCKET_ASYNC;