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