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