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