Misc. updates to get a clean make on Linux. A bit of
[catagits/fcgi2.git] / cgi-fcgi / cgi-fcgi.c
CommitLineData
0198fd3c 1/*
2 * cgifcgi.c --
3 *
4 * CGI to FastCGI bridge
5 *
6 *
7 * Copyright (c) 1996 Open Market, Inc.
8 *
9 * See the file "LICENSE.TERMS" for information on usage and redistribution
10 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
11 *
12 */
13
14#ifndef lint
52c5620a 15static const char rcsid[] = "$Id: cgi-fcgi.c,v 1.2 1999/01/30 22:33:23 roberts Exp $";
0198fd3c 16#endif /* not lint */
17
18#include <stdio.h>
52c5620a 19#if defined HAVE_UNISTD_H || defined __linux__
0198fd3c 20#include <unistd.h>
21#endif
22#include <fcntl.h>
23#include <assert.h>
24#include <stdlib.h>
25#include <string.h>
26#include <errno.h>
27#include <ctype.h>
28#ifdef HAVE_NETDB_H
29#include <netdb.h>
30#endif
31#ifdef HAVE_SYS_TIME_H
32#include <sys/time.h>
33#endif
34#ifdef HAVE_SYS_PARAM_H
35#include <sys/param.h>
36#endif
37#include "fcgimisc.h"
38#include "fcgiapp.h"
39#include "fcgiappmisc.h"
40#include "fastcgi.h"
41#include "fcgi_config.h"
42#include "fcgios.h"
43
44static int wsReadPending = 0;
0198fd3c 45static int fcgiReadPending = 0;
46static int fcgiWritePending = 0;
47
48static void ScheduleIo(void);
49
50\f
51/*
52 * Simple buffer (not ring buffer) type, used by all event handlers.
53 */
54#define BUFFLEN 8192
55typedef struct {
56 char *next;
57 char *stop;
58 char buff[BUFFLEN];
59} Buffer;
60
61/*
62 *----------------------------------------------------------------------
63 *
64 * GetPtr --
65 *
66 * Returns a count of the number of characters available
67 * in the buffer (at most n) and advances past these
68 * characters. Stores a pointer to the first of these
69 * characters in *ptr.
70 *
71 *----------------------------------------------------------------------
72 */
73
74static int GetPtr(char **ptr, int n, Buffer *pBuf)
75{
76 int result;
77 *ptr = pBuf->next;
78 result = min(n, pBuf->stop - pBuf->next);
79 pBuf->next += result;
80 return result;
81}
82\f
83/*
84 *----------------------------------------------------------------------
85 *
86 * MakeHeader --
87 *
88 * Constructs an FCGI_Header struct.
89 *
90 *----------------------------------------------------------------------
91 */
92static FCGI_Header MakeHeader(
93 int type,
94 int requestId,
95 int contentLength,
96 int paddingLength)
97{
98 FCGI_Header header;
99 ASSERT(contentLength >= 0 && contentLength <= FCGI_MAX_LENGTH);
100 ASSERT(paddingLength >= 0 && paddingLength <= 0xff);
101 header.version = FCGI_VERSION_1;
102 header.type = type;
103 header.requestIdB1 = (requestId >> 8) & 0xff;
104 header.requestIdB0 = (requestId ) & 0xff;
105 header.contentLengthB1 = (contentLength >> 8) & 0xff;
106 header.contentLengthB0 = (contentLength ) & 0xff;
107 header.paddingLength = paddingLength;
108 header.reserved = 0;
109 return header;
110}
111\f
112/*
113 *----------------------------------------------------------------------
114 *
115 * MakeBeginRequestBody --
116 *
117 * Constructs an FCGI_BeginRequestBody record.
118 *
119 *----------------------------------------------------------------------
120 */
121static FCGI_BeginRequestBody MakeBeginRequestBody(
122 int role,
123 int keepConnection)
124{
125 FCGI_BeginRequestBody body;
126 ASSERT((role >> 16) == 0);
127 body.roleB1 = (role >> 8) & 0xff;
128 body.roleB0 = (role ) & 0xff;
129 body.flags = (keepConnection) ? FCGI_KEEP_CONN : 0;
130 memset(body.reserved, 0, sizeof(body.reserved));
131 return body;
132}
133
134\f
135static int bytesToRead; /* number of bytes to read from Web Server */
136static int appServerSock = -1; /* Socket connected to FastCGI application,
137 * used by AppServerReadHandler and
138 * AppServerWriteHandler. */
139static Buffer fromAS; /* Bytes read from the FCGI application server. */
140static FCGI_Header header; /* Header of the current record. Is global
141 * since read may return a partial header. */
142static int headerLen = 0; /* Number of valid bytes contained in header.
143 * If headerLen < sizeof(header),
144 * AppServerReadHandler is reading a record header;
145 * otherwise it is reading bytes of record content
146 * or padding. */
147static int contentLen; /* If headerLen == sizeof(header), contentLen
148 * is the number of content bytes still to be
149 * read. */
150static int paddingLen; /* If headerLen == sizeof(header), paddingLen
151 * is the number of padding bytes still
152 * to be read. */
153static int requestId; /* RequestId of the current request.
154 * Set by main. */
155static FCGI_EndRequestBody erBody;
156static int readingEndRequestBody = FALSE;
157 /* If readingEndRequestBody, erBody contains
158 * partial content: contentLen more bytes need
159 * to be read. */
160static int exitStatus = 0;
161static int exitStatusSet = FALSE;
162
163static int stdinFds[3];
164
165\f
166/*
167 *----------------------------------------------------------------------
168 *
169 * FCGIexit --
170 *
171 * FCGIexit provides a single point of exit. It's main use is for
172 * application debug when porting to other operating systems.
173 *
174 *----------------------------------------------------------------------
175 */
176static void FCGIexit(int exitCode)
177{
178 if(appServerSock != -1) {
179 OS_Close(appServerSock);
180 appServerSock = -1;
181 }
182 OS_LibShutdown();
183 exit(exitCode);
184}
185
186#undef exit
187#define exit FCGIexit
188
189\f
190/*
191 *----------------------------------------------------------------------
192 *
193 * AppServerReadHandler --
194 *
195 * Reads data from the FCGI application server and (blocking)
196 * writes all of it to the Web server. Exits the program upon
197 * reading EOF from the FCGI application server. Called only when
198 * there's data ready to read from the application server.
199 *
200 *----------------------------------------------------------------------
201 */
202
203static void AppServerReadHandler(ClientData clientData, int bytesRead)
204{
205 int count, outFD;
206 char *ptr;
207
208 assert(fcgiReadPending == TRUE);
209 fcgiReadPending = FALSE;
210 count = bytesRead;
211
212 if(count <= 0) {
213 if(count < 0) {
214 exit(OS_Errno);
215 }
216 if(headerLen > 0 || paddingLen > 0) {
217 exit(FCGX_PROTOCOL_ERROR);
218 }
219 if(appServerSock != -1) {
220 OS_Close(appServerSock);
221 appServerSock = -1;
222 }
223 /*
224 * XXX: Shouldn't be here if exitStatusSet.
225 */
226 exit((exitStatusSet) ? exitStatus : FCGX_PROTOCOL_ERROR);
227 }
228 fromAS.stop = fromAS.next + count;
229 while(fromAS.next != fromAS.stop) {
230 /*
231 * fromAS is not empty. What to do with the contents?
232 */
233 if(headerLen < sizeof(header)) {
234 /*
235 * First priority is to complete the header.
236 */
237 count = GetPtr(&ptr, sizeof(header) - headerLen, &fromAS);
238 assert(count > 0);
239 memcpy(&header + headerLen, ptr, count);
240 headerLen += count;
241 if(headerLen < sizeof(header)) {
242 break;
243 }
244 if(header.version != FCGI_VERSION_1) {
245 exit(FCGX_UNSUPPORTED_VERSION);
246 }
247 if((header.requestIdB1 << 8) + header.requestIdB0 != requestId) {
248 exit(FCGX_PROTOCOL_ERROR);
249 }
250 contentLen = (header.contentLengthB1 << 8)
251 + header.contentLengthB0;
252 paddingLen = header.paddingLength;
253 } else {
254 /*
255 * Header is complete (possibly from previous call). What now?
256 */
257 switch(header.type) {
258 case FCGI_STDOUT:
259 case FCGI_STDERR:
260 /*
261 * Write the buffered content to stdout or stderr.
262 * Blocking writes are OK here; can't prevent a slow
263 * client from tying up the app server without buffering
264 * output in temporary files.
265 */
266 count = GetPtr(&ptr, contentLen, &fromAS);
267 contentLen -= count;
268 if(count > 0) {
269 outFD = (header.type == FCGI_STDOUT) ?
270 STDOUT_FILENO : STDERR_FILENO;
271 if(OS_Write(outFD, ptr, count) < 0) {
272 exit(OS_Errno);
273 }
274 }
275 break;
276 case FCGI_END_REQUEST:
277 if(!readingEndRequestBody) {
278 if(contentLen != sizeof(erBody)) {
279 exit(FCGX_PROTOCOL_ERROR);
280 }
281 readingEndRequestBody = TRUE;
282 }
283 count = GetPtr(&ptr, contentLen, &fromAS);
284 if(count > 0) {
285 memcpy(&erBody + sizeof(erBody) - contentLen,
286 ptr, count);
287 contentLen -= count;
288 }
289 if(contentLen == 0) {
290 if(erBody.protocolStatus != FCGI_REQUEST_COMPLETE) {
291 /*
292 * XXX: What to do with FCGI_OVERLOADED?
293 */
294 exit(FCGX_PROTOCOL_ERROR);
295 }
296 exitStatus = (erBody.appStatusB3 << 24)
297 + (erBody.appStatusB2 << 16)
298 + (erBody.appStatusB1 << 8)
299 + (erBody.appStatusB0 );
300 exitStatusSet = TRUE;
301 readingEndRequestBody = FALSE;
302 }
303 break;
304 case FCGI_GET_VALUES_RESULT:
305 /* coming soon */
306 case FCGI_UNKNOWN_TYPE:
307 /* coming soon */
308 default:
309 exit(FCGX_PROTOCOL_ERROR);
310 }
311 if(contentLen == 0) {
312 if(paddingLen > 0) {
313 paddingLen -= GetPtr(&ptr, paddingLen, &fromAS);
314 }
315 /*
316 * If we've processed all the data and skipped all the
317 * padding, discard the header and look for the next one.
318 */
319 if(paddingLen == 0) {
320 headerLen = 0;
321 }
322 }
323 } /* headerLen >= sizeof(header) */
324 } /*while*/
325 ScheduleIo();
326}
327\f
328static Buffer fromWS; /* Buffer for data read from Web server
329 * and written to FastCGI application. Used
330 * by WebServerReadHandler and
331 * AppServerWriteHandler. */
332static int webServerReadHandlerEOF;
333 /* TRUE iff WebServerReadHandler has read EOF from
334 * the Web server. Used in main to prevent
335 * rescheduling WebServerReadHandler. */
336
337/*
338 *----------------------------------------------------------------------
339 *
340 * WebServerReadHandler --
341 *
342 * Non-blocking reads data from the Web server into the fromWS
343 * buffer. Called only when fromWS is empty, no EOF has been
344 * received from the Web server, and there's data available to read.
345 *
346 *----------------------------------------------------------------------
347 */
348
349static void WebServerReadHandler(ClientData clientData, int bytesRead)
350{
351 assert(fromWS.next == fromWS.stop);
352 assert(fromWS.next == &fromWS.buff[0]);
353 assert(wsReadPending == TRUE);
354 wsReadPending = FALSE;
355
356 if(bytesRead < 0) {
357 exit(OS_Errno);
358 }
359 *((FCGI_Header *) &fromWS.buff[0])
360 = MakeHeader(FCGI_STDIN, requestId, bytesRead, 0);
361 bytesToRead -= bytesRead;
362 fromWS.stop = &fromWS.buff[sizeof(FCGI_Header) + bytesRead];
363 webServerReadHandlerEOF = (bytesRead == 0);
364 ScheduleIo();
365}
366\f
367/*
368 *----------------------------------------------------------------------
369 *
370 * AppServerWriteHandler --
371 *
372 * Non-blocking writes data from the fromWS buffer to the FCGI
373 * application server. Called only when fromWS is non-empty
374 * and the socket is ready to accept some data.
375 *
376 *----------------------------------------------------------------------
377 */
378
379static void AppServerWriteHandler(ClientData clientData, int bytesWritten)
380{
381 int length = fromWS.stop - fromWS.next;
382
383 assert(length > 0);
384 assert(fcgiWritePending == TRUE);
385
386 fcgiWritePending = FALSE;
387 if(bytesWritten < 0) {
388 exit(OS_Errno);
389 }
390 if((int)bytesWritten < length) {
391 fromWS.next += bytesWritten;
392 } else {
393 fromWS.stop = fromWS.next = &fromWS.buff[0];
394 }
395
396 ScheduleIo();
397}
398
399\f
400/*
401 * ScheduleIo --
402 *
403 * This functions is responsible for scheduling all I/O to move
404 * data between a web server and a FastCGI application.
405 *
406 * Results:
407 * None.
408 *
409 * Side effects:
410 * This routine will signal the ioEvent upon completion.
411 *
412 */
413static void ScheduleIo(void)
414{
415 int length;
416
417 /*
418 * Move data between standard in and the FastCGI connection.
419 */
420 if(!fcgiWritePending && appServerSock != -1 &&
421 ((length = fromWS.stop - fromWS.next) != 0)) {
422 if(OS_AsyncWrite(appServerSock, 0, fromWS.next, length,
423 AppServerWriteHandler,
424 (ClientData)appServerSock) == -1) {
425 FCGIexit(OS_Errno);
426 } else {
427 fcgiWritePending = TRUE;
428 }
429 }
430
431 /*
432 * Schedule a read from the FastCGI application if there's not
433 * one pending and there's room in the buffer.
434 */
435 if(!fcgiReadPending && appServerSock != -1) {
436 fromAS.next = &fromAS.buff[0];
437
438 if(OS_AsyncRead(appServerSock, 0, fromAS.next, BUFFLEN,
439 AppServerReadHandler,
440 (ClientData)appServerSock) == -1) {
441 FCGIexit(OS_Errno);
442 } else {
443 fcgiReadPending = TRUE;
444 }
445 }
446
447 /*
448 * Schedule a read from standard in if necessary.
449 */
450 if((bytesToRead > 0) && !webServerReadHandlerEOF && !wsReadPending &&
451 !fcgiWritePending &&
452 fromWS.next == &fromWS.buff[0]) {
453 if(OS_AsyncReadStdin(fromWS.next + sizeof(FCGI_Header),
454 BUFFLEN - sizeof(FCGI_Header),
455 WebServerReadHandler, STDIN_FILENO)== -1) {
456 FCGIexit(OS_Errno);
457 } else {
458 wsReadPending = TRUE;
459 }
460 }
461}
462
463\f
464/*
465 *----------------------------------------------------------------------
466 *
467 * FCGI_Start --
468 *
469 * Starts nServers copies of FCGI application appPath, all
470 * listening to a Unix Domain socket at bindPath.
471 *
472 *----------------------------------------------------------------------
473 */
474
475static void FCGI_Start(char *bindPath, char *appPath, int nServers)
476{
477 int listenFd, i;
0198fd3c 478
479 if((listenFd = OS_CreateLocalIpcFd(bindPath)) == -1) {
480 exit(OS_Errno);
481 }
482
483 if(access(appPath, X_OK) == -1) {
484 fprintf(stderr, "%s is not executable\n", appPath);
485 exit(1);
486 }
487
488 /*
489 * Create the server processes
490 */
491 for(i = 0; i < nServers; i++) {
492 if(OS_SpawnChild(appPath, listenFd) == -1) {
493 exit(OS_Errno);
494 }
495 }
496 OS_Close(listenFd);
497}
498\f
499/*
500 *----------------------------------------------------------------------
501 *
502 * FCGIUtil_BuildNameValueHeader --
503 *
504 * Builds a name-value pair header from the name length
505 * and the value length. Stores the header into *headerBuffPtr,
506 * and stores the length of the header into *headerLenPtr.
507 *
508 * Side effects:
509 * Stores header's length (at most 8) into *headerLenPtr,
510 * and stores the header itself into
511 * headerBuffPtr[0 .. *headerLenPtr - 1].
512 *
513 *----------------------------------------------------------------------
514 */
515static buildNameValueHeaderCalls = 0; /* XXX: for testing */
516
517static void FCGIUtil_BuildNameValueHeader(
518 int nameLen,
519 int valueLen,
520 unsigned char *headerBuffPtr,
521 int *headerLenPtr) {
522 unsigned char *startHeaderBuffPtr = headerBuffPtr;
523
524 ASSERT(nameLen >= 0);
525 if(nameLen < 0x80 && (buildNameValueHeaderCalls & 1) == 0) {
526 *headerBuffPtr++ = nameLen;
527 } else {
528 *headerBuffPtr++ = (nameLen >> 24) | 0x80;
529 *headerBuffPtr++ = (nameLen >> 16);
530 *headerBuffPtr++ = (nameLen >> 8);
531 *headerBuffPtr++ = nameLen;
532 }
533 ASSERT(valueLen >= 0);
534 if(valueLen < 0x80 && (buildNameValueHeaderCalls & 2) == 0) {
535 *headerBuffPtr++ = valueLen;
536 } else {
537 *headerBuffPtr++ = (valueLen >> 24) | 0x80;
538 *headerBuffPtr++ = (valueLen >> 16);
539 *headerBuffPtr++ = (valueLen >> 8);
540 *headerBuffPtr++ = valueLen;
541 }
542 *headerLenPtr = headerBuffPtr - startHeaderBuffPtr;
543 buildNameValueHeaderCalls++;
544}
545\f
546
547#define MAXARGS 16
548static int ParseArgs(int argc, char *argv[],
549 int *doBindPtr, int *doStartPtr,
550 char *connectPathPtr, char *appPathPtr, int *nServersPtr) {
551 int i,
552 x,
553 err = 0,
554 ac;
555 char *tp1,
556 *tp2,
557 *av[MAXARGS];
558 FILE *fp;
559 char line[BUFSIZ];
560
561 *doBindPtr = TRUE;
562 *doStartPtr = TRUE;
563 *connectPathPtr = '\0';
564 *appPathPtr = '\0';
565 *nServersPtr = 0;
566
567 for(i = 0; i < MAXARGS; i++)
568 av[i] = NULL;
569 for(i = 1; i < argc; i++) {
570 if(argv[i][0] == '-') {
571 if(!strcmp(argv[i], "-f")) {
572 if(++i == argc) {
573 fprintf(stderr,
574 "Missing command file name after -f\n");
575 return 1;
576 }
577 if((fp = fopen(argv[i], "r")) == NULL) {
578 fprintf(stderr, "Cannot open command file %s\n", argv[i]);
579 return 1;
580 }
581 ac = 1;
582 while(fgets(line, BUFSIZ, fp)) {
583 if(line[0] == '#') {
584 continue;
585 }
586 if((tp1 = (char *) strrchr(line,'\n')) != NULL) {
587 *tp1-- = 0;
588 while(*tp1 == ' ' || *tp1 =='\t') {
589 *tp1-- = 0;
590 }
591 } else {
592 fprintf(stderr, "Line to long\n");
593 return 1;
594 }
595 tp1 = line;
596 while(tp1) {
597 if((tp2 = strchr(tp1, ' ')) != NULL) {
598 *tp2++ = 0;
599 }
600 if(ac >= MAXARGS) {
601 fprintf(stderr,
602 "To many arguments, "
603 "%d is max from a file\n", MAXARGS);
604 exit(-1);
605 }
606 if((av[ac] = malloc(strlen(tp1)+1)) == NULL) {
607 fprintf(stderr, "Cannot allocate %d bytes\n",
608 strlen(tp1)+1);
609 exit(-1);
610 }
611 strcpy(av[ac++], tp1);
612 tp1 = tp2;
613 }
614 }
615 err = ParseArgs(ac, av, doBindPtr, doStartPtr,
616 connectPathPtr, appPathPtr, nServersPtr);
617 for(x = 1; x < ac; x++) {
618 ASSERT(av[x] != NULL);
619 free(av[x]);
620 }
621 return err;
622#ifdef _WIN32
623 } else if (!strcmp(argv[i], "-jitcgi")) {
624 DebugBreak();
625 } else if (!strcmp(argv[i], "-dbgfcgi")) {
626 putenv("DEBUG_FCGI=TRUE");
627#endif
628 } else if(!strcmp(argv[i], "-start")) {
629 *doBindPtr = FALSE;
630 } else if(!strcmp(argv[i], "-bind")) {
631 *doStartPtr = FALSE;
632 } else if(!strcmp(argv[i], "-connect")) {
633 if(++i == argc) {
634 fprintf(stderr,
635 "Missing connection name after -connect\n");
636 err++;
637 } else {
638 strcpy(connectPathPtr, argv[i]);
639 }
640 } else {
641 fprintf(stderr, "Unknown option %s\n", argv[i]);
642 err++;
643 }
644 } else if(*appPathPtr == '\0') {
645 strcpy(appPathPtr, argv[i]);
646 } else if(isdigit(argv[i][0]) && *nServersPtr == 0) {
647 *nServersPtr = atoi(argv[i]);
648 if(*nServersPtr <= 0) {
649 fprintf(stderr, "Number of servers must be greater than 0\n");
650 err++;
651 }
652 } else {
653 fprintf(stderr, "Unknown argument %s\n", argv[i]);
654 err++;
655 }
656 }
657 if(*doStartPtr && *appPathPtr == 0) {
658 fprintf(stderr, "Missing application pathname\n");
659 err++;
660 }
661 if(*connectPathPtr == 0) {
662 fprintf(stderr, "Missing -connect <connName>\n");
663 err++;
664 } else if(strchr(connectPathPtr, ':')) {
665/*
666 * XXX: Test to see if we can use IP connect locally...
667 This hack lets me test the ability to create a local process listening
668 to a TCP/IP port for connections and subsequently connect to the app
669 like we do for Unix domain and named pipes.
670
671 if(*doStartPtr && *doBindPtr) {
672 fprintf(stderr,
673 "<connName> of form hostName:portNumber "
674 "requires -start or -bind\n");
675 err++;
676 }
677 */
678 }
679 if(*nServersPtr == 0) {
680 *nServersPtr = 1;
681 }
682 return err;
683}
684\f
685void main(int argc, char **argv, char **envp)
686{
687 int count;
688 FCGX_Stream *paramsStream;
689 int numFDs;
690 unsigned char headerBuff[8];
691 int headerLen, valueLen;
692 char *equalPtr;
693 FCGI_BeginRequestRecord beginRecord;
694 int doBind, doStart, nServers;
695 char appPath[MAXPATHLEN], bindPath[MAXPATHLEN];
696
697 if(ParseArgs(argc, argv, &doBind, &doStart,
698 (char *) &bindPath, (char *) &appPath, &nServers)) {
699 fprintf(stderr,
700"Usage:\n"
701" cgi-fcgi -f <cmdPath> , or\n"
702" cgi-fcgi -connect <connName> <appPath> [<nServers>] , or\n"
703" cgi-fcgi -start -connect <connName> <appPath> [<nServers>] , or\n"
704" cgi-fcgi -bind -connect <connName> ,\n"
705"where <connName> is either the pathname of a UNIX domain socket\n"
706"or (if -bind is given) a hostName:portNumber specification\n"
707"or (if -start is given) a :portNumber specification (uses local host).\n");
708 exit(1);
709 }
710
711 if(OS_LibInit(stdinFds)) {
712 fprintf(stderr, "Error initializing OS library: %d\n", OS_Errno);
713 exit(0);
714 }
715
716 equalPtr = getenv("CONTENT_LENGTH");
717 if(equalPtr != NULL) {
718 bytesToRead = atoi(equalPtr);
719 } else {
720 bytesToRead = 0;
721 }
722
723 if(doBind) {
724 appServerSock = OS_FcgiConnect(bindPath);
725 }
726 if(doStart && (!doBind || appServerSock < 0)) {
727 FCGI_Start(bindPath, appPath, nServers);
728 if(!doBind) {
729 exit(0);
730 } else {
731 appServerSock = OS_FcgiConnect(bindPath);
732 }
733 }
734 if(appServerSock < 0) {
735 fprintf(stderr, "Could not connect to %s\n", bindPath);
736 exit(OS_Errno);
737 }
738 /*
739 * Set an arbitrary non-null FCGI RequestId
740 */
741 requestId = 1;
742 /*
743 * XXX: Send FCGI_GET_VALUES
744 */
745
746 /*
747 * XXX: Receive FCGI_GET_VALUES_RESULT
748 */
749
750 /*
751 * Send FCGI_BEGIN_REQUEST (XXX: hack, separate write)
752 */
753 beginRecord.header = MakeHeader(FCGI_BEGIN_REQUEST, requestId,
754 sizeof(beginRecord.body), 0);
755 beginRecord.body = MakeBeginRequestBody(FCGI_RESPONDER, FALSE);
756 count = OS_Write(appServerSock, (char *)&beginRecord, sizeof(beginRecord));
757 if(count != sizeof(beginRecord)) {
758 exit(OS_Errno);
759 }
760 /*
761 * Send environment to the FCGI application server
762 */
763 paramsStream = CreateWriter(appServerSock, requestId, 8192, FCGI_PARAMS);
764 for( ; *envp != NULL; envp++) {
765 equalPtr = strchr(*envp, '=');
766 if(equalPtr == NULL) {
767 exit(1000);
768 }
769 valueLen = strlen(equalPtr + 1);
770 FCGIUtil_BuildNameValueHeader(
771 equalPtr - *envp,
772 valueLen,
773 &headerBuff[0],
774 &headerLen);
775 if(FCGX_PutStr((char *) &headerBuff[0], headerLen, paramsStream) < 0
776 || FCGX_PutStr(*envp, equalPtr - *envp, paramsStream) < 0
777 || FCGX_PutStr(equalPtr + 1, valueLen, paramsStream) < 0) {
778 exit(FCGX_GetError(paramsStream));
779 }
780 }
781 FCGX_FClose(paramsStream);
782 FreeStream(&paramsStream);
783 /*
784 * Perform the event loop until AppServerReadHander sees FCGI_END_REQUEST
785 */
786 fromWS.stop = fromWS.next = &fromWS.buff[0];
787 webServerReadHandlerEOF = FALSE;
788 /*
789 * XXX: might want to use numFDs in the os library.
790 */
791 numFDs = max(appServerSock, STDIN_FILENO) + 1;
792 OS_SetFlags(appServerSock, O_NONBLOCK);
793
794 ScheduleIo();
795 while(!exitStatusSet) {
796 /*
797 * NULL = wait forever (or at least until there's something
798 * to do.
799 */
800 OS_DoIo(NULL);
801 }
802 if(exitStatusSet) {
803 FCGIexit(exitStatus);
804 } else {
805 FCGIexit(999);
806 }
807}