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