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