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