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