mod_fcgid-2.3.9/0000755000175000017500000000000012223627015013332 5ustar trawicktrawickmod_fcgid-2.3.9/docs/0000755000175000017500000000000012223627015014262 5ustar trawicktrawickmod_fcgid-2.3.9/docs/conf/0000755000175000017500000000000012223627015015207 5ustar trawicktrawickmod_fcgid-2.3.9/docs/conf/extra/0000755000175000017500000000000012223627015016332 5ustar trawicktrawickmod_fcgid-2.3.9/docs/manual/0000755000175000017500000000000012223627014015536 5ustar trawicktrawickmod_fcgid-2.3.9/docs/manual/mod/0000755000175000017500000000000012223627015016316 5ustar trawicktrawickmod_fcgid-2.3.9/docs/manual/mod/mod_fcgid.html.en0000644000175000017500000023666411741265611021545 0ustar trawicktrawick mod_fcgid - Apache HTTP Server
<-
Apache > HTTP Server > Documentation > Version 2.4 > Modules

Apache Module mod_fcgid

Available Languages:  en 

Description:Provides for execution of FastCGI applications
Status:External
Module Identifier:fcgid_module
Source File:mod_fcgid.c
Compatibility:Apache 2.0 and higher

Summary

Any program assigned to the handler fcgid-script is processed using the FastCGI protocol; mod_fcgid starts a sufficient number instances of the program to handle concurrent requests, and these programs remain running to handle further incoming requests. This is significantly faster than using the default mod_cgi or mod_cgid modules to launch the program upon each request. However, the programs invoked by mod_fcgid continue to consume resources, so the administrator must weigh the impact of invoking a particular program once per request against the resources required to leave a sufficient number of instances running continuously.

The pool of fcgid-invoked programs is shared between all httpd workers. Configuration directives below let the administrator tune the number of instances of the program that will run concurrently.

Specific executables are assigned this handler either by having a name containing an extension defined by the AddHandler directive, or with an override using the SetHandler directive (e.g., for all files in a specific directory such as cgi-bin).

Some changes have been made in the ASF release of mod_fcgid which can affect existing configurations. All documentation refers to new names for the directives. (The old names still work but are now deprecated.) Please read the Upgrade Notes for details.

For an introduction to using CGI scripts with Apache, see the generic tutorial on Dynamic Content With CGI.

Directives

Topics

See also

top

Upgrade Notes

The following changes have been made in the ASF release of mod_fcgid and should be considered when upgrading from the original version by Ryan Pan (Pan Qingfeng).

top

Examples

Note

The examples assume that mod_fcgid and other necessary modules are loaded into the server already, either built-in or via the LoadModule directive.

Additionally, the example configurations provide full access to the applications using access control directives which work with Apache 2.0 and 2.2. These directives are not appropriate for all environments, and they do not work for development levels of Apache HTTP Server (Subversion trunk).

The first example is a very simple Perl FastCGI application, and its configuration directives. This is typical for FastCGI applications which require no special configuration.

Perl FastCGI application - /usr/local/apache/fcgi-bin/foo.pl

#!/usr/bin/perl
use CGI::Fast;

while (my $q = CGI::Fast->new) {
print("Content-Type: text/plain\n\n");
foreach $var (sort(keys(%ENV))) {
$val = $ENV{$var};
$val =~ s|\n|\\n|g;
$val =~ s|"|\\"|g;
print "${var}=\"${val}\"\n";
}
}

Configuration directives

<Directory /usr/local/apache/fcgi-bin/>
SetHandler fcgid-script
Options +ExecCGI

# Customize the next two directives for your requirements.
Order allow,deny
Allow from all
</Directory>

PHP applications are usually configured using the FcgidWrapper directive and a corresponding wrapper script. The wrapper script can be an appropriate place to define any environment variables required by the application, such as PHP_FCGI_MAX_REQUESTS or anything else. (Environment variables can also be set with FcgidInitialEnv, but they then apply to all applications.)

Here is an example that uses a wrapper script to invoke PHP:

PHP application - /usr/local/phpapp/phpinfo.php

<?php
phpinfo();
?>

Configuration directives

# FcgidMaxRequestsPerProcess should be <= PHP_FCGI_MAX_REQUESTS
# The example PHP wrapper script overrides the default PHP setting.
FcgidMaxRequestsPerProcess 10000

# Uncomment the following line if cgi.fix_pathinfo is set to 1 in
# php.ini:
# FcgidFixPathinfo 1

Alias /phpapp/ /usr/local/phpapp/
<Location /phpapp/>
AddHandler fcgid-script .php
Options +ExecCGI
FcgidWrapper /usr/local/bin/php-wrapper .php

# Customize the next two directives for your requirements.
Order allow,deny
Allow from all
</Location>

PHP wrapper script - /usr/local/bin/php-wrapper

#!/bin/sh
# Set desired PHP_FCGI_* environment variables.
# Example:
# PHP FastCGI processes exit after 500 requests by default.
PHP_FCGI_MAX_REQUESTS=10000
export PHP_FCGI_MAX_REQUESTS

# Replace with the path to your FastCGI-enabled PHP executable
exec /usr/local/bin/php-cgi

Special PHP considerations

By default, PHP FastCGI processes exit after handling 500 requests, and they may exit after this module has already connected to the application and sent the next request. When that occurs, an error will be logged and 500 Internal Server Error will be returned to the client. This PHP behavior can be disabled by setting PHP_FCGI_MAX_REQUESTS to 0, but that can be a problem if the PHP application leaks resources. Alternatively, PHP_FCGI_MAX_REQUESTS can be set to a much higher value than the default to reduce the frequency of this problem. FcgidMaxRequestsPerProcess can be set to a value less than or equal to PHP_FCGI_MAX_REQUESTS to resolve the problem.

PHP child process management (PHP_FCGI_CHILDREN) should always be disabled with mod_fcgid, which will only route one request at a time to application processes it has spawned; thus, any child processes created by PHP will not be used effectively. (Additionally, the PHP child processes may not be terminated properly.) By default, and with the environment variable setting PHP_FCGI_CHILDREN=0, PHP child process management is disabled.

The popular APC opcode cache for PHP cannot share a cache between PHP FastCGI processes unless PHP manages the child processes. Thus, the effectiveness of the cache is limited with mod_fcgid; concurrent PHP requests will use different opcode caches.

top

Process Management

mod_fcgid has several types of controls which affect the creation of additional application processes:

Type of controlDirective
global limit on number of processes FcgidMaxProcesses
limit on number of processes per application FcgidMaxProcessesPerClass
limit on rate of spawning new application processes FcgidSpawnScoreUpLimit and other score-related directives

mod_fcgid has several types of controls which affect the termination of existing application processes:

Type of controlDirective
termination after an idle period FcgidIdleTimeout
termination after it handles a certain number of requests FcgidMaxRequestsPerProcess
termination after a certain lifetime FcgidProcessLifetime

Several of the directives control processing for a process class. A process class is the set of processes which were started with the same executable file and share certain other characteristics such as virtual host and identity. Two commands which are links to or otherwise refer to the same executable file share the same process class.

Note

Certain settings or other concepts that depend on the virtual host, such as FcgidInitialEnv or process classes, distinguish between virtual hosts only if they have distinct server names. (See the ServerName documentation for more information.) In the case of FcgidInitialEnv, if two virtual hosts have the same server name but different environments as defined by FcgidInitialEnv, the environment used for a particular request will be that defined for the virtual host of the request that caused the FastCGI process to be started.

Information about each process will be displayed in the mod_status server-status page.

top

FcgidAccessChecker Directive

Description:full path to FastCGI access checker
Syntax:FcgidAccessChecker command
Default:none
Context:directory, .htaccess
Override:FileInfo
Status:External
Module:mod_fcgid

Access checking or, more formally, access control, is a procedure which verifies that the client is allowed to access a resource, using some mechanism other than authentication and authorization.

Key environment variables passed to the application for access checking are:

FCGI_APACHE_ROLE
set to ACCESS_CHECKER; by checking the current role, the same FastCGI application can handle multiple stages of request processing

The application must output a Status line to indicate the result of the check.

Warning

Before 2.3.6, only one FastCGI application of any type (AAA or handler) can be used for a particular request URI. Otherwise, the wrong FastCGI application may be invoked for one or more phases of request processing.

top

FcgidAccessCheckerAuthoritative Directive

Description:Set to 'off' to allow access control to be passed along to lower modules upon failure
Syntax:FcgidAccessCheckerAuthoritative On|Off
Default:FcgidAccessCheckerAuthoritative On
Context:directory, .htaccess
Override:FileInfo
Status:External
Module:mod_fcgid

This directive controls whether or not other access checkers are allowed to run when this module has an access checker configured and it fails a request. If this directive is On (default) and a FastCGI access checker returns a failure status, a failure is returned to the client without giving other access checkers a chance to allow access. If this directive is Off, other access checkers will be called.

top

FcgidAuthenticator Directive

Description:full path to FastCGI authenticator
Syntax:FcgidAuthenticator command
Default:none
Context:directory, .htaccess
Override:FileInfo
Status:External
Module:mod_fcgid

Authentication is the procedure which verifies that the user is who they claim they are. This directive specifies the full path to a FastCGI application which will handle authentication for a particular context, such as a directory.

Key environment variables passed to the application on authentication are:

REMOTE_USER
set to the user id of the client
REMOTE_PASSWD
set to the plain text password provided by the client
FCGI_APACHE_ROLE
set to AUTHENTICATOR; by checking the current role, the same FastCGI application can handle multiple stages of request processing

The application must output a Status line to indicate the result of authentication.

Warning

Before 2.3.6, only one FastCGI application of any type (AAA or handler) can be used for a particular request URI. Otherwise, the wrong FastCGI application may be invoked for one or more phases of request processing.

top

FcgidAuthenticatorAuthoritative Directive

Description:Set to 'off' to allow authentication to be passed along to lower modules upon failure
Syntax:FcgidAuthenticatorAuthoritative On|Off
Default:FcgidAuthenticatorAuthoritative On
Context:directory, .htaccess
Override:FileInfo
Status:External
Module:mod_fcgid

This directive controls whether or not other authenticators are allowed to run when this module has an authenticator configured and it fails a request. If this directive is On (default) and a FastCGI authenticator returns a failure status, a failure is returned to the client without giving other authenticators a chance to validate the client identity. If this directive is Off, other authenticators will be called.

top

FcgidAuthorizer Directive

Description:full path to FastCGI authorizer
Syntax:FcgidAuthorizer command
Default:none
Context:directory, .htaccess
Override:FileInfo
Status:External
Module:mod_fcgid

Authorization is the procedure which verifies that the user is allowed to access a particular resource. This directive specifies the full path to a FastCGI application which will handle authorization for a particular context, such as a directory.

Key environment variables passed to the application on authorization are:

REMOTE_USER
set to the user id of the client, which has already been authenticated
FCGI_APACHE_ROLE
set to AUTHORIZER; by checking the current role, the same FastCGI application can handle multiple stages of request processing

The application must output a Status line to indicate the result of authorization.

Warning

Before 2.3.6, only one FastCGI application of any type (AAA or handler) can be used for a particular request URI. Otherwise, the wrong FastCGI application may be invoked for one or more phases of request processing.

top

FcgidAuthorizerAuthoritative Directive

Description:Set to 'off' to allow authorization to be passed along to lower modules upon failure
Syntax:FcgidAuthorizerAuthoritative On|Off
Default:FcgidAuthorizerAuthoritative On
Context:directory, .htaccess
Override:FileInfo
Status:External
Module:mod_fcgid

This directive controls whether or not other authorizers are allowed to run when this module has an authorizer configured and it fails a request. If this directive is On (default) and a FastCGI authorizer returns a failure status, a failure is returned to the client without giving other authorizer a chance to access the resource. If this directive is Off, other authorizers will be called.

top

FcgidBusyScanInterval Directive

Description:scan interval for busy timeout process
Syntax:FcgidBusyScanInterval seconds
Default:FcgidBusyScanInterval 120
Context:server config
Status:External
Module:mod_fcgid

The module performs the FcgidBusyTimeout check at this interval.

top

FcgidBusyTimeout Directive

Description:a FastCGI application will be killed after handling a request for FcgidBusyTimeout
Syntax:FcgidBusyTimeout seconds
Default:FcgidBusyTimeout 300
Context:server config, virtual host
Status:External
Module:mod_fcgid

This is the maximum time limit for request handling. If a FastCGI request does not complete within FcgidBusyTimeout seconds, it will be subject to termination. Because the check is performed at the interval defined by FcgidBusyScanInterval, request handling may be allowed to proceed for a longer period of time.

The purpose of this directive is to terminate hung applications. The default timeout may need to be increased for applications that can take longer to process the request.

top

FcgidCmdOptions Directive

Description:Set processing options for a FastCGI command
Syntax:FcgidCmdOptions command option [option] ...
Context:server config, virtual host
Status:External
Module:mod_fcgid

This directive allows processing options to be specified for a specific command spawned by mod_fcgid. Each option for the command corresponds to another directive that normally applies to all commands started within a particular context. If a particular option is not specified on this directive, the default will be used.

The following table provides a list of options and corresponding directives:

Option name and syntaxCorresponding directive
ConnectTimeout seconds FcgidConnectTimeout
IdleTimeout seconds FcgidIdleTimeout
InitialEnv name[=value] FcgidInitialEnv
IOTimeout seconds FcgidIOTimeout
MaxProcesses value FcgidMaxProcessesPerClass
MaxProcessLifeTime seconds FcgidProcessLifeTime
MaxRequestsPerProcess value FcgidMaxRequestsPerProcess
MinProcesses value FcgidMinProcessesPerClass

Multiple environment variables are defined by repeating the InitialEnv option.

Example

FcgidCmdOptions /usr/local/bin/wrapper \
InitialEnv MAX_REQUESTS=2000 \
MaxRequestsPerProcess 2000 \
IOTimeout 90

When /usr/local/bin/wrapper is spawned, its initial environment contains the MAX_REQUESTS=2000 environment variable setting; additionally, mod_fcgid will terminate it after it has handled 2000 requests, and I/O operations will time out after 90 seconds. Directives corresponding to other options, such as FcgidIdleTimeout or FcgidProcessLifeTime, will be ignored for this command; defaults will be used for options not specified on FcgidCmdOptions.

top

FcgidConnectTimeout Directive

Description:Connect timeout to FastCGI server
Syntax:FcgidConnectTimeout seconds
Default:FcgidConnectTimeout 3
Context:server config, virtual host
Status:External
Module:mod_fcgid

This is the maximum period of time the module will wait while trying to connect to a FastCGI application on Windows. (This directive is not respected on Unix, where AF_UNIX defaults will apply.)

This setting will apply to all applications spawned for this server or virtual host. Use FcgidCmdOptions to apply this setting to a single application.

top

FcgidErrorScanInterval Directive

Description:scan interval for exited process
Syntax:FcgidErrorScanInterval seconds
Default:FcgidErrorScanInterval 3
Context:server config
Status:External
Module:mod_fcgid

This is the interval at which the module will handle pending process termination. Termination is pending for any processes which have exceeded FcgidIdleTimeout or FcgidProcessLifeTime.

Unix: mod_fcgid will terminate such processes with SIGTERM; if the process is still active during the next scan, the process will be terminated with SIGKILL. Thus, this directive controls the amount of time for orderly process terminate before being forcibly killed.

top

FcgidFixPathinfo Directive

Description:Mirror the PHP cgi.fix_pathinfo setting
Syntax:FcgidFixPathinfo 1
Default:FcgidFixPathinfo 0
Context:server config
Status:External
Module:mod_fcgid

This directive enables special SCRIPT_NAME processing which allows PHP to provide additional path information. The setting of FcgidFixPathinfo should mirror the cgi.fix_pathinfo setting in php.ini.

top

FcgidIdleScanInterval Directive

Description:scan interval for idle timeout process
Syntax:FcgidIdleScanInterval seconds
Default:FcgidIdleScanInterval 120
Context:server config
Status:External
Module:mod_fcgid

This is the interval at which the module will search for processes which have exceeded FcgidIdleTimeout or FcgidProcessLifeTime.

top

FcgidIdleTimeout Directive

Description:An idle FastCGI application will be killed after FcgidIdleTimeout
Syntax:FcgidIdleTimeout seconds
Default:FcgidIdleTimeout 300
Context:server config, virtual host
Status:External
Module:mod_fcgid

Application processes which have not handled a request for this period of time will be terminated, if the number of processses for the class exceeds FcgidMinProcessesPerClass. A value of 0 disables the check.

This idle timeout check is performed at the frequency of the configured FcgidIdleScanInterval.

This setting will apply to all applications spawned for this server or virtual host. Use FcgidCmdOptions to apply this setting to a single application.

top

FcgidInitialEnv Directive

Description:an environment variable name and optional value to pass to FastCGI.
Syntax:FcgidInitialEnv name [ value ]
Default:none
Context:server config, virtual host
Status:External
Module:mod_fcgid

Use FcgidInitialEnv to define environment variables to pass to the FastCGI application. This directive can be used multiple times.

This setting will apply to all applications spawned for this server or virtual host. Use FcgidCmdOptions to apply this setting to a single application.

top

FcgidIOTimeout Directive

Description:Communication timeout to FastCGI server
Syntax:FcgidIOTimeout seconds
Default:FcgidIOTimeout 40
Context:server config, virtual host
Status:External
Module:mod_fcgid

This is the maximum period of time the module will wait while trying to read from or write to a FastCGI application.

Note

The FastCGI application must begin generating the response within this period of time. Increase this directive as necessary to handle applications which take a relatively long period of time to respond.

This setting will apply to all applications spawned for this server or virtual host. Use FcgidCmdOptions to apply this setting to a single application.

top

FcgidIPCDir Directive

Description:directory for AF_UNIX sockets (Unix) or pipes (Windows)
Syntax:FcgidIPCDir pathname
Default:FcgidIPCDir logs/fcgidsock
Context:server config
Status:External
Module:mod_fcgid

This module uses AF_UNIX sockets or named pipes, depending on the platform, to communicate with FastCGI applications. This directive specifies the directory where those sockets or named pipes will be created.

top

FcgidMaxProcesses Directive

Description:maximum number of FastCGI application processes
Syntax:FcgidMaxProcesses value
Default:FcgidMaxProcesses 1000
Context:server config
Status:External
Module:mod_fcgid

This directive sets the maximum number of FastCGI application processes which can be active at one time.

top

FcgidMaxProcessesPerClass Directive

Description:Max process count of one class of FastCGI application
Syntax:FcgidMaxProcessesPerClass value
Default:FcgidMaxProcessesPerClass 100
Context:server config, virtual host
Status:External
Module:mod_fcgid

This directive sets the maximum number of processes that can be started for each process class.

This setting will apply to all applications spawned for this server or virtual host. Use FcgidCmdOptions to apply this setting to a single application.

top

FcgidMaxRequestInMem Directive

Description:maximum size of a request which will be held in memory
Syntax:FcgidMaxRequestInMem bytes
Default:FcgidMaxRequestInMem 65536
Context:server config, virtual host
Status:External
Module:mod_fcgid

This module reads the entire request body from the client before sending it to the application. Normally the request body will be stored in memory. Once the amount of request body read from the client exceeds FcgidMaxRequestInMem bytes, the remainder of the request body will be stored in a temporary file.

top

FcgidMaxRequestLen Directive

Description:maximum HTTP request length
Syntax:FcgidMaxRequestLen bytes
Default:FcgidMaxRequestLen 131072
Context:server config, virtual host
Status:External
Module:mod_fcgid

If the size of the request body exceeds this amount, the request will fail with 500 Server Error.

Administrators should change this to an appropriate value for their site based on application requirements.

Warning

Before 2.3.6, this defaulted to 1GB. Most users of earlier versions should use this directive to set a more reasonable limit.

See also

top

FcgidMaxRequestsPerProcess Directive

Description:Max requests handled by each FastCGI application
Syntax:FcgidMaxRequestsPerProcess value
Default:FcgidMaxRequestsPerProcess 0
Context:server config, virtual host
Status:External
Module:mod_fcgid

FastCGI application processes will be terminated after handling the specified number of requests. A value of 0 disables the check.

Note

A value of -1 is currently accepted for ease of migration for existing configurations. It is treated the same as 0.

Certain applications, notably PHP as FastCGI, have their own facility for terminating after handling a certain number of requests. This directive can be used to avoid sending additional requests to the application after it has handled its limit.

Note

If this is set such that frequent process creation will be required, you will likely need to adjust FcgidSpawnScoreUpLimit or other score-related directives to allow more frequent process creation.

This setting will apply to all applications spawned for this server or virtual host. Use FcgidCmdOptions to apply this setting to a single application.

top

FcgidMinProcessesPerClass Directive

Description:Min process count of one class of FastCGI application
Syntax:FcgidMinProcessesPerClass value
Default:FcgidMinProcessesPerClass 3
Context:server config, virtual host
Status:External
Module:mod_fcgid

This directive sets the minimum number of processes that will be retained in a process class after finishing requests.

This setting will apply to all applications spawned for this server or virtual host. Use FcgidCmdOptions to apply this setting to a single application.

top

FcgidOutputBufferSize Directive

Description:CGI output buffer size
Syntax:FcgidOutputBufferSize bytes
Default:FcgidOutputBufferSize 65536
Context:server config, virtual host
Status:External
Module:mod_fcgid

This is the maximum amount of response data the module will read from the FastCGI application before flushing the data to the client.

top

FcgidPassHeader Directive

Description:Header name which will be passed to FastCGI as environment variable.
Syntax:FcgidPassHeader name
Default:none
Context:server config, virtual host
Status:External
Module:mod_fcgid

This directive specifies the name of a request header which will be passed to the FastCGI application as an environment variable. The name of the environment variable is derived from the value specified on this directive, as discussed below:

The legacy behavior is to use the value specified on this directive as the environment variable name, converting hyphens to underscores. No case conversion is performed.

Beginning with release 2.3.6, an additional environment variable is created. The value specified on this directive is converted to upper case, prefixed with HTTP_, and hyphens are converted to underscores.

Note

Most request headers are already available to the application as environment variables, and generally are prefixed with HTTP_. (Notable exceptions are Content-type and Content-length, which do not have the HTTP_ prefix.) Thus, this directive is only required for request headers that are purposefully omitted, such as Authorization and Proxy-Authorization. Only pass these request headers if absolutely required.

top

FcgidProcessLifeTime Directive

Description:maximum FastCGI application process lifetime
Syntax:FcgidProcessLifeTime seconds
Default:FcgidProcessLifeTime 3600
Context:server config, virtual host
Status:External
Module:mod_fcgid

Idle application processes which have existed for greater than this time will be terminated, if the number of processses for the class exceeds FcgidMinProcessesPerClass. A value of 0 disables the check.

This process lifetime check is performed at the frequency of the configured FcgidIdleScanInterval.

This setting will apply to all applications spawned for this server or virtual host. Use FcgidCmdOptions to apply this setting to a single application.

top

FcgidProcessTableFile Directive

Description:shared memory file path
Syntax:FcgidProcessTableFile pathname
Default:FcgidProcessTableFile logs/fcgid_shm
Context:server config
Status:External
Module:mod_fcgid

This module uses shared memory on Unix to maintain state which is shared between httpd processes. This directive specifies the name of the shared memory file.

top

FcgidSpawnScore Directive

Description:Each spawn adds this value to the process activity score.
Syntax:FcgidSpawnScore value
Default:FcgidSpawnScore 1
Context:server config
Status:External
Module:mod_fcgid

Lower values of this directive increase the allowed spawn rate.

Refer to the FcgidSpawnScoreUpLimit directive for more information.

top

FcgidSpawnScoreUpLimit Directive

Description:Maximum value of the process activity score which allows a spawn to occur
Syntax:FcgidSpawnScoreUpLimit value
Default:FcgidSpawnScoreUpLimit 10
Context:server config
Status:External
Module:mod_fcgid

A process activity score is maintained for each FastCGI application; the score is used to control the rate of spawning in order to avoid placing too much load on the system, particularly for applications that are repeatedly exiting abnormally.

The value of FcgidSpawnScore is added to the score for every spawned application process. The value of FcgidTerminationScore is added to the score for every terminated application process. The value of FcgidTimeScore is subtracted from the score every second.

When the current score is higher than the value of FcgidSpawnScoreUpLimit, no additional application processes will be spawned; subsequent requests must wait until an existing process is free or until the score decreases below the limit.

If the limit is reached under normal load, it may not be sufficient to simply increase the limit, as that would only delay the amount of time before the limit is reached again. Decrease the value of FcgidSpawnScore and/or FcgidTerminationScore, or increase the value of FcgidTimeScore, to allow a higher rate of spawning.

top

FcgidTerminationScore Directive

Description:Each terminated process adds this value to the process activity score.
Syntax:FcgidTerminationScore value
Default:FcgidTerminationScore 2
Context:server config
Status:External
Module:mod_fcgid

Lower values of this directive increase the allowed spawn rate. Negative values can be useful in some circumstances, such as allowing process replacement without increasing the score.

Refer to the FcgidSpawnScoreUpLimit directive for more information.

top

FcgidTimeScore Directive

Description:Amount subtracted from process activity score each second
Syntax:FcgidTimeScore value
Default:FcgidTimeScore 1
Context:server config
Status:External
Module:mod_fcgid

Higher values of this directive increase the allowed spawn rate.

Refer to the FcgidSpawnScoreUpLimit directive for more information.

top

FcgidWin32PreventOrphans Directive

Description:Job Control orphan prevention for fcgi workers.
Syntax:FcgidWin32PreventOrphans On|Off
Default:FcgidWin32PreventOrphans Off
Context:server config
Status:External
Module:mod_fcgid

Uses Job Control Objects on Windows, only, to enforce shutdown of all fcgi processes created by the httpd worker when the httpd worker has been terminated. Processes terminated in this way do not have the opportunity to clean up gracefully, complete pending disk writes, or similar closure transactions, therefore this behavior is experimental and disabled, by default.

top

FcgidWrapper Directive

Description:The CGI wrapper setting
Syntax:FcgidWrapper command [ suffix ] [ virtual ]
Default:none
Context:server config, virtual host, directory, .htaccess
Override:FileInfo
Status:External
Module:mod_fcgid

The given command is used to spawn FCGI server processes. If this directive is not used, the file pointed to by the request URL will be used instead. Options for the command can be included using quotation marks surrounding the command and options.

The optional suffix argument restricts the use of this FCGI server to all URLs with the given exact path suffix. A suffix needs to start with '.'.

The virtual flag signals that there will be no check whether the request URL actually points to an existing file. The only file which needs to exist is the wrapper itself.

The directive can be used multiple times. A wrapper defined without a suffix is used as a default in case no suffix matches.

top

FcgidZombieScanInterval Directive

Description:scan interval for zombie process
Syntax:FcgidZombieScanInterval seconds
Default:FcgidZombieScanInterval 3
Context:server config
Status:External
Module:mod_fcgid

The module checks for exited FastCGI applications at this interval. During this period of time, the application may exist in the process table as a zombie (on Unix).

Available Languages:  en 

mod_fcgid-2.3.9/docs/manual/mod/mod_fcgid.xml0000644000175000017500000014600211741153110020750 0ustar trawicktrawick mod_fcgid Provides for execution of FastCGI applications External mod_fcgid.c fcgid_module Apache 2.0 and higher

Any program assigned to the handler fcgid-script is processed using the FastCGI protocol; mod_fcgid starts a sufficient number instances of the program to handle concurrent requests, and these programs remain running to handle further incoming requests. This is significantly faster than using the default mod_cgi or mod_cgid modules to launch the program upon each request. However, the programs invoked by mod_fcgid continue to consume resources, so the administrator must weigh the impact of invoking a particular program once per request against the resources required to leave a sufficient number of instances running continuously.

The pool of fcgid-invoked programs is shared between all httpd workers. Configuration directives below let the administrator tune the number of instances of the program that will run concurrently.

Specific executables are assigned this handler either by having a name containing an extension defined by the AddHandler directive, or with an override using the SetHandler directive (e.g., for all files in a specific directory such as cgi-bin).

Some changes have been made in the ASF release of mod_fcgid which can affect existing configurations. All documentation refers to new names for the directives. (The old names still work but are now deprecated.) Please read the Upgrade Notes for details.

For an introduction to using CGI scripts with Apache, see the generic tutorial on Dynamic Content With CGI.

Dynamic Content With CGI mod_cgi mod_cgid
Upgrade Notes

The following changes have been made in the ASF release of mod_fcgid and should be considered when upgrading from the original version by Ryan Pan (Pan Qingfeng).

Examples Note

The examples assume that mod_fcgid and other necessary modules are loaded into the server already, either built-in or via the LoadModule directive.

Additionally, the example configurations provide full access to the applications using access control directives which work with Apache 2.0 and 2.2. These directives are not appropriate for all environments, and they do not work for development levels of Apache HTTP Server (Subversion trunk).

The first example is a very simple Perl FastCGI application, and its configuration directives. This is typical for FastCGI applications which require no special configuration.

Perl FastCGI application - /usr/local/apache/fcgi-bin/foo.pl #!/usr/bin/perl
use CGI::Fast;

while (my $q = CGI::Fast->new) {
print("Content-Type: text/plain\n\n");
foreach $var (sort(keys(%ENV))) {
$val = $ENV{$var};
$val =~ s|\n|\\n|g;
$val =~ s|"|\\"|g;
print "${var}=\"${val}\"\n";
}
}
Configuration directives <Directory /usr/local/apache/fcgi-bin/>
SetHandler fcgid-script
Options +ExecCGI

# Customize the next two directives for your requirements.
Order allow,deny
Allow from all
</Directory>

PHP applications are usually configured using the FcgidWrapper directive and a corresponding wrapper script. The wrapper script can be an appropriate place to define any environment variables required by the application, such as PHP_FCGI_MAX_REQUESTS or anything else. (Environment variables can also be set with FcgidInitialEnv, but they then apply to all applications.)

Here is an example that uses a wrapper script to invoke PHP:

PHP application - /usr/local/phpapp/phpinfo.php <?php
phpinfo();
?>
Configuration directives # FcgidMaxRequestsPerProcess should be <= PHP_FCGI_MAX_REQUESTS
# The example PHP wrapper script overrides the default PHP setting.
FcgidMaxRequestsPerProcess 10000

# Uncomment the following line if cgi.fix_pathinfo is set to 1 in
# php.ini:
# FcgidFixPathinfo 1

Alias /phpapp/ /usr/local/phpapp/
<Location /phpapp/>
AddHandler fcgid-script .php
Options +ExecCGI
FcgidWrapper /usr/local/bin/php-wrapper .php

# Customize the next two directives for your requirements.
Order allow,deny
Allow from all
</Location>
PHP wrapper script - /usr/local/bin/php-wrapper #!/bin/sh
# Set desired PHP_FCGI_* environment variables.
# Example:
# PHP FastCGI processes exit after 500 requests by default.
PHP_FCGI_MAX_REQUESTS=10000
export PHP_FCGI_MAX_REQUESTS

# Replace with the path to your FastCGI-enabled PHP executable
exec /usr/local/bin/php-cgi
Special PHP considerations

By default, PHP FastCGI processes exit after handling 500 requests, and they may exit after this module has already connected to the application and sent the next request. When that occurs, an error will be logged and 500 Internal Server Error will be returned to the client. This PHP behavior can be disabled by setting PHP_FCGI_MAX_REQUESTS to 0, but that can be a problem if the PHP application leaks resources. Alternatively, PHP_FCGI_MAX_REQUESTS can be set to a much higher value than the default to reduce the frequency of this problem. FcgidMaxRequestsPerProcess can be set to a value less than or equal to PHP_FCGI_MAX_REQUESTS to resolve the problem.

PHP child process management (PHP_FCGI_CHILDREN) should always be disabled with mod_fcgid, which will only route one request at a time to application processes it has spawned; thus, any child processes created by PHP will not be used effectively. (Additionally, the PHP child processes may not be terminated properly.) By default, and with the environment variable setting PHP_FCGI_CHILDREN=0, PHP child process management is disabled.

The popular APC opcode cache for PHP cannot share a cache between PHP FastCGI processes unless PHP manages the child processes. Thus, the effectiveness of the cache is limited with mod_fcgid; concurrent PHP requests will use different opcode caches.

Process Management

mod_fcgid has several types of controls which affect the creation of additional application processes:

Type of controlDirective
global limit on number of processes FcgidMaxProcesses
limit on number of processes per application FcgidMaxProcessesPerClass
limit on rate of spawning new application processes FcgidSpawnScoreUpLimit and other score-related directives

mod_fcgid has several types of controls which affect the termination of existing application processes:

Type of controlDirective
termination after an idle period FcgidIdleTimeout
termination after it handles a certain number of requests FcgidMaxRequestsPerProcess
termination after a certain lifetime FcgidProcessLifetime

Several of the directives control processing for a process class. A process class is the set of processes which were started with the same executable file and share certain other characteristics such as virtual host and identity. Two commands which are links to or otherwise refer to the same executable file share the same process class.

Note

Certain settings or other concepts that depend on the virtual host, such as FcgidInitialEnv or process classes, distinguish between virtual hosts only if they have distinct server names. (See the ServerName documentation for more information.) In the case of FcgidInitialEnv, if two virtual hosts have the same server name but different environments as defined by FcgidInitialEnv, the environment used for a particular request will be that defined for the virtual host of the request that caused the FastCGI process to be started.

Information about each process will be displayed in the mod_status server-status page.

FcgidAccessChecker full path to FastCGI access checker FcgidAccessChecker command none directory .htaccess FileInfo

Access checking or, more formally, access control, is a procedure which verifies that the client is allowed to access a resource, using some mechanism other than authentication and authorization.

Key environment variables passed to the application for access checking are:

FCGI_APACHE_ROLE
set to ACCESS_CHECKER; by checking the current role, the same FastCGI application can handle multiple stages of request processing

The application must output a Status line to indicate the result of the check.

Warning

Before 2.3.6, only one FastCGI application of any type (AAA or handler) can be used for a particular request URI. Otherwise, the wrong FastCGI application may be invoked for one or more phases of request processing.

FcgidAccessCheckerAuthoritative Set to 'off' to allow access control to be passed along to lower modules upon failure FcgidAccessCheckerAuthoritative On|Off FcgidAccessCheckerAuthoritative On directory .htaccess FileInfo

This directive controls whether or not other access checkers are allowed to run when this module has an access checker configured and it fails a request. If this directive is On (default) and a FastCGI access checker returns a failure status, a failure is returned to the client without giving other access checkers a chance to allow access. If this directive is Off, other access checkers will be called.

FcgidAuthenticator full path to FastCGI authenticator FcgidAuthenticator command none directory .htaccess FileInfo

Authentication is the procedure which verifies that the user is who they claim they are. This directive specifies the full path to a FastCGI application which will handle authentication for a particular context, such as a directory.

Key environment variables passed to the application on authentication are:

REMOTE_USER
set to the user id of the client
REMOTE_PASSWD
set to the plain text password provided by the client
FCGI_APACHE_ROLE
set to AUTHENTICATOR; by checking the current role, the same FastCGI application can handle multiple stages of request processing

The application must output a Status line to indicate the result of authentication.

Warning

Before 2.3.6, only one FastCGI application of any type (AAA or handler) can be used for a particular request URI. Otherwise, the wrong FastCGI application may be invoked for one or more phases of request processing.

FcgidAuthenticatorAuthoritative Set to 'off' to allow authentication to be passed along to lower modules upon failure FcgidAuthenticatorAuthoritative On|Off FcgidAuthenticatorAuthoritative On directory .htaccess FileInfo

This directive controls whether or not other authenticators are allowed to run when this module has an authenticator configured and it fails a request. If this directive is On (default) and a FastCGI authenticator returns a failure status, a failure is returned to the client without giving other authenticators a chance to validate the client identity. If this directive is Off, other authenticators will be called.

FcgidAuthorizer full path to FastCGI authorizer FcgidAuthorizer command none directory .htaccess FileInfo

Authorization is the procedure which verifies that the user is allowed to access a particular resource. This directive specifies the full path to a FastCGI application which will handle authorization for a particular context, such as a directory.

Key environment variables passed to the application on authorization are:

REMOTE_USER
set to the user id of the client, which has already been authenticated
FCGI_APACHE_ROLE
set to AUTHORIZER; by checking the current role, the same FastCGI application can handle multiple stages of request processing

The application must output a Status line to indicate the result of authorization.

Warning

Before 2.3.6, only one FastCGI application of any type (AAA or handler) can be used for a particular request URI. Otherwise, the wrong FastCGI application may be invoked for one or more phases of request processing.

FcgidAuthorizerAuthoritative Set to 'off' to allow authorization to be passed along to lower modules upon failure FcgidAuthorizerAuthoritative On|Off FcgidAuthorizerAuthoritative On directory .htaccess FileInfo

This directive controls whether or not other authorizers are allowed to run when this module has an authorizer configured and it fails a request. If this directive is On (default) and a FastCGI authorizer returns a failure status, a failure is returned to the client without giving other authorizer a chance to access the resource. If this directive is Off, other authorizers will be called.

FcgidBusyScanInterval scan interval for busy timeout process FcgidBusyScanInterval seconds FcgidBusyScanInterval 120 server config

The module performs the FcgidBusyTimeout check at this interval.

FcgidBusyTimeout a FastCGI application will be killed after handling a request for FcgidBusyTimeout FcgidBusyTimeout seconds FcgidBusyTimeout 300 server config virtual host

This is the maximum time limit for request handling. If a FastCGI request does not complete within FcgidBusyTimeout seconds, it will be subject to termination. Because the check is performed at the interval defined by FcgidBusyScanInterval, request handling may be allowed to proceed for a longer period of time.

The purpose of this directive is to terminate hung applications. The default timeout may need to be increased for applications that can take longer to process the request.

FcgidCmdOptions Set processing options for a FastCGI command FcgidCmdOptions command option [option] ... server config virtual host

This directive allows processing options to be specified for a specific command spawned by mod_fcgid. Each option for the command corresponds to another directive that normally applies to all commands started within a particular context. If a particular option is not specified on this directive, the default will be used.

The following table provides a list of options and corresponding directives:

Option name and syntaxCorresponding directive
ConnectTimeout seconds FcgidConnectTimeout
IdleTimeout seconds FcgidIdleTimeout
InitialEnv name[=value] FcgidInitialEnv
IOTimeout seconds FcgidIOTimeout
MaxProcesses value FcgidMaxProcessesPerClass
MaxProcessLifeTime seconds FcgidProcessLifeTime
MaxRequestsPerProcess value FcgidMaxRequestsPerProcess
MinProcesses value FcgidMinProcessesPerClass

Multiple environment variables are defined by repeating the InitialEnv option.

Example FcgidCmdOptions /usr/local/bin/wrapper \
InitialEnv MAX_REQUESTS=2000 \
MaxRequestsPerProcess 2000 \
IOTimeout 90

When /usr/local/bin/wrapper is spawned, its initial environment contains the MAX_REQUESTS=2000 environment variable setting; additionally, mod_fcgid will terminate it after it has handled 2000 requests, and I/O operations will time out after 90 seconds. Directives corresponding to other options, such as FcgidIdleTimeout or FcgidProcessLifeTime, will be ignored for this command; defaults will be used for options not specified on FcgidCmdOptions.

FcgidInitialEnv an environment variable name and optional value to pass to FastCGI. FcgidInitialEnv name [ value ] none server config virtual host

Use FcgidInitialEnv to define environment variables to pass to the FastCGI application. This directive can be used multiple times.

This setting will apply to all applications spawned for this server or virtual host. Use FcgidCmdOptions to apply this setting to a single application.

FcgidMaxProcessesPerClass Max process count of one class of FastCGI application FcgidMaxProcessesPerClass value FcgidMaxProcessesPerClass 100 server config virtual host

This directive sets the maximum number of processes that can be started for each process class.

This setting will apply to all applications spawned for this server or virtual host. Use FcgidCmdOptions to apply this setting to a single application.

FcgidMinProcessesPerClass Min process count of one class of FastCGI application FcgidMinProcessesPerClass value FcgidMinProcessesPerClass 3 server config virtual host

This directive sets the minimum number of processes that will be retained in a process class after finishing requests.

This setting will apply to all applications spawned for this server or virtual host. Use FcgidCmdOptions to apply this setting to a single application.

FcgidErrorScanInterval scan interval for exited process FcgidErrorScanInterval seconds FcgidErrorScanInterval 3 server config

This is the interval at which the module will handle pending process termination. Termination is pending for any processes which have exceeded FcgidIdleTimeout or FcgidProcessLifeTime.

Unix: mod_fcgid will terminate such processes with SIGTERM; if the process is still active during the next scan, the process will be terminated with SIGKILL. Thus, this directive controls the amount of time for orderly process terminate before being forcibly killed.

FcgidIdleScanInterval scan interval for idle timeout process FcgidIdleScanInterval seconds FcgidIdleScanInterval 120 server config

This is the interval at which the module will search for processes which have exceeded FcgidIdleTimeout or FcgidProcessLifeTime.

FcgidIdleTimeout An idle FastCGI application will be killed after FcgidIdleTimeout FcgidIdleTimeout seconds FcgidIdleTimeout 300 server config virtual host

Application processes which have not handled a request for this period of time will be terminated, if the number of processses for the class exceeds FcgidMinProcessesPerClass. A value of 0 disables the check.

This idle timeout check is performed at the frequency of the configured FcgidIdleScanInterval.

This setting will apply to all applications spawned for this server or virtual host. Use FcgidCmdOptions to apply this setting to a single application.

FcgidIOTimeout Communication timeout to FastCGI server FcgidIOTimeout seconds FcgidIOTimeout 40 server config virtual host

This is the maximum period of time the module will wait while trying to read from or write to a FastCGI application.

Note

The FastCGI application must begin generating the response within this period of time. Increase this directive as necessary to handle applications which take a relatively long period of time to respond.

This setting will apply to all applications spawned for this server or virtual host. Use FcgidCmdOptions to apply this setting to a single application.

FcgidConnectTimeout Connect timeout to FastCGI server FcgidConnectTimeout seconds FcgidConnectTimeout 3 server config virtual host

This is the maximum period of time the module will wait while trying to connect to a FastCGI application on Windows. (This directive is not respected on Unix, where AF_UNIX defaults will apply.)

This setting will apply to all applications spawned for this server or virtual host. Use FcgidCmdOptions to apply this setting to a single application.

FcgidMaxProcesses maximum number of FastCGI application processes FcgidMaxProcesses value FcgidMaxProcesses 1000 server config

This directive sets the maximum number of FastCGI application processes which can be active at one time.

FcgidMaxRequestInMem maximum size of a request which will be held in memory FcgidMaxRequestInMem bytes FcgidMaxRequestInMem 65536 server config virtual host

This module reads the entire request body from the client before sending it to the application. Normally the request body will be stored in memory. Once the amount of request body read from the client exceeds FcgidMaxRequestInMem bytes, the remainder of the request body will be stored in a temporary file.

FcgidMaxRequestLen maximum HTTP request length FcgidMaxRequestLen bytes FcgidMaxRequestLen 131072 server config virtual host

If the size of the request body exceeds this amount, the request will fail with 500 Server Error.

Administrators should change this to an appropriate value for their site based on application requirements.

Warning

Before 2.3.6, this defaulted to 1GB. Most users of earlier versions should use this directive to set a more reasonable limit.

FcgidMaxRequestInMem
FcgidMaxRequestsPerProcess Max requests handled by each FastCGI application FcgidMaxRequestsPerProcess value FcgidMaxRequestsPerProcess 0 server config virtual host

FastCGI application processes will be terminated after handling the specified number of requests. A value of 0 disables the check.

Note

A value of -1 is currently accepted for ease of migration for existing configurations. It is treated the same as 0.

Certain applications, notably PHP as FastCGI, have their own facility for terminating after handling a certain number of requests. This directive can be used to avoid sending additional requests to the application after it has handled its limit.

Note

If this is set such that frequent process creation will be required, you will likely need to adjust FcgidSpawnScoreUpLimit or other score-related directives to allow more frequent process creation.

This setting will apply to all applications spawned for this server or virtual host. Use FcgidCmdOptions to apply this setting to a single application.

FcgidOutputBufferSize CGI output buffer size FcgidOutputBufferSize bytes FcgidOutputBufferSize 65536 server config virtual host

This is the maximum amount of response data the module will read from the FastCGI application before flushing the data to the client.

FcgidPassHeader Header name which will be passed to FastCGI as environment variable. FcgidPassHeader name none server config virtual host

This directive specifies the name of a request header which will be passed to the FastCGI application as an environment variable. The name of the environment variable is derived from the value specified on this directive, as discussed below:

The legacy behavior is to use the value specified on this directive as the environment variable name, converting hyphens to underscores. No case conversion is performed.

Beginning with release 2.3.6, an additional environment variable is created. The value specified on this directive is converted to upper case, prefixed with HTTP_, and hyphens are converted to underscores.

Note

Most request headers are already available to the application as environment variables, and generally are prefixed with HTTP_. (Notable exceptions are Content-type and Content-length, which do not have the HTTP_ prefix.) Thus, this directive is only required for request headers that are purposefully omitted, such as Authorization and Proxy-Authorization. Only pass these request headers if absolutely required.

FcgidFixPathinfo Mirror the PHP cgi.fix_pathinfo setting FcgidFixPathinfo 1 FcgidFixPathinfo 0 server config

This directive enables special SCRIPT_NAME processing which allows PHP to provide additional path information. The setting of FcgidFixPathinfo should mirror the cgi.fix_pathinfo setting in php.ini.

FcgidProcessLifeTime maximum FastCGI application process lifetime FcgidProcessLifeTime seconds FcgidProcessLifeTime 3600 server config virtual host

Idle application processes which have existed for greater than this time will be terminated, if the number of processses for the class exceeds FcgidMinProcessesPerClass. A value of 0 disables the check.

This process lifetime check is performed at the frequency of the configured FcgidIdleScanInterval.

This setting will apply to all applications spawned for this server or virtual host. Use FcgidCmdOptions to apply this setting to a single application.

FcgidProcessTableFile shared memory file path FcgidProcessTableFile pathname FcgidProcessTableFile logs/fcgid_shm server config

This module uses shared memory on Unix to maintain state which is shared between httpd processes. This directive specifies the name of the shared memory file.

FcgidIPCDir directory for AF_UNIX sockets (Unix) or pipes (Windows) FcgidIPCDir pathname FcgidIPCDir logs/fcgidsock server config

This module uses AF_UNIX sockets or named pipes, depending on the platform, to communicate with FastCGI applications. This directive specifies the directory where those sockets or named pipes will be created.

FcgidSpawnScore Each spawn adds this value to the process activity score. FcgidSpawnScore value FcgidSpawnScore 1 server config

Lower values of this directive increase the allowed spawn rate.

Refer to the FcgidSpawnScoreUpLimit directive for more information.

FcgidSpawnScoreUpLimit Maximum value of the process activity score which allows a spawn to occur FcgidSpawnScoreUpLimit value FcgidSpawnScoreUpLimit 10 server config

A process activity score is maintained for each FastCGI application; the score is used to control the rate of spawning in order to avoid placing too much load on the system, particularly for applications that are repeatedly exiting abnormally.

The value of FcgidSpawnScore is added to the score for every spawned application process. The value of FcgidTerminationScore is added to the score for every terminated application process. The value of FcgidTimeScore is subtracted from the score every second.

When the current score is higher than the value of FcgidSpawnScoreUpLimit, no additional application processes will be spawned; subsequent requests must wait until an existing process is free or until the score decreases below the limit.

If the limit is reached under normal load, it may not be sufficient to simply increase the limit, as that would only delay the amount of time before the limit is reached again. Decrease the value of FcgidSpawnScore and/or FcgidTerminationScore, or increase the value of FcgidTimeScore, to allow a higher rate of spawning.

FcgidTerminationScore Each terminated process adds this value to the process activity score. FcgidTerminationScore value FcgidTerminationScore 2 server config

Lower values of this directive increase the allowed spawn rate. Negative values can be useful in some circumstances, such as allowing process replacement without increasing the score.

Refer to the FcgidSpawnScoreUpLimit directive for more information.

FcgidTimeScore Amount subtracted from process activity score each second FcgidTimeScore value FcgidTimeScore 1 server config

Higher values of this directive increase the allowed spawn rate.

Refer to the FcgidSpawnScoreUpLimit directive for more information.

FcgidWin32PreventOrphans Job Control orphan prevention for fcgi workers. FcgidWin32PreventOrphans On|Off FcgidWin32PreventOrphans Off server config

Uses Job Control Objects on Windows, only, to enforce shutdown of all fcgi processes created by the httpd worker when the httpd worker has been terminated. Processes terminated in this way do not have the opportunity to clean up gracefully, complete pending disk writes, or similar closure transactions, therefore this behavior is experimental and disabled, by default.

FcgidWrapper The CGI wrapper setting FcgidWrapper command [ suffix ] [ virtual ] none server config virtual host directory .htaccess FileInfo

The given command is used to spawn FCGI server processes. If this directive is not used, the file pointed to by the request URL will be used instead. Options for the command can be included using quotation marks surrounding the command and options.

The optional suffix argument restricts the use of this FCGI server to all URLs with the given exact path suffix. A suffix needs to start with '.'.

The virtual flag signals that there will be no check whether the request URL actually points to an existing file. The only file which needs to exist is the wrapper itself.

The directive can be used multiple times. A wrapper defined without a suffix is used as a default in case no suffix matches.

FcgidZombieScanInterval scan interval for zombie process FcgidZombieScanInterval seconds FcgidZombieScanInterval 3 server config

The module checks for exited FastCGI applications at this interval. During this period of time, the application may exist in the process table as a zombie (on Unix).

mod_fcgid-2.3.9/docs/manual/mod/mod_fcgid.html0000644000175000017500000000017511250262623021121 0ustar trawicktrawick# GENERATED FROM XML -- DO NOT EDIT URI: mod_fcgid.html.en Content-Language: en Content-type: text/html; charset=ISO-8859-1 mod_fcgid-2.3.9/docs/manual/mod/mod_fcgid.xml.meta0000644000175000017500000000040711740730630021702 0ustar trawicktrawick mod_fcgid /mod/ .. en mod_fcgid-2.3.9/NOTICE-FCGID0000644000175000017500000000026112222063206015042 0ustar trawicktrawickApache HTTP Server mod_fcgid Copyright 2013 The Apache Software Foundation This product includes software developed at The Apache Software Foundation (http://www.apache.org/). mod_fcgid-2.3.9/modules/0000755000175000017500000000000012223627015015002 5ustar trawicktrawickmod_fcgid-2.3.9/modules/fcgid/0000755000175000017500000000000012223627015016056 5ustar trawicktrawickmod_fcgid-2.3.9/modules/fcgid/fcgid_proctbl_unix.c0000644000175000017500000002425611637106173022104 0ustar trawicktrawick/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "fcgid_proctbl.h" #include "apr_version.h" #include "apr_shm.h" #include "apr_global_mutex.h" #include "fcgid_global.h" #include "fcgid_conf.h" #include "fcgid_mutex.h" #include static apr_shm_t *g_sharemem = NULL; static apr_global_mutex_t *g_sharelock = NULL; static const char *g_sharelock_name; static const char *g_sharelock_mutex_type = "fcgid-proctbl"; static fcgid_procnode *g_proc_array = NULL; /* Contain all process slot */ static fcgid_procnode *g_free_list_header = NULL; /* Attach to no process list */ static fcgid_procnode *g_busy_list_header = NULL; /* Attach to a working process list */ static fcgid_procnode *g_idle_list_header = NULL; /* Attach to an idle process list */ static fcgid_procnode *g_error_list_header = NULL; /* Attach to an error process list */ static fcgid_share *_global_memory = NULL; static fcgid_global_share *g_global_share = NULL; /* global information */ static size_t g_table_size = FCGID_PROC_TABLE_SIZE; /* apr version 0.x not support apr_shm_remove, I have to copy it from apr version 1.x */ #if (APR_MAJOR_VERSION < 1) #ifdef HAVE_SYS_MMAN_H #include #endif #ifdef HAVE_SYS_IPC_H #include #endif #ifdef HAVE_SYS_MUTEX_H #include #endif #ifdef HAVE_SYS_SHM_H #include #endif #if !defined(SHM_R) #define SHM_R 0400 #endif #if !defined(SHM_W) #define SHM_W 0200 #endif #ifdef HAVE_SYS_FILE_H #include #endif static apr_status_t apr_shm_remove(const char *filename, apr_pool_t * pool) { #if APR_USE_SHMEM_SHMGET apr_status_t status; apr_file_t *file; key_t shmkey; int shmid; #endif #if APR_USE_SHMEM_MMAP_TMP return apr_file_remove(filename, pool); #endif #if APR_USE_SHMEM_MMAP_SHM if (shm_unlink(filename) == -1) { return errno; } return APR_SUCCESS; #endif #if APR_USE_SHMEM_SHMGET /* Presume that the file already exists; just open for writing */ status = apr_file_open(&file, filename, APR_WRITE, APR_OS_DEFAULT, pool); if (status) { return status; } /* ftok() (on solaris at least) requires that the file actually * exist before calling ftok(). */ shmkey = ftok(filename, 1); if (shmkey == (key_t) - 1) { goto shm_remove_failed; } apr_file_close(file); if ((shmid = shmget(shmkey, 0, SHM_R | SHM_W)) < 0) { goto shm_remove_failed; } /* Indicate that the segment is to be destroyed as soon * as all processes have detached. This also disallows any * new attachments to the segment. */ if (shmctl(shmid, IPC_RMID, NULL) == -1) { goto shm_remove_failed; } return apr_file_remove(filename, pool); shm_remove_failed: status = errno; /* ensure the file has been removed anyway. */ apr_file_remove(filename, pool); return status; #endif /* No support for anonymous shm */ return APR_ENOTIMPL; } #endif /* APR_MAJOR_VERSION<1 */ apr_status_t proctable_pre_config(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp) { return fcgid_mutex_register(g_sharelock_mutex_type, p); } apr_status_t proctable_post_config(server_rec * main_server, apr_pool_t * configpool) { size_t shmem_size = sizeof(fcgid_share); fcgid_procnode *ptmpnode = NULL; int i; apr_status_t rv; fcgid_server_conf *sconf = ap_get_module_config(main_server->module_config, &fcgid_module); /* Remove share memory first */ apr_shm_remove(sconf->shmname_path, main_server->process->pconf); /* Create share memory */ if ((rv = apr_shm_create(&g_sharemem, shmem_size, sconf->shmname_path, main_server->process->pconf)) != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_EMERG, rv, main_server, "mod_fcgid: Can't create shared memory for size %" APR_SIZE_T_FMT " bytes", shmem_size); exit(1); } _global_memory = apr_shm_baseaddr_get(g_sharemem); /* Create global mutex */ rv = fcgid_mutex_create(&g_sharelock, &g_sharelock_name, g_sharelock_mutex_type, main_server->process->pconf, main_server); if (rv != APR_SUCCESS) { exit(1); } memset(_global_memory, 0, shmem_size); g_proc_array = _global_memory->procnode_array; g_global_share = &_global_memory->global; g_global_share->must_exit = 0; /* Init the array */ g_idle_list_header = g_proc_array; g_busy_list_header = g_idle_list_header + 1; g_error_list_header = g_busy_list_header + 1; g_free_list_header = g_error_list_header + 1; ptmpnode = g_free_list_header; for (i = 0; i < FCGID_MAX_APPLICATION; i++) { ptmpnode->next_index = ptmpnode - g_proc_array + 1; ptmpnode++; } return APR_SUCCESS; } apr_status_t proctable_child_init(server_rec * main_server, apr_pool_t * configpool) { apr_status_t rv; if ((rv = apr_global_mutex_child_init(&g_sharelock, g_sharelock_name, main_server->process->pconf)) != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_EMERG, rv, main_server, "mod_fcgid: apr_global_mutex_child_init error"); exit(1); } return rv; } static apr_status_t proctable_lock_internal(void) { return apr_global_mutex_lock(g_sharelock); } static apr_status_t proctable_unlock_internal(void) { return apr_global_mutex_unlock(g_sharelock); } fcgid_procnode *proctable_get_free_list(void) { return g_free_list_header; } fcgid_procnode *proctable_get_busy_list(void) { return g_busy_list_header; } fcgid_procnode *proctable_get_idle_list(void) { return g_idle_list_header; } fcgid_procnode *proctable_get_table_array(void) { return g_proc_array; } fcgid_procnode *proctable_get_error_list(void) { return g_error_list_header; } fcgid_global_share *proctable_get_globalshare(void) { return g_global_share; } size_t proctable_get_table_size(void) { return g_table_size; } void proctable_lock(request_rec *r) { /* Lock error is a fatal error */ apr_status_t rv; if ((rv = proctable_lock_internal()) != APR_SUCCESS) { ap_log_rerror(APLOG_MARK, APLOG_EMERG, rv, r, "mod_fcgid: can't lock process table in pid %" APR_PID_T_FMT, getpid()); exit(1); } } void proctable_unlock(request_rec *r) { /* Lock error is a fatal error */ apr_status_t rv; if ((rv = proctable_unlock_internal()) != APR_SUCCESS) { ap_log_rerror(APLOG_MARK, APLOG_EMERG, rv, r, "mod_fcgid: can't unlock process table in pid %" APR_PID_T_FMT, getpid()); exit(1); } } void proctable_pm_lock(server_rec *s) { apr_status_t rv; if (g_global_share->must_exit) { ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, "mod_fcgid: server is restarted, pid %" APR_PID_T_FMT " must exit", getpid()); kill(getpid(), SIGTERM); } /* Lock error is a fatal error */ if ((rv = proctable_lock_internal()) != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s, "mod_fcgid: can't lock process table in PM, pid %" APR_PID_T_FMT, getpid()); exit(1); } } void proctable_pm_unlock(server_rec *s) { /* Lock error is a fatal error */ apr_status_t rv; if ((rv = proctable_unlock_internal()) != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s, "mod_fcgid: can't unlock process table in PM, pid %" APR_PID_T_FMT, getpid()); exit(1); } } void proctable_print_debug_info(server_rec * main_server) { int freecount = 0; fcgid_procnode *current_node; for (current_node = &g_proc_array[g_free_list_header->next_index]; current_node != g_proc_array; current_node = &g_proc_array[current_node->next_index]) freecount++; ap_log_error(APLOG_MARK, APLOG_WARNING, 0, main_server, "mod_fcgid: total node count: %d, free node count: %d", FCGID_MAX_APPLICATION, freecount); for (current_node = &g_proc_array[g_idle_list_header->next_index]; current_node != g_proc_array; current_node = &g_proc_array[current_node->next_index]) { ap_log_error(APLOG_MARK, APLOG_WARNING, 0, main_server, "mod_fcgid: idle node index: %ld", (long)(current_node - g_proc_array)); } for (current_node = &g_proc_array[g_busy_list_header->next_index]; current_node != g_proc_array; current_node = &g_proc_array[current_node->next_index]) { ap_log_error(APLOG_MARK, APLOG_WARNING, 0, main_server, "mod_fcgid: busy node index: %ld", (long)(current_node - g_proc_array)); } for (current_node = &g_proc_array[g_error_list_header->next_index]; current_node != g_proc_array; current_node = &g_proc_array[current_node->next_index]) { ap_log_error(APLOG_MARK, APLOG_WARNING, 0, main_server, "mod_fcgid: error node index: %ld", (long)(current_node - g_proc_array)); } } mod_fcgid-2.3.9/modules/fcgid/fcgid_global.h0000644000175000017500000000336111513147317020631 0ustar trawicktrawick/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef FCGID_GLOBAL_H #define FCGID_GLOBAL_H #include "httpd.h" #include "http_config.h" #include "http_core.h" #include "http_log.h" #if AP_MODULE_MAGIC_AT_LEAST(20100606,0) APLOG_USE_MODULE(fcgid); #endif #ifdef FCGID_APXS_BUILD #include "fcgid_config.h" #endif /* FCGID_PATH_MAX * - includes terminating '\0' * - based on minimum supported path length (on Unix at least) * - should be used in declarations, but logic should use sizeof * wherever possible */ #ifndef FCGID_PATH_MAX #ifdef _POSIX_PATH_MAX #define FCGID_PATH_MAX _POSIX_PATH_MAX #else #define FCGID_PATH_MAX 256 #endif #endif /* FCGID_CMDLINE_MAX * - includes terminating '\0' * - FCGID_PATH_MAX represents the executable, remainder represents * the args * - should be used in declarations, but logic should use sizeof * wherever possible */ #ifndef FCGID_CMDLINE_MAX #define FCGID_CMDLINE_MAX (FCGID_PATH_MAX + 256) #endif #define fcgid_min(a,b) (((a) < (b)) ? (a) : (b)) #endif mod_fcgid-2.3.9/modules/fcgid/fcgid_conf.h0000644000175000017500000002254612223626116020322 0ustar trawicktrawick/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef FCGID_CONF_H #define FCGID_CONF_H #define MODFCGID_COPYRIGHT \ "Copyright 2013 The Apache Software Foundation." #define MODFCGID_VERSION_MAJOR 2 #define MODFCGID_VERSION_MINOR 3 #define MODFCGID_VERSION_SUBVER 9 #define MODFCGID_VERSION_DEV 0 #if MODFCGID_VERSION_DEV #define MODFCGID_VERSION_DEVSTR "-dev" #else #define MODFCGID_VERSION_DEVSTR "" #endif /* APR_STRINGIFY is defined here, and also in apr_general.h, so wrap it */ #ifndef APR_STRINGIFY /** Properly quote a value as a string in the C preprocessor */ #define APR_STRINGIFY(n) APR_STRINGIFY_HELPER(n) /** Helper macro for APR_STRINGIFY */ #define APR_STRINGIFY_HELPER(n) #n #endif #define MODFCGID_REVISION APR_STRINGIFY(MODFCGID_VERSION_MAJOR) \ "." APR_STRINGIFY(MODFCGID_VERSION_MINOR) \ "." APR_STRINGIFY(MODFCGID_VERSION_SUBVER) #define MODFCGID_VERSION MODFCGID_REVISION MODFCGID_VERSION_DEVSTR #define MODFCGID_PRODUCT "mod_fcgid/" MODFCGID_VERSION #ifndef VERSION_ONLY #include "apr_user.h" #include "fcgid_global.h" typedef struct { const char *cgipath; /* executable file path */ const char *cmdline; /* entire command line */ apr_ino_t inode; apr_dev_t deviceid; int virtual; } fcgid_cmd_conf; typedef struct { /* not based on config */ int vhost_id; /* global only */ apr_hash_t *cmdopts_hash; int busy_scan_interval; int error_scan_interval; int idle_scan_interval; int max_process_count; int php_fix_pathinfo_enable; char *shmname_path; char *sockname_prefix; int spawn_score; int spawnscore_uplimit; int termination_score; int time_score; int zombie_scan_interval; #ifdef WIN32 /* FcgidWin32PreventOrphans - Win32 CGI processes automatic cleanup */ HANDLE hJobObjectForAutoCleanup; #endif /* global or vhost * scalar values have corresponding _set field to aid merging */ apr_table_t *default_init_env; int ipc_comm_timeout; int ipc_comm_timeout_set; int ipc_connect_timeout; int ipc_connect_timeout_set; int max_mem_request_len; int max_mem_request_len_set; apr_off_t max_request_len; int max_request_len_set; int max_requests_per_process; int max_requests_per_process_set; int output_buffersize; int output_buffersize_set; apr_array_header_t *pass_headers; int max_class_process_count; int max_class_process_count_set; int min_class_process_count; int min_class_process_count_set; int busy_timeout; int busy_timeout_set; int idle_timeout; int idle_timeout_set; int proc_lifetime; int proc_lifetime_set; } fcgid_server_conf; typedef struct { /* scalar values have corresponding _set field to aid merging */ /* wrapper */ apr_hash_t *wrapper_info_hash; /* authenticator */ fcgid_cmd_conf *authenticator_info; int authenticator_authoritative; int authenticator_authoritative_set; /* authorizer */ fcgid_cmd_conf *authorizer_info; int authorizer_authoritative; int authorizer_authoritative_set; /* access check */ fcgid_cmd_conf *access_info; int access_authoritative; int access_authoritative_set; } fcgid_dir_conf; /* processing options which are sent to the PM with a spawn request * and/or configurable via FCGIDCmdOptions; envvars are kept in a * separate structure to keep them out of the process table in order * to limit shared memory use */ #define INITENV_KEY_LEN 64 #define INITENV_VAL_LEN 128 #define INITENV_CNT 64 typedef struct { char initenv_key[INITENV_CNT][INITENV_KEY_LEN]; char initenv_val[INITENV_CNT][INITENV_VAL_LEN]; } fcgid_cmd_env; typedef struct { int busy_timeout; int idle_timeout; int ipc_comm_timeout; int ipc_connect_timeout; int max_class_process_count; int max_requests_per_process; int min_class_process_count; int proc_lifetime; fcgid_cmd_env *cmdenv; } fcgid_cmd_options; void *create_fcgid_server_config(apr_pool_t * p, server_rec * s); void *merge_fcgid_server_config(apr_pool_t * p, void *basev, void *overridesv); void *create_fcgid_dir_config(apr_pool_t * p, char *dummy); void *merge_fcgid_dir_config(apr_pool_t * p, void *basev, void *overridesv); const char *set_idle_timeout(cmd_parms * cmd, void *dummy, const char *arg); const char *set_idle_scan_interval(cmd_parms * cmd, void *dummy, const char *arg); const char *set_busy_timeout(cmd_parms * cmd, void *dummy, const char *arg); const char *set_busy_scan_interval(cmd_parms * cmd, void *dummy, const char *arg); const char *set_proc_lifetime(cmd_parms * cmd, void *dummy, const char *arg); const char *set_error_scan_interval(cmd_parms * cmd, void *dummy, const char *arg); const char *set_zombie_scan_interval(cmd_parms * cmd, void *dummy, const char *arg); const char *set_socketpath(cmd_parms * cmd, void *dummy, const char *arg); const char *set_shmpath(cmd_parms * cmd, void *dummy, const char *arg); const char *set_time_score(cmd_parms * cmd, void *dummy, const char *arg); const char *set_max_request_len(cmd_parms * cmd, void *dummy, const char *arg); const char *set_max_mem_request_len(cmd_parms * cmd, void *dummy, const char *arg); const char *set_termination_score(cmd_parms * cmd, void *dummy, const char *arg); const char *set_spawn_score(cmd_parms * cmd, void *dummy, const char *arg); const char *set_spawnscore_uplimit(cmd_parms * cmd, void *dummy, const char *arg); const char *set_max_process(cmd_parms * cmd, void *dummy, const char *arg); const char *set_max_class_process(cmd_parms * cmd, void *dummy, const char *arg); const char *set_min_class_process(cmd_parms * cmd, void *dummy, const char *arg); const char *set_ipc_connect_timeout(cmd_parms * cmd, void *dummy, const char *arg); const char *set_ipc_comm_timeout(cmd_parms * cmd, void *dummy, const char *arg); const char *set_output_buffersize(cmd_parms * cmd, void *dummy, const char *arg); const char *add_default_env_vars(cmd_parms * cmd, void *sconf, const char *name, const char *value); const char *add_pass_headers(cmd_parms * cmd, void *sconf, const char *name); apr_array_header_t *get_pass_headers(request_rec * r); const char *set_wrapper_config(cmd_parms * cmd, void *dummy, const char *wrapper, const char *extension, const char* virtual); fcgid_cmd_conf *get_wrapper_info(const char *cgipath, request_rec * r); const char *set_authenticator_info(cmd_parms * cmd, void *config, const char *arg); const char *set_authenticator_authoritative(cmd_parms * cmd, void *config, int arg); fcgid_cmd_conf *get_authenticator_info(request_rec * r, int *authoritative); const char *set_authorizer_info(cmd_parms * cmd, void *config, const char *arg); const char *set_authorizer_authoritative(cmd_parms * cmd, void *config, int arg); fcgid_cmd_conf *get_authorizer_info(request_rec * r, int *authoritative); const char *set_access_info(cmd_parms * cmd, void *config, const char *arg); const char *set_access_authoritative(cmd_parms * cmd, void *config, int arg); fcgid_cmd_conf *get_access_info(request_rec * r, int *authoritative); const char *set_php_fix_pathinfo_enable(cmd_parms * cmd, void *dummy, const char *arg); const char *set_max_requests_per_process(cmd_parms * cmd, void *dummy, const char *arg); #ifdef WIN32 const char *set_win32_prevent_process_orphans(cmd_parms *cmd, void *dummy, int arg); #endif const char *set_cmd_options(cmd_parms *cmd, void *dummy, const char *arg); void get_cmd_options(request_rec *r, const char *cmdpath, fcgid_cmd_options *cmdopts, fcgid_cmd_env *cmdenv); AP_MODULE_DECLARE_DATA extern module fcgid_module; #endif #endif mod_fcgid-2.3.9/modules/fcgid/fcgid_protocol.h0000644000175000017500000000624311254514265021236 0ustar trawicktrawick/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef FCGID_PROTOCOL_H #define FCGID_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; /* * 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 /* * 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; /* * 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 /* * 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" int init_header(int type, int requestId, apr_size_t contentLength, apr_size_t paddingLength, FCGI_Header * header); int build_begin_block(int role, request_rec * r, apr_bucket_alloc_t * alloc, apr_bucket_brigade * request_brigade); int build_env_block(request_rec * r, char **envp, apr_bucket_alloc_t * alloc, apr_bucket_brigade * request_brigade); #endif mod_fcgid-2.3.9/modules/fcgid/fcgid_proc_win.c0000644000175000017500000004371512051503352021204 0ustar trawicktrawick/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "httpd.h" #include "apr_thread_proc.h" #include "apr_strings.h" #include "apr_portable.h" #include "apr_pools.h" #include "util_script.h" #include "mod_core.h" #include "mod_cgi.h" #include "apr_tables.h" #include "fcgid_proc.h" #include "fcgid_proctbl.h" #include "fcgid_protocol.h" #include "fcgid_conf.h" #include "fcgid_pm.h" #include "fcgid_spawn_ctl.h" #define SHUTDOWN_EVENT_NAME "_FCGI_SHUTDOWN_EVENT_" #define FINISH_EVENT_DATA_NAME "finish_event" #ifndef SIGKILL #define SIGKILL 9 #endif /* It's tested on WinNT ONLY, if it work on the other MS platform, let me know */ #if WINVER < 0x0400 #error It is tested on WinNT only #endif typedef struct { HANDLE handle_pipe; OVERLAPPED overlap_read; OVERLAPPED overlap_write; } fcgid_namedpipe_handle; static int g_process_counter = 0; static apr_status_t close_finish_event(void *finishevent) { HANDLE *finish_event = finishevent; CloseHandle(*finish_event); return APR_SUCCESS; } apr_status_t proc_spawn_process(const char *cmdline, fcgid_proc_info *procinfo, fcgid_procnode *procnode) { HANDLE *finish_event, listen_handle; SECURITY_ATTRIBUTES SecurityAttributes; fcgid_server_conf *sconf; apr_procattr_t *proc_attr; apr_status_t rv; apr_file_t *file; const char * const *proc_environ; char sock_path[FCGID_PATH_MAX]; const char **wargv; /* Build wrapper args */ apr_tokenize_to_argv(cmdline, (char ***)&wargv, procnode->proc_pool); memset(&SecurityAttributes, 0, sizeof(SecurityAttributes)); /* Prepare finish event */ finish_event = apr_palloc(procnode->proc_pool, sizeof(HANDLE)); *finish_event = CreateEvent(NULL, TRUE, FALSE, NULL); if (*finish_event == NULL || !SetHandleInformation(*finish_event, HANDLE_FLAG_INHERIT, TRUE)) { ap_log_error(APLOG_MARK, APLOG_WARNING, apr_get_os_error(), procinfo->main_server, "mod_fcgid: can't create mutex for subprocess"); return APR_ENOLOCK; } apr_pool_cleanup_register(procnode->proc_pool, finish_event, close_finish_event, apr_pool_cleanup_null); /* For proc_kill_gracefully() */ apr_pool_userdata_set(finish_event, FINISH_EVENT_DATA_NAME, NULL, procnode->proc_pool); /* Pass the finish event id to subprocess */ apr_table_setn(procinfo->proc_environ, SHUTDOWN_EVENT_NAME, apr_ltoa(procnode->proc_pool, (long) *finish_event)); /* Prepare the listen namedpipe file name (no check for truncation) */ apr_snprintf(sock_path, sizeof sock_path, "\\\\.\\pipe\\fcgidpipe-%lu.%d", GetCurrentProcessId(), g_process_counter++); /* Prepare the listen namedpipe handle */ SecurityAttributes.bInheritHandle = TRUE; SecurityAttributes.nLength = sizeof(SecurityAttributes); SecurityAttributes.lpSecurityDescriptor = NULL; listen_handle = CreateNamedPipe(sock_path, PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, PIPE_UNLIMITED_INSTANCES, 8192, 8192, 0, &SecurityAttributes); if (listen_handle == INVALID_HANDLE_VALUE) { ap_log_error(APLOG_MARK, APLOG_WARNING, apr_get_os_error(), procinfo->main_server, "mod_fcgid: can't create namedpipe for subprocess"); return APR_ENOSOCKET; } apr_cpystrn(procnode->socket_path, sock_path, sizeof(procnode->socket_path)); apr_cpystrn(procnode->executable_path, wargv[0], sizeof(procnode->executable_path)); /* Build environment variables */ proc_environ = (const char * const *) ap_create_environment(procnode->proc_pool, procinfo->proc_environ); if (!proc_environ) { ap_log_error(APLOG_MARK, APLOG_WARNING, apr_get_os_error(), procinfo->main_server, "mod_fcgid: can't build environment variables"); return APR_ENOMEM; } /* Create process now */ if ((rv = apr_procattr_create(&proc_attr, procnode->proc_pool)) != APR_SUCCESS || (rv = apr_procattr_dir_set(proc_attr, ap_make_dirstr_parent(procnode->proc_pool, wargv[0]))) != APR_SUCCESS || (rv = apr_procattr_cmdtype_set(proc_attr, APR_PROGRAM)) != APR_SUCCESS || (rv = apr_procattr_detach_set(proc_attr, 1)) != APR_SUCCESS || (rv = apr_procattr_io_set(proc_attr, APR_NO_PIPE, APR_NO_FILE, APR_NO_FILE)) != APR_SUCCESS || (rv = apr_os_file_put(&file, &listen_handle, 0, procnode->proc_pool)) != APR_SUCCESS || (rv = apr_procattr_child_in_set(proc_attr, file, NULL)) != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_WARNING, rv, procinfo->main_server, "mod_fcgid: can't create FastCGI process attribute"); CloseHandle(listen_handle); return APR_ENOPROC; } /* fork and exec now */ rv = apr_proc_create(&(procnode->proc_id), wargv[0], wargv, proc_environ, proc_attr, procnode->proc_pool); /* OK, I created the process, now put it back to idle list */ CloseHandle(listen_handle); if (rv != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_ERR, rv, procinfo->main_server, "mod_fcgid: can't run %s", wargv[0]); return rv; } /* FcgidWin32PreventOrphans feature */ sconf = ap_get_module_config(procinfo->main_server->module_config, &fcgid_module); if (sconf->hJobObjectForAutoCleanup != NULL) { /* Associate cgi process to current process */ if (AssignProcessToJobObject(sconf->hJobObjectForAutoCleanup, procnode->proc_id.hproc) == 0) { ap_log_error(APLOG_MARK, APLOG_WARNING, apr_get_os_error(), procinfo->main_server, "mod_fcgid: unable to assign child process to " "job object"); } } return APR_SUCCESS; } apr_status_t proc_kill_gracefully(fcgid_procnode *procnode, server_rec *main_server) { HANDLE *finish_event = NULL; apr_pool_userdata_get((void **) &finish_event, FINISH_EVENT_DATA_NAME, procnode->proc_pool); if (finish_event != NULL) SetEvent(*finish_event); return APR_SUCCESS; } apr_status_t proc_kill_force(fcgid_procnode *procnode, server_rec *main_server) { return apr_proc_kill(&(procnode->proc_id), SIGKILL); } apr_status_t proc_wait_process(server_rec *main_server, fcgid_procnode *procnode) { apr_status_t rv; int exitcode; apr_exit_why_e exitwhy; if ((rv = apr_proc_wait(&(procnode->proc_id), &exitcode, &exitwhy, APR_NOWAIT)) == APR_CHILD_DONE) { /* Log why and how it die */ proc_print_exit_info(procnode, exitcode, exitwhy, main_server); /* Register the death */ register_termination(main_server, procnode); /* Destroy pool */ apr_pool_destroy(procnode->proc_pool); procnode->proc_pool = NULL; } return rv; } static apr_status_t ipc_handle_cleanup(void *thehandle) { fcgid_namedpipe_handle *handle = thehandle; /* Sanity check */ if (handle) { if (handle->handle_pipe != INVALID_HANDLE_VALUE) CloseHandle(handle->handle_pipe); if (handle->overlap_read.hEvent != NULL) CloseHandle(handle->overlap_read.hEvent); if (handle->overlap_write.hEvent != NULL) CloseHandle(handle->overlap_write.hEvent); handle->handle_pipe = INVALID_HANDLE_VALUE; handle->overlap_read.hEvent = NULL; handle->overlap_write.hEvent = NULL; } return APR_SUCCESS; } apr_status_t proc_connect_ipc(fcgid_procnode *procnode, fcgid_ipc *ipc_handle) { /* Prepare the ipc struct */ fcgid_namedpipe_handle *handle_info; ipc_handle->ipc_handle_info = (fcgid_namedpipe_handle *) apr_pcalloc(ipc_handle->request->pool, sizeof (fcgid_namedpipe_handle)); handle_info = (fcgid_namedpipe_handle *) ipc_handle->ipc_handle_info; /* Prepare OVERLAPPED struct for non-block I/O */ handle_info->overlap_read.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); handle_info->overlap_write.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); handle_info->handle_pipe = INVALID_HANDLE_VALUE; apr_pool_cleanup_register(ipc_handle->request->pool, handle_info, ipc_handle_cleanup, apr_pool_cleanup_null); if (handle_info->overlap_read.hEvent == NULL || handle_info->overlap_write.hEvent == NULL) return APR_ENOMEM; /* Connect to name pipe */ handle_info->handle_pipe = CreateFile(procnode->socket_path, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); if (handle_info->handle_pipe == INVALID_HANDLE_VALUE && ipc_handle->connect_timeout != 0 && GetLastError() == ERROR_PIPE_BUSY) { /* XXX - there appears to be a race, here * Wait for pipe to be ready for connect, and try again */ if (WaitNamedPipe(procnode->socket_path, ipc_handle->connect_timeout)) { handle_info->handle_pipe = CreateFile(procnode->socket_path, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); } } if (handle_info->handle_pipe == INVALID_HANDLE_VALUE) { if (GetLastError() == ERROR_FILE_NOT_FOUND) /* The process has exited */ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, ipc_handle->request, "mod_fcgid: can't connect to named pipe, FastCGI" " server %" APR_PID_T_FMT " has been terminated", procnode->proc_id.pid); else ap_log_rerror(APLOG_MARK, APLOG_DEBUG, apr_get_os_error(), ipc_handle->request, "mod_fcgid: can't connect to named pipe, FastCGI" " server pid %" APR_PID_T_FMT, procnode->proc_id.pid); return APR_ESPIPE; } /* Now named pipe connected */ return APR_SUCCESS; } apr_status_t proc_close_ipc(fcgid_ipc * ipc_handle) { apr_status_t rv; rv = apr_pool_cleanup_run(ipc_handle->request->pool, ipc_handle->ipc_handle_info, ipc_handle_cleanup); ipc_handle->ipc_handle_info = NULL; return rv; } apr_status_t proc_read_ipc(fcgid_ipc * ipc_handle, const char *buffer, apr_size_t * size) { apr_status_t rv; fcgid_namedpipe_handle *handle_info; DWORD bytesread; handle_info = (fcgid_namedpipe_handle *) ipc_handle->ipc_handle_info; if (ReadFile(handle_info->handle_pipe, (LPVOID) buffer, *size, &bytesread, &handle_info->overlap_read)) { *size = bytesread; return APR_SUCCESS; } else if ((rv = GetLastError()) != ERROR_IO_PENDING) { ap_log_rerror(APLOG_MARK, APLOG_WARNING, APR_FROM_OS_ERROR(rv), ipc_handle->request, "mod_fcgid: can't read from pipe"); return rv; } else { /* it's ERROR_IO_PENDING */ DWORD transferred; DWORD dwWaitResult = WaitForSingleObject(handle_info->overlap_read.hEvent, ipc_handle->communation_timeout * 1000); if (dwWaitResult == WAIT_OBJECT_0) { if (!GetOverlappedResult(handle_info->handle_pipe, &handle_info->overlap_read, &transferred, FALSE /* don't wait */ ) || transferred == 0) { rv = apr_get_os_error(); ap_log_rerror(APLOG_MARK, APLOG_WARNING, rv, ipc_handle->request, "mod_fcgid: get overlap result error"); return rv; } *size = transferred; return APR_SUCCESS; } else { ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, ipc_handle->request, "mod_fcgid: read timeout from pipe"); return APR_ETIMEDOUT; } } } apr_status_t proc_write_ipc(fcgid_ipc * ipc_handle, apr_bucket_brigade * birgade_send) { fcgid_namedpipe_handle *handle_info; apr_bucket *bucket_request; apr_status_t rv; DWORD transferred; handle_info = (fcgid_namedpipe_handle *) ipc_handle->ipc_handle_info; for (bucket_request = APR_BRIGADE_FIRST(birgade_send); bucket_request != APR_BRIGADE_SENTINEL(birgade_send); bucket_request = APR_BUCKET_NEXT(bucket_request)) { const char *write_buf; apr_size_t write_buf_len; apr_size_t has_write; if (APR_BUCKET_IS_METADATA(bucket_request)) continue; if ((rv = apr_bucket_read(bucket_request, &write_buf, &write_buf_len, APR_BLOCK_READ)) != APR_SUCCESS) { ap_log_rerror(APLOG_MARK, APLOG_WARNING, rv, ipc_handle->request, "mod_fcgid: can't read request from bucket"); return rv; } /* Write the buffer to fastcgi server */ has_write = 0; while (has_write < write_buf_len) { DWORD byteswrite; if (WriteFile(handle_info->handle_pipe, write_buf + has_write, write_buf_len - has_write, &byteswrite, &handle_info->overlap_write)) { has_write += byteswrite; continue; } else if ((rv = GetLastError()) != ERROR_IO_PENDING) { ap_log_rerror(APLOG_MARK, APLOG_WARNING, APR_FROM_OS_ERROR(rv), ipc_handle->request, "mod_fcgid: can't write to pipe"); return rv; } else { /* it's ERROR_IO_PENDING on write */ DWORD dwWaitResult = WaitForSingleObject(handle_info->overlap_write.hEvent, ipc_handle->communation_timeout * 1000); if (dwWaitResult == WAIT_OBJECT_0) { if (!GetOverlappedResult(handle_info->handle_pipe, &handle_info->overlap_write, &transferred, FALSE /* don't wait */ ) || transferred == 0) { ap_log_rerror(APLOG_MARK, APLOG_WARNING, apr_get_os_error(), ipc_handle->request, "mod_fcgid: get overlap result error"); return APR_ESPIPE; } has_write += transferred; continue; } else { ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, ipc_handle->request, "mod_fcgid: write timeout to pipe"); return APR_ESPIPE; } } } } return APR_SUCCESS; } void proc_print_exit_info(fcgid_procnode * procnode, int exitcode, apr_exit_why_e exitwhy, server_rec * main_server) { char *diewhy = NULL; /* Reasons to exit */ switch (procnode->diewhy) { case FCGID_DIE_KILLSELF: if (exitwhy == APR_PROC_EXIT) diewhy = "normal exit"; else diewhy = "access violation"; break; case FCGID_DIE_IDLE_TIMEOUT: diewhy = "idle timeout"; break; case FCGID_DIE_LIFETIME_EXPIRED: diewhy = "lifetime expired"; break; case FCGID_DIE_BUSY_TIMEOUT: diewhy = "busy timeout"; break; case FCGID_DIE_CONNECT_ERROR: diewhy = "connect error, server may has exited"; break; case FCGID_DIE_COMM_ERROR: diewhy = "communication error"; break; case FCGID_DIE_SHUTDOWN: diewhy = "shutting down"; break; default: diewhy = "unknown"; } /* Print log now */ ap_log_error(APLOG_MARK, APLOG_INFO, 0, main_server, "mod_fcgid: process %s(%" APR_PID_T_FMT ") exit(%s), return code %d", procnode->executable_path, procnode->proc_id.pid, diewhy, exitcode); } mod_fcgid-2.3.9/modules/fcgid/mod_fcgid.dep0000644000175000017500000000401211263306074020462 0ustar trawicktrawick# Microsoft Developer Studio Generated Dependency File, included by mod_fcgid.mak .\fcgid_bridge.c : \ ".\fcgid_bridge.h"\ ".\fcgid_bucket.h"\ ".\fcgid_conf.h"\ ".\fcgid_config.h"\ ".\fcgid_global.h"\ ".\fcgid_pm.h"\ ".\fcgid_proc.h"\ ".\fcgid_proctbl.h"\ ".\fcgid_protocol.h"\ ".\fcgid_spawn_ctl.h"\ .\fcgid_bucket.c : \ ".\fcgid_bridge.h"\ ".\fcgid_bucket.h"\ ".\fcgid_conf.h"\ ".\fcgid_config.h"\ ".\fcgid_global.h"\ ".\fcgid_proc.h"\ ".\fcgid_proctbl.h"\ ".\fcgid_protocol.h"\ .\fcgid_conf.c : \ ".\fcgid_conf.h"\ ".\fcgid_config.h"\ ".\fcgid_global.h"\ !IF "$(CFG)" == "mod_fcgid - Win32 Release" !ELSEIF "$(CFG)" == "mod_fcgid - Win32 Debug" !ENDIF .\fcgid_filter.c : \ ".\fcgid_bucket.h"\ ".\fcgid_conf.h"\ ".\fcgid_config.h"\ ".\fcgid_filter.h"\ ".\fcgid_global.h"\ ".\fcgid_proc.h"\ ".\fcgid_proctbl.h"\ .\fcgid_pm_main.c : \ ".\fcgid_conf.h"\ ".\fcgid_config.h"\ ".\fcgid_global.h"\ ".\fcgid_pm.h"\ ".\fcgid_pm_main.h"\ ".\fcgid_proc.h"\ ".\fcgid_proctbl.h"\ ".\fcgid_spawn_ctl.h"\ .\fcgid_pm_win.c : \ ".\fcgid_conf.h"\ ".\fcgid_config.h"\ ".\fcgid_global.h"\ ".\fcgid_pm.h"\ ".\fcgid_pm_main.h"\ ".\fcgid_proctbl.h"\ ".\fcgid_spawn_ctl.h"\ .\fcgid_proc_win.c : \ ".\fcgid_conf.h"\ ".\fcgid_config.h"\ ".\fcgid_global.h"\ ".\fcgid_pm.h"\ ".\fcgid_proc.h"\ ".\fcgid_proctbl.h"\ ".\fcgid_protocol.h"\ ".\fcgid_spawn_ctl.h"\ .\fcgid_proctbl_win.c : \ ".\fcgid_conf.h"\ ".\fcgid_config.h"\ ".\fcgid_global.h"\ ".\fcgid_proctbl.h"\ .\fcgid_protocol.c : \ ".\fcgid_config.h"\ ".\fcgid_global.h"\ ".\fcgid_protocol.h"\ .\fcgid_spawn_ctl.c : \ ".\fcgid_conf.h"\ ".\fcgid_config.h"\ ".\fcgid_global.h"\ ".\fcgid_pm.h"\ ".\fcgid_proctbl.h"\ ".\fcgid_spawn_ctl.h"\ .\mod_fcgid.c : \ ".\fcgid_bridge.h"\ ".\fcgid_conf.h"\ ".\fcgid_config.h"\ ".\fcgid_filter.h"\ ".\fcgid_global.h"\ ".\fcgid_pm.h"\ ".\fcgid_proctbl.h"\ ".\fcgid_protocol.h"\ ".\fcgid_spawn_ctl.h"\ .\mod_fcgid.rc : \ ".\fcgid_conf.h"\ ".\fcgid_config.h"\ ".\fcgid_global.h"\ mod_fcgid-2.3.9/modules/fcgid/fcgid_pm_win.c0000644000175000017500000002317512223417637020666 0ustar trawicktrawick/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "apr_thread_proc.h" #include "apr_strings.h" #include "apr_queue.h" #include "fcgid_pm.h" #include "fcgid_pm_main.h" #include "fcgid_conf.h" #include "fcgid_spawn_ctl.h" #define FCGID_MSGQUEUE_SIZE 10 static apr_thread_t *g_thread = NULL; static apr_queue_t *g_msgqueue = NULL; static apr_queue_t *g_notifyqueue = NULL; static apr_thread_mutex_t *g_reqlock = NULL; static apr_thread_t *g_wakeup_thread = NULL; static int g_must_exit = 0; static int g_wakeup_timeout = 0; static void *APR_THREAD_FUNC wakeup_thread(apr_thread_t * thd, void *data) { while (!g_must_exit) { /* Wake up every second to check g_must_exit flag */ int i; for (i = 0; i < g_wakeup_timeout; i++) { if (g_must_exit) break; apr_sleep(apr_time_from_sec(1)); } /* Send a wake up message to procmgr_fetch_cmd() */ if (!g_must_exit && g_msgqueue) apr_queue_trypush(g_msgqueue, NULL); } return NULL; } static void *APR_THREAD_FUNC worker_thread(apr_thread_t * thd, void *data) { server_rec *main_server = data; pm_main(main_server, main_server->process->pconf); return NULL; } apr_status_t procmgr_pre_config(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp) { return APR_SUCCESS; } apr_status_t procmgr_post_config(server_rec * main_server, apr_pool_t * pconf) { apr_status_t rv; fcgid_server_conf *sconf = ap_get_module_config(main_server->module_config, &fcgid_module); /* Initialize spawn controler */ spawn_control_init(main_server, pconf); /* Create a message queues */ if ((rv = apr_queue_create(&g_msgqueue, FCGID_MSGQUEUE_SIZE, pconf)) != APR_SUCCESS || (rv = apr_queue_create(&g_notifyqueue, FCGID_MSGQUEUE_SIZE, pconf)) != APR_SUCCESS) { /* Fatal error */ ap_log_error(APLOG_MARK, APLOG_EMERG, rv, main_server, "mod_fcgid: can't create message queue"); exit(1); } /* Create request lock */ if ((rv = apr_thread_mutex_create(&g_reqlock, APR_THREAD_MUTEX_DEFAULT, pconf)) != APR_SUCCESS) { /* Fatal error */ ap_log_error(APLOG_MARK, APLOG_EMERG, rv, main_server, "mod_fcgid: Can't create request mutex"); exit(1); } /* Calculate procmgr_fetch_cmd wake up interval */ g_wakeup_timeout = min(sconf->error_scan_interval, sconf->busy_scan_interval); g_wakeup_timeout = min(sconf->idle_scan_interval, g_wakeup_timeout); if (g_wakeup_timeout == 0) g_wakeup_timeout = 1; /* Make it reasonable */ /* Create process manager worker thread */ if ((rv = apr_thread_create(&g_thread, NULL, worker_thread, main_server, pconf)) != APR_SUCCESS) { /* It's a fatal error */ ap_log_error(APLOG_MARK, APLOG_EMERG, rv, main_server, "mod_fcgid: can't create process manager thread"); exit(1); } /* Create wake up thread */ /* XXX If there was a function such like apr_queue_pop_timedwait(), then I don't need such an ugly thread to do the wake up job */ if ((rv = apr_thread_create(&g_wakeup_thread, NULL, wakeup_thread, NULL, pconf)) != APR_SUCCESS) { /* It's a fatal error */ ap_log_error(APLOG_MARK, APLOG_EMERG, rv, main_server, "mod_fcgid: can't create wake up thread"); exit(1); } apr_pool_cleanup_register(pconf, main_server, procmgr_stop_procmgr, apr_pool_cleanup_null); return APR_SUCCESS; } void procmgr_init_spawn_cmd(fcgid_command * command, request_rec * r, fcgid_cmd_conf *cmd_conf) { fcgid_server_conf *sconf = ap_get_module_config(r->server->module_config, &fcgid_module); /* no truncation should ever occur */ AP_DEBUG_ASSERT(sizeof command->cgipath > strlen(cmd_conf->cgipath)); apr_cpystrn(command->cgipath, cmd_conf->cgipath, sizeof command->cgipath); AP_DEBUG_ASSERT(sizeof command->cmdline > strlen(cmd_conf->cmdline)); apr_cpystrn(command->cmdline, cmd_conf->cmdline, sizeof command->cmdline); command->inode = (apr_ino_t) -1; command->deviceid = (dev_t) -1; command->uid = (uid_t) - 1; command->gid = (gid_t) - 1; command->userdir = 0; command->vhost_id = sconf->vhost_id; if (r->server->server_hostname) { apr_cpystrn(command->server_hostname, r->server->server_hostname, sizeof command->server_hostname); } else { command->server_hostname[0] = '\0'; } get_cmd_options(r, command->cgipath, &command->cmdopts, &command->cmdenv); } apr_status_t procmgr_send_spawn_cmd(fcgid_command * command, request_rec * r) { if (g_thread && g_msgqueue && !g_must_exit && g_reqlock && g_notifyqueue) { apr_status_t rv; /* Prepare the message send to another thread destroy the message if I can't push to message */ fcgid_command *postcmd = (fcgid_command *) malloc(sizeof(fcgid_command)); if (!postcmd) return APR_ENOMEM; memcpy(postcmd, command, sizeof(*command)); /* Get request lock first */ if ((rv = apr_thread_mutex_lock(g_reqlock)) != APR_SUCCESS) { ap_log_rerror(APLOG_MARK, APLOG_EMERG, rv, r, "mod_fcgid: can't get request lock"); return rv; } /* Try push the message */ if ((rv = apr_queue_push(g_msgqueue, postcmd)) != APR_SUCCESS) { apr_thread_mutex_unlock(g_reqlock); free(postcmd); ap_log_rerror(APLOG_MARK, APLOG_EMERG, rv, r, "mod_fcgid: can't push request message"); return rv; } else { /* Wait the respond from process manager */ char *notifybyte = NULL; if ((rv = apr_queue_pop(g_notifyqueue, (void **)¬ifybyte)) != APR_SUCCESS) { apr_thread_mutex_unlock(g_reqlock); ap_log_rerror(APLOG_MARK, APLOG_EMERG, rv, r, "mod_fcgid: can't pop notify message"); return rv; } } /* Release the lock now */ if ((rv = apr_thread_mutex_unlock(g_reqlock)) != APR_SUCCESS) { /* It's a fatal error */ ap_log_rerror(APLOG_MARK, APLOG_EMERG, rv, r, "mod_fcgid: can't release request lock"); exit(1); } } return APR_SUCCESS; } apr_status_t procmgr_finish_notify(server_rec * main_server) { apr_status_t rv; char *notify = NULL; if ((rv = apr_queue_push(g_notifyqueue, notify)) != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_EMERG, rv, main_server, "mod_fcgid: can't send spawn notify"); } return rv; } apr_status_t procmgr_fetch_cmd(fcgid_command * command, server_rec * main_server) { fcgid_command *peakcmd = NULL; if (!g_must_exit && g_msgqueue) { if (apr_queue_pop(g_msgqueue, (void **)&peakcmd) == APR_SUCCESS) { if (!peakcmd) return APR_TIMEUP; /* This a wake up message */ else { /* Copy the command, and then free the memory */ memcpy(command, peakcmd, sizeof(*peakcmd)); free(peakcmd); return APR_SUCCESS; } } } return APR_TIMEUP; } apr_status_t procmgr_child_init(server_rec * main_server, apr_pool_t * pchild) { /* Noop on windows, but used by unix */ return APR_SUCCESS; } int procmgr_must_exit() { return g_must_exit; } apr_status_t procmgr_stop_procmgr(void *server) { apr_status_t status; fcgid_server_conf *conf; /* Tell the world to die */ g_must_exit = 1; if (g_msgqueue) apr_queue_push(g_msgqueue, NULL); /* Wait */ if (g_thread && apr_thread_join(&status, g_thread) == APR_SUCCESS) { /* Free the memory left in queue */ fcgid_command *peakcmd = NULL; while (apr_queue_trypop(g_msgqueue, (void **)&peakcmd) == APR_SUCCESS) { if (peakcmd) free(peakcmd); } } /* Clean up the Job object if present */ conf = ap_get_module_config(((server_rec*)server)->module_config, &fcgid_module); if (conf->hJobObjectForAutoCleanup != NULL) { CloseHandle(conf->hJobObjectForAutoCleanup); } if (g_wakeup_thread) return apr_thread_join(&status, g_wakeup_thread); return APR_SUCCESS; } mod_fcgid-2.3.9/modules/fcgid/config.m40000644000175000017500000000314611552127610017571 0ustar trawicktrawickdnl Licensed to the Apache Software Foundation (ASF) under one or more dnl contributor license agreements. See the NOTICE file distributed with dnl this work for additional information regarding copyright ownership. dnl The ASF licenses this file to You under the Apache License, Version 2.0 dnl (the "License"); you may not use this file except in compliance with dnl the License. You may obtain a copy of the License at dnl dnl http://www.apache.org/licenses/LICENSE-2.0 dnl dnl Unless required by applicable law or agreed to in writing, software dnl distributed under the License is distributed on an "AS IS" BASIS, dnl WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. dnl See the License for the specific language governing permissions and dnl limitations under the License. dnl # start of module specific part APACHE_MODPATH_INIT(fcgid) case $host in *mingw*) fcgid_platform_objs="fcgid_pm_win.lo fcgid_proc_win.lo fcgid_proctbl_win.lo" ;; *) fcgid_platform_objs="fcgid_pm_unix.lo fcgid_proc_unix.lo fcgid_proctbl_unix.lo fcgid_mutex_unix.lo" ;; esac dnl # list of module object files fcigd_objs="dnl mod_fcgid.lo dnl fcgid_bridge.lo dnl fcgid_conf.lo dnl fcgid_pm_main.lo dnl fcgid_protocol.lo dnl fcgid_spawn_ctl.lo dnl fcgid_bucket.lo dnl fcgid_filter.lo dnl $fcgid_platform_objs dnl " APACHE_MODULE(fcgid, [FastCGI support (mod_fcgid)], $fcigd_objs, , no, [ AC_CHECK_HEADERS(sys/file.h) AC_CHECK_HEADERS(sys/mman.h) AC_CHECK_HEADERS(sys/mutex.h) AC_CHECK_HEADERS(sys/shm.h) ]) dnl # end of module specific part APACHE_MODPATH_FINISH mod_fcgid-2.3.9/modules/fcgid/fcgid_mutex_unix.c0000644000175000017500000001000711732663706021574 0ustar trawicktrawick/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "apr_global_mutex.h" #include "httpd.h" #include "ap_mmn.h" #include "http_log.h" #include "fcgid_mutex.h" #if AP_MODULE_MAGIC_AT_LEAST(20100504,0) #include "util_mutex.h" apr_status_t fcgid_mutex_register(const char *mutex_type, apr_pool_t *pconf) { return ap_mutex_register(pconf, mutex_type, NULL, APR_LOCK_DEFAULT, 0); } apr_status_t fcgid_mutex_create(apr_global_mutex_t **mutex, const char **lockfile, const char *mutex_type, apr_pool_t *pconf, server_rec *main_server) { apr_status_t rv; rv = ap_global_mutex_create(mutex, lockfile, mutex_type, NULL, main_server, pconf, 0); if (rv != APR_SUCCESS) { return rv; } return APR_SUCCESS; } #else /* no support for Mutex directive and related APIs */ #include "ap_mpm.h" #if MODULE_MAGIC_NUMBER_MAJOR < 20051115 #ifndef AP_NEED_SET_MUTEX_PERMS #define AP_NEED_SET_MUTEX_PERMS 1 #endif #endif #if AP_NEED_SET_MUTEX_PERMS #include "unixd.h" #endif #if MODULE_MAGIC_NUMBER_MAJOR < 20081201 #define ap_unixd_set_global_mutex_perms unixd_set_global_mutex_perms #endif apr_status_t fcgid_mutex_register(const char *mutex_type, apr_pool_t *pconf) { return APR_SUCCESS; } static apr_lockmech_e pick_mutex_mechanism(void) { apr_lockmech_e mechanism = APR_LOCK_DEFAULT; #if defined(SOLARIS2) && APR_USE_FCNTL_SERIALIZE /* default is fcntl(), which isn't thread-aware on Solaris; fcgid will * exit with EDEADLK errors if it is used with a threaded MPM */ int threaded; apr_status_t rv; rv = ap_mpm_query(AP_MPMQ_IS_THREADED, &threaded); if (rv == APR_SUCCESS && threaded) { #if APR_HAS_PROC_PTHREAD_SERIALIZE mechanism = APR_LOCK_PROC_PTHREAD; #elif APR_HAS_SYSVSEM_SERIALIZE mechanism = APR_LOCK_SYSVSEM; #endif } #endif return mechanism; } apr_status_t fcgid_mutex_create(apr_global_mutex_t **mutex, const char **lockfilep, const char *mutex_type, apr_pool_t *pconf, server_rec *s) { apr_status_t rv; apr_lockmech_e mechanism = pick_mutex_mechanism(); char *lockfile; /* XXX This lock file name generation is unfortunate, but defaulting * to a better place would require a directive to override it. This * is resolved for httpd 2.3+ by hooking into the Mutex support. */ lockfile = apr_palloc(pconf, L_tmpnam); tmpnam(lockfile); rv = apr_global_mutex_create(mutex, lockfile, mechanism, pconf); if (rv != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s, "mod_fcgid: Can't create global %s mutex", mutex_type); return rv; } #ifdef AP_NEED_SET_MUTEX_PERMS rv = ap_unixd_set_global_mutex_perms(*mutex); if (rv != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s, "mod_fcgid: Can't set global %s mutex perms", mutex_type); return rv; } #endif *lockfilep = lockfile; return APR_SUCCESS; } #endif mod_fcgid-2.3.9/modules/fcgid/Makefile.in0000644000175000017500000000154211244631702020125 0ustar trawicktrawick# Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # standard stuff # include $(top_srcdir)/build/special.mk all: all-recursive mod_fcgid-2.3.9/modules/fcgid/fcgid_spawn_ctl.h0000644000175000017500000000245411254440007021357 0ustar trawicktrawick/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef FCGID_SPAWN_CONTROL_H #define FCGID_SPAWN_CONTROL_H #include "fcgid_proctbl.h" #include "fcgid_pm.h" void spawn_control_init(server_rec * main_server, apr_pool_t * configpool); void register_termination(server_rec * main_server, fcgid_procnode * procnode); void register_spawn(server_rec * main_server, fcgid_procnode * procnode); int is_spawn_allowed(server_rec * main_server, fcgid_command * command); int is_kill_allowed(server_rec * main_server, fcgid_procnode * procnode); #endif mod_fcgid-2.3.9/modules/fcgid/mod_fcgid.rc0000644000175000017500000000374611251523032020323 0ustar trawicktrawick#define VERSION_ONLY #include "fcgid_conf.h" #define ASF_LICENSE \ "Licensed to the Apache Software Foundation (ASF) under one or more " \ "contributor license agreements. See the NOTICE file distributed with " \ "this work for additional information regarding copyright ownership. " \ "The ASF licenses this file to You under the Apache License, Version 2.0 " \ "(the ""License""); you may not use this file except in compliance with " \ "the License. You may obtain a copy of the License at\r\n\r\n" \ "http://www.apache.org/licenses/LICENSE-2.0\r\n\r\n" \ "Unless required by applicable law or agreed to in writing, software " \ "distributed under the License is distributed on an ""AS IS"" BASIS, " \ "WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. " \ "See the License for the specific language governing permissions and " \ "limitations under the License." /* macro for Win32 .rc files using numeric csv representation */ #define MODFCGID_REVISION_CSV MODFCGID_VERSION_MAJOR ##, \ ##MODFCGID_VERSION_MINOR ##, \ ##MODFCGID_VERSION_SUBVER 1 VERSIONINFO FILEVERSION MODFCGID_REVISION_CSV,200 PRODUCTVERSION MODFCGID_REVISION_CSV,200 FILEFLAGSMASK 0x3fL #if defined(_DEBUG) FILEFLAGS 0x01L #else FILEFLAGS 0x00L #endif FILEOS 0x40004L FILETYPE 0x1L FILESUBTYPE 0x0L BEGIN BLOCK "StringFileInfo" BEGIN BLOCK "040904b0" BEGIN VALUE "Comments", ASF_LICENSE "\0" VALUE "CompanyName", "Apache Software Foundation\0" VALUE "FileDescription", "fcgid_module for Apache\0" VALUE "FileVersion", MODFCGID_REVISION "\0" VALUE "InternalName", "mod_fcgid.so\0" VALUE "LegalCopyright", MODFCGID_COPYRIGHT "\0" VALUE "OriginalFilename", "mod_fcgid.so\0" VALUE "ProductName", "Apache HTTP Server Project\0" VALUE "ProductVersion", MODFCGID_REVISION "\0" END END BLOCK "VarFileInfo" BEGIN VALUE "Translation", 0x409, 1200 END END mod_fcgid-2.3.9/modules/fcgid/fcgid_bridge.h0000644000175000017500000000210311332122674020614 0ustar trawicktrawick/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef FCGID_BRIDGE_H #define FCGID_BRIDGE_H #include "httpd.h" #include "ap_config.h" #include "http_config.h" #include "apr_hash.h" #include "fcgid_conf.h" apr_status_t bucket_ctx_cleanup(void *thectx); int bridge_request(request_rec * r, int role, fcgid_cmd_conf *cmd_conf); #endif mod_fcgid-2.3.9/modules/fcgid/fcgid_proc.h0000644000175000017500000000473411334325511020334 0ustar trawicktrawick/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef FCGID_PM_PROC_H #define FCGID_PM_PROC_H #include "httpd.h" #include "apr_pools.h" #include "apr_file_io.h" #include "fcgid_proctbl.h" typedef struct { apr_table_t *proc_environ; server_rec *main_server; apr_pool_t *configpool; char *cgipath; uid_t uid; /* For suEXEC */ gid_t gid; /* For suEXEC */ int userdir; /* For suEXEC */ } fcgid_proc_info; typedef struct { int connect_timeout; /* in second */ int communation_timeout; /* in second */ void *ipc_handle_info; request_rec *request; } fcgid_ipc; apr_status_t proc_spawn_process(const char *cmdline, fcgid_proc_info * procinfo, fcgid_procnode * procnode); apr_status_t proc_kill_gracefully(fcgid_procnode * procnode, server_rec * main_server); apr_status_t proc_kill_force(fcgid_procnode * procnode, server_rec * main_server); apr_status_t proc_wait_process(server_rec * main_server, fcgid_procnode * procnode); apr_status_t proc_connect_ipc(fcgid_procnode * procnode, fcgid_ipc * ipc_handle); apr_status_t proc_read_ipc(fcgid_ipc * ipc_handle, const char *buffer, apr_size_t * size); apr_status_t proc_write_ipc(fcgid_ipc * ipc_handle, apr_bucket_brigade * output_brigade); apr_status_t proc_close_ipc(fcgid_ipc * ipc_handle); void proc_print_exit_info(fcgid_procnode * procnode, int exitcode, apr_exit_why_e exitwhy, server_rec * main_server); #endif mod_fcgid-2.3.9/modules/fcgid/fcgid_pm.h0000644000175000017500000000467712223417144020015 0ustar trawicktrawick/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef FCGID_PM_H #define FCGID_PM_H #include "fcgid_global.h" #include "fcgid_conf.h" typedef struct { char cgipath[FCGID_PATH_MAX]; char cmdline[FCGID_CMDLINE_MAX]; apr_ino_t inode; dev_t deviceid; /* can't reference these via server_rec because some mass vhost * module may have copied it for per-request customization */ int vhost_id; char server_hostname[32]; /* for logging only; ok to truncate */ uid_t uid; /* For suEXEC */ gid_t gid; /* For suEXEC */ int userdir; /* For suEXEC */ fcgid_cmd_options cmdopts; /* context-specific configuration, other than * envvars */ fcgid_cmd_env cmdenv; /* start the command with these env settings */ } fcgid_command; void procmgr_init_spawn_cmd(fcgid_command * command, request_rec * r, fcgid_cmd_conf *cmd_conf); apr_status_t procmgr_send_spawn_cmd(fcgid_command * command, request_rec * r); apr_status_t procmgr_fetch_cmd(fcgid_command * command, server_rec * main_server); apr_status_t procmgr_finish_notify(server_rec * main_server); apr_status_t procmgr_child_init(server_rec * main_server, apr_pool_t * pchild); apr_status_t procmgr_pre_config(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp); apr_status_t procmgr_post_config(server_rec * main_server, apr_pool_t * pconf); apr_status_t procmgr_stop_procmgr(void *dummy); int procmgr_must_exit(void); #endif mod_fcgid-2.3.9/modules/fcgid/Makefile.apxs0000644000175000017500000000137011244631702020471 0ustar trawicktrawick## ## Makefile.apxs -- Build procedure for mod_fcgid Apache module ## ## Do not use this target; build from the mod_fcgid dir root ## # top_builddir and top_srcdir are misnomers, because build/*.mk # scripts expect it them be the parent of the build directory, # and fail to trust the installbuilddir. exp_installbuilddir=$(shell $(APXS) -q exp_installbuilddir) top_srcdir=$(installbuilddir)/.. top_builddir=$(installbuilddir)/.. fcgid_builddir=../.. fcgid_srcdir=../.. builddir=. srcdir=. CLEAN_TARGETS = *.loT include $(exp_installbuilddir)/special.mk all: local-shared-build all-recursive # additional defines, includes and libraries DEFS=-DFCGID_APXS_BUILD INCLUDES=-I$(builddir) -I$(srcdir) -I$(fcgid_srcdir)/include #LIBS=-Lmy/lib/dir -lmylib mod_fcgid-2.3.9/modules/fcgid/fcgid_mutex.h0000644000175000017500000000224211732663706020540 0ustar trawicktrawick/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ apr_status_t fcgid_mutex_register(const char *mutex_type, apr_pool_t *pconf); apr_status_t fcgid_mutex_create(apr_global_mutex_t **mutex, const char **lockfile, const char *mutex_type, apr_pool_t *pconf, server_rec *s); mod_fcgid-2.3.9/modules/fcgid/fcgid_pm_main.c0000644000175000017500000005522112223417144021003 0ustar trawicktrawick/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* For DEFAULT_PATH */ #define CORE_PRIVATE #include "httpd.h" #include "http_config.h" #include "apr_strings.h" #include "fcgid_pm.h" #include "fcgid_pm_main.h" #include "fcgid_conf.h" #include "fcgid_proctbl.h" #include "fcgid_proc.h" #include "fcgid_spawn_ctl.h" #define HAS_GRACEFUL_KILL "Gracefulkill" static void link_node_to_list(server_rec * main_server, fcgid_procnode * header, fcgid_procnode * node, fcgid_procnode * table_array) { proctable_pm_lock(main_server); node->next_index = header->next_index; header->next_index = node - table_array; proctable_pm_unlock(main_server); } static apr_time_t lastidlescan = 0; static void scan_idlelist(server_rec * main_server) { /* Scan the idle list 1. move all processes idle timeout to error list 2. move all processes lifetime expired to error list */ fcgid_procnode *previous_node, *current_node, *next_node; fcgid_procnode *error_list_header; fcgid_procnode *proc_table; apr_time_t last_active_time, start_time; apr_time_t now = apr_time_now(); int idle_timeout, proc_lifetime; fcgid_server_conf *sconf = ap_get_module_config(main_server->module_config, &fcgid_module); /* Should I check the idle list now? */ if (procmgr_must_exit() || apr_time_sec(now) - apr_time_sec(lastidlescan) <= sconf->idle_scan_interval) return; lastidlescan = now; /* Check the list */ proc_table = proctable_get_table_array(); previous_node = proctable_get_idle_list(); error_list_header = proctable_get_error_list(); proctable_pm_lock(main_server); current_node = &proc_table[previous_node->next_index]; while (current_node != proc_table) { next_node = &proc_table[current_node->next_index]; last_active_time = current_node->last_active_time; start_time = current_node->start_time; idle_timeout = current_node->cmdopts.idle_timeout; proc_lifetime = current_node->cmdopts.proc_lifetime; if (((idle_timeout && (apr_time_sec(now) - apr_time_sec(last_active_time) > idle_timeout)) || (proc_lifetime && (apr_time_sec(now) - apr_time_sec(start_time) > proc_lifetime))) && is_kill_allowed(main_server, current_node)) { /* Set die reason for log */ if (idle_timeout && (apr_time_sec(now) - apr_time_sec(last_active_time) > idle_timeout)) current_node->diewhy = FCGID_DIE_IDLE_TIMEOUT; else if (proc_lifetime && (apr_time_sec(now) - apr_time_sec(start_time) > proc_lifetime)) current_node->diewhy = FCGID_DIE_LIFETIME_EXPIRED; /* Unlink from idle list */ previous_node->next_index = current_node->next_index; /* Link to error list */ current_node->next_index = error_list_header->next_index; error_list_header->next_index = current_node - proc_table; } else previous_node = current_node; current_node = next_node; } proctable_pm_unlock(main_server); } static apr_time_t lastbusyscan = 0; static void scan_busylist(server_rec * main_server) { fcgid_procnode *current_node; fcgid_procnode *proc_table; apr_time_t last_active_time; apr_time_t now = apr_time_now(); fcgid_server_conf *sconf = ap_get_module_config(main_server->module_config, &fcgid_module); /* Should I check the busy list? */ if (procmgr_must_exit() || apr_time_sec(now) - apr_time_sec(lastbusyscan) <= sconf->busy_scan_interval) return; lastbusyscan = now; /* Check busy list */ proc_table = proctable_get_table_array(); proctable_pm_lock(main_server); current_node = &proc_table[proctable_get_busy_list()->next_index]; while (current_node != proc_table) { last_active_time = current_node->last_active_time; if (apr_time_sec(now) - apr_time_sec(last_active_time) > (current_node->cmdopts.busy_timeout)) { /* Protocol: 1. diewhy init with FCGID_DIE_KILLSELF 2. Process manager set diewhy to FCGID_DIE_BUSY_TIMEOUT and gracefully kill process while busy timeout 3. Process manager forced kill process while busy timeout and diewhy is FCGID_DIE_BUSY_TIMEOUT */ if (current_node->diewhy == FCGID_DIE_BUSY_TIMEOUT) proc_kill_force(current_node, main_server); else { current_node->diewhy = FCGID_DIE_BUSY_TIMEOUT; proc_kill_gracefully(current_node, main_server); } } current_node = &proc_table[current_node->next_index]; } proctable_pm_unlock(main_server); } static apr_time_t lastzombiescan = 0; static void scan_idlelist_zombie(server_rec * main_server) { /* Scan the idle list 1. pick up the node for scan(now-last_activ>g_zombie_scan_interval) 2. check if it's zombie process 3. if it's zombie process, wait() and return to free list 4. return to idle list if it's not zombie process */ pid_t thepid; fcgid_procnode *previous_node, *current_node, *next_node; fcgid_procnode *check_list_header; fcgid_procnode *proc_table; apr_time_t last_active_time; apr_time_t now = apr_time_now(); fcgid_procnode temp_header; fcgid_server_conf *sconf = ap_get_module_config(main_server->module_config, &fcgid_module); memset(&temp_header, 0, sizeof(temp_header)); /* Should I check zombie processes in idle list now? */ if (procmgr_must_exit() || apr_time_sec(now) - apr_time_sec(lastzombiescan) <= sconf->zombie_scan_interval) return; lastzombiescan = now; /* Check the list */ proc_table = proctable_get_table_array(); previous_node = proctable_get_idle_list(); check_list_header = &temp_header; proctable_pm_lock(main_server); current_node = &proc_table[previous_node->next_index]; while (current_node != proc_table) { next_node = &proc_table[current_node->next_index]; /* Is it time for zombie check? */ last_active_time = current_node->last_active_time; if (apr_time_sec(now) - apr_time_sec(last_active_time) > sconf->zombie_scan_interval) { /* Unlink from idle list */ previous_node->next_index = current_node->next_index; /* Link to check list */ current_node->next_index = check_list_header->next_index; check_list_header->next_index = current_node - proc_table; } else previous_node = current_node; current_node = next_node; } proctable_pm_unlock(main_server); /* Now check every node in check list 1) If it's zombie process, wait() and return to free list 2) If it's not zombie process, link it to the tail of idle list */ previous_node = check_list_header; current_node = &proc_table[previous_node->next_index]; while (current_node != proc_table) { next_node = &proc_table[current_node->next_index]; /* Is it zombie process? */ thepid = current_node->proc_id.pid; if (proc_wait_process(main_server, current_node) == APR_CHILD_DONE) { ap_log_error(APLOG_MARK, APLOG_WARNING, 0, main_server, "mod_fcgid: cleanup zombie process %" APR_PID_T_FMT, thepid); /* Unlink from check list */ previous_node->next_index = current_node->next_index; /* Link to free list */ link_node_to_list(main_server, proctable_get_free_list(), current_node, proc_table); } else previous_node = current_node; current_node = next_node; } /* Now link the check list back to the tail of idle list */ if (check_list_header->next_index) { proctable_pm_lock(main_server); previous_node = proctable_get_idle_list(); current_node = &proc_table[previous_node->next_index]; /* Find the tail of idle list */ while (current_node != proc_table) { previous_node = current_node; current_node = &proc_table[current_node->next_index]; } /* Link check list to the tail of idle list */ previous_node->next_index = check_list_header->next_index; proctable_pm_unlock(main_server); } } static apr_time_t lasterrorscan = 0; static void scan_errorlist(server_rec * main_server) { /* kill() and wait() every node in error list put them back to free list after that */ void *dummy; fcgid_procnode *previous_node, *current_node, *next_node; apr_time_t now = apr_time_now(); fcgid_procnode *error_list_header = proctable_get_error_list(); fcgid_procnode *free_list_header = proctable_get_free_list(); fcgid_procnode *proc_table = proctable_get_table_array(); fcgid_procnode temp_error_header; fcgid_server_conf *sconf = ap_get_module_config(main_server->module_config, &fcgid_module); int graceful_terminations = 0; /* Should I check the busy list? */ if (procmgr_must_exit() || apr_time_sec(now) - apr_time_sec(lasterrorscan) <= sconf->error_scan_interval) return; lasterrorscan = now; /* Try wait dead processes, restore to free list */ /* Note: I can't keep the lock during the scan */ proctable_pm_lock(main_server); temp_error_header.next_index = error_list_header->next_index; error_list_header->next_index = 0; proctable_pm_unlock(main_server); previous_node = &temp_error_header; current_node = &proc_table[previous_node->next_index]; while (current_node != proc_table) { next_node = &proc_table[current_node->next_index]; if (proc_wait_process(main_server, current_node) != APR_CHILD_NOTDONE) { /* Unlink from error list */ previous_node->next_index = current_node->next_index; /* Link to free list */ current_node->next_index = free_list_header->next_index; free_list_header->next_index = current_node - proc_table; } else previous_node = current_node; current_node = next_node; } /* Kill the left processes, wait() them in the next round */ for (current_node = &proc_table[temp_error_header.next_index]; current_node != proc_table; current_node = &proc_table[current_node->next_index]) { /* Try gracefully first */ dummy = NULL; apr_pool_userdata_get(&dummy, HAS_GRACEFUL_KILL, current_node->proc_pool); if (!dummy) { proc_kill_gracefully(current_node, main_server); ++graceful_terminations; apr_pool_userdata_set("set", HAS_GRACEFUL_KILL, apr_pool_cleanup_null, current_node->proc_pool); } else { ap_log_error(APLOG_MARK, APLOG_WARNING, 0, main_server, "mod_fcgid: process %" APR_PID_T_FMT " graceful kill fail, sending SIGKILL", current_node->proc_id.pid); proc_kill_force(current_node, main_server); } } /* Link the temp error list back */ proctable_pm_lock(main_server); /* Find the tail of error list */ previous_node = error_list_header; current_node = &proc_table[previous_node->next_index]; while (current_node != proc_table) { previous_node = current_node; current_node = &proc_table[current_node->next_index]; } previous_node->next_index = temp_error_header.next_index; proctable_pm_unlock(main_server); if (graceful_terminations) { ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, main_server, "mod_fcgid: gracefully terminated %d processes", graceful_terminations); } } typedef enum action_t {DO_NOTHING, KILL_GRACEFULLY, KILL_FORCEFULLY, HARD_WAIT} action_t; static int reclaim_one_pid(server_rec *main_server, fcgid_procnode *proc, action_t action) { int exitcode; apr_exit_why_e exitwhy; apr_wait_how_e wait_how = action == HARD_WAIT ? APR_WAIT : APR_NOWAIT; if (apr_proc_wait(&proc->proc_id, &exitcode, &exitwhy, wait_how) != APR_CHILD_NOTDONE) { proc->diewhy = FCGID_DIE_SHUTDOWN; proc_print_exit_info(proc, exitcode, exitwhy, main_server); proc->proc_pool = NULL; return 1; } switch(action) { case DO_NOTHING: case HARD_WAIT: break; case KILL_GRACEFULLY: proc_kill_gracefully(proc, main_server); break; case KILL_FORCEFULLY: ap_log_error(APLOG_MARK, APLOG_ERR, 0, main_server, "FastCGI process %" APR_PID_T_FMT " still did not exit, " "terminating forcefully", proc->proc_id.pid); proc_kill_force(proc, main_server); break; } return 0; } static void kill_all_subprocess(server_rec *main_server) { apr_time_t waittime = 1024 * 16; size_t i, table_size = proctable_get_table_size(); int not_dead_yet; int cur_action, next_action; apr_time_t starttime = apr_time_now(); struct { action_t action; apr_time_t action_time; } action_table[] = { {DO_NOTHING, 0}, /* dummy entry for iterations where * we reap children but take no action * against stragglers */ {KILL_GRACEFULLY, apr_time_from_sec(0)}, {KILL_GRACEFULLY, apr_time_from_sec(1)}, {KILL_FORCEFULLY, apr_time_from_sec(8)}, {HARD_WAIT, apr_time_from_sec(8)} }; fcgid_procnode *proc_table = proctable_get_table_array(); next_action = 1; do { apr_sleep(waittime); /* don't let waittime get longer than 1 second; otherwise, we don't * react quickly to the last child exiting, and taking action can * be delayed */ waittime = waittime * 4; if (waittime > apr_time_from_sec(1)) { waittime = apr_time_from_sec(1); } /* see what action to take, if any */ if (action_table[next_action].action_time <= apr_time_now() - starttime) { cur_action = next_action; ++next_action; } else { cur_action = 0; /* index of DO_NOTHING entry */ } /* now see who is done */ not_dead_yet = 0; for (i = 0; i < table_size; i++) { if (!proc_table[i].proc_pool) { continue; /* unused */ } if (!reclaim_one_pid(main_server, &proc_table[i], action_table[cur_action].action)) { ++not_dead_yet; } } } while (not_dead_yet && action_table[cur_action].action != HARD_WAIT); } /* This should be proposed as a stand-alone improvement to the httpd module, * either in the arch/ platform-specific modules or util_script.c from whence * it came. */ static void default_proc_env(apr_table_t * e) { const char *env_temp; if (!(env_temp = getenv("PATH"))) { env_temp = DEFAULT_PATH; } apr_table_addn(e, "PATH", env_temp); #ifdef WIN32 if ((env_temp = getenv("SYSTEMROOT"))) { apr_table_addn(e, "SYSTEMROOT", env_temp); } if ((env_temp = getenv("COMSPEC"))) { apr_table_addn(e, "COMSPEC", env_temp); } if ((env_temp = getenv("PATHEXT"))) { apr_table_addn(e, "PATHEXT", env_temp); } if ((env_temp = getenv("WINDIR"))) { apr_table_addn(e, "WINDIR", env_temp); } #elif defined(OS2) if ((env_temp = getenv("COMSPEC")) != NULL) { apr_table_addn(e, "COMSPEC", env_temp); } if ((env_temp = getenv("ETC")) != NULL) { apr_table_addn(e, "ETC", env_temp); } if ((env_temp = getenv("DPATH")) != NULL) { apr_table_addn(e, "DPATH", env_temp); } if ((env_temp = getenv("PERLLIB_PREFIX")) != NULL) { apr_table_addn(e, "PERLLIB_PREFIX", env_temp); } #elif defined(BEOS) if ((env_temp = getenv("LIBRARY_PATH")) != NULL) { apr_table_addn(e, "LIBRARY_PATH", env_temp); } #elif defined (AIX) if ((env_temp = getenv("LIBPATH"))) { apr_table_addn(e, "LIBPATH", env_temp); } #else /* DARWIN, HPUX vary depending on circumstance */ #if defined (DARWIN) if ((env_temp = getenv("DYLD_LIBRARY_PATH"))) { apr_table_addn(e, "DYLD_LIBRARY_PATH", env_temp); } #elif defined (HPUX11) || defined (HPUX10) || defined (HPUX) if ((env_temp = getenv("SHLIB_PATH"))) { apr_table_addn(e, "SHLIB_PATH", env_temp); } #endif if ((env_temp = getenv("LD_LIBRARY_PATH"))) { apr_table_addn(e, "LD_LIBRARY_PATH", env_temp); } #endif } /* End of common to util_script.c */ static void fastcgi_spawn(fcgid_command * command, server_rec * main_server, apr_pool_t * configpool) { fcgid_procnode *free_list_header, *proctable_array, *procnode, *idle_list_header; fcgid_proc_info procinfo; apr_status_t rv; int i; free_list_header = proctable_get_free_list(); idle_list_header = proctable_get_idle_list(); proctable_array = proctable_get_table_array(); /* Apply a slot from free list */ proctable_pm_lock(main_server); if (free_list_header->next_index == 0) { proctable_pm_unlock(main_server); ap_log_error(APLOG_MARK, APLOG_WARNING, 0, main_server, "mod_fcgid: too much processes, please increase FCGID_MAX_APPLICATION"); return; } procnode = &proctable_array[free_list_header->next_index]; free_list_header->next_index = procnode->next_index; procnode->next_index = 0; proctable_pm_unlock(main_server); /* Prepare to spawn */ procnode->deviceid = command->deviceid; procnode->inode = command->inode; /* no truncation should ever occur */ AP_DEBUG_ASSERT(sizeof procnode->cmdline > strlen(command->cmdline)); apr_cpystrn(procnode->cmdline, command->cmdline, sizeof procnode->cmdline); procnode->vhost_id = command->vhost_id; procnode->uid = command->uid; procnode->gid = command->gid; procnode->start_time = procnode->last_active_time = apr_time_now(); procnode->requests_handled = 0; procnode->diewhy = FCGID_DIE_KILLSELF; procnode->proc_pool = NULL; procnode->cmdopts = command->cmdopts; procinfo.cgipath = command->cgipath; procinfo.configpool = configpool; procinfo.main_server = main_server; procinfo.uid = command->uid; procinfo.gid = command->gid; procinfo.userdir = command->userdir; if ((rv = apr_pool_create(&procnode->proc_pool, configpool)) != APR_SUCCESS || (procinfo.proc_environ = apr_table_make(procnode->proc_pool, INITENV_CNT)) == NULL) { /* Link the node back to free list in this case */ if (procnode->proc_pool) apr_pool_destroy(procnode->proc_pool); link_node_to_list(main_server, free_list_header, procnode, proctable_array); ap_log_error(APLOG_MARK, APLOG_WARNING, rv, main_server, "mod_fcgid: can't create pool for process"); return; } /* Set up longer, system defaults before falling into parsing fixed-limit * request-by-request variables, so if any are overriden, they preempt * any system default assumptions */ default_proc_env(procinfo.proc_environ); for (i = 0; i < INITENV_CNT; i++) { if (command->cmdenv.initenv_key[i][0] == '\0') break; apr_table_set(procinfo.proc_environ, command->cmdenv.initenv_key[i], command->cmdenv.initenv_val[i]); } /* Spawn the process now */ /* XXX Spawn uses wrapper_cmdline, but log uses cgipath ? */ if ((rv = proc_spawn_process(command->cmdline, &procinfo, procnode)) != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_WARNING, rv, main_server, "mod_fcgid: spawn process %s error", command->cgipath); apr_pool_destroy(procnode->proc_pool); link_node_to_list(main_server, free_list_header, procnode, proctable_array); return; } else { /* The job done */ link_node_to_list(main_server, idle_list_header, procnode, proctable_array); ap_log_error(APLOG_MARK, APLOG_INFO, 0, main_server, "mod_fcgid: server %s:%s(%" APR_PID_T_FMT ") started", command->server_hostname[0] ? command->server_hostname : "(unknown)", command->cgipath, procnode->proc_id.pid); register_spawn(main_server, procnode); } } apr_status_t pm_main(server_rec * main_server, apr_pool_t * configpool) { fcgid_command command; while (1) { if (procmgr_must_exit()) break; /* Wait for command */ if (procmgr_fetch_cmd(&command, main_server) == APR_SUCCESS) { if (is_spawn_allowed(main_server, &command)) fastcgi_spawn(&command, main_server, configpool); procmgr_finish_notify(main_server); } /* Move matched node to error list */ scan_idlelist_zombie(main_server); scan_idlelist(main_server); scan_busylist(main_server); /* Kill() and wait() nodes in error list */ scan_errorlist(main_server); } /* Stop all processes */ kill_all_subprocess(main_server); return APR_SUCCESS; } mod_fcgid-2.3.9/modules/fcgid/mod_fcgid.mak0000644000175000017500000002216611251523551020472 0ustar trawicktrawick# Microsoft Developer Studio Generated NMAKE File, Based on mod_fcgid.dsp !IF "$(CFG)" == "" CFG=mod_fcgid - Win32 Release !MESSAGE No configuration specified. Defaulting to mod_fcgid - Win32 Release. !ENDIF !IF "$(CFG)" != "mod_fcgid - Win32 Release" && "$(CFG)" != "mod_fcgid - Win32 Debug" !MESSAGE Invalid configuration "$(CFG)" specified. !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_fcgid.mak" CFG="mod_fcgid - Win32 Release" !MESSAGE !MESSAGE Possible choices for configuration are: !MESSAGE !MESSAGE "mod_fcgid - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") !MESSAGE "mod_fcgid - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") !MESSAGE !ERROR An invalid configuration is specified. !ENDIF !IF "$(OS)" == "Windows_NT" NULL= !ELSE NULL=nul !ENDIF !IF "$(CFG)" == "mod_fcgid - Win32 Release" OUTDIR=.\Release INTDIR=.\Release DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep # Begin Custom Macros OutDir=.\Release # End Custom Macros ALL : "$(OUTDIR)\mod_fcgid.so" "$(DS_POSTBUILD_DEP)" CLEAN : -@erase "$(INTDIR)\fcgid_bridge.obj" -@erase "$(INTDIR)\fcgid_bucket.obj" -@erase "$(INTDIR)\fcgid_conf.obj" -@erase "$(INTDIR)\fcgid_filter.obj" -@erase "$(INTDIR)\fcgid_pm_main.obj" -@erase "$(INTDIR)\fcgid_pm_win.obj" -@erase "$(INTDIR)\fcgid_proc_win.obj" -@erase "$(INTDIR)\fcgid_proctbl_win.obj" -@erase "$(INTDIR)\fcgid_protocol.obj" -@erase "$(INTDIR)\fcgid_spawn_ctl.obj" -@erase "$(INTDIR)\mod_fcgid.obj" -@erase "$(INTDIR)\mod_fcgid.res" -@erase "$(INTDIR)\mod_fcgid_src.idb" -@erase "$(INTDIR)\mod_fcgid_src.pdb" -@erase "$(OUTDIR)\mod_fcgid.exp" -@erase "$(OUTDIR)\mod_fcgid.lib" -@erase "$(OUTDIR)\mod_fcgid.pdb" -@erase "$(OUTDIR)\mod_fcgid.so" "$(OUTDIR)" : if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" CPP=cl.exe CPP_PROJ=/nologo /MD /W3 /Zi /O2 /Oy- /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /I "$(APACHE2_HOME)/include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\mod_fcgid_src" /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 /win32 RSC=rc.exe RSC_PROJ=/l 0x409 /fo"$(INTDIR)\mod_fcgid.res" /i "../../srclib/apr/include" /i "$(APACHE2_HOME)/include" /d "NDEBUG" BSC32=bscmake.exe BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_fcgid.bsc" BSC32_SBRS= \ LINK32=link.exe LINK32_FLAGS=libhttpd.lib libaprutil-1.lib libapr-1.lib kernel32.lib /nologo /base:"0x46430000" /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_fcgid.pdb" /debug /out:"$(OUTDIR)\mod_fcgid.so" /implib:"$(OUTDIR)\mod_fcgid.lib" /libpath:"..\..\Release" /libpath:"..\..\srclib\apr\Release" /libpath:"..\..\srclib\apr-util\Release" /libpath:"$(APACHE2_HOME)/lib" /opt:ref LINK32_OBJS= \ "$(INTDIR)\fcgid_bridge.obj" \ "$(INTDIR)\fcgid_bucket.obj" \ "$(INTDIR)\fcgid_conf.obj" \ "$(INTDIR)\fcgid_filter.obj" \ "$(INTDIR)\fcgid_pm_main.obj" \ "$(INTDIR)\fcgid_pm_win.obj" \ "$(INTDIR)\fcgid_proc_win.obj" \ "$(INTDIR)\fcgid_proctbl_win.obj" \ "$(INTDIR)\fcgid_protocol.obj" \ "$(INTDIR)\fcgid_spawn_ctl.obj" \ "$(INTDIR)\mod_fcgid.obj" \ "$(INTDIR)\mod_fcgid.res" "$(OUTDIR)\mod_fcgid.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) $(LINK32) @<< $(LINK32_FLAGS) $(LINK32_OBJS) << TargetPath=.\Release\mod_fcgid.so SOURCE="$(InputPath)" PostBuild_Desc=Embed .manifest DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep # Begin Custom Macros OutDir=.\Release # End Custom Macros "$(DS_POSTBUILD_DEP)" : "$(OUTDIR)\mod_fcgid.so" if exist .\Release\mod_fcgid.so.manifest mt.exe -manifest .\Release\mod_fcgid.so.manifest -outputresource:.\Release\mod_fcgid.so;2 echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)" !ELSEIF "$(CFG)" == "mod_fcgid - Win32 Debug" OUTDIR=.\Debug INTDIR=.\Debug DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep # Begin Custom Macros OutDir=.\Debug # End Custom Macros ALL : ".\fcgid_config.h" "$(OUTDIR)\mod_fcgid.so" "$(DS_POSTBUILD_DEP)" CLEAN : -@erase "$(INTDIR)\fcgid_bridge.obj" -@erase "$(INTDIR)\fcgid_bucket.obj" -@erase "$(INTDIR)\fcgid_conf.obj" -@erase "$(INTDIR)\fcgid_filter.obj" -@erase "$(INTDIR)\fcgid_pm_main.obj" -@erase "$(INTDIR)\fcgid_pm_win.obj" -@erase "$(INTDIR)\fcgid_proc_win.obj" -@erase "$(INTDIR)\fcgid_proctbl_win.obj" -@erase "$(INTDIR)\fcgid_protocol.obj" -@erase "$(INTDIR)\fcgid_spawn_ctl.obj" -@erase "$(INTDIR)\mod_fcgid.obj" -@erase "$(INTDIR)\mod_fcgid.res" -@erase "$(INTDIR)\mod_fcgid_src.idb" -@erase "$(INTDIR)\mod_fcgid_src.pdb" -@erase "$(OUTDIR)\mod_fcgid.exp" -@erase "$(OUTDIR)\mod_fcgid.lib" -@erase "$(OUTDIR)\mod_fcgid.pdb" -@erase "$(OUTDIR)\mod_fcgid.so" -@erase ".\fcgid_config.h" "$(OUTDIR)" : if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" CPP=cl.exe CPP_PROJ=/nologo /MDd /W3 /GX /Zi /Od /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /I "$(APACHE2_HOME)/include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\mod_fcgid_src" /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 /win32 RSC=rc.exe RSC_PROJ=/l 0x409 /fo"$(INTDIR)\mod_fcgid.res" /i "../../srclib/apr/include" /i "$(APACHE2_HOME)/include" /d "_DEBUG" BSC32=bscmake.exe BSC32_FLAGS=/nologo /o"$(OUTDIR)\mod_fcgid.bsc" BSC32_SBRS= \ LINK32=link.exe LINK32_FLAGS=libhttpd.lib libaprutil-1.lib libapr-1.lib kernel32.lib /nologo /base:"0x46430000" /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\mod_fcgid.pdb" /debug /out:"$(OUTDIR)\mod_fcgid.so" /implib:"$(OUTDIR)\mod_fcgid.lib" /libpath:"..\..\Debug" /libpath:"..\..\srclib\apr\Debug" /libpath:"..\..\srclib\apr-util\Debug" /libpath:"$(APACHE2_HOME)/lib" LINK32_OBJS= \ "$(INTDIR)\fcgid_bridge.obj" \ "$(INTDIR)\fcgid_bucket.obj" \ "$(INTDIR)\fcgid_conf.obj" \ "$(INTDIR)\fcgid_filter.obj" \ "$(INTDIR)\fcgid_pm_main.obj" \ "$(INTDIR)\fcgid_pm_win.obj" \ "$(INTDIR)\fcgid_proc_win.obj" \ "$(INTDIR)\fcgid_proctbl_win.obj" \ "$(INTDIR)\fcgid_protocol.obj" \ "$(INTDIR)\fcgid_spawn_ctl.obj" \ "$(INTDIR)\mod_fcgid.obj" \ "$(INTDIR)\mod_fcgid.res" "$(OUTDIR)\mod_fcgid.so" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) $(LINK32) @<< $(LINK32_FLAGS) $(LINK32_OBJS) << TargetPath=.\Debug\mod_fcgid.so SOURCE="$(InputPath)" PostBuild_Desc=Embed .manifest DS_POSTBUILD_DEP=$(INTDIR)\postbld.dep # Begin Custom Macros OutDir=.\Debug # End Custom Macros "$(DS_POSTBUILD_DEP)" : "$(OUTDIR)\mod_fcgid.so" if exist .\Debug\mod_fcgid.so.manifest mt.exe -manifest .\Debug\mod_fcgid.so.manifest -outputresource:.\Debug\mod_fcgid.so;2 echo Helper for Post-build step > "$(DS_POSTBUILD_DEP)" !ENDIF !IF "$(NO_EXTERNAL_DEPS)" != "1" !IF EXISTS("mod_fcgid.dep") !INCLUDE "mod_fcgid.dep" !ELSE !MESSAGE Warning: cannot find "mod_fcgid.dep" !ENDIF !ENDIF !IF "$(CFG)" == "mod_fcgid - Win32 Release" || "$(CFG)" == "mod_fcgid - Win32 Debug" SOURCE=.\fcgid_bridge.c "$(INTDIR)\fcgid_bridge.obj" : $(SOURCE) "$(INTDIR)" ".\fcgid_config.h" SOURCE=.\fcgid_bucket.c "$(INTDIR)\fcgid_bucket.obj" : $(SOURCE) "$(INTDIR)" ".\fcgid_config.h" SOURCE=.\fcgid_conf.c "$(INTDIR)\fcgid_conf.obj" : $(SOURCE) "$(INTDIR)" ".\fcgid_config.h" SOURCE=".\fcgid_config.h.in" !IF "$(CFG)" == "mod_fcgid - Win32 Release" InputPath=".\fcgid_config.h.in" ".\fcgid_config.h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" < .\fcgid_config.h << !ELSEIF "$(CFG)" == "mod_fcgid - Win32 Debug" InputPath=".\fcgid_config.h.in" ".\fcgid_config.h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" < .\fcgid_config.h << !ENDIF SOURCE=.\fcgid_filter.c "$(INTDIR)\fcgid_filter.obj" : $(SOURCE) "$(INTDIR)" ".\fcgid_config.h" SOURCE=.\fcgid_pm_main.c "$(INTDIR)\fcgid_pm_main.obj" : $(SOURCE) "$(INTDIR)" ".\fcgid_config.h" SOURCE=.\fcgid_pm_win.c "$(INTDIR)\fcgid_pm_win.obj" : $(SOURCE) "$(INTDIR)" ".\fcgid_config.h" SOURCE=.\fcgid_proc_win.c "$(INTDIR)\fcgid_proc_win.obj" : $(SOURCE) "$(INTDIR)" ".\fcgid_config.h" SOURCE=.\fcgid_proctbl_win.c "$(INTDIR)\fcgid_proctbl_win.obj" : $(SOURCE) "$(INTDIR)" ".\fcgid_config.h" SOURCE=.\fcgid_protocol.c "$(INTDIR)\fcgid_protocol.obj" : $(SOURCE) "$(INTDIR)" ".\fcgid_config.h" SOURCE=.\fcgid_spawn_ctl.c "$(INTDIR)\fcgid_spawn_ctl.obj" : $(SOURCE) "$(INTDIR)" ".\fcgid_config.h" SOURCE=.\mod_fcgid.c "$(INTDIR)\mod_fcgid.obj" : $(SOURCE) "$(INTDIR)" ".\fcgid_config.h" SOURCE=.\mod_fcgid.rc "$(INTDIR)\mod_fcgid.res" : $(SOURCE) "$(INTDIR)" ".\fcgid_config.h" $(RSC) $(RSC_PROJ) $(SOURCE) !ENDIF mod_fcgid-2.3.9/modules/fcgid/fcgid_proctbl_win.c0000644000175000017500000001471411552127660021714 0ustar trawicktrawick/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "fcgid_proctbl.h" #include "apr_shm.h" #include "fcgid_global.h" static apr_thread_mutex_t *g_sharelock = NULL; static fcgid_procnode *g_proc_array = NULL; /* Contain all process slot */ static fcgid_procnode *g_free_list_header = NULL; /* Attach to no process list */ static fcgid_procnode *g_busy_list_header = NULL; /* Attach to a working process list */ static fcgid_procnode *g_idle_list_header = NULL; /* Attach to an idle process list */ static fcgid_procnode *g_error_list_header = NULL; /* Attach to an error process list */ static fcgid_share *_global_memory = NULL; static fcgid_global_share *g_global_share = NULL; /* global information */ static size_t g_table_size = FCGID_PROC_TABLE_SIZE; apr_status_t proctable_pre_config(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp) { return APR_SUCCESS; } apr_status_t proctable_post_config(server_rec * main_server, apr_pool_t * pconf) { long shmem_size = sizeof(fcgid_share); fcgid_procnode *ptmpnode = NULL; int i; apr_status_t rv = APR_SUCCESS; if ((rv = apr_thread_mutex_create(&g_sharelock, APR_THREAD_MUTEX_DEFAULT, pconf)) != APR_SUCCESS) { /* Fatal error */ ap_log_error(APLOG_MARK, APLOG_EMERG, rv, main_server, "mod_fcgid: Can't create global mutex"); exit(1); } /* There is only one process in WinNT mpm, share memory is not necessary */ _global_memory = (fcgid_share *) apr_pcalloc(pconf, shmem_size); g_proc_array = _global_memory->procnode_array; g_global_share = &_global_memory->global; g_global_share->must_exit = 0; /* Init the array */ g_idle_list_header = g_proc_array; g_busy_list_header = g_idle_list_header + 1; g_error_list_header = g_busy_list_header + 1; g_free_list_header = g_error_list_header + 1; ptmpnode = g_free_list_header; for (i = 0; i < FCGID_MAX_APPLICATION; i++) { ptmpnode->next_index = ptmpnode - g_proc_array + 1; ptmpnode++; } return APR_SUCCESS; } apr_status_t proctable_child_init(server_rec * main_server, apr_pool_t * pchild) { return APR_SUCCESS; } static apr_status_t proctable_lock_internal(void) { return apr_thread_mutex_lock(g_sharelock); } static apr_status_t proctable_unlock_internal(void) { return apr_thread_mutex_unlock(g_sharelock); } fcgid_procnode *proctable_get_free_list(void) { return g_free_list_header; } fcgid_procnode *proctable_get_busy_list(void) { return g_busy_list_header; } fcgid_procnode *proctable_get_idle_list(void) { return g_idle_list_header; } fcgid_procnode *proctable_get_table_array(void) { return g_proc_array; } fcgid_procnode *proctable_get_error_list(void) { return g_error_list_header; } fcgid_global_share *proctable_get_globalshare(void) { return g_global_share; } size_t proctable_get_table_size(void) { return g_table_size; } void proctable_lock(request_rec *r) { /* Lock error is a fatal error */ apr_status_t rv; if ((rv = proctable_lock_internal()) != APR_SUCCESS) { ap_log_rerror(APLOG_MARK, APLOG_EMERG, rv, r, "mod_fcgid: can't lock process table"); exit(1); } } void proctable_unlock(request_rec *r) { /* Lock error is a fatal error */ apr_status_t rv; if ((rv = proctable_unlock_internal()) != APR_SUCCESS) { ap_log_rerror(APLOG_MARK, APLOG_EMERG, rv, r, "mod_fcgid: can't unlock process table"); exit(1); } } void proctable_pm_lock(server_rec * s) { /* Lock error is a fatal error */ apr_status_t rv; if ((rv = proctable_lock_internal()) != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s, "mod_fcgid: can't lock process table in PM"); exit(1); } } void proctable_pm_unlock(server_rec * s) { /* Lock error is a fatal error */ apr_status_t rv; if ((rv = proctable_unlock_internal()) != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s, "mod_fcgid: can't unlock process table in PM"); exit(1); } } void proctable_print_debug_info(server_rec * main_server) { int freecount = 0; fcgid_procnode *current_node; for (current_node = &g_proc_array[g_free_list_header->next_index]; current_node != g_proc_array; current_node = &g_proc_array[current_node->next_index]) freecount++; ap_log_error(APLOG_MARK, APLOG_WARNING, 0, main_server, "mod_fcgid: total node count: %d, free node count: %d", FCGID_MAX_APPLICATION, freecount); for (current_node = &g_proc_array[g_idle_list_header->next_index]; current_node != g_proc_array; current_node = &g_proc_array[current_node->next_index]) { ap_log_error(APLOG_MARK, APLOG_WARNING, 0, main_server, "mod_fcgid: idle node index: %d", current_node - g_proc_array); } for (current_node = &g_proc_array[g_busy_list_header->next_index]; current_node != g_proc_array; current_node = &g_proc_array[current_node->next_index]) { ap_log_error(APLOG_MARK, APLOG_WARNING, 0, main_server, "mod_fcgid: busy node index: %d", current_node - g_proc_array); } for (current_node = &g_proc_array[g_error_list_header->next_index]; current_node != g_proc_array; current_node = &g_proc_array[current_node->next_index]) { ap_log_error(APLOG_MARK, APLOG_WARNING, 0, main_server, "mod_fcgid: error node index: %d", current_node - g_proc_array); } } mod_fcgid-2.3.9/modules/fcgid/fcgid_config.h.in0000644000175000017500000000057411262733061021244 0ustar trawicktrawick/* modules/fcgid/fcgid_config.h.in used only for apxs builds */ /* Define to 1 if you have the `sys/file.h' header. */ #undef HAVE_SYS_FILE_H /* Define to 1 if you have the `sys/mman.h' header. */ #undef HAVE_SYS_MMAN_H /* Define to 1 if you have the `sys/mutex.h' header. */ #undef HAVE_SYS_MUTEX_H /* Define to 1 if you have the `sys/shm.h' header. */ #undef HAVE_SYS_SHM_H mod_fcgid-2.3.9/modules/fcgid/mod_fcgid.dsp0000644000175000017500000001450611251525446020514 0ustar trawicktrawick# Microsoft Developer Studio Project File - Name="mod_fcgid" - 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_fcgid - Win32 Release !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_fcgid.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_fcgid.mak" CFG="mod_fcgid - Win32 Release" !MESSAGE !MESSAGE Possible choices for configuration are: !MESSAGE !MESSAGE "mod_fcgid - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") !MESSAGE "mod_fcgid - 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_fcgid - 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 /MD /W3 /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FD /c # ADD CPP /nologo /MD /W3 /O2 /Oy- /Zi /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /I "$(APACHE2_HOME)/include" /Fd"Release\mod_fcgid_src" /FD /c # ADD BASE MTL /nologo /D "NDEBUG" /win32 # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" /I "../../srclib/apr/include" /I "$(APACHE2_HOME)/include" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib /nologo /subsystem:windows /dll /machine:I386 /out:"Release/mod_fcgid.so" # ADD LINK32 libhttpd.lib libaprutil-1.lib libapr-1.lib kernel32.lib /nologo /subsystem:windows /dll /debug /incremental:no /machine:I386 /out:"Release/mod_fcgid.so" /libpath:"..\..\Release" /libpath:"..\..\srclib\apr\Release" /libpath:"..\..\srclib\apr-util\Release" /libpath:"$(APACHE2_HOME)/lib" /base:"0x46430000" /opt:ref # Begin Special Build Tool TargetPath=.\Release\mod_fcgid.so SOURCE="$(InputPath)" PostBuild_Desc=Embed .manifest PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2 # End Special Build Tool !ELSEIF "$(CFG)" == "mod_fcgid - 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 /MDd /W3 /GX /Od /Zi /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FD /c # ADD CPP /nologo /MDd /W3 /GX /Od /Zi /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /I "../../include" /I "../../srclib/apr/include" /I "../../srclib/apr-util/include" /I "$(APACHE2_HOME)/include" /Fd"Debug\mod_fcgid_src" /FD /c # ADD BASE MTL /nologo /D "_DEBUG" /win32 # ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" /I "../../srclib/apr/include" /I "$(APACHE2_HOME)/include" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug /machine:I386 /out:"Debug/mod_fcgid.so" # ADD LINK32 libhttpd.lib libaprutil-1.lib libapr-1.lib kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug /machine:I386 /libpath:"..\..\Debug" /libpath:"..\..\srclib\apr\Debug" /libpath:"..\..\srclib\apr-util\Debug" /libpath:"$(APACHE2_HOME)/lib" /out:"Debug/mod_fcgid.so" /base:"0x46430000" # Begin Special Build Tool TargetPath=.\Debug\mod_fcgid.so SOURCE="$(InputPath)" PostBuild_Desc=Embed .manifest PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2 # End Special Build Tool !ENDIF # Begin Target # Name "mod_fcgid - Win32 Release" # Name "mod_fcgid - Win32 Debug" # Begin Source File SOURCE=.\fcgid_bridge.c # End Source File # Begin Source File SOURCE=.\fcgid_bridge.h # End Source File # Begin Source File SOURCE=.\fcgid_bucket.c # End Source File # Begin Source File SOURCE=.\fcgid_bucket.h # End Source File # Begin Source File SOURCE=.\fcgid_conf.c # End Source File # Begin Source File SOURCE=.\fcgid_conf.h # End Source File # Begin Source File SOURCE=.\fcgid_filter.c # End Source File # Begin Source File SOURCE=.\fcgid_filter.h # End Source File # Begin Source File SOURCE=.\fcgid_global.h # End Source File # Begin Source File SOURCE=.\fcgid_pm.h # End Source File # Begin Source File SOURCE=.\fcgid_pm_main.c # End Source File # Begin Source File SOURCE=.\fcgid_pm_main.h # End Source File # Begin Source File SOURCE=.\fcgid_pm_win.c # End Source File # Begin Source File SOURCE=.\fcgid_proc.h # End Source File # Begin Source File SOURCE=.\fcgid_proc_win.c # End Source File # Begin Source File SOURCE=.\fcgid_proctbl.h # End Source File # Begin Source File SOURCE=.\fcgid_proctbl_win.c # End Source File # Begin Source File SOURCE=.\fcgid_protocol.c # End Source File # Begin Source File SOURCE=.\fcgid_spawn_ctl.c # End Source File # Begin Source File SOURCE=.\fcgid_spawn_ctl.h # End Source File # Begin Source File SOURCE=.\mod_fcgid.c # End Source File # Begin Source File SOURCE=.\mod_fcgid.rc # End Source File # Begin Source File SOURCE=.\fcgid_config.h # End Source File # Begin Source File SOURCE=".\fcgid_config.h.in" !IF "$(CFG)" == "mod_fcgid - Win32 Release" # Begin Custom Build - Generating fcgid_config.h InputPath=".\fcgid_config.h.in" ".\fcgid_config.h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" echo /* No configuration */ > .\fcgid_config.h # End Custom Build !ELSEIF "$(CFG)" == "mod_fcgid - Win32 Debug" # Begin Custom Build - Generating fcgid_config.h InputPath=".\fcgid_config.h.in" ".\fcgid_config.h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" echo /* No configuration */ > .\fcgid_config.h # End Custom Build !ENDIF # End Source File # End Target # End Project mod_fcgid-2.3.9/modules/fcgid/CMakeLists.txt0000644000175000017500000000457112216606636020635 0ustar trawicktrawick# Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # Read the section on cmake builds in README-FCGID. PROJECT(mod_fcgid C) CMAKE_MINIMUM_REQUIRED(VERSION 2.8) IF(NOT EXISTS ${CMAKE_INSTALL_PREFIX}/lib/libhttpd.lib) MESSAGE(FATAL_ERROR "libhttpd.lib was not found in prefix ${CMAKE_INSTALL_PREFIX}") ENDIF() # Select APR trunk (libapr-2.lib) if it exists in PREFIX/lib; otherwise, select # APR 1.x + APR-util 1.x IF(EXISTS "${CMAKE_INSTALL_PREFIX}/lib/libapr-2.lib") SET(apr_libraries ${CMAKE_INSTALL_PREFIX}/lib/libapr-2.lib) ELSEIF(EXISTS "${CMAKE_INSTALL_PREFIX}/lib/libapr-1.lib") SET(apr_libraries ${CMAKE_INSTALL_PREFIX}/lib/libapr-1.lib ${CMAKE_INSTALL_PREFIX}/lib/libaprutil-1.lib) ELSE() MESSAGE(FATAL_ERROR "APR libraries were not found in prefix ${CMAKE_INSTALL_PREFIX}") ENDIF() # Misc. options OPTION(INSTALL_PDB "Install .pdb file (if generated)" ON) SET(mod_fcgid_sources fcgid_bridge.c fcgid_bucket.c fcgid_conf.c fcgid_filter.c fcgid_pm_main.c fcgid_pm_win.c fcgid_proc_win.c fcgid_proctbl_win.c fcgid_protocol.c fcgid_spawn_ctl.c mod_fcgid.c mod_fcgid.rc ) INCLUDE_DIRECTORIES(${CMAKE_INSTALL_PREFIX}/include) ADD_LIBRARY(mod_fcgid SHARED ${mod_fcgid_sources}) # magic base address taken from traditional Windows build SET_TARGET_PROPERTIES(mod_fcgid PROPERTIES SUFFIX .so LINK_FLAGS /base:0x46430000) TARGET_LINK_LIBRARIES(mod_fcgid ${CMAKE_INSTALL_PREFIX}/lib/libhttpd.lib ${apr_libraries}) INSTALL(TARGETS mod_fcgid RUNTIME DESTINATION modules) IF(INSTALL_PDB) INSTALL(FILES ${CMAKE_BINARY_DIR}/mod_fcgid.pdb DESTINATION modules CONFIGURATIONS RelWithDebInfo Debug) ENDIF() mod_fcgid-2.3.9/modules/fcgid/fcgid_protocol.c0000644000175000017500000001661112216034065021223 0ustar trawicktrawick/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "httpd.h" #include "apr_strings.h" #include "apr_portable.h" #include "apr_pools.h" #include "fcgid_global.h" #include "fcgid_protocol.h" static size_t init_environment(char *buf, char **envp) { char *spliter; apr_size_t namelen, valuelen; char *cur_buf = buf; size_t buffer_size = 0; for (; *envp != NULL; envp++) { spliter = strchr(*envp, '='); if (spliter == NULL) continue; namelen = spliter - *envp; valuelen = strlen(spliter + 1); /* Put name length to buffer */ if (namelen < 0x80) { if (!buf) buffer_size++; else *cur_buf++ = (unsigned char) namelen; } else { if (!buf) buffer_size += 4; else { *cur_buf++ = (unsigned char) ((namelen >> 24) | 0x80); *cur_buf++ = (unsigned char) (namelen >> 16); *cur_buf++ = (unsigned char) (namelen >> 8); *cur_buf++ = (unsigned char) namelen; } } /* Put value length to buffer */ if (valuelen < 0x80) { if (!buf) buffer_size++; else *cur_buf++ = (unsigned char) valuelen; } else { if (!buf) buffer_size += 4; else { *cur_buf++ = (unsigned char) ((valuelen >> 24) | 0x80); *cur_buf++ = (unsigned char) (valuelen >> 16); *cur_buf++ = (unsigned char) (valuelen >> 8); *cur_buf++ = (unsigned char) valuelen; } } /* Now the name and body buffer */ if (!buf) { buffer_size += namelen; buffer_size += valuelen; } else { memcpy(cur_buf, *envp, namelen); cur_buf += namelen; memcpy(cur_buf, spliter + 1, valuelen); cur_buf += valuelen; } } return buffer_size; } static void init_begin_request_body(int role, FCGI_BeginRequestBody * begin_request_body) { begin_request_body->roleB1 = (unsigned char) (((role >> 8) & 0xff)); begin_request_body->roleB0 = (unsigned char) (role & 0xff); begin_request_body->flags = 0; memset(begin_request_body->reserved, 0, sizeof(begin_request_body->reserved)); } int init_header(int type, int requestId, apr_size_t contentLength, apr_size_t paddingLength, FCGI_Header * header) { if (contentLength > 65535 || paddingLength > 255) return 0; header->version = FCGI_VERSION_1; header->type = (unsigned char) type; header->requestIdB1 = (unsigned char) ((requestId >> 8) & 0xff); header->requestIdB0 = (unsigned char) (requestId & 0xff); header->contentLengthB1 = (unsigned char) ((contentLength >> 8) & 0xff); header->contentLengthB0 = (unsigned char) ((contentLength) & 0xff); header->paddingLength = (unsigned char) paddingLength; header->reserved = 0; return 1; } int build_begin_block(int role, request_rec * r, apr_bucket_alloc_t * alloc, apr_bucket_brigade * request_brigade) { /* Alloc memory for begin request header & body */ FCGI_Header *begin_request_header = apr_bucket_alloc(sizeof(FCGI_Header), alloc); FCGI_BeginRequestBody *begin_request_body = apr_bucket_alloc(sizeof(FCGI_BeginRequestBody), alloc); apr_bucket *bucket_header = apr_bucket_heap_create((const char *) begin_request_header, sizeof(*begin_request_header), apr_bucket_free, alloc); apr_bucket *bucket_body = apr_bucket_heap_create((const char *) begin_request_body, sizeof(*begin_request_body), apr_bucket_free, alloc); /* Initialize begin request header and body */ if (!init_header(FCGI_BEGIN_REQUEST, 1, sizeof(FCGI_BeginRequestBody), 0, begin_request_header)) { ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, "mod_fcgid: can't init begin request header"); return 0; } init_begin_request_body(role, begin_request_body); /* Append the header and body to request brigade */ APR_BRIGADE_INSERT_TAIL(request_brigade, bucket_header); APR_BRIGADE_INSERT_TAIL(request_brigade, bucket_body); return 1; } int build_env_block(request_rec * r, char **envp, apr_bucket_alloc_t * alloc, apr_bucket_brigade * request_brigade) { /* Get the size of the destination buffer */ apr_size_t bufsize = init_environment(NULL, envp); /* Alloc memory for environment header and body */ FCGI_Header *env_request_header = apr_bucket_alloc(sizeof(FCGI_Header), alloc); FCGI_Header *env_empty_header = apr_bucket_alloc(sizeof(FCGI_Header), alloc); char *buf = apr_bucket_alloc(bufsize, alloc); apr_bucket *bucket_header = apr_bucket_heap_create((const char *) env_request_header, sizeof (*env_request_header), apr_bucket_free, alloc); apr_bucket *bucket_body = apr_bucket_heap_create(buf, bufsize, apr_bucket_free, alloc); apr_bucket *bucket_empty_header = apr_bucket_heap_create((const char *) env_empty_header, sizeof (*env_empty_header), apr_bucket_free, alloc); /* Initialize header and body */ if (!init_header(FCGI_PARAMS, 1, bufsize, 0, env_request_header) || !init_header(FCGI_PARAMS, 1, 0, 0, env_empty_header)) { ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, "mod_fcgid: can't init env request header"); return 0; } init_environment(buf, envp); /* Append the header and body to request brigade */ APR_BRIGADE_INSERT_TAIL(request_brigade, bucket_header); APR_BRIGADE_INSERT_TAIL(request_brigade, bucket_body); APR_BRIGADE_INSERT_TAIL(request_brigade, bucket_empty_header); return 1; } mod_fcgid-2.3.9/modules/fcgid/fcgid_pm_unix.c0000644000175000017500000004556312223417144021052 0ustar trawicktrawick/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "unixd.h" #include "ap_mpm.h" #include "apr_thread_proc.h" #include "apr_strings.h" #include "apr_queue.h" #include "apr_global_mutex.h" #include "apr_version.h" #if APR_MAJOR_VERSION < 2 #include "apr_support.h" #endif #include "http_config.h" #include "fcgid_pm.h" #include "fcgid_pm_main.h" #include "fcgid_conf.h" #include "fcgid_proctbl.h" #include "fcgid_spawn_ctl.h" #include "fcgid_mutex.h" #include #if MODULE_MAGIC_NUMBER_MAJOR >= 20090209 #include "mod_unixd.h" #endif #if MODULE_MAGIC_NUMBER_MAJOR < 20081201 #define ap_unixd_config unixd_config #define ap_unixd_setup_child unixd_setup_child #endif /* The APR other-child API doesn't tell us how the daemon exited * (SIGSEGV vs. exit(1)). The other-child maintenance function * needs to decide whether to restart the daemon after a failure * based on whether or not it exited due to a fatal startup error * or something that happened at steady-state. This exit status * is unlikely to collide with exit signals. */ #define DAEMON_STARTUP_ERROR 254 static apr_status_t create_process_manager(server_rec * main_server, apr_pool_t * configpool); static int g_wakeup_timeout = 0; static apr_proc_t *g_process_manager = NULL; static apr_file_t *g_pm_read_pipe = NULL; static apr_file_t *g_pm_write_pipe = NULL; static apr_file_t *g_ap_write_pipe = NULL; static apr_file_t *g_ap_read_pipe = NULL; static apr_global_mutex_t *g_pipelock = NULL; static const char *g_pipelock_name; static const char *g_pipelock_mutex_type = "fcgid-pipe"; static int volatile g_caughtSigTerm = 0; static pid_t g_pm_pid; static void signal_handler(int signo) { /* Sanity check, Make sure I am not the subprocess. A subprocess may get signale after fork() and before execve() */ if (getpid() != g_pm_pid) { exit(0); return; } if ((signo == SIGTERM) || (signo == SIGUSR1) || (signo == SIGHUP)) { g_caughtSigTerm = 1; /* Tell the world it's time to die */ proctable_get_globalshare()->must_exit = 1; } } static apr_status_t init_signal(server_rec * main_server) { 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(APLOG_MARK, APLOG_ERR, errno, main_server, "mod_fcgid: Can't install SIGTERM handler"); return APR_EGENERAL; } /* Httpd restart */ if (sigaction(SIGHUP, &sa, NULL) < 0) { ap_log_error(APLOG_MARK, APLOG_ERR, errno, main_server, "mod_fcgid: Can't install SIGHUP handler"); return APR_EGENERAL; } /* Httpd graceful restart */ if (sigaction(SIGUSR1, &sa, NULL) < 0) { ap_log_error(APLOG_MARK, APLOG_ERR, errno, main_server, "mod_fcgid: Can't install SIGUSR1 handler"); return APR_EGENERAL; } /* Ignore SIGPIPE */ sa.sa_handler = SIG_IGN; if (sigaction(SIGPIPE, &sa, NULL) < 0) { ap_log_error(APLOG_MARK, APLOG_ERR, errno, main_server, "mod_fcgid: Can't install SIGPIPE handler"); return APR_EGENERAL; } return APR_SUCCESS; } static void fcgid_maint(int reason, void *data, apr_wait_t status) { apr_proc_t *proc = data; int mpm_state; switch (reason) { case APR_OC_REASON_DEATH: apr_proc_other_child_unregister(data); if (ap_mpm_query(AP_MPMQ_MPM_STATE, &mpm_state) == APR_SUCCESS && mpm_state != AP_MPMQ_STOPPING) { if (status == DAEMON_STARTUP_ERROR) { ap_log_error(APLOG_MARK, APLOG_CRIT, 0, NULL, "mod_fcgid: fcgid process manager failed to initialize; " "stopping httpd"); /* mod_fcgid requests will hang due to lack of a process manager; * try to terminate httpd */ kill(getpid(), SIGTERM); } else { ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "mod_fcgid: fcgid process manager died, restarting the server"); /* HACK: I can't just call create_process_manager() to restart a process manager, because it will use the dirty share memory, I have to kill myself a SIGHUP, to make a clean restart */ /* FIXME: This is the httpd parent; mod_fcgid is doing a hard * restart of the server! */ if (kill(getpid(), SIGHUP) < 0) { ap_log_error(APLOG_MARK, APLOG_EMERG, apr_get_os_error(), NULL, "mod_fcgid: can't send SIGHUP to self"); exit(0); } } } break; case APR_OC_REASON_RESTART: apr_proc_other_child_unregister(data); break; case APR_OC_REASON_LOST: apr_proc_other_child_unregister(data); /* It hack here too, a note above */ /* FIXME: This is the httpd parent; mod_fcgid is doing a hard * restart of the server! */ if (kill(getpid(), SIGHUP) < 0) { ap_log_error(APLOG_MARK, APLOG_EMERG, apr_get_os_error(), NULL, "mod_fcgid: can't send SIGHUP to self"); exit(0); } break; case APR_OC_REASON_UNREGISTER: /* I don't think it's going to happen */ kill(proc->pid, SIGHUP); break; } } static int set_group_privs(void) { if (!geteuid()) { const char *name; /* Get username if passed as a uid */ if (ap_unixd_config.user_name[0] == '#') { struct passwd *ent; uid_t uid = atoi(&ap_unixd_config.user_name[1]); if ((ent = getpwuid(uid)) == NULL) { ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL, "getpwuid: couldn't determine user name from uid %u, " "you probably need to modify the User directive", (unsigned) uid); return -1; } name = ent->pw_name; } else name = ap_unixd_config.user_name; #if !defined(OS2) && !defined(TPF) /* OS/2 and TPF don't support groups. */ /* * Set the GID before initgroups(), since on some platforms * setgid() is known to zap the group list. */ if (setgid(ap_unixd_config.group_id) == -1) { ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL, "setgid: unable to set group id to Group %u", (unsigned) ap_unixd_config.group_id); return -1; } /* Reset `groups' attributes. */ if (initgroups(name, ap_unixd_config.group_id) == -1) { ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL, "initgroups: unable to set groups for User %s " "and Group %u", name, (unsigned) ap_unixd_config.group_id); return -1; } #endif /* !defined(OS2) && !defined(TPF) */ } return 0; } /* Base on ap_unixd_setup_child() */ static int suexec_setup_child(void) { if (set_group_privs()) { exit(-1); } /* Only try to switch if we're running as root */ if (!geteuid() && (seteuid(ap_unixd_config.user_id) == -1)) { ap_log_error(APLOG_MARK, APLOG_ALERT, errno, NULL, "seteuid: unable to change to uid %ld", (long) ap_unixd_config.user_id); exit(-1); } return 0; } static apr_status_t create_process_manager(server_rec * main_server, apr_pool_t * configpool) { apr_status_t rv; g_process_manager = (apr_proc_t *) apr_pcalloc(configpool, sizeof(*g_process_manager)); rv = apr_proc_fork(g_process_manager, configpool); if (rv == APR_INCHILD) { /* I am the child */ g_pm_pid = getpid(); ap_log_error(APLOG_MARK, APLOG_INFO, 0, main_server, "mod_fcgid: Process manager %" APR_PID_T_FMT " started", getpid()); if ((rv = init_signal(main_server)) != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_EMERG, rv, main_server, "mod_fcgid: can't install signal handler, exiting now"); exit(DAEMON_STARTUP_ERROR); } /* If running as root, switch to configured user. * * When running children via suexec, only the effective uid is * switched, so that the PM can return to euid 0 to kill child * processes. * * When running children as the configured user, the real uid * is switched. */ if (ap_unixd_config.suexec_enabled) { if (getuid() != 0) { ap_log_error(APLOG_MARK, APLOG_EMERG, 0, main_server, "mod_fcgid: current user is not root while suexec is enabled, exiting now"); exit(DAEMON_STARTUP_ERROR); } suexec_setup_child(); } else ap_unixd_setup_child(); apr_file_pipe_timeout_set(g_pm_read_pipe, apr_time_from_sec(g_wakeup_timeout)); apr_file_close(g_ap_write_pipe); apr_file_close(g_ap_read_pipe); /* Initialize spawn controler */ spawn_control_init(main_server, configpool); pm_main(main_server, configpool); ap_log_error(APLOG_MARK, APLOG_INFO, 0, main_server, "mod_fcgid: Process manager %" APR_PID_T_FMT " stopped", getpid()); exit(0); } else if (rv != APR_INPARENT) { ap_log_error(APLOG_MARK, APLOG_EMERG, rv, main_server, "mod_fcgid: Create process manager error"); exit(1); } /* I am the parent I will send the stop signal in procmgr_stop_procmgr() */ apr_pool_note_subprocess(configpool, g_process_manager, APR_KILL_ONLY_ONCE); apr_proc_other_child_register(g_process_manager, fcgid_maint, g_process_manager, NULL, configpool); return APR_SUCCESS; } apr_status_t procmgr_child_init(server_rec * main_server, apr_pool_t * configpool) { apr_status_t rv; if ((rv = apr_global_mutex_child_init(&g_pipelock, g_pipelock_name, main_server->process->pconf)) != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_EMERG, rv, main_server, "mod_fcgid: apr_global_mutex_child_init error for pipe mutex"); exit(1); } return APR_SUCCESS; } apr_status_t procmgr_pre_config(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp) { return fcgid_mutex_register(g_pipelock_mutex_type, p); } apr_status_t procmgr_post_config(server_rec * main_server, apr_pool_t * configpool) { apr_status_t rv; apr_finfo_t finfo; fcgid_server_conf *sconf = ap_get_module_config(main_server->module_config, &fcgid_module); /* Calculate procmgr_fetch_cmd wake up interval */ g_wakeup_timeout = fcgid_min(sconf->error_scan_interval, sconf->busy_scan_interval); g_wakeup_timeout = fcgid_min(sconf->idle_scan_interval, g_wakeup_timeout); if (g_wakeup_timeout == 0) g_wakeup_timeout = 1; /* Make it reasonable */ rv = apr_stat(&finfo, sconf->sockname_prefix, APR_FINFO_USER, configpool); if (rv != APR_SUCCESS) { /* Make dir for unix domain socket */ if ((rv = apr_dir_make_recursive(sconf->sockname_prefix, APR_UREAD | APR_UWRITE | APR_UEXECUTE, configpool)) != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_ERR, rv, main_server, "mod_fcgid: Can't create unix socket dir %s", sconf->sockname_prefix); exit(1); } /* Child processes need to be able to create sockets in the unix * socket dir. Change the ownership to the child user only if * running as root and we just successfully created the directory * (avoiding any concerns about changing the target of a link * created by another user). * * If the directory already existed and was owned by a different user, * FastCGI requests will fail at steady state, and manual intervention * will be required. */ if (!geteuid()) { if (chown(sconf->sockname_prefix, ap_unixd_config.user_id, -1) < 0) { ap_log_error(APLOG_MARK, APLOG_ERR, errno, main_server, "mod_fcgid: Can't set ownership of unix socket dir %s", sconf->sockname_prefix); exit(1); } } } /* Create pipes to communicate between process manager and apache */ if ((rv = apr_file_pipe_create(&g_pm_read_pipe, &g_ap_write_pipe, configpool)) != APR_SUCCESS || (rv = apr_file_pipe_create(&g_ap_read_pipe, &g_pm_write_pipe, configpool))) { ap_log_error(APLOG_MARK, APLOG_ERR, rv, main_server, "mod_fcgid: Can't create pipe between PM and stub"); return rv; } /* Create mutex for pipe reading and writing */ rv = fcgid_mutex_create(&g_pipelock, &g_pipelock_name, g_pipelock_mutex_type, main_server->process->pconf, main_server); if (rv != APR_SUCCESS) { exit(1); } /* Create process manager process */ return create_process_manager(main_server, configpool); } void procmgr_init_spawn_cmd(fcgid_command * command, request_rec * r, fcgid_cmd_conf *cmd_conf) { ap_unix_identity_t *ugid; fcgid_server_conf *sconf = ap_get_module_config(r->server->module_config, &fcgid_module); /* suEXEC check */ if ((ugid = ap_run_get_suexec_identity(r))) { command->uid = ugid->uid; command->gid = ugid->gid; command->userdir = ugid->userdir; } else { command->uid = (uid_t) - 1; command->gid = (gid_t) - 1; command->userdir = 0; } /* no truncation should ever occur */ AP_DEBUG_ASSERT(sizeof command->cgipath > strlen(cmd_conf->cgipath)); apr_cpystrn(command->cgipath, cmd_conf->cgipath, sizeof command->cgipath); AP_DEBUG_ASSERT(sizeof command->cmdline > strlen(cmd_conf->cmdline)); apr_cpystrn(command->cmdline, cmd_conf->cmdline, sizeof command->cmdline); command->deviceid = cmd_conf->deviceid; command->inode = cmd_conf->inode; command->vhost_id = sconf->vhost_id; if (r->server->server_hostname) { apr_cpystrn(command->server_hostname, r->server->server_hostname, sizeof command->server_hostname); } else { command->server_hostname[0] = '\0'; } get_cmd_options(r, command->cgipath, &command->cmdopts, &command->cmdenv); } apr_status_t procmgr_send_spawn_cmd(fcgid_command * command, request_rec * r) { apr_status_t rv; char notifybyte; apr_size_t nbytes = sizeof(*command); /* Get the global mutex before posting the request */ if ((rv = apr_global_mutex_lock(g_pipelock)) != APR_SUCCESS) { ap_log_rerror(APLOG_MARK, APLOG_EMERG, rv, r, "mod_fcgid: can't get pipe mutex"); exit(0); } if ((rv = apr_file_write_full(g_ap_write_pipe, command, nbytes, NULL)) != APR_SUCCESS) { /* Just print some error log and fall through */ ap_log_rerror(APLOG_MARK, APLOG_WARNING, rv, r, "mod_fcgid: can't write spawn command"); } else { /* Wait the finish notify while send the request successfully */ nbytes = sizeof(notifybyte); if ((rv = apr_file_read(g_ap_read_pipe, ¬ifybyte, &nbytes)) != APR_SUCCESS) { ap_log_rerror(APLOG_MARK, APLOG_WARNING, rv, r, "mod_fcgid: can't get notify from process manager"); } } /* Release the lock */ if ((rv = apr_global_mutex_unlock(g_pipelock)) != APR_SUCCESS) { ap_log_rerror(APLOG_MARK, APLOG_EMERG, rv, r, "mod_fcgid: can't release pipe mutex"); exit(0); } return APR_SUCCESS; } apr_status_t procmgr_finish_notify(server_rec * main_server) { apr_status_t rv; char notifybyte = 'p'; apr_size_t nbytes = sizeof(notifybyte); if ((rv = apr_file_write(g_pm_write_pipe, ¬ifybyte, &nbytes)) != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_WARNING, rv, main_server, "mod_fcgid: can't send notify from process manager"); } return rv; } #define FOR_READ 1 apr_status_t procmgr_fetch_cmd(fcgid_command * command, server_rec * main_server) { apr_status_t rv; /* Sanity check */ if (!g_pm_read_pipe) return APR_EPIPE; /* Wait for next command */ #if APR_MAJOR_VERSION < 2 rv = apr_wait_for_io_or_timeout(g_pm_read_pipe, NULL, FOR_READ); #else rv = apr_file_pipe_wait(g_pm_read_pipe, APR_WAIT_READ); #endif /* Log any unexpect result */ if (rv != APR_SUCCESS && !APR_STATUS_IS_TIMEUP(rv)) { ap_log_error(APLOG_MARK, APLOG_WARNING, rv, main_server, "mod_fcgid: error while waiting for message from pipe"); return rv; } /* Timeout */ if (rv != APR_SUCCESS) return rv; return apr_file_read_full(g_pm_read_pipe, command, sizeof(*command), NULL); } int procmgr_must_exit() { return g_caughtSigTerm; } apr_status_t procmgr_stop_procmgr(void *server) { return APR_SUCCESS; } mod_fcgid-2.3.9/modules/fcgid/fcgid_conf.c0000644000175000017500000011237412216034065020312 0ustar trawicktrawick/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "ap_config.h" #include "ap_mmn.h" #include "apr_strings.h" #include "apr_hash.h" #include "apr_lib.h" #include "apr_tables.h" #include "apr_version.h" #include "http_main.h" #include "httpd.h" #include "http_config.h" #include "fcgid_global.h" #include "fcgid_conf.h" #ifndef DEFAULT_REL_RUNTIMEDIR /* Win32, etc. */ #define DEFAULT_REL_RUNTIMEDIR "logs" #endif #define DEFAULT_IDLE_TIMEOUT 300 #define DEFAULT_IDLE_SCAN_INTERVAL 120 #define DEFAULT_BUSY_TIMEOUT 300 #define DEFAULT_BUSY_SCAN_INTERVAL 120 #define DEFAULT_ERROR_SCAN_INTERVAL 3 #define DEFAULT_ZOMBIE_SCAN_INTERVAL 3 #define DEFAULT_PROC_LIFETIME (60*60) #define DEFAULT_SOCKET_PREFIX DEFAULT_REL_RUNTIMEDIR "/fcgidsock" #define DEFAULT_SHM_PATH DEFAULT_REL_RUNTIMEDIR "/fcgid_shm" #define DEFAULT_SPAWNSOCRE_UPLIMIT 10 #define DEFAULT_SPAWN_SCORE 1 #define DEFAULT_TERMINATION_SCORE 2 #define DEFAULT_TIME_SCORE 1 #define DEFAULT_MAX_PROCESS_COUNT 1000 #define DEFAULT_MAX_CLASS_PROCESS_COUNT 100 #define DEFAULT_MIN_CLASS_PROCESS_COUNT 3 #define DEFAULT_IPC_CONNECT_TIMEOUT 3 #define DEFAULT_IPC_COMM_TIMEOUT 40 #define DEFAULT_OUTPUT_BUFFERSIZE 65536 #define DEFAULT_MAX_REQUESTS_PER_PROCESS 0 /* by default, allow spooling of request bodies up to * 128k (first 64k in memory) */ #define DEFAULT_MAX_REQUEST_LEN (1024*128) #define DEFAULT_MAX_MEM_REQUEST_LEN (1024*64) #define DEFAULT_WRAPPER_KEY "ALL" #define WRAPPER_FLAG_VIRTUAL "virtual" void *create_fcgid_server_config(apr_pool_t * p, server_rec * s) { fcgid_server_conf *config = apr_pcalloc(p, sizeof(*config)); static int vhost_id = 0; /* allow vhost comparison even when some mass-vhost module * makes a copy of the server_rec to override docroot or * other such settings */ ++vhost_id; config->vhost_id = vhost_id; if (!s->is_virtual) { config->busy_scan_interval = DEFAULT_BUSY_SCAN_INTERVAL; config->error_scan_interval = DEFAULT_ERROR_SCAN_INTERVAL; config->idle_scan_interval = DEFAULT_IDLE_SCAN_INTERVAL; config->max_process_count = DEFAULT_MAX_PROCESS_COUNT; config->shmname_path = ap_server_root_relative(p, DEFAULT_SHM_PATH); config->sockname_prefix = ap_server_root_relative(p, DEFAULT_SOCKET_PREFIX); config->spawn_score = DEFAULT_SPAWN_SCORE; config->spawnscore_uplimit = DEFAULT_SPAWNSOCRE_UPLIMIT; config->termination_score = DEFAULT_TERMINATION_SCORE; config->time_score = DEFAULT_TIME_SCORE; config->zombie_scan_interval = DEFAULT_ZOMBIE_SCAN_INTERVAL; } /* Redundant; pcalloc creates this structure; * config->default_init_env = NULL; * config->pass_headers = NULL; * config->php_fix_pathinfo_enable = 0; * config->*_set = 0; */ config->cmdopts_hash = apr_hash_make(p); config->ipc_comm_timeout = DEFAULT_IPC_COMM_TIMEOUT; config->ipc_connect_timeout = DEFAULT_IPC_CONNECT_TIMEOUT; config->max_mem_request_len = DEFAULT_MAX_MEM_REQUEST_LEN; config->max_request_len = DEFAULT_MAX_REQUEST_LEN; config->max_requests_per_process = DEFAULT_MAX_REQUESTS_PER_PROCESS; config->output_buffersize = DEFAULT_OUTPUT_BUFFERSIZE; config->max_class_process_count = DEFAULT_MAX_CLASS_PROCESS_COUNT; config->min_class_process_count = DEFAULT_MIN_CLASS_PROCESS_COUNT; config->busy_timeout = DEFAULT_BUSY_TIMEOUT; config->idle_timeout = DEFAULT_IDLE_TIMEOUT; config->proc_lifetime = DEFAULT_PROC_LIFETIME; return config; } #define MERGE_SCALAR(base, local, merged, field) \ if (!(local)->field##_set) { \ merged->field = base->field; \ } void *merge_fcgid_server_config(apr_pool_t * p, void *basev, void *locv) { fcgid_server_conf *base = (fcgid_server_conf *) basev; fcgid_server_conf *local = (fcgid_server_conf *) locv; fcgid_server_conf *merged = (fcgid_server_conf *) apr_pmemdup(p, local, sizeof(fcgid_server_conf)); merged->cmdopts_hash = apr_hash_overlay(p, local->cmdopts_hash, base->cmdopts_hash); /* Merge environment variables */ if (base->default_init_env == NULL) { /* merged already set to local */ } else if (local->default_init_env == NULL) { merged->default_init_env = base->default_init_env; } else { merged->default_init_env = apr_table_copy(p, base->default_init_env); apr_table_overlap(merged->default_init_env, local->default_init_env, APR_OVERLAP_TABLES_SET); } /* Merge pass headers */ if (base->pass_headers == NULL) { /* merged already set to local */ } else if (local->pass_headers == NULL) { merged->pass_headers = base->pass_headers; } else { merged->pass_headers = apr_array_append(p, base->pass_headers, local->pass_headers); } /* Merge the scalar settings */ MERGE_SCALAR(base, local, merged, ipc_comm_timeout); MERGE_SCALAR(base, local, merged, ipc_connect_timeout); MERGE_SCALAR(base, local, merged, max_mem_request_len); MERGE_SCALAR(base, local, merged, max_request_len); MERGE_SCALAR(base, local, merged, max_requests_per_process); MERGE_SCALAR(base, local, merged, output_buffersize); MERGE_SCALAR(base, local, merged, max_class_process_count); MERGE_SCALAR(base, local, merged, min_class_process_count); MERGE_SCALAR(base, local, merged, busy_timeout); MERGE_SCALAR(base, local, merged, idle_timeout); MERGE_SCALAR(base, local, merged, proc_lifetime); return merged; } void *create_fcgid_dir_config(apr_pool_t * p, char *dummy) { fcgid_dir_conf *config = apr_pcalloc(p, sizeof(fcgid_dir_conf)); config->wrapper_info_hash = apr_hash_make(p); /* config->authenticator_info = NULL; */ config->authenticator_authoritative = 1; /* config->authorizer_info = NULL; */ config->authorizer_authoritative = 1; /* config->access_info = NULL; */ config->access_authoritative = 1; return (void *) config; } void *merge_fcgid_dir_config(apr_pool_t *p, void *basev, void *locv) { fcgid_dir_conf *base = (fcgid_dir_conf *) basev; fcgid_dir_conf *local = (fcgid_dir_conf *) locv; fcgid_dir_conf *merged = (fcgid_dir_conf *) apr_pmemdup(p, local, sizeof(fcgid_dir_conf)); merged->wrapper_info_hash = apr_hash_overlay(p, local->wrapper_info_hash, base->wrapper_info_hash); if (!local->authenticator_info) { merged->authenticator_info = base->authenticator_info; } if (!local->authorizer_info) { merged->authorizer_info = base->authorizer_info; } if (!local->access_info) { merged->access_info = base->access_info; } MERGE_SCALAR(base, local, merged, authenticator_authoritative); MERGE_SCALAR(base, local, merged, authorizer_authoritative); MERGE_SCALAR(base, local, merged, access_authoritative); return merged; } const char *set_idle_timeout(cmd_parms * cmd, void *dummy, const char *arg) { server_rec *s = cmd->server; fcgid_server_conf *config = ap_get_module_config(s->module_config, &fcgid_module); config->idle_timeout = atol(arg); config->idle_timeout_set = 1; return NULL; } const char *set_idle_scan_interval(cmd_parms * cmd, void *dummy, const char *arg) { server_rec *s = cmd->server; fcgid_server_conf *config = ap_get_module_config(s->module_config, &fcgid_module); const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); if (err != NULL) { return err; } config->idle_scan_interval = atol(arg); return NULL; } const char *set_busy_timeout(cmd_parms * cmd, void *dummy, const char *arg) { server_rec *s = cmd->server; fcgid_server_conf *config = ap_get_module_config(s->module_config, &fcgid_module); config->busy_timeout = atol(arg); config->busy_timeout_set = 1; return NULL; } const char *set_busy_scan_interval(cmd_parms * cmd, void *dummy, const char *arg) { server_rec *s = cmd->server; fcgid_server_conf *config = ap_get_module_config(s->module_config, &fcgid_module); const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); if (err != NULL) { return err; } config->busy_scan_interval = atol(arg); return NULL; } const char *set_proc_lifetime(cmd_parms * cmd, void *dummy, const char *arg) { server_rec *s = cmd->server; fcgid_server_conf *config = ap_get_module_config(s->module_config, &fcgid_module); config->proc_lifetime = atol(arg); config->proc_lifetime_set = 1; return NULL; } const char *set_error_scan_interval(cmd_parms * cmd, void *dummy, const char *arg) { server_rec *s = cmd->server; fcgid_server_conf *config = ap_get_module_config(s->module_config, &fcgid_module); const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); if (err != NULL) { return err; } config->error_scan_interval = atol(arg); return NULL; } const char *set_zombie_scan_interval(cmd_parms * cmd, void *dummy, const char *arg) { server_rec *s = cmd->server; fcgid_server_conf *config = ap_get_module_config(s->module_config, &fcgid_module); const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); if (err != NULL) { return err; } config->zombie_scan_interval = atol(arg); return NULL; } const char *set_socketpath(cmd_parms * cmd, void *dummy, const char *arg) { server_rec *s = cmd->server; fcgid_server_conf *config = ap_get_module_config(s->module_config, &fcgid_module); const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); if (err != NULL) { return err; } config->sockname_prefix = ap_server_root_relative(cmd->pool, arg); if (!config->sockname_prefix) return "Invalid socket path"; return NULL; } const char *set_shmpath(cmd_parms * cmd, void *dummy, const char *arg) { server_rec *s = cmd->server; fcgid_server_conf *config = ap_get_module_config(s->module_config, &fcgid_module); const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); if (err != NULL) { return err; } config->shmname_path = ap_server_root_relative(cmd->pool, arg); if (!config->shmname_path) return "Invalid shmname path"; return NULL; } const char *set_spawnscore_uplimit(cmd_parms * cmd, void *dummy, const char *arg) { server_rec *s = cmd->server; fcgid_server_conf *config = ap_get_module_config(s->module_config, &fcgid_module); const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); if (err != NULL) { return err; } config->spawnscore_uplimit = atol(arg); return NULL; } static int strtoff(apr_off_t *val, const char *arg) { char *errp; #if APR_MAJOR_VERSION < 1 *val = (apr_off_t)strtol(arg, &errp, 10); if (*errp) { return 1; } #else if (APR_SUCCESS != apr_strtoff(val, arg, &errp, 10) || *errp) { return 1; } #endif return 0; } const char *set_max_request_len(cmd_parms * cmd, void *dummy, const char *arg) { server_rec *s = cmd->server; fcgid_server_conf *config = ap_get_module_config(s->module_config, &fcgid_module); if (strtoff(&config->max_request_len, arg) || config->max_request_len < 0) { return "FcgidMaxRequestLen requires a non-negative integer."; } config->max_request_len_set = 1; return NULL; } const char *set_max_mem_request_len(cmd_parms * cmd, void *dummy, const char *arg) { server_rec *s = cmd->server; fcgid_server_conf *config = ap_get_module_config(s->module_config, &fcgid_module); config->max_mem_request_len = atol(arg); config->max_mem_request_len_set = 1; return NULL; } const char *set_spawn_score(cmd_parms * cmd, void *dummy, const char *arg) { server_rec *s = cmd->server; fcgid_server_conf *config = ap_get_module_config(s->module_config, &fcgid_module); const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); if (err != NULL) { return err; } config->spawn_score = atol(arg); return NULL; } const char *set_time_score(cmd_parms * cmd, void *dummy, const char *arg) { server_rec *s = cmd->server; fcgid_server_conf *config = ap_get_module_config(s->module_config, &fcgid_module); const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); if (err != NULL) { return err; } config->time_score = atol(arg); return NULL; } const char *set_termination_score(cmd_parms * cmd, void *dummy, const char *arg) { server_rec *s = cmd->server; fcgid_server_conf *config = ap_get_module_config(s->module_config, &fcgid_module); const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); if (err != NULL) { return err; } config->termination_score = atol(arg); return NULL; } const char *set_max_process(cmd_parms * cmd, void *dummy, const char *arg) { server_rec *s = cmd->server; fcgid_server_conf *config = ap_get_module_config(s->module_config, &fcgid_module); const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); if (err != NULL) { return err; } config->max_process_count = atol(arg); return NULL; } const char *set_output_buffersize(cmd_parms * cmd, void *dummy, const char *arg) { server_rec *s = cmd->server; fcgid_server_conf *config = ap_get_module_config(s->module_config, &fcgid_module); config->output_buffersize = atol(arg); config->output_buffersize_set = 1; return NULL; } const char *set_max_class_process(cmd_parms * cmd, void *dummy, const char *arg) { server_rec *s = cmd->server; fcgid_server_conf *config = ap_get_module_config(s->module_config, &fcgid_module); config->max_class_process_count = atol(arg); config->max_class_process_count_set = 1; return NULL; } const char *set_min_class_process(cmd_parms * cmd, void *dummy, const char *arg) { server_rec *s = cmd->server; fcgid_server_conf *config = ap_get_module_config(s->module_config, &fcgid_module); config->min_class_process_count = atol(arg); config->min_class_process_count_set = 1; return NULL; } const char *set_php_fix_pathinfo_enable(cmd_parms * cmd, void *dummy, const char *arg) { server_rec *s = cmd->server; fcgid_server_conf *config = ap_get_module_config(s->module_config, &fcgid_module); const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); if (err != NULL) { return err; } config->php_fix_pathinfo_enable = atol(arg); return NULL; } const char *set_max_requests_per_process(cmd_parms * cmd, void *dummy, const char *arg) { server_rec *s = cmd->server; fcgid_server_conf *config = ap_get_module_config(s->module_config, &fcgid_module); if ((config->max_requests_per_process = atol(arg)) == -1) { config->max_requests_per_process = 0; } config->max_requests_per_process_set = 1; return NULL; } const char *set_ipc_connect_timeout(cmd_parms * cmd, void *dummy, const char *arg) { server_rec *s = cmd->server; fcgid_server_conf *config = ap_get_module_config(s->module_config, &fcgid_module); config->ipc_connect_timeout = atol(arg); config->ipc_connect_timeout_set = 1; return NULL; } const char *set_ipc_comm_timeout(cmd_parms * cmd, void *dummy, const char *arg) { server_rec *s = cmd->server; fcgid_server_conf *config = ap_get_module_config(s->module_config, &fcgid_module); config->ipc_comm_timeout = atol(arg); if (config->ipc_comm_timeout <= 0) { return "FcgidIOTimeout must be greater than 0"; } config->ipc_comm_timeout_set = 1; return NULL; } static void add_envvar_to_table(apr_table_t *t, apr_pool_t *p, const char *name, const char *value) { #if defined(WIN32) || defined(OS2) || defined(NETWARE) /* Case insensitive environment platforms */ char *pstr; for (name = pstr = apr_pstrdup(p, name); *pstr; ++pstr) { *pstr = apr_toupper(*pstr); } #endif apr_table_set(t, name, value ? value : ""); } const char *add_default_env_vars(cmd_parms * cmd, void *dummy, const char *name, const char *value) { fcgid_server_conf *config = ap_get_module_config(cmd->server->module_config, &fcgid_module); if (config->default_init_env == NULL) config->default_init_env = apr_table_make(cmd->pool, 20); add_envvar_to_table(config->default_init_env, cmd->pool, name, value); return NULL; } const char *add_pass_headers(cmd_parms * cmd, void *dummy, const char *names) { const char **header; fcgid_server_conf *config = ap_get_module_config(cmd->server->module_config, &fcgid_module); if (config->pass_headers == NULL) config->pass_headers = apr_array_make(cmd->pool, 10, sizeof(const char *)); header = (const char **) apr_array_push(config->pass_headers); *header = ap_getword_conf(cmd->pool, &names); return header ? NULL : "Invalid PassHeaders"; } apr_array_header_t *get_pass_headers(request_rec * r) { fcgid_server_conf *config = ap_get_module_config(r->server->module_config, &fcgid_module); return config->pass_headers; } static const char *missing_file_msg(apr_pool_t *p, const char *filetype, const char *filename, apr_status_t rv) { char errbuf[120]; apr_strerror(rv, errbuf, sizeof errbuf); return apr_psprintf(p, "%s %s cannot be accessed: (%d)%s", filetype, filename, rv, errbuf); } const char *set_authenticator_info(cmd_parms * cmd, void *config, const char *authenticator) { apr_status_t rv; apr_finfo_t finfo; fcgid_dir_conf *dirconfig = (fcgid_dir_conf *) config; char **args; /* Get wrapper path */ apr_tokenize_to_argv(authenticator, &args, cmd->temp_pool); if (*args == NULL || **args == '\0') return "Invalid authenticator config"; /* Fetch only required file details inode + device */ if ((rv = apr_stat(&finfo, args[0], APR_FINFO_IDENT, cmd->temp_pool)) != APR_SUCCESS) { return missing_file_msg(cmd->pool, "Authenticator", authenticator, rv); } /* Create the wrapper node */ dirconfig->authenticator_info = apr_pcalloc(cmd->server->process->pconf, sizeof(*dirconfig->authenticator_info)); dirconfig->authenticator_info->cgipath = apr_pstrdup(cmd->pool, args[0]); dirconfig->authenticator_info->cmdline = authenticator; dirconfig->authenticator_info->inode = finfo.inode; dirconfig->authenticator_info->deviceid = finfo.device; return NULL; } const char *set_authenticator_authoritative(cmd_parms * cmd, void *config, int arg) { fcgid_dir_conf *dirconfig = (fcgid_dir_conf *) config; dirconfig->authenticator_authoritative = arg; dirconfig->authenticator_authoritative_set = 1; return NULL; } fcgid_cmd_conf *get_authenticator_info(request_rec * r, int *authoritative) { fcgid_dir_conf *config = ap_get_module_config(r->per_dir_config, &fcgid_module); if (config != NULL && config->authenticator_info != NULL) { *authoritative = config->authenticator_authoritative; return config->authenticator_info; } return NULL; } const char *set_authorizer_info(cmd_parms * cmd, void *config, const char *authorizer) { apr_status_t rv; apr_finfo_t finfo; fcgid_dir_conf *dirconfig = (fcgid_dir_conf *) config; char **args; /* Get wrapper path */ apr_tokenize_to_argv(authorizer, &args, cmd->temp_pool); if (*args == NULL || **args == '\0') return "Invalid authorizer config"; /* Fetch only required file details inode + device */ if ((rv = apr_stat(&finfo, args[0], APR_FINFO_IDENT, cmd->temp_pool)) != APR_SUCCESS) { return missing_file_msg(cmd->pool, "Authorizer", authorizer, rv); } /* Create the wrapper node */ dirconfig->authorizer_info = apr_pcalloc(cmd->server->process->pconf, sizeof(*dirconfig->authorizer_info)); dirconfig->authorizer_info->cgipath = apr_pstrdup(cmd->pool, args[0]); dirconfig->authorizer_info->cmdline = authorizer; dirconfig->authorizer_info->inode = finfo.inode; dirconfig->authorizer_info->deviceid = finfo.device; return NULL; } const char *set_authorizer_authoritative(cmd_parms * cmd, void *config, int arg) { fcgid_dir_conf *dirconfig = (fcgid_dir_conf *) config; dirconfig->authorizer_authoritative = arg; dirconfig->authorizer_authoritative_set = 1; return NULL; } fcgid_cmd_conf *get_authorizer_info(request_rec * r, int *authoritative) { fcgid_dir_conf *config = ap_get_module_config(r->per_dir_config, &fcgid_module); if (config != NULL && config->authorizer_info != NULL) { *authoritative = config->authorizer_authoritative; return config->authorizer_info; } return NULL; } const char *set_access_info(cmd_parms * cmd, void *config, const char *access) { apr_status_t rv; apr_finfo_t finfo; fcgid_dir_conf *dirconfig = (fcgid_dir_conf *) config; char **args; /* Get wrapper path */ apr_tokenize_to_argv(access, &args, cmd->temp_pool); if (*args == NULL || **args == '\0') return "Invalid access config"; /* Fetch only required file details inode + device */ if ((rv = apr_stat(&finfo, args[0], APR_FINFO_IDENT, cmd->temp_pool)) != APR_SUCCESS) { return missing_file_msg(cmd->pool, "Access checker", access, rv); } /* Create the wrapper node */ dirconfig->access_info = apr_pcalloc(cmd->server->process->pconf, sizeof(*dirconfig->access_info)); dirconfig->access_info->cgipath = apr_pstrdup(cmd->pool, args[0]); dirconfig->access_info->cmdline = access; dirconfig->access_info->inode = finfo.inode; dirconfig->access_info->deviceid = finfo.device; return NULL; } const char *set_access_authoritative(cmd_parms * cmd, void *config, int arg) { fcgid_dir_conf *dirconfig = (fcgid_dir_conf *) config; dirconfig->access_authoritative = arg; dirconfig->access_authoritative_set = 1; return NULL; } fcgid_cmd_conf *get_access_info(request_rec * r, int *authoritative) { fcgid_dir_conf *config = ap_get_module_config(r->per_dir_config, &fcgid_module); if (config != NULL && config->access_info != NULL) { *authoritative = config->access_authoritative; return config->access_info; } return NULL; } #ifdef WIN32 /* FcgidWin32PreventOrphans * * When Apache process gets recycled or shutdown abruptly, CGI processes * spawned by mod_fcgid will get orphaned. Orphaning happens mostly when * Apache worker threads take more than 30 seconds to exit gracefully. * * Apache when run as windows service during shutdown/restart of service * process (master/parent) will terminate child httpd process within 30 * seconds (refer \server\mpm\winnt\mpm_winnt.c:master_main() * int timeout = 30000; ~line#1142), therefore if Apache worker threads * are too busy to react to Master's graceful exit signal within 30 seconds * mod_fcgid cleanup routines will not get invoked (refer child_main() * \server\mpm\winnt\child.c: apr_pool_destroy(pchild); ~line#2275) * thereby orphaning all mod_fcgid spwaned CGI processes. Therefore we utilize * Win32 JobObjects to clean up child processes automatically so that CGI * processes are force-killed by win32 during abnormal mod_fcgid termination. * */ const char *set_win32_prevent_process_orphans(cmd_parms *cmd, void *dummy, int arg) { server_rec *s = cmd->server; fcgid_server_conf *config = ap_get_module_config(s->module_config, &fcgid_module); const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); #define SETUP_ERR_MSG "Error enabling CGI process orphan prevention" if (err != NULL) { return err; } if (arg && config->hJobObjectForAutoCleanup == NULL) { /* Create Win32 job object to prevent CGI process oprhaning */ JOBOBJECT_EXTENDED_LIMIT_INFORMATION job_info = { 0 }; config->hJobObjectForAutoCleanup = CreateJobObject(NULL, NULL); if (config->hJobObjectForAutoCleanup == NULL) { ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_ERR, apr_get_os_error(), cmd->pool, "mod_fcgid: unable to create job object."); return SETUP_ERR_MSG; } /* Set job info so that all spawned CGI processes are associated * with mod_fcgid */ job_info.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE; if (SetInformationJobObject(config->hJobObjectForAutoCleanup, JobObjectExtendedLimitInformation, &job_info, sizeof(job_info)) == 0) { ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_ERR, apr_get_os_error(), cmd->pool, "mod_fcgid: unable to set job object information."); CloseHandle(config->hJobObjectForAutoCleanup); config->hJobObjectForAutoCleanup = NULL; return SETUP_ERR_MSG; } } return NULL; } #endif /* WIN32*/ const char *set_wrapper_config(cmd_parms * cmd, void *dirconfig, const char *wrapper_cmdline, const char *extension, const char *virtual) { const char *path; apr_status_t rv; apr_finfo_t finfo; fcgid_cmd_conf *wrapper = NULL; fcgid_dir_conf *config = (fcgid_dir_conf *) dirconfig; char **args; /* Sanity checks */ if (virtual == NULL && extension != NULL && !strcasecmp(extension, WRAPPER_FLAG_VIRTUAL)) { virtual = WRAPPER_FLAG_VIRTUAL; extension = NULL; } if (virtual != NULL && strcasecmp(virtual, WRAPPER_FLAG_VIRTUAL)) { return "Invalid wrapper flag"; } if (extension != NULL && (*extension != '.' || *(extension + 1) == '\0' || ap_strchr_c(extension, '/') || ap_strchr_c(extension, '\\'))) return "Invalid wrapper file extension"; /* Get wrapper path */ apr_tokenize_to_argv(wrapper_cmdline, &args, cmd->temp_pool); path = apr_pstrdup(cmd->pool, args[0]); if (path == NULL || *path == '\0') return "Invalid wrapper config"; /* Fetch only required file details inode + device */ if ((rv = apr_stat(&finfo, path, APR_FINFO_IDENT, cmd->temp_pool)) != APR_SUCCESS) { return missing_file_msg(cmd->pool, "Wrapper", path, rv); } wrapper = apr_pcalloc(cmd->pool, sizeof(*wrapper)); if (strlen(path) >= FCGID_PATH_MAX) { return "Executable path length exceeds compiled-in limit"; } wrapper->cgipath = apr_pstrdup(cmd->pool, path); if (strlen(wrapper_cmdline) >= FCGID_CMDLINE_MAX) { return "Command line length exceeds compiled-in limit"; } wrapper->cmdline = apr_pstrdup(cmd->pool, wrapper_cmdline); wrapper->inode = finfo.inode; wrapper->deviceid = finfo.device; wrapper->virtual = (virtual != NULL && !strcasecmp(virtual, WRAPPER_FLAG_VIRTUAL)); if (extension == NULL) extension = DEFAULT_WRAPPER_KEY; /* Add the node now */ /* If an extension is configured multiple times, the last directive wins. */ apr_hash_set(config->wrapper_info_hash, extension, strlen(extension), wrapper); return NULL; } fcgid_cmd_conf *get_wrapper_info(const char *cgipath, request_rec * r) { const char *extension; fcgid_cmd_conf *wrapper; fcgid_dir_conf *config = ap_get_module_config(r->per_dir_config, &fcgid_module); /* Get file name extension */ extension = ap_strrchr_c(cgipath, '.'); if (extension == NULL) extension = DEFAULT_WRAPPER_KEY; /* Search file name extension in per_dir_config */ if (config) { wrapper = apr_hash_get(config->wrapper_info_hash, extension, strlen(extension)); if (wrapper == NULL) wrapper = apr_hash_get(config->wrapper_info_hash, DEFAULT_WRAPPER_KEY, strlen(DEFAULT_WRAPPER_KEY)); return wrapper; } return NULL; } static int set_cmd_envvars(fcgid_cmd_env *cmdenv, apr_table_t *envvars) { const apr_array_header_t *envvars_arr; const apr_table_entry_t *envvars_entry; int i; int overflow = 0; if (envvars) { envvars_arr = apr_table_elts(envvars); envvars_entry = (apr_table_entry_t *) envvars_arr->elts; if (envvars_arr->nelts > INITENV_CNT) { overflow = envvars_arr->nelts - INITENV_CNT; } for (i = 0; i < envvars_arr->nelts && i < INITENV_CNT; ++i) { if (envvars_entry[i].key == NULL || envvars_entry[i].key[0] == '\0') break; apr_cpystrn(cmdenv->initenv_key[i], envvars_entry[i].key, INITENV_KEY_LEN); apr_cpystrn(cmdenv->initenv_val[i], envvars_entry[i].val, INITENV_VAL_LEN); } if (i < INITENV_CNT) { cmdenv->initenv_key[i][0] = '\0'; } } else { cmdenv->initenv_key[0][0] = '\0'; } return overflow; } const char *set_cmd_options(cmd_parms *cmd, void *dummy, const char *args) { server_rec *s = cmd->server; fcgid_server_conf *sconf = ap_get_module_config(s->module_config, &fcgid_module); const char *cmdname; fcgid_cmd_options *cmdopts; apr_table_t *envvars = NULL; int overflow; apr_finfo_t finfo; apr_status_t rv; cmdopts = apr_pcalloc(cmd->pool, sizeof *cmdopts); cmdopts->cmdenv = apr_pcalloc(cmd->pool, sizeof *cmdopts->cmdenv); cmdopts->busy_timeout = DEFAULT_BUSY_TIMEOUT; cmdopts->idle_timeout = DEFAULT_IDLE_TIMEOUT; cmdopts->ipc_comm_timeout = DEFAULT_IPC_COMM_TIMEOUT; cmdopts->ipc_connect_timeout = DEFAULT_IPC_CONNECT_TIMEOUT; cmdopts->max_class_process_count = DEFAULT_MAX_CLASS_PROCESS_COUNT; cmdopts->max_requests_per_process = DEFAULT_MAX_REQUESTS_PER_PROCESS; cmdopts->min_class_process_count = DEFAULT_MIN_CLASS_PROCESS_COUNT; cmdopts->proc_lifetime = DEFAULT_PROC_LIFETIME; /* via pcalloc: cmdopts->initenv_key[0][0] = '\0'; */ cmdname = ap_getword_conf(cmd->pool, &args); if (!strlen(cmdname)) { return "A command must be specified for FcgidCmdOptions"; } /* Test only for file existence */ rv = apr_stat(&finfo, cmdname, APR_FINFO_MIN, cmd->temp_pool); if (rv != APR_SUCCESS) { return missing_file_msg(cmd->pool, "Command", cmdname, rv); } if (!*args) { return "At least one option must be specified for FcgidCmdOptions"; } while (*args) { const char *option = ap_getword_conf(cmd->pool, &args); const char *val; /* TODO: Consider supporting BusyTimeout. */ if (!strcasecmp(option, "ConnectTimeout")) { val = ap_getword_conf(cmd->pool, &args); if (!strlen(val)) { return "ConnectTimeout must have an argument"; } cmdopts->ipc_connect_timeout = atoi(val); continue; } if (!strcasecmp(option, "IdleTimeout")) { val = ap_getword_conf(cmd->pool, &args); if (!strlen(val)) { return "IdleTimeout must have an argument"; } cmdopts->idle_timeout = atoi(val); continue; } if (!strcasecmp(option, "InitialEnv")) { char *name; char *eql; name = ap_getword_conf(cmd->pool, &args); if (!strlen(name)) { return "InitialEnv must have an argument"; } eql = strchr(name, '='); if (eql) { *eql = '\0'; ++eql; } if (!envvars) { envvars = apr_table_make(cmd->pool, 20); } add_envvar_to_table(envvars, cmd->pool, name, eql); continue; } if (!strcasecmp(option, "IOTimeout")) { val = ap_getword_conf(cmd->pool, &args); if (!strlen(val)) { return "IOTimeout must have an argument"; } cmdopts->ipc_comm_timeout = atoi(val); continue; } if (!strcasecmp(option, "MaxProcesses")) { val = ap_getword_conf(cmd->pool, &args); if (!strlen(val)) { return "MaxProcesses must have an argument"; } cmdopts->max_class_process_count = atoi(val); continue; } if (!strcasecmp(option, "MaxProcessLifetime")) { val = ap_getword_conf(cmd->pool, &args); if (!strlen(val)) { return "MaxProcessLifetime must have an argument"; } cmdopts->proc_lifetime = atoi(val); continue; } if (!strcasecmp(option, "MaxRequestsPerProcess")) { val = ap_getword_conf(cmd->pool, &args); if (!strlen(val)) { return "MaxRequestsPerProcess must have an argument"; } cmdopts->max_requests_per_process = atoi(val); continue; } if (!strcasecmp(option, "MinProcesses")) { val = ap_getword_conf(cmd->pool, &args); if (!strlen(val)) { return "MinProcesses must have an argument"; } cmdopts->min_class_process_count = atoi(val); continue; } return apr_psprintf(cmd->pool, "Invalid option for FcgidCmdOptions: %s", option); } if ((overflow = set_cmd_envvars(cmdopts->cmdenv, envvars)) != 0) { return apr_psprintf(cmd->pool, "mod_fcgid: environment variable table " "overflow; increase INITENV_CNT in fcgid_pm.h from" " %d to at least %d", INITENV_CNT, INITENV_CNT + overflow); } apr_hash_set(sconf->cmdopts_hash, cmdname, strlen(cmdname), cmdopts); return NULL; } void get_cmd_options(request_rec *r, const char *cmdpath, fcgid_cmd_options *cmdopts, fcgid_cmd_env *cmdenv) { fcgid_server_conf *sconf = ap_get_module_config(r->server->module_config, &fcgid_module); fcgid_cmd_options *cmd_specific = apr_hash_get(sconf->cmdopts_hash, cmdpath, strlen(cmdpath)); int overflow; if (cmd_specific) { /* ignore request context configuration */ *cmdopts = *cmd_specific; *cmdenv = *cmdopts->cmdenv; cmdopts->cmdenv = NULL; /* pick up configuration for values that can't be configured * on FcgidCmdOptions */ cmdopts->busy_timeout = sconf->busy_timeout; return; } cmdopts->busy_timeout = sconf->busy_timeout; cmdopts->idle_timeout = sconf->idle_timeout; cmdopts->ipc_comm_timeout = sconf->ipc_comm_timeout; cmdopts->ipc_connect_timeout = sconf->ipc_connect_timeout; cmdopts->max_class_process_count = sconf->max_class_process_count; cmdopts->max_requests_per_process = sconf->max_requests_per_process; cmdopts->min_class_process_count = sconf->min_class_process_count; cmdopts->proc_lifetime = sconf->proc_lifetime; if ((overflow = set_cmd_envvars(cmdenv, sconf->default_init_env)) != 0) { ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, "mod_fcgid: %d environment variables dropped; increase " "INITENV_CNT in fcgid_pm.h from %d to at least %d", overflow, INITENV_CNT, INITENV_CNT + overflow); } cmdopts->cmdenv = NULL; } mod_fcgid-2.3.9/modules/fcgid/ChangeLog0000644000175000017500000002226511245301144017632 0ustar trawicktrawickNote: A log of changes released after moving to the ASF (releases after 2.2) is in the file CHANGES-FCGID. version 2.2 1. Support configuration "PassHeader" Thank Hans Christian Saustrup for the suggestion. 2. Support apr_shm_remove() in httpd.2.0.X Thank Hans Christian Saustrup for bug report. 3. Support configuration "TimeScore" Thank Tim Jensen for the patch. 4. Support new configurations "MaxRequestInMem" and "MaxRequestLen" If the length of http request longer than "MaxRequestInMem", it will store in tmp file. It the length of http request longer than "MaxRequestLen", it will return internal server error. Thank Gabriel Barazer(gabriel at oxeva.fr) for the bug report. Thank Steffen(info at apachelounge.com) for the help on this issue. 5. Fix miner Sanity check bug Thank Yuya Tanaka for bug report version 2.1 ( Feb 15th 2007 ) 1. Add missing config.m4 and Makefile.in for static linking Thank Mark Drago for notice 2. FCGIWrapper disallowed in .htaccess Thank Balinares for patch 3. Bug fix. Authoritative flag reversed Thank Chris Darroch for the patch 4. Support arguments in FCGIWrapper Thank Andre Nathan for suggestion and great help on testing it. 5. Support new config "SharememPath", which specifies the location of share memory path. 6. Check running user is root or not, while suexec is enabled. Thank Chris Holleman for the bug report. 7. Bug fix. Should not pass respond to auth checkers. Thank Szabolcs Hock for bug report. version 2.0 ( Oct 29th 2006 ) 1. Support FastCGI Authorizer protocols now. 2. Add apxs compile instruction in INSTALL.txt. Thank Hans Christian Saustrup, hc at saustrup.net for the suggestion. 3. Bug fix. (Win32 only) PHP script can not create socket on Win32. Thank bbscool at zjip.com for the bug report and the help. 4. GREAT patchs from Robert L Mathews, rob at tigertech.com Fix compile warnings Adds a MaxRequestsPerProcess parameter that allows mod_fcgid to exit after handling a certain number of requests Close socket before fork avoid the 1-second sleep the first time a process is spawned 5. Print warning log while read data error from FastCGI process. 6. Apply patch from Scott Lamb, Fix mod_fcgid 1.10 warnings on x86_64 version 1.10 ( Jul 3rd 2006 ) 1. Use poll() instead of select() in UNIX. "It becomes problematic on apache2 with large number of logfiles. Apache2 calls poll() (when OS supports it), and in that case it doesn't need to be recompiled with larger FD_SETSIZE. select() is still limited to FD_SETSIZE." Thank Piotr Gackiewicz gacek at intertele.pl for the patch. 2. Bug fix: "Some requests fail with HTTP 500 and no errorlog entry is generated" Thank Piotr Gackiewicz gacek at intertele.pl for the patch. 3. Use anonymouse share memeory to make OS X happy. Thank andkjar at obtech.net for the patch. 4. Add config.m4, mod_fcgid now can be static linked in httpd(See INSTALL.txt) version 1.09 ( Apr 25th 2006 ) 1. Add configuration DefaultMinClassProcessCount(default 3). Idle fastcgi will not be killed if their count less than DefaultMinClassProcessCount. (Thank Finn Smith, finn at timeghost.net for suggestion) 2. Add configuration PHP_Fix_Pathinfo_Enable(default 0). If you are using PHP and set cgi.fix_pathinfo=1 in php.ini, please add "PHP_Fix_Pathinfo_Enable 1" in httpd.conf. (Thank Florian Munz, flo at myhosting.de for bug report)? 3. Split error log whle '\r' or '\n' are inside the text send to "stderr". (Thank frederic at jolliton.com for the patch) version 1.08 ( Jan 22nd 2006 ) 1. apr_bucket_copy() does not work with buckets from mod_ssl, use apr_bucket_heap_create() instead :( (Thank Grzegorz Sampolski, gs at blink.pl for the bug report) 2. Wrapper binary can be stored in a different location to the web content (like /usr/local/apache2/fcgi-bin) (Patch from Stephen Grier, s.e.grier at qmul.ac.uk) 3. Support Apache 2.2 now (Patch from RyoYazaki, yazaki.ryo at mind.co.jp, and many other people report the compiling problem) 4. Support "\r\n\r\n " HTTP header from CGI (Thank Grzegorz Sampolski, gs at blink.pl for the bug report) version 1.07 ( Nov 10th 2005 ) 1. Configuration IPCConnectTimeout, IPCCommTimeout, BusyTimeout can be overwrite in VirtualHost section (Thank cthulhu at planet-multiplayer.de for the suggestion) 2. Add EXTRA_CFLAGS = -I$(builddir) in Makefile (Thank contagion at gmail.com for the suggestion) 3. Support Apache 2.1 now (Patch by nick at webthing.com) (Thank nick at webthing.com for the excellent patch) 4. Support "\r\n\t" HTTP header from CGI version 1.06 ( Apr 27th 2005 ) 1. "DefaultInitEnv" now can be placed inside VirtualHost section 2. Bug fix. "FCGIWrapper" now stores in a per-directory config structure. (The old implementation stores it in a per-server config structure, which may be overwrited by another section) (Thank phyre at rogers.com for bug report and the great help for the bug fix AGAIN) version 1.05 ( Mar 4th 2005 ) 1. Bug fix. suEXEC wrapper in virtualhost environment will shares the process interpreters. (Thank phyre at rogers.com for bug report and the great help for the bug fix) version 1.04 ( Dec 2nd 2004 ) 1. Bug fix. ap_scan_script_header_err_core can return non OK without errors. e.g. CGI outputs Last-Modified header and browser request with If-Mofieided-Since header, ap_scan_script_header_err_core() may returns 302(Not Modified) (Thank Tatsuki Sugiura, sugi at nemui.org for the bug fix patch) 2. Choose FCGI wrappers based on file extentions. e.g. FCGIWrapper /usr/local/bin/php .php Tells mod_fcgid calling all *.php scripts with wrapper /usr/local/bin/php. (Thank Mathijs Brands, mathijs at crooked.net for the suggestion) verison 1.03 ( Nov 3rd 2004 ) 1. Add configuration "OutputBufferSize". The old implementation keep CGI output in a 64k bytes buffer, before send them to web browser(for better network IO performance). If FCGI_Fflush() is called in your application, please add "OutputBufferSize 0" in you httpd.conf, which will not keep any CGI output in cache buffer. The default value of OutputBufferSize is 64k byte. (Thank Grzegorz Sampolski, gs at blink.pl) 2. Return HTTP_SERVICE_UNAVAILABLE instead of HTTP_INTERNAL_SERVER_ERROR, while mod_fcgid can not apply a FastCGI process slot. (Thank Grzegorz Sampolski, gs at blink.pl for the suggestion) version 1.02 ( Oct 1st 2004 ) 1. Bug fix. (Win32 only) Forward request to incorrect process when a script is a directory in the URL. For example, if in the "cgi-bin" directory there are two programs, "a.exe" and "b.exe". If you go to the URL "http://localhost/cgi-bin/a.exe/defghi" then a.exe will start. Then if you go to the URL "http://localhost/cgi-bin/b.exe/uvwxyz" the request will be processed by the already running a.exe. (Thank rripley at amadvertising.com) version 1.01 ( Sep 21st 2004 ) 1. More graceful implementation of suEXEC on UNIX. (Thank radek at karnet.pl for the advice) version 1.00 ( Sep 10th 2004 ) 1. Release FastCGI process slot before sending buffer back to browser. (less process count) version 0.88 ( Sep 3rd 2004 ) 1. Bug fix. File descriptor is closed twice if connect to UNIX domain socket error. 2. Bug fix. Get server last active time incorrectly. version 0.87 ( Aug 26th 2004 ) 1. suEXEC supported. version 0.86 ( Aug 22th 2004 ) Some major changes for performance. (Hits/Second is 30% greater than the old implementation) 1. Socket bucket instead of heap bucket. (less memory) 2. Pass buffer to browser, once the size of buffer exceed 64k bytes. (less memory) 3. Non-block unix domain socket on UNIX. (better I/O performance) 4. writev() instead of write() on UNIX. (better I/O performance) 5. Try to read at least 8k bytes each single reading. (better I/O perormance) 6. Disconnect FastCGI server, once browser disconnect. (less process count) version 0.80 ( Jul 27th 2004 ) 1. Duplex channel added, Apache now will get notification once FastCGI process is spawned. 2. Bug fix. The FastCGI process share the signal handler with PM(Process Manager) process, if the FastCGI process get signal after fork() and before execve(), it will corrupt the share memory. ( It's a very short interval between fork() and execve(), so this rarely happens ) version 0.77 ( Jul 9th 2004 ) 1. Organize the configuration again 1) add "FCGIWrapperGroup" setting 2) "FCGIWrapper" now takes only one argument 3) remove "ServerConfig" setting, because it't not work with wrapper yet Please visit http://fastcgi.coremail.cn/doc.htm for more information about configuration version 0.76 ( Jul 6th 2004 ) 1. Code fix. Replace the depreciated BRIGADE_FOREACH macro, which compile against httpd 2.1-Dev. (Patch by Paul Querna(chip at force-elite.com)) 2. PHP FastCGI Wrapper now can be run both with "share" and "non-share" mode. version 0.74 ( Jun 23rd 2004 ) 1. Bug fix. Ignore script checking while running in Win32 PHP wrapper mode. 2. Indent the code with K&R style. version 0.72 ( Jun 13rd 2004 ) 1. Bug fix. Trim the padding nuls at the end of data. (Thank rick.stewart at theinternetco.net) version 0.7 ( May 22nd 2004 ) 1. PHP FastCGI Wrapper is supported on UNIX and Windows. version 0.62 ( May 18th 2004 ) 1. Unix version now compilable with gcc2.95.2 2. Unix version is tested on Solaris7 & Solaris8 version 0.6 ( May 8th 2004 ) 1. Unix version is tested on Redhat8(gcc3) 2. Windows version is tested on Win2k(VC6) mod_fcgid-2.3.9/modules/fcgid/fcgid_bucket.c0000644000175000017500000002174612222063037020642 0ustar trawicktrawick/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "fcgid_bucket.h" #include "fcgid_protocol.h" #include "fcgid_bridge.h" #define FCGID_FEED_LEN 8192 static apr_status_t fcgid_feed_data(fcgid_bucket_ctx * ctx, apr_bucket_alloc_t * bucketalloc, char **buffer, apr_size_t * bufferlen) { apr_status_t rv; if (!ctx->buffer) { *buffer = apr_bucket_alloc(FCGID_FEED_LEN, bucketalloc); *bufferlen = FCGID_FEED_LEN; if ((rv = proc_read_ipc(&ctx->ipc, *buffer, bufferlen)) != APR_SUCCESS) { ctx->has_error = 1; apr_bucket_free(*buffer); return rv; } ctx->buffer = apr_bucket_heap_create(*buffer, FCGID_FEED_LEN, apr_bucket_free, bucketalloc); if (*bufferlen != FCGID_FEED_LEN) { apr_bucket *buckettmp; apr_bucket_split(ctx->buffer, *bufferlen); buckettmp = APR_BUCKET_NEXT(ctx->buffer); apr_bucket_delete(buckettmp); } } else { apr_bucket_read(ctx->buffer, (const char **) buffer, bufferlen, APR_BLOCK_READ); } return APR_SUCCESS; } static void fcgid_ignore_bytes(fcgid_bucket_ctx * ctx, apr_size_t ignorebyte) { apr_bucket *buckettmp; if (ignorebyte == ctx->buffer->length) { apr_bucket_destroy(ctx->buffer); ctx->buffer = NULL; } else { apr_bucket_split(ctx->buffer, ignorebyte); buckettmp = ctx->buffer; ctx->buffer = APR_BUCKET_NEXT(ctx->buffer); apr_bucket_delete(buckettmp); } } static apr_status_t fcgid_header_bucket_read(apr_bucket * b, const char **str, apr_size_t * len, apr_read_type_e block) { fcgid_bucket_ctx *ctx = (fcgid_bucket_ctx *) b->data; apr_status_t rv; apr_size_t hasread, bodysize; FCGI_Header header; apr_bucket *curbucket = b; /* Keep reading until I get a fastcgi header */ hasread = 0; while (hasread < sizeof(header)) { char *buffer; apr_size_t bufferlen, putsize; /* Feed some data if necessary */ if ((rv = fcgid_feed_data(ctx, b->list, &buffer, &bufferlen)) != APR_SUCCESS) return rv; /* Initialize header */ putsize = fcgid_min(bufferlen, sizeof(header) - hasread); memcpy((apr_byte_t *)&header + hasread, buffer, putsize); hasread += putsize; /* Ignore the bytes that have read */ fcgid_ignore_bytes(ctx, putsize); } /* Get the body size */ bodysize = header.contentLengthB1; bodysize <<= 8; bodysize += header.contentLengthB0; /* Handle FCGI_STDERR body, write the content to log file */ if (header.type == FCGI_STDERR) { char *logbuf = apr_bucket_alloc(APR_BUCKET_BUFF_SIZE, b->list); char *line; apr_size_t hasput; memset(logbuf, 0, APR_BUCKET_BUFF_SIZE); hasread = 0; hasput = 0; while (hasread < bodysize) { char *buffer; apr_size_t bufferlen, canput, willput; /* Feed some data if necessary */ if ((rv = fcgid_feed_data(ctx, b->list, &buffer, &bufferlen)) != APR_SUCCESS) { apr_bucket_free(logbuf); return rv; } canput = fcgid_min(bufferlen, bodysize - hasread); willput = fcgid_min(canput, APR_BUCKET_BUFF_SIZE - hasput - 1); memcpy(logbuf + hasput, buffer, willput); hasread += canput; hasput += willput; /* Ignore the "canput" bytes */ fcgid_ignore_bytes(ctx, canput); } /* Now I get the log data, write log and release the buffer */ line = logbuf; while (*line) { char *end = strpbrk(line, "\r\n"); if (end != NULL) { *end = '\0'; } ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, ctx->ipc.request, "mod_fcgid: stderr: %s", line); if (end == NULL) { break; } ++end; line = end + strspn(end, "\r\n"); } apr_bucket_free(logbuf); } /* if( header.type==FCGI_STDERR ) */ /* Now handle FCGI_STDOUT */ else if (header.type == FCGI_STDOUT) { hasread = 0; while (hasread < bodysize) { char *buffer; apr_size_t bufferlen, canput; apr_bucket *buckettmp; /* Feed some data if necessary */ if ((rv = fcgid_feed_data(ctx, b->list, &buffer, &bufferlen)) != APR_SUCCESS) return rv; canput = fcgid_min(bufferlen, bodysize - hasread); /* Change the current bucket to refer to what we read */ buckettmp = ctx->buffer; if (canput == (bodysize - hasread)) { apr_bucket_split(ctx->buffer, canput); ctx->buffer = APR_BUCKET_NEXT(ctx->buffer); APR_BUCKET_REMOVE(buckettmp); } else { /* canput==bufferlen */ ctx->buffer = NULL; } APR_BUCKET_INSERT_AFTER(curbucket, buckettmp); curbucket = buckettmp; hasread += canput; } /* while( hasreadlist, &buffer, &bufferlen)) != APR_SUCCESS) return rv; canignore = fcgid_min(bufferlen, bodysize); hasread += canignore; /* Ignore the bytes */ fcgid_ignore_bytes(ctx, canignore); } } /* Now ignore padding data */ hasread = 0; while (hasread < header.paddingLength) { char *buffer; apr_size_t bufferlen, canignore; /* Feed some data if necessary */ if ((rv = fcgid_feed_data(ctx, b->list, &buffer, &bufferlen)) != APR_SUCCESS) return rv; canignore = fcgid_min(bufferlen, header.paddingLength - hasread); hasread += canignore; /* Ignore the bytes */ fcgid_ignore_bytes(ctx, canignore); } /* Tail another fastcgi header bucket if it's not ending */ if (header.type != FCGI_END_REQUEST) { apr_bucket *headerbucket = ap_bucket_fcgid_header_create(b->list, ctx); APR_BUCKET_INSERT_AFTER(curbucket, headerbucket); } else { /* Release the process ASAP */ if ((rv = apr_pool_cleanup_run(ctx->ipc.request->pool, ctx, bucket_ctx_cleanup)) != APR_SUCCESS) return rv; } b = apr_bucket_immortal_make(b, "", 0); return apr_bucket_read(b, str, len, APR_BLOCK_READ); } apr_bucket *ap_bucket_fcgid_header_make(apr_bucket * b, fcgid_bucket_ctx * ctx) { b->length = (apr_size_t) (-1); b->start = -1; b->data = ctx; b->type = &ap_bucket_type_fcgid_header; return b; } apr_bucket *ap_bucket_fcgid_header_create(apr_bucket_alloc_t * list, fcgid_bucket_ctx * ctx) { apr_bucket *b = apr_bucket_alloc(sizeof(*b), list); APR_BUCKET_INIT(b); b->free = apr_bucket_free; b->list = list; return ap_bucket_fcgid_header_make(b, ctx); } const apr_bucket_type_t ap_bucket_type_fcgid_header = { "FCGID_HEADER", 5, APR_BUCKET_DATA, apr_bucket_destroy_noop, fcgid_header_bucket_read, apr_bucket_setaside_notimpl, apr_bucket_split_notimpl, apr_bucket_copy_notimpl }; mod_fcgid-2.3.9/modules/fcgid/modules.mk.apxs0000644000175000017500000000256611305263775021053 0ustar trawicktrawick# Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # this is used/needed by the APACHE2 build system # mod_fcgid.la: mod_fcgid.slo fcgid_bridge.slo fcgid_conf.slo fcgid_pm_main.slo fcgid_protocol.slo fcgid_spawn_ctl.slo fcgid_proctbl_unix.slo fcgid_pm_unix.slo fcgid_proc_unix.slo fcgid_bucket.slo fcgid_filter.slo fcgid_mutex_unix.slo $(SH_LINK) -rpath $(libexecdir) -module -avoid-version mod_fcgid.lo fcgid_bridge.lo fcgid_conf.lo fcgid_pm_main.lo fcgid_protocol.lo fcgid_spawn_ctl.lo fcgid_proctbl_unix.lo fcgid_pm_unix.lo fcgid_proc_unix.lo fcgid_bucket.lo fcgid_filter.lo fcgid_mutex_unix.lo DISTCLEAN_TARGETS = modules.mk static = shared = mod_fcgid.la mod_fcgid-2.3.9/modules/fcgid/fcgid_spawn_ctl.c0000644000175000017500000002155511723772367021377 0ustar trawicktrawick/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "fcgid_spawn_ctl.h" #include "fcgid_conf.h" #include "apr_strings.h" #define REGISTER_LIFE 1 #define REGISTER_DEATH 2 struct fcgid_stat_node { apr_ino_t inode; dev_t deviceid; uid_t uid; gid_t gid; const char *cmdline; int vhost_id; int score; int process_counter; int max_class_process_count; int min_class_process_count; apr_time_t last_stat_time; struct fcgid_stat_node *next; }; static apr_pool_t *g_stat_pool = NULL; static struct fcgid_stat_node *g_stat_list_header = NULL; static int g_total_process; static void register_life_death(server_rec * main_server, fcgid_procnode * procnode, int life_or_death) { struct fcgid_stat_node *previous_node, *current_node; fcgid_server_conf *sconf = ap_get_module_config(main_server->module_config, &fcgid_module); apr_time_t now = apr_time_now(); if (!g_stat_pool || !procnode) abort(); /* Can I find the node base on inode, device id and cmdline? */ previous_node = g_stat_list_header; for (current_node = previous_node; current_node != NULL; current_node = current_node->next) { if (current_node->inode == procnode->inode && current_node->deviceid == procnode->deviceid && !strcmp(current_node->cmdline, procnode->cmdline) && current_node->vhost_id == procnode->vhost_id && current_node->uid == procnode->uid && current_node->gid == procnode->gid) break; previous_node = current_node; } if (!current_node) { /* I can't find it, create one */ if (life_or_death == REGISTER_DEATH) { ap_log_error(APLOG_MARK, APLOG_CRIT, 0, main_server, "stat node not found for exiting process %s", procnode->cmdline); return; } current_node = apr_pcalloc(g_stat_pool, sizeof(*current_node)); current_node->deviceid = procnode->deviceid; current_node->inode = procnode->inode; current_node->cmdline = apr_pstrdup(g_stat_pool, procnode->cmdline); current_node->vhost_id = procnode->vhost_id; current_node->uid = procnode->uid; current_node->gid = procnode->gid; current_node->last_stat_time = now; current_node->score = 0; current_node->process_counter = 0; current_node->max_class_process_count = procnode->cmdopts.max_class_process_count; current_node->min_class_process_count = procnode->cmdopts.min_class_process_count; current_node->next = NULL; /* append it to stat list for next search */ if (!previous_node) g_stat_list_header = current_node; else previous_node->next = current_node; } /* Increase the score first */ if (life_or_death == REGISTER_LIFE) { current_node->score += sconf->spawn_score; current_node->process_counter++; } else { current_node->score += sconf->termination_score; current_node->process_counter--; } /* Decrease the score based on elapsed time */ current_node->score -= sconf->time_score * (int)(apr_time_sec(now) - apr_time_sec(current_node->last_stat_time)); /* Make score reasonable */ if (current_node->score < 0) current_node->score = 0; current_node->last_stat_time = now; } void spawn_control_init(server_rec * main_server, apr_pool_t * configpool) { apr_status_t rv; if ((rv = apr_pool_create(&g_stat_pool, configpool)) != APR_SUCCESS) { /* Fatal error */ ap_log_error(APLOG_MARK, APLOG_EMERG, rv, main_server, "mod_fcgid: can't create stat pool"); exit(1); } } void register_termination(server_rec * main_server, fcgid_procnode * procnode) { register_life_death(main_server, procnode, REGISTER_DEATH); g_total_process--; } void register_spawn(server_rec * main_server, fcgid_procnode * procnode) { register_life_death(main_server, procnode, REGISTER_LIFE); g_total_process++; } /* * Spawn control strategy: * 1. Add FcgidSpawnScore to score if application is terminated * 2. Add FcgidTerminationScore to score if application is spawned * 3. Subtract FcgidTimeScore from score each second while score is positive * 4. Ignore spawn request if score is higher than FcgidSpawnScoreUpLimit * 5. Ignore spawn request if total process count higher than * FcgidSpawnScoreUpLimit * 6. Ignore spawn request if process count of this class is higher than * FcgidMaxProcessesPerClass */ int is_spawn_allowed(server_rec * main_server, fcgid_command * command) { struct fcgid_stat_node *current_node; fcgid_server_conf *sconf = ap_get_module_config(main_server->module_config, &fcgid_module); if (!command || !g_stat_pool) return 1; /* Total process count higher than up limit? */ if (g_total_process >= sconf->max_process_count) { ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, main_server, "mod_fcgid: %s total process count %d >= %d, skip the spawn request", command->cgipath, g_total_process, sconf->max_process_count); return 0; } /* Can I find the node base on inode, device id and cmdline? */ for (current_node = g_stat_list_header; current_node != NULL; current_node = current_node->next) { if (current_node->inode == command->inode && current_node->deviceid == command->deviceid && !strcmp(current_node->cmdline, command->cmdline) && current_node->vhost_id == command->vhost_id && current_node->uid == command->uid && current_node->gid == command->gid) break; } if (!current_node) { /* There are no existing processes for this class, so obviously * no class-specific limits have been exceeded. */ return 1; } else { apr_time_t now = apr_time_now(); current_node->score -= sconf->time_score * (int)(apr_time_sec(now) - apr_time_sec(current_node->last_stat_time)); current_node->last_stat_time = now; if (current_node->score < 0) current_node->score = 0; /* Score is higher than up limit? */ if (current_node->score >= sconf->spawnscore_uplimit) { ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, main_server, "mod_fcgid: %s spawn score %d >= %d, skip the spawn request", command->cgipath, current_node->score, sconf->spawnscore_uplimit); return 0; } /* * Process count of this class higher than up limit? */ if (current_node->process_counter >= current_node->max_class_process_count) { ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, main_server, "mod_fcgid: too many %s processes (current:%d, max:%d), skip the spawn request", command->cgipath, current_node->process_counter, current_node->max_class_process_count); return 0; } return 1; } } int is_kill_allowed(server_rec * main_server, fcgid_procnode * procnode) { struct fcgid_stat_node *current_node; if (!g_stat_pool || !procnode) return 0; /* Can I find the node base on inode, device id and cmdline? */ for (current_node = g_stat_list_header; current_node != NULL; current_node = current_node->next) { if (current_node->inode == procnode->inode && current_node->deviceid == procnode->deviceid && !strcmp(current_node->cmdline, procnode->cmdline) && current_node->vhost_id == procnode->vhost_id && current_node->uid == procnode->uid && current_node->gid == procnode->gid) break; } if (current_node) { /* Found the node */ if (current_node->process_counter <= current_node->min_class_process_count) return 0; } return 1; } mod_fcgid-2.3.9/modules/fcgid/fcgid_filter.c0000644000175000017500000000651412216034065020650 0ustar trawicktrawick/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "httpd.h" #include "http_config.h" #include "http_log.h" #include "fcgid_filter.h" #include "fcgid_bucket.h" #include "fcgid_conf.h" apr_status_t fcgid_filter(ap_filter_t * f, apr_bucket_brigade * bb) { apr_status_t rv; apr_bucket_brigade *tmp_brigade; apr_size_t save_size = 0; conn_rec *c = f->c; server_rec *s = f->r->server; fcgid_server_conf *sconf = ap_get_module_config(s->module_config, &fcgid_module); tmp_brigade = apr_brigade_create(f->r->pool, f->r->connection->bucket_alloc); while (!APR_BRIGADE_EMPTY(bb)) { apr_size_t readlen; const char *buffer; apr_bucket *e = APR_BRIGADE_FIRST(bb); if (APR_BUCKET_IS_EOS(e)) break; if (APR_BUCKET_IS_METADATA(e)) { apr_bucket_delete(e); continue; } /* Read the bucket now */ if ((rv = apr_bucket_read(e, &buffer, &readlen, APR_BLOCK_READ)) != APR_SUCCESS) { ap_log_rerror(APLOG_MARK, APLOG_INFO, rv, f->r, "mod_fcgid: can't read data from fcgid handler"); return rv; } /* Move on to next bucket if it's fastcgi header bucket */ if (e->type == &ap_bucket_type_fcgid_header || (e->type == &apr_bucket_type_immortal && readlen == 0)) { apr_bucket_delete(e); continue; } save_size += readlen; /* Cache it to tmp_brigade */ APR_BUCKET_REMOVE(e); APR_BRIGADE_INSERT_TAIL(tmp_brigade, e); /* I will pass tmp_brigade to next filter if I have got too much buckets */ if (save_size > sconf->output_buffersize) { APR_BRIGADE_INSERT_TAIL(tmp_brigade, apr_bucket_flush_create(f->r-> connection-> bucket_alloc)); if ((rv = ap_pass_brigade(f->next, tmp_brigade)) != APR_SUCCESS) return rv; /* Is the client aborted? */ if (c && c->aborted) return APR_SUCCESS; save_size = 0; } } /* Any thing left? */ if (!APR_BRIGADE_EMPTY(tmp_brigade)) { if ((rv = ap_pass_brigade(f->next, tmp_brigade)) != APR_SUCCESS) return rv; } /* This filter is done once it has served up its content */ ap_remove_output_filter(f); return APR_SUCCESS; } mod_fcgid-2.3.9/modules/fcgid/mod_fcgid.c0000644000175000017500000011311612001125622020127 0ustar trawicktrawick/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "httpd.h" #include "http_request.h" #include "http_protocol.h" #include "ap_mmn.h" #include "apr_lib.h" #include "apr_buckets.h" #include "apr_strings.h" #include "apr_thread_proc.h" #include "mod_cgi.h" #include "mod_status.h" #include "util_script.h" #include "fcgid_global.h" #include "fcgid_pm.h" #include "fcgid_proctbl.h" #include "fcgid_conf.h" #include "fcgid_spawn_ctl.h" #include "fcgid_bridge.h" #include "fcgid_filter.h" #include "fcgid_protocol.h" #include "fcgid_proc.h" static APR_OPTIONAL_FN_TYPE(ap_cgi_build_command) * cgi_build_command; static ap_filter_rec_t *fcgid_filter_handle; static int g_php_fix_pathinfo_enable = 0; enum fcgid_procnode_type { FCGID_PROCNODE_TYPE_IDLE, FCGID_PROCNODE_TYPE_BUSY, FCGID_PROCNODE_TYPE_ERROR, }; enum fcgid_auth_check_mode { FCGID_AUTH_CHECK_AUTHN, FCGID_AUTH_CHECK_AUTHZ, FCGID_AUTH_CHECK_ACCESS }; /* Stolen from mod_cgi.c */ /* KLUDGE --- for back-compatibility, 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 is_scriptaliased(request_rec * r) { const char *t = apr_table_get(r->notes, "alias-forced-type"); return t && (!strcasecmp(t, "cgi-script")); } static apr_status_t default_build_command(const char **cmd, const char ***argv, request_rec * r, apr_pool_t * p, cgi_exec_info_t * e_info) { int numwords, x, idx; char *w; const char *args = NULL; if (e_info->process_cgi) { *cmd = r->filename; /* Do not process r->args if they contain an '=' assignment */ if (r->args && r->args[0] && !ap_strchr_c(r->args, '=')) { args = r->args; } } if (!args) { numwords = 1; } else { /* count the number of keywords */ for (x = 0, numwords = 2; args[x]; x++) { if (args[x] == '+') { ++numwords; } } } /* Everything is - 1 to account for the first parameter * which is the program name. */ if (numwords > APACHE_ARG_MAX - 1) { numwords = APACHE_ARG_MAX - 1; /* Truncate args to prevent overrun */ } *argv = apr_palloc(p, (numwords + 2) * sizeof(char *)); (*argv)[0] = *cmd; for (x = 1, idx = 1; x < numwords; x++) { w = ap_getword_nulls(p, &args, '+'); ap_unescape_url(w); (*argv)[idx++] = ap_escape_shell_cmd(p, w); } (*argv)[idx] = NULL; return APR_SUCCESS; } /* http2env stolen from util_script.c */ static char *http2env(apr_pool_t *a, const char *w) { char *res = (char *)apr_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 (!apr_isalnum(c)) { *cp++ = '_'; } else { *cp++ = apr_toupper(c); } } *cp = 0; return res; } static void fcgid_add_cgi_vars(request_rec * r) { apr_array_header_t *passheaders = get_pass_headers(r); if (passheaders != NULL) { const char **hdr = (const char **) passheaders->elts; int hdrcnt = passheaders->nelts; int i; for (i = 0; i < hdrcnt; i++, ++hdr) { const char *val = apr_table_get(r->headers_in, *hdr); if (val) { /* no munging of header name to create envvar name; * consistent with legacy mod_fcgid behavior and mod_fastcgi * prior to 2.4.7 */ apr_table_setn(r->subprocess_env, *hdr, val); /* standard munging of header name (upcase, HTTP_, etc.) */ apr_table_setn(r->subprocess_env, http2env(r->pool, *hdr), val); } } } /* Work around cgi.fix_pathinfo = 1 in php.ini */ if (g_php_fix_pathinfo_enable) { char *merge_path; apr_table_t *e = r->subprocess_env; /* "DOCUMENT_ROOT"/"SCRIPT_NAME" -> "SCRIPT_NAME" */ const char *doc_root = apr_table_get(e, "DOCUMENT_ROOT"); const char *script_name = apr_table_get(e, "SCRIPT_NAME"); if (doc_root && script_name && apr_filepath_merge(&merge_path, doc_root, script_name, 0, r->pool) == APR_SUCCESS) { apr_table_setn(e, "SCRIPT_NAME", merge_path); } } } static int fcgid_handler(request_rec * r) { cgi_exec_info_t e_info; const char *command; const char **argv; apr_status_t rv; int http_retcode; fcgid_cmd_conf *wrapper_conf; if (strcmp(r->handler, "fcgid-script")) return DECLINED; if (!(ap_allow_options(r) & OPT_EXECCGI) && !is_scriptaliased(r)) return HTTP_FORBIDDEN; if ((r->used_path_info == AP_REQ_REJECT_PATH_INFO) && r->path_info && *r->path_info) return HTTP_NOT_FOUND; e_info.process_cgi = 1; e_info.cmd_type = APR_PROGRAM; e_info.detached = 0; e_info.in_pipe = APR_CHILD_BLOCK; e_info.out_pipe = APR_CHILD_BLOCK; e_info.err_pipe = APR_CHILD_BLOCK; e_info.prog_type = RUN_AS_CGI; e_info.bb = NULL; e_info.ctx = NULL; e_info.next = NULL; wrapper_conf = get_wrapper_info(r->filename, r); /* Check for existence of requested file, unless we use a virtual wrapper. */ if (wrapper_conf == NULL || !wrapper_conf->virtual) { if (r->finfo.filetype == 0) return HTTP_NOT_FOUND; if (r->finfo.filetype == APR_DIR) return HTTP_FORBIDDEN; } /* Build the command line */ if (wrapper_conf) { if ((rv = default_build_command(&command, &argv, r, r->pool, &e_info)) != APR_SUCCESS) { ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, "mod_fcgid: don't know how to spawn wrapper child process: %s", r->filename); return HTTP_INTERNAL_SERVER_ERROR; } } else { if ((rv = cgi_build_command(&command, &argv, r, r->pool, &e_info)) != APR_SUCCESS) { ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, "mod_fcgid: don't know how to spawn child process: %s", r->filename); return HTTP_INTERNAL_SERVER_ERROR; } /* Check request like "http://localhost/cgi-bin/a.exe/defghi" */ if (r->finfo.inode == 0 && r->finfo.device == 0) { if ((rv = apr_stat(&r->finfo, command, APR_FINFO_IDENT, r->pool)) != APR_SUCCESS) { ap_log_rerror(APLOG_MARK, APLOG_WARNING, rv, r, "mod_fcgid: can't get %s file info", command); return HTTP_NOT_FOUND; } } /* Dummy up a wrapper configuration, using the requested file as * both the executable path and command-line. */ wrapper_conf = apr_pcalloc(r->pool, sizeof(*wrapper_conf)); if (strlen(command) >= fcgid_min(FCGID_PATH_MAX, FCGID_CMDLINE_MAX)) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "mod_fcgid: Executable path length exceeds compiled-in limit: %s", command); return HTTP_INTERNAL_SERVER_ERROR; } wrapper_conf->cgipath = apr_pstrdup(r->pool, command); wrapper_conf->cmdline = wrapper_conf->cgipath; wrapper_conf->inode = r->finfo.inode; wrapper_conf->deviceid = r->finfo.device; } ap_add_common_vars(r); ap_add_cgi_vars(r); fcgid_add_cgi_vars(r); /* Remove hop-by-hop headers handled by http */ apr_table_unset(r->subprocess_env, "HTTP_KEEP_ALIVE"); apr_table_unset(r->subprocess_env, "HTTP_TE"); apr_table_unset(r->subprocess_env, "HTTP_TRAILER"); apr_table_unset(r->subprocess_env, "HTTP_TRANSFER_ENCODING"); apr_table_unset(r->subprocess_env, "HTTP_UPGRADE"); /* Connection hop-by-hop header to prevent the CGI from hanging */ apr_table_set(r->subprocess_env, "HTTP_CONNECTION", "close"); /* Insert output filter */ ap_add_output_filter_handle(fcgid_filter_handle, NULL, r, r->connection); http_retcode = bridge_request(r, FCGI_RESPONDER, wrapper_conf); return (http_retcode == HTTP_OK ? OK : http_retcode); } static int fcgidsort(fcgid_procnode **e1, fcgid_procnode **e2) { int cmp = strcmp((*e1)->executable_path, (*e2)->executable_path); if (cmp != 0) return cmp; if ((*e1)->gid != (*e2)->gid) return (*e1)->gid > (*e2)->gid ? 1 : -1; if ((*e1)->uid != (*e2)->uid) return (*e1)->uid > (*e2)->uid ? 1 : -1; cmp = strcmp((*e1)->cmdline, (*e2)->cmdline); if (cmp != 0) return cmp; if ((*e1)->vhost_id != (*e2)->vhost_id) return (*e1)->vhost_id > (*e2)->vhost_id ? 1 : -1; if ((*e1)->diewhy != (*e2)->diewhy) return (*e1)->diewhy > (*e2)->diewhy ? 1 : -1; if ((*e1)->node_type != (*e2)->node_type) return (*e1)->node_type > (*e2)->node_type ? 1 : -1; return 0; } static char *get_state_desc(fcgid_procnode *node) { if (node->node_type == FCGID_PROCNODE_TYPE_IDLE) return "Ready"; else if (node->node_type == FCGID_PROCNODE_TYPE_BUSY) return "Working"; else { switch (node->diewhy) { case FCGID_DIE_KILLSELF: return "Exiting(normal exit)"; case FCGID_DIE_IDLE_TIMEOUT: return "Exiting(idle timeout)"; case FCGID_DIE_LIFETIME_EXPIRED: return "Exiting(lifetime expired)"; case FCGID_DIE_BUSY_TIMEOUT: return "Exiting(busy timeout)"; case FCGID_DIE_CONNECT_ERROR: return "Exiting(connect error)"; case FCGID_DIE_COMM_ERROR: return "Exiting(communication error)"; case FCGID_DIE_SHUTDOWN: return "Exiting(shutting down)"; default: return "Exiting"; } } } /* fcgid Extension to mod_status */ static int fcgid_status_hook(request_rec *r, int flags) { fcgid_procnode **ar = NULL, *current_node; int num_ent, index; apr_ino_t last_inode = 0; apr_dev_t last_deviceid = 0; gid_t last_gid = 0; uid_t last_uid = 0; const char *last_cmdline = ""; apr_time_t now; int last_vhost_id = -1; const char *basename, *tmpbasename; fcgid_procnode *proc_table = proctable_get_table_array(); fcgid_procnode *error_list_header = proctable_get_error_list(); fcgid_procnode *idle_list_header = proctable_get_idle_list(); fcgid_procnode *busy_list_header = proctable_get_busy_list(); if ((flags & AP_STATUS_SHORT) || (proc_table == NULL)) return OK; proctable_lock(r); /* Get element count */ num_ent = 0; current_node = &proc_table[busy_list_header->next_index]; while (current_node != proc_table) { num_ent++; current_node = &proc_table[current_node->next_index]; } current_node = &proc_table[idle_list_header->next_index]; while (current_node != proc_table) { num_ent++; current_node = &proc_table[current_node->next_index]; } current_node = &proc_table[error_list_header->next_index]; while (current_node != proc_table) { num_ent++; current_node = &proc_table[current_node->next_index]; } /* Create an array for qsort() */ if (num_ent != 0) { ar = (fcgid_procnode **)apr_palloc(r->pool, num_ent * sizeof(fcgid_procnode*)); index = 0; current_node = &proc_table[busy_list_header->next_index]; while (current_node != proc_table) { ar[index] = apr_palloc(r->pool, sizeof(fcgid_procnode)); *ar[index] = *current_node; ar[index++]->node_type = FCGID_PROCNODE_TYPE_BUSY; current_node = &proc_table[current_node->next_index]; } current_node = &proc_table[idle_list_header->next_index]; while (current_node != proc_table) { ar[index] = apr_palloc(r->pool, sizeof(fcgid_procnode)); *ar[index] = *current_node; ar[index++]->node_type = FCGID_PROCNODE_TYPE_IDLE; current_node = &proc_table[current_node->next_index]; } current_node = &proc_table[error_list_header->next_index]; while (current_node != proc_table) { ar[index] = apr_palloc(r->pool, sizeof(fcgid_procnode)); *ar[index] = *current_node; ar[index++]->node_type = FCGID_PROCNODE_TYPE_ERROR; current_node = &proc_table[current_node->next_index]; } } proctable_unlock(r); now = apr_time_now(); /* Sort the array */ if (num_ent != 0) qsort((void *)ar, num_ent, sizeof(fcgid_procnode *), (int (*)(const void *, const void *))fcgidsort); /* Output */ ap_rputs("
\n

mod_fcgid status:

\n", r); ap_rprintf(r, "Total FastCGI processes: %d\n", num_ent); for (index = 0; index < num_ent; index++) { current_node = ar[index]; if (current_node->inode != last_inode || current_node->deviceid != last_deviceid || current_node->gid != last_gid || current_node->uid != last_uid || strcmp(current_node->cmdline, last_cmdline) || current_node->vhost_id != last_vhost_id) { if (index != 0) ap_rputs("\n\n", r); /* Print executable path basename */ tmpbasename = ap_strrchr_c(current_node->executable_path, '/'); if (tmpbasename != NULL) tmpbasename++; basename = ap_strrchr_c(tmpbasename, '\\'); if (basename != NULL) basename++; else basename = tmpbasename; ap_rprintf(r, "
\nProcess: %s  (%s)
\n", basename, current_node->cmdline); /* Create a new table for this process info */ ap_rputs("\n\n" "" "" "\n", r); last_inode = current_node->inode; last_deviceid = current_node->deviceid; last_gid = current_node->gid; last_uid = current_node->uid; last_cmdline = current_node->cmdline; last_vhost_id = current_node->vhost_id; } ap_rprintf(r, "", current_node->proc_id.pid, apr_time_sec(now - current_node->start_time), apr_time_sec(now - current_node->last_active_time), current_node->requests_handled, get_state_desc(current_node)); } if (num_ent != 0) { ap_rputs("
PidActiveIdleAccessesState
%" APR_PID_T_FMT "%" APR_TIME_T_FMT "%" APR_TIME_T_FMT "%d%s
\n\n", r); ap_rputs("
\n" "Active and Idle are time active and time since\n" "last request, in seconds.\n", r); } return OK; } static int mod_fcgid_modify_auth_header(void *subprocess_env, const char *key, const char *val) { /* When the application gives a 200 response, the server ignores response headers whose names aren't prefixed with Variable- prefix, and ignores any response content */ if (strncasecmp(key, "Variable-", 9) == 0) apr_table_setn(subprocess_env, key + 9, val); return 1; } static int mod_fcgid_check_auth(request_rec *r, enum fcgid_auth_check_mode auth_check_mode) { int res = 0; const char *password = NULL; apr_table_t *saved_subprocess_env = NULL; fcgid_cmd_conf *auth_cmd_info = NULL; int authoritative; const char *auth_role = NULL; const char *role_log_msg = NULL; const char *user_log_msg = ""; /* Because we don't function as authn/z providers, integration with * the standard httpd authn/z modules is somewhat problematic. * * With httpd 2.4 in particular, our hook functions may be * circumvented by mod_authz_core's check_access_ex hook, unless * Require directives specify that user-based authn/z is needed. * * Even then, APR_HOOK_MIDDLE may cause our authentication hook to be * ordered after mod_auth_basic's check_authn hook, in which case it * will be skipped unless AuthBasicAuthoritative is Off and no authn * provider recognizes the user or outright denies the request. * * Also, when acting as an authenticator, we don't have a mechanism to * set r->user based on the script response, so scripts can't implement * a private authentication scheme; instead we use ap_get_basic_auth_pw() * and only support Basic HTTP authentication. * * It is possible to act reliably as both authenticator and authorizer * if mod_authn_core is loaded to support AuthType and AuthName, but * mod_authz_core and mod_auth_basic are not loaded. However, in this * case the Require directive is not available, which defeats many * common configuration tropes. */ switch (auth_check_mode) { case FCGID_AUTH_CHECK_AUTHN: auth_cmd_info = get_authenticator_info(r, &authoritative); auth_role = "AUTHENTICATOR"; role_log_msg = "Authentication"; break; case FCGID_AUTH_CHECK_AUTHZ: auth_cmd_info = get_authorizer_info(r, &authoritative); auth_role = "AUTHORIZER"; role_log_msg = "Authorization"; break; case FCGID_AUTH_CHECK_ACCESS: auth_cmd_info = get_access_info(r, &authoritative); auth_role = "ACCESS_CHECKER"; role_log_msg = "Access check"; break; } /* Is this auth check command enabled? */ if (auth_cmd_info == NULL) return DECLINED; /* Get the user password */ if (auth_check_mode == FCGID_AUTH_CHECK_AUTHN && (res = ap_get_basic_auth_pw(r, &password)) != OK) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "mod_fcgid: authenticator requires " "basic HTTP auth credentials"); return res; } if (auth_check_mode != FCGID_AUTH_CHECK_ACCESS) { user_log_msg = apr_psprintf(r->pool, " of user %s", r->user); } /* Save old process environment */ saved_subprocess_env = apr_table_copy(r->pool, r->subprocess_env); /* Add some environment variables */ ap_add_common_vars(r); ap_add_cgi_vars(r); fcgid_add_cgi_vars(r); if (auth_check_mode == FCGID_AUTH_CHECK_AUTHN) { apr_table_setn(r->subprocess_env, "REMOTE_PASSWD", password); } apr_table_setn(r->subprocess_env, "FCGI_APACHE_ROLE", auth_role); /* Drop the variables CONTENT_LENGTH, PATH_INFO, PATH_TRANSLATED, * SCRIPT_NAME and most Hop-By-Hop headers - EXCEPT we will pass * PROXY_AUTH to allow CGI to perform proxy auth for httpd */ apr_table_unset(r->subprocess_env, "CONTENT_LENGTH"); apr_table_unset(r->subprocess_env, "PATH_INFO"); apr_table_unset(r->subprocess_env, "PATH_TRANSLATED"); apr_table_unset(r->subprocess_env, "SCRIPT_NAME"); apr_table_unset(r->subprocess_env, "HTTP_KEEP_ALIVE"); apr_table_unset(r->subprocess_env, "HTTP_TE"); apr_table_unset(r->subprocess_env, "HTTP_TRAILER"); apr_table_unset(r->subprocess_env, "HTTP_TRANSFER_ENCODING"); apr_table_unset(r->subprocess_env, "HTTP_UPGRADE"); /* Connection hop-by-hop header to prevent the CGI from hanging */ apr_table_set(r->subprocess_env, "HTTP_CONNECTION", "close"); /* Handle the request */ res = bridge_request(r, FCGI_AUTHORIZER, auth_cmd_info); /* Restore r->subprocess_env */ r->subprocess_env = saved_subprocess_env; if (res == OK && r->status == HTTP_OK && apr_table_get(r->headers_out, "Location") == NULL) { /* Pass */ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "mod_fcgid: %s%s to access %s succeeded", role_log_msg, user_log_msg, r->uri); /* Modify headers: An Authorizer application's 200 response may include headers whose names are prefixed with Variable-. */ apr_table_do(mod_fcgid_modify_auth_header, r->subprocess_env, r->err_headers_out, NULL); return OK; } else { const char *add_err_msg = ""; /* Print error info first */ if (res != OK) { add_err_msg = apr_psprintf(r->pool, "; error or unexpected condition " "while parsing response (%d)", res); } else if (r->status == HTTP_OK) { add_err_msg = "; internal redirection not allowed"; } ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "mod_fcgid: %s%s to access %s failed, reason: " "script returned status %d%s", role_log_msg, user_log_msg, r->uri, r->status, add_err_msg); /* Handle error */ if (!authoritative) { ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "mod_fcgid: not authoritative"); return DECLINED; } else { if (auth_check_mode != FCGID_AUTH_CHECK_ACCESS) { ap_note_basic_auth_failure(r); } return (res == OK) ? HTTP_UNAUTHORIZED : res; } } } static int mod_fcgid_authenticator(request_rec *r) { return mod_fcgid_check_auth(r, FCGID_AUTH_CHECK_AUTHN); } static int mod_fcgid_authorizer(request_rec *r) { return mod_fcgid_check_auth(r, FCGID_AUTH_CHECK_AUTHZ); } static int mod_fcgid_check_access(request_rec *r) { return mod_fcgid_check_auth(r, FCGID_AUTH_CHECK_ACCESS); } static void initialize_child(apr_pool_t * pchild, server_rec * main_server) { apr_status_t rv; if ((rv = proctable_child_init(main_server, pchild)) != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_EMERG, rv, main_server, "mod_fcgid: Can't initialize shared memory or mutex in child"); return; } if ((rv = procmgr_child_init(main_server, pchild)) != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_EMERG, rv, main_server, "mod_fcgid: Can't initialize process manager"); return; } return; } static int fcgid_init(apr_pool_t * config_pool, apr_pool_t * plog, apr_pool_t * ptemp, server_rec * main_server) { const char *userdata_key = "fcgid_init"; apr_status_t rv; void *dummy = NULL; fcgid_server_conf *sconf = ap_get_module_config(main_server->module_config, &fcgid_module); ap_add_version_component(config_pool, MODFCGID_PRODUCT); g_php_fix_pathinfo_enable = sconf->php_fix_pathinfo_enable; /* Initialize process manager only once */ apr_pool_userdata_get(&dummy, userdata_key, main_server->process->pool); if (!dummy) { apr_pool_userdata_set((const void *)1, userdata_key, apr_pool_cleanup_null, main_server->process->pool); return OK; } /* Initialize share memory and share lock */ if ((rv = proctable_post_config(main_server, config_pool)) != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_EMERG, rv, main_server, "mod_fcgid: Can't initialize shared memory or mutex"); return rv; } /* Initialize process manager */ if ((rv = procmgr_post_config(main_server, config_pool)) != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_EMERG, rv, main_server, "mod_fcgid: Can't initialize process manager"); return rv; } /* This is the means by which unusual (non-unix) os's may find alternate * means to run a given command (e.g. shebang/registry parsing on Win32) */ cgi_build_command = APR_RETRIEVE_OPTIONAL_FN(ap_cgi_build_command); if (!cgi_build_command) { cgi_build_command = default_build_command; } return APR_SUCCESS; } static const command_rec fcgid_cmds[] = { AP_INIT_TAKE1("FcgidAccessChecker", set_access_info, NULL, ACCESS_CONF | OR_FILEINFO, "a absolute access checker file path"), AP_INIT_FLAG("FcgidAccessCheckerAuthoritative", set_access_authoritative, NULL, ACCESS_CONF | OR_FILEINFO, "Set to 'off' to allow access control to be passed along to lower modules upon failure"), AP_INIT_TAKE1("FcgidAuthenticator", set_authenticator_info, NULL, ACCESS_CONF | OR_FILEINFO, "a absolute authenticator file path"), AP_INIT_FLAG("FcgidAuthenticatorAuthoritative", set_authenticator_authoritative, NULL, ACCESS_CONF | OR_FILEINFO, "Set to 'off' to allow authentication to be passed along to lower modules upon failure"), AP_INIT_TAKE1("FcgidAuthorizer", set_authorizer_info, NULL, ACCESS_CONF | OR_FILEINFO, "a absolute authorizer file path"), AP_INIT_FLAG("FcgidAuthorizerAuthoritative", set_authorizer_authoritative, NULL, ACCESS_CONF | OR_FILEINFO, "Set to 'off' to allow authorization to be passed along to lower modules upon failure"), AP_INIT_TAKE1("FcgidBusyScanInterval", set_busy_scan_interval, NULL, RSRC_CONF, "scan interval for busy timeout process"), AP_INIT_TAKE1("FcgidBusyTimeout", set_busy_timeout, NULL, RSRC_CONF, "a fastcgi application will be killed after handling a request for BusyTimeout"), AP_INIT_RAW_ARGS("FcgidCmdOptions", set_cmd_options, NULL, RSRC_CONF, "set processing options for a FastCGI command"), AP_INIT_TAKE12("FcgidInitialEnv", add_default_env_vars, NULL, RSRC_CONF, "an environment variable name and optional value to pass to FastCGI."), AP_INIT_TAKE1("FcgidMaxProcessesPerClass", set_max_class_process, NULL, RSRC_CONF, "Max process count of one class of fastcgi application"), AP_INIT_TAKE1("FcgidMinProcessesPerClass", set_min_class_process, NULL, RSRC_CONF, "Min process count of one class of fastcgi application"), AP_INIT_TAKE1("FcgidErrorScanInterval", set_error_scan_interval, NULL, RSRC_CONF, "scan interval for exited process"), AP_INIT_TAKE1("FcgidIdleScanInterval", set_idle_scan_interval, NULL, RSRC_CONF, "scan interval for idle timeout process"), AP_INIT_TAKE1("FcgidIdleTimeout", set_idle_timeout, NULL, RSRC_CONF, "an idle fastcgi application will be killed after IdleTimeout"), AP_INIT_TAKE1("FcgidIOTimeout", set_ipc_comm_timeout, NULL, RSRC_CONF, "Communication timeout to fastcgi server"), AP_INIT_TAKE1("FcgidConnectTimeout", set_ipc_connect_timeout, NULL, RSRC_CONF, "Connect timeout to fastcgi server"), AP_INIT_TAKE1("FcgidMaxProcesses", set_max_process, NULL, RSRC_CONF, "Max total process count"), AP_INIT_TAKE1("FcgidMaxRequestInMem", set_max_mem_request_len, NULL, RSRC_CONF, "The part of HTTP request which greater than this limit will swap to disk"), AP_INIT_TAKE1("FcgidMaxRequestLen", set_max_request_len, NULL, RSRC_CONF, "Max HTTP request length in byte"), AP_INIT_TAKE1("FcgidMaxRequestsPerProcess", set_max_requests_per_process, NULL, RSRC_CONF, "Max requests handled by each fastcgi application"), AP_INIT_TAKE1("FcgidOutputBufferSize", set_output_buffersize, NULL, RSRC_CONF, "CGI output buffer size"), AP_INIT_TAKE1("FcgidPassHeader", add_pass_headers, NULL, RSRC_CONF, "Header name which will be passed to FastCGI as environment variable."), AP_INIT_TAKE1("FcgidFixPathinfo", set_php_fix_pathinfo_enable, NULL, RSRC_CONF, "Set 1, if cgi.fix_pathinfo=1 in php.ini"), AP_INIT_TAKE1("FcgidProcessLifeTime", set_proc_lifetime, NULL, RSRC_CONF, "fastcgi application lifetime"), AP_INIT_TAKE1("FcgidProcessTableFile", set_shmpath, NULL, RSRC_CONF, "fastcgi shared memory file path"), AP_INIT_TAKE1("FcgidIPCDir", set_socketpath, NULL, RSRC_CONF, "fastcgi socket file path"), AP_INIT_TAKE1("FcgidSpawnScore", set_spawn_score, NULL, RSRC_CONF, "Score of spawn"), AP_INIT_TAKE1("FcgidSpawnScoreUpLimit", set_spawnscore_uplimit, NULL, RSRC_CONF, "Spawn score up limit"), AP_INIT_TAKE1("FcgidTerminationScore", set_termination_score, NULL, RSRC_CONF, "Score of termination"), AP_INIT_TAKE1("FcgidTimeScore", set_time_score, NULL, RSRC_CONF, "Score of passage of time (in seconds)"), AP_INIT_TAKE123("FcgidWrapper", set_wrapper_config, NULL, RSRC_CONF | ACCESS_CONF | OR_FILEINFO, "The CGI wrapper file an optional URL suffix and an optional flag"), AP_INIT_TAKE1("FcgidZombieScanInterval", set_zombie_scan_interval, NULL, RSRC_CONF, "scan interval for zombie process"), #ifdef WIN32 AP_INIT_FLAG("FcgidWin32PreventOrphans", set_win32_prevent_process_orphans, NULL, RSRC_CONF, "Prevented fcgi process orphaning during Apache worker " "abrupt shutdowns [see documentation]"), #endif /* The following directives are all deprecated in favor * of a consistent use of the Fcgid prefix. * Add all new command above this line. */ AP_INIT_TAKE1("BusyScanInterval", set_busy_scan_interval, NULL, RSRC_CONF, "Deprecated - Use 'FcgidBusyScanInterval' instead"), AP_INIT_TAKE1("BusyTimeout", set_busy_timeout, NULL, RSRC_CONF, "Deprecated - Use 'FcgidBusyTimeout' instead"), AP_INIT_TAKE12("DefaultInitEnv", add_default_env_vars, NULL, RSRC_CONF, "Deprecated - Use 'FcgidInitialEnv' instead"), AP_INIT_TAKE1("DefaultMaxClassProcessCount", set_max_class_process, NULL, RSRC_CONF, "Deprecated - Use 'FcgidMaxProcessesPerClass' instead"), AP_INIT_TAKE1("DefaultMinClassProcessCount", set_min_class_process, NULL, RSRC_CONF, "Deprecated - Use 'FcgidMinProcessesPerClass' instead"), AP_INIT_TAKE1("ErrorScanInterval", set_error_scan_interval, NULL, RSRC_CONF, "Deprecated - Use 'FcgidErrorScanInterval' instead"), AP_INIT_TAKE1("FastCgiAccessChecker", set_access_info, NULL, ACCESS_CONF | OR_FILEINFO, "Deprecated - Use 'FcgidAccessChecker' instead"), AP_INIT_FLAG("FastCgiAccessCheckerAuthoritative", set_access_authoritative, NULL, ACCESS_CONF | OR_FILEINFO, "Deprecated - Use 'FcgidAccessCheckerAuthoritative' instead"), AP_INIT_TAKE1("FastCgiAuthenticator", set_authenticator_info, NULL, ACCESS_CONF | OR_FILEINFO, "Deprecated - Use 'FcgidAuthenticator' instead"), AP_INIT_FLAG("FastCgiAuthenticatorAuthoritative", set_authenticator_authoritative, NULL, ACCESS_CONF | OR_FILEINFO, "Deprecated - Use 'FcgidAuthenticatorAuthoritative' instead"), AP_INIT_TAKE1("FastCgiAuthorizer", set_authorizer_info, NULL, ACCESS_CONF | OR_FILEINFO, "Deprecated - Use 'FcgidAuthorizer' instead"), AP_INIT_FLAG("FastCgiAuthorizerAuthoritative", set_authorizer_authoritative, NULL, ACCESS_CONF | OR_FILEINFO, "Deprecated - Use 'FcgidAuthorizerAuthoritative' instead"), AP_INIT_TAKE123("FCGIWrapper", set_wrapper_config, NULL, RSRC_CONF | ACCESS_CONF | OR_FILEINFO, "Deprecated - Use 'FcgidWrapper' instead"), AP_INIT_TAKE1("IdleScanInterval", set_idle_scan_interval, NULL, RSRC_CONF, "Deprecated - Use 'FcgidIdleScanInterval' instead"), AP_INIT_TAKE1("IdleTimeout", set_idle_timeout, NULL, RSRC_CONF, "Deprecated - Use 'FcgidIdleTimeout' instead"), AP_INIT_TAKE1("IPCCommTimeout", set_ipc_comm_timeout, NULL, RSRC_CONF, "Deprecated - Use 'FcgidIOTimeout' instead"), AP_INIT_TAKE1("IPCConnectTimeout", set_ipc_connect_timeout, NULL, RSRC_CONF, "Deprecated - Use 'FcgidConnectTimeout' instead"), AP_INIT_TAKE1("MaxProcessCount", set_max_process, NULL, RSRC_CONF, "Deprecated - Use 'FcgidMaxProcesses' instead"), AP_INIT_TAKE1("MaxRequestInMem", set_max_mem_request_len, NULL, RSRC_CONF, "Deprecated - Use 'FcgidMaxRequestInMem' instead"), AP_INIT_TAKE1("MaxRequestLen", set_max_request_len, NULL, RSRC_CONF, "Deprecated - Use 'FcgidMaxRequestLen' instead"), AP_INIT_TAKE1("MaxRequestsPerProcess", set_max_requests_per_process, NULL, RSRC_CONF, "Deprecated - Use 'FcgidMaxRequestsPerProcess' instead"), AP_INIT_TAKE1("OutputBufferSize", set_output_buffersize, NULL, RSRC_CONF, "Deprecated - Use 'FcgidOutputBufferSize' instead"), AP_INIT_TAKE1("PassHeader", add_pass_headers, NULL, RSRC_CONF, "Deprecated - Use 'FcgidPassHeader' instead"), AP_INIT_TAKE1("PHP_Fix_Pathinfo_Enable", set_php_fix_pathinfo_enable, NULL, RSRC_CONF, "Deprecated - Use 'FcgidFixPathinfo' instead"), AP_INIT_TAKE1("ProcessLifeTime", set_proc_lifetime, NULL, RSRC_CONF, "Deprecated - Use 'FcgidProcessLifeTime' instead"), AP_INIT_TAKE1("SharememPath", set_shmpath, NULL, RSRC_CONF, "Deprecated - Use 'FcgidProcessTableFile' instead"), AP_INIT_TAKE1("SocketPath", set_socketpath, NULL, RSRC_CONF, "Deprecated - Use 'FcgidIPCDir' instead"), AP_INIT_TAKE1("SpawnScore", set_spawn_score, NULL, RSRC_CONF, "Deprecated - Use 'FcgidSpawnScore' instead"), AP_INIT_TAKE1("SpawnScoreUpLimit", set_spawnscore_uplimit, NULL, RSRC_CONF, "Deprecated - Use 'FcgidSpawnScoreUpLimit' instead"), AP_INIT_TAKE1("TerminationScore", set_termination_score, NULL, RSRC_CONF, "Deprecated - Use 'FcgidTerminationScore' instead"), AP_INIT_TAKE1("TimeScore", set_time_score, NULL, RSRC_CONF, "Deprecated - Use 'FcgidTimeScore' instead"), AP_INIT_TAKE1("ZombieScanInterval", set_zombie_scan_interval, NULL, RSRC_CONF, "Deprecated - Use 'FcgidZombieScanInterval' instead"), {NULL} }; static int fcgid_pre_config(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *ptemp) { apr_status_t rv; APR_OPTIONAL_HOOK(ap, status_hook, fcgid_status_hook, NULL, NULL, APR_HOOK_MIDDLE); rv = procmgr_pre_config(pconf, plog, ptemp); if (rv != APR_SUCCESS) { return rv; } rv = proctable_pre_config(pconf, plog, ptemp); if (rv != APR_SUCCESS) { return rv; } return OK; } static void register_hooks(apr_pool_t * p) { ap_hook_pre_config(fcgid_pre_config, NULL, NULL, APR_HOOK_MIDDLE); ap_hook_post_config(fcgid_init, NULL, NULL, APR_HOOK_MIDDLE); ap_hook_child_init(initialize_child, NULL, NULL, APR_HOOK_MIDDLE); ap_hook_handler(fcgid_handler, NULL, NULL, APR_HOOK_MIDDLE); ap_hook_check_user_id(mod_fcgid_authenticator, NULL, NULL, APR_HOOK_MIDDLE); ap_hook_auth_checker(mod_fcgid_authorizer, NULL, NULL, APR_HOOK_MIDDLE); ap_hook_access_checker(mod_fcgid_check_access, NULL, NULL, APR_HOOK_MIDDLE); /* Insert fcgid output filter */ fcgid_filter_handle = ap_register_output_filter("FCGID_OUT", fcgid_filter, NULL, AP_FTYPE_RESOURCE - 10); } module AP_MODULE_DECLARE_DATA fcgid_module = { STANDARD20_MODULE_STUFF, create_fcgid_dir_config, /* create per-directory config structure */ merge_fcgid_dir_config, /* merge per-directory config structures */ create_fcgid_server_config, /* create per-server config structure */ merge_fcgid_server_config, /* merge per-server config structures */ fcgid_cmds, /* command apr_table_t */ register_hooks /* register hooks */ }; mod_fcgid-2.3.9/modules/fcgid/fcgid_bridge.c0000644000175000017500000007154112223417144020622 0ustar trawicktrawick/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "httpd.h" #include "http_request.h" #include "apr_strings.h" #include "apr_portable.h" #include "apr_pools.h" #include "apr_file_io.h" #include "util_script.h" #include "fcgid_bridge.h" #include "fcgid_pm.h" #include "fcgid_proctbl.h" #include "fcgid_proc.h" #include "fcgid_conf.h" #include "fcgid_spawn_ctl.h" #include "fcgid_protocol.h" #include "fcgid_bucket.h" #define FCGID_APPLY_TRY_COUNT 2 #define FCGID_REQUEST_COUNT 32 #define FCGID_BRIGADE_CLEAN_STEP 32 static fcgid_procnode *apply_free_procnode(request_rec *r, fcgid_command * command) { /* Scan idle list, find a node match inode, deviceid and groupid If there is no one there, return NULL */ fcgid_procnode *previous_node, *current_node, *next_node; fcgid_procnode *busy_list_header, *proc_table; apr_ino_t inode = command->inode; apr_dev_t deviceid = command->deviceid; uid_t uid = command->uid; gid_t gid = command->gid; const char *cmdline = command->cmdline; proc_table = proctable_get_table_array(); previous_node = proctable_get_idle_list(); busy_list_header = proctable_get_busy_list(); proctable_lock(r); current_node = &proc_table[previous_node->next_index]; while (current_node != proc_table) { next_node = &proc_table[current_node->next_index]; if (current_node->inode == inode && current_node->deviceid == deviceid && !strcmp(current_node->cmdline, cmdline) && current_node->vhost_id == command->vhost_id && current_node->uid == uid && current_node->gid == gid) { /* Unlink from idle list */ previous_node->next_index = current_node->next_index; /* Link to busy list */ current_node->next_index = busy_list_header->next_index; busy_list_header->next_index = current_node - proc_table; proctable_unlock(r); return current_node; } else previous_node = current_node; current_node = next_node; } proctable_unlock(r); /* Found nothing */ return NULL; } static void return_procnode(request_rec *r, fcgid_procnode *procnode, int communicate_error) { fcgid_procnode *previous_node, *current_node, *next_node; fcgid_procnode *proc_table = proctable_get_table_array(); fcgid_procnode *error_list_header = proctable_get_error_list(); fcgid_procnode *idle_list_header = proctable_get_idle_list(); fcgid_procnode *busy_list_header = proctable_get_busy_list(); proctable_lock(r); /* Unlink the node from busy list first */ previous_node = busy_list_header; current_node = &proc_table[previous_node->next_index]; while (current_node != proc_table) { next_node = &proc_table[current_node->next_index]; if (current_node == procnode) { /* Unlink from busy list */ previous_node->next_index = current_node->next_index; break; } else previous_node = current_node; current_node = next_node; } /* Return to error list or idle list */ if (communicate_error) { /* Link to error list */ procnode->next_index = error_list_header->next_index; error_list_header->next_index = procnode - proc_table; } else { /* Link to idle list */ procnode->next_index = idle_list_header->next_index; idle_list_header->next_index = procnode - proc_table; } proctable_unlock(r); } static int count_busy_processes(request_rec *r, fcgid_command *command) { int result = 0; fcgid_procnode *previous_node, *current_node, *next_node; fcgid_procnode *proc_table = proctable_get_table_array(); fcgid_procnode *busy_list_header = proctable_get_busy_list(); proctable_lock(r); previous_node = busy_list_header; current_node = &proc_table[previous_node->next_index]; while (current_node != proc_table) { if (current_node->inode == command->inode && current_node->deviceid == command->deviceid && !strcmp(current_node->cmdline, command->cmdline) && current_node->vhost_id == command->vhost_id && current_node->uid == command->uid && current_node->gid == command->gid) { result++; } next_node = &proc_table[current_node->next_index]; current_node = next_node; } proctable_unlock(r); return result; } apr_status_t bucket_ctx_cleanup(void *thectx) { /* Cleanup jobs: 1. Free bucket buffer 2. Return procnode NOTE: ipc will be clean when request pool cleanup, so I don't need to close it here */ fcgid_bucket_ctx *ctx = (fcgid_bucket_ctx *) thectx; request_rec *r = ctx->ipc.request; /* Free bucket buffer */ if (ctx->buffer) { apr_bucket_destroy(ctx->buffer); ctx->buffer = NULL; } /* proc_close_ipc() and ipc_handle_cleanup() do their own sanity * checks, but we'll do our own anyway */ if (ctx->ipc.ipc_handle_info) { proc_close_ipc(&ctx->ipc); ctx->ipc.ipc_handle_info = NULL; } if (ctx->procnode) { ++ctx->procnode->requests_handled; /* Return procnode I will return this slot to idle(or error) list */ if (ctx->procnode->diewhy == FCGID_DIE_BUSY_TIMEOUT) { ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, "mod_fcgid: %s took longer than busy timeout " "(%d secs)", r->uri, ctx->procnode->cmdopts.busy_timeout); return_procnode(r, ctx->procnode, 1 /* busy timeout */ ); } else if (ctx->has_error) { ctx->procnode->diewhy = FCGID_DIE_COMM_ERROR; return_procnode(r, ctx->procnode, 1 /* communication error */ ); } else if (ctx->procnode->cmdopts.max_requests_per_process && ctx->procnode->requests_handled >= ctx->procnode->cmdopts.max_requests_per_process) { ctx->procnode->diewhy = FCGID_DIE_LIFETIME_EXPIRED; return_procnode(r, ctx->procnode, 1 /* handled all requests */ ); } else return_procnode(r, ctx->procnode, 0 /* communication ok */ ); ctx->procnode = NULL; } return APR_SUCCESS; } static int getsfunc_fcgid_BRIGADE(char *buf, int len, void *arg) { apr_bucket_brigade *bb = (apr_bucket_brigade *) arg; const char *dst_end = buf + len - 1; /* leave room for terminating null */ char *dst = buf; apr_bucket *e = APR_BRIGADE_FIRST(bb); apr_status_t rv; int done = 0; int getLF = 0; int getColon = 0; while ((dst < dst_end) && !done && e != APR_BRIGADE_SENTINEL(bb)) { const char *bucket_data; apr_size_t bucket_data_len; const char *src; const char *src_end; apr_bucket *next; rv = apr_bucket_read(e, &bucket_data, &bucket_data_len, APR_BLOCK_READ); if (rv != APR_SUCCESS) { return 0; } /* Move on to next bucket if it's fastcgi header bucket */ if (e->type == &ap_bucket_type_fcgid_header || e->type == &apr_bucket_type_immortal) { next = APR_BUCKET_NEXT(e); apr_bucket_delete(e); e = next; if (getLF) { done = 1; } continue; } if (bucket_data_len == 0) return 0; /* Base on RFC2616 section 4.2 */ src = bucket_data; src_end = bucket_data + bucket_data_len; while ((src < src_end) && (dst < dst_end) && !done) { if (*src == ':') getColon = 1; if (getLF && ((*src != ' ' && *src != '\t') || !getColon)) { done = 1; getColon = 0; break; } else if (getLF && (*src == ' ' || *src == '\t')) { *dst++ = '\r'; *dst++ = '\n'; getLF = 0; } if (*src == '\n') { getLF = 1; } else if (*src != '\r') { *dst++ = *src; } src++; } if (src < src_end) { apr_bucket_split(e, src - bucket_data); } next = APR_BUCKET_NEXT(e); apr_bucket_delete(e); e = next; } *dst = 0; return done; } static int handle_request_ipc(request_rec *r, int role, apr_bucket_brigade *output_brigade, fcgid_bucket_ctx *bucket_ctx, const char **location_ptr) { int cond_status; apr_status_t rv; apr_bucket_brigade *brigade_stdout; char sbuf[MAX_STRING_LEN]; const char *location; /* Write output_brigade to fastcgi server */ if ((rv = proc_write_ipc(&bucket_ctx->ipc, output_brigade)) != APR_SUCCESS) { bucket_ctx->has_error = 1; return HTTP_INTERNAL_SERVER_ERROR; } /* Create brigade */ brigade_stdout = apr_brigade_create(r->pool, r->connection->bucket_alloc); APR_BRIGADE_INSERT_TAIL(brigade_stdout, ap_bucket_fcgid_header_create(r->connection-> bucket_alloc, bucket_ctx)); /* Check the script header first; return immediately on error. */ if ((cond_status = ap_scan_script_header_err_core(r, sbuf, getsfunc_fcgid_BRIGADE, brigade_stdout))) { /* * cond_status could be HTTP_NOT_MODIFIED in the case that the FCGI * script does not set an explicit status and ap_meets_conditions, * which is called by ap_scan_script_header_err_brigade, detects that * the conditions of the requests are met and the response is * not modified. * In this case set r->status and return OK in order to prevent * running through the error processing stack as this would * break with mod_cache, if the conditions had been set by * mod_cache itself to validate a stale entity. * BTW: We circumvent the error processing stack anyway if the * FCGI script set an explicit status code (whatever it is) and * the only possible values for cond_status here are: * * HTTP_NOT_MODIFIED (set by ap_meets_conditions) * HTTP_PRECONDITION_FAILED (set by ap_meets_conditions) * HTTP_GATEWAY_TIME_OUT (script timed out, returned no headers) * HTTP_INTERNAL_SERVER_ERROR (if something went wrong during the * processing of the response of the FCGI script, e.g broken headers * or a crashed FCGI process). */ if (cond_status == HTTP_NOT_MODIFIED) { /* We need to remove our fcgid_filter before returning this * status and code; otherwise, when ap_process_async_request() * invokes ap_finalize_request_protocol() and that calls * ap_pass_brigade(), fcgid_filter notices it has an empty * brigade and returns without calling ap_pass_brigade() itself, * which incorrectly circumvents the standard output filters. */ ap_remove_output_filter(r->output_filters); r->status = cond_status; return OK; } return cond_status; } if (role == FCGI_AUTHORIZER) { return cond_status; } /* Check redirect */ location = apr_table_get(r->headers_out, "Location"); if (location && location[0] == '/' && r->status == 200) { /* This redirect needs to be a GET no matter what the original * method was. */ r->method = apr_pstrdup(r->pool, "GET"); r->method_number = M_GET; /* 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. */ apr_table_unset(r->headers_in, "Content-Length"); /* Setting this Location header value causes handle_request() to * invoke ap_internal_redirect_handler(); that calls * internal_internal_redirect() which sets the new sub-request's * r->output_filters back to r->proto_output_filters before * running the sub-request's handler. Because we return here * without invoking ap_pass_brigade(), our fcgid_filter is ignored. */ *location_ptr = location; return OK; } else if (location && r->status == 200) { /* XX Note that if a script wants to produce its own Redirect * body, it now has to explicitly *say* "Status: 302" */ /* This return code causes ap_process_async_request() to invoke * ap_die(); that calls ap_send_error_response(), which resets * r->output_filters back to r->proto_output_filters, thus removing * our fcgid_filter from the output chain before making a final call * to ap_finalize_request_protocol(), which passes the brigade to * the standard output filters. */ return HTTP_MOVED_TEMPORARILY; } /* Now pass any remaining response body data to output filters */ if ((rv = ap_pass_brigade(r->output_filters, brigade_stdout)) != APR_SUCCESS) { if (!APR_STATUS_IS_ECONNABORTED(rv)) { ap_log_rerror(APLOG_MARK, APLOG_WARNING, rv, r, "mod_fcgid: ap_pass_brigade failed in " "handle_request_ipc function"); } return HTTP_INTERNAL_SERVER_ERROR; } return cond_status; } static int handle_request(request_rec * r, int role, fcgid_cmd_conf *cmd_conf, apr_bucket_brigade * output_brigade) { fcgid_command fcgi_request; fcgid_bucket_ctx *bucket_ctx; int i, j, cond_status; const char *location = NULL; bucket_ctx = apr_pcalloc(r->pool, sizeof(*bucket_ctx)); bucket_ctx->ipc.request = r; apr_pool_cleanup_register(r->pool, bucket_ctx, bucket_ctx_cleanup, apr_pool_cleanup_null); procmgr_init_spawn_cmd(&fcgi_request, r, cmd_conf); /* Try to get a connected ipc handle */ for (i = 0; i < FCGID_REQUEST_COUNT; i++) { /* Apply a free process slot, send a spawn request if I can't get one */ for (j = 0; j < FCGID_APPLY_TRY_COUNT; j++) { bucket_ctx->ipc.connect_timeout = fcgi_request.cmdopts.ipc_connect_timeout; bucket_ctx->ipc.communation_timeout = fcgi_request.cmdopts.ipc_comm_timeout; /* Apply a process slot */ bucket_ctx->procnode = apply_free_procnode(r, &fcgi_request); if (bucket_ctx->procnode) break; /* Avoid sleeping the very first time through if there are no busy processes; the problem is just that we haven't spawned anything yet, so waiting is pointless */ if (i > 0 || j > 0 || count_busy_processes(r, &fcgi_request)) { apr_sleep(apr_time_from_sec(1)); bucket_ctx->procnode = apply_free_procnode(r, &fcgi_request); if (bucket_ctx->procnode) break; } /* Send a spawn request if I can't get a process slot */ procmgr_send_spawn_cmd(&fcgi_request, r); } /* Connect to the fastcgi server */ if (bucket_ctx->procnode) { if (proc_connect_ipc(bucket_ctx->procnode, &bucket_ctx->ipc) != APR_SUCCESS) { proc_close_ipc(&bucket_ctx->ipc); bucket_ctx->procnode->diewhy = FCGID_DIE_CONNECT_ERROR; return_procnode(r, bucket_ctx->procnode, 1 /* has error */ ); bucket_ctx->procnode = NULL; } else break; } } /* Now I get a connected ipc handle */ if (!bucket_ctx->procnode) { ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, "mod_fcgid: can't apply process slot for %s", cmd_conf->cmdline); return HTTP_SERVICE_UNAVAILABLE; } bucket_ctx->active_time = bucket_ctx->procnode->last_active_time = apr_time_now(); bucket_ctx->procnode->diewhy = FCGID_DIE_KILLSELF; cond_status = handle_request_ipc(r, role, output_brigade, bucket_ctx, &location); /* Release the process ASAP. This may already have been done in * ap_pass_brigade() by fcgid_header_bucket_read(), but not in the * case where handle_request_ipc() returned early without reading * the body of the HTTP response. This could be because of an error, * or because of a role or a status code which permits us to ignore * the message body. * * As an example, when handling a request in the FCGI_AUTHORIZER role, * we don't read through to the end of the response from the process, * we just read the HTTP headers. That means each phase of the * request handling sequence (e.g., authentication, authorization, etc.) * will require its own process unless we make sure to always release * any process we acquired regardless of whether we're reading the * response body. * * As another example, if we perform or cause an internal redirection * (for instance, by returning an error code that invokes a script * handler in ap_die() because of an ErrorDocument configuration), then * we must also release the process we acquired here so that it is * potentially available during the next handling phase. */ apr_pool_cleanup_run(r->pool, bucket_ctx, bucket_ctx_cleanup); /* Perform internal redirection if necessary */ if (location) { ap_internal_redirect_handler(location, r); } /* Return condition status */ return cond_status; } static int add_request_body(request_rec *r, apr_pool_t *request_pool, apr_bucket_brigade *output_brigade) { apr_bucket *bucket_input, *bucket_header; apr_file_t *fd = NULL; apr_off_t cur_pos = 0, request_size = 0; apr_status_t rv; FCGI_Header *stdin_request_header; fcgid_server_conf *sconf = ap_get_module_config(r->server->module_config, &fcgid_module); int seen_eos = 0; /* Stdin header and body */ /* I have to read all the request into memory before sending it to fastcgi application server, this prevents slow clients from keeping the server in processing too long. But sometimes it's not acceptable (think about uploading a large attachment) Request will be stored in tmp file if the size larger than max_mem_request_len */ apr_bucket_brigade *input_brigade = apr_brigade_create(request_pool, r->connection-> bucket_alloc); apr_bucket_brigade *tmp_brigade = apr_brigade_create(request_pool, r->connection-> bucket_alloc); do { int loop_counter = 0; if ((rv = ap_get_brigade(r->input_filters, input_brigade, AP_MODE_READBYTES, APR_BLOCK_READ, HUGE_STRING_LEN)) != APR_SUCCESS) { ap_log_rerror(APLOG_MARK, APLOG_WARNING, rv, r, "mod_fcgid: can't get data from http client"); apr_brigade_destroy(output_brigade); apr_brigade_destroy(tmp_brigade); apr_brigade_destroy(input_brigade); return HTTP_INTERNAL_SERVER_ERROR; } while ((bucket_input = APR_BRIGADE_FIRST(input_brigade)) != APR_BRIGADE_SENTINEL(input_brigade)) { const char *data; apr_size_t len; apr_bucket *bucket_stdin; ++loop_counter; if ((loop_counter % FCGID_BRIGADE_CLEAN_STEP) == 0) { apr_brigade_cleanup(tmp_brigade); } APR_BUCKET_REMOVE(bucket_input); APR_BRIGADE_INSERT_TAIL(tmp_brigade, bucket_input); if (APR_BUCKET_IS_EOS(bucket_input)) { seen_eos = 1; break; } if (APR_BUCKET_IS_METADATA(bucket_input)) continue; if ((rv = apr_bucket_read(bucket_input, &data, &len, APR_BLOCK_READ)) != APR_SUCCESS) { ap_log_rerror(APLOG_MARK, APLOG_WARNING, rv, r, "mod_fcgid: can't read request from HTTP client"); apr_brigade_destroy(input_brigade); apr_brigade_destroy(tmp_brigade); apr_brigade_destroy(output_brigade); return HTTP_INTERNAL_SERVER_ERROR; } /* Append a header, and the the bucket */ stdin_request_header = apr_bucket_alloc(sizeof(FCGI_Header), r->connection-> bucket_alloc); bucket_header = apr_bucket_heap_create((const char *) stdin_request_header, sizeof(*stdin_request_header), apr_bucket_free, r->connection->bucket_alloc); request_size += len; if (request_size > sconf->max_request_len) { ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, "mod_fcgid: HTTP request length %" APR_OFF_T_FMT " (so far) exceeds MaxRequestLen (%" APR_OFF_T_FMT ")", request_size, sconf->max_request_len); return HTTP_INTERNAL_SERVER_ERROR; } if (request_size > sconf->max_mem_request_len) { apr_size_t wrote_len; static const char *fd_key = "fcgid_fd"; if (fd == NULL) { void *tmp; apr_pool_userdata_get(&tmp, fd_key, r->connection->pool); fd = tmp; if (fd != NULL) { if ((rv = apr_file_trunc(fd, 0)) != APR_SUCCESS) { ap_log_rerror(APLOG_MARK, APLOG_WARNING, rv, r, "mod_fcgid: can't truncate existing " "temporary file"); return HTTP_INTERNAL_SERVER_ERROR; } } } if (fd == NULL) { const char *tempdir = NULL; char *template; rv = apr_temp_dir_get(&tempdir, r->pool); if (rv != APR_SUCCESS) { ap_log_rerror(APLOG_MARK, APLOG_WARNING, rv, r, "mod_fcgid: can't get tmp dir"); return HTTP_INTERNAL_SERVER_ERROR; } apr_filepath_merge(&template, tempdir, "fcgid.tmp.XXXXXX", APR_FILEPATH_NATIVE, r->pool); rv = apr_file_mktemp(&fd, template, 0, r->connection->pool); if (rv != APR_SUCCESS) { ap_log_rerror(APLOG_MARK, APLOG_WARNING, rv, r, "mod_fcgid: can't open tmp file fot stdin request"); return HTTP_INTERNAL_SERVER_ERROR; } apr_pool_userdata_set((const void *) fd, fd_key, apr_pool_cleanup_null, r->connection->pool); } /* Write request to tmp file */ if ((rv = apr_file_write_full(fd, (const void *) data, len, &wrote_len)) != APR_SUCCESS || len != wrote_len) { ap_log_rerror(APLOG_MARK, APLOG_WARNING, rv, r, "mod_fcgid: can't write tmp file for stdin request"); return HTTP_INTERNAL_SERVER_ERROR; } /* Create file bucket */ bucket_stdin = apr_bucket_file_create(fd, cur_pos, len, r->pool, r->connection->bucket_alloc); cur_pos += len; } else { if (APR_BUCKET_IS_HEAP(bucket_input)) apr_bucket_copy(bucket_input, &bucket_stdin); else { /* mod_ssl have a bug? */ char *pcopydata = apr_bucket_alloc(len, r->connection->bucket_alloc); memcpy(pcopydata, data, len); bucket_stdin = apr_bucket_heap_create(pcopydata, len, apr_bucket_free, r->connection->bucket_alloc); } } if (!init_header(FCGI_STDIN, 1, len, 0, stdin_request_header)) { ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, "mod_fcgid: header overflow"); apr_brigade_destroy(input_brigade); apr_brigade_destroy(tmp_brigade); apr_brigade_destroy(output_brigade); return HTTP_INTERNAL_SERVER_ERROR; } APR_BRIGADE_INSERT_TAIL(output_brigade, bucket_header); APR_BRIGADE_INSERT_TAIL(output_brigade, bucket_stdin); } apr_brigade_cleanup(input_brigade); apr_brigade_cleanup(tmp_brigade); } while (!seen_eos); apr_brigade_destroy(input_brigade); apr_brigade_destroy(tmp_brigade); /* Append an empty body stdin header */ stdin_request_header = apr_bucket_alloc(sizeof(FCGI_Header), r->connection->bucket_alloc); bucket_header = apr_bucket_heap_create((const char *) stdin_request_header, sizeof(*stdin_request_header), apr_bucket_free, r->connection->bucket_alloc); if (!init_header(FCGI_STDIN, 1, 0, 0, stdin_request_header)) { ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, "mod_fcgid: header overflow"); return HTTP_INTERNAL_SERVER_ERROR; } APR_BRIGADE_INSERT_TAIL(output_brigade, bucket_header); return 0; } int bridge_request(request_rec * r, int role, fcgid_cmd_conf *cmd_conf) { apr_bucket_brigade *output_brigade; apr_bucket *bucket_eos; char **envp = ap_create_environment(r->pool, r->subprocess_env); int rc; /* Create brigade for the request to fastcgi server */ output_brigade = apr_brigade_create(r->pool, r->connection->bucket_alloc); /* Build the begin request and environ request, append them to output_brigade */ if (!build_begin_block (role, r, r->connection->bucket_alloc, output_brigade) || !build_env_block(r, envp, r->connection->bucket_alloc, output_brigade)) { ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, "mod_fcgid: can't build begin or env request"); return HTTP_INTERNAL_SERVER_ERROR; } if (role == FCGI_RESPONDER) { rc = add_request_body(r, r->pool, output_brigade); if (rc) { return rc; } } /* The eos bucket now */ bucket_eos = apr_bucket_eos_create(r->connection->bucket_alloc); APR_BRIGADE_INSERT_TAIL(output_brigade, bucket_eos); /* Bridge the request */ return handle_request(r, role, cmd_conf, output_brigade); } mod_fcgid-2.3.9/modules/fcgid/fcgid_filter.h0000644000175000017500000000172111157610742020655 0ustar trawicktrawick/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef FCGID_FILTER_H #define FCGID_FILTER_H #include "util_filter.h" #include "apr_buckets.h" apr_status_t fcgid_filter(ap_filter_t * f, apr_bucket_brigade * bb); #endif mod_fcgid-2.3.9/modules/fcgid/fcgid_proctbl.h0000644000175000017500000001026611462406371021041 0ustar trawicktrawick/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef FCGID_TABLE_H #define FCGID_TABLE_H #include #include "httpd.h" #include "apr_thread_proc.h" #include "fcgid_global.h" #include "fcgid_conf.h" /* Increase it if necessary */ #define FCGID_MAX_APPLICATION (1024) /* FCGID_MAX_APPLICATION + 4 list headers */ #define FCGID_PROC_TABLE_SIZE (FCGID_MAX_APPLICATION+4) /* nNextIndex is for making a node list, there are four kind of list: 1) free list: no process associate to this node 2) busy list: a process is associated, and it's handling request 3) idle list: a process is associated, and it's waiting request 4) error list: a process is associated, and killing the process now */ typedef struct { /* only one of next_index or node_type is used, depending on context */ int next_index; /* the next array index in the list */ int node_type; /* the type of this node, used in fcgid_status_hook() only */ apr_pool_t *proc_pool; /* pool for process */ apr_proc_t proc_id; /* the process id */ char executable_path[FCGID_PATH_MAX]; /* executable file path */ char socket_path[FCGID_PATH_MAX]; /* cgi application socket path */ apr_ino_t inode; /* cgi file inode */ apr_dev_t deviceid; /* cgi file device id */ char cmdline[FCGID_CMDLINE_MAX]; /* entire command line */ gid_t gid; /* for suEXEC */ uid_t uid; /* for suEXEC */ int vhost_id; /* the vhost to which this process belongs (the server_rec * addr fails with some mass-vhost mods which allocate * them per-request) */ apr_time_t start_time; /* the time of this process create */ apr_time_t last_active_time; /* the time this process last active */ int requests_handled; /* number of requests process has handled */ char diewhy; /* why it die */ fcgid_cmd_options cmdopts; /* context-specific configuration */ } fcgid_procnode; /* Macros for diewhy */ #define FCGID_DIE_KILLSELF 0 #define FCGID_DIE_IDLE_TIMEOUT 1 #define FCGID_DIE_LIFETIME_EXPIRED 2 #define FCGID_DIE_BUSY_TIMEOUT 3 #define FCGID_DIE_CONNECT_ERROR 4 #define FCGID_DIE_COMM_ERROR 5 #define FCGID_DIE_SHUTDOWN 6 typedef struct { int must_exit; /* All processes using this share memory must exit */ } fcgid_global_share; typedef struct { fcgid_global_share global; fcgid_procnode procnode_array[FCGID_PROC_TABLE_SIZE]; } fcgid_share; apr_status_t proctable_child_init(server_rec * main_server, apr_pool_t * pchild); apr_status_t proctable_pre_config(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp); apr_status_t proctable_post_config(server_rec * main_server, apr_pool_t * pconf); fcgid_procnode *proctable_get_free_list(void); fcgid_procnode *proctable_get_busy_list(void); fcgid_procnode *proctable_get_idle_list(void); fcgid_procnode *proctable_get_error_list(void); fcgid_procnode *proctable_get_table_array(void); size_t proctable_get_table_size(void); fcgid_global_share *proctable_get_globalshare(void); void proctable_pm_lock(server_rec *s); void proctable_pm_unlock(server_rec *s); void proctable_lock(request_rec *r); void proctable_unlock(request_rec *r); /* Just for debug */ void proctable_print_debug_info(server_rec * main_server); #endif mod_fcgid-2.3.9/modules/fcgid/fcgid_bucket.h0000644000175000017500000000250511160221513020632 0ustar trawicktrawick/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef FCGID_BUCKET_H #define FCGID_BUCKET_H #include "httpd.h" #include "fcgid_proc.h" typedef struct fcgid_bucket_ctx_t { fcgid_ipc ipc; apr_bucket *buffer; fcgid_procnode *procnode; apr_time_t active_time; int has_error; } fcgid_bucket_ctx; extern const apr_bucket_type_t ap_bucket_type_fcgid_header; apr_bucket *ap_bucket_fcgid_header_create(apr_bucket_alloc_t * list, fcgid_bucket_ctx * ctx); apr_bucket *ap_bucket_fcgid_header_make(apr_bucket *, fcgid_bucket_ctx *); #endif mod_fcgid-2.3.9/modules/fcgid/fcgid_proc_unix.c0000644000175000017500000007150612166062610021375 0ustar trawicktrawick/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include /* For TCP_NODELAY */ #include #include #define CORE_PRIVATE #include "httpd.h" #include "apr_version.h" #include "apr_thread_proc.h" #include "apr_strings.h" #include "apr_portable.h" #include "apr_pools.h" #include "apr_network_io.h" #include "ap_mpm.h" #include "http_config.h" #include "mpm_common.h" #include "util_script.h" #include "unixd.h" #include "mod_core.h" #include "mod_cgi.h" #include "apr_tables.h" #include "fcgid_proc.h" #include "fcgid_proctbl.h" #include "fcgid_protocol.h" #include "fcgid_conf.h" #include "fcgid_pm.h" #include "fcgid_spawn_ctl.h" #if MODULE_MAGIC_NUMBER_MAJOR < 20081201 #define ap_unixd_config unixd_config #endif #if APR_MAJOR_VERSION < 1 #define APR_FPROT_UWRITE APR_UWRITE #define APR_FPROT_UREAD APR_UREAD #define APR_FPROT_UEXECUTE APR_UEXECUTE #endif #define DEFAULT_FCGID_LISTENBACKLOG 5 typedef struct { int handle_socket; } fcgid_namedpipe_handle; static int g_process_counter = 0; static apr_status_t ap_unix_create_privileged_process(apr_proc_t *newproc, const char *progname, const char *const *args, const char *const *env, apr_procattr_t *attr, ap_unix_identity_t *ugid, apr_pool_t *p) { int i = 0; const char **newargs; const char *newprogname; const char *execuser, *execgroup; const char *argv0; if (!ap_unixd_config.suexec_enabled) { return apr_proc_create(newproc, progname, args, env, attr, p); } argv0 = ap_strrchr_c(progname, '/'); /* Allow suexec's "/" check to succeed */ if (argv0 != NULL) { argv0++; } else { argv0 = progname; } if (ugid->userdir) { execuser = apr_psprintf(p, "~%ld", (long) ugid->uid); } else { execuser = apr_psprintf(p, "%ld", (long) ugid->uid); } execgroup = apr_psprintf(p, "%ld", (long) ugid->gid); if (!execuser || !execgroup) { return APR_ENOMEM; } i = 0; while (args[i]) { i++; } /* allocate space for 4 new args, the input args, and a null terminator */ newargs = apr_palloc(p, sizeof(char *) * (i + 4)); newprogname = SUEXEC_BIN; newargs[0] = SUEXEC_BIN; newargs[1] = execuser; newargs[2] = execgroup; newargs[3] = apr_pstrdup(p, argv0); /* ** using a shell to execute suexec makes no sense thus ** we force everything to be APR_PROGRAM, and never ** APR_SHELLCMD */ if (apr_procattr_cmdtype_set(attr, APR_PROGRAM) != APR_SUCCESS) { return APR_EGENERAL; } i = 1; do { newargs[i + 3] = args[i]; } while (args[i++]); return apr_proc_create(newproc, newprogname, newargs, env, attr, p); } static apr_status_t fcgid_create_privileged_process(apr_proc_t *newproc, const char *progname, const char *const *args, const char *const *env, apr_procattr_t *attr, fcgid_proc_info *procinfo, apr_pool_t *p) { ap_unix_identity_t ugid; if (!ap_unixd_config.suexec_enabled || (procinfo->uid == (uid_t) - 1 && procinfo->gid == (gid_t) - 1)) { return apr_proc_create(newproc, progname, args, env, attr, p); } ugid.gid = procinfo->gid; ugid.uid = procinfo->uid; ugid.userdir = procinfo->userdir; return ap_unix_create_privileged_process(newproc, progname, args, env, attr, &ugid, p); } static apr_status_t socket_file_cleanup(void *theprocnode) { fcgid_procnode *procnode = (fcgid_procnode *) theprocnode; unlink(procnode->socket_path); return APR_SUCCESS; } static void log_setid_failure(const char *proc_type, const char *id_type, uid_t user_id) { char errno_desc[120]; char errmsg[240]; apr_strerror(errno, errno_desc, sizeof errno_desc); apr_snprintf(errmsg, sizeof errmsg, "(%d)%s: %s unable to set %s to %ld\n", errno, errno_desc, proc_type, id_type, (long)user_id); write(STDERR_FILENO, errmsg, strlen(errmsg)); } /* When suexec is enabled, this runs in the forked child * process prior to exec(). */ static apr_status_t exec_setuid_cleanup(void *dummy) { if (seteuid(0) == -1) { log_setid_failure("mod_fcgid child", "effective uid", 0); _exit(1); } if (setuid(ap_unixd_config.user_id) == -1) { log_setid_failure("mod_fcgid child", "uid", ap_unixd_config.user_id); _exit(1); } return APR_SUCCESS; } apr_status_t proc_spawn_process(const char *cmdline, fcgid_proc_info *procinfo, fcgid_procnode *procnode) { server_rec *main_server = procinfo->main_server; fcgid_server_conf *sconf = ap_get_module_config(main_server->module_config, &fcgid_module); apr_status_t rv = APR_SUCCESS; apr_file_t *file; apr_proc_t tmpproc; int omask, retcode, unix_socket; char **proc_environ; struct sockaddr_un unix_addr; apr_procattr_t *procattr = NULL; int len; const char **wargv; /* Build wrapper args */ apr_tokenize_to_argv(cmdline, (char ***)&wargv, procnode->proc_pool); /* Create UNIX domain socket before spawn */ /* Generate a UNIX domain socket file path */ memset(&unix_addr, 0, sizeof(unix_addr)); unix_addr.sun_family = AF_UNIX; len = apr_snprintf(unix_addr.sun_path, sizeof(unix_addr.sun_path), "%s/%" APR_PID_T_FMT ".%d", sconf->sockname_prefix, getpid(), g_process_counter++); /* check for truncation of the socket path * * cheap but overly zealous check for sun_path overflow: if length of * prepared string is at the limit, assume truncation */ if (len + 1 == sizeof(unix_addr.sun_path) || len >= sizeof procnode->socket_path) { ap_log_error(APLOG_MARK, APLOG_ERR, 0, main_server, "mod_fcgid: socket path length exceeds compiled-in limits"); return APR_EGENERAL; } apr_cpystrn(procnode->socket_path, unix_addr.sun_path, sizeof(procnode->socket_path)); /* truncation already checked for in handler or FcgidWrapper parser */ AP_DEBUG_ASSERT(wargv[0] != NULL); AP_DEBUG_ASSERT(strlen(wargv[0]) < sizeof(procnode->executable_path)); apr_cpystrn(procnode->executable_path, wargv[0], sizeof(procnode->executable_path)); /* Unlink the file just in case */ unlink(unix_addr.sun_path); /* Create the socket */ if ((unix_socket = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { ap_log_error(APLOG_MARK, APLOG_ERR, errno, main_server, "mod_fcgid: couldn't create unix domain socket"); return errno; } /* Register cleanups to * 1. Unlink the socket when the process exits * 2. (suexec mode only, in the child cleanup) Switch to the configured uid */ if (ap_unixd_config.suexec_enabled) { apr_pool_cleanup_register(procnode->proc_pool, procnode, socket_file_cleanup, exec_setuid_cleanup); } else { apr_pool_cleanup_register(procnode->proc_pool, procnode, socket_file_cleanup, apr_pool_cleanup_null); } /* Bind the socket */ omask = umask(0077); retcode = bind(unix_socket, (struct sockaddr *) &unix_addr, sizeof(unix_addr)); umask(omask); if (retcode < 0) { ap_log_error(APLOG_MARK, APLOG_ERR, errno, main_server, "mod_fcgid: couldn't bind unix domain socket %s", unix_addr.sun_path); close(unix_socket); return errno; } /* IPC directory permissions are safe, but avoid confusion */ /* Not all flavors of unix use the current umask for AF_UNIX perms */ rv = apr_file_perms_set(unix_addr.sun_path, APR_FPROT_UREAD|APR_FPROT_UWRITE|APR_FPROT_UEXECUTE); if (rv != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_CRIT, rv, main_server, "mod_fcgid: Couldn't set permissions on unix domain socket %s", unix_addr.sun_path); return rv; } /* Listen the socket */ if (listen(unix_socket, DEFAULT_FCGID_LISTENBACKLOG) < 0) { ap_log_error(APLOG_MARK, APLOG_ERR, errno, main_server, "mod_fcgid: couldn't listen on unix domain socket"); close(unix_socket); return errno; } /* Correct the file owner */ if (!geteuid()) { if (chown(unix_addr.sun_path, ap_unixd_config.user_id, -1) < 0) { ap_log_error(APLOG_MARK, APLOG_ERR, errno, main_server, "mod_fcgid: couldn't change owner of unix domain socket %s", unix_addr.sun_path); close(unix_socket); return errno; } } { int oldflags = fcntl(unix_socket, F_GETFD, 0); if (oldflags < 0) { ap_log_error(APLOG_MARK, APLOG_WARNING, apr_get_os_error(), procinfo->main_server, "mod_fcgid: fcntl F_GETFD failed"); close(unix_socket); return errno; } oldflags |= FD_CLOEXEC; if (fcntl(unix_socket, F_SETFD, oldflags) < 0) { ap_log_error(APLOG_MARK, APLOG_WARNING, apr_get_os_error(), procinfo->main_server, "mod_fcgid: fcntl F_SETFD failed"); close(unix_socket); return errno; } } /* Build environment variables */ proc_environ = ap_create_environment(procnode->proc_pool, procinfo->proc_environ); if (!proc_environ) { ap_log_error(APLOG_MARK, APLOG_WARNING, apr_get_os_error(), procinfo->main_server, "mod_fcgid: can't build environment variables"); close(unix_socket); return APR_ENOMEM; } /* Prepare the fork */ if ((rv = apr_procattr_create(&procattr, procnode->proc_pool)) != APR_SUCCESS || (rv = apr_procattr_child_err_set(procattr, procinfo->main_server->error_log, NULL)) != APR_SUCCESS || (rv = apr_procattr_child_out_set(procattr, procinfo->main_server->error_log, NULL)) != APR_SUCCESS || (rv = apr_procattr_dir_set(procattr, ap_make_dirstr_parent(procnode->proc_pool, wargv[0]))) != APR_SUCCESS || (rv = apr_procattr_cmdtype_set(procattr, APR_PROGRAM)) != APR_SUCCESS || (rv = apr_os_file_put(&file, &unix_socket, 0, procnode->proc_pool)) != APR_SUCCESS || (rv = apr_procattr_child_in_set(procattr, file, NULL)) != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_ERR, rv, procinfo->main_server, "mod_fcgid: couldn't set child process attributes: %s", unix_addr.sun_path); close(unix_socket); return rv; } /* fork and exec now */ /* Note, don't pass &(procnode->proc_id) to fcgid_create_privileged_process(), * for it's a share memory address, both parent and child process may modify * procnode->proc_id->pid, so sometimes it's 0 and sometimes it's >0 */ rv = fcgid_create_privileged_process(&tmpproc, wargv[0], wargv, (const char *const *)proc_environ, procattr, procinfo, procnode->proc_pool); if (ap_unixd_config.suexec_enabled) { /* Prior to creating the child process, a child cleanup was registered * to switch the uid in the child. No-op the child cleanup for this * pool so that it won't run again as other child processes are created. * (The cleanup will be registered for the pool associated with those * processes too.) */ apr_pool_child_cleanup_set(procnode->proc_pool, procnode, socket_file_cleanup, apr_pool_cleanup_null); } /* Close socket before try to connect to it */ close(unix_socket); procnode->proc_id = tmpproc; if (rv != APR_SUCCESS) { memset(&procnode->proc_id, 0, sizeof(procnode->proc_id)); ap_log_error(APLOG_MARK, APLOG_ERR, rv, procinfo->main_server, "mod_fcgid: can't run %s", wargv[0]); } return rv; } static apr_status_t proc_kill_internal(fcgid_procnode *procnode, int sig) { /* su as root before sending signal, for suEXEC */ apr_status_t rv; if (procnode->proc_id.pid == 0) { /* procnode->proc_id.pid be 0 while fcgid_create_privileged_process() fail */ return APR_SUCCESS; } if (ap_unixd_config.suexec_enabled && seteuid(0) != 0) { /* can't gain privileges to send signal (should not occur); do NOT * proceed, as something is broken with current identity */ log_setid_failure("mod_fcgid PM", "effective uid", 0); _exit(1); } rv = apr_proc_kill(&(procnode->proc_id), sig); if (ap_unixd_config.suexec_enabled && seteuid(ap_unixd_config.user_id) != 0) { /* can't drop privileges after signalling (should not occur); do NOT * proceed any further as euid(0)! */ log_setid_failure("mod_fcgid PM", "effective uid", ap_unixd_config.user_id); _exit(1); } return rv; } apr_status_t proc_kill_gracefully(fcgid_procnode *procnode, server_rec *main_server) { return proc_kill_internal(procnode, SIGTERM); } apr_status_t proc_kill_force(fcgid_procnode * procnode, server_rec * main_server) { return proc_kill_internal(procnode, SIGKILL); } apr_status_t proc_wait_process(server_rec *main_server, fcgid_procnode *procnode) { apr_status_t rv; int exitcode; apr_exit_why_e exitwhy; rv = apr_proc_wait(&(procnode->proc_id), &exitcode, &exitwhy, APR_NOWAIT); if (rv == APR_CHILD_DONE || rv == APR_EGENERAL) { /* Log why and how it die */ proc_print_exit_info(procnode, exitcode, exitwhy, main_server); /* Register the death */ register_termination(main_server, procnode); /* Destroy pool */ apr_pool_destroy(procnode->proc_pool); procnode->proc_pool = NULL; memset(&procnode->proc_id, 0, sizeof(procnode->proc_id)); return APR_CHILD_DONE; } return rv; } static apr_status_t ipc_handle_cleanup(void *thesocket) { fcgid_namedpipe_handle *handle_info = (fcgid_namedpipe_handle *) thesocket; if (handle_info) { if (handle_info->handle_socket != -1) { close(handle_info->handle_socket); handle_info->handle_socket = -1; } } return APR_SUCCESS; } static apr_status_t set_socket_nonblock(int sd) { #ifndef BEOS int fd_flags; fd_flags = fcntl(sd, F_GETFL, 0); #if defined(O_NONBLOCK) fd_flags |= O_NONBLOCK; #elif defined(O_NDELAY) fd_flags |= O_NDELAY; #elif defined(FNDELAY) fd_flags |= FNDELAY; #else #error Please teach APR how to make sockets non-blocking on your platform. #endif if (fcntl(sd, F_SETFL, fd_flags) == -1) { return errno; } #else int on = 1; if (setsockopt(sd, SOL_SOCKET, SO_NONBLOCK, &on, sizeof(int)) < 0) return errno; #endif /* BEOS */ return APR_SUCCESS; } apr_status_t proc_connect_ipc(fcgid_procnode *procnode, fcgid_ipc *ipc_handle) { fcgid_namedpipe_handle *handle_info; struct sockaddr_un unix_addr; apr_status_t rv; /* Alloc memory for unix domain socket */ ipc_handle->ipc_handle_info = (fcgid_namedpipe_handle *) apr_pcalloc(ipc_handle->request->pool, sizeof (fcgid_namedpipe_handle)); handle_info = (fcgid_namedpipe_handle *) ipc_handle->ipc_handle_info; handle_info->handle_socket = socket(AF_UNIX, SOCK_STREAM, 0); apr_pool_cleanup_register(ipc_handle->request->pool, handle_info, ipc_handle_cleanup, apr_pool_cleanup_null); /* Connect to fastcgi server */ memset(&unix_addr, 0, sizeof(unix_addr)); unix_addr.sun_family = AF_UNIX; /* PM already made this check for truncation */ AP_DEBUG_ASSERT(sizeof unix_addr.sun_path > strlen(procnode->socket_path)); apr_cpystrn(unix_addr.sun_path, procnode->socket_path, sizeof(unix_addr.sun_path)); /* I am the only one who connecting the server So I don't have to worry about ECONNREFUSED(listen queue overflow) problem, and I will never retry on error */ if (connect(handle_info->handle_socket, (struct sockaddr *) &unix_addr, sizeof(unix_addr)) < 0) { ap_log_rerror(APLOG_MARK, APLOG_DEBUG, apr_get_os_error(), ipc_handle->request, "mod_fcgid: can't connect unix domain socket: %s", procnode->socket_path); return APR_ECONNREFUSED; } /* Set nonblock option */ if ((rv = set_socket_nonblock(handle_info->handle_socket)) != APR_SUCCESS) { ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rv, ipc_handle->request, "mod_fcgid: can't make unix domain socket nonblocking"); return rv; } return APR_SUCCESS; } apr_status_t proc_close_ipc(fcgid_ipc * ipc_handle) { apr_status_t rv; rv = apr_pool_cleanup_run(ipc_handle->request->pool, ipc_handle->ipc_handle_info, ipc_handle_cleanup); ipc_handle->ipc_handle_info = NULL; return rv; } apr_status_t proc_read_ipc(fcgid_ipc *ipc_handle, const char *buffer, apr_size_t *size) { int retcode, unix_socket; fcgid_namedpipe_handle *handle_info; struct pollfd pollfds[1]; handle_info = (fcgid_namedpipe_handle *) ipc_handle->ipc_handle_info; unix_socket = handle_info->handle_socket; do { if ((retcode = read(unix_socket, (void *) buffer, *size)) > 0) { *size = retcode; return APR_SUCCESS; } } while (retcode == -1 && APR_STATUS_IS_EINTR(errno)); if (retcode == -1 && !APR_STATUS_IS_EAGAIN(errno)) { ap_log_rerror(APLOG_MARK, APLOG_WARNING, errno, ipc_handle->request, "mod_fcgid: error reading data from FastCGI server"); return errno; } /* I have to wait a while */ pollfds[0].fd = unix_socket; pollfds[0].events = POLLIN; do { retcode = poll(pollfds, 1, ipc_handle->communation_timeout * 1000); } while (retcode <= 0 && APR_STATUS_IS_EINTR(errno)); if (retcode == -1) { ap_log_rerror(APLOG_MARK, APLOG_WARNING, errno, ipc_handle->request, "mod_fcgid: error polling unix domain socket"); return errno; } else if (retcode == 0) { ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, ipc_handle->request, "mod_fcgid: read data timeout in %d seconds", ipc_handle->communation_timeout); return APR_ETIMEDOUT; } do { if ((retcode = read(unix_socket, (void *) buffer, *size)) > 0) { *size = retcode; return APR_SUCCESS; } } while (retcode == -1 && APR_STATUS_IS_EINTR(errno)); if (retcode == 0) { ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, ipc_handle->request, "mod_fcgid: error reading data, FastCGI server closed connection"); return APR_EPIPE; } ap_log_rerror(APLOG_MARK, APLOG_WARNING, errno, ipc_handle->request, "mod_fcgid: error reading data from FastCGI server"); return errno; } static apr_status_t socket_writev(fcgid_ipc *ipc_handle, struct iovec *vec, int nvec, int *writecnt) { apr_status_t rv; int retcode, unix_socket; fcgid_namedpipe_handle *handle_info; struct pollfd pollfds[1]; handle_info = (fcgid_namedpipe_handle *) ipc_handle->ipc_handle_info; unix_socket = handle_info->handle_socket; /* Try nonblock write */ do { if ((retcode = writev(unix_socket, vec, nvec)) > 0) { *writecnt = retcode; return APR_SUCCESS; } } while (retcode == -1 && APR_STATUS_IS_EINTR(errno)); rv = errno; if (APR_STATUS_IS_EAGAIN(rv)) { /* poll() */ pollfds[0].fd = unix_socket; pollfds[0].events = POLLOUT; do { retcode = poll(pollfds, 1, ipc_handle->communation_timeout * 1000); } while (retcode < 0 && APR_STATUS_IS_EINTR(errno)); if (retcode < 0) { rv = errno; } else if (retcode == 0) { rv = APR_TIMEUP; } else { /* Write again */ do { if ((retcode = writev(unix_socket, vec, nvec)) > 0) { *writecnt = retcode; return APR_SUCCESS; } } while (retcode == -1 && APR_STATUS_IS_EINTR(errno)); rv = errno; } } if (APR_STATUS_IS_EAGAIN(rv)) { /* socket is writable, but we can't write the entire buffer; try to write a * smaller amount, and if even that fails then sleep */ size_t to_write = vec[0].iov_len; int slept = 0; const apr_interval_time_t sleep_time = APR_USEC_PER_SEC / 4; const int max_sleeps = 8; do { if ((retcode = write(unix_socket, vec[0].iov_base, to_write)) > 0) { ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, ipc_handle->request, "wrote %d byte(s) and slept %d time(s) after EAGAIN", retcode, slept); *writecnt = retcode; return APR_SUCCESS; } if (APR_STATUS_IS_EAGAIN(errno)) { if (to_write == 1) { apr_sleep(sleep_time); ++slept; } else { to_write /= 2; } } } while ((APR_STATUS_IS_EINTR(errno) || APR_STATUS_IS_EAGAIN(errno)) && slept < max_sleeps); rv = errno; } ap_log_rerror(APLOG_MARK, APLOG_INFO, rv, ipc_handle->request, "mod_fcgid: error writing data to FastCGI server"); return rv; } static apr_status_t writev_it_all(fcgid_ipc *ipc_handle, struct iovec *vec, int nvec) { apr_size_t bytes_written = 0; apr_status_t rv; apr_size_t len = 0; int i = 0; int writecnt = 0; /* Calculate the total size */ for (i = 0; i < nvec; i++) { len += vec[i].iov_len; } i = 0; while (bytes_written != len) { rv = socket_writev(ipc_handle, vec + i, nvec - i, &writecnt); if (rv != APR_SUCCESS) return rv; bytes_written += writecnt; if (bytes_written < len) { /* Skip over the vectors that have already been written */ apr_size_t cnt = vec[i].iov_len; while (writecnt >= cnt && i + 1 < nvec) { i++; cnt += vec[i].iov_len; } if (writecnt < cnt) { /* Handle partial write of vec i */ vec[i].iov_base = (char *) vec[i].iov_base + (vec[i].iov_len - (cnt - writecnt)); vec[i].iov_len = cnt - writecnt; } } } return APR_SUCCESS; } #define FCGID_VEC_COUNT 8 apr_status_t proc_write_ipc(fcgid_ipc *ipc_handle, apr_bucket_brigade *output_brigade) { apr_status_t rv; struct iovec vec[FCGID_VEC_COUNT]; int nvec = 0; apr_bucket *e; for (e = APR_BRIGADE_FIRST(output_brigade); e != APR_BRIGADE_SENTINEL(output_brigade); e = APR_BUCKET_NEXT(e)) { apr_size_t len; const char* base; if (APR_BUCKET_IS_METADATA(e)) { continue; } if ((rv = apr_bucket_read(e, &base, &len, APR_BLOCK_READ)) != APR_SUCCESS) { ap_log_rerror(APLOG_MARK, APLOG_WARNING, rv, ipc_handle->request, "mod_fcgid: can't read request from bucket"); return rv; } vec[nvec].iov_len = len; vec[nvec].iov_base = (char*) base; if (nvec == (FCGID_VEC_COUNT - 1)) { /* It's time to write now */ if ((rv = writev_it_all(ipc_handle, vec, FCGID_VEC_COUNT)) != APR_SUCCESS) return rv; nvec = 0; } else nvec++; } /* There are something left */ if (nvec != 0) { if ((rv = writev_it_all(ipc_handle, vec, nvec)) != APR_SUCCESS) return rv; } return APR_SUCCESS; } void proc_print_exit_info(fcgid_procnode *procnode, int exitcode, apr_exit_why_e exitwhy, server_rec *main_server) { const char *diewhy = NULL; char signal_info[HUGE_STRING_LEN]; int signum = exitcode; int loglevel = APLOG_INFO; memset(signal_info, 0, HUGE_STRING_LEN); /* Reasons to exit */ switch (procnode->diewhy) { case FCGID_DIE_KILLSELF: diewhy = "normal exit"; break; case FCGID_DIE_IDLE_TIMEOUT: diewhy = "idle timeout"; break; case FCGID_DIE_LIFETIME_EXPIRED: diewhy = "lifetime expired"; break; case FCGID_DIE_BUSY_TIMEOUT: diewhy = "busy timeout"; break; case FCGID_DIE_CONNECT_ERROR: diewhy = "connect error"; break; case FCGID_DIE_COMM_ERROR: diewhy = "communication error"; break; case FCGID_DIE_SHUTDOWN: diewhy = "shutting down"; break; default: loglevel = APLOG_ERR; diewhy = "unknown"; } /* Get signal info */ if (APR_PROC_CHECK_SIGNALED(exitwhy)) { switch (signum) { case SIGTERM: case SIGHUP: case AP_SIG_GRACEFUL: case SIGKILL: apr_snprintf(signal_info, HUGE_STRING_LEN - 1, "get stop signal %d", signum); break; default: loglevel = APLOG_ERR; if (APR_PROC_CHECK_CORE_DUMP(exitwhy)) { apr_snprintf(signal_info, HUGE_STRING_LEN - 1, "get signal %d, possible coredump generated", signum); } else { apr_snprintf(signal_info, HUGE_STRING_LEN - 1, "get unexpected signal %d", signum); } } } else if (APR_PROC_CHECK_EXIT(exitwhy)) { apr_snprintf(signal_info, HUGE_STRING_LEN - 1, "terminated by calling exit(), return code: %d", exitcode); if (procnode->diewhy == FCGID_DIE_CONNECT_ERROR) diewhy = "server exited"; } /* Print log now */ ap_log_error(APLOG_MARK, loglevel, 0, main_server, "mod_fcgid: process %s(%" APR_PID_T_FMT ") exit(%s), %s", procnode->executable_path, procnode->proc_id.pid, diewhy, signal_info); } mod_fcgid-2.3.9/modules/fcgid/fcgid_pm_main.h0000644000175000017500000000164511157610742021015 0ustar trawicktrawick/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef FCGID_PM_MAIN_H #define FCGID_PM_MAIN_H apr_status_t pm_main(server_rec * main_server, apr_pool_t * configpool); #endif mod_fcgid-2.3.9/CHANGES-FCGID0000644000175000017500000003136312223417637015154 0ustar trawicktrawick -*- coding: utf-8 -*- Changes with mod_fcgid 2.3.9 *) Revert fix for PR 53693, added in 2.3.8 but undocumented. Fix issues with a minor optimization added in 2.3.8. [Jeff Trawick] Changes with mod_fcgid 2.3.8 *) SECURITY: CVE-2013-4365 (cve.mitre.org) Fix possible heap buffer overwrite. Reported and solved by: [Robert Matthews ] *) Add experimental cmake-based build system for Windows. [Jeff Trawick] *) Correctly parse quotation and escaped spaces in FcgidWrapper and the AAA Authenticator/Authorizor/Access directives' command line argument, as currently documented. PR 51194 [William Rowe] *) Honor quoted FcgidCmdOptions arguments (notably for InitialEnv assignments). PR 51657 [William Rowe] *) Conform script response parsing with mod_cgid and ensure no response body is sent when ap_meets_conditions() determines that request conditions are met. [Chris Darroch] *) Improve logging in access control hook functions. [Chris Darroch] *) Avoid making internal sub-requests and processing Location headers when in FCGI_AUTHORIZER mode, as the auth hook functions already treat Location headers returned by scripts as an error since redirections are not meaningful in this mode. [Chris Darroch] Changes with mod_fcgid 2.3.7 *) Introduce FcgidWin32PreventOrphans directive on Windows to use OS Job Control Objects to terminate all running fcgi's when the worker process has been abruptly terminated. PR: 51078 [Thangaraj AntonyCrouse ] *) Periodically clean out the brigades which are pulling in the request body for handoff to the fcgid child. PR: 51749 [Dominic Benson ] *) Resolve crash during graceful restarts. PR 50309 [Mario Brandt ] *) Solve latency/cogestion of resolving effective user file access rights when no such info is desired, for config related filename stats. PR: 51020 [Thangaraj AntonyCrouse , William Rowe] *) Fix regression in 2.3.6 which broke process controls when using vhost- specific configuration. [Jeff Trawick] *) Account for first process in class in the spawn score. [Jeff Trawick] Changes with mod_fcgid 2.3.6 *) SECURITY: CVE-2010-3872 (cve.mitre.org) Fix possible stack buffer overwrite. Diagnosed by the reporter. PR 49406. [Edgar Frank ] *) Change the default for FcgidMaxRequestLen from 1GB to 128K. Administrators should change this to an appropriate value based on site requirements. [Jeff Trawick] *) Allow FastCGI apps more time to exit at shutdown before being forcefully killed. [Jeff Trawick] *) Correct a problem that resulted in FcgidMaxProcesses being ignored in some situations. PR 48981. [] *) Fix the search for processes with the proper vhost config when ServerName isn't set in every vhost or a module updates r->server->server_hostname dynamically (e.g., mod_vhost_cdb) or a module updates r->server dynamically (e.g., mod_vhost_ldap). [Jeff Trawick] *) FcgidPassHeader now maps header names to environment variable names in the usual manner: The header name is converted to upper case and is prefixed with HTTP_. An additional environment variable is created with the legacy name. PR 48964. [Jeff Trawick] *) Allow processes to be reused within multiple phases of a request by releasing them into the free list as soon as possible. [Chris Darroch] *) Fix lookup of process command lines when using FcgidWrapper or access control directives, including within .htaccess files. [Chris Darroch] *) Resolve a regression in 2.3.5 with httpd 2.0.x on some Unix platforms; ownership of mutex files was incorrect, resulting in a startup failure. PR 48651. [Jeff Trawick, ] *) Return 500 instead of segfaulting when the application returns no output. [Tatsuki Sugiura , Jeff Trawick] *) In FCGI_AUTHORIZER role, avoid spawning a new process for every different HTTP request. [Chris Darroch] Changes with mod_fcgid 2.3.5 *) Stop using the unsuppressable "notice" log level for debug and informational messages. PR 48536. [Jeff Trawick] *) Respect DEFAULT_REL_RUNTIMEDIR for default values of FcgidIPCDir and FcgidProcessTableFile. [Jeff Trawick] *) Resolve fatal EDEADLK errors with threaded MPMs on Solaris. [Jeff Trawick] *) Display information about active processes in the server-status page. [Ryan Pan] *) Fix compatibility of httpd.conf-editing logic with non-GNU awk. PR 48067. [Hans Werner Strube ] *) Fix startup errors creating shared memory in constrained systems, such as OS X in its default configuration. This is a regression since mod_fcgid 2.2. [Jeff Trawick] *) Recover from most "Resource temporarily unavailable" errors writing the request to the FastCGI application. These were common with large request bodies on Mac OS X and intermittent on Solaris. PR 48025. [Jeff Trawick] *) Fix a bug in fixconf.sed that resulted in a prefix of "FcgidFcgid" on the updated directives. [Dan Hulme ] *) Fix possible corruption or truncation of request bodies which exceed FcgidMaxRequestInMem. This is a regression since mod_fcgid 2.2, which effectively ignored FcgidMaxRequestInMem if larger than 8K. PR 48021. [Jeff Trawick] *) Fix handling of the request body when a FastCGI access checker/ authenticator/authorizer (AAA) was configured. The body wasn't available for the request handler. PR 47973. [Jeff Trawick, Barry Scott ] *) Fix handling of FcgidCmdOptions so that it can apply to wrapper scripts which were defined with command-line arguments on the FcgidWrapper directive. [Jeff Trawick] Changes with mod_fcgid 2.3.4 *) Corrected unix 'make install' target regression in 2.3.3. [Jeff Trawick] Changes with mod_fcgid 2.3.3 *) Add FcgidCmdOptions directive to associate some of the existing configuration settings with a specific command. [Jeff Trawick] *) Allow/respect virtual host settings for the following directives: FcgidBusyTimeout, FcgidMaxProcessesPerClass, FcgidMinProcessesPerClass, FcgidIdleTimeout, and FcgidProcessLifetime. [Jeff Trawick] Changes with mod_fcgid 2.3.2 *) Fix a make install DESTDIR problem handling the reference manual and potentially other files (specific to 2.3.1). [Paul Howarth ] *) Fix a mod_fcgid 2.3.1 failure with when building for httpd 2.0.x on some platforms. [Paul Howarth ] *) Termination of idle processes after inactivity timeout can now be disabled by setting FcgidIdleTimeout to 0. Termination of idle processes based on the process lifetime can now be disabled by setting FcgidProcessLifeTime to 0. FcgidMaxRequestsPerProcess now accepts 0 for unlimited. [Ricardo Cantu ] *) All directives have been renamed in order to use a common prefix "Fcgid". Underscores in directive names have been eliminated in favor of CamelCase. The old directive names will still work but are deprecated. To fix your configuration you can use the sed script build/fixconf.sed. The following tables contains old and new directive names. Old Name New Name ................................................................... BusyScanInterval FcgidBusyScanInterval BusyTimeout FcgidBusyTimeout DefaultInitEnv FcgidInitialEnv DefaultMaxClassProcessCount FcgidMaxProcessesPerClass DefaultMinClassProcessCount FcgidMinProcessesPerClass ErrorScanInterval FcgidErrorScanInterval FastCgiAccessChecker FcgidAccessChecker FastCgiAccessCheckerAuthoritative FcgidAccessCheckerAuthoritative FastCgiAuthenticator FcgidAuthenticator FastCgiAuthenticatorAuthoritative FcgidAuthenticatorAuthoritative FastCgiAuthorizer FcgidAuthorizer FastCgiAuthorizerAuthoritative FcgidAuthorizerAuthoritative FCGIWrapper FcgidWrapper IdleScanInterval FcgidIdleScanInterval IdleTimeout FcgidIdleTimeout IPCCommTimeout FcgidIOTimeout IPCConnectTimeout FcgidConnectTimeout MaxProcessCount FcgidMaxProcesses MaxRequestInMem FcgidMaxRequestInMem MaxRequestLen FcgidMaxRequestLen MaxRequestsPerProcess FcgidMaxRequestsPerProcess OutputBufferSize FcgidOutputBufferSize PassHeader FcgidPassHeader PHP_Fix_Pathinfo_Enable FcgidFixPathinfo ProcessLifeTime FcgidProcessLifeTime SharememPath FcgidProcessTableFile SocketPath FcgidIPCDir SpawnScore FcgidSpawnScore SpawnScoreUpLimit FcgidSpawnScoreUpLimit TerminationScore FcgidTerminationScore TimeScore FcgidTimeScore ZombieScanInterval FcgidZombieScanInterval *) Separate classes by virtual host also on Windows. [Rainer Jung] *) Log client IP address with many more error log messages. [Jeff Trawick] *) Fix basic implementation of FcgidMaxRequestInMem and FcgidMaxRequestLen directives. [Jeff Trawick] *) Merge per-directory directives so that they can be inherited or overridden within other containers as expected. Merge server config/ virtual host directives so that they can be inherited or overridden within a virtual host as expected. [Jeff Trawick] *) Use the virtual host settings for the request being processed instead of those of the first FastCGI request handled by this httpd child process. Affected directives: FcgidBusyTimeout, FcgidIOTimeout, FcgidConnectTimeout, FcgidMaxRequestsPerProcess, and FcgidOutputBufferSize. [Jeff Trawick] *) Directives which previously were ignored in a virtual host context are no longer allowed. [Jeff Trawick] *) Add an optional flag "virtual" to FcgidWrapper. If virtual is set, the URLs passed to the wrapper are not checked, whether they resolve to a file. [Rainer Jung] *) Make the second argument (suffix) for FcgidWrapper optional. A wrapper defined without a suffix applies to all URLs, unless there is another more specific wrapper with a suffix. [Rainer Jung] Changes with mod_fcgid 2.3.1 *) Suppress "need AuthType to note auth failure" error-level messages when a FastCGIAccessChecker fails without any other kind of authentication (Basic, Digest) configured. [Eric Covener] *) Complete the unix port to 2.3-dev trunk. [William Rowe] *) Provide a default, mandatory environment as with mod_cgi (with the inclusion of LD_LIBRARY_PATH or similar variables on other platforms), unless overridden by DefaultInitEnv. [William Rowe] *) Handle DefaultInitEnv for case-insensitive platforms by forcing the env variable names to uppercase on Win32, OS2 and Netware. [William Rowe] *) Don't try to set the ownership of the socket directory unless running as root and the directory was just created. This allows the default httpd.conf (with some daemon User/Group) to be used by non-root. [Jeff Trawick] *) Fix formatting of several messages, including the oft-seen "mod_fcgid: Can't create shared memory for size %zu byte". [Jeff Trawick] *) Fix declared names of FastCgiAuthenticator and FastCgiAuthenticator- Authoritative directives, allowing them to be used. [Ulf Haueisen ] *) Fix vhost-specific DefaultInitEnv settings. Previously, when setting multiple virtual hosts with the same SuexecUserGroup user and group, the process manager use the same process pool for both virtual hosts. This means if one virtual host has a DefaultInitEnv and the other has different values set, a fastcgi request from any of these virtual host can go to the same processes, which is inconsistent (a request from virtualhost a with DefaultInitEnv VAL "a", can go to a process spawned with virtualhost b with DefaultInitEnv VAL "b" set). [Gabriel Barazer ] Note: A log of changes released before moving to the ASF (releases 2.2 and earlier) is in the file ChangeLog. mod_fcgid-2.3.9/STATUS-FCGID0000644000175000017500000000527312223626116015122 0ustar trawicktrawickMOD_FCGID STATUS: -*-text-*- Last modified at [$Date: 2013-10-04 20:59:58 +0000 (Fri, 04 Oct 2013) $] The current version of this file can be found at: * http://svn.apache.org/repos/asf/httpd/mod_fcgid/trunk/STATUS Consult the following STATUS files for information on related projects: * http://svn.apache.org/repos/asf/httpd/httpd/trunk/STATUS * http://svn.apache.org/repos/asf/apr/apr/trunk/STATUS Release history: [NOTE that x.{odd}.z versions are strictly Alpha/Beta releases, while x.{even}.z versions are Stable/GA releases.] 2.3.10 : in development 2.3.9 : tagged October 4, 2013 2.3.8 : not released 2.3.7 : released April 23, 2012 2.3.6 : released November 6, 2010 2.3.5 : released January 28, 2010 2.3.4 : released October 15, 2009 2.3.3 : not released 2.3.2 : not released 2.3.1 : released September 16, 2009 2.3.0 : not released 2.2.x : Final release branch external to the ASF Contributors looking for a mission: * Just do an egrep on "TODO" or "XXX" in the source. * Review the bug database at: http://issues.apache.org/bugzilla/ * Review the "PatchAvailable" bugs in the bug database: https://issues.apache.org/bugzilla/buglist.cgi?bug_status=NEW&bug_status=ASSIGNED&bug_status=REOPENED&product=Apache+httpd-2&component=mod_fcgid&keywords=PatchAvailable After testing, you can append a comment saying "Reviewed and tested". * Open bugs in the bug database https://issues.apache.org/bugzilla/buglist.cgi?bug_status=NEW&bug_status=ASSIGNED&bug_status=REOPENED&product=Apache+httpd-2&component=mod_fcgid RELEASE SHOWSTOPPERS: CURRENT RELEASE NOTES: CURRENT VOTES: REALLY NICE TO WRAP THESE UP: * For in-tree builds, extending config_vars.mk with our local [exp_]vars and installing that tree. * Create a manual module page. * Create and install an conf/extra/httpd-fcgid.conf. * Win32 stderr is broken. It's not possible to capture errors immediately upon invocation; this is apparently a design failure in std handle passing. * defining the process class a few references: http://mail-archives.apache.org/mod_mbox/httpd-dev/201004.mbox/%3Cq2l81403a941004131831lce28460bqfc9fa53c2058e79b@mail.gmail.com%3E http://mail-archives.apache.org/mod_mbox/httpd-dev/201002.mbox/%3C4B7EA227.90100@binero.se%3E http://mail-archives.apache.org/mod_mbox/httpd-dev/200909.mbox/%3C4ABA234F.3000002@kippdata.de%3E http://mail-archives.apache.org/mod_mbox/httpd-dev/200909.mbox/%3C4239a4320909210011w6c716fe6kec5915edc49bc546@mail.gmail.com%3E https://issues.apache.org/bugzilla/show_bug.cgi?id=49902 REFERENCES: mod_fcgid-2.3.9/Makefile.apxs0000644000175000017500000001023511276552233015753 0ustar trawicktrawick## ## Makefile.apxs -- Build procedure for mod_fcgid Apache module ## ## Do not use the .apxs makefile, run ./configure.apxs and build from 'Makefile' ## # top_builddir and top_srcdir are misnomers, because build/*.mk scripts # expect each of them to be the parent of the build directory, and fail # to trust the installbuilddir. exp_installbuilddir=$(shell $(APXS) -q exp_installbuilddir) top_srcdir=$(installbuilddir)/.. top_builddir=$(installbuilddir)/.. fcgid_builddir=. fcgid_srcdir=. builddir=. srcdir=. awk=. SUBDIRS = modules/fcgid CLEAN_SUBDIRS = TARGETS = INSTALL_TARGETS = install-conf install-manual ## no such targets yet; install-include DISTCLEAN_TARGETS = config.apxs.log modules/fcgid/fcgid_config.h EXTRACLEAN_TARGETS = include $(exp_installbuilddir)/rules.mk x-local-distclean: rm -rf docs/manual/build docs/manual/style # Dang nabbit, these are stripped! Reconstitute them; rel_libexecdir=`echo $(exp_libexecdir) | sed -e "s#^$(prefix)/##;"` rel_sysconfdir=`echo $(exp_sysconfdir) | sed -e "s#^$(prefix)/##;"` rel_logfiledir=`echo $(exp_logfiledir) | sed -e "s#^$(prefix)/##;"` httpd_conffile=$(exp_sysconfdir)/$(progname).conf httpd_origconffile=$(exp_sysconfdir)/original/$(progname).conf install-conf: @echo Installing configuration files @$(MKINSTALLDIRS) $(DESTDIR)$(exp_sysconfdir) \ $(DESTDIR)$(exp_sysconfdir)/original for i in $(DESTDIR)$(httpd_conffile) $(DESTDIR)$(httpd_origconffile); do \ if test -f $$i; then \ ($(awk) -f $(fcgid_srcdir)/build/addloadexample.awk \ -v MODULE=fcgid -v DSO=.so -v LIBPATH=$(rel_libexecdir) \ < $$i > $$i.new && \ mv $$i $$i.bak && mv $$i.new $$i \ ) || true; \ fi; \ done # Todo - add this flag to awk above, if/when this Include is distributed # -v EXAMPLECONF=$(rel_sysconfdir)/extra/httpd-fcgid.conf install-conf-unused: @$(MKINSTALLDIRS) $(DESTDIR)$(exp_sysconfdir)/extra \ $(DESTDIR)$(exp_sysconfdir)/original/extra @cd $(fcgid_srcdir)/docs/conf; \ for j in $(fcgid_srcdir)/docs/conf; do \ cd $$j ; \ for i in extra/httpd-fcgid.conf; do \ if test -f $$i; then \ sed -e '/^\#@@LoadFcgidModules@@/d;' \ -e 's#@exp_runtimedir@#$(exp_runtimedir)#;' \ -e 's#@exp_sysconfdir@#$(exp_sysconfdir)#;' \ -e 's#@rel_sysconfdir@#$(rel_sysconfdir)#;' \ -e 's#@rel_logfiledir@#$(rel_logfiledir)#;' \ < $$i > $(DESTDIR)$(exp_sysconfdir)/original/$$i; \ chmod 0644 $(DESTDIR)$(exp_sysconfdir)/original/$$i; \ if test ! -f $(DESTDIR)$(exp_sysconfdir)/$$i; then \ cp $(DESTDIR)$(exp_sysconfdir)/original/$$i \ $(DESTDIR)$(exp_sysconfdir)/$$i; \ chmod 0644 $(DESTDIR)$(exp_sysconfdir)/$$i; \ fi; \ fi; \ done ; \ done svnroot=http://svn.apache.org/repos/asf/httpd manualdir=$(fcgid_srcdir)/docs/manual # Note; by default, make generate-docs rebuilds the local pages # To regenerate the installed pages (after using make install to # drop in the fcgid content), simply # # make manualdir=/path/to/manual generate-docs # generate-docs: @if test ! -d $(manualdir)/build; then \ cd $(manualdir); \ svn export $(svnroot)/docs-build/trunk build; \ fi @if test ! -d $(manualdir)/style; then \ cd $(manualdir); \ svn export $(svnroot)/httpd/trunk/docs/manual/style; \ fi cd $(manualdir)/build; \ ./build.sh all generate-dox: cd $(fcgid_srcdir); \ doxygen $(fcgid_srcdir)/docs/doxygen-fcgid.conf install-manual: @echo Installing online manual @test -d $(DESTDIR)$(exp_manualdir) \ || $(MKINSTALLDIRS) $(DESTDIR)$(exp_manualdir) @if test "x$(RSYNC)" != "x" && test -x $(RSYNC) ; then \ $(RSYNC) --exclude .svn -rlpt --numeric-ids \ $(fcgid_srcdir)/docs/manual/ $(DESTDIR)$(exp_manualdir)/; \ else \ cp -rp $(fcgid_srcdir)/docs/manual/* $(DESTDIR)$(exp_manualdir)/ && \ find $(DESTDIR)$(exp_manualdir) -name ".svn" -type d -print \ | xargs rm -rf 2>/dev/null || true; \ fi install-include-unused: @echo Installing header files @$(MKINSTALLDIRS) $(DESTDIR)$(exp_includedir) && \ cp $(fcgid_srcdir)/include/mod_fcgid.h $(DESTDIR)$(exp_includedir)/ && \ chmod 0644 $(DESTDIR)$(exp_includedir)/mod_fcgid.h .PHONY: generate-dox generate-docs mod_fcgid-2.3.9/Makefile-fcgid.win0000644000175000017500000001367411262762562016664 0ustar trawicktrawick# # NMAKE Makefile for Microsoft Windows # # Targets are: # _apacher - build Apache in Release mode # _apached - build Apache in Debug mode # installr - build and install a Release build # installd - build and install a Debug build # clean - remove (most) generated files # # The following install defaults may be customized; # # Option Default # INSTDIR /Apache22 # # For example; # # nmake -f Makefile-fcgid.win INSTDIR="d:/Program Files/Apache" installr # # Be aware that certain awk's will not accept backslashed names, # so the server root should be given in forward slashes (quoted), # preferably with the drive designation! !IF "$(INSTDIR)" == "" INSTDIR=\Apache22 !ENDIF !IF EXIST("modules\fcgid\mod_fcgid.vcproj") \ && ([devenv /help > NUL 2>&1] == 0) \ && !defined(USEMAK) && !defined(USEDSW) USESLN=1 USEMAK=0 USEDSW=0 !ELSEIF EXIST("modules\fcgid\mod_fcgid.mak") && !defined(USEDSW) USESLN=0 USEMAK=1 USEDSW=0 !ELSE USESLN=0 USEMAK=0 USEDSW=1 !ENDIF !IF ("$(CTARGET)" == "") && ($(USESLN) == 1) CTARGET=/build !ENDIF !IF "$(APACHE2_HOME)" == "" APACHE2_HOME=$(INSTDIR) !ENDIF !IF EXIST("$(APACHE2_HOME)\lib\libapr-2.lib") APACHE2_APRSFX=-2 !ELSEIF EXIST("$(APACHE2_HOME)\lib\libapr-1.lib") APACHE2_APRSFX=-1 !ELSEIF EXIST("$(APACHE2_HOME)\lib\libapr.lib") APACHE2_APRSFX= !ELSE !MESSAGE Point INSTDIR at the directory containing an already installed httpd !MESSAGE including build support directories of lib and include. It must !MESSAGE include apr and apr-util. mod_fcgid cannot build without these files! !ENDIF !IF "$(LONG)" == "" !MESSAGE !MESSAGE INSTDIR = $(INSTDIR) !MESSAGE APACHE2_HOME = $(APACHE2_HOME) !MESSAGE !MESSAGE To change these options use 'nmake -f Makefile-fcgid.win [option=value]' !MESSAGE Example: nmake -f Makefile-fcgid.win !MESSAGE !ENDIF !IFNDEF MAKEOPT # Only default the behavior if MAKEOPT= is omitted !IFDEF _NMAKE_VER # Microsoft NMake options MAKEOPT=-nologo !ELSEIF "$(MAKE)" == "make" # Borland make options? Not really supported (yet) MAKEOPT=-s -N !ENDIF !ENDIF _buildr: @$(MAKE) $(MAKEOPT) -f Makefile-fcgid.win \ INSTDIR="$(INSTDIR)" APACHE2_HOME="$(APACHE2_HOME)" \ SHORT=R LONG=Release _build _buildd: @$(MAKE) $(MAKEOPT) -f Makefile-fcgid.win \ INSTDIR="$(INSTDIR)" APACHE2_HOME="$(APACHE2_HOME)" \ SHORT=D LONG=Debug _build installr: @$(MAKE) $(MAKEOPT) -f Makefile-fcgid.win \ INSTDIR="$(INSTDIR)" APACHE2_HOME="$(APACHE2_HOME)" \ SHORT=R LONG=Release _build _install installd: @$(MAKE) $(MAKEOPT) -f Makefile-fcgid.win \ INSTDIR="$(INSTDIR)" APACHE2_HOME="$(APACHE2_HOME)" \ SHORT=D LONG=Debug _build _install clean: _cleanr _cleand -if exist Browse\. rd /s Browse < << > nul y << !IF $(USEMAK) == 1 _cleanr: $(MAKE) $(MAKEOPT) -f Makefile-fcgid.win \ INSTDIR="$(INSTDIR)" APACHE2_HOME="$(APACHE2_HOME)" \ SHORT=R LONG=Release CTARGET=CLEAN _build _cleand: $(MAKE) $(MAKEOPT) -f Makefile-fcgid.win \ INSTDIR="$(INSTDIR)" APACHE2_HOME="$(APACHE2_HOME)" \ SHORT=D LONG=Debug CTARGET=CLEAN _build _build: echo Building Win32 $(LONG) targets ($(SHORT) suffixes) cd modules\fcgid $(MAKE) $(MAKEOPT) -f mod_fcgid.mak CFG="mod_fcgid - Win32 $(LONG)" RECURSE=0 $(CTARGET) cd ..\.. !ELSEIF $(USESLN) == 1 _cleanr: $(MAKE) $(MAKEOPT) -f Makefile-fcgid.win \ INSTDIR="$(INSTDIR)" APACHE2_HOME="$(APACHE2_HOME)" \ SHORT=R LONG=Release CTARGET="/clean" _build _cleand: $(MAKE) $(MAKEOPT) -f Makefile-fcgid.win \ INSTDIR="$(INSTDIR)" APACHE2_HOME="$(APACHE2_HOME)" \ SHORT=D LONG=Debug CTARGET="/clean" _build _build: echo Building Win32 $(LONG) targets ($(SHORT) suffixes) devenv mod_fcgid.sln /useenv $(CTARGET) $(LONG) /project mod_fcgid !ELSE _cleanr: @$(MAKE) $(MAKEOPT) -f Makefile-fcgid.win \ INSTDIR="$(INSTDIR)" APACHE2_HOME="$(APACHE2_HOME)" \ SHORT=R LONG=Release CTARGET="/CLEAN" _build _cleand: @$(MAKE) $(MAKEOPT) -f Makefile-fcgid.win \ INSTDIR="$(INSTDIR)" APACHE2_HOME="$(APACHE2_HOME)" \ SHORT=D LONG=Debug CTARGET="/CLEAN" _build _build: @echo Building Win32 $(LONG) targets ($(SHORT) suffixes) @msdev mod_fcgid.dsw /USEENV /MAKE "mod_fcgid - Win32 $(LONG)" $(CTARGET) !ENDIF httpd_conffile=$(INSTDIR)\conf\httpd.conf httpd_origconffile=$(INSTDIR)\conf\original\httpd.conf _install: echo Y >.y echo A >.A -mkdir "$(INSTDIR)" -mkdir "$(INSTDIR)\conf" -mkdir "$(INSTDIR)\conf\original" !IFDEF HAVE_HTTPD_FCGID_CONF -mkdir "$(INSTDIR)\conf\extra" -mkdir "$(INSTDIR)\conf\original\extra" !ENDIF !IFDEF HAVE_MOD_FCGID_H -mkdir "$(INSTDIR)\include" !ENDIF -mkdir "$(INSTDIR)\manual" -mkdir "$(INSTDIR)\modules" copy CHANGES-FCGID "$(INSTDIR)\CHANGES-FCGID.txt" <.y copy LICENSE-FCGID "$(INSTDIR)\LICENSE-FCGID.txt" <.y copy NOTICE-FCGID "$(INSTDIR)\NOTICE-FCGID.txt" <.y copy README-FCGID "$(INSTDIR)\README-FCGID.txt" <.y copy modules\fcgid\$(LONG)\mod_fcgid.so "$(INSTDIR)\modules" <.y copy modules\fcgid\$(LONG)\mod_fcgid.pdb "$(INSTDIR)\modules" <.y xcopy docs\manual "$(INSTDIR)\manual" /s /d < .a !IFDEF HAVE_MOD_FCGID_H copy include\mod_fcgid.h "$(INSTDIR)\include" < .y > nul !ENDIF for %f in ("$(httpd_origconffile)" "$(httpd_conffile)") do \ if exist "%f" ( \ awk -f build/addloadexample.awk -v MODULE=fcgid -v DSO=.so \ !IFDEF HAVE_HTTPD_FCGID_CONF -v EXAMPLECONF=conf/extra/httpd-fcgid.conf \ !ENDIF -v LIBPATH=modules "%f" > "%f.new" && \ move "%f" "%f.bak" && move "%f.new" "%f" \ ) !IFDEF HAVE_HTTPD_FCGID_CONF copy docs\conf\extra\httpd-fcgid.conf "$(INSTDIR)\conf\original\extra\httpd-fcgid.conf" <.y awk -f << docs\conf\extra\httpd-fcgid.conf > "$(INSTDIR)\conf\original\extra\httpd-fcgid.conf" /^\#@@LoadFcgidModules@@/ { next; } { sub(/@exp_runtimedir@/, "logs"); sub(/@exp_sysconfdir@/, "conf"); sub(/@rel_sysconfdir@/, "conf"); sub(/@rel_logfiledir@/, "logs"); print $0; } << if not exist "$(INSTDIR)\conf\extra\httpd-fcgid.conf" \ copy "$(INSTDIR)\conf\original\extra\httpd-fcgid.conf" \ "$(INSTDIR)\conf\extra\httpd-fcgid.conf" <.y !ENDIF del .y del .a mod_fcgid-2.3.9/mod_fcgid.dsw0000644000175000017500000000101411245344732015765 0ustar trawicktrawickMicrosoft Developer Studio Workspace File, Format Version 6.00 # WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! ############################################################################### Project: "mod_fcgid"=modules\fcgid\mod_fcgid.dsp - Package Owner=<4> Package=<5> {{{ }}} Package=<4> {{{ }}} ############################################################################### Global: Package=<5> {{{ }}} Package=<3> {{{ }}} ############################################################################### mod_fcgid-2.3.9/README-FCGID0000644000175000017500000002016012216606636015033 0ustar trawicktrawickUnix Build Instructions ----------------------- To build and install as a DSO outside of the httpd source build, from the fcgid source root directory, simply; ./configure.apxs make make install If apxs is not in your path, or you are building to a different httpd installation, or your distribution has an alternate script name for apxs (e.g. apxs2), then either set the APXS environment variable, or use the syntax; APXS=/path/to/bin/apxs ./configure.apxs so the desired configuration is used. To build static, or as a DSO but within the same build as httpd, copy the entire fcgid source directory tree on top of your existing httpd source tree, and from the httpd source root directory ./buildconf (to pick up fcgid) ./configure --enable-fcgid {your usual options} and proceed as usual. Win32 Build Instructions ------------------------ 1. Win32 build based on Visual Studio The windows packages prior to 2.2.7 (or 2.0.62) left out the file include\mod_log_config.h, just copy these from the source tree or you can export them from subversion, just change to your installed Apache 2.2 (or 2.0) include subdirectory and... svn export http://svn.apache.org/repos/asf/httpd/httpd/branches/2.2.x/modules/loggers/mod_log_config.h (for your 2.0 installation, replace 2.2.x with 2.0.x in the command above). You should be ready to compile the project. On windows, before building for httpd-2.0, you must adjust the two apr 1.x specific lines in modules\fcgid\mod_fcgid.dsp which begin # ADD LINK32 libapr-1.lib libaprutil-1.lib ... to start with the apr 0.9 equivilants for httpd-2.0, # ADD LINK32 libapr.lib libaprutil.lib ... If using httpd development version 2.3 plus APR 2.0, replace both of these entries with the single (combined) entry; # ADD LINK32 libapr-2.lib ... To build on windows out-of-tree as a DSO, simply set APACHE2_HOME=c:\path\to\Apache2.2 and then, for Visual Studio 6.0 (98)... msdev /useenv mod_fcgid.dsw or for Visual Studio .NET (2002) and later ... devenv /useenv mod_fcgid.dsw The later command is needed on Visual Studio .NET/2002 and later, and converts mod_fcgid.dsw to mod_fcgid.sln. So after converting once, use the newly converted solution instead... devenv /useenv mod_fcgid.sln On windows you can overlay mod_fcgid source files into the httpd source file tree, and make the following changes for an in-tree build; * Manually add the project mod_fcgid.dsp to the Apache.dsw workspace. * Ensure the BuildBin project includes the mod_fcgid project dependency. * Add mod_fcgid project dependencies of libhttpd, libapr and libaprutil. * Remove /D "FCGID_APXS_BUILD" from the # ADD CPP lines of modules\fcgid\mod_fcgid.dsp. * Replace /I "$(APACHE2_HOME)/include" with /I "../../modules/loggers" for both # ADD CPP lines of modules\fcgid\mod_fcgid.dsp. * Remove the libraries libapr[-1].lib libaprutil[-1].lib libhttpd.lib and the /libpath:"$(APACHE2_HOME)\lib" flag from the # ADD LINK32 lines of modules\fcgid\mod_fcgid.dsp. Note that mod_fcgid.so needs to be added to the module installation lines in Makefile.win, or you must manually copy the .so module from modules\fcgid\Release after compiling. 2. Win32 build based on cmake: Note: This support is experimental and may not build mod_fcgid in a manner compatible with the existing Windows build support. The build interfaces may change as feedback is received and bugs are resolved. Currently a .conf file is not created. Install httpd and APR to a common prefix, and point CMAKE_INSTALL_PREFIX to that prefix when configuring mod_fcgid. Example using the "NMake Makefiles" generator from a Visual Studio command prompt: cd some-build-directory cmake -G "NMake Makefiles" ^ -DCMAKE_INSTALL_PREFIX=C:\Apache246 ^ -DCMAKE_BUILD_TYPE=RelWithDebInfo ^ C:\path\to\fcgid-sources\modules\fcgid nmake && nmake install The last argument to cmake in the example is the directory "modules\fcgid" within your svn checkout or tarball/zip extract of mod_fcgid. Add -DINSTALL_PDB=OFF to the cmake invocation to leave mod_fcgid.pdb (if generated) in the build directory. Add the following LoadModule directive to your configuration: LoadModule fcgid_module modules/mod_fcgid.so Documentation Build ------------------- To regenerate the html.en documentation, here again it's as simple as copying the content docs/ into an httpd/docs/ tree and regenerating httpd's documentation. However, it's also possible you are generating a local copy for reference in mod_fcgid's tree, in that case you must have a copy of the httpd docs/manual/style. For example; cd docs/manual svn co http://svn.apache.org/repos/asf/httpd/httpd/branches/2.2.x/docs/manual/style In either case; cd docs/manual svn co http://svn.apache.org/repos/asf/httpd/docs-build/trunk build cd build and finally; ./build.sh all or on windows... build.bat all To make this simpler on unix, after invoking ./configure.apxs in the top level directory, you can simply; make generate-docs which will fetch up those style and build directories (for httpd-2.2) and generate the docs for you. After using make and make install, you can even merge the directives for the installed manual using this target; make manualdir=/path/to/httpd/manual generate-docs The same rules about an installed, locatable JAVA_HOME apply to building mod_fcgid docs as apply to building the httpd manual. The advantage to building in-tree within httpd is that you gain the complete directive cross references applicable to all httpd and mod_fcgid directives, before installing the httpd\manual files. Incompatible configuration changes ---------------------------------- Some changes have been made in the ASF release of mod_fcgid which can affect existing configurations: i. All directives have been renamed in order to use a common prefix "Fcgid". Underscores in directive names have been eliminated in favor of CamelCase. The old directive names will still work but are deprecated. To fix your configuration you can use the sed script build/fixconf.sed. A table with old and new directive names is included in CHANGES-FCGID. ii. Some directives which could be placed inside but were ignored before now result in configuration errors. As before, these directives must be set at global scope to have the desired effect. The directives are FcgidBusyScanInterval, FcgidBusyTimeout, FcgidMaxProcessesPerClass, FcgidDefaultMinProcessCount, FcgidErrorScanInterval, FcgidIdleScanInterval, FcgidIdleTimeout, FcgidMaxProcesses, FcgidFixPathinfo, FcgidProcessLifetime, FcgidProcessTableFile, FcgidIPCDir, FcgidSpawnScore, FcgidSpawnScoreUpLimit, FcgidTerminationScore, FcgidTimeScore, and FcgidZombieScanInterval. iii. Some directives which could be placed inside but were ignored before are now respected. These include FcgidIdleTimeout, FcgidProcessLifeTime, and others. (Consult CHANGES-FCGID for the complete list.) iv. Some directives which can optionally be placed inside were not inherited as expected in older releases. This has been corrected, and behavior will change for some configurations. The affected directives are FcgidIOTimeout, FcgidConnectTimeout, FcgidMaxRequestInMem, FcgidMaxRequestLen, FcgidMaxRequestsPerProcess, and FcgidOutputBufferSize. v. Some directives which can be placed inside , , etc. were not inherited as expected in older releases. This has been corrected, and behavior will change for some configurations. The affected directives are FcgidAccessChecker, FcgidAccessCheckerAuthoritative, FcgidAuthenticator, FcgidAuthenticatorAuthoritative, FcgidAuthorizer, FcgidAuthorizerAuthoritative, and FcgidWrapper. Acknowledgements ---------------- Portions of this software were originally developed by Ryan Pan (Pan Qingfeng) . This software implements portions of the FastCGI specification as defined by Open Market, Inc. The specification is available from http://www.fastcgi.com/devkit/doc/fcgi-spec.html mod_fcgid-2.3.9/LICENSE-FCGID0000644000175000017500000002613611254702026015160 0ustar trawicktrawick Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. mod_fcgid-2.3.9/build/0000755000175000017500000000000012223627014014430 5ustar trawicktrawickmod_fcgid-2.3.9/build/addloadexample.awk0000644000175000017500000000172411276405264020114 0ustar trawicktrawick# Invoke as awk addloadexample.awk BEGIN { lms = 0; } tolower($0) ~ /^[# \t]*loadmodule[ \t]/ { if ( $2 == MODULE "_module" ) { print "LoadModule " MODULE "_module " LIBPATH "/mod_" MODULE DSO; lms = 2; next; } # test $3 since # LoadModule is split into two tokens else if ( $3 == MODULE "_module" ) { print $1 "LoadModule " MODULE "_module " LIBPATH "/mod_" MODULE DSO; lms = 2; next; } else if ( ! lms ) lms = 1; } $0 ~ /^[ \t]*$/ && lms == 1 { print "LoadModule " MODULE "_module " LIBPATH "/mod_" MODULE DSO; lms = 2; } tolower($0) ~ /^[# \t]*include[ \t]/ && $NF == EXAMPLECONF { lms = 3; } { print } END { if ( lms < 3 ) { if ( $0 !~ /^[ \t]*$/ ) print ""; if ( lms < 2 ) { print "LoadModule " MODULE "_module " LIBPATH "/mod_" MODULE DSO; print ""; } if ( length(EXAMPLECONF) ) { print "# Example mod_" MODULE " configuration"; print "#Include " EXAMPLECONF "\n"; } } } mod_fcgid-2.3.9/build/Makefile.apxs0000644000175000017500000000360211307760247017053 0ustar trawicktrawick## ## Makefile.apxs -- Feature test procedures for mod_fcgid Apache module ## ## Do not use this target; ./configure.apxs from the mod_fcgid dir root ## # top_builddir and top_srcdir are misnomers, because build/*.mk # scripts expect it them be the parent of the build directory, # and fail to trust the installbuilddir. exp_installbuilddir=$(shell $(APXS) -q exp_installbuilddir) top_srcdir=$(installbuilddir)/.. top_builddir=$(installbuilddir)/.. fcgid_builddir=.. fcgid_srcdir=.. builddir=. srcdir=. CLEAN_TARGETS = conftest_foofn.c \ conftest_sys_file_h.c \ conftest_sys_mman_h.c \ conftest_sys_mutex_h.c \ conftest_sys_shm_h.c \ *.loT TARGETS = conftest_foofn \ conftest_sys_file_h \ conftest_sys_mman_h \ conftest_sys_mutex_h \ conftest_sys_shm_h PROGRAM_LDADD = PROGRAM_DEPENDENCIES = include $(exp_installbuilddir)/rules.mk # Function checks follow this pattern; one to execute, one to create source # Header file checks simply validate that compilation into an .lo succeeds # TODO When the first function test is added, hijack conftest_foofn conftest_foofn: conftest_foofn.lo $(LINK) conftest_foofn.lo conftest_sys_file_h: conftest_sys_file_h.lo @echo "success" > $@ conftest_sys_mman_h: conftest_sys_mman_h.lo @echo "success" > $@ conftest_sys_mutex_h: conftest_sys_mutex_h.lo @echo "success" > $@ conftest_sys_shm_h: conftest_sys_shm_h.lo @echo "success" > $@ conftest_foofn.c: @echo "#include " > $@ @echo "#include " >> $@ @echo "int main() { return foofn(2, 0644); }" >> $@ conftest_sys_file_h.c: @echo "#include " > $@ conftest_sys_mman_h.c: @echo "#include " > $@ conftest_sys_mutex_h.c: @echo "#include " > $@ conftest_sys_shm_h.c: @echo "#include " > $@ mod_fcgid-2.3.9/build/fixconf.sed0000755000175000017500000001001411267320663016567 0ustar trawicktrawick#!/usr/bin/sed -f # # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # # sed script to replace all old directive names with the new ones. # # First we fix occurences at the beginning of lines s/^BusyScanInterval/FcgidBusyScanInterval/g s/^BusyTimeout/FcgidBusyTimeout/g s/^DefaultInitEnv/FcgidInitialEnv/g s/^DefaultMaxClassProcessCount/FcgidMaxProcessesPerClass/g s/^DefaultMinClassProcessCount/FcgidMinProcessesPerClass/g s/^ErrorScanInterval/FcgidErrorScanInterval/g s/^FastCgiAccessChecker/FcgidAccessChecker/g s/^FastCgiAccessCheckerAuthoritative/FcgidAccessCheckerAuthoritative/g s/^FastCgiAuthenticator/FcgidAuthenticator/g s/^FastCgiAuthenticatorAuthoritative/FcgidAuthenticatorAuthoritative/g s/^FastCgiAuthorizer/FcgidAuthorizer/g s/^FastCgiAuthorizerAuthoritative/FcgidAuthorizerAuthoritative/g s/^FCGIWrapper/FcgidWrapper/g s/^IdleScanInterval/FcgidIdleScanInterval/g s/^IdleTimeout/FcgidIdleTimeout/g s/^IPCCommTimeout/FcgidIOTimeout/g s/^IPCConnectTimeout/FcgidConnectTimeout/g s/^MaxProcessCount/FcgidMaxProcesses/g s/^MaxRequestInMem/FcgidMaxRequestInMem/g s/^MaxRequestLen/FcgidMaxRequestLen/g s/^MaxRequestsPerProcess/FcgidMaxRequestsPerProcess/g s/^OutputBufferSize/FcgidOutputBufferSize/g s/^PassHeader/FcgidPassHeader/g s/^PHP_Fix_Pathinfo_Enable/FcgidFixPathinfo/g s/^ProcessLifeTime/FcgidProcessLifeTime/g s/^SharememPath/FcgidProcessTableFile/g s/^SocketPath/FcgidIPCDir/g s/^SpawnScore/FcgidSpawnScore/g s/^SpawnScoreUpLimit/FcgidSpawnScoreUpLimit/g s/^TerminationScore/FcgidTerminationScore/g s/^TimeScore/FcgidTimeScore/g s/^ZombieScanInterval/FcgidZombieScanInterval/g # Next we fix all other occurences without matching # the ones, that are already OK s/\([^d]\)BusyScanInterval/\1FcgidBusyScanInterval/g s/\([^d]\)BusyTimeout/\1FcgidBusyTimeout/g s/\([^d]\)DefaultInitEnv/\1FcgidInitialEnv/g s/\([^d]\)DefaultMaxClassProcessCount/\1FcgidMaxProcessesPerClass/g s/\([^d]\)DefaultMinClassProcessCount/\1FcgidMinProcessesPerClass/g s/\([^d]\)ErrorScanInterval/\1FcgidErrorScanInterval/g s/\([^d]\)FastCgiAccessChecker/\1FcgidAccessChecker/g s/\([^d]\)FastCgiAccessCheckerAuthoritative/\1FcgidAccessCheckerAuthoritative/g s/\([^d]\)FastCgiAuthenticator/\1FcgidAuthenticator/g s/\([^d]\)FastCgiAuthenticatorAuthoritative/\1FcgidAuthenticatorAuthoritative/g s/\([^d]\)FastCgiAuthorizer/\1FcgidAuthorizer/g s/\([^d]\)FastCgiAuthorizerAuthoritative/\1FcgidAuthorizerAuthoritative/g s/\([^d]\)FCGIWrapper/\1FcgidWrapper/g s/\([^d]\)IdleScanInterval/\1FcgidIdleScanInterval/g s/\([^d]\)IdleTimeout/\1FcgidIdleTimeout/g s/\([^d]\)IPCCommTimeout/\1FcgidIOTimeout/g s/\([^d]\)IPCConnectTimeout/\1FcgidConnectTimeout/g s/\([^d]\)MaxProcessCount/\1FcgidMaxProcesses/g s/\([^d]\)MaxRequestInMem/\1FcgidMaxRequestInMem/g s/\([^d]\)MaxRequestLen/\1FcgidMaxRequestLen/g s/\([^d]\)MaxRequestsPerProcess/\1FcgidMaxRequestsPerProcess/g s/\([^d]\)OutputBufferSize/\1FcgidOutputBufferSize/g s/\([^d]\)PassHeader/\1FcgidPassHeader/g s/\([^d]\)PHP_Fix_Pathinfo_Enable/\1FcgidFixPathinfo/g s/\([^d]\)ProcessLifeTime/\1FcgidProcessLifeTime/g s/\([^d]\)SharememPath/\1FcgidProcessTableFile/g s/\([^d]\)SocketPath/\1FcgidIPCDir/g s/\([^d]\)SpawnScore/\1FcgidSpawnScore/g s/\([^d]\)SpawnScoreUpLimit/\1FcgidSpawnScoreUpLimit/g s/\([^d]\)TerminationScore/\1FcgidTerminationScore/g s/\([^d]\)TimeScore/\1FcgidTimeScore/g s/\([^d]\)ZombieScanInterval/\1FcgidZombieScanInterval/g mod_fcgid-2.3.9/configure.apxs0000755000175000017500000001040311401515447016213 0ustar trawicktrawick#!/bin/sh # # configure.apxs --- build configuration script for creating mod_fcgid # out of tree using the apxs utility and httpd build toolset # if test "$APXS" = ""; then APXS=`which apxs 2>/dev/null` fi; if test "$APXS" = ""; then echo $0 must be able to find apxs in your path, echo or the environment variable APXS must provide the full path of APXS, echo or you may specify it with: echo echo APXS=/path/to/apxs $0 echo echo configuration failed exit 1 fi echo Configuring mod_fcgid for APXS in $APXS # top_builddir and top_srcdir are a misnomers, because build/*.mk # scripts expect them to be the parent of the build directory # they fail to use the $installbuilddir path. exp_installbuilddir=`$APXS -q exp_installbuilddir` top_installbuilddir=`cd $exp_installbuilddir/..; pwd` top_installbuilddir=`echo $exp_installbuilddir | sed -e "s#/[^/]*\\\$##;"` builddir=`pwd` srcdir=$builddir fcgid_builddir=$builddir fcgid_srcdir=$builddir # prefix is the default @@ServerRoot@@, where libexecdir/sysconfdir may # be relative (if it is not their prefix, the rel_ paths remain unchanged rel_fix_prefix=`$APXS -q prefix` rel_libexecdir=`$APXS -q exp_libexecdir | sed -e "s#^$rel_fix_prefix/##;"` rel_sysconfdir=`$APXS -q exp_sysconfdir | sed -e "s#^$rel_fix_prefix/##;"` rel_logfiledir=`$APXS -q exp_logfiledir | sed -e "s#^$rel_fix_prefix/##;"` httpd_conffile=`$APXS -q exp_sysconfdir`/`$APXS -q progname`.conf if test -z "$AWK"; then AWK=`$APXS -q AWK` fi for i in Makefile build/Makefile modules/fcgid/Makefile modules/fcgid/modules.mk; do l_r=`echo $i|sed -e "s#/*[^/]*\\\$##;s#^\(..*\)\\\$#/\1#"` sed -e "s#^\(exp_installbuilddir\)=.*#\1=$exp_installbuilddir#;" \ -e "s#^\(include\) \$(exp_installbuilddir)#\1 $exp_installbuilddir#;" \ -e "s#^\(top_builddir\)=.*#\1=$top_installbuilddir#;" \ -e "s#^\(top_srcdir\)=.*#\1=$top_installbuilddir#;" \ -e "s#^\(fcgid_srcdir\)=.*#\1=$srcdir#;" \ -e "s#^\(fcgid_builddir\)=.*#\1=$builddir#;" \ -e "s#^\(srcdir\)=.*#\1=$srcdir$l_r#;" \ -e "s#^\(builddir\)=.*#\1=$builddir$l_r#;" \ -e "s#^\(rel_libexecdir\)=.*#\1=$rel_libexecdir#;" \ -e "s#^\(rel_sysconfdir\)=.*#\1=$rel_sysconfdir#;" \ -e "s#^\(rel_logfiledir\)=.*#\1=$rel_logfiledir#;" \ -e "s#^\(httpd_conffile\)=.*#\1=$httpd_conffile#;" \ -e "s#^\(awk\)=.*#\1=$AWK#;" \ < $i.apxs > $i done touch .deps touch build/.deps touch modules/fcgid/.deps cd build found_features="" echo "Detecting features" echo "Detecting features" > ../config.apxs.log #test invocations follow the pattern; # #if make local-clean conftest_foofn >>../config.apxs.log 2>&1; then # found_features="$found_features \ # -e \"s/^#undef \(HAVE_FOOFN\)[ \t]*/#define \1 1/;\"" #fi if make local-clean conftest_sys_file_h >>../config.apxs.log 2>&1; then found_features="$found_features \ -e \"s/^#undef \(HAVE_SYS_FILE_H\)[ \t]*/#define \1 1/;\"" fi if make local-clean conftest_sys_mman_h >>../config.apxs.log 2>&1; then found_features="$found_features \ -e \"s/^#undef \(HAVE_SYS_MMAN_H\)[ \t]*/#define \1 1/;\"" fi if make local-clean conftest_sys_mutex_h >>../config.apxs.log 2>&1; then found_features="$found_features \ -e \"s/^#undef \(HAVE_SYS_MUTEX_H\)[ \t]*/#define \1 1/;\"" fi if make local-clean conftest_sys_shm_h >>../config.apxs.log 2>&1; then found_features="$found_features \ -e \"s/^#undef \(HAVE_SYS_SHM_H\)[ \t]*/#define \1 1/;\"" fi make local-distclean >>../config.apxs.log 2>&1 cd .. if test "x$found_features" = "x"; then cp modules/fcgid/fcgid_config.h.in modules/fcgid/fcgid_config.h else eval sed "$found_features" < modules/fcgid/fcgid_config.h.in \ > modules/fcgid/fcgid_config.h fi echo "" echo "Finished, run 'make' to compile mod_fcgid" echo "" echo "Run 'make install' to install mod_fcgid" echo "" #echo "The manual pages fcgid/index.html and mod/mod_fcgid.html" #echo "will be installed to help get you started." #echo #echo "The conf/extra/httpd-fcgid.conf will be installed as an example" #echo "for you to work from. In your configuration file," #echo " `$APXS -q exp_sysconfdir`/`$APXS -q progname`.conf" #echo "uncomment the line '#Include conf/extra/httpd-fcgid.conf'" #echo "to activate this example mod_fcgid configuration."