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