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 |
cc6db5a6 |
14 | static 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 |
35 | extern 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 |
57 | static int wsReadPending = 0; |
0198fd3c |
58 | static int fcgiReadPending = 0; |
59 | static int fcgiWritePending = 0; |
60 | |
61 | static void ScheduleIo(void); |
62 | |
63 | \f |
64 | /* |
65 | * Simple buffer (not ring buffer) type, used by all event handlers. |
66 | */ |
67 | #define BUFFLEN 8192 |
68 | typedef 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 | |
87 | static 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 | */ |
105 | static 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 | */ |
134 | static 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 |
148 | static int bytesToRead; /* number of bytes to read from Web Server */ |
149 | static int appServerSock = -1; /* Socket connected to FastCGI application, |
150 | * used by AppServerReadHandler and |
151 | * AppServerWriteHandler. */ |
152 | static Buffer fromAS; /* Bytes read from the FCGI application server. */ |
153 | static FCGI_Header header; /* Header of the current record. Is global |
154 | * since read may return a partial header. */ |
155 | static 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. */ |
160 | static int contentLen; /* If headerLen == sizeof(header), contentLen |
161 | * is the number of content bytes still to be |
162 | * read. */ |
163 | static int paddingLen; /* If headerLen == sizeof(header), paddingLen |
164 | * is the number of padding bytes still |
165 | * to be read. */ |
166 | static int requestId; /* RequestId of the current request. |
167 | * Set by main. */ |
168 | static FCGI_EndRequestBody erBody; |
169 | static int readingEndRequestBody = FALSE; |
170 | /* If readingEndRequestBody, erBody contains |
171 | * partial content: contentLen more bytes need |
172 | * to be read. */ |
173 | static int exitStatus = 0; |
174 | static int exitStatusSet = FALSE; |
175 | |
176 | static 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 | */ |
189 | static 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 |
216 | static 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 |
344 | static Buffer fromWS; /* Buffer for data read from Web server |
345 | * and written to FastCGI application. Used |
346 | * by WebServerReadHandler and |
347 | * AppServerWriteHandler. */ |
348 | static int webServerReadHandlerEOF; |
349 | /* TRUE iff WebServerReadHandler has read EOF from |
350 | * the Web server. Used in main to prevent |
351 | * rescheduling WebServerReadHandler. */ |
352 | |
b6c774b6 |
353 | static 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 |
377 | static 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 |
414 | static 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 | */ |
451 | static 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 | |
513 | static 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 |
554 | static 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 |
584 | static 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 |
721 | int 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(¶msStream); |
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 | } |