Remove unnecessary .dfc files.
[catagits/fcgi2.git] / cgi-fcgi / cgi-fcgi.c
CommitLineData
e3fe7c0c 1/*
0198fd3c 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 */
0198fd3c 13#ifndef lint
bfc019c2 14static const char rcsid[] = "$Id: cgi-fcgi.c,v 1.9 1999/08/14 21:36:58 roberts Exp $";
0198fd3c 15#endif /* not lint */
16
3d683188 17#include "fcgi_config.h"
18
0198fd3c 19#include <assert.h>
3d683188 20#include <ctype.h>
21#include <errno.h>
22#include <fcntl.h>
23#include <stdio.h>
0198fd3c 24#include <stdlib.h>
25#include <string.h>
3d683188 26
0198fd3c 27#ifdef HAVE_NETDB_H
28#include <netdb.h>
29#endif
3d683188 30
31#ifdef _WIN32
32#include <stdlib.h>
33#else
34extern char **environ;
0198fd3c 35#endif
3d683188 36
0198fd3c 37#ifdef HAVE_SYS_PARAM_H
38#include <sys/param.h>
39#endif
3d683188 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
0198fd3c 49#include "fcgimisc.h"
50#include "fcgiapp.h"
51#include "fcgiappmisc.h"
52#include "fastcgi.h"
0198fd3c 53#include "fcgios.h"
54
04d12200 55
0198fd3c 56static int wsReadPending = 0;
0198fd3c 57static int fcgiReadPending = 0;
58static int fcgiWritePending = 0;
59
60static void ScheduleIo(void);
61
62\f
63/*
64 * Simple buffer (not ring buffer) type, used by all event handlers.
65 */
66#define BUFFLEN 8192
67typedef 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
86static 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 */
104static 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 */
133static 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
147static int bytesToRead; /* number of bytes to read from Web Server */
148static int appServerSock = -1; /* Socket connected to FastCGI application,
149 * used by AppServerReadHandler and
150 * AppServerWriteHandler. */
151static Buffer fromAS; /* Bytes read from the FCGI application server. */
152static FCGI_Header header; /* Header of the current record. Is global
153 * since read may return a partial header. */
154static 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. */
159static int contentLen; /* If headerLen == sizeof(header), contentLen
160 * is the number of content bytes still to be
161 * read. */
162static int paddingLen; /* If headerLen == sizeof(header), paddingLen
163 * is the number of padding bytes still
164 * to be read. */
165static int requestId; /* RequestId of the current request.
166 * Set by main. */
167static FCGI_EndRequestBody erBody;
168static int readingEndRequestBody = FALSE;
169 /* If readingEndRequestBody, erBody contains
170 * partial content: contentLen more bytes need
171 * to be read. */
172static int exitStatus = 0;
173static int exitStatusSet = FALSE;
174
175static 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 */
188static 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
215static 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
340static Buffer fromWS; /* Buffer for data read from Web server
341 * and written to FastCGI application. Used
342 * by WebServerReadHandler and
343 * AppServerWriteHandler. */
344static 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
361static 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
391static 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();
e3fe7c0c 409}
0198fd3c 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.
e3fe7c0c 423 *
0198fd3c 424 */
425static 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
e3fe7c0c 450 if(OS_AsyncRead(appServerSock, 0, fromAS.next, BUFFLEN,
0198fd3c 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),
e3fe7c0c 466 BUFFLEN - sizeof(FCGI_Header),
0198fd3c 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
487static void FCGI_Start(char *bindPath, char *appPath, int nServers)
488{
489 int listenFd, i;
0198fd3c 490
0b7c9662 491 /* @@@ Should be able to pick up the backlog as an arg */
492 if((listenFd = OS_CreateLocalIpcFd(bindPath, 5)) == -1) {
0198fd3c 493 exit(OS_Errno);
494 }
e3fe7c0c 495
0198fd3c 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 */
0198fd3c 528static 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);
3d683188 536 if (nameLen < 0x80) {
0198fd3c 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);
3d683188 545 if (valueLen < 0x80) {
0198fd3c 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;
0198fd3c 554}
555\f
556
557#define MAXARGS 16
558static 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 }
3d683188 616 if((av[ac] = (char *)malloc(strlen(tp1)+1)) == NULL) {
0198fd3c 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]);
bfc019c2 656 } else if(isdigit((int)argv[i][0]) && *nServersPtr == 0) {
0198fd3c 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.
e3fe7c0c 680
0198fd3c 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
04d12200 695int main(int argc, char **argv)
0198fd3c 696{
04d12200 697 char **envp = environ;
0198fd3c 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 }
e3fe7c0c 733
0198fd3c 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 }
283822e9 818
04d12200 819 return 0;
0198fd3c 820}