Try and find an open slot in the fdTable if the fd is >WIN32_OPEN_MAX. Suggested...
[catagits/fcgi2.git] / libfcgi / os_win32.c
CommitLineData
6ad90ad2 1/*
0198fd3c 2 * os_win32.c --
3 *
4 *
5 * Copyright (c) 1995 Open Market, Inc.
6 * All rights reserved.
7 *
8 * This file contains proprietary and confidential information and
6ad90ad2 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.
0198fd3c 12 *
13 * Bill Snapper
14 * snapper@openmarket.com
15 *
16 * (Special thanks to Karen and Bill. They made my job much easier and
17 * significantly more enjoyable.)
18 */
0198fd3c 19#ifndef lint
26c9b575 20static const char rcsid[] = "$Id: os_win32.c,v 1.14 2001/06/06 16:03:41 robs Exp $";
0198fd3c 21#endif /* not lint */
22
6ad90ad2 23#include "fcgi_config.h"
0198fd3c 24
6ad90ad2 25#define DLLAPI __declspec(dllexport)
0198fd3c 26
27#include <assert.h>
6ad90ad2 28#include <stdio.h>
0198fd3c 29#include <sys/timeb.h>
87abe107 30#include <Winsock2.h>
31#include <Windows.h>
0198fd3c 32
6ad90ad2 33#include "fcgios.h"
0198fd3c 34
35#define ASSERT assert
36
87abe107 37#define WIN32_OPEN_MAX 128 /* XXX: Small hack */
a6b4e4d4 38
6ad90ad2 39#define MUTEX_VARNAME "_FCGI_MUTEX_"
87abe107 40#define SHUTDOWN_EVENT_NAME "_FCGI_SHUTDOWN_EVENT_"
a6b4e4d4 41#define LOCALHOST "localhost"
6ad90ad2 42
0198fd3c 43static HANDLE hIoCompPort = INVALID_HANDLE_VALUE;
44static HANDLE hStdinCompPort = INVALID_HANDLE_VALUE;
45static HANDLE hStdinThread = INVALID_HANDLE_VALUE;
46
47static HANDLE stdioHandles[3] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE,
48 INVALID_HANDLE_VALUE};
49
87abe107 50static HANDLE acceptMutex = INVALID_HANDLE_VALUE;
51
52static BOOLEAN shutdownPending = FALSE;
0198fd3c 53
6ad90ad2 54/*
0198fd3c 55 * An enumeration of the file types
56 * supported by the FD_TABLE structure.
57 *
58 * XXX: Not all currently supported. This allows for future
59 * functionality.
60 */
61typedef enum {
62 FD_UNUSED,
63 FD_FILE_SYNC,
64 FD_FILE_ASYNC,
65 FD_SOCKET_SYNC,
66 FD_SOCKET_ASYNC,
67 FD_PIPE_SYNC,
68 FD_PIPE_ASYNC
69} FILE_TYPE;
70
71typedef union {
72 HANDLE fileHandle;
73 SOCKET sock;
74 unsigned int value;
75} DESCRIPTOR;
76
6ad90ad2 77/*
0198fd3c 78 * Structure used to map file handle and socket handle
79 * values into values that can be used to create unix-like
80 * select bitmaps, read/write for both sockets/files.
81 */
82struct FD_TABLE {
83 DESCRIPTOR fid;
84 FILE_TYPE type;
85 char *path;
86 DWORD Errno;
87 unsigned long instance;
88 int status;
89 int offset; /* only valid for async file writes */
90 LPDWORD offsetHighPtr; /* pointers to offset high and low words */
91 LPDWORD offsetLowPtr; /* only valid for async file writes (logs) */
92 HANDLE hMapMutex; /* mutex handle for multi-proc offset update */
93 LPVOID ovList; /* List of associated OVERLAPPED_REQUESTs */
94};
95
364045ff 96/*
97 * XXX Note there is no dyanmic sizing of this table, so if the
98 * number of open file descriptors exceeds WIN32_OPEN_MAX the
99 * app will blow up.
100 */
0198fd3c 101static struct FD_TABLE fdTable[WIN32_OPEN_MAX];
102
73389ec2 103static CRITICAL_SECTION fdTableCritical;
104
0198fd3c 105struct OVERLAPPED_REQUEST {
106 OVERLAPPED overlapped;
107 unsigned long instance; /* file instance (won't match after a close) */
108 OS_AsyncProc procPtr; /* callback routine */
109 ClientData clientData; /* callback argument */
110 ClientData clientData1; /* additional clientData */
111};
112typedef struct OVERLAPPED_REQUEST *POVERLAPPED_REQUEST;
113
114static const char *bindPathPrefix = "\\\\.\\pipe\\FastCGI\\";
115
a6b4e4d4 116static enum FILE_TYPE listenType = FD_UNUSED;
87abe107 117
118// XXX This should be a DESCRIPTOR
0198fd3c 119static HANDLE hListen = INVALID_HANDLE_VALUE;
87abe107 120
121static OVERLAPPED listenOverlapped;
122static BOOLEAN libInitialized = FALSE;
0198fd3c 123
0198fd3c 124/*
125 *--------------------------------------------------------------
126 *
127 * Win32NewDescriptor --
128 *
129 * Set up for I/O descriptor masquerading.
130 *
131 * Results:
132 * Returns "fake id" which masquerades as a UNIX-style "small
133 * non-negative integer" file/socket descriptor.
134 * Win32_* routine below will "do the right thing" based on the
135 * descriptor's actual type. -1 indicates failure.
136 *
137 * Side effects:
138 * Entry in fdTable is reserved to represent the socket/file.
139 *
140 *--------------------------------------------------------------
141 */
142static int Win32NewDescriptor(FILE_TYPE type, int fd, int desiredFd)
143{
73389ec2 144 int index = -1;
145
146 EnterCriticalSection(&fdTableCritical);
0198fd3c 147
148 /*
73389ec2 149 * If desiredFd is set, try to get this entry (this is used for
150 * mapping stdio handles). Otherwise try to get the fd entry.
151 * If this is not available, find a the first empty slot. .
0198fd3c 152 */
87abe107 153 if (desiredFd >= 0 && desiredFd < WIN32_OPEN_MAX)
154 {
73389ec2 155 if (fdTable[desiredFd].type == FD_UNUSED)
87abe107 156 {
73389ec2 157 index = desiredFd;
87abe107 158 }
0198fd3c 159 }
26c9b575 160 else if (fd > 0)
87abe107 161 {
26c9b575 162 if (fd < WIN32_OPEN_MAX && fdTable[fd].type == FD_UNUSED)
87abe107 163 {
26c9b575 164 index = fd;
165 }
166 else
167 {
168 int i;
73389ec2 169
26c9b575 170 for (i = 1; i < WIN32_OPEN_MAX; ++i)
171 {
172 if (fdTable[i].type == FD_UNUSED)
73389ec2 173 {
26c9b575 174 index = i;
175 break;
73389ec2 176 }
87abe107 177 }
178 }
0198fd3c 179 }
73389ec2 180
181 if (index != -1)
182 {
183 fdTable[index].fid.value = fd;
184 fdTable[index].type = type;
185 fdTable[index].path = NULL;
186 fdTable[index].Errno = NO_ERROR;
187 fdTable[index].status = 0;
188 fdTable[index].offset = -1;
189 fdTable[index].offsetHighPtr = fdTable[index].offsetLowPtr = NULL;
190 fdTable[index].hMapMutex = NULL;
191 fdTable[index].ovList = NULL;
192 }
0198fd3c 193
73389ec2 194 LeaveCriticalSection(&fdTableCritical);
0198fd3c 195 return index;
196}
62e100c7 197
0198fd3c 198/*
199 *--------------------------------------------------------------
200 *
201 * StdinThread--
202 *
203 * This thread performs I/O on stadard input. It is needed
204 * because you can't guarantee that all applications will
205 * create standard input with sufficient access to perform
206 * asynchronous I/O. Since we don't want to block the app
6ad90ad2 207 * reading from stdin we make it look like it's using I/O
0198fd3c 208 * completion ports to perform async I/O.
209 *
210 * Results:
211 * Data is read from stdin and posted to the io completion
212 * port.
213 *
214 * Side effects:
215 * None.
216 *
217 *--------------------------------------------------------------
218 */
219static void StdinThread(LPDWORD startup){
220
221 int doIo = TRUE;
222 int fd;
223 int bytesRead;
224 POVERLAPPED_REQUEST pOv;
6ad90ad2 225
0198fd3c 226 while(doIo) {
227 /*
228 * Block until a request to read from stdin comes in or a
229 * request to terminate the thread arrives (fd = -1).
230 */
231 if (!GetQueuedCompletionStatus(hStdinCompPort, &bytesRead, &fd,
232 (LPOVERLAPPED *)&pOv, (DWORD)-1) && !pOv) {
233 doIo = 0;
234 break;
235 }
6ad90ad2 236
0198fd3c 237 ASSERT((fd == STDIN_FILENO) || (fd == -1));
238 if(fd == -1) {
239 doIo = 0;
240 break;
241 }
242 ASSERT(pOv->clientData1 != NULL);
243
244 if(ReadFile(stdioHandles[STDIN_FILENO], pOv->clientData1, bytesRead,
245 &bytesRead, NULL)) {
6ad90ad2 246 PostQueuedCompletionStatus(hIoCompPort, bytesRead,
0198fd3c 247 STDIN_FILENO, (LPOVERLAPPED)pOv);
248 } else {
249 doIo = 0;
250 break;
251 }
252 }
253
254 ExitThread(0);
255}
256
87abe107 257static DWORD WINAPI ShutdownRequestThread(LPVOID arg)
258{
259 HANDLE shutdownEvent = (HANDLE) arg;
260
261 if (WaitForSingleObject(shutdownEvent, INFINITE) == WAIT_FAILED)
262 {
263 // Assuming it will happen again, all we can do is exit the thread
264 return -1;
265 }
266 else
267 {
268 // "Simple reads and writes to properly-aligned 32-bit variables are atomic"
269 shutdownPending = TRUE;
270
271 // Before an accept() is entered the shutdownPending flag is checked.
272 // If set, OS_Accept() will return -1. If not, it waits
273 // on a connection request for one second, checks the flag, & repeats.
274 // Only one process/thread is allowed to do this at time by
275 // wrapping the accept() with mutex.
276 return 0;
277 }
278}
279
0198fd3c 280/*
281 *--------------------------------------------------------------
282 *
283 * OS_LibInit --
284 *
285 * Set up the OS library for use.
286 *
287 * Results:
288 * Returns 0 if success, -1 if not.
289 *
290 * Side effects:
291 * Sockets initialized, pseudo file descriptors setup, etc.
292 *
293 *--------------------------------------------------------------
294 */
295int OS_LibInit(int stdioFds[3])
296{
297 WORD wVersion;
298 WSADATA wsaData;
299 int err;
300 int fakeFd;
0198fd3c 301 DWORD threadId;
302 char *cLenPtr = NULL;
87abe107 303 char *val = NULL;
304
0198fd3c 305 if(libInitialized)
306 return 0;
307
73389ec2 308 InitializeCriticalSection(&fdTableCritical);
309
0198fd3c 310 /*
311 * Initialize windows sockets library.
312 */
87abe107 313 wVersion = MAKEWORD(2,0);
0198fd3c 314 err = WSAStartup( wVersion, &wsaData );
315 if (err) {
316 fprintf(stderr, "Error starting Windows Sockets. Error: %d",
317 WSAGetLastError());
318 exit(111);
319 }
320
321 /*
322 * Create the I/O completion port to be used for our I/O queue.
323 */
324 if (hIoCompPort == INVALID_HANDLE_VALUE) {
325 hIoCompPort = CreateIoCompletionPort (INVALID_HANDLE_VALUE, NULL,
326 0, 1);
327 if(hIoCompPort == INVALID_HANDLE_VALUE) {
328 printf("<H2>OS_LibInit Failed CreateIoCompletionPort! ERROR: %d</H2>\r\n\r\n",
329 GetLastError());
330 return -1;
331 }
332 }
333
334 /*
87abe107 335 * If a shutdown event is in the env, save it (I don't see any to
336 * remove it from the environment out from under the application).
337 * Spawn a thread to wait on the shutdown request.
338 */
339 val = getenv(SHUTDOWN_EVENT_NAME);
340 if (val != NULL)
341 {
342 HANDLE shutdownEvent = (HANDLE) atoi(val);
343
344 putenv(SHUTDOWN_EVENT_NAME"=");
345
346 if (! CreateThread(NULL, 0, ShutdownRequestThread,
347 shutdownEvent, 0, NULL))
348 {
349 return -1;
350 }
351 }
352
353 /*
354 * If an accept mutex is in the env, save it and remove it.
355 */
356 val = getenv(MUTEX_VARNAME);
357 if (val != NULL)
358 {
359 acceptMutex = (HANDLE) atoi(val);
360 }
361
362
363 /*
0198fd3c 364 * Determine if this library is being used to listen for FastCGI
365 * connections. This is communicated by STDIN containing a
366 * valid handle to a listener object. In this case, both the
367 * "stdout" and "stderr" handles will be INVALID (ie. closed) by
368 * the starting process.
369 *
370 * The trick is determining if this is a pipe or a socket...
371 *
372 * XXX: Add the async accept test to determine socket or handle to a
373 * pipe!!!
374 */
375 if((GetStdHandle(STD_OUTPUT_HANDLE) == INVALID_HANDLE_VALUE) &&
376 (GetStdHandle(STD_ERROR_HANDLE) == INVALID_HANDLE_VALUE) &&
87abe107 377 (GetStdHandle(STD_INPUT_HANDLE) != INVALID_HANDLE_VALUE) )
378 {
379 DWORD pipeMode = PIPE_READMODE_BYTE | PIPE_WAIT;
380 HANDLE oldStdIn = GetStdHandle(STD_INPUT_HANDLE);
381
382 // Move the handle to a "low" number
383 if (! DuplicateHandle(GetCurrentProcess(), oldStdIn,
384 GetCurrentProcess(), &hListen,
385 0, TRUE, DUPLICATE_SAME_ACCESS))
386 {
387 return -1;
388 }
0198fd3c 389
87abe107 390 if (! SetStdHandle(STD_INPUT_HANDLE, hListen))
391 {
392 return -1;
393 }
394
395 CloseHandle(oldStdIn);
0198fd3c 396
397 /*
398 * Set the pipe handle state so that it operates in wait mode.
399 *
400 * NOTE: The listenFd is not mapped to a pseudo file descriptor
401 * as all work done on it is contained to the OS library.
402 *
403 * XXX: Initial assumption is that SetNamedPipeHandleState will
404 * fail if this is an IP socket...
405 */
87abe107 406 if (SetNamedPipeHandleState(hListen, &pipeMode, NULL, NULL))
407 {
0198fd3c 408 listenType = FD_PIPE_SYNC;
87abe107 409 listenOverlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
410 }
411 else
412 {
0198fd3c 413 listenType = FD_SOCKET_SYNC;
414 }
415 }
416
417 /*
418 * If there are no stdioFds passed in, we're done.
419 */
420 if(stdioFds == NULL) {
421 libInitialized = 1;
422 return 0;
423 }
6ad90ad2 424
0198fd3c 425 /*
426 * Setup standard input asynchronous I/O. There is actually a separate
427 * thread spawned for this purpose. The reason for this is that some
428 * web servers use anonymous pipes for the connection between itself
429 * and a CGI application. Anonymous pipes can't perform asynchronous
430 * I/O or use I/O completion ports. Therefore in order to present a
431 * consistent I/O dispatch model to an application we emulate I/O
432 * completion port behavior by having the standard input thread posting
433 * messages to the hIoCompPort which look like a complete overlapped
434 * I/O structure. This keeps the event dispatching simple from the
435 * application perspective.
436 */
437 stdioHandles[STDIN_FILENO] = GetStdHandle(STD_INPUT_HANDLE);
438
439 if(!SetHandleInformation(stdioHandles[STDIN_FILENO],
440 HANDLE_FLAG_INHERIT, 0)) {
441/*
442 * XXX: Causes error when run from command line. Check KB
443 err = GetLastError();
444 DebugBreak();
445 exit(99);
446 */
447 }
448
449 if ((fakeFd = Win32NewDescriptor(FD_PIPE_SYNC,
450 (int)stdioHandles[STDIN_FILENO],
451 STDIN_FILENO)) == -1) {
452 return -1;
453 } else {
454 /*
455 * Set stdin equal to our pseudo FD and create the I/O completion
456 * port to be used for async I/O.
457 */
458 stdioFds[STDIN_FILENO] = fakeFd;
459 }
460
461 /*
462 * Create the I/O completion port to be used for communicating with
463 * the thread doing I/O on standard in. This port will carry read
464 * and possibly thread termination requests to the StdinThread.
465 */
466 if (hStdinCompPort == INVALID_HANDLE_VALUE) {
467 hStdinCompPort = CreateIoCompletionPort (INVALID_HANDLE_VALUE, NULL,
468 0, 1);
469 if(hStdinCompPort == INVALID_HANDLE_VALUE) {
470 printf("<H2>OS_LibInit Failed CreateIoCompletionPort: STDIN! ERROR: %d</H2>\r\n\r\n",
471 GetLastError());
472 return -1;
473 }
474 }
475
6ad90ad2 476 /*
0198fd3c 477 * Create the thread that will read stdin if the CONTENT_LENGTH
478 * is non-zero.
479 */
480 if((cLenPtr = getenv("CONTENT_LENGTH")) != NULL &&
481 atoi(cLenPtr) > 0) {
482 hStdinThread = CreateThread(NULL, 8192,
483 (LPTHREAD_START_ROUTINE)&StdinThread,
484 NULL, 0, &threadId);
485 if (hStdinThread == NULL) {
486 printf("<H2>OS_LibInit Failed to create STDIN thread! ERROR: %d</H2>\r\n\r\n",
487 GetLastError());
488 return -1;
489 }
490 }
491
492 /*
493 * STDOUT will be used synchronously.
494 *
495 * XXX: May want to convert this so that it could be used for OVERLAPPED
496 * I/O later. If so, model it after the Stdin I/O as stdout is
497 * also incapable of async I/O on some servers.
498 */
499 stdioHandles[STDOUT_FILENO] = GetStdHandle(STD_OUTPUT_HANDLE);
500 if(!SetHandleInformation(stdioHandles[STDOUT_FILENO],
501 HANDLE_FLAG_INHERIT, FALSE)) {
502 DebugBreak();
503 exit(99);
504 }
505
506 if ((fakeFd = Win32NewDescriptor(FD_PIPE_SYNC,
507 (int)stdioHandles[STDOUT_FILENO],
508 STDOUT_FILENO)) == -1) {
509 return -1;
510 } else {
511 /*
512 * Set stdout equal to our pseudo FD
513 */
514 stdioFds[STDOUT_FILENO] = fakeFd;
515 }
516
517 stdioHandles[STDERR_FILENO] = GetStdHandle(STD_ERROR_HANDLE);
518 if(!SetHandleInformation(stdioHandles[STDERR_FILENO],
519 HANDLE_FLAG_INHERIT, FALSE)) {
520 DebugBreak();
521 exit(99);
522 }
523 if ((fakeFd = Win32NewDescriptor(FD_PIPE_SYNC,
524 (int)stdioHandles[STDERR_FILENO],
525 STDERR_FILENO)) == -1) {
526 return -1;
527 } else {
528 /*
529 * Set stderr equal to our pseudo FD
530 */
531 stdioFds[STDERR_FILENO] = fakeFd;
532 }
533
534 return 0;
535}
536
0198fd3c 537/*
538 *--------------------------------------------------------------
539 *
540 * OS_LibShutdown --
541 *
542 * Shutdown the OS library.
543 *
544 * Results:
545 * None.
546 *
547 * Side effects:
548 * Memory freed, handles closed.
549 *
550 *--------------------------------------------------------------
551 */
552void OS_LibShutdown()
553{
554
555 if(hIoCompPort != INVALID_HANDLE_VALUE) {
556 CloseHandle(hIoCompPort);
557 hIoCompPort = INVALID_HANDLE_VALUE;
558 }
559
560 if(hStdinCompPort != INVALID_HANDLE_VALUE) {
561 CloseHandle(hStdinCompPort);
562 hStdinCompPort = INVALID_HANDLE_VALUE;
563 }
564
565 /*
566 * Shutdown the socket library.
567 */
568 WSACleanup();
569 return;
570}
571
0198fd3c 572/*
573 *--------------------------------------------------------------
574 *
575 * Win32FreeDescriptor --
576 *
577 * Free I/O descriptor entry in fdTable.
578 *
579 * Results:
580 * Frees I/O descriptor entry in fdTable.
581 *
582 * Side effects:
583 * None.
584 *
585 *--------------------------------------------------------------
586 */
587static void Win32FreeDescriptor(int fd)
588{
589 /* Catch it if fd is a bogus value */
590 ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX));
0198fd3c 591
73389ec2 592 EnterCriticalSection(&fdTableCritical);
593
594 if (fdTable[fd].type != FD_UNUSED)
595 {
596 switch (fdTable[fd].type)
597 {
598 case FD_FILE_SYNC:
599 case FD_FILE_ASYNC:
600
601 /* Free file path string */
602 ASSERT(fdTable[fd].path != NULL);
603 free(fdTable[fd].path);
604 fdTable[fd].path = NULL;
605 break;
606
607 default:
608 break;
609 }
610
611 ASSERT(fdTable[fd].path == NULL);
612
613 fdTable[fd].type = FD_UNUSED;
614 fdTable[fd].path = NULL;
615 fdTable[fd].Errno = NO_ERROR;
616 fdTable[fd].offsetHighPtr = fdTable[fd].offsetLowPtr = NULL;
617
618 if (fdTable[fd].hMapMutex != NULL)
619 {
620 CloseHandle(fdTable[fd].hMapMutex);
621 fdTable[fd].hMapMutex = NULL;
622 }
0198fd3c 623 }
73389ec2 624
625 LeaveCriticalSection(&fdTableCritical);
626
0198fd3c 627 return;
628}
629
a6b4e4d4 630static short getPort(const char * bindPath)
631{
632 short port = 0;
633 char * p = strchr(bindPath, ':');
634
635 if (p && *++p)
636 {
637 char buf[6];
638
639 strncpy(buf, p, 6);
640 buf[5] = '\0';
641
642 port = atoi(buf);
643 }
644
645 return port;
646}
647
0198fd3c 648/*
649 * OS_CreateLocalIpcFd --
650 *
651 * This procedure is responsible for creating the listener pipe
652 * on Windows NT for local process communication. It will create a
653 * named pipe and return a file descriptor to it to the caller.
654 *
655 * Results:
656 * Listener pipe created. This call returns either a valid
657 * pseudo file descriptor or -1 on error.
658 *
659 * Side effects:
660 * Listener pipe and IPC address are stored in the FCGI info
661 * structure.
662 * 'errno' will set on errors (-1 is returned).
663 *
664 *----------------------------------------------------------------------
665 */
0b7c9662 666int OS_CreateLocalIpcFd(const char *bindPath, int backlog)
0198fd3c 667{
a6b4e4d4 668 int pseudoFd = -1;
669 short port = getPort(bindPath);
87abe107 670 HANDLE mutex = CreateMutex(NULL, FALSE, NULL);
a6b4e4d4 671 char * mutexEnvString;
6ad90ad2 672
87abe107 673 if (mutex == NULL)
674 {
675 return -1;
676 }
677
678 if (! SetHandleInformation(mutex, HANDLE_FLAG_INHERIT, TRUE))
679 {
680 return -1;
681 }
682
683 // This is a nail for listening to more than one port..
684 // This should really be handled by the caller.
87abe107 685
a6b4e4d4 686 mutexEnvString = malloc(strlen(MUTEX_VARNAME) + 7);
687 sprintf(mutexEnvString, MUTEX_VARNAME "=%d", (int) mutex);
688 putenv(mutexEnvString);
87abe107 689
a6b4e4d4 690 // There's nothing to be gained (at the moment) by a shutdown Event
87abe107 691
a6b4e4d4 692 if (port && *bindPath != ':' && strncmp(bindPath, LOCALHOST, strlen(LOCALHOST)))
693 {
694 fprintf(stderr, "To start a service on a TCP port can not "
695 "specify a host name.\n"
696 "You should either use \"localhost:<port>\" or "
697 " just use \":<port>.\"\n");
698 exit(1);
0198fd3c 699 }
a6b4e4d4 700
701 listenType = (port) ? FD_SOCKET_SYNC : FD_PIPE_ASYNC;
87abe107 702
a6b4e4d4 703 if (port)
704 {
705 SOCKET listenSock;
706 struct sockaddr_in sockAddr;
707 int sockLen = sizeof(sockAddr);
708
709 memset(&sockAddr, 0, sizeof(sockAddr));
710 sockAddr.sin_family = AF_INET;
711 sockAddr.sin_addr.s_addr = htonl(INADDR_ANY);
712 sockAddr.sin_port = htons(port);
0198fd3c 713
a6b4e4d4 714 listenSock = socket(AF_INET, SOCK_STREAM, 0);
715 if (listenSock == INVALID_SOCKET)
716 {
717 return -1;
718 }
0198fd3c 719
a6b4e4d4 720 if (! bind(listenSock, (struct sockaddr *) &sockAddr, sockLen)
721 || ! listen(listenSock, backlog))
722 {
723 return -1;
724 }
725
726 pseudoFd = Win32NewDescriptor(listenType, listenSock, -1);
727
728 if (pseudoFd == -1)
729 {
730 closesocket(listenSock);
731 return -1;
732 }
733
734 hListen = (HANDLE) listenSock;
0198fd3c 735 }
a6b4e4d4 736 else
737 {
738 HANDLE hListenPipe = INVALID_HANDLE_VALUE;
739 char *pipePath = malloc(strlen(bindPathPrefix) + strlen(bindPath) + 1);
740
741 if (! pipePath)
742 {
743 return -1;
744 }
0198fd3c 745
a6b4e4d4 746 strcpy(pipePath, bindPathPrefix);
747 strcat(pipePath, bindPath);
6ad90ad2 748
a6b4e4d4 749 hListenPipe = CreateNamedPipe(pipePath,
750 PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
751 PIPE_TYPE_BYTE | PIPE_WAIT | PIPE_READMODE_BYTE,
752 PIPE_UNLIMITED_INSTANCES,
753 4096, 4096, 0, NULL);
754
755 free(pipePath);
6ad90ad2 756
a6b4e4d4 757 if (hListenPipe == INVALID_HANDLE_VALUE)
758 {
759 return -1;
760 }
761
762 if (! SetHandleInformation(hListenPipe, HANDLE_FLAG_INHERIT, TRUE))
763 {
764 return -1;
765 }
766
767 pseudoFd = Win32NewDescriptor(listenType, (int) hListenPipe, -1);
768
769 if (pseudoFd == -1)
770 {
771 CloseHandle(hListenPipe);
772 return -1;
773 }
774
775 hListen = (HANDLE) hListenPipe;
0198fd3c 776 }
777
a6b4e4d4 778 return pseudoFd;
0198fd3c 779}
780
0198fd3c 781/*
782 *----------------------------------------------------------------------
783 *
784 * OS_FcgiConnect --
785 *
786 * Create the pipe pathname connect to the remote application if
787 * possible.
788 *
789 * Results:
790 * -1 if fail or a valid handle if connection succeeds.
791 *
792 * Side effects:
793 * Remote connection established.
794 *
795 *----------------------------------------------------------------------
796 */
797int OS_FcgiConnect(char *bindPath)
798{
a6b4e4d4 799 short port = getPort(bindPath);
800 int pseudoFd = -1;
87abe107 801
a6b4e4d4 802 if (port)
803 {
804 struct hostent *hp;
805 char *host = NULL;
806 struct sockaddr_in sockAddr;
807 int sockLen = sizeof(sockAddr);
808 SOCKET sock;
809
810 if (*bindPath != ':')
811 {
812 char * p = strchr(bindPath, ':');
813 int len = p - bindPath + 1;
6ad90ad2 814
a6b4e4d4 815 host = malloc(len);
816 strncpy(host, bindPath, len);
817 host[len] = '\0';
818 }
819
820 hp = gethostbyname(host ? host : LOCALHOST);
821
822 if (host)
823 {
824 free(host);
825 }
826
827 if (hp == NULL)
828 {
829 fprintf(stderr, "Unknown host: %s\n", bindPath);
830 return -1;
831 }
832
833 memset(&sockAddr, 0, sizeof(sockAddr));
834 sockAddr.sin_family = AF_INET;
835 memcpy(&sockAddr.sin_addr, hp->h_addr, hp->h_length);
836 sockAddr.sin_port = htons(port);
837
838 sock = socket(AF_INET, SOCK_STREAM, 0);
839 if (sock == INVALID_SOCKET)
840 {
841 return -1;
842 }
843
844 if (! connect(sock, (struct sockaddr *) &sockAddr, sockLen))
845 {
846 closesocket(sock);
847 return -1;
848 }
849
850 pseudoFd = Win32NewDescriptor(FD_SOCKET_SYNC, sock, -1);
851 if (pseudoFd == -1)
852 {
853 closesocket(sock);
854 return -1;
855 }
0198fd3c 856 }
a6b4e4d4 857 else
858 {
859 char *pipePath = malloc(strlen(bindPathPrefix) + strlen(bindPath) + 1);
860 HANDLE hPipe;
861
862 if (! pipePath)
863 {
864 return -1;
865 }
0198fd3c 866
a6b4e4d4 867 strcpy(pipePath, bindPathPrefix);
868 strcat(pipePath, bindPath);
869
870 hPipe = CreateFile(pipePath,
871 GENERIC_WRITE | GENERIC_READ,
872 FILE_SHARE_READ | FILE_SHARE_WRITE,
873 NULL,
874 OPEN_EXISTING,
875 FILE_FLAG_OVERLAPPED,
876 NULL);
877
878 free(pipePath);
879
880 if( hPipe == INVALID_HANDLE_VALUE)
881 {
882 return -1;
883 }
884
885 pseudoFd = Win32NewDescriptor(FD_PIPE_ASYNC, (int) hPipe, -1);
886
887 if (pseudoFd == -1)
888 {
889 CloseHandle(hPipe);
890 return -1;
891 }
892
0198fd3c 893 /*
a6b4e4d4 894 * Set stdin equal to our pseudo FD and create the I/O completion
895 * port to be used for async I/O.
896 */
897 if (! CreateIoCompletionPort(hPipe, hIoCompPort, pseudoFd, 1))
898 {
899 Win32FreeDescriptor(pseudoFd);
900 CloseHandle(hPipe);
901 return -1;
902 }
0198fd3c 903 }
a6b4e4d4 904
905 return pseudoFd;
0198fd3c 906}
6ad90ad2 907
0198fd3c 908/*
909 *--------------------------------------------------------------
910 *
911 * OS_Read --
912 *
913 * Pass through to the appropriate NT read function.
914 *
915 * Results:
916 * Returns number of byes read. Mimics unix read:.
917 * n bytes read, 0 or -1 failure: errno contains actual error
918 *
919 * Side effects:
920 * None.
921 *
922 *--------------------------------------------------------------
923 */
924int OS_Read(int fd, char * buf, size_t len)
925{
926 DWORD bytesRead;
225369c3 927 int ret = -1;
0198fd3c 928
0198fd3c 929 ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX));
930
225369c3 931 switch (fdTable[fd].type)
932 {
0198fd3c 933 case FD_FILE_SYNC:
934 case FD_FILE_ASYNC:
935 case FD_PIPE_SYNC:
936 case FD_PIPE_ASYNC:
225369c3 937
938 if (ReadFile(fdTable[fd].fid.fileHandle, buf, len, &bytesRead, NULL))
939 {
940 ret = bytesRead;
941 }
942 else
943 {
944 fdTable[fd].Errno = GetLastError();
0198fd3c 945 }
225369c3 946
947 break;
0198fd3c 948
949 case FD_SOCKET_SYNC:
950 case FD_SOCKET_ASYNC:
225369c3 951
952 ret = recv(fdTable[fd].fid.sock, buf, len, 0);
953 if (ret == SOCKET_ERROR)
954 {
955 fdTable[fd].Errno = WSAGetLastError();
956 ret = -1;
0198fd3c 957 }
225369c3 958
959 break;
960
961 default:
962
963 ASSERT(0);
0198fd3c 964 }
225369c3 965
966 return ret;
0198fd3c 967}
62e100c7 968
0198fd3c 969/*
970 *--------------------------------------------------------------
971 *
972 * OS_Write --
973 *
974 * Perform a synchronous OS write.
975 *
976 * Results:
977 * Returns number of bytes written. Mimics unix write:
978 * n bytes written, 0 or -1 failure (??? couldn't find man page).
979 *
980 * Side effects:
981 * none.
982 *
983 *--------------------------------------------------------------
984 */
985int OS_Write(int fd, char * buf, size_t len)
986{
987 DWORD bytesWritten;
225369c3 988 int ret = -1;
0198fd3c 989
225369c3 990 ASSERT(fd >= 0 && fd < WIN32_OPEN_MAX);
0198fd3c 991
225369c3 992 switch (fdTable[fd].type)
993 {
0198fd3c 994 case FD_FILE_SYNC:
995 case FD_FILE_ASYNC:
996 case FD_PIPE_SYNC:
997 case FD_PIPE_ASYNC:
225369c3 998
999 if (WriteFile(fdTable[fd].fid.fileHandle, buf, len, &bytesWritten, NULL))
1000 {
1001 ret = bytesWritten;
1002 }
1003 else
1004 {
1005 fdTable[fd].Errno = GetLastError();
0198fd3c 1006 }
225369c3 1007
1008 break;
1009
0198fd3c 1010 case FD_SOCKET_SYNC:
1011 case FD_SOCKET_ASYNC:
225369c3 1012
1013 ret = send(fdTable[fd].fid.sock, buf, len, 0);
1014 if (ret == SOCKET_ERROR)
1015 {
1016 fdTable[fd].Errno = WSAGetLastError();
1017 ret = -1;
0198fd3c 1018 }
225369c3 1019
1020 break;
1021
1022 default:
1023
1024 ASSERT(0);
0198fd3c 1025 }
225369c3 1026
1027 return ret;
0198fd3c 1028}
1029
0198fd3c 1030/*
1031 *----------------------------------------------------------------------
1032 *
1033 * OS_SpawnChild --
1034 *
1035 * Spawns a new server listener process, and stores the information
1036 * relating to the child in the supplied record. A wait handler is
1037 * registered on the child's completion. This involves creating
1038 * a process on NT and preparing a command line with the required
1039 * state (currently a -childproc flag and the server socket to use
1040 * for accepting connections).
1041 *
1042 * Results:
1043 * 0 if success, -1 if error.
1044 *
1045 * Side effects:
1046 * Child process spawned.
1047 *
1048 *----------------------------------------------------------------------
1049 */
1050int OS_SpawnChild(char *execPath, int listenFd)
1051{
1052 STARTUPINFO StartupInfo;
1053 PROCESS_INFORMATION pInfo;
1054 BOOL success;
1055
1056 memset((void *)&StartupInfo, 0, sizeof(STARTUPINFO));
1057 StartupInfo.cb = sizeof (STARTUPINFO);
1058 StartupInfo.lpReserved = NULL;
1059 StartupInfo.lpReserved2 = NULL;
1060 StartupInfo.cbReserved2 = 0;
1061 StartupInfo.lpDesktop = NULL;
1062
1063 /*
1064 * FastCGI on NT will set the listener pipe HANDLE in the stdin of
1065 * the new process. The fact that there is a stdin and NULL handles
1066 * for stdout and stderr tells the FastCGI process that this is a
1067 * FastCGI process and not a CGI process.
1068 */
1069 StartupInfo.dwFlags = STARTF_USESTDHANDLES;
1070 /*
1071 * XXX: Do I have to dup the handle before spawning the process or is
1072 * it sufficient to use the handle as it's reference counted
1073 * by NT anyway?
1074 */
1075 StartupInfo.hStdInput = fdTable[listenFd].fid.fileHandle;
1076 StartupInfo.hStdOutput = INVALID_HANDLE_VALUE;
1077 StartupInfo.hStdError = INVALID_HANDLE_VALUE;
6ad90ad2 1078
0198fd3c 1079 /*
1080 * Make the listener socket inheritable.
1081 */
1082 success = SetHandleInformation(StartupInfo.hStdInput, HANDLE_FLAG_INHERIT,
1083 TRUE);
1084 if(!success) {
1085 exit(99);
1086 }
1087
1088 /*
1089 * XXX: Might want to apply some specific security attributes to the
1090 * processes.
1091 */
1092 success = CreateProcess(execPath, /* LPCSTR address of module name */
1093 NULL, /* LPCSTR address of command line */
6ad90ad2 1094 NULL, /* Process security attributes */
0198fd3c 1095 NULL, /* Thread security attributes */
1096 TRUE, /* Inheritable Handes inherited. */
1097 0, /* DWORD creation flags */
1098 NULL, /* Use parent environment block */
1099 NULL, /* Address of current directory name */
1100 &StartupInfo, /* Address of STARTUPINFO */
1101 &pInfo); /* Address of PROCESS_INFORMATION */
1102 if(success) {
1103 return 0;
1104 } else {
1105 return -1;
1106 }
1107}
1108
0198fd3c 1109/*
1110 *--------------------------------------------------------------
1111 *
1112 * OS_AsyncReadStdin --
1113 *
1114 * This initiates an asynchronous read on the standard
1115 * input handle. This handle is not guaranteed to be
6ad90ad2 1116 * capable of performing asynchronous I/O so we send a
0198fd3c 1117 * message to the StdinThread to do the synchronous read.
1118 *
1119 * Results:
1120 * -1 if error, 0 otherwise.
1121 *
1122 * Side effects:
1123 * Asynchronous message is queued to the StdinThread and an
1124 * overlapped structure is allocated/initialized.
1125 *
1126 *--------------------------------------------------------------
1127 */
6ad90ad2 1128int OS_AsyncReadStdin(void *buf, int len, OS_AsyncProc procPtr,
0198fd3c 1129 ClientData clientData)
1130{
1131 POVERLAPPED_REQUEST pOv;
1132
1133 ASSERT(fdTable[STDIN_FILENO].type != FD_UNUSED);
1134
1135 pOv = (POVERLAPPED_REQUEST)malloc(sizeof(struct OVERLAPPED_REQUEST));
1136 ASSERT(pOv);
1137 memset((void *)pOv, 0, sizeof(struct OVERLAPPED_REQUEST));
1138 pOv->clientData1 = (ClientData)buf;
1139 pOv->instance = fdTable[STDIN_FILENO].instance;
1140 pOv->procPtr = procPtr;
1141 pOv->clientData = clientData;
1142
1143 PostQueuedCompletionStatus(hStdinCompPort, len, STDIN_FILENO,
1144 (LPOVERLAPPED)pOv);
1145 return 0;
1146}
1147
0198fd3c 1148/*
1149 *--------------------------------------------------------------
1150 *
1151 * OS_AsyncRead --
1152 *
1153 * This initiates an asynchronous read on the file
1154 * handle which may be a socket or named pipe.
1155 *
1156 * We also must save the ProcPtr and ClientData, so later
1157 * when the io completes, we know who to call.
1158 *
1159 * We don't look at any results here (the ReadFile may
1160 * return data if it is cached) but do all completion
1161 * processing in OS_Select when we get the io completion
1162 * port done notifications. Then we call the callback.
1163 *
1164 * Results:
1165 * -1 if error, 0 otherwise.
1166 *
1167 * Side effects:
1168 * Asynchronous I/O operation is queued for completion.
1169 *
1170 *--------------------------------------------------------------
1171 */
1172int OS_AsyncRead(int fd, int offset, void *buf, int len,
1173 OS_AsyncProc procPtr, ClientData clientData)
1174{
1175 DWORD bytesRead;
1176 POVERLAPPED_REQUEST pOv;
1177
1178 /*
1179 * Catch any bogus fd values
1180 */
1181 ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX));
1182 /*
1183 * Confirm that this is an async fd
1184 */
1185 ASSERT(fdTable[fd].type != FD_UNUSED);
1186 ASSERT(fdTable[fd].type != FD_FILE_SYNC);
1187 ASSERT(fdTable[fd].type != FD_PIPE_SYNC);
1188 ASSERT(fdTable[fd].type != FD_SOCKET_SYNC);
1189
1190 pOv = (POVERLAPPED_REQUEST)malloc(sizeof(struct OVERLAPPED_REQUEST));
1191 ASSERT(pOv);
1192 memset((void *)pOv, 0, sizeof(struct OVERLAPPED_REQUEST));
1193 /*
1194 * Only file offsets should be non-zero, but make sure.
1195 */
1196 if (fdTable[fd].type == FD_FILE_ASYNC)
1197 if (fdTable[fd].offset >= 0)
1198 pOv->overlapped.Offset = fdTable[fd].offset;
1199 else
1200 pOv->overlapped.Offset = offset;
1201 pOv->instance = fdTable[fd].instance;
1202 pOv->procPtr = procPtr;
1203 pOv->clientData = clientData;
1204 bytesRead = fd;
1205 /*
1206 * ReadFile returns: TRUE success, FALSE failure
1207 */
1208 if (!ReadFile(fdTable[fd].fid.fileHandle, buf, len, &bytesRead,
1209 (LPOVERLAPPED)pOv)) {
1210 fdTable[fd].Errno = GetLastError();
1211 if(fdTable[fd].Errno == ERROR_NO_DATA ||
1212 fdTable[fd].Errno == ERROR_PIPE_NOT_CONNECTED) {
1213 PostQueuedCompletionStatus(hIoCompPort, 0, fd, (LPOVERLAPPED)pOv);
1214 return 0;
1215 }
1216 if(fdTable[fd].Errno != ERROR_IO_PENDING) {
1217 PostQueuedCompletionStatus(hIoCompPort, 0, fd, (LPOVERLAPPED)pOv);
1218 return -1;
1219 }
1220 fdTable[fd].Errno = 0;
1221 }
1222 return 0;
1223}
62e100c7 1224
0198fd3c 1225/*
1226 *--------------------------------------------------------------
1227 *
1228 * OS_AsyncWrite --
1229 *
1230 * This initiates an asynchronous write on the "fake" file
1231 * descriptor (which may be a file, socket, or named pipe).
1232 * We also must save the ProcPtr and ClientData, so later
1233 * when the io completes, we know who to call.
1234 *
1235 * We don't look at any results here (the WriteFile generally
1236 * completes immediately) but do all completion processing
1237 * in OS_DoIo when we get the io completion port done
1238 * notifications. Then we call the callback.
1239 *
1240 * Results:
1241 * -1 if error, 0 otherwise.
1242 *
1243 * Side effects:
1244 * Asynchronous I/O operation is queued for completion.
1245 *
1246 *--------------------------------------------------------------
1247 */
6ad90ad2 1248int OS_AsyncWrite(int fd, int offset, void *buf, int len,
0198fd3c 1249 OS_AsyncProc procPtr, ClientData clientData)
1250{
1251 DWORD bytesWritten;
1252 POVERLAPPED_REQUEST pOv;
1253
1254 /*
1255 * Catch any bogus fd values
1256 */
1257 ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX));
1258 /*
1259 * Confirm that this is an async fd
1260 */
1261 ASSERT(fdTable[fd].type != FD_UNUSED);
1262 ASSERT(fdTable[fd].type != FD_FILE_SYNC);
1263 ASSERT(fdTable[fd].type != FD_PIPE_SYNC);
1264 ASSERT(fdTable[fd].type != FD_SOCKET_SYNC);
1265
1266 pOv = (POVERLAPPED_REQUEST)malloc(sizeof(struct OVERLAPPED_REQUEST));
1267 ASSERT(pOv);
1268 memset((void *)pOv, 0, sizeof(struct OVERLAPPED_REQUEST));
1269 /*
1270 * Only file offsets should be non-zero, but make sure.
1271 */
1272 if (fdTable[fd].type == FD_FILE_ASYNC)
6ad90ad2 1273 /*
0198fd3c 1274 * Only file opened via OS_AsyncWrite with
1275 * O_APPEND will have an offset != -1.
1276 */
1277 if (fdTable[fd].offset >= 0)
6ad90ad2 1278 /*
0198fd3c 1279 * If the descriptor has a memory mapped file
1280 * handle, take the offsets from there.
1281 */
1282 if (fdTable[fd].hMapMutex != NULL) {
1283 /*
1284 * Wait infinitely; this *should* not cause problems.
6ad90ad2 1285 */
0198fd3c 1286 WaitForSingleObject(fdTable[fd].hMapMutex, INFINITE);
6ad90ad2 1287
0198fd3c 1288 /*
1289 * Retrieve the shared offset values.
1290 */
1291 pOv->overlapped.OffsetHigh = *(fdTable[fd].offsetHighPtr);
1292 pOv->overlapped.Offset = *(fdTable[fd].offsetLowPtr);
6ad90ad2 1293
0198fd3c 1294 /*
1295 * Update the shared offset values for the next write
1296 */
1297 *(fdTable[fd].offsetHighPtr) += 0; /* XXX How do I handle overflow */
1298 *(fdTable[fd].offsetLowPtr) += len;
6ad90ad2 1299
0198fd3c 1300 ReleaseMutex(fdTable[fd].hMapMutex);
6ad90ad2 1301 } else
0198fd3c 1302 pOv->overlapped.Offset = fdTable[fd].offset;
1303 else
1304 pOv->overlapped.Offset = offset;
1305 pOv->instance = fdTable[fd].instance;
1306 pOv->procPtr = procPtr;
1307 pOv->clientData = clientData;
1308 bytesWritten = fd;
1309 /*
1310 * WriteFile returns: TRUE success, FALSE failure
1311 */
1312 if (!WriteFile(fdTable[fd].fid.fileHandle, buf, len, &bytesWritten,
1313 (LPOVERLAPPED)pOv)) {
1314 fdTable[fd].Errno = GetLastError();
1315 if(fdTable[fd].Errno != ERROR_IO_PENDING) {
1316 PostQueuedCompletionStatus(hIoCompPort, 0, fd, (LPOVERLAPPED)pOv);
1317 return -1;
1318 }
1319 fdTable[fd].Errno = 0;
1320 }
1321 if (fdTable[fd].offset >= 0)
1322 fdTable[fd].offset += len;
1323 return 0;
1324}
1325
0198fd3c 1326/*
1327 *--------------------------------------------------------------
1328 *
1329 * OS_Close --
1330 *
1331 * Closes the descriptor with routine appropriate for
1332 * descriptor's type.
1333 *
1334 * Results:
1335 * Socket or file is closed. Return values mimic Unix close:
1336 * 0 success, -1 failure
1337 *
1338 * Side effects:
1339 * Entry in fdTable is marked as free.
1340 *
1341 *--------------------------------------------------------------
1342 */
1343int OS_Close(int fd)
1344{
1345 int ret = 0;
1346
1347 /*
1348 * Catch it if fd is a bogus value
1349 */
1350 ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX));
1351 ASSERT(fdTable[fd].type != FD_UNUSED);
1352
1353 switch (fdTable[fd].type) {
1354 case FD_PIPE_SYNC:
1355 case FD_PIPE_ASYNC:
1356 case FD_FILE_SYNC:
1357 case FD_FILE_ASYNC:
0198fd3c 1358 break;
87abe107 1359
1360 case FD_SOCKET_SYNC:
0198fd3c 1361 case FD_SOCKET_ASYNC:
1362 /*
1363 * Closing a socket that has an async read outstanding causes a
1364 * tcp reset and possible data loss. The shutdown call seems to
1365 * prevent this.
1366 */
1367 shutdown(fdTable[fd].fid.sock, 2);
1368 /*
1369 * closesocket returns: 0 success, SOCKET_ERROR failure
1370 */
1371 if (closesocket(fdTable[fd].fid.sock) == SOCKET_ERROR)
1372 ret = -1;
1373 break;
1374 default:
1375 return -1; /* fake failure */
1376 }
1377
1378 Win32FreeDescriptor(fd);
1379 return ret;
1380}
62e100c7 1381
0198fd3c 1382/*
1383 *--------------------------------------------------------------
1384 *
1385 * OS_CloseRead --
1386 *
1387 * Cancel outstanding asynchronous reads and prevent subsequent
1388 * reads from completing.
1389 *
1390 * Results:
1391 * Socket or file is shutdown. Return values mimic Unix shutdown:
1392 * 0 success, -1 failure
1393 *
1394 *--------------------------------------------------------------
1395 */
1396int OS_CloseRead(int fd)
1397{
1398 int ret = 0;
1399
1400 /*
1401 * Catch it if fd is a bogus value
1402 */
1403 ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX));
1404 ASSERT(fdTable[fd].type == FD_SOCKET_ASYNC
1405 || fdTable[fd].type == FD_SOCKET_SYNC);
1406
1407 if (shutdown(fdTable[fd].fid.sock,0) == SOCKET_ERROR)
1408 ret = -1;
1409 return ret;
1410}
62e100c7 1411
0198fd3c 1412/*
1413 *--------------------------------------------------------------
1414 *
1415 * OS_DoIo --
1416 *
1417 * This function was formerly OS_Select. It's purpose is
1418 * to pull I/O completion events off the queue and dispatch
1419 * them to the appropriate place.
1420 *
1421 * Results:
1422 * Returns 0.
1423 *
1424 * Side effects:
1425 * Handlers are called.
1426 *
1427 *--------------------------------------------------------------
1428 */
1429int OS_DoIo(struct timeval *tmo)
1430{
1431 int fd;
1432 int bytes;
1433 POVERLAPPED_REQUEST pOv;
1434 struct timeb tb;
1435 int ms;
1436 int ms_last;
1437 int err;
6ad90ad2 1438
0198fd3c 1439 /* XXX
1440 * We can loop in here, but not too long, as wait handlers
1441 * must run.
1442 * For cgi stdin, apparently select returns when io completion
1443 * ports don't, so don't wait the full timeout.
1444 */
1445 if(tmo)
1446 ms = (tmo->tv_sec*1000 + tmo->tv_usec/1000) / 2;
1447 else
1448 ms = 1000;
1449 ftime(&tb);
1450 ms_last = tb.time*1000 + tb.millitm;
1451 while (ms >= 0) {
1452 if(tmo && (ms = tmo->tv_sec*1000 + tmo->tv_usec/1000)> 100)
1453 ms = 100;
1454 if (!GetQueuedCompletionStatus(hIoCompPort, &bytes, &fd,
1455 (LPOVERLAPPED *)&pOv, ms) && !pOv) {
1456 err = WSAGetLastError();
1457 return 0; /* timeout */
1458 }
6ad90ad2 1459
0198fd3c 1460 ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX));
1461 /* call callback if descriptor still valid */
1462 ASSERT(pOv);
1463 if(pOv->instance == fdTable[fd].instance)
1464 (*pOv->procPtr)(pOv->clientData, bytes);
1465 free(pOv);
1466
1467 ftime(&tb);
1468 ms -= (tb.time*1000 + tb.millitm - ms_last);
1469 ms_last = tb.time*1000 + tb.millitm;
1470 }
1471 return 0;
1472}
1473
87abe107 1474
1475static int CALLBACK isAddrOK(LPWSABUF lpCallerId,
1476 LPWSABUF dc0,
1477 LPQOS dc1,
1478 LPQOS dc2,
1479 LPWSABUF dc3,
1480 LPWSABUF dc4,
1481 GROUP *dc5,
1482 DWORD dwCallbackData)
1483{
1484 const char *okAddrs = (char *) dwCallbackData;
1485 struct sockaddr *sockaddr = (struct sockaddr *) lpCallerId->buf;
1486
1487 if (okAddrs == NULL || sockaddr->sa_family != AF_INET)
1488 {
1489 return TRUE;
1490 }
1491 else
1492 {
1493 static const char *token = " ,;:\t";
1494 struct sockaddr_in * inet_sockaddr = (struct sockaddr_in *) sockaddr;
1495 char *ipaddr = inet_ntoa(inet_sockaddr->sin_addr);
1496 char *p = strstr(okAddrs, ipaddr);
1497
1498 if (p == NULL)
1499 {
1500 return FALSE;
1501 }
1502 else if (p == okAddrs)
1503 {
1504 p += strlen(ipaddr);
1505 return (strchr(token, *p) != NULL);
1506 }
1507 else if (strchr(token, *--p))
1508 {
1509 p += strlen(ipaddr) + 1;
1510 return (strchr(token, *p) != NULL);
1511 }
1512 else
1513 {
1514 return FALSE;
1515 }
1516 }
1517}
1518
0198fd3c 1519/*
1520 *----------------------------------------------------------------------
1521 *
0b7c9662 1522 * OS_Accept --
0198fd3c 1523 *
1524 * Accepts a new FastCGI connection. This routine knows whether
1525 * we're dealing with TCP based sockets or NT Named Pipes for IPC.
1526 *
1527 * Results:
1528 * -1 if the operation fails, otherwise this is a valid IPC fd.
1529 *
1530 * Side effects:
1531 * New IPC connection is accepted.
1532 *
1533 *----------------------------------------------------------------------
1534 */
1dd5d7a8 1535int OS_Accept(int listen_sock, int fail_on_intr, const char *webServerAddrs)
0198fd3c 1536{
0b7c9662 1537 /* XXX This is broken for listen_sock & fail_on_intr */
0198fd3c 1538 int ipcFd = -1;
1539 BOOL pConnected;
0198fd3c 1540 SOCKET hSock;
0198fd3c 1541
87abe107 1542 if (shutdownPending)
1543 {
1544 return -1;
1545 }
0198fd3c 1546
87abe107 1547 // The mutex is to keep other processes (and threads, when supported)
1548 // from going into the accept cycle. The accept cycle needs to
1549 // periodically break out to check the state of the shutdown flag
1550 // and there's no point to having more than one thread do that.
1551
1552 if (acceptMutex != INVALID_HANDLE_VALUE)
1553 {
1554 if (WaitForSingleObject(acceptMutex, INFINITE) == WAIT_FAILED)
1555 {
0198fd3c 1556 return -1;
1557 }
87abe107 1558 }
1559
1560 if (shutdownPending)
1561 {
1562 if (acceptMutex != INVALID_HANDLE_VALUE)
1563 {
1564 ReleaseMutex(acceptMutex);
1565 }
1566 return -1;
1567 }
1568
1569 if (listenType == FD_PIPE_SYNC)
1570 {
1571 pConnected = ConnectNamedPipe(hListen, &listenOverlapped)
1572 ? TRUE
1573 : (GetLastError() == ERROR_PIPE_CONNECTED);
1574
1575 if (! pConnected)
1576 {
1577 while (WaitForSingleObject(listenOverlapped.hEvent, 1000) == WAIT_TIMEOUT)
1578 {
1579 if (shutdownPending)
1580 {
1581 if (acceptMutex != INVALID_HANDLE_VALUE)
1582 {
1583 ReleaseMutex(acceptMutex);
1584 }
1585 CancelIo(hListen);
1586 return -1;
1587 }
0198fd3c 1588 }
87abe107 1589 }
1590
1591 if (acceptMutex != INVALID_HANDLE_VALUE)
1592 {
1593 ReleaseMutex(acceptMutex);
1594 }
1595
1596 ipcFd = Win32NewDescriptor(FD_PIPE_SYNC, (int) hListen, -1);
1597 if (ipcFd == -1)
1598 {
1599 DisconnectNamedPipe(hListen);
1600 }
0198fd3c 1601 }
87abe107 1602 else if (listenType == FD_SOCKET_SYNC)
1603 {
1604 struct sockaddr sockaddr;
1605 int sockaddrLen = sizeof(sockaddr);
1606 fd_set readfds;
1607 const struct timeval timeout = {1, 0};
1608
1609 FD_ZERO(&readfds);
1610 FD_SET((unsigned int) hListen, &readfds);
1611
1612 while (select(0, &readfds, NULL, NULL, &timeout) == 0)
1613 {
1614 if (shutdownPending)
1615 {
1616 return -1;
1617 }
1618 }
1619
1620 hSock = (webServerAddrs == NULL)
1621 ? accept((SOCKET) hListen,
1622 &sockaddr,
1623 &sockaddrLen)
1624 : WSAAccept((unsigned int) hListen,
1625 &sockaddr,
1626 &sockaddrLen,
1627 isAddrOK,
1628 (DWORD) webServerAddrs);
1629
1630 if (acceptMutex != INVALID_HANDLE_VALUE)
1631 {
1632 ReleaseMutex(acceptMutex);
1633 }
0198fd3c 1634
87abe107 1635 if (hSock == -1)
1636 {
1637 return -1;
1638 }
1639
1640 ipcFd = Win32NewDescriptor(FD_SOCKET_SYNC, hSock, -1);
1641 if (ipcFd == -1)
1642 {
1643 closesocket(hSock);
0198fd3c 1644 }
0198fd3c 1645 }
87abe107 1646 else
1647 {
1648 ASSERT(0);
1649 }
1650
1651 return ipcFd;
0198fd3c 1652}
62e100c7 1653
0198fd3c 1654/*
1655 *----------------------------------------------------------------------
1656 *
1657 * OS_IpcClose
1658 *
1659 * OS IPC routine to close an IPC connection.
1660 *
1661 * Results:
1662 *
1663 *
1664 * Side effects:
1665 * IPC connection is closed.
1666 *
1667 *----------------------------------------------------------------------
1668 */
1669int OS_IpcClose(int ipcFd)
1670{
8462b1ec 1671 if (ipcFd == -1)
1672 return 0;
1673
0198fd3c 1674 /*
1675 * Catch it if fd is a bogus value
1676 */
1677 ASSERT((ipcFd >= 0) && (ipcFd < WIN32_OPEN_MAX));
1678 ASSERT(fdTable[ipcFd].type != FD_UNUSED);
1679
1680 switch(listenType) {
1681
1682 case FD_PIPE_SYNC:
1683 /*
1684 * Make sure that the client (ie. a Web Server in this case) has
1685 * read all data from the pipe before we disconnect.
1686 */
1687 if(!FlushFileBuffers(fdTable[ipcFd].fid.fileHandle))
1688 return -1;
1689 if(DisconnectNamedPipe(fdTable[ipcFd].fid.fileHandle)) {
1690 OS_Close(ipcFd);
1691 return 0;
1692 } else {
1693 return -1;
1694 }
1695 break;
1696
1697 case FD_SOCKET_SYNC:
1698 OS_Close(ipcFd);
3bc7b7d9 1699 return 0;
0198fd3c 1700 break;
1701
1702 case FD_UNUSED:
1703 default:
1704 exit(106);
1705 break;
1706 }
0198fd3c 1707}
1708
0198fd3c 1709/*
1710 *----------------------------------------------------------------------
1711 *
1712 * OS_IsFcgi --
1713 *
1714 * Determines whether this process is a FastCGI process or not.
1715 *
1716 * Results:
1717 * Returns 1 if FastCGI, 0 if not.
1718 *
1719 * Side effects:
1720 * None.
1721 *
1722 *----------------------------------------------------------------------
1723 */
0b7c9662 1724int OS_IsFcgi(int sock)
0198fd3c 1725{
225369c3 1726 // This is still broken. There is not currently a way to differentiate
1727 // a CGI from a FCGI pipe (try the Unix method).
1728
1729 return (fdTable[sock].type != FD_UNUSED);
0198fd3c 1730}
1731
0198fd3c 1732/*
1733 *----------------------------------------------------------------------
1734 *
1735 * OS_SetFlags --
1736 *
1737 * Sets selected flag bits in an open file descriptor. Currently
1738 * this is only to put a SOCKET into non-blocking mode.
1739 *
1740 *----------------------------------------------------------------------
1741 */
1742void OS_SetFlags(int fd, int flags)
1743{
3bc7b7d9 1744 unsigned long pLong = 1L;
0198fd3c 1745 int err;
6ad90ad2 1746
3bc7b7d9 1747 if (fdTable[fd].type == FD_SOCKET_SYNC && flags == O_NONBLOCK) {
0198fd3c 1748 if (ioctlsocket(fdTable[fd].fid.sock, FIONBIO, &pLong) ==
1749 SOCKET_ERROR) {
1750 exit(WSAGetLastError());
1751 }
1752 if (!CreateIoCompletionPort((HANDLE)fdTable[fd].fid.sock,
1753 hIoCompPort, fd, 1)) {
1754 err = GetLastError();
1755 exit(err);
1756 }
1757
1758 fdTable[fd].type = FD_SOCKET_ASYNC;
1759 }
1760 return;
1761}
1762