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