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