mod_fastcgi-SNAP-0910052141/0002775000635400022240000000000011262520023015546 5ustar robsfastcgi00000000000000mod_fastcgi-SNAP-0910052141/modules.mk0000664000635400022240000000046307521116042017555 0ustar robsfastcgi00000000000000# # this is used/needed by the APACHE2 build system # MOD_FASTCGI = mod_fastcgi fcgi_pm fcgi_util fcgi_protocol fcgi_buf fcgi_config mod_fastcgi.la: ${MOD_FASTCGI:=.slo} $(SH_LINK) -rpath $(libexecdir) -module -avoid-version ${MOD_FASTCGI:=.lo} DISTCLEAN_TARGETS = modules.mk shared = mod_fastcgi.la mod_fastcgi-SNAP-0910052141/CHANGES0000664000635400022240000011174511260253002016546 0ustar robsfastcgi000000000000002.4.7 *) *nix: On graceful restart, send SIGTERM, then wait 2sec before removing any unix domain sockets. Based on a patch by Bernd Wurst. *) Fix uid_t/gid_t compiler warnings. [Artur Zaprzala ] *) A few more EINTR fixes. [Artur Zaprzala ] *) Don't let the Content-Length header propagate on errors or across redirects. [Artur Zaprzala ] *) Fix pass-header handling (prefix with HTTP_). Based on a patch by [Christian Seiler ] *) Add an EOS bucket to the output filter chain. Based on a patch by [Philipp Dunkel ] *) Handle EINTR and EAGAIN in places AIX stress testing revealed issues. Based on a patch by [Rainer Jung ] *) Fix process spawning on WIN under Apache 2.2.9 (an incompatible change was introduced in APR 1.3). Patch by [Tom Donovan ] *) Allow duplicate Status, Location and ContentType headers (consistent with mod_cgi). Based on a patch by [Christian Seiler ] *) Fix a spurious idle timeout error that occurred under unique application timing and response size conditions. Based on a report and patch by [Joe Strout ] 2.4.6 *) Fix a bug I introduced in 2.4.4 that broke dynamic application restarts. Reported by [Yar ] 2.4.4 *) Allow FastCgiServer and FastCgiExternal server directives to be used within VirtualHosts (again). Add docs to explain potential accessibility from other VirtualHosts. [Rob Saccoccio ] *) Check for a null filename in the request to prevent an NPE that was occurring when a WebSphere Apache module was also in use. [Fabian Pehla } *) [AP2] Call ap_set_content_type() rather than setting the content_type directly so that the AddOutputFilterByType directive can work correctly. [Thomas 'Freaky' Hurst ] *) Don't use initializers for timeval structs because on 64bit MVS there is a padding field in between tv_sec and tv_usec. [Eric Covener ] *) [AP2] Add support for nph (non parsed header) scripts. [Peter Zijlstra ] *) Abort the request if the client connection ends prematurely. [Peter Zijlstra ] *) Introduce the apr_* backward compatibility macros removed in Apache 2.2. ["Daniel Smertnig" ] *) Fix a problem (remove install-modules) in Makefile.AP2 under Apache 2.2. ["Daniel Smertnig" ] *) Don't count an application exit towards the number of failures when doing restart backoff handling if the exit status is 0. ["Rob Saccoccio" ] *) [*nix] Don't use suexec when there is no user/group in effect. This change is consistent with Apache2 handling. Identified by ["Florian Effenberger" ]. *) Add a -min-server-life option to the FastCgiConfig and FastCgiServer directives to provide better control of the restart backoff feature. ["Benjamin Osheroff" ] 2.4.2 *) [WIN] Fix handle leaks in the process manager. ["Sascha Schumann" ] *) [WIN] Use a permanent pool for allocating the SystemRoot environment variable. [Sakamoto ] *) [WIN] Fix starting of scripts under AP2. *) Do the connect() to the application *after* collecting a chunk of client data. This reinstates the pre-2.4.0 behaviour. ["James Jurach" ] *) Remove an assert that was triggering on WIN when spawn() failed. *) Provide the NO_SUEXEC_FOR_AP_USER_N_GROUP macro for building mod_fastcgi with the AP13 suexec behaviour (don't use suexec if httpd's user and group match that needed for the application). *) Prevent the use of all but the "auth" directives from being used anywhere but in global scope. Prevent more than one instance of the FastCgiWrapper directive. *) Return NOT_FOUND (404) or FORBIDDEN (403) instead of INTERNAL_SERVER_ERROR (500) when there are configuration issues or the script does't exist. Suggested by ["Jeff Lawson" ]. 2.4.0 *) [*nix Security] - When FastCgiWrapper (FastCgiSuexec) was in use and a vhost configured to use the same uid/gid as the main server, mod_fastcgi would not bother using the wrapper (suexec) because its effective uid/gid was already appropriate. This is consistent with Apache's v1.3 mod_cgi behaviour. There are two problems with this approach: 1) when FastCgWrapper is in use mod_fastcgi's process manager keeps its root privileges (as its real uid/gid) so it can terminate the applications its starts - this privilege was being passed to applications when the use of the wrapper was bypassed 2) wrappers are often employed to perform functionality beyond setting the uid/gid - by not calling the wrapper under certain circumstances, application invocation environments were inconsistent. With this change, the wrapper is always used (when enabled) under both Apache 1.3 and 2. Reported by ["Michael Richards" ]. *) [*nix/AP2] Use the vhost uid/gid instead of the server uid/gid for dynamic application invocation when the FastCgiWrapper is in use. Reported by ["Michael Richards" ] *) [*nix] Fix handling of FastCgiWrapper when passed a real path, i.e. other than "on" or "off". ["Michael Richards" ] *) Eliminate the logging of "incomplete headers (0 bytes) received from server" when a client aborts. *) [WIN32] Fix a delay in handling large POSTs to named pipe based servers. ["Philip Gladstone" ] *) [*nix/AP2] Prevent the module from being initalized twice at startup (resulting in confusing error messages to the log). *) Eliminate the need for SetHandler or AddHandler with static or external applications. *) Limit PM requests to start a dynamic application to 5sec to prevent endless spinning (this is a drop-dead limit that should only occur if the socket/named_pipe directory is removed out from under a running server). *) [*nix] Change the default socket directory from /tmp/fcgi to: Apache - logs/fastcgi Apache2 - RUNTIMEDIR/fastcgi *) Add -user & -group args to FastCgiServer and FastCgiExternalServer for use with wrappers (in lieu of finding the user/group associated with a virtual host - under Apache2 this isn't accomodated). *) [WIN32] Under Apache2, require v2.0.41 or later in order to pickup my apr_proc_create() changes. *) Log when invoking and restoring the restart backoff policy. *) [WIN32] Prevent intermittent ReadFile() failures (properly initialize the OVERLAPPED structure). *) Eliminate need for dummy files for external servers under Apache2 *) Fix auth compatibility mode handling for access checker and authorizer *) Fix HEAD request handling. Based on a patch by ["Chris Lightfoot" ]. *) [*nix] When autoupdate is enabled touch the socket when restarting the processes to prevent further requests. ["Eckebrecht von Pappenheim" ] *) Apache 2.0 support. *) [WIN32] Don't read from a potentially closed named pipe. ["Philip Gladstone" ] *) Require the Apache version 1.3.6 or later to eliminate some signal handling funk. *) [WIN32] Use asyncronous io with named pipes instead of polled nonblocking io. This should eliminate the last of the npipe issues. *) Handle an application returning a complete and valid response without having consumed all of the data sent to it. *) Consume remaining client data (RESPONDERs only) if any. *) Add support for backing off attempts to start applications that continuously fail to start. Three new macros defined in mod_fastcgi.h control this behaviour: MAX_FAILED_STARTS, RUNTIME_SUCCESS_INTERVAL, FAILED_STARTS_DELAY *) [WIN32] Add (back) support for use of TerminateProcess() to accomodate applications that do not (properly) support the shutdown event (this feature was introduced in fcgi2 2.2.2 and improved in 2.2.4). The new macro WIN32_SHUTDOWN_GRACEFUL_WAIT in mod_fastcgi.h conrols the interval between signaling a proper shutdown and wacking the process(s) with a TerminateProcess(). *) [WIN32] Don't set the OVERLAPPED_IO flag on NamedPipe listen HANDLEs - setting it was just plain broken. *) [WIN32] Fix the accept mutex - all applications were sharing one!? *) Fix 'FastCgiConfig -autoUpdate'. *) Fix 'FastCgiConfig -flush'. *) Prevent silly maxProcesses and processSlack combinations. ["Dmitry Dorofeev" ] *) Properly handle the killing of idle processes when one takes a long time to exit once signaled down (or the config is funky). ["Dmitry Dorofeev" ] *) Always kill the youngest instance of an application. Suggested by ["Dmitry Dorofeev" ] 2.2.12 *) Delay the logging of write errors to the pm to account for shutdown/restart. *) (Win32) An assortment of fixes. *) Fix some broken casts that were likely the cause of an assert. *) Win32. Eliminate forward slashes from the named pipe path name. ["Gerald Richter" ] *) SIGUSR2 is no longer blocked in the process manager and the fastcgi applications it spawns. [] *) Added support for the -flush argument to FastCgiConfig. ["Eric Sit" ] *) Change the "which call to module_init() is this" check to a more reliable approach. ["Doru Petrescu" ] *) Close the old pipe file descriptor in apache main on USR1/HUP (elimnates a small leak). ["James E. Jurach Jr." ] *) Fix a bug in fcgi_config_set_authoritative_slot(). ["Tetsuya Furukawa" ] *) Eliminate the use of locks to assist in the clean shutdown of applications. Instead, it is assumed that applications handle termination signals properly (this is now embedded in the C application lib). *) Fix Win32 process termination. Proper operation requires the use of an updated application lib (termination is now signalled with an Event and handled by specialized thread). *) Docs cleanup. ["Andrew Benham" ] *) Added code so if the last instance of a dynamic application died without provocation, then don't restart it if singleThreshold > 0 (i.e. if the configuration allows the last instance to be killed, then allow it to die). ["Andrew Benham" ] *) Fix the loadFactor calculation used to determine when dyanmic applications could be killed off due to low demand. [] *) Fix a deadlock condition that could occur with Win32 named pipes (dynamic). *) Fix a potential deadlock condition when FastCGI application sent responses while still reading the client request (POST data). 2.2.10 *) Allow absolute pathnames in the -socket argument. Suggested by ["Christian Jaeger" ]. *) Don't invoke suexec when the user/group for the fastcgi application is the same as the apache main server. This is consistent with apache's suexec handling. Suggested by ["Nikolaus Rath" ]. *) Reset the apache drop dead timer upon successful read or writes to/from the client. This eliminates timeouts that were occuring during the large file transfers to/from slow clients. *) Support generic wrappers such as cgiwrap by eliminating dependencies on Apache's SUEXEC, renaming the FastCgiSuexec directive FastCgiWrapper and eliminating any checks regarding the target application (this is the repsonibility of the wrapper). *) Fix a nasty bug that occurred when a client aborted a POST request before the connection to a dynamic FastCGI application was opened. The application's lock file descriptor wasn't setup but was being closed which resulted in FD0 being closed. Normally this is open to /dev/null and should pose no problems except that because the FD was available it was being returned by Apache's accept(). This caused it to be registered for pool cleanup. mod_cgi though moves the CGI stdin pipe to FD0 and thus it was getting waxed during pool cleanup. Problem identified by []. Changes with mod_fastcgi 2.2.8 *) Eliminate the concept of disabled applications. If a failure occurs trying to setup an application (e.g. bind() error) its tried repeatedly every init-start-delay seconds. *) Tweak to Makefile.tmpl to support DSOs. ["Dave Hill" ] Changes with mod_fastcgi 2.2.6 *) Shutdown the PM when Apache appears to have disappeared. *) seteuid() tweak for HP-UX 11. ["Milton L. Hankins" ] *) (Win32) More dynamic fixes. *) (Win32) Eliminate the per application named pipe mutex. Its now per process to keep the lib happy (the use _FCGI_MUTEX_ should removed from the lib). This allows pipe-based applications to now handle multiple simoultanous requests (one per process). *) Increase the number of open FDs we look to close when spawning apps. *) Prevent an assert from popping unnecessarily. *) (Win32) Add support for interpretter scripts. *) (Win32) Fix named pipe handling (problems with large responses). *) (Win32) Remove the "can exec" check. Changes with mod_fastcgi 2.2.4 *) Beta WinNT support. ["David Allen" ] and [Rob Saccoccio ] *) Remove request type restriction (GET and POST only) in order to support Web DAV requests. ["Scott Robertson" ] *) Allow requests for URLs such as /server/some/other/qualifier to match a FastCGI server defined as /server. This was done primarily for Java Servlets, but is generically useful. *) Change the comm between the PM and the request handlers from a regular file to a pipe and an assortment of other dynamic fixes. *) Log dynamic process termination scheduling and the resulting process exit notification. *) Change the default singleThreshhold from 10 to 0 to prevent the last dynamic application process from being killed off due to low demand. *) Clean up FastCGI application pathnames (e.g. remove duplicate slashes). *) Fix bugs that prevented dynamic processes from being shutdown when the load subsided. ["Lars Heete" ] *) Prevent dynamic processes from being scheduled to be started before the init-start-delay or restart-delay period expires. This prevents the queuing of process start requests while an application which has a long initialization period starts up. *) When suexec is enabled, allow the process manager to become root again in order to signal applications it spawned. *) Change the "server started" log message level from INFO to WARN. *) Fix dynamic server "Connection Refused" handling. *) Fix demand-based dynamic process spawing (uses a more portable approach). *) Add -idle-timeout arg to FastCgiServer, FastCgiExternalServer, and FastCgiConfig. mod_fastcgi will now abort a connection if inactive for longer than this period. It applies to the initiation of connections as well (and thus is similar to appConnTimeout). Default is 30 seconds. *) appConnTimeout is 0 by default now resulting in blocking connect()s. This is more platform portable/predicable. *) Leave STDOUT and STDERR open to the main server error_log. This should eliminate a pile of protocol errors and having to track down 3rd party libs writing to stderr inadvertantly. This doesn't mean these shouldn't be fixed in the application to use the FastCGI I/O, it just allows these applications to run when previously they'd crash and burn. *) Add the pass-header arg to server directives. *) Allow supplementary groups to be passed to spawned FastCGI applications (as is done for CGI) - at least until PR2580 is resolved. *) Initialize the default (empty) environment in a more socially acceptable way. *) Miscellaneous doc improvements based on notes I've collected up over the last few months. *) Fix a bug in the stderr handling introduced in 2.2.2. There were conditions that resulted in not or improperly terminated strings. *) Fix the call to setsockopt() to disable Nagle. Based on a bug report by ["Johannes Plassmann" ]. Changes with mod_fastcgi 2.2.2 *) Added support for blocking connect()s by setting appConnTimeout to 0. Non-blocking connect()s (the default) can be troublesome on some platforms. Its expected that blocking connect()s will become the default (and non-blocking connect()s will become optional) in the next release. *) Dump a compile time error if the version of Apache is too old. *) Wrap the SIGPIPE handler manipulation code such that it is only applicable to Apache releases prior to 1.3.6. *) Minor tweaks for RUSSIAN_APACHE. ["Sergey Gershtein" ] *) Dynamic updates: Always restart a failed dynamic application if it is the last instance. This means there once started there will always be at least one process instance of a dynamic application. Send PLEASE_START to the PM when a connect() results in an ECONNREFUSED. ECONNREFUSED means the listen queue is full (or there isn't one). Asking the PM to start (another) application instance may help empty it faster. Change two sleep() calls to select() based snoozes because alarm() is in effect and sleep() and alarm() don't always play nice together. Fixed a couple of error messages. *) Fix -listen-queue-depth arg on FastCgiConfig (dynamic). Previously it was ignored and the default was always used. *) Allow the -initial-env argument to be used to pass variables from the Apache process environment to the FastCGI server (by specifying a variable name without the "=" or a value). Suggested by ["Martin Lichtin" ] *) Cleanup some debug macros. *) Improved script stderr handling. Based on suggestions by ["David Birnbaum" ] *) Added IRIX and FreeBSD to the list of supported platforms and other minor updates to the INSTALL doc. *) Changed the default listen-queue-depth (FCGI_DEFAULT_LISTEN_Q) from 5 to 100. Its still configurable with the -listen-queue-depth option. This should help eliminate the FAQ - Why do I see "Connection Refused" messages in the log? *) Fix a bug in FastCgiExternalServer that broke support for external servers on other hosts. ["Dave Neuer" ] Changes with mod_fastcgi 2.2.1 *) Updates to the INSTALL doc to describe building as a DSO. *) If the FastCgiIpcDir directive was in use and "httpd -t" was issued while Apache was running ("apachectl restart" does this), the contents of the dynamic directory (dynamic sockets and the mbox) would be blown away (a 2.1b1 bug). *) Add an extern declaration for ap_sys_siglist (its exposed but not in any Apache header file) to prevent a compile error on systems without SYS_SIGLIST defined. *) During auth requests, the subprocess_env table was left holding variables sent to the auth FastCGI server (including REMOTE_PASSWD!) which means they were passed to other processing phases (such as CGI/FastCGI). The subprocess_env is now restored to its pre-auth condition. *) Added a FastCGI Authorizer Role compatibility mode which implements the specification to the tee. Use -compat arg with any of the Auth server directives. This is intended for new and existing FastCGI authorizer applications that require compatibility with other server implementations. *) Fix logging from the process manager. Previously, all calls to the logging routines used NULL for the server_rec. This works fine in a dev environment, but results in most of these messages being tossed by log_error_core() because DEFAULT_LOGLEVEL is too low. Odd logic. *) Always setup fr->header so that in the event the FastCGI server doesn't get a valid FastCGI protocol header over the wire, we can still print our error message to the log from do_work(). *) Fix a compile error in open_connection_to_fs() on systems with TCP_NODELAY defined. ["Don Locrasto" ] Changes with mod_fastcgi 2.2.0 *) Serious rewrite of mod_fastcgi.html. An example conf will have to wait until another time - I'm burned out on docs updates. *) Add to the mod_fastcgi to the server version string. *) SCO doesn't like the const in the arg to inet_addr() and gethostbyname(). *) Use Apache's NET_SIZE_T to get the correct arg type for getsockopt(). *) Deleted redundant header files from fcgi.h. *) Full path names are no longer required for directives which take file names as arguments. Like other Apache directives you can specify paths from server_root (don't start with a "/"). *) The fcgiKillMgr is gone. This intermediate process sat between the Apache parent and the FastCGI Process Manager under Apache 1.2. The process manager tries to name itself fcgi_pm (it used to be fcgiProcMgr). *) Authentication, Authorization, and Access phases are now supported. I've bent the FastCgi spec a bit: as many of the standard environment variables (that were easy) are sent (the spec says don't send some by name), all headers (except Status) sent by the auth FastCgi server are passed to subprocesses (CGI/FastCGI invocations) as environment variables rather than just those prefixed by "Variable-". Custom responses for auth failures aren't supported (yet.. we'll see what the demand for this is like). In addition to the FastCGI protocol defined environment variable "FCGI_ROLE" being set to "AUTHORIZER" an environment variable "FCGI_APACHE_ROLE" is set indicating which of the three phases is being processed. See the docs for info on the new per-directory directives: FastCgiAuthenticator, FastCgiAuthenticatorAuthoritative, FastCgiAuthorizer, FastCgiAuthorizerAuthoritative, FastCgiAccessChecker, FastCgiAccessCheckerAuthoritative *) The code has been broken into logical chunks making figuring it out much easier. All of the #defines a user may want to change are now in mod_fastcgi.h. *) All the logging has been converted to the "supported" Apache logging routines, ap_log_error() and ap_log_rerror(). Log entries for request specific errors should now be directed to the correct server errorlog (if your running more than one). Some attempt has been made at setting appropriate log levels (comments of course are welcome) and printing errno info when its of value. All of log messages now have "FastCGI" in them (nice for grep). *) All of the calls to Apache routines have been updated to the ap_ convention, eliminating the need for the compat header. *) By default we no longer flush every piece of data we get from the FastCGI server to the client. This allows the FastCGI server to release without having to wait for the client flush to complete. The old behaviour can revived with the -flush argument to AppClass. *) I renamed some of the directives for consistency (the old names still work). AppClass -> FastCgiServer, FCGIConfig -> FastCgiConfig, ExternalAppClass -> FastCgiExternalServer *) Directive arguments are no longer case sensitive. *) The module now uses hard_timeout() rather than soft_timeout. This means that the module will longjump out of the request if the drop-dead timeout expires (set with Apache's Timeout directive, the default is 5min) or if the client closes the connection (SIGPIPE). This is more typical for a Apache modules and I'm not convinced we properly handle all of the error cases well enough to use soft_timeout (I can think of one place we don't anyway). This means your FastCGI application can see SIGPIPE. *) SIGPIPE is now ignored by default in FastCGI servers spawned by Apache. Without this the default behaviour is exit(). *) It should (I think) run now under DSO now. *) The bug which prevented sending of binary data in 2.1b1 is fixed. *) The broken -initial-env bug ins 2.1b1 is fixed. *) Maybe some other stuff. *** Well there's a big gap here. Maybe I'll go back and extract the CVS commit notes and stick 'em in here.. when I get some free time. ;) --robs (8 Feb 99) *** Originally from docs/README.. Apache[X1.03.01]/mod_fastcgi[02.00.05] 19970909 unsupported From: "Stanley Gambarin" *) Yet more changes to the source distribution. Separated out the Tcl dynamic string and buffer libraries into separate files. Did the same for the OS library, however, it is not really an abstraction... more like a bunch of wrappers. Added to DEVNOTES a note about the problem of Apache not allowing including header file more than once. Apache[X1.03.01]/mod_fastcgi[02.00.04] 19970908 unsupported From: "Stanley Gambarin" *) More changes to the installation scripts. Theoretically we should be able to to use the same Makefile/installation script for both Apache 1.2.x and 1.3.x. Need some sort of the abstraction layer to use the same source code for both 1.2.x and 1.3.x sources. Apache[X1.03.01]/mod_fastcgi[02.00.03] 19970905 unsupported From: "Stanley Gambarin" *) Created Makefile to compile the module into the archive library, just like the proxy module. Also initial draft of the installation script, which should work for both 1.2 and 1.3 sources of Apache. Apache[01.02.04]/mod_fastcgi[02.00.02] 19970903 unsupported From: "Stanley Gambarin" *) More source reorg. Separated out some header files. Need much more work on this. Updated the TODO file. Added fcgivers.h and CHANGES file to track the history. Apache[01.02.04]/mod_fastcgi[02.00.01] 19970902 unsupported From: "Stanley Gambarin" *) Source reorganization. This is done to provide a basis for later functionality implementation. See TODO file for more information of what possible future enhancements are possible/desired. Apache[01.02.04]/mod_fastcgi[02.00.00] 19970902 unsupported From: "David MacKenzie" *) Create the "dynamic" dir and "mbox" as the user specified in the User and Group directives instead of as root. They are created with restrictive permissions, and then the module checks to see whether the user specified in the User and Group directives has read, write, and execute permissions on them. Those permissions had been explicitly denied by creating them as "root". *) Some documentation was garbled or incomplete. *) When a FastCGI application can't be execl'd, the code used a value of errno that may have been stomped on by intervening system calls. That happened on BSD/OS 2.1, where the error_log was reporting errno=25 (NOTTY) instead of the correct errno=13 (EACCES). An intervening stdio call was setting errno as a side-effect, which it has the right to do. *) Erroneous fprintf arguments, a missing return value, an unused function, missing declarations, and other problems detected by gcc -Wall. *) Duplicated code to create the lock file name merged into a single function, closing several memory leaks. *) FCGIConfig -minProcesses didn't allow a value of 0, so the last FCGI app in a given dynamic class would live forever, even if it hadn't been requested for weeks. *) The test for whether to keep looking for dynamic app victims to kill was backwards, so no dynamic apps were ever selected as victims. *) The killInterval and updateInterval were being ignored; the sigsuspend() forced recalculations only when a child died, instead of at the intervals specified. *) When a dynamic app couldn't be started, the program SEGV'd when trying to free the ipcAddrPtr twice. *) Remove the dead lock file and socket when cleaning up after a server whose last child has been killed, so the FastCGIHandler isn't fooled into thinking there is still a process serving that app. *) Make file locking robust in the presence of signals. *) Rename some badly named variables. *) Fix typos in many comments. *) Fix some memory and file descriptor leaks. *) Make the blocking kill of a server closer to working. *) More fixes to calculations in dynamic application management. *** Originally from docs/README.OMI.. What's New: Version 2.0b1, 16 Apr 1997 *) Implemented a mechanism by which FastCGI applications are started by the web server on the first request and continue running. Also, a heuristics have been implemented to allow for dynamic killing of the running FastCGI apps. The configuration options are supplied via the new FCGIConfig directive, that is described in the mod_fastcgi.html *) When performing internal redirect, since the original request's body has already been read, do not allow the redirected request to think that is has one [body]. *) More conditional compilation for OS/2. *) Fixed occassional "Assertion failed: len > 0, file mod_fastcgi.c" *) Fixed "failed assertion `count >= 0 && count <= bufPtr->length'" *) Added bflush() call after bwrite() in DrainReqOutBuf function to force some output to be written in the period of inactivity. This circumvents the Apache's buffering during the server-push. *) Added a header-parser function placeholder for compatibility with an Apache source code. *) Additional checks have been placed with regard to permissions of the FastCGI processes. These include the existence of the file, ExecCGI and IncludesNOEXEC checks, disallowing of nph- scripts, etc. *) Implemented a restart cleanup, so that no parentlesses FastCGI applications are left after Apache is restarted and/or terminated. This was accomplished via two independent mechanisms, where the first one prevents a start of the process manager on the first reading of the configuration files. The second mechanism deals away from Apache's implementation of fork() (via spawn_child()) and implements its own forking, thus removing any dependencies on the Apache to cleanup processes during termination phase. *) Removed the definition of the Sigfunc, as Apache 2.0b10 defines it itself in the file conf.h What's New: Version 1.4.3, 15 Jan 1997 *) Fixed compilation warnings for various platforms, as well as conditional compilation for OS/2. What's New: Version 1.4.2, 12 Dec 1996 *) mod_fastcgi.c is ported to Apache 1.2b. Any further development will proceed under this version of Apache web server. *) As the result of porting, the "include virtual" construct of SSI will now work correctly using either or directive. What's New: Version 1.4.1, 4 Dec 1996 *) Checks have been removed from the ScanCGIHeaders that provided for the presence of both Status and Location headers as being an error. Contradictory to CGI/1.1 Internet Draft, both of these headers are used by the current CGI applications. What's New: Version 1.4, 22 Nov 1996 *) Added the -port option to AppClass, allowing TCP/IP communication. Added the -socket option to AppClass, allowing Unix domain communication via a configurable pathname. *) Added the ExternalAppClass directive, allowing TCP/IP communication with remote FastCGI applications. *) The handler had its own code for generating HTTP response headers; now it uses Apache's. This reduces the size of the module. More importantly, it fixes the bug in which "include virtual" sees the HTTP response headers. *) The response header parser performed very little checking. Now the parser enforces the guidelines in the CGI/1.1 Internet-Draft: Status and Location are mutually exclusive, Location can only be a response to GET or HEAD, CGI response headers can't be repeated, etc. (The CGI response headers are the ones the handler interprets: Status, Location, and Content-type.) *) The response header parser used to miss CGI headers with no whitespace after the colon, e.g. "Status:200 OK". *) The response header parser sometimes interpreted the first line of content as an RFC822 continuation line. *) The handler implemented a nonstandard version of Location which never used internal redirects. The handler now attempts to implement Location as specified in the CGI/1.1 Internet-Draft. The module documentation explains the new behavior. *) Error log entries from the response header parser were pretty uninformative; they are better now. If a header is malformed, the log entry includes it. If the headers are unterminated rather than malformed, the log entry says that, and says how many bytes were received from the app. *) When the application manager forked a new process, and that process ran into trouble before executing the first instruction of application code, the process used to exit with status = errno. This made certain configuration problems (e.g. incorrect file permissions when server parent is root) quite difficult to pin down. Now the failing child process opens up the error log and writes an informative entry before exiting. *) The module now correctly handles a slash at the end of the DocumentRoot directive. This was a one-line fix. What's New: Version 1.3.3, 17 Oct 1996 *) The module now registers its request handler under the name fastcgi-script in addition to the name application/x-httpd-fcgi. This was a one-line addition to the module, but had quite a large impact on the documentation and sample configuration. What's New: Version 1.3.2, 27 Sept 1996 *) On some systems (SunOS, Linux?), fopen for append has a bug that strikes when two processes append to the same file. This bug causes the process manager to corrupt the error log. Work around the bug by calling open, then fdopen. A patch to Apache 1.1.1 is also required, as described in mod_fastcgi.html. (Reported by Bob Ramstad.) What's New: Version 1.3.1, 17 Sept 1996 *) On Linux and SunOS, the Apache default user_id (-1) is not a legal value of uid_t, so cast it. (Reported by Bob Ramstad, Scott Langley, others.) *) On some systems (Linux, some Solaris?, Irix?), connect requires write access to a Unix Domain socket, so provide it. (Reported by Scott Langley, , others.) *) If you hit a .fcg file, but there's no AppClass defined, the error should be NOT_FOUND instead of SERVER_ERROR. (Reported by Michael Smith.) What's New: Version 1.3, 4 Sept 1996 *) Module sometimes busy-waited in FastCgiDoWork. Fixed. *) Module violated the Apache buff abstraction. Fixed. Might now work with SSL (not tested). As part of the fix, eliminated support for Apache 1.0x versions. *) Module failed to chmod the Unix domain listening sockets it created, so protections were set according to the current umask. Fixed. *) Module forked too many process manager processes: One per AppClass. When the Apache parent ran as root, these process manager processes ran as root. New process manager is a single process (only started if AppClass is used) and runs with same privileges as other children. The new process manager doesn't do any polling, so there's less system overhead than before. *) AppClass insisted on getting at least two arguments. Fixed. *) Module used setjmp/longjmp, causing compiler warnings on some platforms (e.g. Linux.) The module no longer uses setjmp/longjmp. *) Module created listening sockets in /tmp, where they were sometimes wiped out by cleanup scripts. Added FastCgiIpcDir directive to give control over the location of listening sockets. *) Module wrote error log entries without a timestamp. Fixed. *) AppClass directive wrote error messages to stderr in addition to returning a char * message to the Apache core. Fixed. What's New: Version 1.2, 3 June 1996 *) Ported from Apache-1.0-based code to Apache-1.1b2 internals by ["Ralf S. Engelschall" ] Add version string: APACHEVERSION macro to mod_fastcgi.c *) chown FastCGI socket to user_id and setuid to user_id for app class processes *) Modify GetFromStream() by having it call an OS dependent function GetStreamSize(FILE *) which uses FILE internal data member. Linux users might need to modify GetStreamSize(). What's New: Version 1.1, 10 May 1996 *) If you specify a non-existent executable in the AppClass directive, or if the file exists and it does not have execute permission you get a constant stream of error messages telling you that "program terminated due to a signal". *) The mod_fastcgi module should use the standard Apache error logging facility instead of writing to stderr. Version 1.0, 30 April 1996 mod_fastcgi-SNAP-0910052141/fcgi_config.c0000664000635400022240000011565511260126412020164 0ustar robsfastcgi00000000000000/* * $Id: fcgi_config.c,v 1.54 2009/09/28 12:33:14 robs Exp $ */ #define CORE_PRIVATE #include "fcgi.h" #ifdef APACHE2 #include #include "mpm_common.h" /* ap_uname2id, ap_gname2id */ #ifdef WIN32 #include #else #include #include "unixd.h" #endif #endif #ifdef WIN32 /* warning C4100: unreferenced formal parameter */ /* warning C4706: assignment within conditional expression */ #pragma warning( disable : 4100 4706 ) #endif /******************************************************************************* * Get the next configuration directive argument, & return an in_addr and port. * The arg must be in the form "host:port" where host can be an IP or hostname. * The pool arg should be persistant storage. */ static const char *get_host_n_port(pool *p, const char **arg, const char **host, u_short *port) { char *cvptr, *portStr; long tmp; *host = ap_getword_conf(p, arg); if (**host == '\0') return "\"\""; portStr = strchr(*host, ':'); if (portStr == NULL) return "missing port specification"; /* Split the host and port portions */ *portStr++ = '\0'; /* Convert port number */ tmp = (u_short) strtol(portStr, &cvptr, 10); if (*cvptr != '\0' || tmp < 1 || tmp > USHRT_MAX) return ap_pstrcat(p, "bad port number \"", portStr, "\"", NULL); *port = (unsigned short) tmp; return NULL; } /******************************************************************************* * Get the next configuration directive argument, & return an u_short. * The pool arg should be temporary storage. */ static const char *get_u_short(pool *p, const char **arg, u_short *num, u_short min) { char *ptr; long tmp; const char *txt = ap_getword_conf(p, arg); if (*txt == '\0') { return "\"\""; } tmp = strtol(txt, &ptr, 10); if (*ptr != '\0') { return ap_pstrcat(p, "\"", txt, "\" must be a positive integer", NULL); } if (tmp < min || tmp > USHRT_MAX) { return ap_psprintf(p, "\"%u\" must be >= %u and < %u", *num, min, USHRT_MAX); } *num = (u_short) tmp; return NULL; } static const char *get_int(pool *p, const char **arg, int *num, int min) { char *cp; const char *val = ap_getword_conf(p, arg); if (*val == '\0') { return "\"\""; } *num = (int) strtol(val, &cp, 10); if (*cp != '\0') { return ap_pstrcat(p, "can't parse ", "\"", val, "\"", NULL); } else if (*num < min) { return ap_psprintf(p, "\"%d\" must be >= %d", *num, min); } return NULL; } /******************************************************************************* * Get the next configuration directive argument, & return an u_int. * The pool arg should be temporary storage. */ static const char *get_u_int(pool *p, const char **arg, u_int *num, u_int min) { char *ptr; const char *val = ap_getword_conf(p, arg); if (*val == '\0') return "\"\""; *num = (u_int)strtol(val, &ptr, 10); if (*ptr != '\0') return ap_pstrcat(p, "\"", val, "\" must be a positive integer", NULL); else if (*num < min) return ap_psprintf(p, "\"%u\" must be >= %u", *num, min); return NULL; } /******************************************************************************* * Get the next configuration directive argument, & return a float. * The pool arg should be temporary storage. */ static const char *get_float(pool *p, const char **arg, float *num, float min, float max) { char *ptr; const char *val = ap_getword_conf(p, arg); if (*val == '\0') return "\"\""; *num = (float) strtod(val, &ptr); if (*ptr != '\0') return ap_pstrcat(p, "\"", val, "\" is not a floating point number", NULL); if (*num < min || *num > max) return ap_psprintf(p, "\"%f\" is not between %f and %f", *num, min, max); return NULL; } const char *fcgi_config_set_env_var(pool *p, char **envp, unsigned int *envc, char * var) { if (*envc >= MAX_INIT_ENV_VARS) { return "too many variables, must be <= MAX_INIT_ENV_VARS"; } if (strchr(var, '=') == NULL) { *(envp + *envc) = ap_pstrcat(p, var, "=", getenv(var), NULL); } else { *(envp + *envc) = var; } (*envc)++; return NULL; } /******************************************************************************* * Get the next configuration directive argument, & add it to an env array. * The pool arg should be permanent storage. */ static const char *get_env_var(pool *p, const char **arg, char **envp, unsigned int *envc) { char * const val = ap_getword_conf(p, arg); if (*val == '\0') { return "\"\""; } return fcgi_config_set_env_var(p, envp, envc, val); } static const char *get_pass_header(pool *p, const char **arg, array_header **array) { const char **header; if (!*array) { *array = ap_make_array(p, 10, sizeof(char*)); } header = (const char **)ap_push_array(*array); *header = ap_getword_conf(p, arg); return header ? NULL : "\"\""; } /******************************************************************************* * Return a "standard" message for common configuration errors. */ static const char *invalid_value(pool *p, const char *cmd, const char *id, const char *opt, const char *err) { return ap_psprintf(p, "%s%s%s: invalid value for %s: %s", cmd, id ? " " : "", id ? id : "", opt, err); } /******************************************************************************* * Set/Reset the uid/gid that Apache and the PM will run as. This is ap_user_id * and ap_group_id if we're started as root, and euid/egid otherwise. Also try * to check that the config files don't set the User/Group after a FastCGI * directive is used that depends on it. */ /*@@@ To be complete, we should save a handle to the server each AppClass is * configured in and at init() check that the user/group is still what we * thought it was. Also the other directives should only be allowed in the * parent Apache server. */ const char *fcgi_config_set_fcgi_uid_n_gid(int set) { static int isSet = 0; #ifndef WIN32 uid_t uid = geteuid(); gid_t gid = getegid(); if (set == 0) { isSet = 0; fcgi_user_id = (uid_t)-1; fcgi_group_id = (gid_t)-1; return NULL; } if (uid == 0) { uid = ap_user_id; } if (gid == 0) { gid = ap_group_id; } if (isSet && (uid != fcgi_user_id || gid != fcgi_group_id)) { return "User/Group commands must preceed FastCGI server definitions"; } isSet = 1; fcgi_user_id = uid; fcgi_group_id = gid; #endif /* !WIN32 */ return NULL; } apcb_t fcgi_config_reset_globals(void* dummy) { fcgi_config_pool = NULL; fcgi_servers = NULL; fcgi_config_set_fcgi_uid_n_gid(0); fcgi_wrapper = NULL; fcgi_socket_dir = NULL; fcgi_dynamic_total_proc_count = 0; fcgi_dynamic_epoch = 0; fcgi_dynamic_last_analyzed = 0; dynamicMaxProcs = FCGI_DEFAULT_MAX_PROCS; dynamicMinProcs = FCGI_DEFAULT_MIN_PROCS; dynamicMaxClassProcs = FCGI_DEFAULT_MAX_CLASS_PROCS; dynamicKillInterval = FCGI_DEFAULT_KILL_INTERVAL; dynamicUpdateInterval = FCGI_DEFAULT_UPDATE_INTERVAL; dynamicGain = FCGI_DEFAULT_GAIN; dynamicThreshold1 = FCGI_DEFAULT_THRESHOLD_1; dynamicThresholdN = FCGI_DEFAULT_THRESHOLD_N; dynamicPleaseStartDelay = FCGI_DEFAULT_START_PROCESS_DELAY; dynamicAppConnectTimeout = FCGI_DEFAULT_APP_CONN_TIMEOUT; dynamicEnvp = &fcgi_empty_env; dynamicProcessSlack = FCGI_DEFAULT_PROCESS_SLACK; dynamicAutoRestart = FCGI_DEFAULT_RESTART_DYNAMIC; dynamicAutoUpdate = FCGI_DEFAULT_AUTOUPDATE; dynamicListenQueueDepth = FCGI_DEFAULT_LISTEN_Q; dynamicInitStartDelay = DEFAULT_INIT_START_DELAY; dynamicRestartDelay = FCGI_DEFAULT_RESTART_DELAY; dynamicMinServerLife = FCGI_DEFAULT_MIN_SERVER_LIFE; dynamic_pass_headers = NULL; dynamic_idle_timeout = FCGI_DEFAULT_IDLE_TIMEOUT; dynamicFlush = FCGI_FLUSH; #ifndef WIN32 /* Close any old pipe (HUP/USR1) */ if (fcgi_pm_pipe[0] != -1) { close(fcgi_pm_pipe[0]); fcgi_pm_pipe[0] = -1; } if (fcgi_pm_pipe[1] != -1) { close(fcgi_pm_pipe[1]); fcgi_pm_pipe[1] = -1; } #endif return APCB_OK; } /******************************************************************************* * Create a directory to hold Unix/Domain sockets. */ const char *fcgi_config_make_dir(pool *tp, char *path) { struct stat finfo; const char *err = NULL; /* Is the directory spec'd correctly */ if (*path != '/') { return "path is not absolute (it must start with a \"/\")"; } else { int i = strlen(path) - 1; /* Strip trailing "/"s */ while(i > 0 && path[i] == '/') path[i--] = '\0'; } /* Does it exist? */ if (stat(path, &finfo) != 0) { /* No, but maybe we can create it */ #ifdef WIN32 if (mkdir(path) != 0) #else if (mkdir(path, S_IRWXU) != 0) #endif { return ap_psprintf(tp, "doesn't exist and can't be created: %s", strerror(errno)); } #ifndef WIN32 /* If we're root, we're gonna setuid/setgid so we need to chown */ if (geteuid() == 0 && chown(path, ap_user_id, ap_group_id) != 0) { return ap_psprintf(tp, "can't chown() to the server (uid %ld, gid %ld): %s", (long)ap_user_id, (long)ap_group_id, strerror(errno)); } #endif } else { /* Yes, is it a directory? */ if (!S_ISDIR(finfo.st_mode)) return "isn't a directory!"; /* Can we RWX in there? */ #ifdef WIN32 err = fcgi_util_check_access(tp, NULL, &finfo, _S_IREAD | _S_IWRITE | _S_IEXEC, fcgi_user_id, fcgi_group_id); #else err = fcgi_util_check_access(tp, NULL, &finfo, R_OK | W_OK | X_OK, fcgi_user_id, fcgi_group_id); #endif if (err != NULL) { return ap_psprintf(tp, "access for server (uid %ld, gid %ld) failed: %s", (long)fcgi_user_id, (long)fcgi_group_id, err); } } return NULL; } /******************************************************************************* * Create a "dynamic" subdirectory. If the directory * already exists we don't mess with it unless 'wax' is set. */ #ifndef WIN32 const char *fcgi_config_make_dynamic_dir(pool *p, const int wax) { const char *err; pool *tp; fcgi_dynamic_dir = ap_pstrcat(p, fcgi_socket_dir, "/dynamic", NULL); if ((err = fcgi_config_make_dir(p, fcgi_dynamic_dir))) return ap_psprintf(p, "can't create dynamic directory \"%s\": %s", fcgi_dynamic_dir, err); /* Don't step on a running server unless its OK. */ if (!wax) return NULL; #ifdef APACHE2 { apr_dir_t * dir; apr_finfo_t finfo; if (apr_pool_create(&tp, p)) return "apr_pool_create() failed"; if (apr_dir_open(&dir, fcgi_dynamic_dir, tp)) return "apr_dir_open() failed"; /* delete the contents */ while (apr_dir_read(&finfo, APR_FINFO_NAME, dir) == APR_SUCCESS) { if (strcmp(finfo.name, ".") == 0 || strcmp(finfo.name, "..") == 0) continue; apr_file_remove(finfo.name, tp); } } #else /* !APACHE2 */ { DIR *dp; struct dirent *dirp = NULL; tp = ap_make_sub_pool(p); dp = ap_popendir(tp, fcgi_dynamic_dir); if (dp == NULL) { ap_destroy_pool(tp); return ap_psprintf(p, "can't open dynamic directory \"%s\": %s", fcgi_dynamic_dir, strerror(errno)); } /* delete the contents */ while ((dirp = readdir(dp)) != NULL) { if (strcmp(dirp->d_name, ".") == 0 || strcmp(dirp->d_name, "..") == 0) continue; unlink(ap_pstrcat(tp, fcgi_dynamic_dir, "/", dirp->d_name, NULL)); } } #endif /* !APACHE2 */ ap_destroy_pool(tp); return NULL; } #endif /******************************************************************************* * Change the directory used for the Unix/Domain sockets from the default. * Create the directory and the "dynamic" subdirectory. */ const char *fcgi_config_set_socket_dir(cmd_parms *cmd, void *dummy, const char *arg) { pool * const tp = cmd->temp_pool; const char * const name = cmd->cmd->name; const char *err; char * arg_nc; err = ap_check_cmd_context(cmd, GLOBAL_ONLY); if (err) { return err; } if (fcgi_socket_dir) { return ap_psprintf(tp, "%s %s: already defined as \"%s\"", name, arg, fcgi_socket_dir); } err = fcgi_config_set_fcgi_uid_n_gid(1); if (err != NULL) return ap_psprintf(tp, "%s %s: %s", name, arg, err); if (fcgi_servers != NULL) { return ap_psprintf(tp, "The %s command must preceed static FastCGI server definitions", name); } arg_nc = ap_pstrdup(cmd->pool, arg); #ifndef WIN32 #ifdef APACHE2 if (apr_filepath_merge(&arg_nc, "", arg, 0, cmd->pool)) return ap_psprintf(tp, "%s %s: invalid filepath", name, arg); #else arg_nc = ap_os_canonical_filename(cmd->pool, arg_nc); #endif arg_nc = ap_server_root_relative(cmd->pool, arg_nc); #else /* WIN32 */ if (strncmp(arg_nc, "\\\\.\\pipe\\", 9) != 0) return ap_psprintf(tp, "%s %s is invalid format",name, arg_nc); #endif fcgi_socket_dir = arg_nc; #ifdef WIN32 fcgi_dynamic_dir = ap_pstrcat(cmd->pool, fcgi_socket_dir, "dynamic", NULL); #else err = fcgi_config_make_dir(tp, fcgi_socket_dir); if (err != NULL) return ap_psprintf(tp, "%s %s: %s", name, arg_nc, err); err = fcgi_config_make_dynamic_dir(cmd->pool, 0); if (err != NULL) return ap_psprintf(tp, "%s %s: %s", name, arg_nc, err); #endif return NULL; } /******************************************************************************* * Enable, disable, or specify the path to a wrapper used to invoke all * FastCGI applications. */ const char *fcgi_config_set_wrapper(cmd_parms *cmd, void *dummy, const char *arg) { #ifdef WIN32 return ap_psprintf(cmd->temp_pool, "the %s directive is not supported on WIN", cmd->cmd->name); #else const char *err = NULL; const char * const name = cmd->cmd->name; pool * const tp = cmd->temp_pool; char * wrapper = NULL; err = ap_check_cmd_context(cmd, GLOBAL_ONLY); if (err) { return err; } if (fcgi_wrapper) { return ap_psprintf(tp, "%s was already set to \"%s\"", name, fcgi_wrapper); } err = fcgi_config_set_fcgi_uid_n_gid(1); if (err != NULL) return ap_psprintf(tp, "%s %s: %s", name, arg, err); if (fcgi_servers != NULL) { return ap_psprintf(tp, "The %s command must preceed static FastCGI server definitions", name); } if (strcasecmp(arg, "Off") == 0) { fcgi_wrapper = NULL; return NULL; } if (strcasecmp(arg, "On") == 0) { wrapper = SUEXEC_BIN; } else { #ifdef APACHE2 if (apr_filepath_merge(&wrapper, "", arg, 0, cmd->pool)) return ap_psprintf(tp, "%s %s: invalid filepath", name, arg); #else wrapper = ap_os_canonical_filename(cmd->pool, (char *) arg); #endif wrapper = ap_server_root_relative(cmd->pool, wrapper); } err = fcgi_util_check_access(tp, wrapper, NULL, X_OK, fcgi_user_id, fcgi_group_id); if (err) { return ap_psprintf(tp, "%s: \"%s\" execute access for server " "(uid %ld, gid %ld) failed: %s", name, wrapper, (long) fcgi_user_id, (long) fcgi_group_id, err); } fcgi_wrapper = wrapper; return NULL; #endif /* !WIN32 */ } /******************************************************************************* * Configure a static FastCGI server. */ const char *fcgi_config_new_static_server(cmd_parms *cmd, void *dummy, const char *arg) { fcgi_server *s; pool *p = cmd->pool, *tp = cmd->temp_pool; const char *name = cmd->cmd->name; char *fs_path = ap_getword_conf(p, &arg); const char *option, *err; /* Allocate temp storage for the array of initial environment variables */ char **envp = ap_pcalloc(tp, sizeof(char *) * (MAX_INIT_ENV_VARS + 3)); unsigned int envc = 0; #ifdef WIN32 HANDLE mutex; #endif err = ap_check_cmd_context(cmd, NOT_IN_LIMIT|NOT_IN_DIR_LOC_FILE); if (err) { return err; } if (*fs_path == '\0') return "AppClass requires a pathname!?"; if ((err = fcgi_config_set_fcgi_uid_n_gid(1)) != NULL) return ap_psprintf(tp, "%s %s: %s", name, fs_path, err); #ifdef APACHE2 if (apr_filepath_merge(&fs_path, "", fs_path, 0, p)) return ap_psprintf(tp, "%s %s: invalid filepath", name, fs_path); #else fs_path = ap_os_canonical_filename(p, fs_path); #endif fs_path = ap_server_root_relative(p, fs_path); ap_getparents(fs_path); ap_no2slash(fs_path); /* See if we've already got one of these configured */ s = fcgi_util_fs_get_by_id(fs_path, fcgi_util_get_server_uid(cmd->server), fcgi_util_get_server_gid(cmd->server)); if (s != NULL) { if (fcgi_wrapper) { return ap_psprintf(tp, "%s: redefinition of a previously defined FastCGI " "server \"%s\" with uid=%ld and gid=%ld", name, fs_path, (long) fcgi_util_get_server_uid(cmd->server), (long) fcgi_util_get_server_gid(cmd->server)); } else { return ap_psprintf(tp, "%s: redefinition of a previously defined FastCGI server \"%s\"", name, fs_path); } } err = fcgi_util_fs_is_path_ok(tp, fs_path, NULL); if (err != NULL) { return ap_psprintf(tp, "%s: \"%s\" %s", name, fs_path, err); } s = fcgi_util_fs_new(p); s->fs_path = fs_path; s->directive = APP_CLASS_STANDARD; s->restartOnExit = TRUE; s->numProcesses = 1; #ifdef WIN32 /* TCP FastCGI applications require SystemRoot be present in the environment * Put it in both for consistency to the application */ fcgi_config_set_env_var(p, envp, &envc, "SystemRoot"); mutex = CreateMutex(NULL, FALSE, fs_path); if (mutex == NULL) { ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server, "FastCGI: CreateMutex() failed"); return "failed to create FastCGI application accept mutex"; } SetHandleInformation(mutex, HANDLE_FLAG_INHERIT, TRUE); s->mutex_env_string = ap_psprintf(p, "_FCGI_MUTEX_=%ld", mutex); #endif /* Parse directive arguments */ while (*arg) { option = ap_getword_conf(tp, &arg); if (strcasecmp(option, "-processes") == 0) { if ((err = get_u_int(tp, &arg, &s->numProcesses, 1))) return invalid_value(tp, name, fs_path, option, err); } else if (strcasecmp(option, "-restart-delay") == 0) { if ((err = get_u_int(tp, &arg, &s->restartDelay, 0))) return invalid_value(tp, name, fs_path, option, err); } else if (strcasecmp(option, "-init-start-delay") == 0) { if ((err = get_int(tp, &arg, &s->initStartDelay, 0))) return invalid_value(tp, name, fs_path, option, err); } else if (strcasecmp(option, "-min-server-life") == 0) { if ((err = get_u_int(tp, &arg, &s->minServerLife, 0))) return invalid_value(tp, name, NULL, option, err); } else if (strcasecmp(option, "-priority") == 0) { if ((err = get_u_int(tp, &arg, &s->processPriority, 0))) return invalid_value(tp, name, fs_path, option, err); } else if (strcasecmp(option, "-listen-queue-depth") == 0) { if ((err = get_u_int(tp, &arg, &s->listenQueueDepth, 1))) return invalid_value(tp, name, fs_path, option, err); } else if (strcasecmp(option, "-appConnTimeout") == 0) { if ((err = get_u_int(tp, &arg, &s->appConnectTimeout, 0))) return invalid_value(tp, name, fs_path, option, err); } else if (strcasecmp(option, "-idle-timeout") == 0) { if ((err = get_u_int(tp, &arg, &s->idle_timeout, 1))) return invalid_value(tp, name, fs_path, option, err); } else if (strcasecmp(option, "-port") == 0) { if ((err = get_u_short(tp, &arg, &s->port, 1))) return invalid_value(tp, name, fs_path, option, err); } else if (strcasecmp(option, "-socket") == 0) { s->socket_path = ap_getword_conf(tp, &arg); if (*s->socket_path == '\0') return invalid_value(tp, name, fs_path, option, "\"\""); } else if (strcasecmp(option, "-initial-env") == 0) { if ((err = get_env_var(p, &arg, envp, &envc))) return invalid_value(tp, name, fs_path, option, err); } else if (strcasecmp(option, "-pass-header") == 0) { if ((err = get_pass_header(p, &arg, &s->pass_headers))) return invalid_value(tp, name, fs_path, option, err); } else if (strcasecmp(option, "-flush") == 0) { s->flush = 1; } else if (strcasecmp(option, "-nph") == 0) { s->nph = 1; } else if (strcasecmp(option, "-user") == 0) { #ifdef WIN32 return ap_psprintf(tp, "%s %s: the -user option isn't supported on WIN", name, fs_path); #else s->user = ap_getword_conf(tp, &arg); if (*s->user == '\0') return invalid_value(tp, name, fs_path, option, "\"\""); #endif } else if (strcasecmp(option, "-group") == 0) { #ifdef WIN32 return ap_psprintf(tp, "%s %s: the -group option isn't supported on WIN", name, fs_path); #else s->group = ap_getword_conf(tp, &arg); if (*s->group == '\0') return invalid_value(tp, name, fs_path, option, "\"\""); #endif } else { return ap_psprintf(tp, "%s %s: invalid option: %s", name, fs_path, option); } } /* while */ #ifndef WIN32 if (fcgi_wrapper) { if (s->group == NULL) { s->group = ap_psprintf(tp, "#%ld", (long) fcgi_util_get_server_gid(cmd->server)); } if (s->user == NULL) { s->user = ap_psprintf(p, "#%ld", (long) fcgi_util_get_server_uid(cmd->server)); } s->uid = ap_uname2id(s->user); s->gid = ap_gname2id(s->group); } else if (s->user || s->group) { ap_log_error(FCGI_LOG_WARN, cmd->server, "FastCGI: there is no " "fastcgi wrapper set, user/group options are ignored"); } if ((err = fcgi_util_fs_set_uid_n_gid(p, s, s->uid, s->gid))) { return ap_psprintf(tp, "%s %s: invalid user or group: %s", name, fs_path, err); } #endif /* !WIN32 */ if (s->socket_path != NULL && s->port != 0) { return ap_psprintf(tp, "%s %s: -port and -socket are mutually exclusive options", name, fs_path); } /* Move env array to a surviving pool */ s->envp = (char **)ap_pcalloc(p, sizeof(char *) * (envc + 4)); memcpy(s->envp, envp, sizeof(char *) * envc); /* Initialize process structs */ s->procs = fcgi_util_fs_create_procs(p, s->numProcesses); /* Build the appropriate sockaddr structure */ if (s->port != 0) { err = fcgi_util_socket_make_inet_addr(p, (struct sockaddr_in **)&s->socket_addr, &s->socket_addr_len, NULL, s->port); if (err != NULL) return ap_psprintf(tp, "%s %s: %s", name, fs_path, err); #ifdef WIN32 err = fcgi_util_socket_make_inet_addr(p, (struct sockaddr_in **)&s->dest_addr, &s->socket_addr_len, "localhost", s->port); if (err != NULL) return ap_psprintf(tp, "%s %s: %s", name, fs_path, err); #endif } else { if (s->socket_path == NULL) s->socket_path = fcgi_util_socket_hash_filename(tp, fs_path, s->user, s->group); if (fcgi_socket_dir == NULL) { #ifdef WIN32 fcgi_socket_dir = DEFAULT_SOCK_DIR; #else fcgi_socket_dir = ap_server_root_relative(p, DEFAULT_SOCK_DIR); #endif } s->socket_path = fcgi_util_socket_make_path_absolute(p, s->socket_path, 0); #ifndef WIN32 err = fcgi_util_socket_make_domain_addr(p, (struct sockaddr_un **)&s->socket_addr, &s->socket_addr_len, s->socket_path); if (err != NULL) return ap_psprintf(tp, "%s %s: %s", name, fs_path, err); #endif } /* Add it to the list of FastCGI servers */ fcgi_util_fs_add(s); return NULL; } /******************************************************************************* * Configure a static FastCGI server that is started/managed elsewhere. */ const char *fcgi_config_new_external_server(cmd_parms *cmd, void *dummy, const char *arg) { fcgi_server *s; pool * const p = cmd->pool, *tp = cmd->temp_pool; const char * const name = cmd->cmd->name; char *fs_path = ap_getword_conf(p, &arg); const char *option, *err; err = ap_check_cmd_context(cmd, NOT_IN_LIMIT|NOT_IN_DIR_LOC_FILE); if (err) { return err; } if (!*fs_path) { return ap_pstrcat(tp, name, " requires a path and either a -socket or -host option", NULL); } #ifdef APACHE2 if (apr_filepath_merge(&fs_path, "", fs_path, 0, p)) return ap_psprintf(tp, "%s %s: invalid filepath", name, fs_path); #else fs_path = ap_os_canonical_filename(p, fs_path); #endif fs_path = ap_server_root_relative(p, fs_path); ap_getparents(fs_path); ap_no2slash(fs_path); /* See if we've already got one of these bettys configured */ s = fcgi_util_fs_get_by_id(fs_path, fcgi_util_get_server_uid(cmd->server), fcgi_util_get_server_gid(cmd->server)); if (s != NULL) { if (fcgi_wrapper) { return ap_psprintf(tp, "%s: redefinition of a previously defined class \"%s\" " "with uid=%ld and gid=%ld", name, fs_path, (long) fcgi_util_get_server_uid(cmd->server), (long) fcgi_util_get_server_gid(cmd->server)); } else { return ap_psprintf(tp, "%s: redefinition of previously defined class \"%s\"", name, fs_path); } } s = fcgi_util_fs_new(p); s->fs_path = fs_path; s->directive = APP_CLASS_EXTERNAL; /* Parse directive arguments */ while (*arg != '\0') { option = ap_getword_conf(tp, &arg); if (strcasecmp(option, "-host") == 0) { if ((err = get_host_n_port(p, &arg, &s->host, &s->port))) return invalid_value(tp, name, fs_path, option, err); } else if (strcasecmp(option, "-socket") == 0) { s->socket_path = ap_getword_conf(tp, &arg); if (*s->socket_path == '\0') return invalid_value(tp, name, fs_path, option, "\"\""); } else if (strcasecmp(option, "-appConnTimeout") == 0) { if ((err = get_u_int(tp, &arg, &s->appConnectTimeout, 0))) return invalid_value(tp, name, fs_path, option, err); } else if (strcasecmp(option, "-idle-timeout") == 0) { if ((err = get_u_int(tp, &arg, &s->idle_timeout, 1))) return invalid_value(tp, name, fs_path, option, err); } else if (strcasecmp(option, "-nph") == 0) { s->nph = 1; } else if (strcasecmp(option, "-pass-header") == 0) { if ((err = get_pass_header(p, &arg, &s->pass_headers))) return invalid_value(tp, name, fs_path, option, err); } else if (strcasecmp(option, "-flush") == 0) { s->flush = 1; } else if (strcasecmp(option, "-user") == 0) { #ifdef WIN32 return ap_psprintf(tp, "%s %s: the -user option isn't supported on WIN", name, fs_path); #else s->user = ap_getword_conf(tp, &arg); if (*s->user == '\0') return invalid_value(tp, name, fs_path, option, "\"\""); #endif } else if (strcasecmp(option, "-group") == 0) { #ifdef WIN32 return ap_psprintf(tp, "%s %s: the -group option isn't supported on WIN", name, fs_path); #else s->group = ap_getword_conf(tp, &arg); if (*s->group == '\0') return invalid_value(tp, name, fs_path, option, "\"\""); #endif } else { return ap_psprintf(tp, "%s %s: invalid option: %s", name, fs_path, option); } } /* while */ #ifndef WIN32 if (fcgi_wrapper) { if (s->group == NULL) { s->group = ap_psprintf(tp, "#%ld", (long) fcgi_util_get_server_gid(cmd->server)); } if (s->user == NULL) { s->user = ap_psprintf(p, "#%ld", (long) fcgi_util_get_server_uid(cmd->server)); } s->uid = ap_uname2id(s->user); s->gid = ap_gname2id(s->group); } else if (s->user || s->group) { ap_log_error(FCGI_LOG_WARN, cmd->server, "FastCGI: there is no " "fastcgi wrapper set, user/group options are ignored"); } if ((err = fcgi_util_fs_set_uid_n_gid(p, s, s->uid, s->gid))) { return ap_psprintf(tp, "%s %s: invalid user or group: %s", name, fs_path, err); } #endif /* !WIN32 */ /* Require one of -socket or -host, but not both */ if (s->socket_path != NULL && s->port != 0) { return ap_psprintf(tp, "%s %s: -host and -socket are mutually exclusive options", name, fs_path); } if (s->socket_path == NULL && s->port == 0) { return ap_psprintf(tp, "%s %s: -socket or -host option missing", name, fs_path); } /* Build the appropriate sockaddr structure */ if (s->port != 0) { err = fcgi_util_socket_make_inet_addr(p, (struct sockaddr_in **)&s->socket_addr, &s->socket_addr_len, s->host, s->port); if (err != NULL) return ap_psprintf(tp, "%s %s: %s", name, fs_path, err); } else { if (fcgi_socket_dir == NULL) { #ifdef WIN32 fcgi_socket_dir = DEFAULT_SOCK_DIR; #else fcgi_socket_dir = ap_server_root_relative(p, DEFAULT_SOCK_DIR); #endif } s->socket_path = fcgi_util_socket_make_path_absolute(p, s->socket_path, 0); #ifndef WIN32 err = fcgi_util_socket_make_domain_addr(p, (struct sockaddr_un **)&s->socket_addr, &s->socket_addr_len, s->socket_path); if (err != NULL) return ap_psprintf(tp, "%s %s: %s", name, fs_path, err); #endif } /* Add it to the list of FastCGI servers */ fcgi_util_fs_add(s); return NULL; } /* *---------------------------------------------------------------------- * * fcgi_config_set_config -- * * Implements the FastCGI FCGIConfig configuration directive. * This command adds routines to control the execution of the * dynamic FastCGI processes. * * *---------------------------------------------------------------------- */ const char *fcgi_config_set_config(cmd_parms *cmd, void *dummy, const char *arg) { pool * const p = cmd->pool; pool * const tp = cmd->temp_pool; const char *err, *option; const char * const name = cmd->cmd->name; /* Allocate temp storage for an initial environment */ unsigned int envc = 0; char **envp = (char **)ap_pcalloc(tp, sizeof(char *) * (MAX_INIT_ENV_VARS + 3)); err = ap_check_cmd_context(cmd, GLOBAL_ONLY); if (err) { return err; } /* Parse the directive arguments */ while (*arg) { option = ap_getword_conf(tp, &arg); if (strcasecmp(option, "-maxProcesses") == 0) { if ((err = get_u_int(tp, &arg, &dynamicMaxProcs, 1))) return invalid_value(tp, name, NULL, option, err); } else if (strcasecmp(option, "-minProcesses") == 0) { if ((err = get_int(tp, &arg, &dynamicMinProcs, 0))) return invalid_value(tp, name, NULL, option, err); } else if (strcasecmp(option, "-maxClassProcesses") == 0) { if ((err = get_int(tp, &arg, &dynamicMaxClassProcs, 1))) return invalid_value(tp, name, NULL, option, err); } else if (strcasecmp(option, "-killInterval") == 0) { if ((err = get_u_int(tp, &arg, &dynamicKillInterval, 1))) return invalid_value(tp, name, NULL, option, err); } else if (strcasecmp(option, "-updateInterval") == 0) { if ((err = get_u_int(tp, &arg, &dynamicUpdateInterval, 1))) return invalid_value(tp, name, NULL, option, err); } else if (strcasecmp(option, "-gainValue") == 0) { if ((err = get_float(tp, &arg, &dynamicGain, 0.0, 1.0))) return invalid_value(tp, name, NULL, option, err); } else if ((strcasecmp(option, "-singleThreshold") == 0) || (strcasecmp(option, "-singleThreshhold") == 0)) { if ((err = get_int(tp, &arg, &dynamicThreshold1, 0))) return invalid_value(tp, name, NULL, option, err); } else if ((strcasecmp(option, "-multiThreshold") == 0) || (strcasecmp(option, "-multiThreshhold") == 0)) { if ((err = get_int(tp, &arg, &dynamicThresholdN, 0))) return invalid_value(tp, name, NULL, option, err); } else if (strcasecmp(option, "-startDelay") == 0) { if ((err = get_u_int(tp, &arg, &dynamicPleaseStartDelay, 1))) return invalid_value(tp, name, NULL, option, err); } else if (strcasecmp(option, "-initial-env") == 0) { if ((err = get_env_var(p, &arg, envp, &envc))) return invalid_value(tp, name, NULL, option, err); } else if (strcasecmp(option, "-pass-header") == 0) { if ((err = get_pass_header(p, &arg, &dynamic_pass_headers))) return invalid_value(tp, name, NULL, option, err); } else if (strcasecmp(option, "-appConnTimeout") == 0) { if ((err = get_u_int(tp, &arg, &dynamicAppConnectTimeout, 0))) return invalid_value(tp, name, NULL, option, err); } else if (strcasecmp(option, "-idle-timeout") == 0) { if ((err = get_u_int(tp, &arg, &dynamic_idle_timeout, 1))) return invalid_value(tp, name, NULL, option, err); } else if (strcasecmp(option, "-listen-queue-depth") == 0) { if ((err = get_u_int(tp, &arg, &dynamicListenQueueDepth, 1))) return invalid_value(tp, name, NULL, option, err); } else if (strcasecmp(option, "-min-server-life") == 0) { if ((err = get_int(tp, &arg, &dynamicMinServerLife, 0))) return invalid_value(tp, name, NULL, option, err); } else if (strcasecmp(option, "-restart-delay") == 0) { if ((err = get_u_int(tp, &arg, &dynamicRestartDelay, 0))) return invalid_value(tp, name, NULL, option, err); } else if (strcasecmp(option, "-init-start-delay") == 0) { if ((err = get_u_int(tp, &arg, &dynamicInitStartDelay, 0))) return invalid_value(tp, name, NULL, option, err); } else if (strcasecmp(option, "-processSlack") == 0) { if ((err = get_u_int(tp, &arg, &dynamicProcessSlack, 1))) return invalid_value(tp, name, NULL, option, err); } else if (strcasecmp(option, "-restart") == 0) { dynamicAutoRestart = 1; } else if (strcasecmp(option, "-autoUpdate") == 0) { dynamicAutoUpdate = 1; } else if (strcasecmp(option, "-flush") == 0) { dynamicFlush = TRUE; } else { return ap_psprintf(tp, "%s: invalid option: %s", name, option); } } /* while */ if (dynamicProcessSlack >= dynamicMaxProcs + 1) { /* the kill policy would work unexpectedly */ return ap_psprintf(tp, "%s: processSlack (%u) must be less than maxProcesses (%u) + 1", name, dynamicProcessSlack, dynamicMaxProcs); } /* Move env array to a surviving pool, leave 2 extra slots for * WIN32 _FCGI_MUTEX_ and _FCGI_SHUTDOWN_EVENT_ */ dynamicEnvp = (char **)ap_pcalloc(p, sizeof(char *) * (envc + 4)); memcpy(dynamicEnvp, envp, sizeof(char *) * envc); return NULL; } void *fcgi_config_create_dir_config(pool *p, char *dummy) { fcgi_dir_config *dir_config = ap_pcalloc(p, sizeof(fcgi_dir_config)); dir_config->authenticator_options = FCGI_AUTHORITATIVE; dir_config->authorizer_options = FCGI_AUTHORITATIVE; dir_config->access_checker_options = FCGI_AUTHORITATIVE; return dir_config; } const char *fcgi_config_new_auth_server(cmd_parms * cmd, void * dircfg, const char *fs_path, const char * compat) { fcgi_dir_config * dir_config = (fcgi_dir_config *) dircfg; pool * const tp = cmd->temp_pool; char * auth_server; #ifdef APACHE2 if (apr_filepath_merge(&auth_server, "", fs_path, 0, cmd->pool)) return ap_psprintf(tp, "%s %s: invalid filepath", cmd->cmd->name, fs_path); #else auth_server = (char *) ap_os_canonical_filename(cmd->pool, fs_path); #endif auth_server = ap_server_root_relative(cmd->pool, auth_server); /* Make sure its already configured or at least a candidate for dynamic */ if (fcgi_util_fs_get_by_id(auth_server, fcgi_util_get_server_uid(cmd->server), fcgi_util_get_server_gid(cmd->server)) == NULL) { const char *err = fcgi_util_fs_is_path_ok(tp, auth_server, NULL); if (err) return ap_psprintf(tp, "%s: \"%s\" %s", cmd->cmd->name, auth_server, err); } if (compat && strcasecmp(compat, "-compat")) return ap_psprintf(cmd->temp_pool, "%s: unknown option: \"%s\"", cmd->cmd->name, compat); switch((int)cmd->info) { case FCGI_AUTH_TYPE_AUTHENTICATOR: dir_config->authenticator = auth_server; dir_config->authenticator_options |= (compat) ? FCGI_COMPAT : 0; break; case FCGI_AUTH_TYPE_AUTHORIZER: dir_config->authorizer = auth_server; dir_config->authorizer_options |= (compat) ? FCGI_COMPAT : 0; break; case FCGI_AUTH_TYPE_ACCESS_CHECKER: dir_config->access_checker = auth_server; dir_config->access_checker_options |= (compat) ? FCGI_COMPAT : 0; break; } return NULL; } const char *fcgi_config_set_authoritative_slot(cmd_parms * cmd, void * dir_config, int arg) { int offset = (int)(long)cmd->info; if (arg) *((u_char *)dir_config + offset) |= FCGI_AUTHORITATIVE; else *((u_char *)dir_config + offset) &= ~FCGI_AUTHORITATIVE; return NULL; } mod_fastcgi-SNAP-0910052141/Makefile.tmpl0000664000635400022240000000260007146371663020201 0ustar robsfastcgi00000000000000# # Makefile.tmpl for the mod_fastcgi module # # Apache v1.3's Configure uses this to create a Makefile # # $Id: Makefile.tmpl,v 1.6 2000/08/16 01:51:47 robs Exp $ LIB=libfastcgi.$(LIBEXT) OBJS=mod_fastcgi.o fcgi_buf.o fcgi_pm.o fcgi_protocol.o fcgi_config.o fcgi_util.o # Build commands all: lib lib: $(LIB) libfastcgi.a: $(OBJS) rm -f $@ ar cr $@ $(OBJS) $(RANLIB) $@ libfastcgi.so: $(OBJS) rm -f $@ $(LD_SHLIB) $(LDFLAGS_SHLIB) -o $@ $(OBJS) .c.o: $(CC) -c $(INCLUDES) $(CFLAGS) $< clean: rm -f $(LIB) $(OBJS) distclean: clean rm -f Makefile # NOT FOR END USERS! depend: cp Makefile.tmpl Makefile.tmpl.bak \ && sed -ne '1,/^# DO NOT REMOVE/p' Makefile.tmpl > Makefile.new \ && gcc -MM $(INCLUDES) $(CFLAGS) *.c >> Makefile.new \ && sed -e '1,$$s: $(INCDIR)/: $$(INCDIR)/:g' \ -e '1,$$s: $(OSDIR)/: $$(OSDIR)/:g' Makefile.new \ > Makefile.tmpl \ && rm Makefile.new # Dependencies $(OBJS): fcgi.h \ mod_fastcgi.h \ fcgi_protocol.h \ $(INCDIR)/httpd.h \ $(INCDIR)/http_config.h \ $(INCDIR)/http_request.h \ $(INCDIR)/http_core.h \ $(INCDIR)/http_protocol.h \ $(INCDIR)/http_main.h \ $(INCDIR)/http_log.h \ $(INCDIR)/util_script.h \ $(INCDIR)/http_conf_globals.h \ $(INCDIR)/util_md5.h mod_fastcgi-SNAP-0910052141/fcgi.h0000664000635400022240000006144711065672426016661 0ustar robsfastcgi00000000000000/* * $Id: fcgi.h,v 1.48 2008/09/22 10:36:06 robs Exp $ */ #ifndef FCGI_H #define FCGI_H #if defined(DEBUG) && ! defined(NDEBUG) #define ASSERT(a) ap_assert(a) #else #define ASSERT(a) ((void) 0) #endif #ifdef WIN32 /* warning C4115: named type definition in parentheses */ #pragma warning(disable : 4115) /* warning C4514: unreferenced inline function has been removed */ #pragma warning(disable:4514) #endif /* Apache header files */ #include "httpd.h" #include "http_config.h" #include "http_request.h" #include "http_core.h" #include "http_protocol.h" #include "http_main.h" #include "http_log.h" #include "util_script.h" #include "util_md5.h" /* AP2TODO there's probably a better way */ #ifdef STANDARD20_MODULE_STUFF #define APACHE2 #endif #ifdef APACHE2 #include #include "ap_compat.h" #include "apr_strings.h" #ifdef WIN32 #if MODULE_MAGIC_NUMBER < 20020903 #error "mod_fastcgi is incompatible with Apache versions older than 2.0.41 under WIN" #endif #endif typedef struct apr_array_header_t array_header; typedef struct apr_table_t table; typedef struct apr_pool_t pool; #define NET_SIZE_T apr_socklen_t typedef apr_status_t apcb_t; #define APCB_OK APR_SUCCESS #define XtOffsetOf APR_OFFSETOF #define ap_select select #define ap_user_id unixd_config.user_id #define ap_group_id unixd_config.group_id #define ap_user_name unixd_config.user_name #define ap_suexec_enabled unixd_config.suexec_enabled #ifndef S_ISDIR #define S_ISDIR(m) (((m)&(S_IFMT)) == (S_IFDIR)) #endif /* obsolete fns */ #define ap_hard_timeout(a,b) #define ap_kill_timeout(a) #define ap_block_alarms() #define ap_reset_timeout(a) #define ap_unblock_alarms() /* starting with apache 2.2 the backward-compatibility defines for * 1.3 APIs are not available anymore. Define them ourselves here. */ #ifndef ap_copy_table #define ap_copy_table apr_table_copy #define ap_cpystrn apr_cpystrn #define ap_destroy_pool apr_pool_destroy #define ap_isalnum apr_isalnum #define ap_isspace apr_isspace #define ap_make_array apr_array_make #define ap_make_table apr_table_make #define ap_null_cleanup apr_pool_cleanup_null #define ap_palloc apr_palloc #define ap_pcalloc apr_pcalloc #define ap_psprintf apr_psprintf #define ap_pstrcat apr_pstrcat #define ap_pstrdup apr_pstrdup #define ap_pstrndup apr_pstrndup #define ap_push_array apr_array_push #define ap_register_cleanup apr_pool_cleanup_register #define ap_snprintf apr_snprintf #define ap_table_add apr_table_add #define ap_table_do apr_table_do #define ap_table_get apr_table_get #define ap_table_set apr_table_set #define ap_table_setn apr_table_setn #define ap_table_unset apr_table_unset #define ap_toupper apr_toupper #endif /* defined(ap_copy_table) */ #if (defined(HAVE_WRITEV) && !HAVE_WRITEV && !defined(NO_WRITEV)) || defined WIN32 #define NO_WRITEV #endif #else /* !APACHE2 */ #include "http_conf_globals.h" typedef void apcb_t; #define APCB_OK #if MODULE_MAGIC_NUMBER < 19990320 #error "This version of mod_fastcgi is incompatible with Apache versions older than 1.3.6." #endif #endif /* !APACHE2 */ #ifndef NO_WRITEV #include #endif #ifdef WIN32 #ifndef APACHE2 #include "multithread.h" #endif #pragma warning(default : 4115) #else #include #endif /* FastCGI header files */ #include "mod_fastcgi.h" /* @@@ This should go away when fcgi_protocol is re-written */ #include "fcgi_protocol.h" typedef struct { int size; /* size of entire buffer */ int length; /* number of bytes in current buffer */ char *begin; /* begining of valid data */ char *end; /* end of valid data */ char data[1]; /* buffer data */ } Buffer; #ifdef WIN32 #define READER 0 #define WRITER 1 #define MBOX_EVENT 0 /* mboc is ready to be read */ #define TERM_EVENT 1 /* termination event */ #define WAKE_EVENT 2 /* notification of child Fserver dieing */ typedef struct _fcgi_pm_job { char id; char *fs_path; char *user; char * group; unsigned long qsec; unsigned long start_time; struct _fcgi_pm_job *next; } fcgi_pm_job; #endif enum process_state { FCGI_RUNNING_STATE, /* currently running */ FCGI_START_STATE, /* needs to be started by PM */ FCGI_VICTIM_STATE, /* SIGTERM was sent by PM */ FCGI_KILLED_STATE, /* a wait() collected VICTIM */ FCGI_READY_STATE /* empty cell, init state */ }; /* * ServerProcess holds data for each process associated with * a class. It is embedded in fcgi_server below. */ typedef struct _FcgiProcessInfo { #ifdef WIN32 HANDLE handle; /* process handle */ HANDLE terminationEvent; /* Event used to signal process termination */ #endif pid_t pid; /* pid of associated process */ enum process_state state; /* state of the process */ time_t start_time; /* time the process was started */ } ServerProcess; /* * fcgi_server holds info for each AppClass specified in this * Web server's configuration. */ typedef struct _FastCgiServerInfo { int flush; char *fs_path; /* pathname of executable */ array_header *pass_headers; /* names of headers to pass in the env */ u_int idle_timeout; /* fs idle secs allowed before aborting */ char **envp; /* if NOT NULL, this is the env to send * to the fcgi app when starting a server * managed app. */ u_int listenQueueDepth; /* size of listen queue for IPC */ u_int appConnectTimeout; /* timeout (sec) for connect() requests */ u_int numProcesses; /* max allowed processes of this class, * or for dynamic apps, the number of * processes actually running */ time_t startTime; /* the time the application was started */ time_t restartTime; /* most recent time when the process * manager started a process in this * class. */ int initStartDelay; /* min number of seconds to wait between * starting of AppClass processes at init */ u_int restartDelay; /* number of seconds to wait between * restarts after failure. Can be zero. */ u_int minServerLife; /* minimum number of seconds a server must * live before it's considered borked. */ int restartOnExit; /* = TRUE = restart. else terminate/free */ u_int numFailures; /* num restarts due to exit failure */ int bad; /* is [not] having start problems */ struct sockaddr *socket_addr; /* Socket Address of FCGI app server class */ #ifdef WIN32 struct sockaddr *dest_addr; /* for local apps on NT need socket address */ /* bound to localhost */ const char *mutex_env_string; /* string holding the accept mutex handle */ #endif int socket_addr_len; /* Length of socket */ enum {APP_CLASS_UNKNOWN, APP_CLASS_STANDARD, APP_CLASS_EXTERNAL, APP_CLASS_DYNAMIC} directive; /* AppClass or ExternalAppClass */ const char *socket_path; /* Name used to create a socket */ const char *host; /* Hostname for externally managed * FastCGI application processes */ unsigned short port; /* Port number either for externally * managed FastCGI applications or for * server managed FastCGI applications, * where server became application mngr. */ int listenFd; /* Listener socket of FCGI app server * class. Passed to app server process * at process creation. */ u_int processPriority; /* If locally server managed process, * this is the priority to run the * processes in this class at. */ struct _FcgiProcessInfo *procs; /* Pointer to array of * processes belonging to this class. */ int keepConnection; /* = 1 = maintain connection to app. */ uid_t uid; /* uid this app should run as (suexec) */ gid_t gid; /* gid this app should run as (suexec) */ const char *username; /* suexec user arg */ const char *group; /* suexec group arg, AND used in comm * between RH and PM */ const char *user; /* used in comm between RH and PM */ /* Dynamic FastCGI apps configuration parameters */ u_long totalConnTime; /* microseconds spent by the web server * waiting while fastcgi app performs * request processing since the last * dynamicUpdateInterval */ u_long smoothConnTime; /* exponentially decayed values of the * connection times. */ u_long totalQueueTime; /* microseconds spent by the web server * waiting to connect to the fastcgi app * since the last dynamicUpdateInterval. */ int nph; struct _FastCgiServerInfo *next; } fcgi_server; /* * fcgi_request holds the state of a particular FastCGI request. */ typedef struct { #ifdef WIN32 SOCKET fd; #else int fd; /* connection to FastCGI server */ #endif int gotHeader; /* TRUE if reading content bytes */ unsigned char packetType; /* type of packet */ int dataLen; /* length of data bytes */ int paddingLen; /* record padding after content */ fcgi_server *fs; /* FastCGI server info */ const char *fs_path; /* fcgi_server path */ Buffer *serverInputBuffer; /* input buffer from FastCgi server */ Buffer *serverOutputBuffer; /* output buffer to FastCgi server */ Buffer *clientInputBuffer; /* client input buffer */ Buffer *clientOutputBuffer; /* client output buffer */ table *authHeaders; /* headers received from an auth fs */ int auth_compat; /* whether the auth request is spec compat */ table *saved_subprocess_env; /* subprocess_env before auth handling */ int expectingClientContent; /* >0 => more content, <=0 => no more */ array_header *header; char *fs_stderr; int fs_stderr_len; int parseHeader; /* TRUE iff parsing response headers */ request_rec *r; int readingEndRequestBody; FCGI_EndRequestBody endRequestBody; Buffer *erBufPtr; int exitStatus; int exitStatusSet; unsigned int requestId; int eofSent; int role; /* FastCGI Role: Authorizer or Responder */ int dynamic; /* whether or not this is a dynamic app */ struct timeval startTime; /* dynamic app's connect() attempt start time */ struct timeval queueTime; /* dynamic app's connect() complete time */ struct timeval completeTime; /* dynamic app's connection close() time */ int keepReadingFromFcgiApp; /* still more to read from fcgi app? */ const char *user; /* user used to invoke app (suexec) */ const char *group; /* group used to invoke app (suexec) */ #ifdef WIN32 BOOL using_npipe_io; /* named pipe io */ #endif int nph; } fcgi_request; /* Values of parseHeader field */ #define SCAN_CGI_READING_HEADERS 1 #define SCAN_CGI_FINISHED 0 #define SCAN_CGI_BAD_HEADER -1 #define SCAN_CGI_INT_REDIRECT -2 #define SCAN_CGI_SRV_REDIRECT -3 /* Opcodes for Server->ProcMgr communication */ #define FCGI_SERVER_START_JOB 83 /* 'S' - start */ #define FCGI_SERVER_RESTART_JOB 82 /* 'R' - restart */ #define FCGI_REQUEST_TIMEOUT_JOB 84 /* 'T' - timeout */ #define FCGI_REQUEST_COMPLETE_JOB 67 /* 'C' - complete */ /* Authorizer types, for auth directives handling */ #define FCGI_AUTH_TYPE_AUTHENTICATOR 0 #define FCGI_AUTH_TYPE_AUTHORIZER 1 #define FCGI_AUTH_TYPE_ACCESS_CHECKER 2 /* Bits for auth_options */ #define FCGI_AUTHORITATIVE 1 #define FCGI_COMPAT 2 typedef struct { const char *authorizer; u_char authorizer_options; const char *authenticator; u_char authenticator_options; const char *access_checker; u_char access_checker_options; } fcgi_dir_config; #define FCGI_OK 0 #define FCGI_FAILED 1 #ifdef APACHE2 #ifdef WIN32 #define FCGI_LOG_EMERG __FILE__,__LINE__,APLOG_EMERG,APR_FROM_OS_ERROR(GetLastError()) #define FCGI_LOG_ALERT __FILE__,__LINE__,APLOG_ALERT,APR_FROM_OS_ERROR(GetLastError()) #define FCGI_LOG_CRIT __FILE__,__LINE__,APLOG_CRIT,APR_FROM_OS_ERROR(GetLastError()) #define FCGI_LOG_ERR __FILE__,__LINE__,APLOG_ERR,APR_FROM_OS_ERROR(GetLastError()) #define FCGI_LOG_WARN __FILE__,__LINE__,APLOG_WARNING,APR_FROM_OS_ERROR(GetLastError()) #define FCGI_LOG_NOTICE __FILE__,__LINE__,APLOG_NOTICE,APR_FROM_OS_ERROR(GetLastError()) #define FCGI_LOG_INFO __FILE__,__LINE__,APLOG_INFO,APR_FROM_OS_ERROR(GetLastError()) #define FCGI_LOG_DEBUG __FILE__,__LINE__,APLOG_DEBUG,APR_FROM_OS_ERROR(GetLastError()) #else /* !WIN32 */ #define FCGI_LOG_EMERG __FILE__,__LINE__,APLOG_EMERG,APR_FROM_OS_ERROR(errno) #define FCGI_LOG_ALERT __FILE__,__LINE__,APLOG_ALERT,APR_FROM_OS_ERROR(errno) #define FCGI_LOG_CRIT __FILE__,__LINE__,APLOG_CRIT,APR_FROM_OS_ERROR(errno) #define FCGI_LOG_ERR __FILE__,__LINE__,APLOG_ERR,APR_FROM_OS_ERROR(errno) #define FCGI_LOG_WARN __FILE__,__LINE__,APLOG_WARNING,APR_FROM_OS_ERROR(errno) #define FCGI_LOG_NOTICE __FILE__,__LINE__,APLOG_NOTICE,APR_FROM_OS_ERROR(errno) #define FCGI_LOG_INFO __FILE__,__LINE__,APLOG_INFO,APR_FROM_OS_ERROR(errno) #define FCGI_LOG_DEBUG __FILE__,__LINE__,APLOG_DEBUG,APR_FROM_OS_ERROR(errno) #endif #define FCGI_LOG_EMERG_ERRNO __FILE__,__LINE__,APLOG_EMERG,APR_FROM_OS_ERROR(errno) #define FCGI_LOG_ALERT_ERRNO __FILE__,__LINE__,APLOG_ALERT,APR_FROM_OS_ERROR(errno) #define FCGI_LOG_CRIT_ERRNO __FILE__,__LINE__,APLOG_CRIT,APR_FROM_OS_ERROR(errno) #define FCGI_LOG_ERR_ERRNO __FILE__,__LINE__,APLOG_ERR,APR_FROM_OS_ERROR(errno) #define FCGI_LOG_WARN_ERRNO __FILE__,__LINE__,APLOG_WARNING,APR_FROM_OS_ERROR(errno) #define FCGI_LOG_NOTICE_ERRNO __FILE__,__LINE__,APLOG_NOTICE,APR_FROM_OS_ERROR(errno) #define FCGI_LOG_INFO_ERRNO __FILE__,__LINE__,APLOG_INFO,APR_FROM_OS_ERROR(errno) #define FCGI_LOG_DEBUG_ERRNO __FILE__,__LINE__,APLOG_DEBUG,APR_FROM_OS_ERROR(errno) #define FCGI_LOG_EMERG_NOERRNO __FILE__,__LINE__,APLOG_EMERG,0 #define FCGI_LOG_ALERT_NOERRNO __FILE__,__LINE__,APLOG_ALERT,0 #define FCGI_LOG_CRIT_NOERRNO __FILE__,__LINE__,APLOG_CRIT,0 #define FCGI_LOG_ERR_NOERRNO __FILE__,__LINE__,APLOG_ERR,0 #define FCGI_LOG_WARN_NOERRNO __FILE__,__LINE__,APLOG_WARNING,0 #define FCGI_LOG_NOTICE_NOERRNO __FILE__,__LINE__,APLOG_NOTICE,0 #define FCGI_LOG_INFO_NOERRNO __FILE__,__LINE__,APLOG_INFO,0 #define FCGI_LOG_DEBUG_NOERRNO __FILE__,__LINE__,APLOG_DEBUG,0 #else /* !APACHE2 */ #ifdef WIN32 #define FCGI_LOG_EMERG __FILE__,__LINE__,APLOG_EMERG|APLOG_WIN32ERROR #define FCGI_LOG_ALERT __FILE__,__LINE__,APLOG_ALERT|APLOG_WIN32ERROR #define FCGI_LOG_CRIT __FILE__,__LINE__,APLOG_CRIT|APLOG_WIN32ERROR #define FCGI_LOG_ERR __FILE__,__LINE__,APLOG_ERR|APLOG_WIN32ERROR #define FCGI_LOG_WARN __FILE__,__LINE__,APLOG_WARNING|APLOG_WIN32ERROR #define FCGI_LOG_NOTICE __FILE__,__LINE__,APLOG_NOTICE|APLOG_WIN32ERROR #define FCGI_LOG_INFO __FILE__,__LINE__,APLOG_INFO|APLOG_WIN32ERROR #define FCGI_LOG_DEBUG __FILE__,__LINE__,APLOG_DEBUG|APLOG_WIN32ERROR #else /* !WIN32 */ #define FCGI_LOG_EMERG __FILE__,__LINE__,APLOG_EMERG #define FCGI_LOG_ALERT __FILE__,__LINE__,APLOG_ALERT #define FCGI_LOG_CRIT __FILE__,__LINE__,APLOG_CRIT #define FCGI_LOG_ERR __FILE__,__LINE__,APLOG_ERR #define FCGI_LOG_WARN __FILE__,__LINE__,APLOG_WARNING #define FCGI_LOG_NOTICE __FILE__,__LINE__,APLOG_NOTICE #define FCGI_LOG_INFO __FILE__,__LINE__,APLOG_INFO #define FCGI_LOG_DEBUG __FILE__,__LINE__,APLOG_DEBUG #endif #define FCGI_LOG_EMERG_ERRNO __FILE__,__LINE__,APLOG_EMERG /* system is unusable */ #define FCGI_LOG_ALERT_ERRNO __FILE__,__LINE__,APLOG_ALERT /* action must be taken immediately */ #define FCGI_LOG_CRIT_ERRNO __FILE__,__LINE__,APLOG_CRIT /* critical conditions */ #define FCGI_LOG_ERR_ERRNO __FILE__,__LINE__,APLOG_ERR /* error conditions */ #define FCGI_LOG_WARN_ERRNO __FILE__,__LINE__,APLOG_WARNING /* warning conditions */ #define FCGI_LOG_NOTICE_ERRNO __FILE__,__LINE__,APLOG_NOTICE /* normal but significant condition */ #define FCGI_LOG_INFO_ERRNO __FILE__,__LINE__,APLOG_INFO /* informational */ #define FCGI_LOG_DEBUG_ERRNO __FILE__,__LINE__,APLOG_DEBUG /* debug-level messages */ #define FCGI_LOG_EMERG_NOERRNO __FILE__,__LINE__,APLOG_EMERG|APLOG_NOERRNO #define FCGI_LOG_ALERT_NOERRNO __FILE__,__LINE__,APLOG_ALERT|APLOG_NOERRNO #define FCGI_LOG_CRIT_NOERRNO __FILE__,__LINE__,APLOG_CRIT|APLOG_NOERRNO #define FCGI_LOG_ERR_NOERRNO __FILE__,__LINE__,APLOG_ERR|APLOG_NOERRNO #define FCGI_LOG_WARN_NOERRNO __FILE__,__LINE__,APLOG_WARNING|APLOG_NOERRNO #define FCGI_LOG_NOTICE_NOERRNO __FILE__,__LINE__,APLOG_NOTICE|APLOG_NOERRNO #define FCGI_LOG_INFO_NOERRNO __FILE__,__LINE__,APLOG_INFO|APLOG_NOERRNO #define FCGI_LOG_DEBUG_NOERRNO __FILE__,__LINE__,APLOG_DEBUG|APLOG_NOERRNO #endif /* !APACHE2 */ #ifdef FCGI_DEBUG #define FCGIDBG1(a) ap_log_error(FCGI_LOG_DEBUG,fcgi_apache_main_server,a); #define FCGIDBG2(a,b) ap_log_error(FCGI_LOG_DEBUG,fcgi_apache_main_server,a,b); #define FCGIDBG3(a,b,c) ap_log_error(FCGI_LOG_DEBUG,fcgi_apache_main_server,a,b,c); #define FCGIDBG4(a,b,c,d) ap_log_error(FCGI_LOG_DEBUG,fcgi_apache_main_server,a,b,c,d); #define FCGIDBG5(a,b,c,d,e) ap_log_error(FCGI_LOG_DEBUG,fcgi_apache_main_server,a,b,c,d,e); #define FCGIDBG6(a,b,c,d,e,f) ap_log_error(FCGI_LOG_DEBUG,fcgi_apache_main_server,a,b,c,d,e,f); #define FCGIDBG7(a,b,c,d,e,f,g) ap_log_error(FCGI_LOG_DEBUG,fcgi_apache_main_server,a,b,c,d,e,f,g); #else #define FCGIDBG1(a) #define FCGIDBG2(a,b) #define FCGIDBG3(a,b,c) #define FCGIDBG4(a,b,c,d) #define FCGIDBG5(a,b,c,d,e) #define FCGIDBG6(a,b,c,d,e,f) #define FCGIDBG7(a,b,c,d,e,f,g) #endif /* * Holds the status of the sending of the environment. * A quick hack to dump the static vars for the NT port. */ typedef struct { enum { PREP, HEADER, NAME, VALUE } pass; char **envp; int headerLen, nameLen, valueLen, totalLen; char *equalPtr; unsigned char headerBuff[8]; } env_status; /* * fcgi_config.c */ void *fcgi_config_create_dir_config(pool *p, char *dummy); const char *fcgi_config_make_dir(pool *tp, char *path); const char *fcgi_config_make_dynamic_dir(pool *p, const int wax); const char *fcgi_config_new_static_server(cmd_parms *cmd, void *dummy, const char *arg); const char *fcgi_config_new_external_server(cmd_parms *cmd, void *dummy, const char *arg); const char *fcgi_config_set_config(cmd_parms *cmd, void *dummy, const char *arg); const char *fcgi_config_set_fcgi_uid_n_gid(int set); const char *fcgi_config_new_auth_server(cmd_parms * cmd, void *dir_config, const char *fs_path, const char * compat); const char *fcgi_config_set_authoritative_slot(cmd_parms * cmd, void * dir_config, int arg); const char *fcgi_config_set_socket_dir(cmd_parms *cmd, void *dummy, const char *arg); const char *fcgi_config_set_wrapper(cmd_parms *cmd, void *dummy, const char *arg); apcb_t fcgi_config_reset_globals(void * dummy); const char *fcgi_config_set_env_var(pool *p, char **envp, unsigned int *envc, char * var); /* * fcgi_pm.c */ #if defined(WIN32) || defined(APACHE2) void fcgi_pm_main(void *dummy); #else int fcgi_pm_main(void *dummy, child_info *info); #endif /* * fcgi_protocol.c */ void fcgi_protocol_queue_begin_request(fcgi_request *fr); void fcgi_protocol_queue_client_buffer(fcgi_request *fr); int fcgi_protocol_queue_env(request_rec *r, fcgi_request *fr, env_status *env); int fcgi_protocol_dequeue(pool *p, fcgi_request *fr); /* * fcgi_buf.c */ #define BufferLength(b) ((b)->length) #define BufferFree(b) ((b)->size - (b)->length) void fcgi_buf_reset(Buffer *bufPtr); Buffer *fcgi_buf_new(pool *p, int size); #ifndef WIN32 typedef int SOCKET; #endif int fcgi_buf_socket_recv(Buffer *b, SOCKET socket); int fcgi_buf_socket_send(Buffer *b, SOCKET socket); void fcgi_buf_added(Buffer * const b, const unsigned int len); void fcgi_buf_removed(Buffer * const b, unsigned int len); void fcgi_buf_get_block_info(Buffer *bufPtr, char **beginPtr, int *countPtr); void fcgi_buf_toss(Buffer *bufPtr, int count); void fcgi_buf_get_free_block_info(Buffer *bufPtr, char **endPtr, int *countPtr); void fcgi_buf_add_update(Buffer *bufPtr, int count); int fcgi_buf_add_block(Buffer *bufPtr, char *data, int datalen); int fcgi_buf_add_string(Buffer *bufPtr, char *str); int fcgi_buf_get_to_block(Buffer *bufPtr, char *data, int datalen); void fcgi_buf_get_to_buf(Buffer *toPtr, Buffer *fromPtr, int len); void fcgi_buf_get_to_array(Buffer *buf, array_header *arr, int len); /* * fcgi_util.c */ char *fcgi_util_socket_hash_filename(pool *p, const char *path, const char *user, const char *group); const char *fcgi_util_socket_make_path_absolute(pool * const p, const char *const file, const int dynamic); #ifndef WIN32 const char *fcgi_util_socket_make_domain_addr(pool *p, struct sockaddr_un **socket_addr, int *socket_addr_len, const char *socket_path); #endif const char *fcgi_util_socket_make_inet_addr(pool *p, struct sockaddr_in **socket_addr, int *socket_addr_len, const char *host, unsigned short port); const char *fcgi_util_check_access(pool *tp, const char * const path, const struct stat *statBuf, const int mode, const uid_t uid, const gid_t gid); fcgi_server *fcgi_util_fs_get_by_id(const char *ePath, uid_t uid, gid_t gid); fcgi_server *fcgi_util_fs_get(const char *ePath, const char *user, const char *group); const char *fcgi_util_fs_is_path_ok(pool * const p, const char * const fs_path, struct stat *finfo); fcgi_server *fcgi_util_fs_new(pool *p); void fcgi_util_fs_add(fcgi_server *s); const char *fcgi_util_fs_set_uid_n_gid(pool *p, fcgi_server *s, uid_t uid, gid_t gid); ServerProcess *fcgi_util_fs_create_procs(pool *p, int num); int fcgi_util_ticks(struct timeval *); #ifdef WIN32 int fcgi_pm_add_job(fcgi_pm_job *new_job); #endif uid_t fcgi_util_get_server_uid(const server_rec * const s); gid_t fcgi_util_get_server_gid(const server_rec * const s); /* * Globals */ extern pool *fcgi_config_pool; extern server_rec *fcgi_apache_main_server; extern const char *fcgi_wrapper; /* wrapper path */ extern uid_t fcgi_user_id; /* the run uid of Apache & PM */ extern gid_t fcgi_group_id; /* the run gid of Apache & PM */ extern fcgi_server *fcgi_servers; extern char *fcgi_socket_dir; /* default FastCgiIpcDir */ /* pipe used for comm between the request handlers and the PM */ extern int fcgi_pm_pipe[]; extern pid_t fcgi_pm_pid; extern char *fcgi_dynamic_dir; /* directory for the dynamic * fastcgi apps' sockets */ extern char *fcgi_empty_env; extern int fcgi_dynamic_total_proc_count; extern time_t fcgi_dynamic_epoch; extern time_t fcgi_dynamic_last_analyzed; #ifdef WIN32 extern HANDLE *fcgi_dynamic_mbox_mutex; extern HANDLE fcgi_event_handles[3]; extern fcgi_pm_job *fcgi_dynamic_mbox; #endif extern u_int dynamicMaxProcs; extern int dynamicMinProcs; extern int dynamicMaxClassProcs; extern u_int dynamicKillInterval; extern u_int dynamicUpdateInterval; extern float dynamicGain; extern int dynamicThreshold1; extern int dynamicThresholdN; extern u_int dynamicPleaseStartDelay; extern u_int dynamicAppConnectTimeout; extern char **dynamicEnvp; extern u_int dynamicProcessSlack; extern int dynamicAutoRestart; extern int dynamicAutoUpdate; extern u_int dynamicListenQueueDepth; extern u_int dynamicInitStartDelay; extern u_int dynamicRestartDelay; extern array_header *dynamic_pass_headers; extern u_int dynamic_idle_timeout; extern int dynamicMinServerLife; extern int dynamicFlush; extern module MODULE_VAR_EXPORT fastcgi_module; #endif /* FCGI_H */ mod_fastcgi-SNAP-0910052141/fcgi_buf.c0000664000635400022240000003052007617573071017500 0ustar robsfastcgi00000000000000/* * $Id: fcgi_buf.c,v 1.18 2003/02/03 23:07:37 robs Exp $ */ #include "fcgi.h" #ifdef WIN32 #pragma warning( disable : 4127 ) #else #ifdef APACHE2 #include #endif #endif /******************************************************************************* * Check buffer consistency with assertions. */ #ifdef DEBUG static void fcgi_buf_check(Buffer *buf) { ASSERT(buf->size > 0); ASSERT(buf->length >= 0); ASSERT(buf->length <= buf->size); ASSERT(buf->begin >= buf->data); ASSERT(buf->begin < buf->data + buf->size); ASSERT(buf->end >= buf->data); ASSERT(buf->end < buf->data + buf->size); ASSERT(((buf->end - buf->begin + buf->size) % buf->size) == (buf->length % buf->size)); } #else #define fcgi_buf_check(a) ((void) 0) #endif /******************************************************************************* * Reset buffer, losing any data that's in it. */ void fcgi_buf_reset(Buffer *buf) { buf->length = 0; buf->begin = buf->end = buf->data; } /******************************************************************************* * Allocate and intialize a new buffer of the specified size. */ Buffer *fcgi_buf_new(pool *p, int size) { Buffer *buf; buf = (Buffer *)ap_pcalloc(p, sizeof(Buffer) + size); buf->size = size; fcgi_buf_reset(buf); return buf; } void fcgi_buf_removed(Buffer * const b, unsigned int len) { b->length -= len; b->begin += len; if (b->length == 0) { b->begin = b->end = b->data; } else if (b->begin >= b->data + b->size) { b->begin -= b->size; } } void fcgi_buf_added(Buffer * const b, const unsigned int len) { b->length += len; b->end += len; if (b->end >= b->data + b->size) { b->end -= b->size; } } #ifdef WIN32 static int socket_recv(SOCKET fd, char *buf, int len) { int bytes_read = recv(fd, buf, len, 0); if (bytes_read == SOCKET_ERROR) { return -1; } return bytes_read; } static int socket_send(SOCKET fd, char * buf, int len) { int bytes_sent = send(fd, buf, len, 0); if (bytes_sent == SOCKET_ERROR) { return -1; } return bytes_sent; } #else /* !WIN32 */ static int socket_recv(int fd, char * buf, int len) { int bytes_read; do { bytes_read = read(fd, buf, len); if (bytes_read < 0) { #ifdef EWOULDBLOCK ASSERT(errno != EWOULDBLOCK); #endif #ifdef EAGAIN ASSERT(errno != EAGAIN); #endif } } while (bytes_read == -1 && errno == EINTR); return bytes_read; } static int socket_send(int fd, char * buf, int len) { int bytes_sent; do { bytes_sent = write(fd, buf, len); if (bytes_sent < 0) { #ifdef EWOULDBLOCK ASSERT(errno != EWOULDBLOCK); #endif #ifdef EAGAIN ASSERT(errno != EAGAIN); #endif } } while (bytes_sent == -1 && errno == EINTR); return bytes_sent; } #endif /* !WIN32 */ /******************************************************************************* * Read from an open file descriptor into buffer. * * The caller should disable the default Apache SIGPIPE handler, * otherwise a bad script could cause the request to abort and appear * as though the client's fd caused it. * * Results: * <0 error, errno is set * =0 EOF reached * >0 successful read or no room in buffer (NOT # of bytes read) */ int fcgi_buf_socket_recv(Buffer *buf, SOCKET fd) { int len; fcgi_buf_check(buf); if (buf->length == buf->size) /* there's no room in the buffer, return "success" */ return 1; if (buf->length == 0) /* the buffer is empty so defrag */ buf->begin = buf->end = buf->data; len = min(buf->size - buf->length, buf->data + buf->size - buf->end); #ifndef NO_WRITEV /* assume there is a readv() since there is a writev() */ if (len == buf->size - buf->length) { #endif len = socket_recv(fd, buf->end, len); #ifndef NO_WRITEV } else { /* the buffer is wrapped, use readv() */ struct iovec vec[2]; vec[0].iov_base = buf->end; vec[0].iov_len = len; vec[1].iov_base = buf->data; vec[1].iov_len = buf->size - buf->length - len; ASSERT(len); ASSERT(vec[1].iov_len); do { len = readv(fd, vec, 2); } while (len == -1 && errno == EINTR); } #endif if (len <= 0) return len; fcgi_buf_added(buf, len); return len; /* this may not contain the number of bytes read */ } /******************************************************************************* * Write from the buffer to an open file descriptor. * * The caller should disable the default Apache SIGPIPE handler, * otherwise a bad script could cause the request to abort appearing * as though the client's fd caused it. * * Results: * <0 if an error occured (bytes may or may not have been written) * =0 if no bytes were written * >0 successful write */ int fcgi_buf_socket_send(Buffer *buf, SOCKET fd) { int len; fcgi_buf_check(buf); if (buf->length == 0) return 0; len = min(buf->length, buf->data + buf->size - buf->begin); #ifndef NO_WRITEV if (len == buf->length) { #endif len = socket_send(fd, buf->begin, len); #ifndef NO_WRITEV } else { struct iovec vec[2]; vec[0].iov_base = buf->begin; vec[0].iov_len = len; vec[1].iov_base = buf->data; vec[1].iov_len = buf->length - len; do { len = writev(fd, vec, 2); } while (len == -1 && errno == EINTR); } #endif if (len <= 0) return len; fcgi_buf_removed(buf, len); return len; } /******************************************************************************* * Return the data block start address and the length of the block. */ void fcgi_buf_get_block_info(Buffer *buf, char **beginPtr, int *countPtr) { fcgi_buf_check(buf); *beginPtr = buf->begin; *countPtr = min(buf->length, buf->data + buf->size - buf->begin); } /******************************************************************************* * Throw away bytes from buffer. */ void fcgi_buf_toss(Buffer *buf, int count) { fcgi_buf_check(buf); ASSERT(count >= 0); ASSERT(count <= buf->length); buf->length -= count; buf->begin += count; if(buf->begin >= buf->data + buf->size) { buf->begin -= buf->size; } } /******************************************************************************* * Return the free data block start address and the length of the block. */ void fcgi_buf_get_free_block_info(Buffer *buf, char **endPtr, int *countPtr) { fcgi_buf_check(buf); *endPtr = buf->end; *countPtr = min(buf->size - buf->length, buf->data + buf->size - buf->end); } /******************************************************************************* * Updates the buf to reflect recently added data. */ void fcgi_buf_add_update(Buffer *buf, int count) { fcgi_buf_check(buf); ASSERT(count >= 0); ASSERT(count <= BufferFree(buf)); buf->length += count; buf->end += count; if(buf->end >= buf->data + buf->size) { buf->end -= buf->size; } fcgi_buf_check(buf); } /******************************************************************************* * Adds a block of data to a buffer, returning the number of bytes added. */ int fcgi_buf_add_block(Buffer *buf, char *data, int datalen) { char *end; int copied = 0; /* Number of bytes actually copied. */ int canCopy; /* Number of bytes to copy in a given op. */ ASSERT(data != NULL); ASSERT(datalen >= 0); if(datalen == 0) { return 0; } ASSERT(datalen > 0); fcgi_buf_check(buf); end = buf->data + buf->size; /* * Copy the first part of the data: from here to the end of the * buffer, or the end of the data, whichever comes first. */ datalen = min(BufferFree(buf), datalen); canCopy = min(datalen, end - buf->end); memcpy(buf->end, data, canCopy); buf->length += canCopy; buf->end += canCopy; copied += canCopy; if (buf->end >= end) { buf->end = buf->data; } datalen -= canCopy; /* * If there's more to go, copy the second part starting from the * beginning of the buffer. */ if (datalen > 0) { data += canCopy; memcpy(buf->end, data, datalen); buf->length += datalen; buf->end += datalen; copied += datalen; } return(copied); } /******************************************************************************* * Add a string to a buffer, returning the number of bytes added. */ int fcgi_buf_add_string(Buffer *buf, char *str) { return fcgi_buf_add_block(buf, str, strlen(str)); } /******************************************************************************* * Gets a data block from a buffer, returning the number of bytes copied. */ int fcgi_buf_get_to_block(Buffer *buf, char *data, int datalen) { char *end; int copied = 0; /* Number of bytes actually copied. */ int canCopy; /* Number of bytes to copy in a given op. */ ASSERT(data != NULL); ASSERT(datalen > 0); fcgi_buf_check(buf); end = buf->data + buf->size; /* * Copy the first part out of the buffer: from here to the end * of the buffer, or all of the requested data. */ canCopy = min(buf->length, datalen); canCopy = min(canCopy, end - buf->begin); memcpy(data, buf->begin, canCopy); buf->length -= canCopy; buf->begin += canCopy; copied += canCopy; if (buf->begin >= end) { buf->begin = buf->data; } /* * If there's more to go, copy the second part starting from the * beginning of the buffer. */ if (copied < datalen && buf->length > 0) { data += copied; canCopy = min(buf->length, datalen - copied); memcpy(data, buf->begin, canCopy); buf->length -= canCopy; buf->begin += canCopy; copied += canCopy; } fcgi_buf_check(buf); return(copied); } /******************************************************************************* * Move 'len' bytes from 'src' buffer to 'dest' buffer. There must be at * least 'len' bytes available in the source buffer and space for 'len' * bytes in the destination buffer. */ void fcgi_buf_get_to_buf(Buffer *dest, Buffer *src, int len) { char *dest_end, *src_begin; int dest_len, src_len, move_len; ASSERT(len > 0); ASSERT(BufferLength(src) >= len); ASSERT(BufferFree(dest) >= len); fcgi_buf_check(src); fcgi_buf_check(dest); for (;;) { if (len == 0) return; fcgi_buf_get_free_block_info(dest, &dest_end, &dest_len); fcgi_buf_get_block_info(src, &src_begin, &src_len); move_len = min(dest_len, src_len); move_len = min(move_len, len); if (move_len == 0) return; memcpy(dest_end, src_begin, move_len); fcgi_buf_toss(src, move_len); fcgi_buf_add_update(dest, move_len); len -= move_len; } } static void array_grow(array_header *arr, int n) { if (n <= 0) return; if (arr->nelts + n > arr->nalloc) { char *new_elts; int new_nalloc = (arr->nalloc <= 0) ? n : arr->nelts + n; new_elts = ap_pcalloc(arr->pool, arr->elt_size * new_nalloc); memcpy(new_elts, arr->elts, arr->nelts * arr->elt_size); arr->elts = new_elts; arr->nalloc = new_nalloc; } } static void array_cat_block(array_header *arr, void *block, int n) { array_grow(arr, n); memcpy(arr->elts + arr->nelts * arr->elt_size, block, n * arr->elt_size); arr->nelts += n; } /*---------------------------------------------------------------------- * Append "len" bytes from "buf" into "arr". Apache arrays are used * whenever the data being handled is binary (may contain null chars). */ void fcgi_buf_get_to_array(Buffer *buf, array_header *arr, int len) { int len1 = min(buf->length, buf->data + buf->size - buf->begin); fcgi_buf_check(buf); ASSERT(len > 0); ASSERT(len <= BufferLength(buf)); array_grow(arr, len); len1 = min(len1, len); array_cat_block(arr, buf->begin, len1); if (len1 < len) array_cat_block(arr, buf->data, len - len1); fcgi_buf_toss(buf, len); } mod_fastcgi-SNAP-0910052141/docs/0002775000635400022240000000000011262520022016475 5ustar robsfastcgi00000000000000mod_fastcgi-SNAP-0910052141/docs/LICENSE.TERMS0000664000635400022240000000471306407523756020424 0ustar robsfastcgi00000000000000This FastCGI application library source and object code (the "Software") and its documentation (the "Documentation") are copyrighted by Open Market, Inc ("Open Market"). The following terms apply to all files associated with the Software and Documentation unless explicitly disclaimed in individual files. Open Market permits you to use, copy, modify, distribute, and license this Software and the Documentation solely for the purpose of implementing the FastCGI specification defined by Open Market or derivative specifications publicly endorsed by Open Market and promulgated by an open standards organization and for no other purpose, provided that existing copyright notices are retained in all copies and that this notice is included verbatim in any distributions. No written agreement, license, or royalty fee is required for any of the authorized uses. Modifications to this Software and Documentation may be copyrighted by their authors and need not follow the licensing terms described here, but the modified Software and Documentation must be used for the sole purpose of implementing the FastCGI specification defined by Open Market or derivative specifications publicly endorsed by Open Market and promulgated by an open standards organization and for no other purpose. If modifications to this Software and Documentation have new licensing terms, the new terms must protect Open Market's proprietary rights in the Software and Documentation to the same extent as these licensing terms and must be clearly indicated on the first page of each file where they apply. Open Market shall retain all right, title and interest in and to the Software and Documentation, including without limitation all patent, copyright, trade secret and other proprietary rights. OPEN MARKET MAKES NO EXPRESS OR IMPLIED WARRANTY WITH RESPECT TO THE SOFTWARE OR THE DOCUMENTATION, INCLUDING WITHOUT LIMITATION ANY WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL OPEN MARKET BE LIABLE TO YOU OR ANY THIRD PARTY FOR ANY DAMAGES ARISING FROM OR RELATING TO THIS SOFTWARE OR THE DOCUMENTATION, INCLUDING, WITHOUT LIMITATION, ANY INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES OR SIMILAR DAMAGES, INCLUDING LOST PROFITS OR LOST DATA, EVEN IF OPEN MARKET HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. THE SOFTWARE AND DOCUMENTATION ARE PROVIDED "AS IS". OPEN MARKET HAS NO LIABILITY IN CONTRACT, TORT, NEGLIGENCE OR OTHERWISE ARISING OUT OF THIS SOFTWARE OR THE DOCUMENTATION. mod_fastcgi-SNAP-0910052141/docs/mod_fastcgi.html0000664000635400022240000022170211065563310021654 0ustar robsfastcgi00000000000000 Apache module mod_fastcgi

[APACHE FEATHER BANNER]

Module mod_fastcgi

This 3rd party module provides support for the FastCGI protocol. FastCGI is a language independent, scalable, open extension to CGI that provides high performance and persistence without the limitations of server specific APIs.

FastCGI applications are not limited to a particular development language (the protocol is open). FastCGI application libraries currently exist for Perl, C/C++, Java, Python, TCL, SmallEiffel, and Smalltalk.

FastCGI applications use TCP or Unix sockets to communicate with the web server. This scalable architecture allows applications to run on the same platform as the web server or on many machines scattered across an enterprise network.

FastCGI applications are portable to other web server platforms. FastCGI is supported either directly or through commercial extensions by most popular web servers.

FastCGI applications are fast because they're persistent. There is no per-request startup and initialization overhead. This makes possible the development of applications which would otherwise be impractical within the CGI paradigm (e.g. a huge Perl script, or an application which requires a connection to one or more databases).

See the FastCGI website for more information. To receive FastCGI related announcements and notifications of software updates, subscribe to fastcgi-announce. To participate in the discussion of mod_fastcgi and FastCGI application development, subscribe to fastcgi-developers.

Summary

For information about building and installing the module, see the INSTALL document that came with the distribution.

FastCGI applications under mod_fastcgi are defined as one of three types: static, dynamic, or external. They're configured using the FastCgiServer, FastCgiConfig, and FastCgiExternalServer directives respectively. Any URI that Apache identifies as a FastCGI application and which hasn't been explicitly configured using a FastCgiServer or FastCgiExternalServer directive is handled as a dynamic application (see the FastCgiConfig directive for more information).

FastCGI static and dynamic applications are spawned and managed by the FastCGI Process Manager, fcgi-pm. The process manager is spawned by Apache at server initialization. External applications are presumed to be started and managed independently.

Apache must be configured to identify requests for FastCGI URIs. mod_fastcgi registers (with Apache) a handler type of fastcgi-script for this purpose.

To configure Apache to handle all files (within the scope of the directive) as FastCGI applications (e.g. for a fcgi-bin directory):

SetHandler fastcgi-script

To configure Apache to handle files (within the scope of the directive) with the specified extension(s) as FastCGI applications:

AddHandler fastcgi-script fcg fcgi fpl

Consult the Apache documentation for more information regarding these and other directives which affect request handling (such as Action).

Dynamic FastCGI applications require the ExecCGI option be enabled (see the Options directive) in the application's directory.

Notes

mod_fastcgi logs FastCGI application error (stderr) output to the server log associated with the request. Errors reported by the FastCGI process manager, fcgi-pm, are reported to the main server log (typically, logs/error_log). Data written to stdout or stderr before entering the FastCGI accept loop or via a mechanism that is not FastCGI protocol aware will also be directed to the main server log. If Apache's LogLevel is set to info additional informational messages are printed to the logs, these messages may be especially helpful while debugging a configuration.

Under Unix, expect your FastCGI application to see SIGPIPE, SIGUSR1, and SIGTERM. The latest FastCGI C, C++ and Perl application library installs default handlers if none are installed by the application. If an http client aborts a request before it completes, mod_fastcgi does too - this results in a SIGPIPE to the FastCGI application. At a minimum, SIGPIPE should be ignored (applications spawned by mod_fastcgi have this setup automatically). Ideally, it should result in an early abort of the request handling within your application and a return to the top of the FastCGI accept() loop. Apache uses SIGUSR1 to request a "graceful" process restart/shutdown. It is sent to Apache's process group (which includes applications spawned by mod_fastcgi). Ideally, it should result in a FastCGI application finishing the current request, if any, and then an exit. The mod_fastcgi process manager isn't particularly patient though (there's room for improvement here) and since it has to shutdown too, sends a SIGTERM to all of the FastCGI applications it is responsible for. Apache will restart the process manager and it will restart its managed applications (as if the server was just started). SIGTERM is, well, SIGTERM - your application should exit quickly.

Under Windows, there are no signals. A shutdown event is used instead. This is setup by mod_fastcgi and honored by the latest version of the C, C++, and Perl application library. If your using a library which doesn't support this, your application will not get shutdown during an Apache restart/shutdown (there's room for improvement here).

To pass per-request environment variables to FastCGI applications, have a look at: mod_env (SetEnv, PassEnv, UnSetEnv), mod_setenvif (BrowserMatch, BrowserMatchNoCase, SetEnvIf, SetEnvIfNoCase), and mod_rewrite (if you're feeling adventurous).

FastCGI application output is buffered by default. This is not the case for CGI scripts (under Apache 1.3). To override the default behavior, use the -flush option (not available for dynamic applications).

Redirects are handled similarly to CGI. Location headers with values that begin with "/" are treated as internal-redirects; otherwise, they are treated as external redirects (302).

Session affinity (as well as distribution) should be achievable outside of mod_fastcgi using mod_rewrite. If you get this working, please post the details to fastcgi-developers@fastcgi.com so they can be included here.

FastCGI Specification Compliance

The FastCGI specification is not implemented in its entirety and I've deviated a bit as well resulting in some Apache specific features.

The file descriptors for stdout and stderr are left open. This is prohibited by the specification. I can't see any reason to require that they be closed, and leaving them open prevents FastCGI applications which were not completely ported to FastCGI from failing miserably. This does not mean the applications shouldn't be fixed such that this doesn't occur, but is invaluable when using a 3rd party library (without source code) which expects to be able to write to stderr. Anything written to stdout or stderr in this manner will be directed to the main server log.

The Filter and Log Roles are not supported. The Filter Role has little value in Apache until the output of one handler can be piped into another (Apache 2.0 is expected to support this). The Log Role has some value, but Apache's "piped logs" feature is similar (and is even more CPU friendly).

The FastCGI protocol supports a feature, described in the specificiation as "multiplexing", that allows a single client-server connection to be simultaneously shared by multiple requests. This is not supported. This does *not* prevent FastCGI applications from supporting multiple simultaneous requests over independent connections. Of course, the application has to be specifically designed to do so by using a threaded or select/poll based server model.

The Authorizer Role has three variations corresponding to three specific Apache request handling phases:  Authentication, Authorization, and Access Control. mod_fastcgi sets up the (Apache specific) environment variable "FCGI_APACHE_ROLE" to indicate which Apache authorizer phase is being performed.

Authorizers under mod_fastcgi are sent nearly all of the standard environment variables typically available to CGI/FastCGI request handlers including some explicitly precluded by the FastCGI specification (for authorizers); I didn't see the point in leaving them out. All headers returned by a FastCGI Authorizer in a successful response (Status: 200) are passed to sub-processes (CGI/FastCGI invocations) as environment variables rather than just those prefixed by Variable- as the FastCGI specification calls for; I didn't see the point in leaving them out either. FastCGI specification compliant authorizer behavior can be obtained by using the -compat option to the Auth server directives.

Custom failure responses from FastCGI authorizer applications are not supported (speak up if you need this). See the ErrorDocument directive for a workaround (a CGI/FastCGI application can serve the error document).

Directives


FastCgiServer

Syntax: FastCgiServer filename [option ...]
Context: server config, virtual host

The FastCgiServer directive defines filename as a static FastCGI application. If the filename does not begin with a slash (/) then it is assumed to be relative to the ServerRoot.

By default, the Process Manager will start one instance of the application with the default configuration specified (in parentheses) below. Should a static application instance die for any reason mod_fastcgi will spawn another to replace it and log the event (at the warn LogLevel).

Note: Using FastCgiServer within a VirtualHost does not necessarily limited access to that host. If filename is accessible via other virtual hosts, they too can leverage the same definition.

Option can be one of (case insensitive):

-appConnTimeout n (0 seconds)
Unix:  The number of seconds to wait for a connection to the FastCGI application to complete or 0 to indicate a blocking connect() should be used. Blocking connect()s have an OS dependent internal timeout. If the timeout expires, a SERVER_ERROR results. For non-zero values, this is the amount of time used in a select() to write to the file descriptor returned by a non-blocking connect(). Non-blocking connect()s are troublesome on many platforms. See also -idle-timeout, it produces similar results but in a more portable manner.
Windows NT:  TCP based applications work as above. Named pipe based applications (static applications configured without the -port option and dynamic applications) use this value successfully to limit the amount of time to wait for a connection (i.e. it's not "troublesome"). By default, this is 90 seconds (FCGI_NAMED_PIPE_CONNECT_TIMEOUT in mod_fastcgi.h).
-group groupname|#gid (none)
Unix (only): When FastCgiWrapper is in use, the group is used to invoke the wrapper. The -group option must be used together with -user.
-idle-timeout n (30 seconds)
The number of seconds of FastCGI application inactivity allowed before the request is aborted and the event is logged (at the error LogLevel). The inactivity timer applies only as long as a connection is pending with the FastCGI application. If a request is queued to an application, but the application doesn't respond (by writing and flushing) within this period, the request will be aborted. If communication is complete with the application but incomplete with the client (the response is buffered), the timeout does not apply.
-initial-env name[=[value]] (none)
A name-value pair to be passed in the FastCGI application's initial environment. To pass a variable from Apache's environment, don't provide the "=" (if the variable isn't actually in the environment, it will be defined without a value). To define a variable without a value, provide the "=" without any value. The option can be used repeatedly.
-init-start-delay n (1 second)
The minimum number of seconds between the spawning of instances of this application. This delay decreases the demand placed on the system at server initialization.
-flush (none)
Force a write to the client as data is received from the application. By default, mod_fastcgi buffers data in order to free the application as quickly as possible.
-listen-queue-depth n (100)
The depth of listen() queue (also known as the backlog) shared by all of the instances of this application. A deeper listen queue allows the server to cope with transient load fluctuations without rejecting requests; it does not increase throughput. Adding additional application instances may increase throughput/performance, depending upon the application and the host.
-min-server-life n (30)
The minimum number of seconds the application must run for before its restart interval is increased to 600 seconds. The server will get 3 tries to run for at least this number of seconds.
-nph
Instructs mod_fastcgi not to parse the headers. See the Apache documentation for more information about nph (non parse header) scripts.
-pass-header header (none)
The name of a Request Header to be passed in the request environment. This option makes available the contents of headers which are normally not available (e.g. Authorization) to a CGI environment. The passed header names are prefixed with "HTTP_" IAW the CGI specification.
-port n (none)
The TCP port number (1-65535) the application will use for communication with the web server. This option makes the application accessible from other machines on the network (as well as this one). The -socket and -port options are mutually exclusive.
-priority n (0)
The process priority to be assigned to the application instances (using setpriority()).
-processes n (1)
The number of instances of the application to spawn at server initialization.
-restart-delay n (5 seconds)
The minimum number of seconds between the respawning of failed instances of this application. This delay prevents a broken application from soaking up too much of the system.
-socket filename (generated)
Unix:  The filename of the Unix domain socket that the application will use for communication with the web server. The module creates the socket within the directory specified by FastCgiIpcDir. This option makes the application accessible to other applications (e.g. cgi-fcgi) on the same machine or via an external FastCGI application definition (FastCgiExternalServer). If neither the -socket nor the -port options are given, the module generates a Unix domain socket filename. The -socket and -port options are mutually exclusive.
Windows NT:  The name of the named pipe that the application will use for communication with the web server. The module creates the named pipe under the named pipe root specified by FastCgiIpcDir. This option makes the application accessible to other applications (e.g. cgi-fcgi) on the same machine or via an external FastCGI application definition (FastCgiExternalServer). If neither the -socket nor the -port options are given, the module generates a name for the named pipe. The -socket and -port options are mutually exclusive.
-user username|#uid (none)
Unix (only): When FastCgiWrapper is in use, the user is used to invoke the wrapper. The -user option must be used together with -group.

FastCgiConfig

Syntax: FastCgiConfig option [option ...]
Context: server config

The FastCgiConfig directive defines the default parameters for all dynamic FastCGI applications. This directive does not affect static or external applications in any way.

Dynamic applications are not started at server initialization, but upon demand. If the demand is heavy, additional application instances are started. As the demand fades, application instances are killed off. Many of the options govern this process.

Option can be one of (case insensitive):

-appConnTimeout n (0 seconds)
Unix:  The number of seconds to wait for a connection to the FastCGI application to complete or 0 to indicate a blocking connect() should be used. Blocking connect()s have an OS dependent internal timeout. If the timeout expires, a SERVER_ERROR results. For non-zero values, this is the amount of time used in a select() to write to the file descriptor returned by a non-blocking connect(). Non-blocking connect()s are troublesome on many platforms. See also -idle-timeout, it produces similar results but in a more portable manner.
Windows NT:  TCP based applications work as above. Named pipe based applications (static applications configured without the -port option and dynamic applications) use this value successfully to limit the amount of time to wait for a connection (i.e. it's not "troublesome"). By default, this is 90 seconds (FCGI_NAMED_PIPE_CONNECT_TIMEOUT in mod_fastcgi.h).
-autoUpdate (none)
Causes mod_fastcgi to check the modification time of the application on disk before processing each request. If the application on disk has been changed, the process manager is notified and all running instances of the application are killed off. In general, it's preferred that this type of functionality be built-in to the application (e.g. every 100th request it checks to see if there's a newer version on disk and exits if so). There may be an outstanding problem (bug) when this option is used with -restart.
-flush (none)
Force a write to the client as data is received from the application. By default, mod_fastcgi buffers data in order to free the application as quickly as possible.
-gainValue n (0.5)
A floating point value between 0 and 1 used as an exponent in the computation of the exponentially decayed connection times load factor of the currently running dynamic FastCGI applications. Old values are scaled by (1 - gainValue), so making it smaller weights old values more than the current value (which is scaled by gainValue).
-idle-timeout n (30 seconds)
The number of seconds of FastCGI application inactivity allowed before the request is aborted and the event is logged (at the error LogLevel). The inactivity timer applies only as long as a connection is pending with the FastCGI application. If a request is queued to an application, but the application doesn't respond (by writing and flushing) within this period, the request will be aborted. If communication is complete with the application but incomplete with the client (the response is buffered), the timeout does not apply.
-initial-env name[=[value]] (none)
A name-value pair to be passed in the initial environment when instances of applications are spawned. To pass a variable from the Apache environment, don't provide the "=" (if the variable isn't actually in the environment, it will be defined without a value). To define a variable without a value, provide the "=" without any value. The option can be used repeatedly.
-init-start-delay n (1 second)
The minimum number of seconds between the spawning of instances of applications. This delay decreases the demand placed on the system at server initialization.
-killInterval n (300 seconds)
Determines how often the dynamic application instance killing policy is implemented within the process manager. Smaller numbers result in a more aggressive policy, larger numbers a less aggressive policy.
-listen-queue-depth n (100)
The depth of listen() queue (also known as the backlog) shared by all instances of applications. A deeper listen queue allows the server to cope with transient load fluctuations without rejecting requests; it does not increase throughput. Adding additional application instances may increase throughput/performance, depending upon the application and the host.
-maxClassProcesses n (10)
The maximum number of dynamic FastCGI application instances allowed to run for any one FastCGI application. It must be <= to -maxProcesses (this is not programmatically enforced).
-maxProcesses n (50)
The maximum total number of dynamic FastCGI application instances allowed to run at any one time. It must be >= to -maxClassProcesses (this is not programmatically enforced).
-min-server-life n (30)
The minimum number of seconds a dynamic FastCGI application must run for before its restart interval is increased to 600 seconds. The server will get 3 tries to run for at least this number of seconds.
-minProcesses n (5)
The minimum total number of dynamic FastCGI application instances allowed to run at any one time without being killed off by the process manager (due to lack of demand).
-multiThreshold n (50)
An integer between 0 and 100 used to determine whether any one instance of a FastCGI application should be terminated. If the application has more than one instance currently running, this attribute will be used to decide whether one of them should be terminated. If only one instance remains, singleThreshold is used instead.
For historic reasons the mis-spelling multiThreshhold is also accepted.
-pass-header header (none)
The name of a Request Header to be passed in the request environment. This option makes available the contents of headers which are normally not available (e.g. Authorization) to a CGI environment. The passed header names are prefixed with "HTTP_" IAW the CGI specification.
-priority n (0)
The process priority to be assigned to the application instances (using setpriority()).
-processSlack n (5)
If the sum of the number of all currently running dynamic FastCGI applications and processSlack exceeds maxProcesses, the process manager invokes the killing policy. This is to improve performance at higher loads by killing some of the most inactive application instances before reaching maxProcesses.
-restart (none)
Causes the process manager to restart dynamic applications upon failure (similar to static applications).
-restart-delay n (5 seconds)
The minimum number of seconds between the respawning of failed instances of applications. This delay prevents a broken application from soaking up too much of the system.
-singleThreshold n (0)
An integer between 0 and 100 used to determine whether the last instance of a FastCGI application can be terminated. If the process manager computed load factor for the application is lower than the specified threshold, the last instance is terminated. In order to make your executables run in the "idle" mode for the long time, you would specify a value closer to 1, however if memory or CPU time is of primary concern, a value closer to 100 would be more applicable. A value of 0 will prevent the last instance of an application from being terminated; this is the default value, changing it is not recommended (especially if -appConnTimeout is set).
For historic reasons the mis-spelling singleThreshhold is also accepted.
-startDelay n (3 seconds)
The number of seconds the web server waits patiently while trying to connect to a dynamic FastCGI application. If the interval expires, the process manager is notified with hope it will start another instance of the application. The startDelay must be less than appConnTimeout to be effective.
-updateInterval n (300 seconds)
The updateInterval determines how often statistical analysis is performed to determine the fate of dynamic FastCGI applications.

FastCgiExternalServer

Syntax: FastCgiExternalServer filename -host hostname:port [option ...]
FastCgiExternalServer filename -socket filename [option ...]
Context: server config, virtual host

The FastCgiExternalServer directive defines filename as an external FastCGI application. If filename does not begin with a slash (/) then it is assumed to be relative to the ServerRoot. The filename does not have to exist in the local filesystem. URIs that Apache resolves to this filename will be handled by this external FastCGI application.

External FastCGI applications are not started by the process manager, they are presumed to be started and managed "external" to Apache and mod_fastcgi. The FastCGI devkit provides a simple tool, cgi-fcgi, for starting FastCGI applications independent of the server (applications can also be self-starting, see the devkit).

Note: Using FastCgiServer within a VirtualHost does not necessarily limited access to that host. If filename is accessible via other virtual hosts, they too can leverage the same definition.

Option can be one of (case insensitive):

-appConnTimeout n (0 seconds)
Unix:  The number of seconds to wait for a connection to the FastCGI application to complete or 0 to indicate a blocking connect() should be used. Blocking connect()s have an OS dependent internal timeout. If the timeout expires, a SERVER_ERROR results. For non-zero values, this is the amount of time used in a select() to write to the file descriptor returned by a non-blocking connect(). Non-blocking connect()s are troublesome on many platforms. See also -idle-timeout, it produces similar results but in a more portable manner.
Windows NT:  TCP based applications work as above. Named pipe based applications (static applications configured without the -port option and dynamic applications) use this value successfully to limit the amount of time to wait for a connection (i.e. it's not "troublesome"). By default, this is 90 seconds (FCGI_NAMED_PIPE_CONNECT_TIMEOUT in mod_fastcgi.h).
-group groupname|#gid (none)
Unix (only): When FastCgiWrapper is in use, the group is used to invoke the wrapper. The -group option must be used together with -user.
-idle-timeout n (30 seconds)
The number of seconds of FastCGI application inactivity allowed before the request is aborted and the event is logged (at the error LogLevel). The inactivity timer applies only as long as a connection is pending with the FastCGI application. If a request is queued to an application, but the application doesn't respond (by writing and flushing) within this period, the request will be aborted. If communication is complete with the application but incomplete with the client (the response is buffered), the timeout does not apply.
-flush (none)
Force a write to the client as data is received from the application. By default, mod_fastcgi buffers data in order to free the application as quickly as possible.
-host hostname:port (none)
The hostname or IP address and TCP port number (1-65535) the application uses for communication with the web server. The -socket and -host options are mutually exclusive.
-nph
Instructs mod_fastcgi not to parse the headers. See the Apache documentation for more information about nph (non parse header) scripts.
-pass-header header (none)
The name of a Request Header to be passed in the request environment. This option makes available the contents of headers which are normally not available (e.g. Authorization) to a CGI environment. The passed header names are prefixed with "HTTP_" IAW the CGI specification.
-socket filename (none)
Unix:  The filename of the Unix domain socket the application uses for communication with the web server. The filename is relative to the FastCgiIpcDir. The -socket and -port options are mutually exclusive.
Windows NT:  The name of the named pipe the application uses for communicating with the web server. the name is relative to the FastCgiIpcDir. The -socket and -port options are mutually exclusive.
-user username|#uid (none)
Unix (only): When FastCgiWrapper is in use, the user is used to invoke the wrapper. The -user option must be used together with -group.

FastCgiIpcDir

Syntax: Unix:  FastCgiIpcDir directory
Windows NT:  FastCgiIpcDir name
Default: Unix/Apache:  FastCgiIpcDir logs/fastcgi
Unix/Apache2:  FastCgiIpcDir RUNTIMEDIR/fastcgi
Windows NT:  FastCgiIpcDir \\\\.\\pipe\\ModFastCgi\\
Context: server config

Unix:  The FastCgiIpcDir directive specifies directory as the place to store (and in the case of external FastCGI applications, find) the Unix socket files used for communication between the applications and the web server. If the directory does not begin with a slash (/) then it is assumed to be relative to the ServerRoot. If the directory doesn't exist, an attempt is made to create it with appropriate permissions. Do not specify a directory that is not on a local filesystem! If you use the default directory (or another directory within /tmp), mod_fastcgi will break if your system periodically deletes files from /tmp.

Windows NT:  The FastCgiIpcDir directive specifies name as the root for the named pipes used for communication between the application and the web server. The name must be in the form of \\\\.\\pipe\\pipename (notice that the backslashes are escaped). The pipename can contain any character other than a backslash.

The FastCgiIpcDir directive must precede any FastCgiServer or FastCgiExternalServer directives (which make use of Unix sockets). The directory must be readable, writeable, and executable (searchable) by the web server, but otherwise should not be accessible to anyone.

FastCgiIpcDir is typically used move the directory someplace more suitable (than the default) for the platform or to prevent multiple Apache instances from sharing FastCGI application instances.


FastCgiWrapper

Syntax: FastCgiWrapper On | Off | filename
Default: FastCgiWrapper Off
Context: server config

Unix (only): The FastCgiWrapper directive is used to enable support for a wrapper such as suexec (included with Apache in the support directory) or cgiwrap. To use the same wrapper used by Apache, set FastCgiWrapper to On (NOTE - mod_fastcgi cannot reliably determine the wrapper used by Apache when built as a DSO). The On argument requires suexec be enabled in Apache (for CGI). To use a specific wrapper, specify a filename. If the filename does not begin with a slash (/) then it is assumed to be relative to the ServerRoot. The wrapper is used to invoke all FastCGI applications (in the future this directive will have directory context).

When FastCgiWrapper is enabled, no assumptions are made about the target application and thus presence and permissions checks cannot be made. This is the responsibility of the wrapper.

The wrapper is invoked with the following arguments: username, group, application. The username and group are determined as described below. The application is the "filename" Apache resolves the requested URI to (dynamic) or the filename provided as an argument to another FastCGI (server or authorizer) directive. These arguments may or may not be used by the wrapper (e.g. suexec uses them, cgiwrap parses the URI and ignores them). The environment passed to the wrapper is identical to the environment passed when a wrapper is not in use.

When FastCgiWrapper is enabled, the location of static or external FastCGI application directives can be important. Under Apache 1.3, they inherit their user and group from the user and group of the virtual server in which they are defined. User and Group directives must precede FastCGI application definitions. Under Apache 2.0, the -user and -group options to FastCgiServer and FastCgiExternalServer directives must be used (dynamic applications still use the virtual server's user and group).

Note that access to (use of) FastCGI applications is not limited to the virtual server in which they were defined. The application is used to service requests from any virtual server with the same user and group.

If a request is received for a FastCGI application without an existing matching definition already running with the correct user and group, a dynamic instance of the application is started with the correct user and group. This can lead to multiple copies of the same application running with different user/group. If this is a problem, preclude navigation to the application from other virtual servers or configure the virtual servers with the same User and Group.

See the Apache documentation for more information about suexec (make sure you fully understand the security implications).


FastCgiAuthenticator

Syntax: FastCgiAuthenticator filename [-compat]
Context: directory

The FastCgiAuthenticator directive is used to define a FastCGI application as a per-directory authenticator. Authenticators verify the requestor is who he says he is by matching the provided username and password against a list or database of known users and passwords. FastCGI based authenticators are useful primarily when the user database is maintained within an existing independent program or resides on a machine other than the web server.

If the FastCGI application filename does not have a corresponding static or external server definition, it is started as a dynamic FastCGI application. If the filename does not begin with a slash (/) then it is assumed to be relative to the ServerRoot.

FastCgiAuthenticator is used within Directory or Location containers and must include an AuthType and AuthName directive. Only the Basic user authentication type is supported. It must be accompanied by a require or FastCgiAuthorizer directive in order to work correctly.

<Directory htdocs/protected>
    AuthType Basic
    AuthName ProtectedRealm
    FastCgiAuthenticator fcgi-bin/authenticator
    require valid-user
</Directory>

mod_fastcgi sends nearly all of the standard environment variables typically available to CGI/FastCGI request handlers. All headers returned by a FastCGI authentication application in a successful response (Status: 200) are passed to sub-processes (CGI/FastCGI invocations) as environment variables. All headers returned in an unsuccessful response are passed on to the client. FastCGI specification compliant behavior can be obtained by using the -compat option.

mod_fastcgi sets the environment variable "FCGI_APACHE_ROLE" to "AUTHENTICATOR" to indicate which (Apache specific) authorizer phase is being performed.

Custom failure responses from FastCGI authorizer applications are not (yet?) supported. See the ErrorDocument directive for a workaround (a FastCGI application can serve the document).


FastCgiAuthenticatorAuthoritative

Syntax: FastCgiAuthenticatorAuthoritative On | Off
Default: FastCgiAuthenticatorAuthoritative On
Context: directory

Setting the FastCgiAuthenticatorAuthoritative directive explicitly to Off allows authentication to be passed on to lower level modules (as defined in the Configuration and modules.c files) if the FastCGI application fails to authenticate the user.

A common use for this is in conjunction with a well protected AuthUserFile containing a few (administration related) users.

By default, control is not passed on and an unknown user will result in an Authorization Required reply. Disabling the default should be carefully considered.


FastCgiAuthorizer

Syntax: FastCgiAuthorizer filename [-compat]
Context: directory

The FastCgiAuthorizer directive is used to define a FastCGI application as a per-directory authorizer. Authorizers validate whether an authenticated requestor is allowed access to the requested resource. FastCGI based authorizers are useful primarily when there is a dynamic component to the authorization decision such as a time of day or whether or not the user has paid his bills.

If the FastCGI application filename does not have a corresponding static or external server definition, it is started as a dynamic FastCGI application. If the filename does not begin with a slash (/) then it is assumed to be relative to the ServerRoot.

FastCgiAuthorizer is used within Directory or Location containers and must include an AuthType and AuthName directive. It must be accompanied by an authentication directive such as FastCgiAuthenticator, AuthUserFile, AuthDBUserFile or AuthDBMUserFile in order to work correctly.

<Directory htdocs/protected>
    AuthType Basic
    AuthName ProtectedRealm
    AuthDBMUserFile conf/authentication-database
    FastCgiAuthorizer fcgi-bin/authorizer
</Directory>

mod_fastcgi sends nearly all of the standard environment variables typically available to CGI/FastCGI request handlers. All headers returned by a FastCGI authorizer application in a successful response (Status: 200) are passed to sub-processes (CGI/FastCGI invocations) as environment variables. All headers returned in an unsuccessful response are passed on to the client. FastCGI specification compliant behavior can be obtained by using the -compat option.

mod_fastcgi sets the environment variable "FCGI_APACHE_ROLE" to "AUTHORIZER" to indicate which (Apache specific) authorizer phase is being performed.

Custom failure responses from FastCGI authorizer applications are not (yet?) supported. See the ErrorDocument directive for a workaround (a FastCGI application can serve the document).


FastCgiAuthorizerAuthoritative

Syntax: FastCgiAuthorizerAuthoritative On | Off
Default: FastCgiAuthorizerAuthoritative On
Context: directory

Setting the FastCgiAuthorizerAuthoritative directive explicitly to Off allows authorization to be passed on to lower level modules (as defined in the Configuration and modules.c files) if the FastCGI application fails to authorize the user.

By default, control is not passed on and an unauthorized user will result in an Authorization Required reply. Disabling the default should be carefully considered.


FastCgiAccessChecker

Syntax: FastCgiAccessChecker filename [-compat]
Context: directory

The FastCgiAccessChecker (suggestions for a better name are welcome) directive is used to define a FastCGI application as a per-directory access validator. The Apache Access phase precede user authentication and thus the decision to (dis)allow access to the requested resource is based on the HTTP headers submitted with the request. FastCGI based authorizers are useful primarily when there is a dynamic component to the access validation decision such as a time of day or whether or not a domain has paid his bills.

If the FastCGI application filename does not have a corresponding static or external server definition, it is started as a dynamic FastCGI application. If the filename does not begin with a slash (/) then it is assumed to be relative to the ServerRoot.

FastCgiAccessChecker is used within Directory or Location containers.

<Directory htdocs/protected>
    FastCgiAccessChecker fcgi-bin/access-checker
</Directory>

mod_fastcgi sends nearly all of the standard environment variables typically available to CGI/FastCGI request handlers. All headers returned by a FastCGI access-checker application in a successful response (Status: 200) are passed to sub-processes (CGI/FastCGI invocations) as environment variables. All headers returned in an unsuccessful response are passed on to the client. FastCGI specification compliant behavior can be obtained by using the -compat option.

mod_fastcgi sets the environment variable "FCGI_APACHE_ROLE" to "ACCESS_CHECKER" to indicate which (Apache specific) authorizer phase is being performed.

Custom failure responses from FastCGI authorizer applications are not (yet?) supported. See the ErrorDocument directive for a workaround (a FastCGI application can serve the document).


FastCgiAccessCheckerAuthoritative

Syntax: FastCgiAccessCheckerAuthoritative On | Off
Default: FastCgiAccessCheckerAuthoritative On
Context: directory

Setting the FastCgiAccessCheckerAuthoritative directive explicitly to Off allows access checking to be passed on to lower level modules (as defined in the Configuration and modules.c files) if the FastCGI application fails to allow access.

By default, control is not passed on and a failed access check will result in a Forbidden reply. Disabling the default should be carefully considered.


www.FastCGI.com

mod_fastcgi-SNAP-0910052141/fcgi_protocol.c0000664000635400022240000004732411066200655020563 0ustar robsfastcgi00000000000000/* * $Id: fcgi_protocol.c,v 1.27 2008/09/23 14:48:13 robs Exp $ */ #include "fcgi.h" #include "fcgi_protocol.h" #ifdef APACHE2 #include "apr_lib.h" #endif #ifdef WIN32 #pragma warning( disable : 4706) #endif /******************************************************************************* * Build and queue a FastCGI message header. It is the caller's * responsibility to make sure that there's enough space in the buffer, and * that the data bytes (specified by 'len') are queued immediately following * this header. */ static void queue_header(fcgi_request *fr, unsigned char type, unsigned int len) { FCGI_Header header; ASSERT(type > 0); ASSERT(type <= FCGI_MAXTYPE); ASSERT(len <= 0xffff); ASSERT(BufferFree(fr->serverOutputBuffer) >= sizeof(FCGI_Header)); /* Assemble and queue the packet header. */ header.version = FCGI_VERSION; header.type = type; header.requestIdB1 = (unsigned char) (fr->requestId >> 8); header.requestIdB0 = (unsigned char) fr->requestId; header.contentLengthB1 = (unsigned char) (len / 256); /* MSB */ header.contentLengthB0 = (unsigned char) (len % 256); /* LSB */ header.paddingLength = 0; header.reserved = 0; fcgi_buf_add_block(fr->serverOutputBuffer, (char *) &header, sizeof(FCGI_Header)); } /******************************************************************************* * Build a FCGI_BeginRequest message body. */ static void build_begin_request(unsigned int role, unsigned char keepConnection, FCGI_BeginRequestBody *body) { ASSERT((role >> 16) == 0); body->roleB1 = (unsigned char) (role >> 8); body->roleB0 = (unsigned char) role; body->flags = (unsigned char) ((keepConnection) ? FCGI_KEEP_CONN : 0); memset(body->reserved, 0, sizeof(body->reserved)); } /******************************************************************************* * Build and queue a FastCGI "Begin Request" message. */ void fcgi_protocol_queue_begin_request(fcgi_request *fr) { FCGI_BeginRequestBody body; int bodySize = sizeof(FCGI_BeginRequestBody); /* We should be the first ones to use this buffer */ ASSERT(BufferLength(fr->serverOutputBuffer) == 0); build_begin_request(fr->role, FALSE, &body); queue_header(fr, FCGI_BEGIN_REQUEST, bodySize); fcgi_buf_add_block(fr->serverOutputBuffer, (char *) &body, bodySize); } /******************************************************************************* * Build a FastCGI name-value pair (env) header. */ static void build_env_header(int nameLen, int valueLen, unsigned char *headerBuffPtr, int *headerLenPtr) { unsigned char *startHeaderBuffPtr = headerBuffPtr; ASSERT(nameLen >= 0); if (nameLen < 0x80) { *headerBuffPtr++ = (unsigned char) nameLen; } else { *headerBuffPtr++ = (unsigned char) ((nameLen >> 24) | 0x80); *headerBuffPtr++ = (unsigned char) (nameLen >> 16); *headerBuffPtr++ = (unsigned char) (nameLen >> 8); *headerBuffPtr++ = (unsigned char) nameLen; } ASSERT(valueLen >= 0); if (valueLen < 0x80) { *headerBuffPtr++ = (unsigned char) valueLen; } else { *headerBuffPtr++ = (unsigned char) ((valueLen >> 24) | 0x80); *headerBuffPtr++ = (unsigned char) (valueLen >> 16); *headerBuffPtr++ = (unsigned char) (valueLen >> 8); *headerBuffPtr++ = (unsigned char) valueLen; } *headerLenPtr = headerBuffPtr - startHeaderBuffPtr; } /* A static fn stolen from Apache's util_script.c... * Obtain the Request-URI from the original request-line, returning * a new string from the request pool containing the URI or "". */ static char *apache_original_uri(request_rec *r) { char *first, *last; if (r->the_request == NULL) return (char *) ap_pcalloc(r->pool, 1); first = r->the_request; /* use the request-line */ while (*first && !ap_isspace(*first)) ++first; /* skip over the method */ while (ap_isspace(*first)) ++first; /* and the space(s) */ last = first; while (*last && !ap_isspace(*last)) ++last; /* end at next whitespace */ return ap_pstrndup(r->pool, first, last - first); } /* Based on Apache's ap_add_cgi_vars() in util_script.c. * Apache's spins in sub_req_lookup_uri() trying to setup PATH_TRANSLATED, * so we just don't do that part. */ static void add_auth_cgi_vars(request_rec *r, const int compat) { table *e = r->subprocess_env; ap_table_setn(e, "GATEWAY_INTERFACE", "CGI/1.1"); ap_table_setn(e, "SERVER_PROTOCOL", r->protocol); ap_table_setn(e, "REQUEST_METHOD", r->method); ap_table_setn(e, "QUERY_STRING", r->args ? r->args : ""); ap_table_setn(e, "REQUEST_URI", apache_original_uri(r)); /* The FastCGI spec precludes sending of CONTENT_LENGTH, PATH_INFO, * PATH_TRANSLATED, and SCRIPT_NAME (for some reason?). PATH_TRANSLATED we * don't have, its the variable that causes Apache to break trying to set * up (and thus the reason this fn exists vs. using ap_add_cgi_vars()). */ if (compat) { ap_table_unset(e, "CONTENT_LENGTH"); return; } /* Note that the code below special-cases scripts run from includes, * because it "knows" that the sub_request has been hacked to have the * args and path_info of the original request, and not any that may have * come with the script URI in the include command. Ugh. */ if (!strcmp(r->protocol, "INCLUDED")) { ap_table_setn(e, "SCRIPT_NAME", r->uri); if (r->path_info && *r->path_info) ap_table_setn(e, "PATH_INFO", r->path_info); } else if (!r->path_info || !*r->path_info) ap_table_setn(e, "SCRIPT_NAME", r->uri); else { int path_info_start = ap_find_path_info(r->uri, r->path_info); ap_table_setn(e, "SCRIPT_NAME", ap_pstrndup(r->pool, r->uri, path_info_start)); ap_table_setn(e, "PATH_INFO", r->path_info); } } /* copied from util_script.c */ static char *http2env(pool *a, const char *w) { char *res = (char *) ap_palloc(a, sizeof("HTTP_") + strlen(w)); char *cp = res; char c; *cp++ = 'H'; *cp++ = 'T'; *cp++ = 'T'; *cp++ = 'P'; *cp++ = '_'; while ((c = *w++) != 0) { if (!ap_isalnum(c)) { *cp++ = '_'; } else { *cp++ = (char) ap_toupper(c); } } *cp = 0; return res; } static void add_pass_header_vars(fcgi_request *fr) { const array_header *ph = fr->dynamic ? dynamic_pass_headers : fr->fs->pass_headers; if (ph) { const char **elt = (const char **)ph->elts; int i = ph->nelts; for ( ; i; --i, ++elt) { const char *val = ap_table_get(fr->r->headers_in, *elt); if (val) { const char *key = *elt; #ifndef USE_BROKEN_PASS_HEADER key = http2env(fr->r->pool, key); #endif ap_table_setn(fr->r->subprocess_env, key, val); } } } } /******************************************************************************* * Build and queue the environment name-value pairs. Returns TRUE if the * complete ENV was buffered, FALSE otherwise. Note: envp is updated to * reflect the current position in the ENV. */ int fcgi_protocol_queue_env(request_rec *r, fcgi_request *fr, env_status *env) { int charCount; if (env->envp == NULL) { ap_add_common_vars(r); add_pass_header_vars(fr); if (fr->role == FCGI_RESPONDER) ap_add_cgi_vars(r); else add_auth_cgi_vars(r, fr->auth_compat); env->envp = ap_create_environment(r->pool, r->subprocess_env); env->pass = PREP; } while (*env->envp) { switch (env->pass) { case PREP: env->equalPtr = strchr(*env->envp, '='); ASSERT(env->equalPtr != NULL); env->nameLen = env->equalPtr - *env->envp; env->valueLen = strlen(++env->equalPtr); build_env_header(env->nameLen, env->valueLen, env->headerBuff, &env->headerLen); env->totalLen = env->headerLen + env->nameLen + env->valueLen; env->pass = HEADER; /* drop through */ case HEADER: if (BufferFree(fr->serverOutputBuffer) < (int)(sizeof(FCGI_Header) + env->headerLen)) { return (FALSE); } queue_header(fr, FCGI_PARAMS, env->totalLen); fcgi_buf_add_block(fr->serverOutputBuffer, (char *)env->headerBuff, env->headerLen); env->pass = NAME; /* drop through */ case NAME: charCount = fcgi_buf_add_block(fr->serverOutputBuffer, *env->envp, env->nameLen); if (charCount != env->nameLen) { *env->envp += charCount; env->nameLen -= charCount; return (FALSE); } env->pass = VALUE; /* drop through */ case VALUE: charCount = fcgi_buf_add_block(fr->serverOutputBuffer, env->equalPtr, env->valueLen); if (charCount != env->valueLen) { env->equalPtr += charCount; env->valueLen -= charCount; return (FALSE); } env->pass = PREP; } ++env->envp; } if (BufferFree(fr->serverOutputBuffer) < sizeof(FCGI_Header)) { return(FALSE); } queue_header(fr, FCGI_PARAMS, 0); return(TRUE); } /******************************************************************************* * Queue data from the client input buffer to the FastCGI server output * buffer (encapsulating the data in FastCGI protocol messages). */ void fcgi_protocol_queue_client_buffer(fcgi_request *fr) { int movelen; int in_len, out_free; if (fr->eofSent) return; /* * If there's some client data and room for at least one byte * of data in the output buffer (after protocol overhead), then * move some data to the output buffer. */ in_len = BufferLength(fr->clientInputBuffer); out_free = max(0, BufferFree(fr->serverOutputBuffer) - sizeof(FCGI_Header)); movelen = min(in_len, out_free); if (movelen > 0) { queue_header(fr, FCGI_STDIN, movelen); fcgi_buf_get_to_buf(fr->serverOutputBuffer, fr->clientInputBuffer, movelen); } /* * If all the client data has been sent, and there's room * in the output buffer, indicate EOF. */ if (movelen == in_len && fr->expectingClientContent <= 0 && BufferFree(fr->serverOutputBuffer) >= sizeof(FCGI_Header)) { queue_header(fr, FCGI_STDIN, 0); fr->eofSent = TRUE; } } /******************************************************************************* * Read FastCGI protocol messages from the FastCGI server input buffer into * fr->header when parsing headers, to fr->fs_stderr when reading stderr data, * or to the client output buffer otherwises. */ int fcgi_protocol_dequeue(pool *p, fcgi_request *fr) { FCGI_Header header; int len; while (BufferLength(fr->serverInputBuffer) > 0) { /* * State #1: looking for the next complete packet header. */ if (fr->gotHeader == FALSE) { if (BufferLength(fr->serverInputBuffer) < sizeof(FCGI_Header)) { return OK; } fcgi_buf_get_to_block(fr->serverInputBuffer, (char *) &header, sizeof(FCGI_Header)); /* * XXX: Better handling of packets with other version numbers * and other packet problems. */ if (header.version != FCGI_VERSION) { ap_log_rerror(FCGI_LOG_ERR_NOERRNO, fr->r, "FastCGI: comm with server \"%s\" aborted: protocol error: invalid version: %d != FCGI_VERSION(%d)", fr->fs_path, header.version, FCGI_VERSION); return HTTP_INTERNAL_SERVER_ERROR; } if (header.type > FCGI_MAXTYPE) { ap_log_rerror(FCGI_LOG_ERR_NOERRNO, fr->r, "FastCGI: comm with server \"%s\" aborted: protocol error: invalid type: %d > FCGI_MAXTYPE(%d)", fr->fs_path, header.type, FCGI_MAXTYPE); return HTTP_INTERNAL_SERVER_ERROR; } fr->packetType = header.type; fr->dataLen = (header.contentLengthB1 << 8) + header.contentLengthB0; fr->gotHeader = TRUE; fr->paddingLen = header.paddingLength; } /* * State #2: got a header, and processing packet bytes. */ len = min(fr->dataLen, BufferLength(fr->serverInputBuffer)); ASSERT(len >= 0); switch (fr->packetType) { case FCGI_STDOUT: if (len > 0) { switch(fr->parseHeader) { case SCAN_CGI_READING_HEADERS: fcgi_buf_get_to_array(fr->serverInputBuffer, fr->header, len); break; case SCAN_CGI_FINISHED: len = min(BufferFree(fr->clientOutputBuffer), len); if (len > 0) { fcgi_buf_get_to_buf(fr->clientOutputBuffer, fr->serverInputBuffer, len); } else { return OK; } break; default: /* Toss data on the floor */ fcgi_buf_removed(fr->serverInputBuffer, len); break; } fr->dataLen -= len; } break; case FCGI_STDERR: if (fr->fs_stderr == NULL) { fr->fs_stderr = ap_palloc(p, FCGI_SERVER_MAX_STDERR_LINE_LEN + 1); } /* We're gonna consume all thats here */ fr->dataLen -= len; while (len > 0) { char *null, *end, *start = fr->fs_stderr; /* Get as much as will fit in the buffer */ int get_len = min(len, FCGI_SERVER_MAX_STDERR_LINE_LEN - fr->fs_stderr_len); fcgi_buf_get_to_block(fr->serverInputBuffer, start + fr->fs_stderr_len, get_len); len -= get_len; fr->fs_stderr_len += get_len; *(start + fr->fs_stderr_len) = '\0'; /* Disallow nulls, we could be nicer but this is the motivator */ while ((null = memchr(start, '\0', fr->fs_stderr_len))) { int discard = ++null - start; ap_log_rerror(FCGI_LOG_ERR_NOERRNO, fr->r, "FastCGI: server \"%s\" sent a null character in the stderr stream!?, " "discarding %d characters of stderr", fr->fs_path, discard); start = null; fr->fs_stderr_len -= discard; } /* Print as much as possible */ while ((end = strpbrk(start, "\r\n"))) { if (start != end) { *end = '\0'; ap_log_rerror(FCGI_LOG_ERR_NOERRNO, fr->r, "FastCGI: server \"%s\" stderr: %s", fr->fs_path, start); } end++; end += strspn(end, "\r\n"); fr->fs_stderr_len -= (end - start); start = end; } if (fr->fs_stderr_len) { if (start != fr->fs_stderr) { /* Move leftovers down */ memmove(fr->fs_stderr, start, fr->fs_stderr_len); } else if (fr->fs_stderr_len == FCGI_SERVER_MAX_STDERR_LINE_LEN) { /* Full buffer, dump it and complain */ ap_log_rerror(FCGI_LOG_ERR_NOERRNO, fr->r, "FastCGI: server \"%s\" stderr: %s", fr->fs_path, fr->fs_stderr); ap_log_rerror(FCGI_LOG_WARN_NOERRNO, fr->r, "FastCGI: too much stderr received from server \"%s\", " "increase FCGI_SERVER_MAX_STDERR_LINE_LEN (%d) and rebuild " "or use \"\\n\" to terminate lines", fr->fs_path, FCGI_SERVER_MAX_STDERR_LINE_LEN); fr->fs_stderr_len = 0; } } } break; case FCGI_END_REQUEST: if (!fr->readingEndRequestBody) { if (fr->dataLen != sizeof(FCGI_EndRequestBody)) { ap_log_rerror(FCGI_LOG_ERR_NOERRNO, fr->r, "FastCGI: comm with server \"%s\" aborted: protocol error: " "invalid FCGI_END_REQUEST size: " "%d != sizeof(FCGI_EndRequestBody)(%d)", fr->fs_path, fr->dataLen, sizeof(FCGI_EndRequestBody)); return HTTP_INTERNAL_SERVER_ERROR; } fr->readingEndRequestBody = TRUE; } if (len>0) { fcgi_buf_get_to_buf(fr->erBufPtr, fr->serverInputBuffer, len); fr->dataLen -= len; } if (fr->dataLen == 0) { FCGI_EndRequestBody *erBody = &fr->endRequestBody; fcgi_buf_get_to_block( fr->erBufPtr, (char *) &fr->endRequestBody, sizeof(FCGI_EndRequestBody)); if (erBody->protocolStatus != FCGI_REQUEST_COMPLETE) { /* * XXX: What to do with FCGI_OVERLOADED? */ ap_log_rerror(FCGI_LOG_ERR_NOERRNO, fr->r, "FastCGI: comm with server \"%s\" aborted: protocol error: invalid FCGI_END_REQUEST status: " "%d != FCGI_REQUEST_COMPLETE(%d)", fr->fs_path, erBody->protocolStatus, FCGI_REQUEST_COMPLETE); return HTTP_INTERNAL_SERVER_ERROR; } fr->exitStatus = (erBody->appStatusB3 << 24) + (erBody->appStatusB2 << 16) + (erBody->appStatusB1 << 8) + (erBody->appStatusB0 ); fr->exitStatusSet = TRUE; fr->readingEndRequestBody = FALSE; } break; case FCGI_GET_VALUES_RESULT: /* XXX coming soon */ case FCGI_UNKNOWN_TYPE: /* XXX coming soon */ /* * XXX Ignore unknown packet types from the FastCGI server. */ default: fcgi_buf_toss(fr->serverInputBuffer, len); fr->dataLen -= len; break; } /* switch */ /* * Discard padding, then start looking for * the next header. */ if (fr->dataLen == 0) { if (fr->paddingLen > 0) { len = min(fr->paddingLen, BufferLength(fr->serverInputBuffer)); fcgi_buf_toss(fr->serverInputBuffer, len); fr->paddingLen -= len; } if (fr->paddingLen == 0) { fr->gotHeader = FALSE; } } } /* while */ return OK; } mod_fastcgi-SNAP-0910052141/mod_fastcgi.c0000664000635400022240000026405311105572047020212 0ustar robsfastcgi00000000000000/* * mod_fastcgi.c -- * * Apache server module for FastCGI. * * $Id: mod_fastcgi.c,v 1.169 2008/11/09 14:31:03 robs Exp $ * * Copyright (c) 1995-1996 Open Market, Inc. * * See the file "LICENSE.TERMS" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * * * Patches for Apache-1.1 provided by * Ralf S. Engelschall * * * Patches for Linux provided by * Scott Langley * * * Patches for suexec handling by * Brian Grossman and * Rob Saccoccio */ /* * Module design notes. * * 1. Restart cleanup. * * mod_fastcgi spawns several processes: one process manager process * and several application processes. None of these processes * handle SIGHUP, so they just go away when the Web server performs * a restart (as Apache does every time it starts.) * * In order to allow the process manager to properly cleanup the * running fastcgi processes (without being disturbed by Apache), * an intermediate process was introduced. The diagram is as follows; * * ApacheWS --> MiddleProc --> ProcMgr --> FCGI processes * * On a restart, ApacheWS sends a SIGKILL to MiddleProc and then * collects it via waitpid(). The ProcMgr periodically checks for * its parent (via getppid()) and if it does not have one, as in * case when MiddleProc has terminated, ProcMgr issues a SIGTERM * to all FCGI processes, waitpid()s on them and then exits, so it * can be collected by init(1). Doing it any other way (short of * changing Apache API), results either in inconsistent results or * in generation of zombie processes. * * XXX: How does Apache 1.2 implement "gentle" restart * that does not disrupt current connections? How does * gentle restart interact with restart cleanup? * * 2. Request timeouts. * * Earlier versions of this module used ap_soft_timeout() rather than * ap_hard_timeout() and ate FastCGI server output until it completed. * This precluded the FastCGI server from having to implement a * SIGPIPE handler, but meant hanging the application longer than * necessary. SIGPIPE handler now must be installed in ALL FastCGI * applications. The handler should abort further processing and go * back into the accept() loop. * * Although using ap_soft_timeout() is better than ap_hard_timeout() * we have to be more careful about SIGINT handling and subsequent * processing, so, for now, make it hard. */ #include "fcgi.h" #ifdef APACHE2 #ifndef WIN32 #include #if APR_HAVE_CTYPE_H #include #endif #include "unixd.h" #endif #endif #ifndef timersub #define timersub(a, b, result) \ do { \ (result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \ (result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \ if ((result)->tv_usec < 0) { \ --(result)->tv_sec; \ (result)->tv_usec += 1000000; \ } \ } while (0) #endif /* * Global variables */ pool *fcgi_config_pool; /* the config pool */ server_rec *fcgi_apache_main_server; const char *fcgi_wrapper = NULL; /* wrapper path */ uid_t fcgi_user_id; /* the run uid of Apache & PM */ gid_t fcgi_group_id; /* the run gid of Apache & PM */ fcgi_server *fcgi_servers = NULL; /* AppClasses */ char *fcgi_socket_dir = NULL; /* default FastCgiIpcDir */ char *fcgi_dynamic_dir = NULL; /* directory for the dynamic * fastcgi apps' sockets */ #ifdef WIN32 #pragma warning( disable : 4706 4100 4127) fcgi_pm_job *fcgi_dynamic_mbox = NULL; HANDLE *fcgi_dynamic_mbox_mutex = NULL; HANDLE fcgi_pm_thread = INVALID_HANDLE_VALUE; #else int fcgi_pm_pipe[2] = { -1, -1 }; pid_t fcgi_pm_pid = -1; #endif char *fcgi_empty_env = NULL; u_int dynamicMaxProcs = FCGI_DEFAULT_MAX_PROCS; int dynamicMinProcs = FCGI_DEFAULT_MIN_PROCS; int dynamicMaxClassProcs = FCGI_DEFAULT_MAX_CLASS_PROCS; u_int dynamicKillInterval = FCGI_DEFAULT_KILL_INTERVAL; u_int dynamicUpdateInterval = FCGI_DEFAULT_UPDATE_INTERVAL; float dynamicGain = FCGI_DEFAULT_GAIN; int dynamicThreshold1 = FCGI_DEFAULT_THRESHOLD_1; int dynamicThresholdN = FCGI_DEFAULT_THRESHOLD_N; u_int dynamicPleaseStartDelay = FCGI_DEFAULT_START_PROCESS_DELAY; u_int dynamicAppConnectTimeout = FCGI_DEFAULT_APP_CONN_TIMEOUT; char **dynamicEnvp = &fcgi_empty_env; u_int dynamicProcessSlack = FCGI_DEFAULT_PROCESS_SLACK; int dynamicAutoRestart = FCGI_DEFAULT_RESTART_DYNAMIC; int dynamicAutoUpdate = FCGI_DEFAULT_AUTOUPDATE; int dynamicFlush = FCGI_FLUSH; u_int dynamicListenQueueDepth = FCGI_DEFAULT_LISTEN_Q; u_int dynamicInitStartDelay = DEFAULT_INIT_START_DELAY; u_int dynamicRestartDelay = FCGI_DEFAULT_RESTART_DELAY; array_header *dynamic_pass_headers = NULL; u_int dynamic_idle_timeout = FCGI_DEFAULT_IDLE_TIMEOUT; int dynamicMinServerLife = FCGI_DEFAULT_MIN_SERVER_LIFE; /******************************************************************************* * Construct a message and write it to the pm_pipe. */ static void send_to_pm(const char id, const char * const fs_path, const char *user, const char * const group, const unsigned long q_usec, const unsigned long req_usec) { #ifdef WIN32 fcgi_pm_job *job = NULL; if (!(job = (fcgi_pm_job *) malloc(sizeof(fcgi_pm_job)))) return; #else static int failed_count = 0; int buflen = 0; char buf[FCGI_MAX_MSG_LEN]; #endif if (strlen(fs_path) > FCGI_MAXPATH) { ap_log_error(FCGI_LOG_ERR_NOERRNO, fcgi_apache_main_server, "FastCGI: the path \"%s\" is too long (>%d) for a dynamic server", fs_path, FCGI_MAXPATH); return; } switch(id) { case FCGI_SERVER_START_JOB: case FCGI_SERVER_RESTART_JOB: #ifdef WIN32 job->id = id; job->fs_path = strdup(fs_path); job->user = strdup(user); job->group = strdup(group); job->qsec = 0L; job->start_time = 0L; #else buflen = sprintf(buf, "%c %s %s %s*", id, fs_path, user, group); #endif break; case FCGI_REQUEST_TIMEOUT_JOB: #ifdef WIN32 job->id = id; job->fs_path = strdup(fs_path); job->user = strdup(user); job->group = strdup(group); job->qsec = 0L; job->start_time = 0L; #else buflen = sprintf(buf, "%c %s %s %s*", id, fs_path, user, group); #endif break; case FCGI_REQUEST_COMPLETE_JOB: #ifdef WIN32 job->id = id; job->fs_path = strdup(fs_path); job->qsec = q_usec; job->start_time = req_usec; job->user = strdup(user); job->group = strdup(group); #else buflen = sprintf(buf, "%c %s %s %s %lu %lu*", id, fs_path, user, group, q_usec, req_usec); #endif break; } #ifdef WIN32 if (fcgi_pm_add_job(job)) return; SetEvent(fcgi_event_handles[MBOX_EVENT]); #else ASSERT(buflen <= FCGI_MAX_MSG_LEN); /* There is no apache flag or function that can be used to id * restart/shutdown pending so ignore the first few failures as * once it breaks it will stay broke */ if (write(fcgi_pm_pipe[1], (const void *)buf, buflen) != buflen && failed_count++ > 10) { ap_log_error(FCGI_LOG_WARN, fcgi_apache_main_server, "FastCGI: write() to PM failed (ignore if a restart or shutdown is pending)"); } #endif } /* *---------------------------------------------------------------------- * * init_module * * An Apache module initializer, called by the Apache core * after reading the server config. * * Start the process manager no matter what, since there may be a * request for dynamic FastCGI applications without any being * configured as static applications. Also, check for the existence * and create if necessary a subdirectory into which all dynamic * sockets will go. * *---------------------------------------------------------------------- */ #ifdef APACHE2 static apcb_t init_module(apr_pool_t * p, apr_pool_t * plog, apr_pool_t * tp, server_rec * s) #else static apcb_t init_module(server_rec *s, pool *p) #endif { #ifndef WIN32 const char *err; #endif /* Register to reset to default values when the config pool is cleaned */ ap_block_alarms(); ap_register_cleanup(p, NULL, fcgi_config_reset_globals, ap_null_cleanup); ap_unblock_alarms(); #ifdef APACHE2 ap_add_version_component(p, "mod_fastcgi/" MOD_FASTCGI_VERSION); #else ap_add_version_component("mod_fastcgi/" MOD_FASTCGI_VERSION); #endif fcgi_config_set_fcgi_uid_n_gid(1); /* keep these handy */ fcgi_config_pool = p; fcgi_apache_main_server = s; #ifdef WIN32 if (fcgi_socket_dir == NULL) fcgi_socket_dir = DEFAULT_SOCK_DIR; fcgi_dynamic_dir = ap_pstrcat(p, fcgi_socket_dir, "dynamic", NULL); #else if (fcgi_socket_dir == NULL) fcgi_socket_dir = ap_server_root_relative(p, DEFAULT_SOCK_DIR); /* Create Unix/Domain socket directory */ if ((err = fcgi_config_make_dir(p, fcgi_socket_dir))) ap_log_error(FCGI_LOG_ERR, s, "FastCGI: %s", err); /* Create Dynamic directory */ if ((err = fcgi_config_make_dynamic_dir(p, 1))) ap_log_error(FCGI_LOG_ERR, s, "FastCGI: %s", err); /* Spawn the PM only once. Under Unix, Apache calls init() routines * twice, once before detach() and once after. Win32 doesn't detach. * Under DSO, DSO modules are unloaded between the two init() calls. * Under Unix, the -X switch causes two calls to init() but no detach * (but all subprocesses are wacked so the PM is toasted anyway)! */ #ifdef APACHE2 { void * first_pass; apr_pool_userdata_get(&first_pass, "mod_fastcgi", s->process->pool); if (first_pass == NULL) { apr_pool_userdata_set((const void *)1, "mod_fastcgi", apr_pool_cleanup_null, s->process->pool); return APCB_OK; } } #else /* !APACHE2 */ if (ap_standalone && ap_restart_time == 0) return; #endif /* Create the pipe for comm with the PM */ if (pipe(fcgi_pm_pipe) < 0) { ap_log_error(FCGI_LOG_ERR, s, "FastCGI: pipe() failed"); } /* Start the Process Manager */ #ifdef APACHE2 { apr_proc_t * proc = apr_palloc(p, sizeof(*proc)); apr_status_t rv; rv = apr_proc_fork(proc, tp); if (rv == APR_INCHILD) { /* child */ fcgi_pm_main(NULL); exit(1); } else if (rv != APR_INPARENT) { return rv; } /* parent */ apr_pool_note_subprocess(p, proc, APR_KILL_ONLY_ONCE); } #else /* !APACHE2 */ fcgi_pm_pid = ap_spawn_child(p, fcgi_pm_main, NULL, kill_only_once, NULL, NULL, NULL); if (fcgi_pm_pid <= 0) { ap_log_error(FCGI_LOG_ALERT, s, "FastCGI: can't start the process manager, spawn_child() failed"); } #endif /* !APACHE2 */ close(fcgi_pm_pipe[0]); #endif /* !WIN32 */ return APCB_OK; } #ifdef WIN32 #ifdef APACHE2 static apcb_t fcgi_child_exit(void * dc) #else static apcb_t fcgi_child_exit(server_rec *dc0, pool *dc1) #endif { /* Signal the PM thread to exit*/ SetEvent(fcgi_event_handles[TERM_EVENT]); /* Waiting on pm thread to exit */ WaitForSingleObject(fcgi_pm_thread, INFINITE); return APCB_OK; } #endif /* WIN32 */ #ifdef APACHE2 static void fcgi_child_init(apr_pool_t * p, server_rec * dc) #else static void fcgi_child_init(server_rec *dc, pool *p) #endif { #ifdef WIN32 /* Create the MBOX, TERM, and WAKE event handlers */ fcgi_event_handles[0] = CreateEvent(NULL, FALSE, FALSE, NULL); if (fcgi_event_handles[0] == NULL) { ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server, "FastCGI: CreateEvent() failed"); } fcgi_event_handles[1] = CreateEvent(NULL, FALSE, FALSE, NULL); if (fcgi_event_handles[1] == NULL) { ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server, "FastCGI: CreateEvent() failed"); } fcgi_event_handles[2] = CreateEvent(NULL, FALSE, FALSE, NULL); if (fcgi_event_handles[2] == NULL) { ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server, "FastCGI: CreateEvent() failed"); } /* Create the mbox mutex (PM - request threads) */ fcgi_dynamic_mbox_mutex = CreateMutex(NULL, FALSE, NULL); if (fcgi_dynamic_mbox_mutex == NULL) { ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server, "FastCGI: CreateMutex() failed"); } /* Spawn of the process manager thread */ fcgi_pm_thread = (HANDLE) _beginthread(fcgi_pm_main, 0, NULL); if (fcgi_pm_thread == (HANDLE) -1) { ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server, "_beginthread() failed to spawn the process manager"); } #ifdef APACHE2 apr_pool_cleanup_register(p, NULL, fcgi_child_exit, fcgi_child_exit); #endif #endif } /* *---------------------------------------------------------------------- * * get_header_line -- * * Terminate a line: scan to the next newline, scan back to the * first non-space character and store a terminating zero. Return * the next character past the end of the newline. * * If the end of the string is reached, ASSERT! * * If the FIRST character(s) in the line are '\n' or "\r\n", the * first character is replaced with a NULL and next character * past the newline is returned. NOTE: this condition supercedes * the processing of RFC-822 continuation lines. * * If continuation is set to 'TRUE', then it parses a (possible) * sequence of RFC-822 continuation lines. * * Results: * As above. * * Side effects: * Termination byte stored in string. * *---------------------------------------------------------------------- */ static char *get_header_line(char *start, int continuation) { char *p = start; char *end = start; if(p[0] == '\r' && p[1] == '\n') { /* If EOL in 1st 2 chars */ p++; /* point to \n and stop */ } else if(*p != '\n') { if(continuation) { while(*p != '\0') { if(*p == '\n' && p[1] != ' ' && p[1] != '\t') break; p++; } } else { while(*p != '\0' && *p != '\n') { p++; } } } ASSERT(*p != '\0'); end = p; end++; /* * Trim any trailing whitespace. */ while(isspace((unsigned char)p[-1]) && p > start) { p--; } *p = '\0'; return end; } #ifdef WIN32 static int set_nonblocking(const fcgi_request * fr, int nonblocking) { if (fr->using_npipe_io) { if (nonblocking) { DWORD mode = PIPE_NOWAIT | PIPE_READMODE_BYTE; if (SetNamedPipeHandleState((HANDLE) fr->fd, &mode, NULL, NULL) == 0) { ap_log_rerror(FCGI_LOG_ERR, fr->r, "FastCGI: SetNamedPipeHandleState() failed"); return -1; } } } else { unsigned long ioctl_arg = (nonblocking) ? 1 : 0; if (ioctlsocket(fr->fd, FIONBIO, &ioctl_arg) != 0) { errno = WSAGetLastError(); ap_log_rerror(FCGI_LOG_ERR_ERRNO, fr->r, "FastCGI: ioctlsocket() failed"); return -1; } } return 0; } #else static int set_nonblocking(const fcgi_request * fr, int nonblocking) { int nb_flag = 0; int fd_flags = fcntl(fr->fd, F_GETFL, 0); if (fd_flags < 0) return -1; #if defined(O_NONBLOCK) nb_flag = O_NONBLOCK; #elif defined(O_NDELAY) nb_flag = O_NDELAY; #elif defined(FNDELAY) nb_flag = FNDELAY; #else #error "TODO - don't read from app until all data from client is posted." #endif fd_flags = (nonblocking) ? (fd_flags | nb_flag) : (fd_flags & ~nb_flag); return fcntl(fr->fd, F_SETFL, fd_flags); } #endif /******************************************************************************* * Close the connection to the FastCGI server. This is normally called by * do_work(), but may also be called as in request pool cleanup. */ static void close_connection_to_fs(fcgi_request *fr) { #ifdef WIN32 if (fr->fd != INVALID_SOCKET) { set_nonblocking(fr, FALSE); if (fr->using_npipe_io) { CloseHandle((HANDLE) fr->fd); } else { /* abort the connection entirely */ struct linger linger = {0, 0}; setsockopt(fr->fd, SOL_SOCKET, SO_LINGER, (void *) &linger, sizeof(linger)); closesocket(fr->fd); } fr->fd = INVALID_SOCKET; #else /* ! WIN32 */ if (fr->fd >= 0) { struct linger linger = {0, 0}; set_nonblocking(fr, FALSE); /* abort the connection entirely */ setsockopt(fr->fd, SOL_SOCKET, SO_LINGER, &linger, sizeof(linger)); close(fr->fd); fr->fd = -1; #endif /* ! WIN32 */ if (fr->dynamic && fr->keepReadingFromFcgiApp == FALSE) { /* XXX FCGI_REQUEST_COMPLETE_JOB is only sent for requests which complete * normally WRT the fcgi app. There is no data sent for * connect() timeouts or requests which complete abnormally. * KillDynamicProcs() and RemoveRecords() need to be looked at * to be sure they can reasonably handle these cases before * sending these sort of stats - theres some funk in there. */ if (fcgi_util_ticks(&fr->completeTime) < 0) { /* there's no point to aborting the request, just log it */ ap_log_error(FCGI_LOG_ERR, fr->r->server, "FastCGI: can't get time of day"); } } } } /* *---------------------------------------------------------------------- * * process_headers -- * * Call with r->parseHeader == SCAN_CGI_READING_HEADERS * and initial script output in fr->header. * * If the initial script output does not include the header * terminator ("\r\n\r\n") process_headers returns with no side * effects, to be called again when more script output * has been appended to fr->header. * * If the initial script output includes the header terminator, * process_headers parses the headers and determines whether or * not the remaining script output will be sent to the client. * If so, process_headers sends the HTTP response headers to the * client and copies any non-header script output to the output * buffer reqOutbuf. * * Results: * none. * * Side effects: * May set r->parseHeader to: * SCAN_CGI_FINISHED -- headers parsed, returning script response * SCAN_CGI_BAD_HEADER -- malformed header from script * SCAN_CGI_INT_REDIRECT -- handler should perform internal redirect * SCAN_CGI_SRV_REDIRECT -- handler should return REDIRECT * *---------------------------------------------------------------------- */ static const char *process_headers(request_rec *r, fcgi_request *fr) { char *p, *next, *name, *value; int len, flag; int hasLocation = FALSE; ASSERT(fr->parseHeader == SCAN_CGI_READING_HEADERS); if (fr->header == NULL) return NULL; /* * Do we have the entire header? Scan for the blank line that * terminates the header. */ p = (char *)fr->header->elts; len = fr->header->nelts; flag = 0; while(len-- && flag < 2) { switch(*p) { case '\r': break; case '\n': flag++; break; case '\0': case '\v': case '\f': name = "Invalid Character"; goto BadHeader; default: flag = 0; break; } p++; } /* Return (to be called later when we have more data) * if we don't have an entire header. */ if (flag < 2) return NULL; /* * Parse all the headers. */ fr->parseHeader = SCAN_CGI_FINISHED; next = (char *)fr->header->elts; for(;;) { next = get_header_line(name = next, TRUE); if (*name == '\0') { break; } if ((p = strchr(name, ':')) == NULL) { goto BadHeader; } value = p + 1; while (p != name && isspace((unsigned char)*(p - 1))) { p--; } if (p == name) { goto BadHeader; } *p = '\0'; if (strpbrk(name, " \t") != NULL) { *p = ' '; goto BadHeader; } while (isspace((unsigned char)*value)) { value++; } if (strcasecmp(name, "Status") == 0) { int statusValue = strtol(value, NULL, 10); if (statusValue < 0) { fr->parseHeader = SCAN_CGI_BAD_HEADER; return ap_psprintf(r->pool, "invalid Status '%s'", value); } r->status = statusValue; r->status_line = ap_pstrdup(r->pool, value); continue; } if (fr->role == FCGI_RESPONDER) { if (strcasecmp(name, "Content-type") == 0) { #ifdef APACHE2 ap_set_content_type(r, value); #else r->content_type = ap_pstrdup(r->pool, value); #endif continue; } /* * Special case headers that should not persist on error * or across redirects, i.e. use headers_out rather than * err_headers_out. */ if (strcasecmp(name, "Location") == 0) { hasLocation = TRUE; ap_table_set(r->headers_out, name, value); continue; } if (strcasecmp(name, "Content-Length") == 0) { ap_table_set(r->headers_out, name, value); continue; } /* If the script wants them merged, it can do it */ ap_table_add(r->err_headers_out, name, value); continue; } else { ap_table_add(fr->authHeaders, name, value); } } if (fr->role != FCGI_RESPONDER) return NULL; /* * Who responds, this handler or Apache? */ if (hasLocation) { const char *location = ap_table_get(r->headers_out, "Location"); /* * Based on internal redirect handling in mod_cgi.c... * * If a script wants to produce its own Redirect * body, it now has to explicitly *say* "Status: 302" */ if (r->status == 200) { if(location[0] == '/') { /* * Location is an relative path. This handler will * consume all script output, then have Apache perform an * internal redirect. */ fr->parseHeader = SCAN_CGI_INT_REDIRECT; return NULL; } else { /* * Location is an absolute URL. If the script didn't * produce a Content-type header, this handler will * consume all script output and then have Apache generate * its standard redirect response. Otherwise this handler * will transmit the script's response. */ fr->parseHeader = SCAN_CGI_SRV_REDIRECT; return NULL; } } } /* * We're responding. Send headers, buffer excess script output. */ ap_send_http_header(r); /* We need to reinstate our timeout, send_http_header() kill()s it */ ap_hard_timeout("FastCGI request processing", r); if (r->header_only) { /* we've got all we want from the server */ close_connection_to_fs(fr); fr->exitStatusSet = 1; fcgi_buf_reset(fr->clientOutputBuffer); fcgi_buf_reset(fr->serverOutputBuffer); return NULL; } len = fr->header->nelts - (next - fr->header->elts); ASSERT(len >= 0); ASSERT(BufferLength(fr->clientOutputBuffer) == 0); if (BufferFree(fr->clientOutputBuffer) < len) { fr->clientOutputBuffer = fcgi_buf_new(r->pool, len); } ASSERT(BufferFree(fr->clientOutputBuffer) >= len); if (len > 0) { int sent; sent = fcgi_buf_add_block(fr->clientOutputBuffer, next, len); ASSERT(sent == len); } return NULL; BadHeader: /* Log first line of a multi-line header */ if ((p = strpbrk(name, "\r\n")) != NULL) *p = '\0'; fr->parseHeader = SCAN_CGI_BAD_HEADER; return ap_psprintf(r->pool, "malformed header '%s'", name); } /* * Read from the client filling both the FastCGI server buffer and the * client buffer with the hopes of buffering the client data before * making the connect() to the FastCGI server. This prevents slow * clients from keeping the FastCGI server in processing longer than is * necessary. */ static int read_from_client_n_queue(fcgi_request *fr) { char *end; int count; long int countRead; while (BufferFree(fr->clientInputBuffer) > 0 || BufferFree(fr->serverOutputBuffer) > 0) { fcgi_protocol_queue_client_buffer(fr); if (fr->expectingClientContent <= 0) return OK; fcgi_buf_get_free_block_info(fr->clientInputBuffer, &end, &count); if (count == 0) return OK; if ((countRead = ap_get_client_block(fr->r, end, count)) < 0) { /* set the header scan state to done to prevent logging an error * - hokey approach - probably should be using a unique value */ fr->parseHeader = SCAN_CGI_FINISHED; return -1; } if (countRead == 0) { fr->expectingClientContent = 0; } else { fcgi_buf_add_update(fr->clientInputBuffer, countRead); ap_reset_timeout(fr->r); } } return OK; } static int write_to_client(fcgi_request *fr) { char *begin; int count; int rv; #ifdef APACHE2 apr_bucket * bkt; apr_bucket_brigade * bde; apr_bucket_alloc_t * const bkt_alloc = fr->r->connection->bucket_alloc; #endif fcgi_buf_get_block_info(fr->clientOutputBuffer, &begin, &count); if (count == 0) return OK; /* If fewer than count bytes are written, an error occured. * ap_bwrite() typically forces a flushed write to the client, this * effectively results in a block (and short packets) - it should * be fixed, but I didn't win much support for the idea on new-httpd. * So, without patching Apache, the best way to deal with this is * to size the fcgi_bufs to hold all of the script output (within * reason) so the script can be released from having to wait around * for the transmission to the client to complete. */ #ifdef APACHE2 bde = apr_brigade_create(fr->r->pool, bkt_alloc); bkt = apr_bucket_transient_create(begin, count, bkt_alloc); APR_BRIGADE_INSERT_TAIL(bde, bkt); if (fr->fs ? fr->fs->flush : dynamicFlush) { bkt = apr_bucket_flush_create(bkt_alloc); APR_BRIGADE_INSERT_TAIL(bde, bkt); } rv = ap_pass_brigade(fr->r->output_filters, bde); #elif defined(RUSSIAN_APACHE) rv = (ap_rwrite(begin, count, fr->r) != count); #else rv = (ap_bwrite(fr->r->connection->client, begin, count) != count); #endif if (rv || fr->r->connection->aborted) { ap_log_rerror(FCGI_LOG_INFO_NOERRNO, fr->r, "FastCGI: client stopped connection before send body completed"); return -1; } #ifndef APACHE2 ap_reset_timeout(fr->r); /* Don't bother with a wrapped buffer, limiting exposure to slow * clients. The BUFF routines don't allow a writev from above, * and don't always memcpy to minimize small write()s, this should * be fixed, but I didn't win much support for the idea on * new-httpd - I'll have to _prove_ its a problem first.. */ /* The default behaviour used to be to flush with every write, but this * can tie up the FastCGI server longer than is necessary so its an option now */ if (fr->fs ? fr->fs->flush : dynamicFlush) { #ifdef RUSSIAN_APACHE rv = ap_rflush(fr->r); #else rv = ap_bflush(fr->r->connection->client); #endif if (rv) { ap_log_rerror(FCGI_LOG_INFO_NOERRNO, fr->r, "FastCGI: client stopped connection before send body completed"); return -1; } ap_reset_timeout(fr->r); } #endif /* !APACHE2 */ fcgi_buf_toss(fr->clientOutputBuffer, count); return OK; } static void get_request_identity(request_rec * const r, uid_t * const uid, gid_t * const gid) { #if defined(WIN32) *uid = (uid_t) 0; *gid = (gid_t) 0; #elif defined(APACHE2) ap_unix_identity_t * identity = ap_run_get_suexec_identity(r); if (identity) { *uid = identity->uid; *gid = identity->gid; } else { *uid = 0; *gid = 0; } #else *uid = r->server->server_uid; *gid = r->server->server_gid; #endif } /******************************************************************************* * Determine the user and group the wrapper should be called with. * Based on code in Apache's create_argv_cmd() (util_script.c). */ static void set_uid_n_gid(request_rec *r, const char **user, const char **group) { if (fcgi_wrapper == NULL) { *user = "-"; *group = "-"; return; } if (strncmp("/~", r->uri, 2) == 0) { /* its a user dir uri, just send the ~user, and leave it to the PM */ char *end = strchr(r->uri + 2, '/'); if (end) *user = memcpy(ap_pcalloc(r->pool, end - r->uri), r->uri + 1, end - r->uri - 1); else *user = ap_pstrdup(r->pool, r->uri + 1); *group = "-"; } else { uid_t uid; gid_t gid; get_request_identity(r, &uid, &gid); *user = ap_psprintf(r->pool, "%ld", (long) uid); *group = ap_psprintf(r->pool, "%ld", (long) gid); } } static void send_request_complete(fcgi_request *fr) { if (fr->completeTime.tv_sec) { struct timeval qtime, rtime; timersub(&fr->queueTime, &fr->startTime, &qtime); timersub(&fr->completeTime, &fr->queueTime, &rtime); send_to_pm(FCGI_REQUEST_COMPLETE_JOB, fr->fs_path, fr->user, fr->group, qtime.tv_sec * 1000000 + qtime.tv_usec, rtime.tv_sec * 1000000 + rtime.tv_usec); } } /******************************************************************************* * Connect to the FastCGI server. */ static int open_connection_to_fs(fcgi_request *fr) { struct timeval tval; fd_set write_fds, read_fds; int status; request_rec * const r = fr->r; pool * const rp = r->pool; const char *socket_path = NULL; struct sockaddr *socket_addr = NULL; int socket_addr_len = 0; #ifndef WIN32 const char *err = NULL; #endif /* Create the connection point */ if (fr->dynamic) { socket_path = fcgi_util_socket_hash_filename(rp, fr->fs_path, fr->user, fr->group); socket_path = fcgi_util_socket_make_path_absolute(rp, socket_path, 1); #ifndef WIN32 err = fcgi_util_socket_make_domain_addr(rp, (struct sockaddr_un **)&socket_addr, &socket_addr_len, socket_path); if (err) { ap_log_rerror(FCGI_LOG_ERR, r, "FastCGI: failed to connect to (dynamic) server \"%s\": " "%s", fr->fs_path, err); return FCGI_FAILED; } #endif } else { #ifdef WIN32 if (fr->fs->dest_addr != NULL) { socket_addr = fr->fs->dest_addr; } else if (fr->fs->socket_addr) { socket_addr = fr->fs->socket_addr; } else { socket_path = fr->fs->socket_path; } #else socket_addr = fr->fs->socket_addr; #endif socket_addr_len = fr->fs->socket_addr_len; } if (fr->dynamic) { #ifdef WIN32 if (fr->fs && fr->fs->restartTime) #else struct stat sock_stat; if (stat(socket_path, &sock_stat) == 0) #endif { /* It exists */ if (dynamicAutoUpdate) { struct stat app_stat; /* TODO: follow sym links */ if (stat(fr->fs_path, &app_stat) == 0) { #ifdef WIN32 if (fr->fs->startTime < app_stat.st_mtime) #else if (sock_stat.st_mtime < app_stat.st_mtime) #endif { #ifndef WIN32 struct timeval tv; tv.tv_sec = 1; tv.tv_usec = 0; #endif /* * There's a newer one, request a restart. */ send_to_pm(FCGI_SERVER_RESTART_JOB, fr->fs_path, fr->user, fr->group, 0, 0); #ifdef WIN32 Sleep(1000); #else /* Avoid sleep/alarm interactions */ ap_select(0, NULL, NULL, NULL, &tv); #endif } } } } else { int i; send_to_pm(FCGI_SERVER_START_JOB, fr->fs_path, fr->user, fr->group, 0, 0); /* wait until it looks like its running - this shouldn't take * very long at all - the exception is when the sockets are * removed out from under a running application - the loop * limit addresses this (preventing spinning) */ for (i = 10; i > 0; i--) { #ifdef WIN32 Sleep(500); fr->fs = fcgi_util_fs_get_by_id(fr->fs_path, 0, 0); if (fr->fs && fr->fs->restartTime) #else struct timeval tv; tv.tv_sec = 0; tv.tv_usec = 500000; /* Avoid sleep/alarm interactions */ ap_select(0, NULL, NULL, NULL, &tv); if (stat(socket_path, &sock_stat) == 0) #endif { break; } } if (i <= 0) { ap_log_rerror(FCGI_LOG_ALERT, r, "FastCGI: failed to connect to (dynamic) server \"%s\": " "something is seriously wrong, any chance the " "socket/named_pipe directory was removed?, see the " "FastCgiIpcDir directive", fr->fs_path); return FCGI_FAILED; } } } #ifdef WIN32 if (socket_path) { BOOL ready; DWORD connect_time; int rv; HANDLE wait_npipe_mutex; DWORD interval; DWORD max_connect_time = FCGI_NAMED_PIPE_CONNECT_TIMEOUT; fr->using_npipe_io = TRUE; if (fr->dynamic) { interval = dynamicPleaseStartDelay * 1000; if (dynamicAppConnectTimeout) { max_connect_time = dynamicAppConnectTimeout; } } else { interval = FCGI_NAMED_PIPE_CONNECT_TIMEOUT * 1000; if (fr->fs->appConnectTimeout) { max_connect_time = fr->fs->appConnectTimeout; } } fcgi_util_ticks(&fr->startTime); { /* xxx this handle should live somewhere (see CloseHandle()s below too) */ char * wait_npipe_mutex_name, * cp; wait_npipe_mutex_name = cp = ap_pstrdup(rp, socket_path); while ((cp = strchr(cp, '\\'))) *cp = '/'; wait_npipe_mutex = CreateMutex(NULL, FALSE, wait_npipe_mutex_name); } if (wait_npipe_mutex == NULL) { ap_log_rerror(FCGI_LOG_ERR, r, "FastCGI: failed to connect to server \"%s\": " "can't create the WaitNamedPipe mutex", fr->fs_path); return FCGI_FAILED; } SetLastError(ERROR_SUCCESS); rv = WaitForSingleObject(wait_npipe_mutex, max_connect_time * 1000); if (rv == WAIT_TIMEOUT || rv == WAIT_FAILED) { if (fr->dynamic) { send_to_pm(FCGI_REQUEST_TIMEOUT_JOB, fr->fs_path, fr->user, fr->group, 0, 0); } ap_log_rerror(FCGI_LOG_ERR_NOERRNO, r, "FastCGI: failed to connect to server \"%s\": " "wait for a npipe instance failed", fr->fs_path); FCGIDBG3("interval=%d, max_connect_time=%d", interval, max_connect_time); CloseHandle(wait_npipe_mutex); return FCGI_FAILED; } fcgi_util_ticks(&fr->queueTime); connect_time = fr->queueTime.tv_sec - fr->startTime.tv_sec; if (fr->dynamic) { if (connect_time >= interval) { send_to_pm(FCGI_REQUEST_TIMEOUT_JOB, fr->fs_path, fr->user, fr->group, 0, 0); FCGIDBG4("connect_time=%d, interval=%d, max_connect_time=%d", connect_time, interval, max_connect_time); } if (max_connect_time - connect_time < interval) { interval = max_connect_time - connect_time; } } else { interval -= connect_time * 1000; } for (;;) { ready = WaitNamedPipe(socket_path, interval); if (ready) { fr->fd = (SOCKET) CreateFile(socket_path, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, /* no security attributes */ OPEN_EXISTING, /* opens existing pipe */ FILE_FLAG_OVERLAPPED, NULL); /* no template file */ if (fr->fd != (SOCKET) INVALID_HANDLE_VALUE) { ReleaseMutex(wait_npipe_mutex); CloseHandle(wait_npipe_mutex); fcgi_util_ticks(&fr->queueTime); FCGIDBG2("got npipe connect: %s", fr->fs_path); return FCGI_OK; } if (GetLastError() != ERROR_PIPE_BUSY && GetLastError() != ERROR_FILE_NOT_FOUND) { ap_log_rerror(FCGI_LOG_ERR, r, "FastCGI: failed to connect to server \"%s\": " "CreateFile() failed", fr->fs_path); break; } FCGIDBG2("missed npipe connect: %s", fr->fs_path); } if (fr->dynamic) { send_to_pm(FCGI_REQUEST_TIMEOUT_JOB, fr->fs_path, fr->user, fr->group, 0, 0); } fcgi_util_ticks(&fr->queueTime); connect_time = fr->queueTime.tv_sec - fr->startTime.tv_sec; FCGIDBG5("interval=%d, max_connect_time=%d, connect_time=%d, ready=%d", interval, max_connect_time, connect_time, ready); if (connect_time >= max_connect_time) { ap_log_rerror(FCGI_LOG_ERR, r, "FastCGI: failed to connect to server \"%s\": " "CreateFile()/WaitNamedPipe() timed out", fr->fs_path); break; } } ReleaseMutex(wait_npipe_mutex); CloseHandle(wait_npipe_mutex); fr->fd = INVALID_SOCKET; return FCGI_FAILED; } #endif /* Create the socket */ fr->fd = socket(socket_addr->sa_family, SOCK_STREAM, 0); #ifdef WIN32 if (fr->fd == INVALID_SOCKET) { errno = WSAGetLastError(); /* Not sure this is going to work as expected */ #else if (fr->fd < 0) { #endif ap_log_rerror(FCGI_LOG_ERR_ERRNO, r, "FastCGI: failed to connect to server \"%s\": " "socket() failed", fr->fs_path); return FCGI_FAILED; } #ifndef WIN32 if (fr->fd >= FD_SETSIZE) { ap_log_rerror(FCGI_LOG_ERR, r, "FastCGI: failed to connect to server \"%s\": " "socket file descriptor (%u) is larger than " "FD_SETSIZE (%u), you probably need to rebuild Apache with a " "larger FD_SETSIZE", fr->fs_path, fr->fd, FD_SETSIZE); return FCGI_FAILED; } #endif /* If appConnectTimeout is non-zero, setup do a non-blocking connect */ if ((fr->dynamic && dynamicAppConnectTimeout) || (!fr->dynamic && fr->fs->appConnectTimeout)) { set_nonblocking(fr, TRUE); } if (fr->dynamic) { fcgi_util_ticks(&fr->startTime); } /* Connect */ do { if (connect(fr->fd, (struct sockaddr *) socket_addr, socket_addr_len) == 0) { goto ConnectionComplete; } } while (errno == EINTR); #ifdef WIN32 errno = WSAGetLastError(); if (errno != WSAEWOULDBLOCK) { ap_log_rerror(FCGI_LOG_ERR_ERRNO, r, "FastCGI: failed to connect to server \"%s\": " "connect() failed", fr->fs_path); return FCGI_FAILED; } #else /* ECONNREFUSED means the listen queue is full (or there isn't one). * With dynamic I can at least make sure the PM knows this is occuring */ if (fr->dynamic && errno == ECONNREFUSED) { /* @@@ This might be better as some other "kind" of message */ send_to_pm(FCGI_REQUEST_TIMEOUT_JOB, fr->fs_path, fr->user, fr->group, 0, 0); errno = ECONNREFUSED; } if (errno != EINPROGRESS) { ap_log_rerror(FCGI_LOG_ERR, r, "FastCGI: failed to connect to server \"%s\": " "connect() failed", fr->fs_path); return FCGI_FAILED; } #endif /* The connect() is non-blocking */ errno = 0; if (fr->dynamic) { do { FD_ZERO(&write_fds); FD_SET(fr->fd, &write_fds); read_fds = write_fds; tval.tv_sec = dynamicPleaseStartDelay; tval.tv_usec = 0; do { status = ap_select(fr->fd + 1, &read_fds, &write_fds, NULL, &tval); } while (status < 0 && errno == EINTR); if (status < 0) break; fcgi_util_ticks(&fr->queueTime); if (status > 0) break; /* select() timed out */ send_to_pm(FCGI_REQUEST_TIMEOUT_JOB, fr->fs_path, fr->user, fr->group, 0, 0); } while ((fr->queueTime.tv_sec - fr->startTime.tv_sec) < (int)dynamicAppConnectTimeout); /* XXX These can be moved down when dynamic vars live is a struct */ if (status == 0) { ap_log_rerror(FCGI_LOG_ERR_NOERRNO, r, "FastCGI: failed to connect to server \"%s\": " "connect() timed out (appConnTimeout=%dsec)", fr->fs_path, dynamicAppConnectTimeout); return FCGI_FAILED; } } /* dynamic */ else { tval.tv_sec = fr->fs->appConnectTimeout; tval.tv_usec = 0; FD_ZERO(&write_fds); FD_SET(fr->fd, &write_fds); read_fds = write_fds; do { status = ap_select(fr->fd + 1, &read_fds, &write_fds, NULL, &tval); } while (status < 0 && errno == EINTR); if (status == 0) { ap_log_rerror(FCGI_LOG_ERR_NOERRNO, r, "FastCGI: failed to connect to server \"%s\": " "connect() timed out (appConnTimeout=%dsec)", fr->fs_path, dynamicAppConnectTimeout); return FCGI_FAILED; } } /* !dynamic */ if (status < 0) { #ifdef WIN32 errno = WSAGetLastError(); #endif ap_log_rerror(FCGI_LOG_ERR_ERRNO, r, "FastCGI: failed to connect to server \"%s\": " "select() failed", fr->fs_path); return FCGI_FAILED; } if (FD_ISSET(fr->fd, &write_fds) || FD_ISSET(fr->fd, &read_fds)) { int error = 0; NET_SIZE_T len = sizeof(error); if (getsockopt(fr->fd, SOL_SOCKET, SO_ERROR, (char *)&error, &len) < 0) { /* Solaris pending error */ #ifdef WIN32 errno = WSAGetLastError(); #endif ap_log_rerror(FCGI_LOG_ERR_ERRNO, r, "FastCGI: failed to connect to server \"%s\": " "select() failed (Solaris pending error)", fr->fs_path); return FCGI_FAILED; } if (error != 0) { /* Berkeley-derived pending error */ errno = error; ap_log_rerror(FCGI_LOG_ERR_ERRNO, r, "FastCGI: failed to connect to server \"%s\": " "select() failed (pending error)", fr->fs_path); return FCGI_FAILED; } } else { #ifdef WIN32 errno = WSAGetLastError(); #endif ap_log_rerror(FCGI_LOG_ERR_ERRNO, r, "FastCGI: failed to connect to server \"%s\": " "select() error - THIS CAN'T HAPPEN!", fr->fs_path); return FCGI_FAILED; } ConnectionComplete: /* Return to blocking mode if it was set up */ if ((fr->dynamic && dynamicAppConnectTimeout) || (!fr->dynamic && fr->fs->appConnectTimeout)) { set_nonblocking(fr, FALSE); } #ifdef TCP_NODELAY if (socket_addr->sa_family == AF_INET) { /* We shouldn't be sending small packets and there's no application * level ack of the data we send, so disable Nagle */ int set = 1; setsockopt(fr->fd, IPPROTO_TCP, TCP_NODELAY, (char *)&set, sizeof(set)); } #endif return FCGI_OK; } static void sink_client_data(fcgi_request *fr) { char *base; int size; fcgi_buf_reset(fr->clientInputBuffer); fcgi_buf_get_free_block_info(fr->clientInputBuffer, &base, &size); while (ap_get_client_block(fr->r, base, size) > 0); } static apcb_t cleanup(void *data) { fcgi_request * const fr = (fcgi_request *) data; if (fr == NULL) return APCB_OK; /* its more than likely already run, but... */ close_connection_to_fs(fr); send_request_complete(fr); if (fr->fs_stderr_len) { ap_log_rerror(FCGI_LOG_ERR_NOERRNO, fr->r, "FastCGI: server \"%s\" stderr: %s", fr->fs_path, fr->fs_stderr); } return APCB_OK; } #ifdef WIN32 static int npipe_io(fcgi_request * const fr) { request_rec * const r = fr->r; enum { STATE_ENV_SEND, STATE_CLIENT_RECV, STATE_SERVER_SEND, STATE_SERVER_RECV, STATE_CLIENT_SEND, STATE_ERROR } state = STATE_ENV_SEND; env_status env_status; int client_recv; int dynamic_first_recv = fr->dynamic; int idle_timeout = fr->dynamic ? dynamic_idle_timeout : fr->fs->idle_timeout; int send_pending = 0; int recv_pending = 0; int client_send = 0; int rv; OVERLAPPED rov = { 0 }; OVERLAPPED sov = { 0 }; HANDLE events[2]; struct timeval timeout; struct timeval dynamic_last_io_time; int did_io = 1; pool * const rp = r->pool; int is_connected = 0; DWORD recv_count = 0; dynamic_last_io_time.tv_sec = 0; dynamic_last_io_time.tv_usec = 0; if (fr->role == FCGI_RESPONDER) { client_recv = (fr->expectingClientContent != 0); } idle_timeout = fr->dynamic ? dynamic_idle_timeout : fr->fs->idle_timeout; env_status.envp = NULL; events[0] = CreateEvent(NULL, TRUE, FALSE, NULL); events[1] = CreateEvent(NULL, TRUE, FALSE, NULL); sov.hEvent = events[0]; rov.hEvent = events[1]; if (fr->dynamic) { dynamic_last_io_time = fr->startTime; if (dynamicAppConnectTimeout) { struct timeval qwait; timersub(&fr->queueTime, &fr->startTime, &qwait); dynamic_first_recv = qwait.tv_sec / dynamicPleaseStartDelay + 1; } } ap_hard_timeout("FastCGI request processing", r); while (state != STATE_CLIENT_SEND) { DWORD msec_timeout; switch (state) { case STATE_ENV_SEND: if (fcgi_protocol_queue_env(r, fr, &env_status) == 0) { goto SERVER_SEND; } state = STATE_CLIENT_RECV; /* fall through */ case STATE_CLIENT_RECV: if (read_from_client_n_queue(fr) != OK) { state = STATE_ERROR; break; } if (fr->eofSent) { state = STATE_SERVER_SEND; } /* fall through */ SERVER_SEND: case STATE_SERVER_SEND: if (! is_connected) { if (open_connection_to_fs(fr) != FCGI_OK) { ap_kill_timeout(r); return HTTP_INTERNAL_SERVER_ERROR; } is_connected = 1; } if (! send_pending && BufferLength(fr->serverOutputBuffer)) { Buffer * b = fr->serverOutputBuffer; DWORD sent, len; len = min(b->length, b->data + b->size - b->begin); if (WriteFile((HANDLE) fr->fd, b->begin, len, &sent, &sov)) { /* sov.hEvent is set */ fcgi_buf_removed(b, sent); } else if (GetLastError() == ERROR_IO_PENDING) { send_pending = 1; } else { ap_log_rerror(FCGI_LOG_ERR, r, "FastCGI: comm with server " "\"%s\" aborted: WriteFile() failed", fr->fs_path); state = STATE_ERROR; break; } } /* fall through */ case STATE_SERVER_RECV: /* * Only get more data when the serverInputBuffer is empty. * Otherwise we may already have the END_REQUEST buffered * (but not processed) and a read on a closed named pipe * results in an error that is normally abnormal. */ if (! recv_pending && BufferLength(fr->serverInputBuffer) == 0) { Buffer * b = fr->serverInputBuffer; DWORD rcvd, len; len = min(b->size - b->length, b->data + b->size - b->end); if (ReadFile((HANDLE) fr->fd, b->end, len, &rcvd, &rov)) { fcgi_buf_added(b, rcvd); recv_count += rcvd; ResetEvent(rov.hEvent); if (dynamic_first_recv) { dynamic_first_recv = 0; } } else if (GetLastError() == ERROR_IO_PENDING) { recv_pending = 1; } else if (GetLastError() == ERROR_HANDLE_EOF) { fr->keepReadingFromFcgiApp = FALSE; state = STATE_CLIENT_SEND; ResetEvent(rov.hEvent); break; } else if (GetLastError() == ERROR_NO_DATA) { break; } else { ap_log_rerror(FCGI_LOG_ERR, r, "FastCGI: comm with server " "\"%s\" aborted: ReadFile() failed", fr->fs_path); state = STATE_ERROR; break; } } /* fall through */ case STATE_CLIENT_SEND: if (client_send || ! BufferFree(fr->clientOutputBuffer)) { if (write_to_client(fr)) { state = STATE_ERROR; break; } client_send = 0; if (fcgi_protocol_dequeue(rp, fr)) { state = STATE_ERROR; break; } } break; default: ASSERT(0); } if (state == STATE_ERROR) { break; } /* setup the io timeout */ if (BufferLength(fr->clientOutputBuffer)) { /* don't let client data sit too long, it might be a push */ timeout.tv_sec = 0; timeout.tv_usec = 100000; } else if (dynamic_first_recv) { int delay; struct timeval qwait; fcgi_util_ticks(&fr->queueTime); if (did_io) { /* a send() succeeded last pass */ dynamic_last_io_time = fr->queueTime; } else { /* timed out last pass */ struct timeval idle_time; timersub(&fr->queueTime, &dynamic_last_io_time, &idle_time); if (idle_time.tv_sec > idle_timeout) { send_to_pm(FCGI_REQUEST_TIMEOUT_JOB, fr->fs_path, fr->user, fr->group, 0, 0); ap_log_rerror(FCGI_LOG_ERR_NOERRNO, r, "FastCGI: comm " "with (dynamic) server \"%s\" aborted: (first read) " "idle timeout (%d sec)", fr->fs_path, idle_timeout); state = STATE_ERROR; break; } } timersub(&fr->queueTime, &fr->startTime, &qwait); delay = dynamic_first_recv * dynamicPleaseStartDelay; if (qwait.tv_sec < delay) { timeout.tv_sec = delay; timeout.tv_usec = 100000; /* fudge for select() slop */ timersub(&timeout, &qwait, &timeout); } else { /* Killed time somewhere.. client read? */ send_to_pm(FCGI_REQUEST_TIMEOUT_JOB, fr->fs_path, fr->user, fr->group, 0, 0); dynamic_first_recv = qwait.tv_sec / dynamicPleaseStartDelay + 1; timeout.tv_sec = dynamic_first_recv * dynamicPleaseStartDelay; timeout.tv_usec = 100000; /* fudge for select() slop */ timersub(&timeout, &qwait, &timeout); } } else { timeout.tv_sec = idle_timeout; timeout.tv_usec = 0; } /* require a pended recv otherwise the app can deadlock */ if (recv_pending) { msec_timeout = timeout.tv_sec * 1000 + timeout.tv_usec / 1000; rv = WaitForMultipleObjects(2, events, FALSE, msec_timeout); if (rv == WAIT_TIMEOUT) { did_io = 0; if (BufferLength(fr->clientOutputBuffer)) { client_send = 1; } else if (dynamic_first_recv) { struct timeval qwait; fcgi_util_ticks(&fr->queueTime); timersub(&fr->queueTime, &fr->startTime, &qwait); send_to_pm(FCGI_REQUEST_TIMEOUT_JOB, fr->fs_path, fr->user, fr->group, 0, 0); dynamic_first_recv = qwait.tv_sec / dynamicPleaseStartDelay + 1; } else { ap_log_rerror(FCGI_LOG_ERR_NOERRNO, r, "FastCGI: comm with " "server \"%s\" aborted: idle timeout (%d sec)", fr->fs_path, idle_timeout); state = STATE_ERROR; break; } } else { int i = rv - WAIT_OBJECT_0; did_io = 1; if (i == 0) { if (send_pending) { DWORD sent; if (GetOverlappedResult((HANDLE) fr->fd, &sov, &sent, FALSE)) { send_pending = 0; fcgi_buf_removed(fr->serverOutputBuffer, sent); } else { ap_log_rerror(FCGI_LOG_ERR, r, "FastCGI: comm with server " "\"%s\" aborted: GetOverlappedResult() failed", fr->fs_path); state = STATE_ERROR; break; } } ResetEvent(sov.hEvent); } else { DWORD rcvd; ASSERT(i == 1); recv_pending = 0; ResetEvent(rov.hEvent); if (GetOverlappedResult((HANDLE) fr->fd, &rov, &rcvd, FALSE)) { fcgi_buf_added(fr->serverInputBuffer, rcvd); if (dynamic_first_recv) { dynamic_first_recv = 0; } } else { ap_log_rerror(FCGI_LOG_ERR, r, "FastCGI: comm with server " "\"%s\" aborted: GetOverlappedResult() failed", fr->fs_path); state = STATE_ERROR; break; } } } } if (fcgi_protocol_dequeue(rp, fr)) { state = STATE_ERROR; break; } if (fr->parseHeader == SCAN_CGI_READING_HEADERS) { const char * err = process_headers(r, fr); if (err) { ap_log_rerror(FCGI_LOG_ERR_NOERRNO, r, "FastCGI: comm with server \"%s\" aborted: " "error parsing headers: %s", fr->fs_path, err); state = STATE_ERROR; break; } } if (fr->exitStatusSet) { fr->keepReadingFromFcgiApp = FALSE; state = STATE_CLIENT_SEND; break; } } if (! fr->exitStatusSet || ! fr->eofSent) { CancelIo((HANDLE) fr->fd); } CloseHandle(rov.hEvent); CloseHandle(sov.hEvent); return (state == STATE_ERROR); } #endif /* WIN32 */ static int socket_io(fcgi_request * const fr) { enum { STATE_SOCKET_NONE, STATE_ENV_SEND, STATE_CLIENT_RECV, STATE_SERVER_SEND, STATE_SERVER_RECV, STATE_CLIENT_SEND, STATE_ERROR, STATE_CLIENT_ERROR } state = STATE_ENV_SEND; request_rec * const r = fr->r; struct timeval timeout; struct timeval dynamic_last_io_time; fd_set read_set; fd_set write_set; int nfds = 0; int select_status = 1; int idle_timeout; int rv; int dynamic_first_recv = fr->dynamic ? 1 : 0; int client_send = FALSE; int client_recv = FALSE; env_status env; pool *rp = r->pool; int is_connected = 0; dynamic_last_io_time.tv_sec = 0; dynamic_last_io_time.tv_usec = 0; if (fr->role == FCGI_RESPONDER) { client_recv = (fr->expectingClientContent != 0); } idle_timeout = fr->dynamic ? dynamic_idle_timeout : fr->fs->idle_timeout; env.envp = NULL; if (fr->dynamic) { dynamic_last_io_time = fr->startTime; if (dynamicAppConnectTimeout) { struct timeval qwait; timersub(&fr->queueTime, &fr->startTime, &qwait); dynamic_first_recv = qwait.tv_sec / dynamicPleaseStartDelay + 1; } } ap_hard_timeout("FastCGI request processing", r); for (;;) { FD_ZERO(&read_set); FD_ZERO(&write_set); switch (state) { case STATE_ENV_SEND: if (fcgi_protocol_queue_env(r, fr, &env) == 0) { goto SERVER_SEND; } state = STATE_CLIENT_RECV; /* fall through */ case STATE_CLIENT_RECV: if (read_from_client_n_queue(fr)) { state = STATE_CLIENT_ERROR; break; } if (fr->eofSent) { state = STATE_SERVER_SEND; } /* fall through */ SERVER_SEND: case STATE_SERVER_SEND: if (! is_connected) { if (open_connection_to_fs(fr) != FCGI_OK) { ap_kill_timeout(r); return HTTP_INTERNAL_SERVER_ERROR; } set_nonblocking(fr, TRUE); is_connected = 1; nfds = fr->fd + 1; } if (BufferLength(fr->serverOutputBuffer)) { FD_SET(fr->fd, &write_set); } else { ASSERT(fr->eofSent); state = STATE_SERVER_RECV; } /* fall through */ case STATE_SERVER_RECV: FD_SET(fr->fd, &read_set); /* fall through */ case STATE_CLIENT_SEND: if (client_send || ! BufferFree(fr->clientOutputBuffer)) { if (write_to_client(fr)) { state = STATE_CLIENT_ERROR; break; } client_send = 0; if (fcgi_protocol_dequeue(rp, fr)) { state = STATE_ERROR; break; } } break; case STATE_ERROR: case STATE_CLIENT_ERROR: break; default: ASSERT(0); } if (state == STATE_CLIENT_ERROR || state == STATE_ERROR) { break; } /* setup the io timeout */ if (BufferLength(fr->clientOutputBuffer)) { /* don't let client data sit too long, it might be a push */ timeout.tv_sec = 0; timeout.tv_usec = 100000; } else if (dynamic_first_recv) { int delay; struct timeval qwait; fcgi_util_ticks(&fr->queueTime); if (select_status) { /* a send() succeeded last pass */ dynamic_last_io_time = fr->queueTime; } else { /* timed out last pass */ struct timeval idle_time; timersub(&fr->queueTime, &dynamic_last_io_time, &idle_time); if (idle_time.tv_sec > idle_timeout) { send_to_pm(FCGI_REQUEST_TIMEOUT_JOB, fr->fs_path, fr->user, fr->group, 0, 0); ap_log_rerror(FCGI_LOG_ERR_NOERRNO, r, "FastCGI: comm " "with (dynamic) server \"%s\" aborted: (first read) " "idle timeout (%d sec)", fr->fs_path, idle_timeout); state = STATE_ERROR; break; } } timersub(&fr->queueTime, &fr->startTime, &qwait); delay = dynamic_first_recv * dynamicPleaseStartDelay; FCGIDBG5("qwait=%ld.%06ld delay=%d first_recv=%d", qwait.tv_sec, qwait.tv_usec, delay, dynamic_first_recv); if (qwait.tv_sec < delay) { timeout.tv_sec = delay; timeout.tv_usec = 100000; /* fudge for select() slop */ timersub(&timeout, &qwait, &timeout); } else { /* Killed time somewhere.. client read? */ send_to_pm(FCGI_REQUEST_TIMEOUT_JOB, fr->fs_path, fr->user, fr->group, 0, 0); dynamic_first_recv = qwait.tv_sec / dynamicPleaseStartDelay + 1; timeout.tv_sec = dynamic_first_recv * dynamicPleaseStartDelay; timeout.tv_usec = 100000; /* fudge for select() slop */ timersub(&timeout, &qwait, &timeout); } } else { timeout.tv_sec = idle_timeout; timeout.tv_usec = 0; } /* wait on the socket */ do { select_status = ap_select(nfds, &read_set, &write_set, NULL, &timeout); } while (select_status < 0 && errno == EINTR); if (select_status < 0) { ap_log_rerror(FCGI_LOG_ERR_ERRNO, r, "FastCGI: comm with server " "\"%s\" aborted: select() failed", fr->fs_path); state = STATE_ERROR; break; } if (select_status == 0) { /* select() timeout */ if (BufferLength(fr->clientOutputBuffer)) { if (fr->role == FCGI_RESPONDER) { client_send = TRUE; } } else if (dynamic_first_recv) { struct timeval qwait; fcgi_util_ticks(&fr->queueTime); timersub(&fr->queueTime, &fr->startTime, &qwait); send_to_pm(FCGI_REQUEST_TIMEOUT_JOB, fr->fs_path, fr->user, fr->group, 0, 0); dynamic_first_recv = qwait.tv_sec / dynamicPleaseStartDelay + 1; continue; } else { ap_log_rerror(FCGI_LOG_ERR_NOERRNO, r, "FastCGI: comm with " "server \"%s\" aborted: idle timeout (%d sec)", fr->fs_path, idle_timeout); state = STATE_ERROR; } } if (FD_ISSET(fr->fd, &write_set)) { /* send to the server */ rv = fcgi_buf_socket_send(fr->serverOutputBuffer, fr->fd); if (rv < 0) { ap_log_rerror(FCGI_LOG_ERR, r, "FastCGI: comm with server " "\"%s\" aborted: write failed", fr->fs_path); state = STATE_ERROR; break; } } if (FD_ISSET(fr->fd, &read_set)) { /* recv from the server */ if (dynamic_first_recv) { dynamic_first_recv = 0; fcgi_util_ticks(&fr->queueTime); } rv = fcgi_buf_socket_recv(fr->serverInputBuffer, fr->fd); if (rv < 0) { if (errno == EAGAIN) { /* this reportedly occurs on AIX 5.2 sporadically */ struct timeval tv; tv.tv_sec = 1; tv.tv_usec = 0; ap_log_rerror(FCGI_LOG_INFO, r, "FastCGI: comm with server " "\"%s\" interrupted: read will be retried in 1 second", fr->fs_path); /* avoid sleep/alarm interactions */ ap_select(0, NULL, NULL, NULL, &tv); } else { ap_log_rerror(FCGI_LOG_ERR, r, "FastCGI: comm with server " "\"%s\" aborted: read failed", fr->fs_path); state = STATE_ERROR; break; } } else if (rv == 0) { fr->keepReadingFromFcgiApp = FALSE; state = STATE_CLIENT_SEND; break; } } if (fcgi_protocol_dequeue(rp, fr)) { state = STATE_ERROR; break; } if (fr->parseHeader == SCAN_CGI_READING_HEADERS) { const char * err = process_headers(r, fr); if (err) { ap_log_rerror(FCGI_LOG_ERR_NOERRNO, r, "FastCGI: comm with server \"%s\" aborted: " "error parsing headers: %s", fr->fs_path, err); state = STATE_ERROR; break; } } if (fr->exitStatusSet) { fr->keepReadingFromFcgiApp = FALSE; state = STATE_CLIENT_SEND; break; } } return (state == STATE_ERROR); } /*---------------------------------------------------------------------- * This is the core routine for moving data between the FastCGI * application and the Web server's client. */ static int do_work(request_rec * const r, fcgi_request * const fr) { int rv; pool *rp = r->pool; fcgi_protocol_queue_begin_request(fr); if (fr->role == FCGI_RESPONDER) { rv = ap_setup_client_block(r, REQUEST_CHUNKED_ERROR); if (rv != OK) { ap_kill_timeout(r); return rv; } fr->expectingClientContent = ap_should_client_block(r); } ap_block_alarms(); ap_register_cleanup(rp, (void *)fr, cleanup, ap_null_cleanup); ap_unblock_alarms(); #ifdef WIN32 if (fr->using_npipe_io) { rv = npipe_io(fr); } else #endif { rv = socket_io(fr); } /* comm with the server is done */ close_connection_to_fs(fr); if (fr->role == FCGI_RESPONDER) { sink_client_data(fr); } while (rv == 0 && (BufferLength(fr->serverInputBuffer) || BufferLength(fr->clientOutputBuffer))) { if (fcgi_protocol_dequeue(rp, fr)) { rv = HTTP_INTERNAL_SERVER_ERROR; } if (fr->parseHeader == SCAN_CGI_READING_HEADERS) { const char * err = process_headers(r, fr); if (err) { ap_log_rerror(FCGI_LOG_ERR_NOERRNO, r, "FastCGI: comm with server \"%s\" aborted: " "error parsing headers: %s", fr->fs_path, err); rv = HTTP_INTERNAL_SERVER_ERROR; } } if (fr->role == FCGI_RESPONDER) { if (write_to_client(fr)) { break; } } else { fcgi_buf_reset(fr->clientOutputBuffer); } } switch (fr->parseHeader) { case SCAN_CGI_FINISHED: if (fr->role == FCGI_RESPONDER) { /* RUSSIAN_APACHE requires rflush() over bflush() */ ap_rflush(r); #ifndef APACHE2 ap_bgetopt(r->connection->client, BO_BYTECT, &r->bytes_sent); #endif } /* fall through */ case SCAN_CGI_INT_REDIRECT: case SCAN_CGI_SRV_REDIRECT: break; case SCAN_CGI_READING_HEADERS: ap_log_rerror(FCGI_LOG_ERR_NOERRNO, r, "FastCGI: incomplete headers " "(%d bytes) received from server \"%s\"", fr->header->nelts, fr->fs_path); /* fall through */ case SCAN_CGI_BAD_HEADER: rv = HTTP_INTERNAL_SERVER_ERROR; break; default: ASSERT(0); rv = HTTP_INTERNAL_SERVER_ERROR; } ap_kill_timeout(r); return rv; } static int create_fcgi_request(request_rec * const r, const char * const path, fcgi_request ** const frP) { const char *fs_path; pool * const p = r->pool; fcgi_server *fs; fcgi_request * const fr = (fcgi_request *)ap_pcalloc(p, sizeof(fcgi_request)); uid_t uid; gid_t gid; fs_path = path ? path : r->filename; get_request_identity(r, &uid, &gid); fs = fcgi_util_fs_get_by_id(fs_path, uid, gid); if (fs == NULL) { const char * err; struct stat *my_finfo; /* dynamic? */ #ifndef APACHE2 if (path == NULL) { /* AP2: its bogus that we don't make use of r->finfo, but * its an apr_finfo_t and there is no apr_os_finfo_get() */ my_finfo = &r->finfo; } else #endif { my_finfo = (struct stat *) ap_palloc(p, sizeof(struct stat)); if (stat(fs_path, my_finfo) < 0) { ap_log_rerror(FCGI_LOG_ERR_ERRNO, r, "FastCGI: stat() of \"%s\" failed", fs_path); return HTTP_NOT_FOUND; } } err = fcgi_util_fs_is_path_ok(p, fs_path, my_finfo); if (err) { ap_log_rerror(FCGI_LOG_ERR_NOERRNO, r, "FastCGI: invalid (dynamic) server \"%s\": %s", fs_path, err); return HTTP_FORBIDDEN; } } fr->nph = (strncmp(strrchr(fs_path, '/'), "/nph-", 5) == 0) || (fs && fs->nph); fr->serverInputBuffer = fcgi_buf_new(p, SERVER_BUFSIZE); fr->serverOutputBuffer = fcgi_buf_new(p, SERVER_BUFSIZE); fr->clientInputBuffer = fcgi_buf_new(p, SERVER_BUFSIZE); fr->clientOutputBuffer = fcgi_buf_new(p, SERVER_BUFSIZE); fr->erBufPtr = fcgi_buf_new(p, sizeof(FCGI_EndRequestBody) + 1); fr->gotHeader = FALSE; fr->parseHeader = SCAN_CGI_READING_HEADERS; fr->header = ap_make_array(p, 1, 1); fr->fs_stderr = NULL; fr->r = r; fr->readingEndRequestBody = FALSE; fr->exitStatus = 0; fr->exitStatusSet = FALSE; fr->requestId = 1; /* anything but zero is OK here */ fr->eofSent = FALSE; fr->role = FCGI_RESPONDER; fr->expectingClientContent = FALSE; fr->keepReadingFromFcgiApp = TRUE; fr->fs = fs; fr->fs_path = fs_path; fr->authHeaders = ap_make_table(p, 10); #ifdef WIN32 fr->fd = INVALID_SOCKET; fr->dynamic = ((fs == NULL) || (fs->directive == APP_CLASS_DYNAMIC)) ? TRUE : FALSE; fr->using_npipe_io = (! fr->dynamic && (fs->dest_addr || fs->socket_addr)) ? 0 : 1; #else fr->dynamic = (fs == NULL) ? TRUE : FALSE; fr->fd = -1; #endif if (fr->nph) { #ifdef APACHE2 struct ap_filter_t *cur; fr->parseHeader = SCAN_CGI_FINISHED; /* remove the filters up through protocol - since the headers * haven't been parsed, there is no way they can work */ cur = r->proto_output_filters; while (cur && cur->frec->ftype < AP_FTYPE_CONNECTION) { cur = cur->next; } r->output_filters = r->proto_output_filters = cur; #else ap_log_rerror(FCGI_LOG_ERR_NOERRNO, r, "FastCGI: invalid request \"%s\": non parsed header support is " "not available in Apache13 (patch welcome)", fs_path); return HTTP_FORBIDDEN; #endif } set_uid_n_gid(r, &fr->user, &fr->group); *frP = fr; return OK; } /* *---------------------------------------------------------------------- * * handler -- * * This routine gets called for a request that corresponds to * a FastCGI connection. It performs the request synchronously. * * Results: * Final status of request: OK or NOT_FOUND or HTTP_INTERNAL_SERVER_ERROR. * * Side effects: * Request performed. * *---------------------------------------------------------------------- */ /* Stolen from mod_cgi.c.. * KLUDGE --- for back-combatibility, we don't have to check ExecCGI * in ScriptAliased directories, which means we need to know if this * request came through ScriptAlias or not... so the Alias module * leaves a note for us. */ static int apache_is_scriptaliased(request_rec *r) { const char *t = ap_table_get(r->notes, "alias-forced-type"); return t && (!strcasecmp(t, "cgi-script")); } /* If a script wants to produce its own Redirect body, it now * has to explicitly *say* "Status: 302". If it wants to use * Apache redirects say "Status: 200". See process_headers(). */ static int post_process_for_redirects(request_rec * const r, const fcgi_request * const fr) { switch(fr->parseHeader) { case SCAN_CGI_INT_REDIRECT: /* @@@ There are still differences between the handling in * mod_cgi and mod_fastcgi. This needs to be revisited. */ /* We already read the message body (if any), so don't allow * the redirected request to think it has one. We can ignore * Transfer-Encoding, since we used REQUEST_CHUNKED_ERROR. */ r->method = "GET"; r->method_number = M_GET; ap_table_unset(r->headers_in, "Content-length"); ap_internal_redirect_handler(ap_table_get(r->headers_out, "Location"), r); return OK; case SCAN_CGI_SRV_REDIRECT: return HTTP_MOVED_TEMPORARILY; default: #ifdef APACHE2 { apr_bucket_brigade *brigade = apr_brigade_create(r->pool, r->connection->bucket_alloc); apr_bucket* bucket = apr_bucket_eos_create(r->connection->bucket_alloc); APR_BRIGADE_INSERT_HEAD(brigade, bucket); return ap_pass_brigade(r->output_filters, brigade); } #else return OK; #endif } } /****************************************************************************** * Process fastcgi-script requests. Based on mod_cgi::cgi_handler(). */ static int content_handler(request_rec *r) { fcgi_request *fr = NULL; int ret; #ifdef APACHE2 if (strcmp(r->handler, FASTCGI_HANDLER_NAME)) return DECLINED; #endif /* Setup a new FastCGI request */ ret = create_fcgi_request(r, NULL, &fr); if (ret) { return ret; } /* If its a dynamic invocation, make sure scripts are OK here */ if (fr->dynamic && ! (ap_allow_options(r) & OPT_EXECCGI) && ! apache_is_scriptaliased(r)) { ap_log_rerror(FCGI_LOG_ERR_NOERRNO, r, "FastCGI: \"ExecCGI Option\" is off in this directory: %s", r->uri); return HTTP_FORBIDDEN; } /* Process the fastcgi-script request */ if ((ret = do_work(r, fr)) != OK) return ret; /* Special case redirects */ ret = post_process_for_redirects(r, fr); return ret; } static int post_process_auth_passed_header(table *t, const char *key, const char * const val) { if (strncasecmp(key, "Variable-", 9) == 0) key += 9; ap_table_setn(t, key, val); return 1; } static int post_process_auth_passed_compat_header(table *t, const char *key, const char * const val) { if (strncasecmp(key, "Variable-", 9) == 0) ap_table_setn(t, key + 9, val); return 1; } static int post_process_auth_failed_header(table * const t, const char * const key, const char * const val) { ap_table_setn(t, key, val); return 1; } static void post_process_auth(fcgi_request * const fr, const int passed) { request_rec * const r = fr->r; /* Restore the saved subprocess_env because we muddied ours up */ r->subprocess_env = fr->saved_subprocess_env; if (passed) { if (fr->auth_compat) { ap_table_do((int (*)(void *, const char *, const char *))post_process_auth_passed_compat_header, (void *)r->subprocess_env, fr->authHeaders, NULL); } else { ap_table_do((int (*)(void *, const char *, const char *))post_process_auth_passed_header, (void *)r->subprocess_env, fr->authHeaders, NULL); } } else { ap_table_do((int (*)(void *, const char *, const char *))post_process_auth_failed_header, (void *)r->err_headers_out, fr->authHeaders, NULL); } /* @@@ Restore these.. its a hack until I rewrite the header handling */ r->status = HTTP_OK; r->status_line = NULL; } static int check_user_authentication(request_rec *r) { int res, authenticated = 0; const char *password; fcgi_request *fr; const fcgi_dir_config * const dir_config = (const fcgi_dir_config *)ap_get_module_config(r->per_dir_config, &fastcgi_module); if (dir_config->authenticator == NULL) return DECLINED; /* Get the user password */ if ((res = ap_get_basic_auth_pw(r, &password)) != OK) return res; res = create_fcgi_request(r, dir_config->authenticator, &fr); if (res) { return res; } /* Save the existing subprocess_env, because we're gonna muddy it up */ fr->saved_subprocess_env = ap_copy_table(r->pool, r->subprocess_env); ap_table_setn(r->subprocess_env, "REMOTE_PASSWD", password); ap_table_setn(r->subprocess_env, "FCGI_APACHE_ROLE", "AUTHENTICATOR"); /* The FastCGI Protocol doesn't differentiate authentication */ fr->role = FCGI_AUTHORIZER; /* Do we need compatibility mode? */ fr->auth_compat = (dir_config->authenticator_options & FCGI_COMPAT); if ((res = do_work(r, fr)) != OK) goto AuthenticationFailed; authenticated = (r->status == 200); post_process_auth(fr, authenticated); /* A redirect shouldn't be allowed during the authentication phase */ if (ap_table_get(r->headers_out, "Location") != NULL) { ap_log_rerror(FCGI_LOG_ERR_NOERRNO, r, "FastCGI: FastCgiAuthenticator \"%s\" redirected (not allowed)", dir_config->authenticator); goto AuthenticationFailed; } if (authenticated) return OK; AuthenticationFailed: if (!(dir_config->authenticator_options & FCGI_AUTHORITATIVE)) return DECLINED; /* @@@ Probably should support custom_responses */ ap_note_basic_auth_failure(r); ap_log_rerror(FCGI_LOG_ERR_NOERRNO, r, "FastCGI: authentication failed for user \"%s\": %s", #ifdef APACHE2 r->user, r->uri); #else r->connection->user, r->uri); #endif return (res == OK) ? HTTP_UNAUTHORIZED : res; } static int check_user_authorization(request_rec *r) { int res, authorized = 0; fcgi_request *fr; const fcgi_dir_config * const dir_config = (const fcgi_dir_config *)ap_get_module_config(r->per_dir_config, &fastcgi_module); if (dir_config->authorizer == NULL) return DECLINED; /* @@@ We should probably honor the existing parameters to the require directive * as well as allow the definition of new ones (or use the basename of the * FastCGI server and pass the rest of the directive line), but for now keep * it simple. */ res = create_fcgi_request(r, dir_config->authorizer, &fr); if (res) { return res; } /* Save the existing subprocess_env, because we're gonna muddy it up */ fr->saved_subprocess_env = ap_copy_table(r->pool, r->subprocess_env); ap_table_setn(r->subprocess_env, "FCGI_APACHE_ROLE", "AUTHORIZER"); fr->role = FCGI_AUTHORIZER; /* Do we need compatibility mode? */ fr->auth_compat = (dir_config->authorizer_options & FCGI_COMPAT); if ((res = do_work(r, fr)) != OK) goto AuthorizationFailed; authorized = (r->status == 200); post_process_auth(fr, authorized); /* A redirect shouldn't be allowed during the authorization phase */ if (ap_table_get(r->headers_out, "Location") != NULL) { ap_log_rerror(FCGI_LOG_ERR_NOERRNO, r, "FastCGI: FastCgiAuthorizer \"%s\" redirected (not allowed)", dir_config->authorizer); goto AuthorizationFailed; } if (authorized) return OK; AuthorizationFailed: if (!(dir_config->authorizer_options & FCGI_AUTHORITATIVE)) return DECLINED; /* @@@ Probably should support custom_responses */ ap_note_basic_auth_failure(r); ap_log_rerror(FCGI_LOG_ERR_NOERRNO, r, "FastCGI: authorization failed for user \"%s\": %s", #ifdef APACHE2 r->user, r->uri); #else r->connection->user, r->uri); #endif return (res == OK) ? HTTP_UNAUTHORIZED : res; } static int check_access(request_rec *r) { int res, access_allowed = 0; fcgi_request *fr; const fcgi_dir_config * const dir_config = (fcgi_dir_config *)ap_get_module_config(r->per_dir_config, &fastcgi_module); if (dir_config == NULL || dir_config->access_checker == NULL) return DECLINED; res = create_fcgi_request(r, dir_config->access_checker, &fr); if (res) { return res; } /* Save the existing subprocess_env, because we're gonna muddy it up */ fr->saved_subprocess_env = ap_copy_table(r->pool, r->subprocess_env); ap_table_setn(r->subprocess_env, "FCGI_APACHE_ROLE", "ACCESS_CHECKER"); /* The FastCGI Protocol doesn't differentiate access control */ fr->role = FCGI_AUTHORIZER; /* Do we need compatibility mode? */ fr->auth_compat = (dir_config->access_checker_options & FCGI_COMPAT); if ((res = do_work(r, fr)) != OK) goto AccessFailed; access_allowed = (r->status == 200); post_process_auth(fr, access_allowed); /* A redirect shouldn't be allowed during the access check phase */ if (ap_table_get(r->headers_out, "Location") != NULL) { ap_log_rerror(FCGI_LOG_ERR_NOERRNO, r, "FastCGI: FastCgiAccessChecker \"%s\" redirected (not allowed)", dir_config->access_checker); goto AccessFailed; } if (access_allowed) return OK; AccessFailed: if (!(dir_config->access_checker_options & FCGI_AUTHORITATIVE)) return DECLINED; /* @@@ Probably should support custom_responses */ ap_log_rerror(FCGI_LOG_ERR_NOERRNO, r, "FastCGI: access denied: %s", r->uri); return (res == OK) ? HTTP_FORBIDDEN : res; } static int fixups(request_rec * r) { if (r->filename) { uid_t uid; gid_t gid; get_request_identity(r, &uid, &gid); if (fcgi_util_fs_get_by_id(r->filename, uid, gid)) { r->handler = FASTCGI_HANDLER_NAME; return OK; } } return DECLINED; } #ifndef APACHE2 # define AP_INIT_RAW_ARGS(directive, func, mconfig, where, help) \ { directive, func, mconfig, where, RAW_ARGS, help } # define AP_INIT_TAKE1(directive, func, mconfig, where, help) \ { directive, func, mconfig, where, TAKE1, help } # define AP_INIT_TAKE12(directive, func, mconfig, where, help) \ { directive, func, mconfig, where, TAKE12, help } # define AP_INIT_FLAG(directive, func, mconfig, where, help) \ { directive, func, mconfig, where, FLAG, help } #endif static const command_rec fastcgi_cmds[] = { AP_INIT_RAW_ARGS("AppClass", fcgi_config_new_static_server, NULL, RSRC_CONF, NULL), AP_INIT_RAW_ARGS("FastCgiServer", fcgi_config_new_static_server, NULL, RSRC_CONF, NULL), AP_INIT_RAW_ARGS("ExternalAppClass", fcgi_config_new_external_server, NULL, RSRC_CONF, NULL), AP_INIT_RAW_ARGS("FastCgiExternalServer", fcgi_config_new_external_server, NULL, RSRC_CONF, NULL), AP_INIT_TAKE1("FastCgiIpcDir", fcgi_config_set_socket_dir, NULL, RSRC_CONF, NULL), AP_INIT_TAKE1("FastCgiSuexec", fcgi_config_set_wrapper, NULL, RSRC_CONF, NULL), AP_INIT_TAKE1("FastCgiWrapper", fcgi_config_set_wrapper, NULL, RSRC_CONF, NULL), AP_INIT_RAW_ARGS("FCGIConfig", fcgi_config_set_config, NULL, RSRC_CONF, NULL), AP_INIT_RAW_ARGS("FastCgiConfig", fcgi_config_set_config, NULL, RSRC_CONF, NULL), AP_INIT_TAKE12("FastCgiAuthenticator", fcgi_config_new_auth_server, (void *)FCGI_AUTH_TYPE_AUTHENTICATOR, ACCESS_CONF, "a fastcgi-script path (absolute or relative to ServerRoot) followed by an optional -compat"), AP_INIT_FLAG("FastCgiAuthenticatorAuthoritative", fcgi_config_set_authoritative_slot, (void *)XtOffsetOf(fcgi_dir_config, authenticator_options), ACCESS_CONF, "Set to 'off' to allow authentication to be passed along to lower modules upon failure"), AP_INIT_TAKE12("FastCgiAuthorizer", fcgi_config_new_auth_server, (void *)FCGI_AUTH_TYPE_AUTHORIZER, ACCESS_CONF, "a fastcgi-script path (absolute or relative to ServerRoot) followed by an optional -compat"), AP_INIT_FLAG("FastCgiAuthorizerAuthoritative", fcgi_config_set_authoritative_slot, (void *)XtOffsetOf(fcgi_dir_config, authorizer_options), ACCESS_CONF, "Set to 'off' to allow authorization to be passed along to lower modules upon failure"), AP_INIT_TAKE12("FastCgiAccessChecker", fcgi_config_new_auth_server, (void *)FCGI_AUTH_TYPE_ACCESS_CHECKER, ACCESS_CONF, "a fastcgi-script path (absolute or relative to ServerRoot) followed by an optional -compat"), AP_INIT_FLAG("FastCgiAccessCheckerAuthoritative", fcgi_config_set_authoritative_slot, (void *)XtOffsetOf(fcgi_dir_config, access_checker_options), ACCESS_CONF, "Set to 'off' to allow access control to be passed along to lower modules upon failure"), { NULL } }; #ifdef APACHE2 static void register_hooks(apr_pool_t * p) { /* ap_hook_pre_config(x_pre_config, NULL, NULL, APR_HOOK_MIDDLE); */ ap_hook_post_config(init_module, NULL, NULL, APR_HOOK_MIDDLE); ap_hook_child_init(fcgi_child_init, NULL, NULL, APR_HOOK_MIDDLE); ap_hook_handler(content_handler, NULL, NULL, APR_HOOK_MIDDLE); ap_hook_check_user_id(check_user_authentication, NULL, NULL, APR_HOOK_MIDDLE); ap_hook_access_checker(check_access, NULL, NULL, APR_HOOK_MIDDLE); ap_hook_auth_checker(check_user_authorization, NULL, NULL, APR_HOOK_MIDDLE); ap_hook_fixups(fixups, NULL, NULL, APR_HOOK_MIDDLE); } module AP_MODULE_DECLARE_DATA fastcgi_module = { STANDARD20_MODULE_STUFF, fcgi_config_create_dir_config, /* per-directory config creator */ NULL, /* dir config merger */ NULL, /* server config creator */ NULL, /* server config merger */ fastcgi_cmds, /* command table */ register_hooks, /* set up other request processing hooks */ }; #else /* !APACHE2 */ handler_rec fastcgi_handlers[] = { { FCGI_MAGIC_TYPE, content_handler }, { FASTCGI_HANDLER_NAME, content_handler }, { NULL } }; module MODULE_VAR_EXPORT fastcgi_module = { STANDARD_MODULE_STUFF, init_module, /* initializer */ fcgi_config_create_dir_config, /* per-dir config creator */ NULL, /* per-dir config merger (default: override) */ NULL, /* per-server config creator */ NULL, /* per-server config merger (default: override) */ fastcgi_cmds, /* command table */ fastcgi_handlers, /* [9] content handlers */ NULL, /* [2] URI-to-filename translation */ check_user_authentication, /* [5] authenticate user_id */ check_user_authorization, /* [6] authorize user_id */ check_access, /* [4] check access (based on src & http headers) */ NULL, /* [7] check/set MIME type */ fixups, /* [8] fixups */ NULL, /* [10] logger */ NULL, /* [3] header-parser */ fcgi_child_init, /* process initialization */ #ifdef WIN32 fcgi_child_exit, /* process exit/cleanup */ #else NULL, #endif NULL /* [1] post read-request handling */ }; #endif /* !APACHE2 */ mod_fastcgi-SNAP-0910052141/INSTALL.AP20000664000635400022240000000224007530330336017165 0ustar robsfastcgi00000000000000 *** Apache FastCGI Module Installation *** See docs/mod_fastcgi.html for configuration information. This module is maintained at http://www.fastcgi.com. See the web page for mailing list information. *NIX ==== $ cd $ cp Makefile.AP2 Makefile $ make $ make install If your Apache2 installation isn't in /usr/local/apache2, then set the top_dir variable when running make (or edit the Makefile), e.g. $ make top_dir=/opt/httpd/2.0.40 Add an entry to httpd.conf like this: LoadModule fastcgi_module modules/mod_fastcgi.so WIN === To build mod_fastcgi as a project you'll need M$ VC++ 6.0 (the Makefile hasn't been updated for AP2 support): Open the mod_fastcgi project file with the VC++. Edit the Project for your configuration (update the Preprocessor and the Link paths). The default assumes a complete Apache2 installation in /Apache2. Build mod_fastcgi.so. Copy it to the Apache modules directory, e.g. /Apache2/modules. Add an entry to httpd.conf like this: LoadModule fastcgi_module modules/mod_fastcgi.so mod_fastcgi-SNAP-0910052141/.deps0000664000635400022240000000000007521122624016476 0ustar robsfastcgi00000000000000mod_fastcgi-SNAP-0910052141/debian/0002775000635400022240000000000011262520022016767 5ustar robsfastcgi00000000000000mod_fastcgi-SNAP-0910052141/debian/copyright0000664000635400022240000000516207754564467020764 0ustar robsfastcgi00000000000000This package was debianized by Davide Puricelli (evo) on Sat, 3 Jun 2000 22:50:10 +0200. It was downloaded from http://www.fastcgi.com/ Copyright: This FastCGI application library source and object code (the "Software") and its documentation (the "Documentation") are copyrighted by Open Market, Inc ("Open Market"). The following terms apply to all files associated with the Software and Documentation unless explicitly disclaimed in individual files. Open Market permits you to use, copy, modify, distribute, and license this Software and the Documentation solely for the purpose of implementing the FastCGI specification defined by Open Market or derivative specifications publicly endorsed by Open Market and promulgated by an open standards organization and for no other purpose, provided that existing copyright notices are retained in all copies and that this notice is included verbatim in any distributions. No written agreement, license, or royalty fee is required for any of the authorized uses. Modifications to this Software and Documentation may be copyrighted by their authors and need not follow the licensing terms described here, but the modified Software and Documentation must be used for the sole purpose of implementing the FastCGI specification defined by Open Market or derivative specifications publicly endorsed by Open Market and promulgated by an open standards organization and for no other purpose. If modifications to this Software and Documentation have new licensing terms, the new terms must protect Open Market's proprietary rights in the Software and Documentation to the same extent as these licensing terms and must be clearly indicated on the first page of each file where they apply. Open Market shall retain all right, title and interest in and to the Software and Documentation, including without limitation all patent, copyright, trade secret and other proprietary rights. OPEN MARKET MAKES NO EXPRESS OR IMPLIED WARRANTY WITH RESPECT TO THE SOFTWARE OR THE DOCUMENTATION, INCLUDING WITHOUT LIMITATION ANY WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL OPEN MARKET BE LIABLE TO YOU OR ANY THIRD PARTY FOR ANY DAMAGES ARISING FROM OR RELATING TO THIS SOFTWARE OR THE DOCUMENTATION, INCLUDING, WITHOUT LIMITATION, ANY INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES OR SIMILAR DAMAGES, INCLUDING LOST PROFITS OR LOST DATA, EVEN IF OPEN MARKET HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. THE SOFTWARE AND DOCUMENTATION ARE PROVIDED "AS IS". OPEN MARKET HAS NO LIABILITY IN CONTRACT, TORT, NEGLIGENCE OR OTHERWISE ARISING OUT OF THIS SOFTWARE OR THE DOCUMENTATION. mod_fastcgi-SNAP-0910052141/debian/postinst0000664000635400022240000000574307754564467020644 0ustar robsfastcgi00000000000000#!/bin/bash # vim: set ts=2 sw=2 et: set -e # These functions ask the user whether to reconfigure apache/apachessl. do_enable () { if [ -s /etc/apache/httpd.conf ] ; then if grep '^LoadModule.*mod_fastcgi\.so' /etc/apache/httpd.conf 2>&1 >/dev/null then exit 0 fi fi echo -n "A new Apache module has been installed. Reconfigure apache [Y/n]? " read CONFIG case "$CONFIG" in [nN]*) # user said no echo -n "" # do nothing ;; *) # user said yes if [ -x /usr/sbin/apacheconfig ] ; then /usr/sbin/apacheconfig --force-modules fi ;; esac } do_enablessl () { if [ -s /etc/apache-ssl/httpd.conf ] ; then if grep '^LoadModule.*mod_fastcgi\.so' /etc/apache-ssl/httpd.conf 2>&1 >/dev/null ; then exit 0 fi fi echo -n "A new Apache module has been installed. Reconfigure apache-ssl [Y/n]? " read CONFIG case "$CONFIG" in [nN]*) # user said no echo -n "" # do nothing ;; *) # user said yes if [ -x /usr/sbin/apache-sslconfig ] ; then /usr/sbin/apache-sslconfig --force-modules fi ;; esac } # These functions ask the user whether to restart apache/apachessl. ask_restart () { echo -n "An Apache module has been modified. Restart apache [Y/n]? " read CONFIG case "$CONFIG" in [nN]*) # user said no echo -n "" # do nothing ;; *) # user said yes /usr/sbin/apachectl restart ;; esac } ask_restartssl () { echo -n "An Apache module has been modified. Restart apache-ssl [Y/n]? " read CONFIG case "$CONFIG" in [nN]*) # user said no echo -n "" # do nothing ;; *) # user said yes /usr/sbin/apache-sslctl restart ;; esac } case "$1" in configure) # Configure this package. If the package must prompt the user for # information, do it here. There are three sub-cases. if [ "${2+set}" != "set" ] ; then # We're being installed by an ancient dpkg which doesn't remember # which version was most recently configured, or even whether # there is a most recently configured version. if [ -s /etc/apache/httpd.conf ] ; then do_enable ; fi if [ -s /etc/apache-ssl/httpd.conf ] ; then do_enablessl ; fi elif [ -z "$2" -o "$2" = "" ] ; then # The package has not ever been configured on this system, or was # purged since it was last configured. # DJ: So let's do_enable the module! if [ -s /etc/apache/httpd.conf ] ; then do_enable ; fi if [ -s /etc/apache-ssl/httpd.conf ] ; then do_enablessl ; fi else # Version $2 is the most recently configured version of this # package. if [ -x /usr/sbin/apachectl ] ; then ask_restart ; fi if [ -x /usr/sbin/apache-sslctl ] ; then ask_restartssl ; fi fi ;; abort-upgrade | abort-remove | abort-deconfigure) ;; *) echo "$0: didn't understand being called with \`$1'" 1>&2 exit 1 ;; esac exit 0 #DEBHELPER# mod_fastcgi-SNAP-0910052141/debian/changelog0000664000635400022240000001106107754564466020675 0ustar robsfastcgi00000000000000libapache-mod-fastcgi (2.4.1) unstable; urgency=low * Debian builds adopted by upstream. See the CHANGES file for a list of changes since the last release. Patches from the Debian project most welcome :-) -- Sam Vilain Thu, 13 Nov 2003 02:00:37 +0000 libapache-mod-fastcgi (2.4.0-2) unstable; urgency=low * Removed a useless and buggy debian/postrm; closes: #181418. -- Davide Puricelli (evo) Wed, 19 Feb 2003 14:34:24 +0100 libapache-mod-fastcgi (2.4.0-1) unstable; urgency=low * New upstream version. * Bumping Standards-Version to 3.5.8. * Enhancing debian/postinst and debian/prerm scripts. -- Davide Puricelli (evo) Thu, 13 Feb 2003 14:21:57 +0100 libapache-mod-fastcgi (2.2.12-4) unstable; urgency=low * Fixed debian/rules (gah). -- Davide Puricelli (evo) Fri, 16 Aug 2002 14:45:48 +0200 libapache-mod-fastcgi (2.2.12-3) unstable; urgency=low * No longer links against (nonexistent) libdb.so.2. -- Davide Puricelli (evo) Fri, 16 Aug 2002 13:51:36 +0200 libapache-mod-fastcgi (2.2.12-2) unstable; urgency=low * Fixed -autoUpdate option, closes: #132981. Patch provided by Freddy Spierenburg -- Davide Puricelli (evo) Sun, 17 Mar 2002 15:32:08 +0100 libapache-mod-fastcgi (2.2.12-1) unstable; urgency=low * New upstream version. -- Davide Puricelli (evo) Sun, 9 Dec 2001 21:07:42 +0000 libapache-mod-fastcgi (2.2.10-3) unstable; urgency=low * Now depends on apache-common 1.3.14-2. -- Davide Puricelli (evo) Thu, 7 Dec 2000 19:47:14 +0100 libapache-mod-fastcgi (2.2.10-2) unstable; urgency=low * Recompiled against apache-dev 1.3.14-1; closes: #78516, #78629. -- Davide Puricelli (evo) Sun, 3 Dec 2000 17:44:21 +0100 libapache-mod-fastcgi (2.2.10-1) unstable; urgency=low * New upstream version. -- Davide Puricelli (evo) Tue, 28 Nov 2000 19:40:33 +0100 libapache-mod-fastcgi (2.2.8-1) unstable; urgency=low * New upstream version. -- Davide Puricelli (evo) Mon, 28 Aug 2000 17:46:13 +0200 libapache-mod-fastcgi (2.2.6-2) unstable; urgency=low * Further fixed debian/postinst. -- Davide Puricelli (evo) Tue, 15 Aug 2000 12:06:59 +0200 libapache-mod-fastcgi (2.2.6-1) unstable; urgency=low * New upstream version. * New maintainer email address. -- Davide Puricelli (evo) Sat, 12 Aug 2000 11:13:05 +0200 libapache-mod-fastcgi (2.2.4-2) unstable; urgency=low * Sponsored upload for Davide Puricelli (evo) . * Build-Depends: apache, thanks to Christian T. Steigies. * debian/postinst and debian/prerm should work fine with apache-ssl; closes: #58590. * Updated 500mod_fastcgi.info. -- Milan Zamazal Sun, 6 Aug 2000 17:32:42 +0200 libapache-mod-fastcgi (2.2.4-1) unstable; urgency=low * Sponsored upload for Davide Puricelli (evo) . -- Milan Zamazal Thu, 13 Jul 2000 22:18:12 +0200 libapache-mod-fastcgi (2.2.4-1) unstable; urgency=low * New maintainer. * New upstream version. * Build-Depends: apache-dev; closes: #59488. -- Davide Puricelli (evo) Wed, 21 Jun 2000 18:53:14 +0200 libapache-mod-fastcgi (2.2.2-2) unstable; urgency=low * Compiled for Apache 1.3.9; closes: #45272. * FHS. * Standards 3.0.1. * No lintian 1.8.2 errors. * Orphaned. -- Milan Zamazal Sun, 19 Sep 1999 11:28:28 +0200 libapache-mod-fastcgi (2.2.2-1) unstable; urgency=low * New upstream version. * Compiled with egcs/glibc 2.1. * Compiled for Apache 1.3.6; closes: #43499. * debian/rules: get apxs from path again. * debian/500mod_fastcgi.info: Handles, not Handlers. * Compiled with -O2. * No lintian 1.7 errors. * Thanks to Mark Eichin for the fixes. -- Milan Zamazal Sat, 28 Aug 1999 11:16:03 +0200 libapache-mod-fastcgi (2.2.1-1) unstable; urgency=low * New upstream version. * Install upstream changelog. -- Milan Zamazal Mon, 15 Mar 1999 11:26:11 +0100 libapache-mod-fastcgi (2.1b1-2) unstable; urgency=low * Depend on apache-common <<1.3.4; closes: #32505. * Do not use wild cards in debian/docs. -- Milan Zamazal Mon, 8 Feb 1999 15:30:23 +0100 libapache-mod-fastcgi (2.1b1-1) unstable; urgency=low * Initial Release. * Lintian 0.9.5 satisfied. -- Milan Zamazal Thu, 14 Jan 1999 12:16:50 +0100 Local variables: mode: debian-changelog End: mod_fastcgi-SNAP-0910052141/debian/rules0000775000635400022240000000203507754564467020105 0ustar robsfastcgi00000000000000#!/usr/bin/make -f CFLAGS = -O2 -Wall ifneq (,$(findstring debug,$(DEB_BUILD_OPTIONS))) CFLAGS += -g endif ifeq (,$(findstring nostrip,$(DEB_BUILD_OPTIONS))) INSTALL_PROGRAM += -s endif binary: binary-indep binary-arch binary-indep: binary-arch: build install dh_testdir dh_testroot #dh_installchangelogs CHANGES dh_installdocs docs/mod_fastcgi.html CHANGES dh_installdeb dh_fixperms dh_shlibdeps dh_compress ifeq (,$(findstring nostrip,$(DEB_BUILD_OPTIONS))) dh_strip endif dh_md5sums dh_gencontrol dh_builddeb build: build-stamp build-stamp: dh_testdir CFLAGS="$(CFLAGS)" /usr/bin/apxs -o mod_fastcgi.so -c *.c touch build-stamp clean: dh_testdir rm -f build-stamp -rm -f *.o mod_fastcgi.so dh_clean build-stamp install: dh_testdir dh_installdirs mkdir -p debian/tmp/usr/lib/apache/1.3 cp mod_fastcgi.so debian/tmp/usr/lib/apache/1.3/ chmod 644 debian/tmp/usr/lib/apache/1.3/mod_fastcgi.so cp debian/500mod_fastcgi.info debian/tmp/usr/lib/apache/1.3/ .PHONY: binary binary-arch binary-indep clean build install mod_fastcgi-SNAP-0910052141/debian/500mod_fastcgi.info0000664000635400022240000000057407754564466022413 0ustar robsfastcgi00000000000000LoadModule: fastcgi_module /usr/lib/apache/1.3/mod_fastcgi.so Directives: FastCgiServer FastCgiConfig FastCgiExternalServer FastCgiIpcDir FastCgiSuexec FastCgiAuthenticator FastCgiAuthenticatorAuthoritative FastCgiAuthorizer FastCgiAuthorizerAuthoritative FastCgiAccessChecker FastCgiAccessCheckerAuthoritative Handles: fastcgi-script Description: Support for FastCGI mod_fastcgi-SNAP-0910052141/debian/control0000664000635400022240000000124307754564467020430 0ustar robsfastcgi00000000000000Source: libapache-mod-fastcgi Section: non-free/web Priority: optional Maintainer: Sam Vilain Standards-Version: 3.5.8.0 Build-Depends: apache-dev (>= 1.3.0), debhelper (>> 3.0.0), lynx, libdb2-dev Package: libapache-mod-fastcgi Architecture: any Depends: apache-common (>= 1.3.0), ${shlibs:Depends} Description: FastCGI module for Apache This package contains a FastCGI module for the Apache (1.3) web server. . FastCGI is an open standard for communicating between a web server and a web application, supported by many free and closed source web servers. It provides CGI-like functionality, without the need to program to a particular server API. mod_fastcgi-SNAP-0910052141/debian/prerm0000664000635400022240000000363507754564467020104 0ustar robsfastcgi00000000000000#!/bin/bash # vim: set ts=2 sw=2 et: set -e # These functions comment out the LoadModule directive in httpd.conf # for apache/apache-ssl. They also ask the user whether to restart # apache/apache-ssl. killconf () { src=/etc/apache/httpd.conf dst=/etc/apache/httpd.conf.tmp.$$ if [ -s $src ] ; then sed 's/^\(LoadModule.*mod_fastcgi\.so\)/# \1/' $src > $dst mv -f $dst $src ask_restart fi } killconfssl () { src=/etc/apache-ssl/httpd.conf dst=/etc/apache-ssl/httpd.conf.tmp.$$ if [ -s $src ] ; then sed 's/^\(LoadModule.*mod_fastcgi\.so\)/# \1/' $src > $dst mv -f $src $dst ask_restartssl fi } # These functions ask the user whether to restart apache/apachessl. ask_restart () { echo -n "An Apache module has been modified. Restart apache [Y/n]? " read CONFIG if [ ".$CONFIG" != ".n" -a ".$CONFIG" != ".N" ] then if [ -x /usr/sbin/apachectl ]; then /usr/sbin/apachectl restart || true else echo 'apachectl not found.' fi fi } ask_restartssl () { echo -n "An Apache module has been modified. Restart apache-ssl [Y/n]? " read CONFIG if [ ".$CONFIG" != ".n" -a ".$CONFIG" != ".N" ] ; then if [ -x /usr/sbin/apache-sslctl ]; then /usr/sbin/apache-sslctl restart || true else echo 'apache-sslctl not found.' fi fi } # This script is called twice during the removal of the package; once # after the removal of the package's files from the system, and as # the final step in the removal of this package, after the package's # conffiles have been removed. case "$1" in remove) # This package has been removed, but its configuration has not yet # been purged. killconf killconfssl : ;; upgrade | deconfigure | failed-upgrade) # I _think_ I'm right here...let it sit on an upgrade. : ;; *) echo "$0: didn't understand being called with \`$1'" 1>&2 exit 1 ;; esac exit 0 #DEBHELPER# mod_fastcgi-SNAP-0910052141/README0000664000635400022240000000240707756276334016461 0ustar robsfastcgi00000000000000README for mod_fastcgi ~~~~~~~~~~~~~~~~~~~~~~ mod_fastcgi is a module for the Apache web server, that enables FastCGI - a standards based protocol for communicating with applications that generate dynamic content for web pages. FastCGI provides a superset of CGI functionality, but a subset of the functionality of programming for a particular web server API. Nonetheless, the feature set is rich enough for programming virtually any type of web application, but the result is generally more scalable. For more information on FastCGI, see http://www.fastcgi.com/ For information on installing mod_fastcgi with Apache 1.3.x, see the file INSTALL. For information on installing mod_fastcgi with Apache 2.x, see the file INSTALL.AP2. For information on configuring an installed instance of mod_fastcgi, see the file mod_fastcgi.html in the docs/ directory, also available online at: http://www.fastcgi.com/mod_fastcgi/docs/mod_fastcgi.html For information on programming FastCGI applications, see: http://www.fastcgi.com/#Docs Finally, if you get stuck - start with the Frequently Asked Questions (FAQ) file at: http://www.fastcgi.com/docs/faq.html If you still can't find an answer, join the developer's mailing list at: http://fastcgi.com/fastcgi-developers mod_fastcgi-SNAP-0910052141/fcgi_util.c0000664000635400022240000003466710675512531017711 0ustar robsfastcgi00000000000000/* * $Id: fcgi_util.c,v 1.32 2007/09/23 16:33:29 robs Exp $ */ #include "fcgi.h" #ifdef WIN32 #pragma warning( disable : 4100 ) #elif defined(APACHE2) #include #include #include #include #if APR_HAVE_ARPA_INET_H #include #endif #include "unixd.h" #endif uid_t fcgi_util_get_server_uid(const server_rec * const s) { #if defined(WIN32) return (uid_t) 0; #elif defined(APACHE2) /* the main server's uid */ return ap_user_id; #else /* the vhost's uid */ return s->server_uid; #endif } uid_t fcgi_util_get_server_gid(const server_rec * const s) { #if defined(WIN32) return (uid_t) 0; #elif defined(APACHE2) /* the main server's gid */ return ap_group_id; #else /* the vhost's gid */ return s->server_gid; #endif } /******************************************************************************* * Compute printable MD5 hash. Pool p is used for scratch as well as for * allocating the hash - use temp storage, and dup it if you need to keep it. */ char * fcgi_util_socket_hash_filename(pool *p, const char *path, const char *user, const char *group) { char *buf = ap_pstrcat(p, path, user, group, NULL); /* Canonicalize the path (remove "//", ".", "..") */ ap_getparents(buf); return ap_md5(p, (unsigned char *)buf); } /******************************************************************************* * Concat src1 and src2 using the approprate path seperator for the platform. */ static char * make_full_path(pool *a, const char *src1, const char *src2) { #ifdef WIN32 register int x; char * p ; char * q ; x = strlen(src1); if (x == 0) { p = ap_pstrcat(a, "\\", src2, NULL); } else if (src1[x - 1] != '\\' && src1[x - 1] != '/') { p = ap_pstrcat(a, src1, "\\", src2, NULL); } else { p = ap_pstrcat(a, src1, src2, NULL); } q = p ; while (*q) { if (*q == '/') { *q = '\\' ; } ++q; } return p ; #else return ap_make_full_path(a, src1, src2); #endif } /******************************************************************************* * Return absolute path to file in either "regular" FCGI socket directory or * the dynamic directory. Result is allocated in pool p. */ const char * fcgi_util_socket_make_path_absolute(pool * const p, const char *const file, const int dynamic) { #ifdef APACHE2 if (ap_os_is_path_absolute(p, (char *) file)) #else if (ap_os_is_path_absolute(file)) #endif { return file; } else { const char * parent_dir = dynamic ? fcgi_dynamic_dir : fcgi_socket_dir; return (const char *) make_full_path(p, parent_dir, file); } } #ifndef WIN32 /******************************************************************************* * Build a Domain Socket Address structure, and calculate its size. * The error message is allocated from the pool p. If you don't want the * struct sockaddr_un also allocated from p, pass it preallocated (!=NULL). */ const char * fcgi_util_socket_make_domain_addr(pool *p, struct sockaddr_un **socket_addr, int *socket_addr_len, const char *socket_path) { int socket_pathLen = strlen(socket_path); if (socket_pathLen >= sizeof((*socket_addr)->sun_path)) { return ap_pstrcat(p, "path \"", socket_path, "\" is too long for a Domain socket", NULL); } if (*socket_addr == NULL) *socket_addr = ap_pcalloc(p, sizeof(struct sockaddr_un)); else memset(*socket_addr, 0, sizeof(struct sockaddr_un)); (*socket_addr)->sun_family = AF_UNIX; strcpy((*socket_addr)->sun_path, socket_path); *socket_addr_len = SUN_LEN(*socket_addr); return NULL; } #endif /******************************************************************************* * Convert a hostname or IP address string to an in_addr struct. */ static int convert_string_to_in_addr(const char * const hostname, struct in_addr * const addr) { struct hostent *hp; int count; addr->s_addr = inet_addr((char *)hostname); #if !defined(INADDR_NONE) && defined(APACHE2) #define INADDR_NONE APR_INADDR_NONE #endif if (addr->s_addr == INADDR_NONE) { if ((hp = gethostbyname((char *)hostname)) == NULL) return -1; memcpy((char *) addr, hp->h_addr, hp->h_length); count = 0; while (hp->h_addr_list[count] != 0) count++; return count; } return 1; } /******************************************************************************* * Build an Inet Socket Address structure, and calculate its size. * The error message is allocated from the pool p. If you don't want the * struct sockaddr_in also allocated from p, pass it preallocated (!=NULL). */ const char * fcgi_util_socket_make_inet_addr(pool *p, struct sockaddr_in **socket_addr, int *socket_addr_len, const char *host, unsigned short port) { if (*socket_addr == NULL) *socket_addr = ap_pcalloc(p, sizeof(struct sockaddr_in)); else memset(*socket_addr, 0, sizeof(struct sockaddr_in)); (*socket_addr)->sin_family = AF_INET; (*socket_addr)->sin_port = htons(port); /* Get an in_addr represention of the host */ if (host != NULL) { if (convert_string_to_in_addr(host, &(*socket_addr)->sin_addr) != 1) { return ap_pstrcat(p, "failed to resolve \"", host, "\" to exactly one IP address", NULL); } } else { (*socket_addr)->sin_addr.s_addr = htonl(INADDR_ANY); } *socket_addr_len = sizeof(struct sockaddr_in); return NULL; } /******************************************************************************* * Determine if a process with uid/gid can access a file with mode permissions. */ const char * fcgi_util_check_access(pool *tp, const char * const path, const struct stat *statBuf, const int mode, const uid_t uid, const gid_t gid) { struct stat myStatBuf; if (statBuf == NULL) { if (stat(path, &myStatBuf) < 0) return ap_psprintf(tp, "stat(%s) failed: %s", path, strerror(errno)); statBuf = &myStatBuf; } #ifndef WIN32 /* If the uid owns the file, check the owner bits */ if (uid == statBuf->st_uid) { if (mode & R_OK && !(statBuf->st_mode & S_IRUSR)) return "read not allowed by owner"; if (mode & W_OK && !(statBuf->st_mode & S_IWUSR)) return "write not allowed by owner"; if (mode & X_OK && !(statBuf->st_mode & S_IXUSR)) return "execute not allowed by owner"; return NULL; } #else if (mode & _S_IREAD && !(statBuf->st_mode & _S_IREAD)) return "read not allowed"; if (mode & _S_IWRITE && !(statBuf->st_mode & _S_IWRITE)) return "write not allowed"; /* I don't think this works on FAT, but since I don't know how to check.. * if (mode & _S_IEXEC && !(statBuf->st_mode & _S_IEXEC)) * return "execute not allowed"; */ #endif #if !defined(__EMX__) && !defined(WIN32) /* If the gid is same as the file's group, check the group bits */ if (gid == statBuf->st_gid) { if (mode & R_OK && !(statBuf->st_mode & S_IRGRP)) return "read not allowed by group"; if (mode & W_OK && !(statBuf->st_mode & S_IWGRP)) return "write not allowed by group"; if (mode & X_OK && !(statBuf->st_mode & S_IXGRP)) return "execute not allowed by group"; return NULL; } /* Get the user membership for the file's group. If the * uid is a member, check the group bits. */ { const struct group * const gr = getgrgid(statBuf->st_gid); const struct passwd * const pw = getpwuid(uid); if (gr != NULL && pw != NULL) { char **user = gr->gr_mem; for ( ; *user != NULL; user++) { if (strcmp(*user, pw->pw_name) == 0) { if (mode & R_OK && !(statBuf->st_mode & S_IRGRP)) return "read not allowed by group"; if (mode & W_OK && !(statBuf->st_mode & S_IWGRP)) return "write not allowed by group"; if (mode & X_OK && !(statBuf->st_mode & S_IXGRP)) return "execute not allowed by group"; return NULL; } } } } /* That just leaves the other bits.. */ if (mode & R_OK && !(statBuf->st_mode & S_IROTH)) return "read not allowed"; if (mode & W_OK && !(statBuf->st_mode & S_IWOTH)) return "write not allowed"; if (mode & X_OK && !(statBuf->st_mode & S_IXOTH)) return "execute not allowed"; #endif return NULL; } /******************************************************************************* * Find a FastCGI server with a matching fs_path, and if fcgi_wrapper is * enabled with matching uid and gid. */ fcgi_server * fcgi_util_fs_get_by_id(const char *ePath, uid_t uid, gid_t gid) { char path[FCGI_MAXPATH]; fcgi_server *s; /* @@@ This should now be done in the loop below */ ap_cpystrn(path, ePath, FCGI_MAXPATH); ap_no2slash(path); for (s = fcgi_servers; s != NULL; s = s->next) { int i; const char *fs_path = s->fs_path; for (i = 0; fs_path[i] && path[i]; ++i) { if (fs_path[i] != path[i]) { break; } } if (fs_path[i]) { continue; } if (path[i] == '\0' || path[i] == '/') { if (fcgi_wrapper == NULL || (uid == s->uid && gid == s->gid)) return s; } } return NULL; } /******************************************************************************* * Find a FastCGI server with a matching fs_path, and if fcgi_wrapper is * enabled with matching user and group. */ fcgi_server * fcgi_util_fs_get(const char *ePath, const char *user, const char *group) { char path[FCGI_MAXPATH]; fcgi_server *s; ap_cpystrn(path, ePath, FCGI_MAXPATH); ap_no2slash(path); for (s = fcgi_servers; s != NULL; s = s->next) { if (strcmp(s->fs_path, path) == 0) { if (fcgi_wrapper == NULL) return s; if (strcmp(user, s->user) == 0 && (user[0] == '~' || strcmp(group, s->group) == 0)) { return s; } } } return NULL; } const char * fcgi_util_fs_is_path_ok(pool * const p, const char * const fs_path, struct stat *finfo) { const char *err; if (finfo == NULL) { finfo = (struct stat *)ap_palloc(p, sizeof(struct stat)); if (stat(fs_path, finfo) < 0) return ap_psprintf(p, "stat(%s) failed: %s", fs_path, strerror(errno)); } if (finfo->st_mode == 0) return ap_psprintf(p, "script not found or unable to stat()"); if (S_ISDIR(finfo->st_mode)) return ap_psprintf(p, "script is a directory!"); /* Let the wrapper determine what it can and can't execute */ if (! fcgi_wrapper) { #ifdef WIN32 err = fcgi_util_check_access(p, fs_path, finfo, _S_IEXEC, fcgi_user_id, fcgi_group_id); #else err = fcgi_util_check_access(p, fs_path, finfo, X_OK, fcgi_user_id, fcgi_group_id); #endif if (err) { return ap_psprintf(p, "access for server (uid %ld, gid %ld) not allowed: %s", (long)fcgi_user_id, (long)fcgi_group_id, err); } } return NULL; } /******************************************************************************* * Allocate a new FastCGI server record from pool p with default values. */ fcgi_server * fcgi_util_fs_new(pool *p) { fcgi_server *s = (fcgi_server *) ap_pcalloc(p, sizeof(fcgi_server)); /* Initialize anything who's init state is not zeroizzzzed */ s->listenQueueDepth = FCGI_DEFAULT_LISTEN_Q; s->appConnectTimeout = FCGI_DEFAULT_APP_CONN_TIMEOUT; s->idle_timeout = FCGI_DEFAULT_IDLE_TIMEOUT; s->initStartDelay = DEFAULT_INIT_START_DELAY; s->restartDelay = FCGI_DEFAULT_RESTART_DELAY; s->minServerLife = FCGI_DEFAULT_MIN_SERVER_LIFE; s->restartOnExit = FALSE; s->directive = APP_CLASS_UNKNOWN; s->processPriority = FCGI_DEFAULT_PRIORITY; s->envp = &fcgi_empty_env; #ifdef WIN32 s->listenFd = (int) INVALID_HANDLE_VALUE; #else s->listenFd = -2; #endif return s; } /******************************************************************************* * Add the server to the linked list of FastCGI servers. */ void fcgi_util_fs_add(fcgi_server *s) { s->next = fcgi_servers; fcgi_servers = s; } /******************************************************************************* * Configure uid, gid, user, group, username for wrapper. */ const char * fcgi_util_fs_set_uid_n_gid(pool *p, fcgi_server *s, uid_t uid, gid_t gid) { #ifndef WIN32 struct passwd *pw; struct group *gr; if (fcgi_wrapper == NULL) return NULL; if (uid == 0 || gid == 0) { return "invalid uid or gid, see the -user and -group options"; } s->uid = uid; pw = getpwuid(uid); if (pw == NULL) { return ap_psprintf(p, "getpwuid() couldn't determine the username for uid '%ld', " "you probably need to modify the User directive: %s", (long)uid, strerror(errno)); } s->user = ap_pstrdup(p, pw->pw_name); s->username = s->user; s->gid = gid; gr = getgrgid(gid); if (gr == NULL) { return ap_psprintf(p, "getgrgid() couldn't determine the group name for gid '%ld', " "you probably need to modify the Group directive: %s", (long)gid, strerror(errno)); } s->group = ap_pstrdup(p, gr->gr_name); #endif /* !WIN32 */ return NULL; } /******************************************************************************* * Allocate an array of ServerProcess records. */ ServerProcess * fcgi_util_fs_create_procs(pool *p, int num) { int i; ServerProcess *proc = (ServerProcess *)ap_pcalloc(p, sizeof(ServerProcess) * num); for (i = 0; i < num; i++) { #ifdef WIN32 proc[i].handle = INVALID_HANDLE_VALUE; proc[i].terminationEvent = INVALID_HANDLE_VALUE; #endif proc[i].pid = 0; proc[i].state = FCGI_READY_STATE; } return proc; } int fcgi_util_ticks(struct timeval * tv) { #ifdef WIN32 /* millisecs is sufficent granularity */ DWORD millis = GetTickCount(); tv->tv_sec = millis / 1000; tv->tv_usec = (millis % 1000) * 1000; return 0; #else return gettimeofday(tv, NULL); #endif } mod_fastcgi-SNAP-0910052141/fcgi_protocol.h0000664000635400022240000000541306657723222020573 0ustar robsfastcgi00000000000000/* * $Id: fcgi_protocol.h,v 1.1 1999/02/09 03:08:02 roberts Exp $ */ #ifndef FCGI_PROTOCOL_H #define FCGI_PROTOCOL_H /* * Listening socket file number */ #define FCGI_LISTENSOCK_FILENO 0 typedef struct { unsigned char version; unsigned char type; unsigned char requestIdB1; unsigned char requestIdB0; unsigned char contentLengthB1; unsigned char contentLengthB0; unsigned char paddingLength; unsigned char reserved; } FCGI_Header; #define FCGI_MAX_LENGTH 0xffff /* * Number of bytes in a FCGI_Header. Future versions of the protocol * will not reduce this number. */ #define FCGI_HEADER_LEN 8 /* * Value for version component of FCGI_Header */ #define FCGI_VERSION_1 1 /* * Current version of the FastCGI protocol */ #define FCGI_VERSION FCGI_VERSION_1 /* * Values for type component of FCGI_Header */ #define FCGI_BEGIN_REQUEST 1 #define FCGI_ABORT_REQUEST 2 #define FCGI_END_REQUEST 3 #define FCGI_PARAMS 4 #define FCGI_STDIN 5 #define FCGI_STDOUT 6 #define FCGI_STDERR 7 #define FCGI_DATA 8 #define FCGI_GET_VALUES 9 #define FCGI_GET_VALUES_RESULT 10 #define FCGI_UNKNOWN_TYPE 11 #define FCGI_MAXTYPE (FCGI_UNKNOWN_TYPE) /* * Value for requestId component of FCGI_Header */ #define FCGI_NULL_REQUEST_ID 0 typedef struct { unsigned char roleB1; unsigned char roleB0; unsigned char flags; unsigned char reserved[5]; } FCGI_BeginRequestBody; typedef struct { FCGI_Header header; FCGI_BeginRequestBody body; } FCGI_BeginRequestRecord; /* * Mask for flags component of FCGI_BeginRequestBody */ #define FCGI_KEEP_CONN 1 /* * Values for role component of FCGI_BeginRequestBody */ #define FCGI_RESPONDER 1 #define FCGI_AUTHORIZER 2 #define FCGI_FILTER 3 typedef struct { unsigned char appStatusB3; unsigned char appStatusB2; unsigned char appStatusB1; unsigned char appStatusB0; unsigned char protocolStatus; unsigned char reserved[3]; } FCGI_EndRequestBody; typedef struct { FCGI_Header header; FCGI_EndRequestBody body; } FCGI_EndRequestRecord; /* * Values for protocolStatus component of FCGI_EndRequestBody */ #define FCGI_REQUEST_COMPLETE 0 #define FCGI_CANT_MPX_CONN 1 #define FCGI_OVERLOADED 2 #define FCGI_UNKNOWN_ROLE 3 /* * Variable names for FCGI_GET_VALUES / FCGI_GET_VALUES_RESULT records */ #define FCGI_MAX_CONNS "FCGI_MAX_CONNS" #define FCGI_MAX_REQS "FCGI_MAX_REQS" #define FCGI_MPXS_CONNS "FCGI_MPXS_CONNS" typedef struct { unsigned char type; unsigned char reserved[7]; } FCGI_UnknownTypeBody; typedef struct { FCGI_Header header; FCGI_UnknownTypeBody body; } FCGI_UnknownTypeRecord; #endif /* FCGI_PROTOCOL_H */ mod_fastcgi-SNAP-0910052141/mod_fastcgi.h0000664000635400022240000002065711262520022020205 0ustar robsfastcgi00000000000000/* * $Id: mod_fastcgi.h,v 1.55 2009/09/29 00:31:55 robs Exp $ */ #ifndef MOD_FASTCGI_H #define MOD_FASTCGI_H #define MOD_FASTCGI_VERSION "mod_fastcgi-SNAP-0910052141" #define FASTCGI_HANDLER_NAME "fastcgi-script" /* * # of idle seconds allowed to pass while connected to a FastCGI before aborting */ #define FCGI_DEFAULT_IDLE_TIMEOUT 30 /* * (WIN32) # of sec to wait while trying to connect using a named pipe. * This is overridden by -appConnTimeout, if set. This value is similiar * to the OS specific (blocking) connect() timeout. According to XXX * this is typically XXX sec. */ #define FCGI_NAMED_PIPE_CONNECT_TIMEOUT 90 /* * [WIN32] The number of millisecs to wait after having signaled the * termination event to its applications before issuing a TerminateProcess(). * If all of the applications are based on a version of the FastCGI * application library that properly handles the shutdown event * (fcgi2 v2.2.4), this can be set to <= 0 to prevent the use of * TerminateProcess() entirely. If none of the applications support the * termination event, this value can be set to 1. It is highly reccomended * that the termination event be supported, as TerminateProcess() is a * brutal way of taking down an application. */ #define WIN32_SHUTDOWN_GRACEFUL_WAIT 1000 /* * The number of failed starts that can occur before the application is * considered broken and start attempts fall back to FAILED_STARTS_DELAY. */ #define MAX_FAILED_STARTS 3 /* * The number of seconds between attempts to start an application that * has been declared broken (see MAX_FAILED_STARTS). */ #define FAILED_STARTS_DELAY 600 #define FCGI_DEFAULT_LISTEN_Q 100 /* listen queue (backlog) depth */ #define FCGI_DEFAULT_RESTART_DELAY 5 /* delay between restarts */ #define DEFAULT_INIT_START_DELAY 1 /* delay between starts */ #define FCGI_DEFAULT_PRIORITY 0 /* process priority - not used */ #define FCGI_MIN_EXEC_RETRY_DELAY 10 /* minimum number of seconds to wait before restarting */ #define MAX_INIT_ENV_VARS 64 /* max # of -initial-env options */ /* max number of chars in a line of stderr we can handle from a FastCGI Server */ #define FCGI_SERVER_MAX_STDERR_LINE_LEN 1023 /* size of the buffer the PM uses to read records from the request handlers */ #define FCGI_MSGS_BUFSIZE 32 * 512 #define SERVER_BUFSIZE 8192 /* Dynamic FastCGI applications */ #define FCGI_DEFAULT_MAX_PROCS 50 /* maximum number of processes that * are allowed to run on system */ #define FCGI_DEFAULT_MIN_PROCS 5 /* minimum number of processes that * can be run without being killed * off by the process manager */ #define FCGI_DEFAULT_MAX_CLASS_PROCS 10 /* maximum number of processes that * are allowed to run for a single * application class */ #define FCGI_DEFAULT_KILL_INTERVAL 300 /* number of seconds in which we * should execute the kill policy * by killing off extra instances */ #define FCGI_DEFAULT_UPDATE_INTERVAL 300 /* number of seconds in which we * should recalculate the value of * totalConnTime variable */ #define FCGI_DEFAULT_GAIN 0.5 /* value used as an exponent in the * calculation of the exponentially * decayed connection times; * old values are scaled by * (1-dynamicGain), so making it * smaller weights them more heavily * compared to the current value, * which is scaled by dynamicGain */ #define FCGI_DEFAULT_THRESHOLD_1 0 /* if load falls below this value * and we have only one instance * running, it is killed off */ #define FCGI_DEFAULT_THRESHOLD_N 50 /* if load falls below this value * and we have more than one * instances, one is killed off */ #define FCGI_DEFAULT_START_PROCESS_DELAY 3 /* specifies the maximum number of * seconds a server should wait in * attempt to connect to fcgi app * before sending FCGI_REQUEST_TIMEOUT_JOB */ #define FCGI_DEFAULT_MIN_SERVER_LIFE 30 /* the default minimum number of * seconds a server must stay alive * before it's considered broken. */ /* * # of sec to wait in a non-blocking connect() to the FastCGI application * before aborting the request, or 0 to indicate that blocking connect()s * should be used. Non-blocking connect()s are problematic on many platforms. */ #define FCGI_DEFAULT_APP_CONN_TIMEOUT 0 #define FCGI_DEFAULT_PROCESS_SLACK 5 /* if this number combined with the * number of the currently running * processes exceeds dynamicMaxProcs, then * the KillDynamicProcs() is invoked */ #define FCGI_DEFAULT_RESTART_DYNAMIC 0 /* Do not restart dynamic processes */ #define FCGI_DEFAULT_AUTOUPDATE 0 /* do not automatically restart * fcgi apps when the binary on the * disk is changed. */ /* * Should data recieved from the FastCGI server be immediately flushed to * the client? Default: FALSE */ #define FCGI_FLUSH FALSE #ifdef WIN32 /* # of millisecs to wait on the mbox mutex */ #define FCGI_MBOX_MUTEX_TIMEOUT 5000 #define DEFAULT_SOCK_DIR "\\\\.\\pipe\\FastCGI\\" #elif defined(APACHE2) /* Default dir for Unix/Domain sockets */ #define DEFAULT_SOCK_DIR DEFAULT_REL_RUNTIMEDIR "/fastcgi" #else /* !WIN32 && !APACHE2 */ /* Default dir for Unix/Domain sockets */ #define DEFAULT_SOCK_DIR "logs/fastcgi" #endif #define FCGI_MAGIC_TYPE "application/x-httpd-fcgi" #if defined(PATH_MAX) #define FCGI_MAXPATH PATH_MAX #elif defined(MAXPATHLEN) #define FCGI_MAXPATH MAXPATHLEN #else #define FCGI_MAXPATH 512 #endif /* FCGI_REQUEST_COMPLETE_JOB is the longest: id, path, user, gid, qtime, start */ #define FCGI_MSG_CRAP 1 + 2 + MAX_USER_NAME_LEN + 1 + MAX_GID_CHAR_LEN + (2 * 11) + 3 #if defined(PIPE_BUF) && PIPE_BUF < FCGI_MAXPATH + FCGI_MSG_CRAP #define FCGI_MAX_MSG_LEN PIPE_BUF #undef FCGI_MAXPATH #define FCGI_MAXPATH PIPE_BUF - FCGI_MSG_CRAP #else #define FCGI_MAX_MSG_LEN FCGI_MAXPATH + FCGI_MSG_CRAP #endif /* There is no way to reliably determiine the highest descriptor that can be * assigned (UNP Vol1 Ed2 p337, and APUE p43) so we pick a number. */ #if (defined FD_SETSIZE) && (FD_SETSIZE > 1024) #define FCGI_MAX_FD FD_SETSIZE #else #define FCGI_MAX_FD 1024 #endif #ifndef SUN_LEN #define SUN_LEN(sock) \ (sizeof(*(sock)) - sizeof((sock)->sun_path) + strlen((sock)->sun_path)) #endif #if defined MAXLOGNAME && MAXLOGNAME > 15 #define MAX_USER_NAME_LEN MAXLOGNAME #elif defined UT_NAMESIZE && UT_NAMESIZE > 15 #define MAX_USER_NAME_LEN UT_NAMESIZE #else #define MAX_USER_NAME_LEN 15 /* Max len of user name (suexec w/ ~user), */ #endif /* must accomodate uid printed as %ld too */ #define MAX_GID_CHAR_LEN 15 /* Max #chars in a gid printed as %ld */ #ifndef TRUE #define TRUE (1) #endif #ifndef FALSE #define FALSE (0) #endif #ifndef min #define min(a,b) ((a) < (b) ? (a) : (b)) #endif #ifndef max #define max(a,b) ((a) > (b) ? (a) : (b)) #endif #ifdef APACHE2 #define get_signal_text(a) apr_signal_description_get(a) #else /* !APACHE2 */ /* This is (more or less) from http_main.c. It should be in an Apache header */ #ifndef SYS_SIGLIST #define SYS_SIGLIST ap_sys_siglist extern const char *ap_sys_siglist[]; #endif #define get_signal_text(a) SYS_SIGLIST[WTERMSIG(a)] #endif /* !APACHE2 */ #endif /* MOD_FASTCGI_H */ mod_fastcgi-SNAP-0910052141/fcgi_pm.c0000664000635400022240000020675211260253002017326 0ustar robsfastcgi00000000000000/* * $Id: fcgi_pm.c,v 1.96 2009/09/29 00:34:10 robs Exp $ */ #include "fcgi.h" #if defined(APACHE2) && !defined(WIN32) #include #include #include "unixd.h" #include "apr_signal.h" #endif #ifndef WIN32 #include #endif #ifdef _HPUX_SOURCE #include #define seteuid(arg) setresuid(-1, (arg), -1) #endif int fcgi_dynamic_total_proc_count = 0; /* number of running apps */ time_t fcgi_dynamic_epoch = 0; /* last time kill_procs was * invoked by process mgr */ time_t fcgi_dynamic_last_analyzed = 0; /* last time calculation was * made for the dynamic procs */ static time_t now = 0; #ifdef WIN32 #ifdef APACHE2 #include "mod_cgi.h" #include "apr_version.h" #endif #pragma warning ( disable : 4100 4102 ) static BOOL bTimeToDie = FALSE; /* process termination flag */ HANDLE fcgi_event_handles[3]; #ifndef SIGKILL #define SIGKILL 9 #endif #endif #ifndef WIN32 static int seteuid_root(void) { int rc = seteuid(getuid()); if (rc) { ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server, "FastCGI: seteuid(0) failed"); } return rc; } static int seteuid_user(void) { int rc = seteuid(ap_user_id); if (rc) { ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server, "FastCGI: seteuid(%u) failed", (unsigned)ap_user_id); } return rc; } #endif /* * Signal the process to exit. How (or if) the process responds * depends on the FastCGI application library (esp. on Win32) and * possibly application code (signal handlers and whether or not * SA_RESTART is on). At any rate, we send the signal with the * hopes that the process will exit on its own. Later, as we * review the state of application processes, if we see one marked * for death, but that hasn't died within a specified period of * time, fcgi_kill() is called again with a KILL) */ static void fcgi_kill(ServerProcess *process, int sig) { FCGIDBG3("fcgi_kill(%ld, %d)", (long) process->pid, sig); process->state = FCGI_VICTIM_STATE; #ifdef WIN32 if (sig == SIGTERM) { SetEvent(process->terminationEvent); } else if (sig == SIGKILL) { TerminateProcess(process->handle, 1); } else { ap_assert(0); } #else /* !WIN32 */ if (fcgi_wrapper) { seteuid_root(); } kill(process->pid, sig); if (fcgi_wrapper) { seteuid_user(); } #endif /* !WIN32 */ } /******************************************************************************* * Send SIGTERM to each process in the server class, remove socket * file if appropriate. Currently this is only called when the PM is shutting * down and thus memory isn't freed and sockets and files aren't closed. */ static void shutdown_all() { fcgi_server *s = fcgi_servers; while (s) { ServerProcess *proc = s->procs; int i; int numChildren = (s->directive == APP_CLASS_DYNAMIC) ? dynamicMaxClassProcs : s->numProcesses; /* Send TERM to all processes */ for (i = 0; i < numChildren; i++, proc++) { if (proc->state == FCGI_RUNNING_STATE) { fcgi_kill(proc, SIGTERM); } } s = s->next; } #ifndef WIN32 s = fcgi_servers; while (s) { if (s->socket_path != NULL && s->directive != APP_CLASS_EXTERNAL) { struct timeval tv; /* sleep two seconds to let the children terminate themselves */ tv.tv_sec = 2; tv.tv_usec = 0; ap_select(0, NULL, NULL, NULL, &tv); while (s) { if (s->socket_path != NULL && s->directive != APP_CLASS_EXTERNAL) { /* Remove the socket file */ if (unlink(s->socket_path) != 0 && errno != ENOENT) { ap_log_error(FCGI_LOG_ERR, fcgi_apache_main_server, "FastCGI: unlink() failed to remove socket file \"%s\" for%s server \"%s\"", s->socket_path, (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "", s->fs_path); } } s = s->next; } break; } s = s->next; } #endif #if defined(WIN32) && (WIN32_SHUTDOWN_GRACEFUL_WAIT > 0) /* * WIN32 applications may not have support for the shutdown event * depending on their application library version */ Sleep(WIN32_SHUTDOWN_GRACEFUL_WAIT); s = fcgi_servers; while (s) { ServerProcess *proc = s->procs; int i; int numChildren = (s->directive == APP_CLASS_DYNAMIC) ? dynamicMaxClassProcs : s->numProcesses; /* Send KILL to all processes */ for (i = 0; i < numChildren; i++, proc++) { if (proc->state == FCGI_RUNNING_STATE) { fcgi_kill(proc, SIGKILL); } } s = s->next; } #endif /* WIN32 */ } static int init_listen_sock(fcgi_server * fs) { ap_assert(fs->directive != APP_CLASS_EXTERNAL); /* Create the socket */ if ((fs->listenFd = socket(fs->socket_addr->sa_family, SOCK_STREAM, 0)) < 0) { #ifdef WIN32 errno = WSAGetLastError(); /* Not sure if this will work as expected */ #endif ap_log_error(FCGI_LOG_CRIT_ERRNO, fcgi_apache_main_server, "FastCGI: can't create %sserver \"%s\": socket() failed", (fs->directive == APP_CLASS_DYNAMIC) ? "(dynamic) " : "", fs->fs_path); return -1; } #ifndef WIN32 if (fs->socket_addr->sa_family == AF_UNIX) { /* Remove any existing socket file.. just in case */ unlink(((struct sockaddr_un *)fs->socket_addr)->sun_path); } else #endif { int flag = 1; setsockopt(fs->listenFd, SOL_SOCKET, SO_REUSEADDR, (char *)&flag, sizeof(flag)); } /* Bind it to the socket_addr */ if (bind(fs->listenFd, fs->socket_addr, fs->socket_addr_len)) { char port[11]; #ifdef WIN32 errno = WSAGetLastError(); #endif ap_snprintf(port, sizeof(port), "port=%d", ((struct sockaddr_in *)fs->socket_addr)->sin_port); ap_log_error(FCGI_LOG_CRIT_ERRNO, fcgi_apache_main_server, "FastCGI: can't create %sserver \"%s\": bind() failed [%s]", (fs->directive == APP_CLASS_DYNAMIC) ? "(dynamic) " : "", fs->fs_path, #ifndef WIN32 (fs->socket_addr->sa_family == AF_UNIX) ? ((struct sockaddr_un *)fs->socket_addr)->sun_path : #endif port); } #ifndef WIN32 /* Twiddle Unix socket permissions */ else if (fs->socket_addr->sa_family == AF_UNIX && chmod(((struct sockaddr_un *)fs->socket_addr)->sun_path, S_IRUSR | S_IWUSR)) { ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server, "FastCGI: can't create %sserver \"%s\": chmod() of socket failed", (fs->directive == APP_CLASS_DYNAMIC) ? "(dynamic) " : "", fs->fs_path); } #endif /* Set to listen */ else if (listen(fs->listenFd, fs->listenQueueDepth)) { #ifdef WIN32 errno = WSAGetLastError(); #endif ap_log_error(FCGI_LOG_CRIT_ERRNO, fcgi_apache_main_server, "FastCGI: can't create %sserver \"%s\": listen() failed", (fs->directive == APP_CLASS_DYNAMIC) ? "(dynamic) " : "", fs->fs_path); } else { return 0; } #ifdef WIN32 closesocket(fs->listenFd); #else close(fs->listenFd); #endif fs->listenFd = -1; return -2; } /* *---------------------------------------------------------------------- * * pm_main * * The FastCGI process manager, which runs as a separate * process responsible for: * - Starting all the FastCGI proceses. * - Restarting any of these processes that die (indicated * by SIGCHLD). * - Catching SIGTERM and relaying it to all the FastCGI * processes before exiting. * * Inputs: * Uses global variable fcgi_servers. * * Results: * Does not return. * * Side effects: * Described above. * *---------------------------------------------------------------------- */ #ifndef WIN32 static int caughtSigTerm = FALSE; static int caughtSigChld = FALSE; static int caughtSigAlarm = FALSE; static void signal_handler(int signo) { if ((signo == SIGTERM) || (signo == SIGUSR1) || (signo == SIGHUP)) { /* SIGUSR1 & SIGHUP are sent by apache to its process group * when apache get 'em. Apache follows up (1.2.x) with attacks * on each of its child processes, but we've got the KillMgr * sitting between us so we never see the KILL. The main loop * in ProcMgr also checks to see if the KillMgr has terminated, * and if it has, we handl it as if we should shutdown too. */ caughtSigTerm = TRUE; } else if(signo == SIGCHLD) { caughtSigChld = TRUE; } else if(signo == SIGALRM) { caughtSigAlarm = TRUE; } } #endif /* *---------------------------------------------------------------------- * * spawn_fs_process -- * * Fork and exec the specified fcgi process. * * Results: * 0 for successful fork, -1 for failed fork. * * In case the child fails before or in the exec, the child * obtains the error log by calling getErrLog, logs * the error, and exits with exit status = errno of * the failed system call. * * Side effects: * Child process created. * *---------------------------------------------------------------------- */ static pid_t spawn_fs_process(fcgi_server *fs, ServerProcess *process) { #ifndef WIN32 pid_t child_pid; int i; char *dirName; char *dnEnd, *failedSysCall; child_pid = fork(); if (child_pid) { return child_pid; } /* We're the child. We're gonna exec() so pools don't matter. */ dnEnd = strrchr(fs->fs_path, '/'); if (dnEnd == NULL) { dirName = "./"; } else { dirName = ap_pcalloc(fcgi_config_pool, dnEnd - fs->fs_path + 1); dirName = memcpy(dirName, fs->fs_path, dnEnd - fs->fs_path); } if (chdir(dirName) < 0) { failedSysCall = "chdir()"; goto FailedSystemCallExit; } #ifndef __EMX__ /* OS/2 dosen't support nice() */ if (fs->processPriority != 0) { if (nice(fs->processPriority) == -1) { failedSysCall = "nice()"; goto FailedSystemCallExit; } } #endif /* Open the listenFd on spec'd fd */ if (fs->listenFd != FCGI_LISTENSOCK_FILENO) dup2(fs->listenFd, FCGI_LISTENSOCK_FILENO); /* Close all other open fds, except stdout/stderr. Leave these two open so * FastCGI applications don't have to find and fix ALL 3rd party libs that * write to stdout/stderr inadvertantly. For now, just leave 'em open to the * main server error_log - @@@ provide a directive control where this goes. */ ap_error_log2stderr(fcgi_apache_main_server); dup2(2, 1); for (i = 0; i < FCGI_MAX_FD; i++) { if (i != FCGI_LISTENSOCK_FILENO && i != 2 && i != 1) { close(i); } } /* Ignore SIGPIPE by default rather than terminate. The fs SHOULD * install its own handler. */ signal(SIGPIPE, SIG_IGN); if (fcgi_wrapper) { char *shortName; /* Relinquish our root real uid powers */ seteuid_root(); setuid(ap_user_id); /* Apache (2 anyway) doesn't use suexec if there is no user/group in * effect - this translates to a uid/gid of 0/0 (which should never * be a valid uid/gid for an suexec invocation so it should be safe */ if (fs->uid == 0 && fs->gid == 0) { goto NO_SUEXEC; } #ifdef NO_SUEXEC_FOR_AP_USER_N_GROUP /* AP13 does not use suexec if the target uid/gid is the same as the * server's - AP20 does. I (now) consider the AP2 approach better * (fcgi_pm.c v1.42 incorporated the 1.3 behaviour, v1.84 reverted it, * v1.85 added the compile time option to use the old behaviour). */ if (fcgi_user_id == fs->uid && fcgi_group_id == fs->gid) { goto NO_SUEXEC; } #endif shortName = strrchr(fs->fs_path, '/') + 1; do { execle(fcgi_wrapper, fcgi_wrapper, fs->username, fs->group, shortName, NULL, fs->envp); } while (errno == EINTR); } else { NO_SUEXEC: do { execle(fs->fs_path, fs->fs_path, NULL, fs->envp); } while (errno == EINTR); } failedSysCall = "execle()"; FailedSystemCallExit: fprintf(stderr, "FastCGI: can't start server \"%s\" (pid %ld), %s failed: %s\n", fs->fs_path, (long) getpid(), failedSysCall, strerror(errno)); exit(-1); /* avoid an irrelevant compiler warning */ return(0); #else /* WIN32 */ #ifdef APACHE2 /* based on mod_cgi.c:run_cgi_child() */ apr_pool_t * tp; char * termination_env_string; HANDLE listen_handle = INVALID_HANDLE_VALUE; apr_procattr_t * procattr; apr_proc_t proc = { 0 }; apr_file_t * file; int i = 0; cgi_exec_info_t e_info = { 0 }; request_rec r = { 0 }; const char *command; const char **argv; int rv; APR_OPTIONAL_FN_TYPE(ap_cgi_build_command) *cgi_build_command; cgi_build_command = APR_RETRIEVE_OPTIONAL_FN(ap_cgi_build_command); if (cgi_build_command == NULL) { ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server, "FastCGI: can't exec server \"%s\", mod_cgi isn't loaded", fs->fs_path); return 0; } if (apr_pool_create(&tp, fcgi_config_pool)) return 0; process->terminationEvent = CreateEvent(NULL, TRUE, FALSE, NULL); if (process->terminationEvent == NULL) goto CLEANUP; SetHandleInformation(process->terminationEvent, HANDLE_FLAG_INHERIT, TRUE); termination_env_string = ap_psprintf(tp, "_FCGI_SHUTDOWN_EVENT_=%ld", process->terminationEvent); while (fs->envp[i]) i++; fs->envp[i++] = termination_env_string; fs->envp[i] = (char *) fs->mutex_env_string; ap_assert(fs->envp[i + 1] == NULL); if (fs->socket_path) { SECURITY_ATTRIBUTES sa = { 0 }; sa.bInheritHandle = TRUE; sa.nLength = sizeof(sa); listen_handle = CreateNamedPipe(fs->socket_path, PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, PIPE_UNLIMITED_INSTANCES, 4096, 4096, 0, &sa); if (listen_handle == INVALID_HANDLE_VALUE) { ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server, "FastCGI: can't exec server \"%s\", CreateNamedPipe() failed", fs->fs_path); goto CLEANUP; } } else { listen_handle = (HANDLE) fs->listenFd; } r.per_dir_config = fcgi_apache_main_server->lookup_defaults; r.server = fcgi_apache_main_server; r.filename = (char *) fs->fs_path; r.pool = tp; r.subprocess_env = apr_table_make(tp, 0); e_info.cmd_type = APR_PROGRAM; rv = cgi_build_command(&command, &argv, &r, tp, &e_info); if (rv != APR_SUCCESS) { ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server, "FastCGI: don't know how to spawn cmd child process: %s", fs->fs_path); goto CLEANUP; } if (apr_procattr_create(&procattr, tp)) goto CLEANUP; if (apr_procattr_dir_set(procattr, ap_make_dirstr_parent(tp, fs->fs_path))) goto CLEANUP; if (apr_procattr_cmdtype_set(procattr, e_info.cmd_type)) goto CLEANUP; if (apr_procattr_detach_set(procattr, 1)) goto CLEANUP; if (apr_os_file_put(&file, &listen_handle, 0, tp)) goto CLEANUP; #if (APR_MAJOR_VERSION >= 1) && (APR_MINOR_VERSION >= 3) if (apr_procattr_io_set(procattr, APR_FULL_BLOCK, APR_NO_FILE, APR_NO_FILE)) goto CLEANUP; #endif /* procattr is opaque so we have to use this - unfortuantely it dups */ if (apr_procattr_child_in_set(procattr, file, NULL)) goto CLEANUP; if (apr_proc_create(&proc, command, argv, fs->envp, procattr, tp)) goto CLEANUP; process->handle = proc.hproc; CLEANUP: if (fs->socket_path && listen_handle != INVALID_HANDLE_VALUE) { CloseHandle(listen_handle); } if (i) { fs->envp[i - 1] = NULL; } ap_destroy_pool(tp); return proc.pid; #else /* WIN32 && !APACHE2 */ /* Adapted from Apache's util_script.c ap_call_exec() */ char *interpreter = NULL; char *quoted_filename; char *pCommand; char *pEnvBlock, *pNext; int i = 0; int iEnvBlockLen = 1; file_type_e fileType; STARTUPINFO si; PROCESS_INFORMATION pi; request_rec r; pid_t pid = -1; pool * tp = ap_make_sub_pool(fcgi_config_pool); HANDLE listen_handle = INVALID_HANDLE_VALUE; char * termination_env_string = NULL; process->terminationEvent = CreateEvent(NULL, TRUE, FALSE, NULL); if (process->terminationEvent == NULL) { ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server, "FastCGI: can't create termination event for server \"%s\", " "CreateEvent() failed", fs->fs_path); goto CLEANUP; } SetHandleInformation(process->terminationEvent, HANDLE_FLAG_INHERIT, TRUE); termination_env_string = ap_psprintf(tp, "_FCGI_SHUTDOWN_EVENT_=%ld", process->terminationEvent); if (fs->socket_path) { SECURITY_ATTRIBUTES sa; sa.lpSecurityDescriptor = NULL; sa.bInheritHandle = TRUE; sa.nLength = sizeof(sa); listen_handle = CreateNamedPipe(fs->socket_path, PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, PIPE_UNLIMITED_INSTANCES, 4096, 4096, 0, &sa); if (listen_handle == INVALID_HANDLE_VALUE) { ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server, "FastCGI: can't exec server \"%s\", CreateNamedPipe() failed", fs->fs_path); goto CLEANUP; } } else { listen_handle = (HANDLE) fs->listenFd; } memset(&si, 0, sizeof(si)); memset(&pi, 0, sizeof(pi)); memset(&r, 0, sizeof(r)); /* Can up a fake request to pass to ap_get_win32_interpreter() */ r.per_dir_config = fcgi_apache_main_server->lookup_defaults; r.server = fcgi_apache_main_server; r.filename = (char *) fs->fs_path; r.pool = tp; fileType = ap_get_win32_interpreter(&r, &interpreter); if (fileType == eFileTypeUNKNOWN) { ap_log_error(FCGI_LOG_ERR_NOERRNO, fcgi_apache_main_server, "FastCGI: %s is not executable; ensure interpreted scripts have " "\"#!\" as their first line", fs->fs_path); ap_destroy_pool(tp); goto CLEANUP; } /* * We have the interpreter (if there is one) and we have * the arguments (if there are any). * Build the command string to pass to CreateProcess. */ quoted_filename = ap_pstrcat(tp, "\"", fs->fs_path, "\"", NULL); if (interpreter && *interpreter) { pCommand = ap_pstrcat(tp, interpreter, " ", quoted_filename, NULL); } else { pCommand = quoted_filename; } /* * Make child process use hPipeOutputWrite as standard out, * and make sure it does not show on screen. */ si.cb = sizeof(si); si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES; si.wShowWindow = SW_HIDE; si.hStdInput = listen_handle; /* XXX These should be open to the error_log */ si.hStdOutput = INVALID_HANDLE_VALUE; si.hStdError = INVALID_HANDLE_VALUE; /* * Win32's CreateProcess call requires that the environment * be passed in an environment block, a null terminated block of * null terminated strings. * @todo we should store the env in this format for win32. */ while (fs->envp[i]) { iEnvBlockLen += strlen(fs->envp[i]) + 1; i++; } iEnvBlockLen += strlen(termination_env_string) + 1; iEnvBlockLen += strlen(fs->mutex_env_string) + 1; pEnvBlock = (char *) ap_pcalloc(tp, iEnvBlockLen); i = 0; pNext = pEnvBlock; while (fs->envp[i]) { strcpy(pNext, fs->envp[i]); pNext += strlen(pNext) + 1; i++; } strcpy(pNext, termination_env_string); pNext += strlen(pNext) + 1; strcpy(pNext, fs->mutex_env_string); if (CreateProcess(NULL, pCommand, NULL, NULL, TRUE, 0, pEnvBlock, ap_make_dirstr_parent(tp, fs->fs_path), &si, &pi)) { /* Hack to get 16-bit CGI's working. It works for all the * standard modules shipped with Apache. pi.dwProcessId is 0 * for 16-bit CGIs and all the Unix specific code that calls * ap_call_exec interprets this as a failure case. And we can't * use -1 either because it is mapped to 0 by the caller. */ pid = (fileType == eFileTypeEXE16) ? -2 : pi.dwProcessId; process->handle = pi.hProcess; CloseHandle(pi.hThread); } CLEANUP: if (fs->socket_path && listen_handle != INVALID_HANDLE_VALUE) { CloseHandle(listen_handle); } ap_destroy_pool(tp); return pid; #endif /* !APACHE2 */ #endif /* WIN32 */ } #ifndef WIN32 static void reduce_privileges(void) { const char *name; if (geteuid() != 0) return; #ifndef __EMX__ /* Get username if passed as a uid */ if (ap_user_name[0] == '#') { uid_t uid = atoi(&ap_user_name[1]); struct passwd *ent = getpwuid(uid); if (ent == NULL) { ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server, "FastCGI: process manager exiting, getpwuid(%u) couldn't determine user name, " "you probably need to modify the User directive", (unsigned)uid); exit(1); } name = ent->pw_name; } else name = ap_user_name; /* Change Group */ if (setgid(ap_group_id) == -1) { ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server, "FastCGI: process manager exiting, setgid(%u) failed", (unsigned)ap_group_id); exit(1); } /* See Apache PR2580. Until its resolved, do it the same way CGI is done.. */ /* Initialize supplementary groups */ if (initgroups(name, ap_group_id) == -1) { ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server, "FastCGI: process manager exiting, initgroups(%s,%u) failed", name, (unsigned)ap_group_id); exit(1); } #endif /* __EMX__ */ /* Change User */ if (fcgi_wrapper) { if (seteuid_user() == -1) { ap_log_error(FCGI_LOG_ALERT_NOERRNO, fcgi_apache_main_server, "FastCGI: process manager exiting, failed to reduce privileges"); exit(1); } } else { if (setuid(ap_user_id) == -1) { ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server, "FastCGI: process manager exiting, setuid(%u) failed", (unsigned)ap_user_id); exit(1); } } } /************* * Change the name of this process - best we can easily. */ static void change_process_name(const char * const name) { /* under Apache2, ap_server_argv0 is const */ strncpy((char *) ap_server_argv0, name, strlen(ap_server_argv0)); } #endif /* !WIN32 */ static void schedule_start(fcgi_server *s, int proc) { /* If we've started one recently, don't register another */ time_t time_passed = now - s->restartTime; if ((s->procs[proc].pid && (time_passed < (int) s->restartDelay)) || ((s->procs[proc].pid == 0) && (time_passed < s->initStartDelay))) { FCGIDBG6("ignore_job: slot=%d, pid=%ld, time_passed=%ld, initStartDelay=%ld, restartDelay=%ld", proc, (long) s->procs[proc].pid, time_passed, s->initStartDelay, s->restartDelay); return; } FCGIDBG3("scheduling_start: %s (%d)", s->fs_path, proc); s->procs[proc].state = FCGI_START_STATE; if (proc == dynamicMaxClassProcs - 1) { ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server, "FastCGI: scheduled the %sstart of the last (dynamic) server " "\"%s\" process: reached dynamicMaxClassProcs (%d)", s->procs[proc].pid ? "re" : "", s->fs_path, dynamicMaxClassProcs); } } /* *---------------------------------------------------------------------- * * dynamic_read_msgs * * Removes the records written by request handlers and decodes them. * We also update the data structures to reflect the changes. * *---------------------------------------------------------------------- */ static void dynamic_read_msgs(int read_ready) { fcgi_server *s; int rc; #ifndef WIN32 static int buflen = 0; static char buf[FCGI_MSGS_BUFSIZE + 1]; char *ptr1, *ptr2, opcode; char execName[FCGI_MAXPATH + 1]; char user[MAX_USER_NAME_LEN + 2]; char group[MAX_GID_CHAR_LEN + 1]; unsigned long q_usec = 0UL, req_usec = 0UL; #else fcgi_pm_job *joblist = NULL; fcgi_pm_job *cjob = NULL; #endif pool *sp = NULL, *tp; #ifndef WIN32 user[MAX_USER_NAME_LEN + 1] = group[MAX_GID_CHAR_LEN] = '\0'; #endif /* * To prevent the idle application from running indefinitely, we * check the timer and if it is expired, we recompute the values * for each running application class. Then, when FCGI_REQUEST_COMPLETE_JOB * message is received, only updates are made to the data structures. */ if (fcgi_dynamic_last_analyzed == 0) { fcgi_dynamic_last_analyzed = now; } if ((now - fcgi_dynamic_last_analyzed) >= (int)dynamicUpdateInterval) { for (s = fcgi_servers; s != NULL; s = s->next) { if (s->directive != APP_CLASS_DYNAMIC) break; /* Advance the last analyzed timestamp by the elapsed time since * it was last set. Round the increase down to the nearest * multiple of dynamicUpdateInterval */ fcgi_dynamic_last_analyzed += (((long)(now-fcgi_dynamic_last_analyzed)/dynamicUpdateInterval)*dynamicUpdateInterval); s->smoothConnTime = (unsigned long) ((1.0-dynamicGain)*s->smoothConnTime + dynamicGain*s->totalConnTime); s->totalConnTime = 0UL; s->totalQueueTime = 0UL; } } if (read_ready <= 0) { return; } #ifndef WIN32 rc = read(fcgi_pm_pipe[0], (void *)(buf + buflen), FCGI_MSGS_BUFSIZE - buflen); if (rc <= 0) { if (!caughtSigTerm) { ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server, "FastCGI: read() from pipe failed (%d)", rc); if (rc == 0) { ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server, "FastCGI: the PM is shutting down, Apache seems to have disappeared - bye"); caughtSigTerm = TRUE; } } return; } buflen += rc; buf[buflen] = '\0'; #else /* dynamic_read_msgs() is called when a MBOX_EVENT is received (a * request to do something) and/or when a timeout expires. * There really should be no reason why this wait would get stuck * but there's no point in waiting forever. */ rc = WaitForSingleObject(fcgi_dynamic_mbox_mutex, FCGI_MBOX_MUTEX_TIMEOUT); if (rc != WAIT_OBJECT_0 && rc != WAIT_ABANDONED) { ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server, "FastCGI: failed to aquire the dynamic mbox mutex - something is broke?!"); return; } joblist = fcgi_dynamic_mbox; fcgi_dynamic_mbox = NULL; if (! ReleaseMutex(fcgi_dynamic_mbox_mutex)) { ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server, "FastCGI: failed to release the dynamic mbox mutex - something is broke?!"); } cjob = joblist; #endif #ifdef APACHE2 apr_pool_create(&tp, fcgi_config_pool); #else tp = ap_make_sub_pool(fcgi_config_pool); #endif #ifndef WIN32 for (ptr1 = buf; ptr1; ptr1 = ptr2) { int scan_failed = 0; ptr2 = strchr(ptr1, '*'); if (ptr2) { *ptr2++ = '\0'; } else { break; } opcode = *ptr1; switch (opcode) { case FCGI_SERVER_START_JOB: case FCGI_SERVER_RESTART_JOB: if (sscanf(ptr1, "%c %s %16s %15s", &opcode, execName, user, group) != 4) { scan_failed = 1; } break; case FCGI_REQUEST_TIMEOUT_JOB: if (sscanf(ptr1, "%c %s %16s %15s", &opcode, execName, user, group) != 4) { scan_failed = 1; } break; case FCGI_REQUEST_COMPLETE_JOB: if (sscanf(ptr1, "%c %s %16s %15s %lu %lu", &opcode, execName, user, group, &q_usec, &req_usec) != 6) { scan_failed = 1; } break; default: scan_failed = 1; break; } FCGIDBG7("read_job: %c %s %s %s %lu %lu", opcode, execName, user, group, q_usec, req_usec); if (scan_failed) { ap_log_error(FCGI_LOG_ERR_NOERRNO, fcgi_apache_main_server, "FastCGI: bogus message, sscanf() failed: \"%s\"", ptr1); goto NextJob; } #else /* Update data structures for processing */ while (cjob != NULL) { joblist = cjob->next; FCGIDBG7("read_job: %c %s %s %s %lu %lu", cjob->id, cjob->fs_path, cjob->user, cjob->group, cjob->qsec, cjob->start_time); #endif #ifndef WIN32 s = fcgi_util_fs_get(execName, user, group); #else s = fcgi_util_fs_get(cjob->fs_path, cjob->user, cjob->group); #endif #ifndef WIN32 if (s==NULL && opcode != FCGI_REQUEST_COMPLETE_JOB) #else if (s==NULL && cjob->id != FCGI_REQUEST_COMPLETE_JOB) #endif { #ifdef WIN32 HANDLE mutex = CreateMutex(NULL, FALSE, cjob->fs_path); if (mutex == NULL) { ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server, "FastCGI: can't create accept mutex " "for (dynamic) server \"%s\"", cjob->fs_path); goto BagNewServer; } SetHandleInformation(mutex, HANDLE_FLAG_INHERIT, TRUE); #else const char *err; #endif /* Create a perm subpool to hold the new server data, * we can destroy it if something doesn't pan out */ #ifdef APACHE2 apr_pool_create(&sp, fcgi_config_pool); #else sp = ap_make_sub_pool(fcgi_config_pool); #endif /* Create a new "dynamic" server */ s = fcgi_util_fs_new(sp); s->directive = APP_CLASS_DYNAMIC; s->restartDelay = dynamicRestartDelay; s->listenQueueDepth = dynamicListenQueueDepth; s->initStartDelay = dynamicInitStartDelay; s->envp = dynamicEnvp; s->flush = dynamicFlush; #ifdef WIN32 s->mutex_env_string = ap_psprintf(sp, "_FCGI_MUTEX_=%ld", mutex); s->fs_path = ap_pstrdup(sp, cjob->fs_path); #else s->fs_path = ap_pstrdup(sp, execName); #endif ap_getparents(s->fs_path); ap_no2slash(s->fs_path); s->procs = fcgi_util_fs_create_procs(sp, dynamicMaxClassProcs); /* XXX the socket_path (both Unix and Win) *is* deducible and * thus can and will be used by other apache instances without * the use of shared data regarding the processes serving the * requests. This can result in slightly unintuitive process * counts and security implications. This is prevented * if suexec (Unix) is in use. This is both a feature and a flaw. * Changing it now would break existing installations. */ #ifndef WIN32 /* Create socket file's path */ s->socket_path = fcgi_util_socket_hash_filename(tp, execName, user, group); s->socket_path = fcgi_util_socket_make_path_absolute(sp, s->socket_path, 1); /* Create sockaddr, prealloc it so it won't get created in tp */ s->socket_addr = ap_pcalloc(sp, sizeof(struct sockaddr_un)); err = fcgi_util_socket_make_domain_addr(tp, (struct sockaddr_un **)&s->socket_addr, &s->socket_addr_len, s->socket_path); if (err) { ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server, "FastCGI: can't create (dynamic) server \"%s\": %s", execName, err); goto BagNewServer; } if (init_listen_sock(s)) { goto BagNewServer; } /* If a wrapper is being used, config user/group info */ if (fcgi_wrapper) { if (user[0] == '~') { /* its a user dir uri, the rest is a username, not a uid */ struct passwd *pw = getpwnam(&user[1]); if (!pw) { ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server, "FastCGI: can't create (dynamic) server \"%s\": can't get uid/gid for wrapper: getpwnam(%s) failed", execName, &user[1]); goto BagNewServer; } s->uid = pw->pw_uid; s->user = ap_pstrdup(sp, user); s->username = s->user; s->gid = pw->pw_gid; s->group = ap_psprintf(sp, "%ld", (long)s->gid); } else { struct passwd *pw; s->uid = (uid_t)atol(user); pw = getpwuid(s->uid); if (!pw) { ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server, "FastCGI: can't create (dynamic) server \"%s\": can't get uid/gid for wrapper: getwpuid(%ld) failed", execName, (long)s->uid); goto BagNewServer; } s->user = ap_pstrdup(sp, user); s->username = ap_pstrdup(sp, pw->pw_name); s->gid = (gid_t)atol(group); s->group = ap_pstrdup(sp, group); } } #else /* Create socket file's path */ s->socket_path = fcgi_util_socket_hash_filename(tp, cjob->fs_path, cjob->user, cjob->group); s->socket_path = fcgi_util_socket_make_path_absolute(sp, s->socket_path, 1); s->listenFd = 0; #endif fcgi_util_fs_add(s); } else { #ifndef WIN32 if (opcode == FCGI_SERVER_RESTART_JOB) { #else if (cjob->id==FCGI_SERVER_RESTART_JOB) { #endif /* Check to see if the binary has changed. If so, * kill the FCGI application processes, and * restart them. */ struct stat stbuf; int i; #ifdef WIN32 char * app_path = cjob->fs_path; #else char * app_path = execName; #endif if (stat(app_path, &stbuf) == 0 && stbuf.st_mtime > s->startTime) { int do_restart = 0; /* prevent addition restart requests */ s->startTime = now; #ifndef WIN32 utime(s->socket_path, NULL); #endif /* kill old server(s) */ for (i = 0; i < dynamicMaxClassProcs; i++) { if (s->procs[i].pid > 0 && stbuf.st_mtime > s->procs[i].start_time) { fcgi_kill(&s->procs[i], SIGTERM); do_restart++; } } if (do_restart) { ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server, "FastCGI: restarting " "old server \"%s\" processes, newer version " "found", app_path); } } /* If dynamicAutoRestart, don't mark any new processes * for starting because we probably got the * FCGI_SERVER_START_JOB due to dynamicAutoUpdate and the ProcMgr * will be restarting all of those we just killed. */ if (dynamicAutoRestart) goto NextJob; } #ifndef WIN32 else if (opcode == FCGI_SERVER_START_JOB) { #else else if (cjob->id==FCGI_SERVER_START_JOB) { #endif /* we've been asked to start a process--only start * it if we're not already running at least one * instance. */ int i; for (i = 0; i < dynamicMaxClassProcs; i++) { if (s->procs[i].state == FCGI_RUNNING_STATE) break; } /* if already running, don't start another one */ if (i < dynamicMaxClassProcs) { goto NextJob; } } } #ifndef WIN32 switch (opcode) #else switch (cjob->id) #endif { int i, start; case FCGI_SERVER_RESTART_JOB: start = FALSE; /* We just waxed 'em all. Try to find an idle slot. */ for (i = 0; i < dynamicMaxClassProcs; ++i) { if (s->procs[i].state == FCGI_START_STATE || s->procs[i].state == FCGI_RUNNING_STATE) { break; } else if (s->procs[i].state == FCGI_KILLED_STATE || s->procs[i].state == FCGI_READY_STATE) { start = TRUE; break; } } /* Nope, just use the first slot */ if (i == dynamicMaxClassProcs) { start = TRUE; i = 0; } if (start) { schedule_start(s, i); } break; case FCGI_SERVER_START_JOB: case FCGI_REQUEST_TIMEOUT_JOB: if ((fcgi_dynamic_total_proc_count + 1) > (int) dynamicMaxProcs) { /* * Extra instances should have been * terminated beforehand, probably need * to increase ProcessSlack parameter */ ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server, "FastCGI: can't schedule the start of another (dynamic) server \"%s\" process: " "exceeded dynamicMaxProcs (%d)", s->fs_path, dynamicMaxProcs); goto NextJob; } /* find next free slot */ for (i = 0; i < dynamicMaxClassProcs; i++) { if (s->procs[i].state == FCGI_START_STATE) { FCGIDBG2("ignore_job: slot (%d) is already scheduled for starting", i); break; } else if (s->procs[i].state == FCGI_RUNNING_STATE) { continue; } schedule_start(s, i); break; } #ifdef FCGI_DEBUG if (i >= dynamicMaxClassProcs) { FCGIDBG1("ignore_job: slots are max'd"); } #endif break; case FCGI_REQUEST_COMPLETE_JOB: /* only record stats if we have a structure */ if (s) { #ifndef WIN32 s->totalConnTime += req_usec; s->totalQueueTime += q_usec; #else s->totalConnTime += cjob->start_time; s->totalQueueTime += cjob->qsec; #endif } break; } NextJob: #ifdef WIN32 /* Cleanup job data */ free(cjob->fs_path); free(cjob->user); free(cjob->group); free(cjob); cjob = joblist; #endif continue; BagNewServer: if (sp) ap_destroy_pool(sp); #ifdef WIN32 free(cjob->fs_path); free(cjob); cjob = joblist; #endif } #ifndef WIN32 if (ptr1 == buf) { ap_log_error(FCGI_LOG_ERR_NOERRNO, fcgi_apache_main_server, "FastCGI: really bogus message: \"%s\"", ptr1); ptr1 += strlen(buf); } buflen -= ptr1 - buf; if (buflen) { memmove(buf, ptr1, buflen); } #endif ap_destroy_pool(tp); } /* *---------------------------------------------------------------------- * * dynamic_kill_idle_fs_procs * * Implement a kill policy for the dynamic FastCGI applications. * We also update the data structures to reflect the changes. * * Side effects: * Processes are marked for deletion possibly killed. * *---------------------------------------------------------------------- */ static void dynamic_kill_idle_fs_procs(void) { fcgi_server *s; int victims = 0; for (s = fcgi_servers; s != NULL; s = s->next) { /* * server's smoothed running time, or if that's 0, the current total */ unsigned long connTime; /* * maximum number of microseconds that all of a server's running * processes together could have spent running since the last check */ unsigned long totalTime; /* * percentage, 0-100, of totalTime that the processes actually used */ int loadFactor; int i; int really_running = 0; if (s->directive != APP_CLASS_DYNAMIC || s->numProcesses == 0) { continue; } /* s->numProcesses includes pending kills so get the "active" count */ for (i = 0; i < dynamicMaxClassProcs; ++i) { if (s->procs[i].state == FCGI_RUNNING_STATE) ++really_running; } connTime = s->smoothConnTime ? s->smoothConnTime : s->totalConnTime; totalTime = really_running * (now - fcgi_dynamic_epoch) * 1000000 + 1; loadFactor = 100 * connTime / totalTime; if (really_running == 1) { if (loadFactor >= dynamicThreshold1) { continue; } } else { int load = really_running / ( really_running - 1) * loadFactor; if (load >= dynamicThresholdN) { continue; } } /* * Run through the procs to see if we can get away w/o waxing one. */ for (i = 0; i < dynamicMaxClassProcs; ++i) { if (s->procs[i].state == FCGI_START_STATE) { s->procs[i].state = FCGI_READY_STATE; break; } else if (s->procs[i].state == FCGI_VICTIM_STATE) { break; } } if (i >= dynamicMaxClassProcs) { ServerProcess * procs = s->procs; int youngest = -1; for (i = 0; i < dynamicMaxClassProcs; ++i) { if (procs[i].state == FCGI_RUNNING_STATE) { if (youngest == -1 || procs[i].start_time >= procs[youngest].start_time) { youngest = i; } } } if (youngest != -1) { ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server, "FastCGI: (dynamic) server \"%s\" (pid %ld) termination signaled", s->fs_path, (long) s->procs[youngest].pid); fcgi_kill(&s->procs[youngest], SIGTERM); victims++; } /* * If the number of non-victims is less than or equal to * the minimum that may be running without being killed off, * don't select any more victims. */ if (fcgi_dynamic_total_proc_count - victims <= dynamicMinProcs) { break; } } } } #ifdef WIN32 /* This is a little bogus, there's gotta be a better way to do this * Can we use WaitForMultipleObjects() */ #define FCGI_PROC_WAIT_TIME 100 void child_wait_thread_main(void *dummy) { fcgi_server *s; DWORD dwRet = WAIT_TIMEOUT; int numChildren; int i; int waited; while (!bTimeToDie) { waited = 0; for (s = fcgi_servers; s != NULL; s = s->next) { if (s->directive == APP_CLASS_EXTERNAL || s->listenFd < 0) { continue; } if (s->directive == APP_CLASS_DYNAMIC) { numChildren = dynamicMaxClassProcs; } else { numChildren = s->numProcesses; } for (i=0; i < numChildren; i++) { if (s->procs[i].handle != INVALID_HANDLE_VALUE) { DWORD exitStatus = 0; /* timeout is currently set for 100 miliecond */ /* it may need to be longer or user customizable */ dwRet = WaitForSingleObject(s->procs[i].handle, FCGI_PROC_WAIT_TIME); waited = 1; if (dwRet != WAIT_TIMEOUT && dwRet != WAIT_FAILED) { /* a child fs has died */ /* mark the child as dead */ GetExitCodeProcess(s->procs[i].handle, &exitStatus); if (s->directive == APP_CLASS_STANDARD) { /* restart static app */ s->procs[i].state = FCGI_START_STATE; if (exitStatus != 0) { /* don't bump failure count on exit 0 */ s->numFailures++; } } else { s->numProcesses--; fcgi_dynamic_total_proc_count--; FCGIDBG2("-- fcgi_dynamic_total_proc_count=%d", fcgi_dynamic_total_proc_count); if (s->procs[i].state == FCGI_VICTIM_STATE) { s->procs[i].state = FCGI_KILLED_STATE; } else { /* dynamic app shouldn't have died or dynamicAutoUpdate killed it*/ if (exitStatus != 0) { /* don't bump failure count on exit 0 */ s->numFailures++; } if (dynamicAutoRestart || (s->numProcesses <= 0 && dynamicThreshold1 == 0)) { s->procs[i].state = FCGI_START_STATE; } else { s->procs[i].state = FCGI_READY_STATE; } } } ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server, "FastCGI:%s server \"%s\" (pid %d) terminated with exit with status '%d'", (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "", s->fs_path, (long) s->procs[i].pid, exitStatus); CloseHandle(s->procs[i].handle); CloseHandle(s->procs[i].terminationEvent); s->procs[i].handle = INVALID_HANDLE_VALUE; s->procs[i].terminationEvent = INVALID_HANDLE_VALUE; s->procs[i].pid = -1; /* wake up the main thread */ SetEvent(fcgi_event_handles[WAKE_EVENT]); } } } } Sleep(waited ? 0 : FCGI_PROC_WAIT_TIME); } } #endif #ifndef WIN32 static void setup_signals(void) { struct sigaction sa; /* Setup handlers */ sa.sa_handler = signal_handler; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; if (sigaction(SIGTERM, &sa, NULL) < 0) { ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server, "sigaction(SIGTERM) failed"); } /* httpd restart */ if (sigaction(SIGHUP, &sa, NULL) < 0) { ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server, "sigaction(SIGHUP) failed"); } /* httpd graceful restart */ if (sigaction(SIGUSR1, &sa, NULL) < 0) { ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server, "sigaction(SIGUSR1) failed"); } /* read messages from request handlers - kill interval expired */ if (sigaction(SIGALRM, &sa, NULL) < 0) { ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server, "sigaction(SIGALRM) failed"); } if (sigaction(SIGCHLD, &sa, NULL) < 0) { ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server, "sigaction(SIGCHLD) failed"); } } #endif #if !defined(WIN32) && !defined(APACHE2) int fcgi_pm_main(void *dummy, child_info *info) #else void fcgi_pm_main(void *dummy) #endif { fcgi_server *s; unsigned int i; int read_ready = 0; int alarmLeft = 0; #ifdef WIN32 DWORD dwRet; HANDLE child_wait_thread = INVALID_HANDLE_VALUE; #else int callWaitPid, callDynamicProcs; #endif #ifdef WIN32 /* Add SystemRoot to the dynamic environment */ char ** envp = dynamicEnvp; for (i = 0; *envp; ++i) { ++envp; } fcgi_config_set_env_var(fcgi_config_pool, dynamicEnvp, &i, "SystemRoot"); #else reduce_privileges(); change_process_name("fcgi-pm"); close(fcgi_pm_pipe[1]); setup_signals(); if (fcgi_wrapper) { ap_log_error(FCGI_LOG_NOTICE_NOERRNO, fcgi_apache_main_server, "FastCGI: wrapper mechanism enabled (wrapper: %s)", fcgi_wrapper); } #endif /* Initialize AppClass */ for (s = fcgi_servers; s != NULL; s = s->next) { if (s->directive != APP_CLASS_STANDARD) continue; #ifdef WIN32 if (s->socket_path) s->listenFd = 0; #endif for (i = 0; i < s->numProcesses; ++i) s->procs[i].state = FCGI_START_STATE; } #ifdef WIN32 child_wait_thread = (HANDLE) _beginthread(child_wait_thread_main, 0, NULL); if (child_wait_thread == (HANDLE) -1) { ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server, "FastCGI: failed to create process manager's wait thread!"); } ap_log_error(FCGI_LOG_NOTICE_NOERRNO, fcgi_apache_main_server, "FastCGI: process manager initialized"); #else ap_log_error(FCGI_LOG_NOTICE_NOERRNO, fcgi_apache_main_server, "FastCGI: process manager initialized (pid %ld)", (long) getpid()); #endif now = time(NULL); /* * Loop until SIGTERM */ for (;;) { int sleepSeconds = min(dynamicKillInterval, dynamicUpdateInterval); #ifdef WIN32 time_t expire; #else pid_t childPid; int waitStatus; #endif unsigned int numChildren; unsigned int minServerLife; /* * If we came out of sigsuspend() for any reason other than * SIGALRM, pick up where we left off. */ if (alarmLeft) sleepSeconds = alarmLeft; /* * Examine each configured AppClass for a process that needs * starting. Compute the earliest time when the start should * be attempted, starting it now if the time has passed. Also, * remember that we do NOT need to restart externally managed * FastCGI applications. */ for (s = fcgi_servers; s != NULL; s = s->next) { if (s->directive == APP_CLASS_EXTERNAL) continue; numChildren = (s->directive == APP_CLASS_DYNAMIC) ? dynamicMaxClassProcs : s->numProcesses; minServerLife = (s->directive == APP_CLASS_DYNAMIC) ? dynamicMinServerLife : s->minServerLife; for (i = 0; i < numChildren; ++i) { if (s->procs[i].pid <= 0 && s->procs[i].state == FCGI_START_STATE) { int restart = (s->procs[i].pid < 0); time_t restartTime = s->restartTime; if (s->bad) { /* we've gone to using the badDelay, the only thing that resets bad is when badDelay has expired. but numFailures is only just set below its threshold. the proc's start_times are all reset when the bad is. the numFailures is reset when we see an app run for a period */ s->procs[i].start_time = 0; } if (s->numFailures > MAX_FAILED_STARTS) { time_t last_start_time = s->procs[i].start_time; if (last_start_time && now - last_start_time > minServerLife) { s->bad = 0; s->numFailures = 0; ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server, "FastCGI:%s server \"%s\" has remained" " running for more than %d seconds, its restart" " interval has been restored to %d seconds", (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "", s->fs_path, minServerLife, s->restartDelay); } else { unsigned int j; for (j = 0; j < numChildren; ++j) { if (s->procs[j].pid <= 0) continue; if (s->procs[j].state != FCGI_RUNNING_STATE) continue; if (s->procs[j].start_time == 0) continue; if (now - s->procs[j].start_time > minServerLife) break; } if (j >= numChildren) { s->bad = 1; ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server, "FastCGI:%s server \"%s\" has failed to remain" " running for %d seconds given %d attempts, its restart" " interval has been backed off to %d seconds", (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "", s->fs_path, minServerLife, MAX_FAILED_STARTS, FAILED_STARTS_DELAY); } else { s->bad = 0; s->numFailures = 0; ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server, "FastCGI:%s server \"%s\" has remained" " running for more than %d seconds, its restart" " interval has been restored to %d seconds", (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "", s->fs_path, minServerLife, s->restartDelay); } } } if (s->bad) { restartTime += FAILED_STARTS_DELAY; } else { restartTime += (restart) ? s->restartDelay : s->initStartDelay; } if (restartTime <= now) { if (s->bad) { s->bad = 0; s->numFailures = MAX_FAILED_STARTS; } if (s->listenFd < 0 && init_listen_sock(s)) { if (sleepSeconds > s->initStartDelay) sleepSeconds = s->initStartDelay; break; } #ifndef WIN32 if (caughtSigTerm) { goto ProcessSigTerm; } #endif s->procs[i].pid = spawn_fs_process(s, &s->procs[i]); if (s->procs[i].pid <= 0) { ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server, "FastCGI: can't start%s server \"%s\": spawn_fs_process() failed", (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "", s->fs_path); sleepSeconds = min(sleepSeconds, max((int) s->restartDelay, FCGI_MIN_EXEC_RETRY_DELAY)); s->procs[i].pid = -1; break; } s->procs[i].start_time = now; s->restartTime = now; if (s->startTime == 0) { s->startTime = now; } if (s->directive == APP_CLASS_DYNAMIC) { s->numProcesses++; fcgi_dynamic_total_proc_count++; FCGIDBG2("++ fcgi_dynamic_total_proc_count=%d", fcgi_dynamic_total_proc_count); } s->procs[i].state = FCGI_RUNNING_STATE; if (fcgi_wrapper) { ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server, "FastCGI:%s server \"%s\" (uid %ld, gid %ld) %sstarted (pid %ld)", (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "", s->fs_path, (long) s->uid, (long) s->gid, restart ? "re" : "", (long) s->procs[i].pid); } else { ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server, "FastCGI:%s server \"%s\" %sstarted (pid %ld)", (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "", s->fs_path, restart ? "re" : "", (long) s->procs[i].pid); } ap_assert(s->procs[i].pid > 0); } else { sleepSeconds = min(sleepSeconds, restartTime - now); } } } } #ifndef WIN32 if(caughtSigTerm) { goto ProcessSigTerm; } if((!caughtSigChld) && (!caughtSigAlarm)) { fd_set rfds; alarm(sleepSeconds); FD_ZERO(&rfds); FD_SET(fcgi_pm_pipe[0], &rfds); read_ready = ap_select(fcgi_pm_pipe[0] + 1, &rfds, NULL, NULL, NULL); alarmLeft = alarm(0); } callWaitPid = caughtSigChld; caughtSigChld = FALSE; callDynamicProcs = caughtSigAlarm; caughtSigAlarm = FALSE; now = time(NULL); /* * Dynamic fcgi process management */ if((callDynamicProcs) || (!callWaitPid)) { dynamic_read_msgs(read_ready); if(fcgi_dynamic_epoch == 0) { fcgi_dynamic_epoch = now; } if(((long)(now-fcgi_dynamic_epoch)>=dynamicKillInterval) || ((fcgi_dynamic_total_proc_count+dynamicProcessSlack)>=dynamicMaxProcs)) { dynamic_kill_idle_fs_procs(); fcgi_dynamic_epoch = now; } } if(!callWaitPid) { continue; } /* We've caught SIGCHLD, so find out who it was using waitpid, * write a log message and update its data structure. */ for (;;) { if (caughtSigTerm) goto ProcessSigTerm; childPid = waitpid(-1, &waitStatus, WNOHANG); if (childPid == -1 || childPid == 0) break; for (s = fcgi_servers; s != NULL; s = s->next) { if (s->directive == APP_CLASS_EXTERNAL) continue; if (s->directive == APP_CLASS_DYNAMIC) numChildren = dynamicMaxClassProcs; else numChildren = s->numProcesses; for (i = 0; i < numChildren; i++) { if (s->procs[i].pid == childPid) goto ChildFound; } } /* TODO: print something about this unknown child */ continue; ChildFound: s->procs[i].pid = -1; if (s->directive == APP_CLASS_STANDARD) { /* Always restart static apps */ s->procs[i].state = FCGI_START_STATE; if (! (WIFEXITED(waitStatus) && (WEXITSTATUS(waitStatus) == 0))) { /* don't bump the failure count if the app exited with 0 */ s->numFailures++; } } else { s->numProcesses--; fcgi_dynamic_total_proc_count--; if (s->procs[i].state == FCGI_VICTIM_STATE) { s->procs[i].state = FCGI_KILLED_STATE; } else { /* A dynamic app died or exited without provocation from the PM */ if (! (WIFEXITED(waitStatus) && (WEXITSTATUS(waitStatus) == 0))) { /* don't bump the failure count if the app exited with 0 */ s->numFailures++; } if (dynamicAutoRestart || (s->numProcesses <= 0 && dynamicThreshold1 == 0)) s->procs[i].state = FCGI_START_STATE; else s->procs[i].state = FCGI_READY_STATE; } } if (WIFEXITED(waitStatus)) { ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server, "FastCGI:%s server \"%s\" (pid %ld) terminated by calling exit with status '%d'", (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "", s->fs_path, (long) childPid, WEXITSTATUS(waitStatus)); } else if (WIFSIGNALED(waitStatus)) { ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server, "FastCGI:%s server \"%s\" (pid %ld) terminated due to uncaught signal '%d' (%s)%s", (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "", s->fs_path, (long) childPid, WTERMSIG(waitStatus), get_signal_text(waitStatus), #ifdef WCOREDUMP WCOREDUMP(waitStatus) ? ", a core file may have been generated" : ""); #else ""); #endif } else if (WIFSTOPPED(waitStatus)) { ap_log_error(FCGI_LOG_WARN_NOERRNO, fcgi_apache_main_server, "FastCGI:%s server \"%s\" (pid %ld) stopped due to uncaught signal '%d' (%s)", (s->directive == APP_CLASS_DYNAMIC) ? " (dynamic)" : "", s->fs_path, (long) childPid, WTERMSIG(waitStatus), get_signal_text(waitStatus)); } } /* for (;;), waitpid() */ #else /* WIN32 */ /* wait for an event to occur or timer expires */ expire = time(NULL) + sleepSeconds; dwRet = WaitForMultipleObjects(3, (HANDLE *) fcgi_event_handles, FALSE, sleepSeconds * 1000); if (dwRet == WAIT_FAILED) { /* There is something seriously wrong here */ ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server, "FastCGI: WaitForMultipleObjects() failed on event handles -- pm is shuting down"); bTimeToDie = TRUE; } if (dwRet != WAIT_TIMEOUT) { now = time(NULL); if (now < expire) alarmLeft = expire - now; } /* * Dynamic fcgi process management */ if ((dwRet == MBOX_EVENT) || (dwRet == WAIT_TIMEOUT)) { if (dwRet == MBOX_EVENT) { read_ready = 1; } now = time(NULL); dynamic_read_msgs(read_ready); if(fcgi_dynamic_epoch == 0) { fcgi_dynamic_epoch = now; } if ((now-fcgi_dynamic_epoch >= (int) dynamicKillInterval) || ((fcgi_dynamic_total_proc_count+dynamicProcessSlack) >= dynamicMaxProcs)) { dynamic_kill_idle_fs_procs(); fcgi_dynamic_epoch = now; } read_ready = 0; } else if (dwRet == WAKE_EVENT) { continue; } else if (dwRet == TERM_EVENT) { ap_log_error(FCGI_LOG_INFO_NOERRNO, fcgi_apache_main_server, "FastCGI: Termination event received process manager shutting down"); bTimeToDie = TRUE; dwRet = WaitForSingleObject(child_wait_thread, INFINITE); goto ProcessSigTerm; } else { /* Have an received an unknown event - should not happen */ ap_log_error(FCGI_LOG_CRIT, fcgi_apache_main_server, "FastCGI: WaitForMultipleobjects() return an unrecognized event"); bTimeToDie = TRUE; dwRet = WaitForSingleObject(child_wait_thread, INFINITE); goto ProcessSigTerm; } #endif /* WIN32 */ } /* for (;;), the whole shoot'n match */ ProcessSigTerm: /* * Kill off the children, then exit. */ shutdown_all(); #ifdef WIN32 return; #else exit(0); #endif } #ifdef WIN32 int fcgi_pm_add_job(fcgi_pm_job *new_job) { int rv = WaitForSingleObject(fcgi_dynamic_mbox_mutex, FCGI_MBOX_MUTEX_TIMEOUT); if (rv != WAIT_OBJECT_0 && rv != WAIT_ABANDONED) { ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server, "FastCGI: failed to aquire the dynamic mbox mutex - something is broke?!"); return -1; } new_job->next = fcgi_dynamic_mbox; fcgi_dynamic_mbox = new_job; if (! ReleaseMutex(fcgi_dynamic_mbox_mutex)) { ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server, "FastCGI: failed to release the dynamic mbox mutex - something is broke?!"); } return 0; } #endif mod_fastcgi-SNAP-0910052141/INSTALL0000664000635400022240000001553607756275600016634 0ustar robsfastcgi00000000000000 Apache FastCGI Module Installation !!! !!! See the INSTALL.AP2 document for information on how to build !!! mod_fastcgi for the Apache 2.X series. !!! Notes ===== See docs/mod_fastcgi.html for configuration information. This module supports Apache 1.3+. If your server is 1.2 based, either upgrade or use mod_fastcgi v2.0.18. mod_fastcgi has not been tested on all of the Apache supported platforms. These are known to work: SunOS, Solaris, SCO, Linux, NetBSD (see http://www.netbsd.org/packages/www/ap-fastcgi/), FreeBSD, Digital Unix, AIX, IRIX, FreeBSD, Windows (NT4 and NT2K), MacOSX (10.1.4), and QNX (Inet sockets only). If you're successful in using this module on other platforms, please email fastcgi-developers @ fastcgi.com. This module is maintained at http://www.fastcgi.com. See the web page for mailing list information. Introduction ============ There are three approaches to configure, compile, and install Apache. o APACI - (Apache 1.3+) described in /INSTALL o manual - (original) described in /src/INSTALL o DSO (Dynamic Shared Object) - described in /htdocs/manual/dso.html If you have a binary Apache distribution, such as Red Hat's Secure Server (or prefer a DSO based Apache), you have to build mod_fastcgi as a Dynamic Shared Object (DSO) - see Section 3. If your on Windows NT, see Section 4. 1) Installing mod_fastcgi with APACI ==================================== 1. Copy or move the mod_fastcgi distribution directory to /src/modules/fastcgi. 2. Specify "--activate-module=src/modules/fastcgi/libfastcgi.a" as an argument to ./configure from the directory. If you've previously used APACI to configure Apache, you can also specify this as an argument to ./config.status (Apache 1.3.1+) in order to preserve the existing configuration. $ ./configure \ --activate-module=src/modules/fastcgi/libfastcgi.a or $ ./config.status \ --activate-module=src/modules/fastcgi/libfastcgi.a 3. Rebuild and reinstall Apache. $ make $ make install 4. Edit the httpd configuration files to enable your FastCGI application(s). See docs/mod_fastcgi.html for details. 5. Stop and start the server. $ /usr/local/apache/sbin/apachectl stop $ /usr/local/apache/sbin/apachectl start 2) Installing mod_fastcgi manually ================================== 1. Copy or move the mod_fastcgi distribution directory to /src/modules/fastcgi. 2. Add the FastCGI module to /src/Configuration. Note that modules are listed in reverse priority order --- the ones that come later can override the behavior of those that come earlier. I put mine just after the mod_cgi entry. AddModule modules/fastcgi/libfastcgi.a 3. From the /src directory, reconfigure and rebuild Apache. /src$ ./Configure /src$ make Install the new httpd. 4. Edit the httpd configuration files to enable your FastCGI application(s). See docs/mod_fastcgi.html for details. 5. Stop and start the server. $ kill -TERM `cat /logs/httpd.pid` $ /bin/httpd -f /conf/httpd.conf 3) Installing mod_fastcgi as a DSO ================================== NOTE: If you use FastCgiSuexec, mod_fastcgi cannot reliably determine the suexec path when built as a DSO. To workaround this, provide the full path in the FastCgiSuexec directive. 1. From the mod_fastcgi directory, compile the module. $ cd $ apxs -o mod_fastcgi.so -c *.c 2. Install the module. $ apxs -i -a -n fastcgi mod_fastcgi.so This should create an entry in httpd.conf that looks like this: LoadModule fastcgi_module /mod_fastcgi.so Note that if there's a ClearModuleList directive after new entry, you'll have to either move the LoadModule after the ClearModuleList or add (have a look at how the other modules are handled): AddModule mod_fastcgi.c 3. Edit the httpd configuration file(s) to enable your FastCGI application(s). See docs/mod_fastcgi.html for details. If you want to wrap the mod_fastcgi directives, use: . . 4. Stop and start the server. $ /bin/apachectl stop $ /bin/apachectl start 4) Windows NT ============= To build mod_fastcgi from the command line: 1. Edit the APACHE_SRC_DIR variable in Makefile.nt. 2. Build the module > nmake -f Makefile.nt CFG=[debug | release] To build mod_fastcgi as a project you'll need M$ VC++ 6.0: 1. Open the mod_fastcgi project file with the VC++. 2. Edit the Project for your configuration. a) Select Project->Settings or press . b) Select "All Configurations" from "Settings For" drop-down menu. c) Select the "C/C++" tab. d) Select "Preprocessor" from the "Category" drop-down menu. e) Edit the path in "Additional include directories" to include your Apache source header files (e.g. C:\apache_1.3.12\src\include). f) Select the "Link" tab. g) Select "General" from the "Category" drop-down menu. h) Select "Win32 Release" from "Settings For" drop-down menu. i) Edit the path in "Object/library modules" to include your Apache Release library (e.g. C:\apache_1.3.12\src\CoreR\ApacheCore.lib). j) Select "Win32 Debug" from "Settings For" drop-down menu. k) Edit the path in "Object/library modules" to include your Apache Debug library (e.g. C:\apache_1.3.12\src\CoreD\ApacheCore.lib). l) Select OK. 3. Select "Set Active Configuration" from the "Build" menu and choose either a release or debug build. 4. Select "Build mod_fastcgi.dll" from the "Build" menu. To install mod_fastcgi (built above or retrieved from http://fastcgi.com/dist/): 1. Copy the mod_fastcgi.dll to the Apache modules directory (e.g. C:\Apache\modules) 2. Edit the httpd configurion file (e.g. C:\Apache\conf\httpd.conf) and add a line like: LoadModule fastcgi_module modules/mod_fastcgi.dll Note that if there's a ClearModuleList directive after new entry, you'll have to either move the LoadModule after the ClearModuleList or add (have a look at how the other modules are handled): AddModule mod_fastcgi.c 3. Edit the httpd configuration file(s) to enable your FastCGI application(s). See docs/mod_fastcgi.html for details. mod_fastcgi-SNAP-0910052141/Makefile.AP20000664000635400022240000000055110675500652017603 0ustar robsfastcgi00000000000000# # Makefile for Apache2 # builddir = . top_dir = /usr/local/apache2 top_srcdir = ${top_dir} top_builddir = ${top_dir} include ${top_builddir}/build/special.mk APXS = apxs APACHECTL = apachectl #DEFS=-Dmy_define=my_value #INCLUDES=-Imy/include/dir #LIBS=-Lmy/lib/dir -lmylib all: local-shared-build clean: -rm -f *.o *.lo *.slo *.la mod_fastcgi-SNAP-0910052141/Makefile.libdir0000664000635400022240000000037406533006563020471 0ustar robsfastcgi00000000000000This is a place-holder which indicates to Configure that it shouldn't provide the default targets when building the Makefile in this directory. Instead it'll just prepend all the important variable definitions, and copy the Makefile.tmpl onto the end. mod_fastcgi-SNAP-0910052141/Win32/0002775000635400022240000000000011262520023016450 5ustar robsfastcgi00000000000000mod_fastcgi-SNAP-0910052141/Win32/Makefile.nt0000664000635400022240000001734207345153715020556 0ustar robsfastcgi00000000000000# Based on Microsoft Developer Studio Generated NMAKE File, Based on mod_fastcgi.dsp # # $Id: Makefile.nt,v 1.3 2001/09/04 13:31:57 robs Exp $ # APACHE_SRC_DIR=C:\Program Files\Apache Group\Apache\src !IF "$(CFG)" == "" CFG=debug !MESSAGE No configuration specified. Defaulting to "debug". !MESSAGE !MESSAGE You can specify a configuration when running NMAKE !MESSAGE by defining the macro CFG on the command line. For example: !MESSAGE !MESSAGE NMAKE /f Makefile.nt CFG=debug !MESSAGE !MESSAGE Possible choices for configuration are: "debug" and "release" !MESSAGE !ENDIF !IF "$(CFG)" != "release" && "$(CFG)" != "debug" !MESSAGE Invalid configuration "$(CFG)" specified. !MESSAGE !MESSAGE You can specify a configuration when running NMAKE !MESSAGE by defining the macro CFG on the command line. For example: !MESSAGE !MESSAGE NMAKE /f Makefile.nt CFG=debug !MESSAGE !MESSAGE Possible choices for configuration are: "debug" and "release" !MESSAGE !ERROR An invalid configuration is specified. !ENDIF !IF "$(OS)" == "Windows_NT" NULL= !ELSE NULL=nul !ENDIF !IF "$(CFG)" == "release" OUTDIR=.\Release INTDIR=.\Release # Begin Custom Macros OutDir=.\Release # End Custom Macros ALL : "$(OUTDIR)\mod_fastcgi.dll" CLEAN : -@erase "$(INTDIR)\fcgi_buf.obj" -@erase "$(INTDIR)\fcgi_config.obj" -@erase "$(INTDIR)\fcgi_pm.obj" -@erase "$(INTDIR)\fcgi_protocol.obj" -@erase "$(INTDIR)\fcgi_util.obj" -@erase "$(INTDIR)\mod_fastcgi.obj" -@erase "$(INTDIR)\vc60.idb" -@erase "$(OUTDIR)\mod_fastcgi.dll" -@erase "$(OUTDIR)\mod_fastcgi.exp" -@erase "$(OUTDIR)\mod_fastcgi.ilk" -@erase "$(OUTDIR)\mod_fastcgi.lib" -@erase "$(OUTDIR)\mod_fastcgi.pdb" "$(OUTDIR)" : if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" CPP=cl.exe CPP_PROJ=/nologo /MT /W3 /GX /O2 /I "$(APACHE_SRC_DIR)\include" /I "$(APACHE_SRC_DIR)\os\win32" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /Fp"$(INTDIR)\mod_fastcgi.pch" /YX /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /c .c{$(INTDIR)}.obj:: $(CPP) @<< $(CPP_PROJ) $< << .cpp{$(INTDIR)}.obj:: $(CPP) @<< $(CPP_PROJ) $< << .cxx{$(INTDIR)}.obj:: $(CPP) @<< $(CPP_PROJ) $< << .c{$(INTDIR)}.sbr:: $(CPP) @<< $(CPP_PROJ) $< << .cpp{$(INTDIR)}.sbr:: $(CPP) @<< $(CPP_PROJ) $< << .cxx{$(INTDIR)}.sbr:: $(CPP) @<< $(CPP_PROJ) $< << MTL=midl.exe MTL_PROJ=/nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 RSC=rc.exe BSC32=bscmake.exe BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_fastcgi.bsc" BSC32_SBRS= \ LINK32=link.exe LINK32_FLAGS="$(APACHE_SRC_DIR)\Release\ApacheCore.lib" kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib ws2_32.lib /nologo /subsystem:windows /dll /incremental:yes /pdb:"$(OUTDIR)\mod_fastcgi.pdb" /debug /machine:I386 /out:"$(OUTDIR)\mod_fastcgi.dll" /implib:"$(OUTDIR)\mod_fastcgi.lib" LINK32_OBJS= \ "$(INTDIR)\fcgi_buf.obj" \ "$(INTDIR)\fcgi_config.obj" \ "$(INTDIR)\fcgi_pm.obj" \ "$(INTDIR)\fcgi_protocol.obj" \ "$(INTDIR)\fcgi_util.obj" \ "$(INTDIR)\mod_fastcgi.obj" "$(OUTDIR)\mod_fastcgi.dll" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) $(LINK32) @<< $(LINK32_FLAGS) $(LINK32_OBJS) << !ELSEIF "$(CFG)" == "debug" OUTDIR=.\Debug INTDIR=.\Debug # Begin Custom Macros OutDir=.\Debug # End Custom Macros ALL : "$(OUTDIR)\mod_fastcgi.dll" "$(OUTDIR)\mod_fastcgi.bsc" CLEAN : -@erase "$(INTDIR)\fcgi_buf.obj" -@erase "$(INTDIR)\fcgi_buf.sbr" -@erase "$(INTDIR)\fcgi_config.obj" -@erase "$(INTDIR)\fcgi_config.sbr" -@erase "$(INTDIR)\fcgi_pm.obj" -@erase "$(INTDIR)\fcgi_pm.sbr" -@erase "$(INTDIR)\fcgi_protocol.obj" -@erase "$(INTDIR)\fcgi_protocol.sbr" -@erase "$(INTDIR)\fcgi_util.obj" -@erase "$(INTDIR)\fcgi_util.sbr" -@erase "$(INTDIR)\mod_fastcgi.obj" -@erase "$(INTDIR)\mod_fastcgi.sbr" -@erase "$(INTDIR)\vc60.idb" -@erase "$(INTDIR)\vc60.pdb" -@erase "$(OUTDIR)\mod_fastcgi.bsc" -@erase "$(OUTDIR)\mod_fastcgi.dll" -@erase "$(OUTDIR)\mod_fastcgi.exp" -@erase "$(OUTDIR)\mod_fastcgi.ilk" -@erase "$(OUTDIR)\mod_fastcgi.lib" -@erase "$(OUTDIR)\mod_fastcgi.pdb" "$(OUTDIR)" : if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" CPP=cl.exe CPP_PROJ=/nologo /MTd /W3 /Gm /GX /Zi /Od /I "$(APACHE_SRC_DIR)\include" /I "$(APACHE_SRC_DIR)\os\win32" /D "WIN32" /D "_DEBUG" /D "FCGI_DEBUG" /D "_WINDOWS" /FR"$(INTDIR)\\" /Fp"$(INTDIR)\mod_fastcgi.pch" /YX /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /c .c{$(INTDIR)}.obj:: $(CPP) @<< $(CPP_PROJ) $< << .cpp{$(INTDIR)}.obj:: $(CPP) @<< $(CPP_PROJ) $< << .cxx{$(INTDIR)}.obj:: $(CPP) @<< $(CPP_PROJ) $< << .c{$(INTDIR)}.sbr:: $(CPP) @<< $(CPP_PROJ) $< << .cpp{$(INTDIR)}.sbr:: $(CPP) @<< $(CPP_PROJ) $< << .cxx{$(INTDIR)}.sbr:: $(CPP) @<< $(CPP_PROJ) $< << MTL=midl.exe MTL_PROJ=/nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 RSC=rc.exe BSC32=bscmake.exe BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_fastcgi.bsc" BSC32_SBRS= \ "$(INTDIR)\fcgi_buf.sbr" \ "$(INTDIR)\fcgi_config.sbr" \ "$(INTDIR)\fcgi_pm.sbr" \ "$(INTDIR)\fcgi_protocol.sbr" \ "$(INTDIR)\fcgi_util.sbr" \ "$(INTDIR)\mod_fastcgi.sbr" "$(OUTDIR)\mod_fastcgi.bsc" : "$(OUTDIR)" $(BSC32_SBRS) $(BSC32) @<< $(BSC32_FLAGS) $(BSC32_SBRS) << LINK32=link.exe LINK32_FLAGS="$(APACHE_SRC_DIR)\Debug\ApacheCore.lib" kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib ws2_32.lib /nologo /subsystem:windows /dll /incremental:yes /pdb:"$(OUTDIR)/mod_fastcgi.pdb" /debug /machine:I386 /out:"$(OUTDIR)\mod_fastcgi.dll" /implib:"$(OUTDIR)\mod_fastcgi.lib" /pdbtype:sept LINK32_OBJS= \ "$(INTDIR)\fcgi_buf.obj" \ "$(INTDIR)\fcgi_config.obj" \ "$(INTDIR)\fcgi_pm.obj" \ "$(INTDIR)\fcgi_protocol.obj" \ "$(INTDIR)\fcgi_util.obj" \ "$(INTDIR)\mod_fastcgi.obj" "$(OUTDIR)\mod_fastcgi.dll" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) $(LINK32) @<< $(LINK32_FLAGS) $(LINK32_OBJS) << !ENDIF !IF "$(NO_EXTERNAL_DEPS)" != "1" !IF EXISTS("mod_fastcgi.dep") !INCLUDE "mod_fastcgi.dep" !ELSE !MESSAGE Warning: cannot find "mod_fastcgi.dep" !ENDIF !ENDIF !IF "$(CFG)" == "release" || "$(CFG)" == "debug" SOURCE=..\fcgi_buf.c !IF "$(CFG)" == "release" "$(INTDIR)\fcgi_buf.obj" : $(SOURCE) "$(INTDIR)" $(CPP) $(CPP_PROJ) $(SOURCE) !ELSEIF "$(CFG)" == "debug" "$(INTDIR)\fcgi_buf.obj" "$(INTDIR)\fcgi_buf.sbr" : $(SOURCE) "$(INTDIR)" $(CPP) $(CPP_PROJ) $(SOURCE) !ENDIF SOURCE=..\fcgi_config.c !IF "$(CFG)" == "release" "$(INTDIR)\fcgi_config.obj" : $(SOURCE) "$(INTDIR)" $(CPP) $(CPP_PROJ) $(SOURCE) !ELSEIF "$(CFG)" == "debug" "$(INTDIR)\fcgi_config.obj" "$(INTDIR)\fcgi_config.sbr" : $(SOURCE) "$(INTDIR)" $(CPP) $(CPP_PROJ) $(SOURCE) !ENDIF SOURCE=..\fcgi_pm.c !IF "$(CFG)" == "release" "$(INTDIR)\fcgi_pm.obj" : $(SOURCE) "$(INTDIR)" $(CPP) $(CPP_PROJ) $(SOURCE) !ELSEIF "$(CFG)" == "debug" "$(INTDIR)\fcgi_pm.obj" "$(INTDIR)\fcgi_pm.sbr" : $(SOURCE) "$(INTDIR)" $(CPP) $(CPP_PROJ) $(SOURCE) !ENDIF SOURCE=..\fcgi_protocol.c !IF "$(CFG)" == "release" "$(INTDIR)\fcgi_protocol.obj" : $(SOURCE) "$(INTDIR)" $(CPP) $(CPP_PROJ) $(SOURCE) !ELSEIF "$(CFG)" == "debug" "$(INTDIR)\fcgi_protocol.obj" "$(INTDIR)\fcgi_protocol.sbr" : $(SOURCE) "$(INTDIR)" $(CPP) $(CPP_PROJ) $(SOURCE) !ENDIF SOURCE=..\fcgi_util.c !IF "$(CFG)" == "release" "$(INTDIR)\fcgi_util.obj" : $(SOURCE) "$(INTDIR)" $(CPP) $(CPP_PROJ) $(SOURCE) !ELSEIF "$(CFG)" == "debug" "$(INTDIR)\fcgi_util.obj" "$(INTDIR)\fcgi_util.sbr" : $(SOURCE) "$(INTDIR)" $(CPP) $(CPP_PROJ) $(SOURCE) !ENDIF SOURCE=..\mod_fastcgi.c !IF "$(CFG)" == "release" "$(INTDIR)\mod_fastcgi.obj" : $(SOURCE) "$(INTDIR)" $(CPP) $(CPP_PROJ) $(SOURCE) !ELSEIF "$(CFG)" == "debug" "$(INTDIR)\mod_fastcgi.obj" "$(INTDIR)\mod_fastcgi.sbr" : $(SOURCE) "$(INTDIR)" $(CPP) $(CPP_PROJ) $(SOURCE) !ENDIF !ENDIF mod_fastcgi-SNAP-0910052141/Win32/mod_fastcgi.dsw0000664000635400022240000000112507751306135021461 0ustar robsfastcgi00000000000000Microsoft Developer Studio Workspace File, Format Version 6.00 # WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! ############################################################################### Project: "mod_fastcgi"=.\mod_fastcgi.dsp - Package Owner=<4> Package=<5> {{{ begin source code control mod_fastcgi .. end source code control }}} Package=<4> {{{ }}} ############################################################################### Global: Package=<5> {{{ }}} Package=<3> {{{ }}} ############################################################################### mod_fastcgi-SNAP-0910052141/Win32/mod_fastcgi.dsp0000664000635400022240000001053507376334036021463 0ustar robsfastcgi00000000000000# Microsoft Developer Studio Project File - Name="mod_fastcgi" - Package Owner=<4> # Microsoft Developer Studio Generated Build File, Format Version 6.00 # ** DO NOT EDIT ** # TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 CFG=mod_fastcgi - Win32 Debug !MESSAGE This is not a valid makefile. To build this project using NMAKE, !MESSAGE use the Export Makefile command and run !MESSAGE !MESSAGE NMAKE /f "mod_fastcgi.mak". !MESSAGE !MESSAGE You can specify a configuration when running NMAKE !MESSAGE by defining the macro CFG on the command line. For example: !MESSAGE !MESSAGE NMAKE /f "mod_fastcgi.mak" CFG="mod_fastcgi - Win32 Debug" !MESSAGE !MESSAGE Possible choices for configuration are: !MESSAGE !MESSAGE "mod_fastcgi - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") !MESSAGE "mod_fastcgi - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") !MESSAGE # Begin Project # PROP AllowPerConfigDependencies 0 # PROP Scc_ProjName "" # PROP Scc_LocalPath "" CPP=cl.exe MTL=midl.exe RSC=rc.exe !IF "$(CFG)" == "mod_fastcgi - Win32 Release" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 0 # PROP BASE Output_Dir "Release" # PROP BASE Intermediate_Dir "Release" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 0 # PROP Output_Dir "Release" # PROP Intermediate_Dir "Release" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /FD /c # ADD CPP /nologo /MT /W4 /GX /O2 /I "C:\Apache\include" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FR /YX /FD /c # ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386 # ADD LINK32 ApacheCore.lib ws2_32.lib /nologo /subsystem:windows /dll /incremental:yes /machine:I386 /out:"../Release/mod_fastcgi.dll" /libpath:"C:\Apache\libexec" # SUBTRACT LINK32 /debug !ELSEIF "$(CFG)" == "mod_fastcgi - Win32 Debug" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 1 # PROP BASE Output_Dir "Debug" # PROP BASE Intermediate_Dir "Debug" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 1 # PROP Output_Dir "Debug" # PROP Intermediate_Dir "Debug" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /FD /c # ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "C:\Apache\include" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "FCGI_DEBUG" /FR /YX /FD /c # ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 # ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386 /pdbtype:sept # ADD LINK32 ApacheCore.lib ws2_32.lib /nologo /subsystem:windows /dll /debug /machine:I386 /out:"../Debug/mod_fastcgi.dll" /pdbtype:sept /libpath:"C:\Apache\libexec" # SUBTRACT LINK32 /pdb:none !ENDIF # Begin Target # Name "mod_fastcgi - Win32 Release" # Name "mod_fastcgi - Win32 Debug" # Begin Source File SOURCE=..\CHANGES # End Source File # Begin Source File SOURCE=..\fcgi.h # End Source File # Begin Source File SOURCE=..\fcgi_buf.c # End Source File # Begin Source File SOURCE=..\fcgi_config.c # End Source File # Begin Source File SOURCE=..\fcgi_pm.c # End Source File # Begin Source File SOURCE=..\fcgi_protocol.c # End Source File # Begin Source File SOURCE=..\fcgi_protocol.h # End Source File # Begin Source File SOURCE=..\fcgi_util.c # End Source File # Begin Source File SOURCE=..\INSTALL # End Source File # Begin Source File SOURCE=.\Makefile.nt # End Source File # Begin Source File SOURCE=..\mod_fastcgi.c # End Source File # Begin Source File SOURCE=..\mod_fastcgi.h # End Source File # Begin Source File SOURCE=..\docs\mod_fastcgi.html # End Source File # End Target # End Project mod_fastcgi-SNAP-0910052141/Win32/mod_fastcgi-AP2.dsw0000664000635400022240000000113307751306135022040 0ustar robsfastcgi00000000000000Microsoft Developer Studio Workspace File, Format Version 6.00 # WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! ############################################################################### Project: "mod_fastcgi"=".\mod_fastcgi-AP2.dsp" - Package Owner=<4> Package=<5> {{{ begin source code control mod_fastcgi .. end source code control }}} Package=<4> {{{ }}} ############################################################################### Global: Package=<5> {{{ }}} Package=<3> {{{ }}} ############################################################################### mod_fastcgi-SNAP-0910052141/Win32/mod_fastcgi-AP2.dsp0000664000635400022240000001060410675530101022023 0ustar robsfastcgi00000000000000# Microsoft Developer Studio Project File - Name="mod_fastcgi" - Package Owner=<4> # Microsoft Developer Studio Generated Build File, Format Version 6.00 # ** DO NOT EDIT ** # TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 CFG=mod_fastcgi - Win32 Debug !MESSAGE This is not a valid makefile. To build this project using NMAKE, !MESSAGE use the Export Makefile command and run !MESSAGE !MESSAGE NMAKE /f "mod_fastcgi-AP2.mak". !MESSAGE !MESSAGE You can specify a configuration when running NMAKE !MESSAGE by defining the macro CFG on the command line. For example: !MESSAGE !MESSAGE NMAKE /f "mod_fastcgi-AP2.mak" CFG="mod_fastcgi - Win32 Debug" !MESSAGE !MESSAGE Possible choices for configuration are: !MESSAGE !MESSAGE "mod_fastcgi - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") !MESSAGE "mod_fastcgi - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") !MESSAGE # Begin Project # PROP AllowPerConfigDependencies 0 # PROP Scc_ProjName "mod_fastcgi" # PROP Scc_LocalPath ".." CPP=cl.exe MTL=midl.exe RSC=rc.exe !IF "$(CFG)" == "mod_fastcgi - Win32 Release" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 0 # PROP BASE Output_Dir "Release" # PROP BASE Intermediate_Dir "Release" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 0 # PROP Output_Dir "Release" # PROP Intermediate_Dir "Release" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /FD /c # ADD CPP /nologo /MT /W4 /GX /O2 /I "C:\Apache2\include" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FR /YX /FD /c # ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386 # ADD LINK32 libhttpd.lib libapr.lib libaprutil.lib ws2_32.lib /nologo /subsystem:windows /dll /incremental:yes /machine:I386 /out:"../Release/mod_fastcgi.so" /libpath:"C:\Apache2\lib" # SUBTRACT LINK32 /debug !ELSEIF "$(CFG)" == "mod_fastcgi - Win32 Debug" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 1 # PROP BASE Output_Dir "Debug" # PROP BASE Intermediate_Dir "Debug" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 1 # PROP Output_Dir "Debug" # PROP Intermediate_Dir "Debug" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /FD /c # ADD CPP /nologo /MDd /W4 /Gm /GX /ZI /Od /I "C:\Apache2\include" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "FCGI_DEBUG" /FR /YX /FD /c # ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 # ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386 /pdbtype:sept # ADD LINK32 libhttpd.lib libapr.lib libaprutil.lib ws2_32.lib /nologo /subsystem:windows /dll /profile /map /debug /machine:I386 /out:"../mod_fastcgi-AP2-debug.so" /libpath:"C:\Apache2\lib" !ENDIF # Begin Target # Name "mod_fastcgi - Win32 Release" # Name "mod_fastcgi - Win32 Debug" # Begin Source File SOURCE=..\CHANGES # End Source File # Begin Source File SOURCE=..\fcgi.h # End Source File # Begin Source File SOURCE=..\fcgi_buf.c # End Source File # Begin Source File SOURCE=..\fcgi_config.c # End Source File # Begin Source File SOURCE=..\fcgi_pm.c # End Source File # Begin Source File SOURCE=..\fcgi_protocol.c # End Source File # Begin Source File SOURCE=..\fcgi_protocol.h # End Source File # Begin Source File SOURCE=..\fcgi_util.c # End Source File # Begin Source File SOURCE=..\INSTALL # End Source File # Begin Source File SOURCE=.\Makefile.nt # End Source File # Begin Source File SOURCE=..\mod_fastcgi.c # End Source File # Begin Source File SOURCE=..\mod_fastcgi.h # End Source File # Begin Source File SOURCE=..\docs\mod_fastcgi.html # End Source File # End Target # End Project