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