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