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 |
0d99bb79 |
14 | static const char rcsid[] = "$Id: cgi-fcgi.c,v 1.16 2003/06/22 02:02:33 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 |
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 | |
eac4d1ef |
46 | #ifdef HAVE_UNISTD_H |
3d683188 |
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 |
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; |
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 | */ |
133 | static 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 |
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) { |
0d99bb79 |
191 | OS_Close(appServerSock, TRUE); |
0198fd3c |
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 |
215 | static 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) { |
0d99bb79 |
235 | OS_Close(appServerSock, TRUE); |
0198fd3c |
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 |
343 | static Buffer fromWS; /* Buffer for data read from Web server |
344 | * and written to FastCGI application. Used |
345 | * by WebServerReadHandler and |
346 | * AppServerWriteHandler. */ |
347 | static int webServerReadHandlerEOF; |
348 | /* TRUE iff WebServerReadHandler has read EOF from |
349 | * the Web server. Used in main to prevent |
350 | * rescheduling WebServerReadHandler. */ |
351 | |
b6c774b6 |
352 | static 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 |
376 | static 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 |
413 | static 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 | */ |
450 | static 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 | |
512 | static 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 | } |
0d99bb79 |
534 | OS_Close(listenFd, TRUE); |
0198fd3c |
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 |
553 | static 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 |
583 | static 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 |
720 | int 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(¶msStream); |
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 | } |