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