Commit | Line | Data |
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 |
14 | static 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 |
34 | extern 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 |
56 | static int wsReadPending = 0; |
0198fd3c |
57 | static int fcgiReadPending = 0; |
58 | static int fcgiWritePending = 0; |
59 | |
60 | static void ScheduleIo(void); |
61 | |
62 | \f |
63 | /* |
64 | * Simple buffer (not ring buffer) type, used by all event handlers. |
65 | */ |
66 | #define BUFFLEN 8192 |
67 | typedef 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 | |
86 | static 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 | */ |
104 | static 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 | */ |
133 | static 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 |
147 | static int bytesToRead; /* number of bytes to read from Web Server */ |
148 | static int appServerSock = -1; /* Socket connected to FastCGI application, |
149 | * used by AppServerReadHandler and |
150 | * AppServerWriteHandler. */ |
151 | static Buffer fromAS; /* Bytes read from the FCGI application server. */ |
152 | static FCGI_Header header; /* Header of the current record. Is global |
153 | * since read may return a partial header. */ |
154 | static 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. */ |
159 | static int contentLen; /* If headerLen == sizeof(header), contentLen |
160 | * is the number of content bytes still to be |
161 | * read. */ |
162 | static int paddingLen; /* If headerLen == sizeof(header), paddingLen |
163 | * is the number of padding bytes still |
164 | * to be read. */ |
165 | static int requestId; /* RequestId of the current request. |
166 | * Set by main. */ |
167 | static FCGI_EndRequestBody erBody; |
168 | static int readingEndRequestBody = FALSE; |
169 | /* If readingEndRequestBody, erBody contains |
170 | * partial content: contentLen more bytes need |
171 | * to be read. */ |
172 | static int exitStatus = 0; |
173 | static int exitStatusSet = FALSE; |
174 | |
175 | static 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 | */ |
188 | static 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 | |
215 | static 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 |
340 | static Buffer fromWS; /* Buffer for data read from Web server |
341 | * and written to FastCGI application. Used |
342 | * by WebServerReadHandler and |
343 | * AppServerWriteHandler. */ |
344 | static 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 | |
361 | static 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 | |
391 | static 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 | */ |
425 | static 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 | |
487 | static 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 |
528 | static 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 |
558 | static 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 |
695 | int 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(¶msStream); |
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 | } |