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.15 2001/06/19 17:11:39 robs Exp $";
23 #define WIN32_LEAN_AND_MEAN
29 #include <sys/timeb.h>
31 #define DLLAPI __declspec(dllexport)
35 #define WIN32_OPEN_MAX 128 /* XXX: Small hack */
38 * millisecs to wait for a client connection before checking the
39 * shutdown flag (then go back to waiting for a connection, etc).
41 #define ACCEPT_TIMEOUT 1000
43 #define MUTEX_VARNAME "_FCGI_MUTEX_"
44 #define SHUTDOWN_EVENT_NAME "_FCGI_SHUTDOWN_EVENT_"
45 #define LOCALHOST "localhost"
47 static HANDLE hIoCompPort = INVALID_HANDLE_VALUE;
48 static HANDLE hStdinCompPort = INVALID_HANDLE_VALUE;
49 static HANDLE hStdinThread = INVALID_HANDLE_VALUE;
51 static HANDLE stdioHandles[3] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE,
52 INVALID_HANDLE_VALUE};
54 static HANDLE acceptMutex = INVALID_HANDLE_VALUE;
56 static BOOLEAN shutdownPending = FALSE;
59 * An enumeration of the file types
60 * supported by the FD_TABLE structure.
62 * XXX: Not all currently supported. This allows for future
82 * Structure used to map file handle and socket handle
83 * values into values that can be used to create unix-like
84 * select bitmaps, read/write for both sockets/files.
91 unsigned long instance;
93 int offset; /* only valid for async file writes */
94 LPDWORD offsetHighPtr; /* pointers to offset high and low words */
95 LPDWORD offsetLowPtr; /* only valid for async file writes (logs) */
96 HANDLE hMapMutex; /* mutex handle for multi-proc offset update */
97 LPVOID ovList; /* List of associated OVERLAPPED_REQUESTs */
101 * XXX Note there is no dyanmic sizing of this table, so if the
102 * number of open file descriptors exceeds WIN32_OPEN_MAX the
105 static struct FD_TABLE fdTable[WIN32_OPEN_MAX];
107 static CRITICAL_SECTION fdTableCritical;
109 struct OVERLAPPED_REQUEST {
110 OVERLAPPED overlapped;
111 unsigned long instance; /* file instance (won't match after a close) */
112 OS_AsyncProc procPtr; /* callback routine */
113 ClientData clientData; /* callback argument */
114 ClientData clientData1; /* additional clientData */
116 typedef struct OVERLAPPED_REQUEST *POVERLAPPED_REQUEST;
118 static const char *bindPathPrefix = "\\\\.\\pipe\\FastCGI\\";
120 static enum FILE_TYPE listenType = FD_UNUSED;
122 // XXX This should be a DESCRIPTOR
123 static HANDLE hListen = INVALID_HANDLE_VALUE;
125 static OVERLAPPED listenOverlapped;
126 static BOOLEAN libInitialized = FALSE;
129 *--------------------------------------------------------------
131 * Win32NewDescriptor --
133 * Set up for I/O descriptor masquerading.
136 * Returns "fake id" which masquerades as a UNIX-style "small
137 * non-negative integer" file/socket descriptor.
138 * Win32_* routine below will "do the right thing" based on the
139 * descriptor's actual type. -1 indicates failure.
142 * Entry in fdTable is reserved to represent the socket/file.
144 *--------------------------------------------------------------
146 static int Win32NewDescriptor(FILE_TYPE type, int fd, int desiredFd)
150 EnterCriticalSection(&fdTableCritical);
153 * If desiredFd is set, try to get this entry (this is used for
154 * mapping stdio handles). Otherwise try to get the fd entry.
155 * If this is not available, find a the first empty slot. .
157 if (desiredFd >= 0 && desiredFd < WIN32_OPEN_MAX)
159 if (fdTable[desiredFd].type == FD_UNUSED)
166 if (fd < WIN32_OPEN_MAX && fdTable[fd].type == FD_UNUSED)
174 for (i = 1; i < WIN32_OPEN_MAX; ++i)
176 if (fdTable[i].type == FD_UNUSED)
187 fdTable[index].fid.value = fd;
188 fdTable[index].type = type;
189 fdTable[index].path = NULL;
190 fdTable[index].Errno = NO_ERROR;
191 fdTable[index].status = 0;
192 fdTable[index].offset = -1;
193 fdTable[index].offsetHighPtr = fdTable[index].offsetLowPtr = NULL;
194 fdTable[index].hMapMutex = NULL;
195 fdTable[index].ovList = NULL;
198 LeaveCriticalSection(&fdTableCritical);
203 *--------------------------------------------------------------
207 * This thread performs I/O on stadard input. It is needed
208 * because you can't guarantee that all applications will
209 * create standard input with sufficient access to perform
210 * asynchronous I/O. Since we don't want to block the app
211 * reading from stdin we make it look like it's using I/O
212 * completion ports to perform async I/O.
215 * Data is read from stdin and posted to the io completion
221 *--------------------------------------------------------------
223 static void StdinThread(LPDWORD startup){
228 POVERLAPPED_REQUEST pOv;
232 * Block until a request to read from stdin comes in or a
233 * request to terminate the thread arrives (fd = -1).
235 if (!GetQueuedCompletionStatus(hStdinCompPort, &bytesRead, &fd,
236 (LPOVERLAPPED *)&pOv, (DWORD)-1) && !pOv) {
241 ASSERT((fd == STDIN_FILENO) || (fd == -1));
246 ASSERT(pOv->clientData1 != NULL);
248 if(ReadFile(stdioHandles[STDIN_FILENO], pOv->clientData1, bytesRead,
250 PostQueuedCompletionStatus(hIoCompPort, bytesRead,
251 STDIN_FILENO, (LPOVERLAPPED)pOv);
261 static DWORD WINAPI ShutdownRequestThread(LPVOID arg)
263 HANDLE shutdownEvent = (HANDLE) arg;
265 if (WaitForSingleObject(shutdownEvent, INFINITE) == WAIT_FAILED)
267 // Assuming it will happen again, all we can do is exit the thread
272 // "Simple reads and writes to properly-aligned 32-bit variables are atomic"
273 shutdownPending = TRUE;
275 // Before an accept() is entered the shutdownPending flag is checked.
276 // If set, OS_Accept() will return -1. If not, it waits
277 // on a connection request for one second, checks the flag, & repeats.
278 // Only one process/thread is allowed to do this at time by
279 // wrapping the accept() with mutex.
285 *--------------------------------------------------------------
289 * Set up the OS library for use.
292 * Returns 0 if success, -1 if not.
295 * Sockets initialized, pseudo file descriptors setup, etc.
297 *--------------------------------------------------------------
299 int OS_LibInit(int stdioFds[3])
306 char *cLenPtr = NULL;
312 InitializeCriticalSection(&fdTableCritical);
315 * Initialize windows sockets library.
317 wVersion = MAKEWORD(2,0);
318 err = WSAStartup( wVersion, &wsaData );
320 fprintf(stderr, "Error starting Windows Sockets. Error: %d",
326 * Create the I/O completion port to be used for our I/O queue.
328 if (hIoCompPort == INVALID_HANDLE_VALUE) {
329 hIoCompPort = CreateIoCompletionPort (INVALID_HANDLE_VALUE, NULL,
331 if(hIoCompPort == INVALID_HANDLE_VALUE) {
332 printf("<H2>OS_LibInit Failed CreateIoCompletionPort! ERROR: %d</H2>\r\n\r\n",
339 * If a shutdown event is in the env, save it (I don't see any to
340 * remove it from the environment out from under the application).
341 * Spawn a thread to wait on the shutdown request.
343 val = getenv(SHUTDOWN_EVENT_NAME);
346 HANDLE shutdownEvent = (HANDLE) atoi(val);
348 putenv(SHUTDOWN_EVENT_NAME"=");
350 if (! CreateThread(NULL, 0, ShutdownRequestThread,
351 shutdownEvent, 0, NULL))
358 * If an accept mutex is in the env, save it and remove it.
360 val = getenv(MUTEX_VARNAME);
363 acceptMutex = (HANDLE) atoi(val);
368 * Determine if this library is being used to listen for FastCGI
369 * connections. This is communicated by STDIN containing a
370 * valid handle to a listener object. In this case, both the
371 * "stdout" and "stderr" handles will be INVALID (ie. closed) by
372 * the starting process.
374 * The trick is determining if this is a pipe or a socket...
376 * XXX: Add the async accept test to determine socket or handle to a
379 if((GetStdHandle(STD_OUTPUT_HANDLE) == INVALID_HANDLE_VALUE) &&
380 (GetStdHandle(STD_ERROR_HANDLE) == INVALID_HANDLE_VALUE) &&
381 (GetStdHandle(STD_INPUT_HANDLE) != INVALID_HANDLE_VALUE) )
383 DWORD pipeMode = PIPE_READMODE_BYTE | PIPE_WAIT;
384 HANDLE oldStdIn = GetStdHandle(STD_INPUT_HANDLE);
386 // Move the handle to a "low" number
387 if (! DuplicateHandle(GetCurrentProcess(), oldStdIn,
388 GetCurrentProcess(), &hListen,
389 0, TRUE, DUPLICATE_SAME_ACCESS))
394 if (! SetStdHandle(STD_INPUT_HANDLE, hListen))
399 CloseHandle(oldStdIn);
402 * Set the pipe handle state so that it operates in wait mode.
404 * NOTE: The listenFd is not mapped to a pseudo file descriptor
405 * as all work done on it is contained to the OS library.
407 * XXX: Initial assumption is that SetNamedPipeHandleState will
408 * fail if this is an IP socket...
410 if (SetNamedPipeHandleState(hListen, &pipeMode, NULL, NULL))
412 listenType = FD_PIPE_SYNC;
413 listenOverlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
417 listenType = FD_SOCKET_SYNC;
422 * If there are no stdioFds passed in, we're done.
424 if(stdioFds == NULL) {
430 * Setup standard input asynchronous I/O. There is actually a separate
431 * thread spawned for this purpose. The reason for this is that some
432 * web servers use anonymous pipes for the connection between itself
433 * and a CGI application. Anonymous pipes can't perform asynchronous
434 * I/O or use I/O completion ports. Therefore in order to present a
435 * consistent I/O dispatch model to an application we emulate I/O
436 * completion port behavior by having the standard input thread posting
437 * messages to the hIoCompPort which look like a complete overlapped
438 * I/O structure. This keeps the event dispatching simple from the
439 * application perspective.
441 stdioHandles[STDIN_FILENO] = GetStdHandle(STD_INPUT_HANDLE);
443 if(!SetHandleInformation(stdioHandles[STDIN_FILENO],
444 HANDLE_FLAG_INHERIT, 0)) {
446 * XXX: Causes error when run from command line. Check KB
447 err = GetLastError();
453 if ((fakeFd = Win32NewDescriptor(FD_PIPE_SYNC,
454 (int)stdioHandles[STDIN_FILENO],
455 STDIN_FILENO)) == -1) {
459 * Set stdin equal to our pseudo FD and create the I/O completion
460 * port to be used for async I/O.
462 stdioFds[STDIN_FILENO] = fakeFd;
466 * Create the I/O completion port to be used for communicating with
467 * the thread doing I/O on standard in. This port will carry read
468 * and possibly thread termination requests to the StdinThread.
470 if (hStdinCompPort == INVALID_HANDLE_VALUE) {
471 hStdinCompPort = CreateIoCompletionPort (INVALID_HANDLE_VALUE, NULL,
473 if(hStdinCompPort == INVALID_HANDLE_VALUE) {
474 printf("<H2>OS_LibInit Failed CreateIoCompletionPort: STDIN! ERROR: %d</H2>\r\n\r\n",
481 * Create the thread that will read stdin if the CONTENT_LENGTH
484 if((cLenPtr = getenv("CONTENT_LENGTH")) != NULL &&
486 hStdinThread = CreateThread(NULL, 8192,
487 (LPTHREAD_START_ROUTINE)&StdinThread,
489 if (hStdinThread == NULL) {
490 printf("<H2>OS_LibInit Failed to create STDIN thread! ERROR: %d</H2>\r\n\r\n",
497 * STDOUT will be used synchronously.
499 * XXX: May want to convert this so that it could be used for OVERLAPPED
500 * I/O later. If so, model it after the Stdin I/O as stdout is
501 * also incapable of async I/O on some servers.
503 stdioHandles[STDOUT_FILENO] = GetStdHandle(STD_OUTPUT_HANDLE);
504 if(!SetHandleInformation(stdioHandles[STDOUT_FILENO],
505 HANDLE_FLAG_INHERIT, FALSE)) {
510 if ((fakeFd = Win32NewDescriptor(FD_PIPE_SYNC,
511 (int)stdioHandles[STDOUT_FILENO],
512 STDOUT_FILENO)) == -1) {
516 * Set stdout equal to our pseudo FD
518 stdioFds[STDOUT_FILENO] = fakeFd;
521 stdioHandles[STDERR_FILENO] = GetStdHandle(STD_ERROR_HANDLE);
522 if(!SetHandleInformation(stdioHandles[STDERR_FILENO],
523 HANDLE_FLAG_INHERIT, FALSE)) {
527 if ((fakeFd = Win32NewDescriptor(FD_PIPE_SYNC,
528 (int)stdioHandles[STDERR_FILENO],
529 STDERR_FILENO)) == -1) {
533 * Set stderr equal to our pseudo FD
535 stdioFds[STDERR_FILENO] = fakeFd;
542 *--------------------------------------------------------------
546 * Shutdown the OS library.
552 * Memory freed, handles closed.
554 *--------------------------------------------------------------
556 void OS_LibShutdown()
559 if (hIoCompPort != INVALID_HANDLE_VALUE)
561 CloseHandle(hIoCompPort);
562 hIoCompPort = INVALID_HANDLE_VALUE;
565 if (hStdinCompPort != INVALID_HANDLE_VALUE)
567 CloseHandle(hStdinCompPort);
568 hStdinCompPort = INVALID_HANDLE_VALUE;
571 if (acceptMutex != INVALID_HANDLE_VALUE)
573 ReleaseMutex(acceptMutex);
576 DisconnectNamedPipe(hListen);
585 *--------------------------------------------------------------
587 * Win32FreeDescriptor --
589 * Free I/O descriptor entry in fdTable.
592 * Frees I/O descriptor entry in fdTable.
597 *--------------------------------------------------------------
599 static void Win32FreeDescriptor(int fd)
601 /* Catch it if fd is a bogus value */
602 ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX));
604 EnterCriticalSection(&fdTableCritical);
606 if (fdTable[fd].type != FD_UNUSED)
608 switch (fdTable[fd].type)
613 /* Free file path string */
614 ASSERT(fdTable[fd].path != NULL);
615 free(fdTable[fd].path);
616 fdTable[fd].path = NULL;
623 ASSERT(fdTable[fd].path == NULL);
625 fdTable[fd].type = FD_UNUSED;
626 fdTable[fd].path = NULL;
627 fdTable[fd].Errno = NO_ERROR;
628 fdTable[fd].offsetHighPtr = fdTable[fd].offsetLowPtr = NULL;
630 if (fdTable[fd].hMapMutex != NULL)
632 CloseHandle(fdTable[fd].hMapMutex);
633 fdTable[fd].hMapMutex = NULL;
637 LeaveCriticalSection(&fdTableCritical);
642 static short getPort(const char * bindPath)
645 char * p = strchr(bindPath, ':');
661 * OS_CreateLocalIpcFd --
663 * This procedure is responsible for creating the listener pipe
664 * on Windows NT for local process communication. It will create a
665 * named pipe and return a file descriptor to it to the caller.
668 * Listener pipe created. This call returns either a valid
669 * pseudo file descriptor or -1 on error.
672 * Listener pipe and IPC address are stored in the FCGI info
674 * 'errno' will set on errors (-1 is returned).
676 *----------------------------------------------------------------------
678 int OS_CreateLocalIpcFd(const char *bindPath, int backlog)
681 short port = getPort(bindPath);
682 HANDLE mutex = CreateMutex(NULL, FALSE, NULL);
683 char * mutexEnvString;
690 if (! SetHandleInformation(mutex, HANDLE_FLAG_INHERIT, TRUE))
695 // This is a nail for listening to more than one port..
696 // This should really be handled by the caller.
698 mutexEnvString = malloc(strlen(MUTEX_VARNAME) + 7);
699 sprintf(mutexEnvString, MUTEX_VARNAME "=%d", (int) mutex);
700 putenv(mutexEnvString);
702 // There's nothing to be gained (at the moment) by a shutdown Event
704 if (port && *bindPath != ':' && strncmp(bindPath, LOCALHOST, strlen(LOCALHOST)))
706 fprintf(stderr, "To start a service on a TCP port can not "
707 "specify a host name.\n"
708 "You should either use \"localhost:<port>\" or "
709 " just use \":<port>.\"\n");
713 listenType = (port) ? FD_SOCKET_SYNC : FD_PIPE_ASYNC;
718 struct sockaddr_in sockAddr;
719 int sockLen = sizeof(sockAddr);
721 memset(&sockAddr, 0, sizeof(sockAddr));
722 sockAddr.sin_family = AF_INET;
723 sockAddr.sin_addr.s_addr = htonl(INADDR_ANY);
724 sockAddr.sin_port = htons(port);
726 listenSock = socket(AF_INET, SOCK_STREAM, 0);
727 if (listenSock == INVALID_SOCKET)
732 if (! bind(listenSock, (struct sockaddr *) &sockAddr, sockLen)
733 || ! listen(listenSock, backlog))
738 pseudoFd = Win32NewDescriptor(listenType, listenSock, -1);
742 closesocket(listenSock);
746 hListen = (HANDLE) listenSock;
750 HANDLE hListenPipe = INVALID_HANDLE_VALUE;
751 char *pipePath = malloc(strlen(bindPathPrefix) + strlen(bindPath) + 1);
758 strcpy(pipePath, bindPathPrefix);
759 strcat(pipePath, bindPath);
761 hListenPipe = CreateNamedPipe(pipePath,
762 PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
763 PIPE_TYPE_BYTE | PIPE_WAIT | PIPE_READMODE_BYTE,
764 PIPE_UNLIMITED_INSTANCES,
765 4096, 4096, 0, NULL);
769 if (hListenPipe == INVALID_HANDLE_VALUE)
774 if (! SetHandleInformation(hListenPipe, HANDLE_FLAG_INHERIT, TRUE))
779 pseudoFd = Win32NewDescriptor(listenType, (int) hListenPipe, -1);
783 CloseHandle(hListenPipe);
787 hListen = (HANDLE) hListenPipe;
794 *----------------------------------------------------------------------
798 * Create the pipe pathname connect to the remote application if
802 * -1 if fail or a valid handle if connection succeeds.
805 * Remote connection established.
807 *----------------------------------------------------------------------
809 int OS_FcgiConnect(char *bindPath)
811 short port = getPort(bindPath);
818 struct sockaddr_in sockAddr;
819 int sockLen = sizeof(sockAddr);
822 if (*bindPath != ':')
824 char * p = strchr(bindPath, ':');
825 int len = p - bindPath + 1;
828 strncpy(host, bindPath, len);
832 hp = gethostbyname(host ? host : LOCALHOST);
841 fprintf(stderr, "Unknown host: %s\n", bindPath);
845 memset(&sockAddr, 0, sizeof(sockAddr));
846 sockAddr.sin_family = AF_INET;
847 memcpy(&sockAddr.sin_addr, hp->h_addr, hp->h_length);
848 sockAddr.sin_port = htons(port);
850 sock = socket(AF_INET, SOCK_STREAM, 0);
851 if (sock == INVALID_SOCKET)
856 if (! connect(sock, (struct sockaddr *) &sockAddr, sockLen))
862 pseudoFd = Win32NewDescriptor(FD_SOCKET_SYNC, sock, -1);
871 char *pipePath = malloc(strlen(bindPathPrefix) + strlen(bindPath) + 1);
879 strcpy(pipePath, bindPathPrefix);
880 strcat(pipePath, bindPath);
882 hPipe = CreateFile(pipePath,
883 GENERIC_WRITE | GENERIC_READ,
884 FILE_SHARE_READ | FILE_SHARE_WRITE,
887 FILE_FLAG_OVERLAPPED,
892 if( hPipe == INVALID_HANDLE_VALUE)
897 pseudoFd = Win32NewDescriptor(FD_PIPE_ASYNC, (int) hPipe, -1);
906 * Set stdin equal to our pseudo FD and create the I/O completion
907 * port to be used for async I/O.
909 if (! CreateIoCompletionPort(hPipe, hIoCompPort, pseudoFd, 1))
911 Win32FreeDescriptor(pseudoFd);
921 *--------------------------------------------------------------
925 * Pass through to the appropriate NT read function.
928 * Returns number of byes read. Mimics unix read:.
929 * n bytes read, 0 or -1 failure: errno contains actual error
934 *--------------------------------------------------------------
936 int OS_Read(int fd, char * buf, size_t len)
941 ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX));
943 switch (fdTable[fd].type)
950 if (ReadFile(fdTable[fd].fid.fileHandle, buf, len, &bytesRead, NULL))
956 fdTable[fd].Errno = GetLastError();
962 case FD_SOCKET_ASYNC:
964 ret = recv(fdTable[fd].fid.sock, buf, len, 0);
965 if (ret == SOCKET_ERROR)
967 fdTable[fd].Errno = WSAGetLastError();
982 *--------------------------------------------------------------
986 * Perform a synchronous OS write.
989 * Returns number of bytes written. Mimics unix write:
990 * n bytes written, 0 or -1 failure (??? couldn't find man page).
995 *--------------------------------------------------------------
997 int OS_Write(int fd, char * buf, size_t len)
1002 ASSERT(fd >= 0 && fd < WIN32_OPEN_MAX);
1004 switch (fdTable[fd].type)
1011 if (WriteFile(fdTable[fd].fid.fileHandle, buf, len, &bytesWritten, NULL))
1017 fdTable[fd].Errno = GetLastError();
1022 case FD_SOCKET_SYNC:
1023 case FD_SOCKET_ASYNC:
1025 ret = send(fdTable[fd].fid.sock, buf, len, 0);
1026 if (ret == SOCKET_ERROR)
1028 fdTable[fd].Errno = WSAGetLastError();
1043 *----------------------------------------------------------------------
1047 * Spawns a new server listener process, and stores the information
1048 * relating to the child in the supplied record. A wait handler is
1049 * registered on the child's completion. This involves creating
1050 * a process on NT and preparing a command line with the required
1051 * state (currently a -childproc flag and the server socket to use
1052 * for accepting connections).
1055 * 0 if success, -1 if error.
1058 * Child process spawned.
1060 *----------------------------------------------------------------------
1062 int OS_SpawnChild(char *execPath, int listenFd)
1064 STARTUPINFO StartupInfo;
1065 PROCESS_INFORMATION pInfo;
1068 memset((void *)&StartupInfo, 0, sizeof(STARTUPINFO));
1069 StartupInfo.cb = sizeof (STARTUPINFO);
1070 StartupInfo.lpReserved = NULL;
1071 StartupInfo.lpReserved2 = NULL;
1072 StartupInfo.cbReserved2 = 0;
1073 StartupInfo.lpDesktop = NULL;
1076 * FastCGI on NT will set the listener pipe HANDLE in the stdin of
1077 * the new process. The fact that there is a stdin and NULL handles
1078 * for stdout and stderr tells the FastCGI process that this is a
1079 * FastCGI process and not a CGI process.
1081 StartupInfo.dwFlags = STARTF_USESTDHANDLES;
1083 * XXX: Do I have to dup the handle before spawning the process or is
1084 * it sufficient to use the handle as it's reference counted
1087 StartupInfo.hStdInput = fdTable[listenFd].fid.fileHandle;
1088 StartupInfo.hStdOutput = INVALID_HANDLE_VALUE;
1089 StartupInfo.hStdError = INVALID_HANDLE_VALUE;
1092 * Make the listener socket inheritable.
1094 success = SetHandleInformation(StartupInfo.hStdInput, HANDLE_FLAG_INHERIT,
1101 * XXX: Might want to apply some specific security attributes to the
1104 success = CreateProcess(execPath, /* LPCSTR address of module name */
1105 NULL, /* LPCSTR address of command line */
1106 NULL, /* Process security attributes */
1107 NULL, /* Thread security attributes */
1108 TRUE, /* Inheritable Handes inherited. */
1109 0, /* DWORD creation flags */
1110 NULL, /* Use parent environment block */
1111 NULL, /* Address of current directory name */
1112 &StartupInfo, /* Address of STARTUPINFO */
1113 &pInfo); /* Address of PROCESS_INFORMATION */
1122 *--------------------------------------------------------------
1124 * OS_AsyncReadStdin --
1126 * This initiates an asynchronous read on the standard
1127 * input handle. This handle is not guaranteed to be
1128 * capable of performing asynchronous I/O so we send a
1129 * message to the StdinThread to do the synchronous read.
1132 * -1 if error, 0 otherwise.
1135 * Asynchronous message is queued to the StdinThread and an
1136 * overlapped structure is allocated/initialized.
1138 *--------------------------------------------------------------
1140 int OS_AsyncReadStdin(void *buf, int len, OS_AsyncProc procPtr,
1141 ClientData clientData)
1143 POVERLAPPED_REQUEST pOv;
1145 ASSERT(fdTable[STDIN_FILENO].type != FD_UNUSED);
1147 pOv = (POVERLAPPED_REQUEST)malloc(sizeof(struct OVERLAPPED_REQUEST));
1149 memset((void *)pOv, 0, sizeof(struct OVERLAPPED_REQUEST));
1150 pOv->clientData1 = (ClientData)buf;
1151 pOv->instance = fdTable[STDIN_FILENO].instance;
1152 pOv->procPtr = procPtr;
1153 pOv->clientData = clientData;
1155 PostQueuedCompletionStatus(hStdinCompPort, len, STDIN_FILENO,
1161 *--------------------------------------------------------------
1165 * This initiates an asynchronous read on the file
1166 * handle which may be a socket or named pipe.
1168 * We also must save the ProcPtr and ClientData, so later
1169 * when the io completes, we know who to call.
1171 * We don't look at any results here (the ReadFile may
1172 * return data if it is cached) but do all completion
1173 * processing in OS_Select when we get the io completion
1174 * port done notifications. Then we call the callback.
1177 * -1 if error, 0 otherwise.
1180 * Asynchronous I/O operation is queued for completion.
1182 *--------------------------------------------------------------
1184 int OS_AsyncRead(int fd, int offset, void *buf, int len,
1185 OS_AsyncProc procPtr, ClientData clientData)
1188 POVERLAPPED_REQUEST pOv;
1191 * Catch any bogus fd values
1193 ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX));
1195 * Confirm that this is an async fd
1197 ASSERT(fdTable[fd].type != FD_UNUSED);
1198 ASSERT(fdTable[fd].type != FD_FILE_SYNC);
1199 ASSERT(fdTable[fd].type != FD_PIPE_SYNC);
1200 ASSERT(fdTable[fd].type != FD_SOCKET_SYNC);
1202 pOv = (POVERLAPPED_REQUEST)malloc(sizeof(struct OVERLAPPED_REQUEST));
1204 memset((void *)pOv, 0, sizeof(struct OVERLAPPED_REQUEST));
1206 * Only file offsets should be non-zero, but make sure.
1208 if (fdTable[fd].type == FD_FILE_ASYNC)
1209 if (fdTable[fd].offset >= 0)
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 * ReadFile returns: TRUE success, FALSE failure
1220 if (!ReadFile(fdTable[fd].fid.fileHandle, buf, len, &bytesRead,
1221 (LPOVERLAPPED)pOv)) {
1222 fdTable[fd].Errno = GetLastError();
1223 if(fdTable[fd].Errno == ERROR_NO_DATA ||
1224 fdTable[fd].Errno == ERROR_PIPE_NOT_CONNECTED) {
1225 PostQueuedCompletionStatus(hIoCompPort, 0, fd, (LPOVERLAPPED)pOv);
1228 if(fdTable[fd].Errno != ERROR_IO_PENDING) {
1229 PostQueuedCompletionStatus(hIoCompPort, 0, fd, (LPOVERLAPPED)pOv);
1232 fdTable[fd].Errno = 0;
1238 *--------------------------------------------------------------
1242 * This initiates an asynchronous write on the "fake" file
1243 * descriptor (which may be a file, socket, or named pipe).
1244 * We also must save the ProcPtr and ClientData, so later
1245 * when the io completes, we know who to call.
1247 * We don't look at any results here (the WriteFile generally
1248 * completes immediately) but do all completion processing
1249 * in OS_DoIo when we get the io completion port done
1250 * notifications. Then we call the callback.
1253 * -1 if error, 0 otherwise.
1256 * Asynchronous I/O operation is queued for completion.
1258 *--------------------------------------------------------------
1260 int OS_AsyncWrite(int fd, int offset, void *buf, int len,
1261 OS_AsyncProc procPtr, ClientData clientData)
1264 POVERLAPPED_REQUEST pOv;
1267 * Catch any bogus fd values
1269 ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX));
1271 * Confirm that this is an async fd
1273 ASSERT(fdTable[fd].type != FD_UNUSED);
1274 ASSERT(fdTable[fd].type != FD_FILE_SYNC);
1275 ASSERT(fdTable[fd].type != FD_PIPE_SYNC);
1276 ASSERT(fdTable[fd].type != FD_SOCKET_SYNC);
1278 pOv = (POVERLAPPED_REQUEST)malloc(sizeof(struct OVERLAPPED_REQUEST));
1280 memset((void *)pOv, 0, sizeof(struct OVERLAPPED_REQUEST));
1282 * Only file offsets should be non-zero, but make sure.
1284 if (fdTable[fd].type == FD_FILE_ASYNC)
1286 * Only file opened via OS_AsyncWrite with
1287 * O_APPEND will have an offset != -1.
1289 if (fdTable[fd].offset >= 0)
1291 * If the descriptor has a memory mapped file
1292 * handle, take the offsets from there.
1294 if (fdTable[fd].hMapMutex != NULL) {
1296 * Wait infinitely; this *should* not cause problems.
1298 WaitForSingleObject(fdTable[fd].hMapMutex, INFINITE);
1301 * Retrieve the shared offset values.
1303 pOv->overlapped.OffsetHigh = *(fdTable[fd].offsetHighPtr);
1304 pOv->overlapped.Offset = *(fdTable[fd].offsetLowPtr);
1307 * Update the shared offset values for the next write
1309 *(fdTable[fd].offsetHighPtr) += 0; /* XXX How do I handle overflow */
1310 *(fdTable[fd].offsetLowPtr) += len;
1312 ReleaseMutex(fdTable[fd].hMapMutex);
1314 pOv->overlapped.Offset = fdTable[fd].offset;
1316 pOv->overlapped.Offset = offset;
1317 pOv->instance = fdTable[fd].instance;
1318 pOv->procPtr = procPtr;
1319 pOv->clientData = clientData;
1322 * WriteFile returns: TRUE success, FALSE failure
1324 if (!WriteFile(fdTable[fd].fid.fileHandle, buf, len, &bytesWritten,
1325 (LPOVERLAPPED)pOv)) {
1326 fdTable[fd].Errno = GetLastError();
1327 if(fdTable[fd].Errno != ERROR_IO_PENDING) {
1328 PostQueuedCompletionStatus(hIoCompPort, 0, fd, (LPOVERLAPPED)pOv);
1331 fdTable[fd].Errno = 0;
1333 if (fdTable[fd].offset >= 0)
1334 fdTable[fd].offset += len;
1339 *--------------------------------------------------------------
1343 * Closes the descriptor with routine appropriate for
1344 * descriptor's type.
1347 * Socket or file is closed. Return values mimic Unix close:
1348 * 0 success, -1 failure
1351 * Entry in fdTable is marked as free.
1353 *--------------------------------------------------------------
1355 int OS_Close(int fd)
1360 * Catch it if fd is a bogus value
1362 ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX));
1363 ASSERT(fdTable[fd].type != FD_UNUSED);
1365 switch (fdTable[fd].type) {
1372 case FD_SOCKET_SYNC:
1373 case FD_SOCKET_ASYNC:
1375 * Closing a socket that has an async read outstanding causes a
1376 * tcp reset and possible data loss. The shutdown call seems to
1379 shutdown(fdTable[fd].fid.sock, 2);
1381 * closesocket returns: 0 success, SOCKET_ERROR failure
1383 if (closesocket(fdTable[fd].fid.sock) == SOCKET_ERROR)
1387 return -1; /* fake failure */
1390 Win32FreeDescriptor(fd);
1395 *--------------------------------------------------------------
1399 * Cancel outstanding asynchronous reads and prevent subsequent
1400 * reads from completing.
1403 * Socket or file is shutdown. Return values mimic Unix shutdown:
1404 * 0 success, -1 failure
1406 *--------------------------------------------------------------
1408 int OS_CloseRead(int fd)
1413 * Catch it if fd is a bogus value
1415 ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX));
1416 ASSERT(fdTable[fd].type == FD_SOCKET_ASYNC
1417 || fdTable[fd].type == FD_SOCKET_SYNC);
1419 if (shutdown(fdTable[fd].fid.sock,0) == SOCKET_ERROR)
1425 *--------------------------------------------------------------
1429 * This function was formerly OS_Select. It's purpose is
1430 * to pull I/O completion events off the queue and dispatch
1431 * them to the appropriate place.
1437 * Handlers are called.
1439 *--------------------------------------------------------------
1441 int OS_DoIo(struct timeval *tmo)
1445 POVERLAPPED_REQUEST pOv;
1452 * We can loop in here, but not too long, as wait handlers
1454 * For cgi stdin, apparently select returns when io completion
1455 * ports don't, so don't wait the full timeout.
1458 ms = (tmo->tv_sec*1000 + tmo->tv_usec/1000) / 2;
1462 ms_last = tb.time*1000 + tb.millitm;
1464 if(tmo && (ms = tmo->tv_sec*1000 + tmo->tv_usec/1000)> 100)
1466 if (!GetQueuedCompletionStatus(hIoCompPort, &bytes, &fd,
1467 (LPOVERLAPPED *)&pOv, ms) && !pOv) {
1468 err = WSAGetLastError();
1469 return 0; /* timeout */
1472 ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX));
1473 /* call callback if descriptor still valid */
1475 if(pOv->instance == fdTable[fd].instance)
1476 (*pOv->procPtr)(pOv->clientData, bytes);
1480 ms -= (tb.time*1000 + tb.millitm - ms_last);
1481 ms_last = tb.time*1000 + tb.millitm;
1487 static int CALLBACK isAddrOK(LPWSABUF lpCallerId,
1494 DWORD dwCallbackData)
1496 const char *okAddrs = (char *) dwCallbackData;
1497 struct sockaddr *sockaddr = (struct sockaddr *) lpCallerId->buf;
1499 if (okAddrs == NULL || sockaddr->sa_family != AF_INET)
1505 static const char *token = " ,;:\t";
1506 struct sockaddr_in * inet_sockaddr = (struct sockaddr_in *) sockaddr;
1507 char *ipaddr = inet_ntoa(inet_sockaddr->sin_addr);
1508 char *p = strstr(okAddrs, ipaddr);
1514 else if (p == okAddrs)
1516 p += strlen(ipaddr);
1517 return (strchr(token, *p) != NULL);
1519 else if (strchr(token, *--p))
1521 p += strlen(ipaddr) + 1;
1522 return (strchr(token, *p) != NULL);
1531 static printLastError(const char * text)
1536 FORMAT_MESSAGE_ALLOCATE_BUFFER |
1537 FORMAT_MESSAGE_FROM_SYSTEM |
1538 FORMAT_MESSAGE_IGNORE_INSERTS,
1547 fprintf(stderr, "%s: %s\n", text, (LPCTSTR) buf);
1551 static int acceptNamedPipe()
1555 if (! ConnectNamedPipe(hListen, &listenOverlapped))
1557 switch (GetLastError())
1559 case ERROR_PIPE_CONNECTED:
1561 // A client connected after CreateNamedPipe but
1562 // before ConnectNamedPipe. Its a good connection.
1566 case ERROR_IO_PENDING:
1568 // Wait for a connection to complete.
1570 while (WaitForSingleObject(listenOverlapped.hEvent,
1571 ACCEPT_TIMEOUT) == WAIT_TIMEOUT)
1573 if (shutdownPending)
1582 case ERROR_PIPE_LISTENING:
1584 // The pipe handle is in nonblocking mode.
1588 // The previous client closed its handle (and we failed
1589 // to call DisconnectNamedPipe)
1593 printLastError("unexpected ConnectNamedPipe() error");
1597 ipcFd = Win32NewDescriptor(FD_PIPE_SYNC, (int) hListen, -1);
1600 DisconnectNamedPipe(hListen);
1606 static int acceptSocket(const char *webServerAddrs)
1608 struct sockaddr sockaddr;
1609 int sockaddrLen = sizeof(sockaddr);
1611 const struct timeval timeout = {1, 0};
1616 FD_SET((unsigned int) hListen, &readfds);
1618 while (select(0, &readfds, NULL, NULL, &timeout) == 0)
1620 if (shutdownPending)
1627 hSock = (webServerAddrs == NULL)
1628 ? accept((SOCKET) hListen,
1631 : WSAAccept((unsigned int) hListen,
1635 (DWORD) webServerAddrs);
1638 if (hSock == INVALID_SOCKET)
1640 // Can I use FormatMessage()?
1641 fprintf(stderr, "accept()/WSAAccept() failed: %d", WSAGetLastError());
1645 ipcFd = Win32NewDescriptor(FD_SOCKET_SYNC, hSock, -1);
1655 *----------------------------------------------------------------------
1659 * Accepts a new FastCGI connection. This routine knows whether
1660 * we're dealing with TCP based sockets or NT Named Pipes for IPC.
1662 * fail_on_intr is ignored in the Win lib.
1665 * -1 if the operation fails, otherwise this is a valid IPC fd.
1667 *----------------------------------------------------------------------
1669 int OS_Accept(int listen_sock, int fail_on_intr, const char *webServerAddrs)
1673 // @todo Muliple listen sockets and sockets other than 0 are not
1674 // supported due to the use of globals.
1675 if (listen_sock != (int) hListen)
1677 fprintf(stderr, "illegal listen_sock value (%d) for OS_Accept()\n", listen_sock);
1681 if (shutdownPending)
1687 // The mutex is to keep other processes (and threads, when supported)
1688 // from going into the accept cycle. The accept cycle needs to
1689 // periodically break out to check the state of the shutdown flag
1690 // and there's no point to having more than one thread do that.
1692 if (acceptMutex != INVALID_HANDLE_VALUE)
1694 if (WaitForSingleObject(acceptMutex, INFINITE) == WAIT_FAILED)
1696 printLastError("WaitForSingleObject() failed");
1701 if (shutdownPending)
1705 else if (listenType == FD_PIPE_SYNC)
1707 ipcFd = acceptNamedPipe();
1709 else if (listenType == FD_SOCKET_SYNC)
1711 ipcFd = acceptSocket(webServerAddrs);
1715 fprintf(stderr, "unknown listenType (%d)\n", listenType);
1718 if (acceptMutex != INVALID_HANDLE_VALUE)
1720 ReleaseMutex(acceptMutex);
1727 *----------------------------------------------------------------------
1731 * OS IPC routine to close an IPC connection.
1737 * IPC connection is closed.
1739 *----------------------------------------------------------------------
1741 int OS_IpcClose(int ipcFd)
1747 * Catch it if fd is a bogus value
1749 ASSERT((ipcFd >= 0) && (ipcFd < WIN32_OPEN_MAX));
1750 ASSERT(fdTable[ipcFd].type != FD_UNUSED);
1752 switch(listenType) {
1756 * Make sure that the client (ie. a Web Server in this case) has
1757 * read all data from the pipe before we disconnect.
1759 if(!FlushFileBuffers(fdTable[ipcFd].fid.fileHandle))
1761 if(DisconnectNamedPipe(fdTable[ipcFd].fid.fileHandle)) {
1769 case FD_SOCKET_SYNC:
1782 *----------------------------------------------------------------------
1786 * Determines whether this process is a FastCGI process or not.
1789 * Returns 1 if FastCGI, 0 if not.
1794 *----------------------------------------------------------------------
1796 int OS_IsFcgi(int sock)
1798 // This is still broken. There is not currently a way to differentiate
1799 // a CGI from a FCGI pipe (try the Unix method).
1801 return (fdTable[sock].type != FD_UNUSED);
1805 *----------------------------------------------------------------------
1809 * Sets selected flag bits in an open file descriptor. Currently
1810 * this is only to put a SOCKET into non-blocking mode.
1812 *----------------------------------------------------------------------
1814 void OS_SetFlags(int fd, int flags)
1816 unsigned long pLong = 1L;
1819 if (fdTable[fd].type == FD_SOCKET_SYNC && flags == O_NONBLOCK) {
1820 if (ioctlsocket(fdTable[fd].fid.sock, FIONBIO, &pLong) ==
1822 exit(WSAGetLastError());
1824 if (!CreateIoCompletionPort((HANDLE)fdTable[fd].fid.sock,
1825 hIoCompPort, fd, 1)) {
1826 err = GetLastError();
1830 fdTable[fd].type = FD_SOCKET_ASYNC;