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