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