--- /dev/null
+.TH ab 1 "March 1998"
+.\" $Id: ab.8,v 1.1 1999/08/24 06:45:53 fielding Exp $
+.\" Copyright (c) 1998-1999 The Apache Group. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\"
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\"
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in
+.\" the documentation and/or other materials provided with the
+.\" distribution.
+.\"
+.\" 3. All advertising materials mentioning features or use of this
+.\" software must display the following acknowledgment:
+.\" "This product includes software developed by the Apache Group
+.\" for use in the Apache HTTP server project (http://www.apache.org/)."
+.\"
+.\" 4. The names "Apache Server" and "Apache Group" must not be used to
+.\" endorse or promote products derived from this software without
+.\" prior written permission.
+.\"
+.\" 5. Products derived from this software may not be called "Apache"
+.\" nor may "Apache" appear in their names without prior written
+.\" permission of the Apache Group.
+.\"
+.\" 6. Redistributions of any form whatsoever must retain the following
+.\" acknowledgment:
+.\" "This product includes software developed by the Apache Group
+.\" for use in the Apache HTTP server project (http://www.apache.org/)."
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+.\" EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
+.\" ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+.\" SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+.\" LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+.\" STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+.\" OF THE POSSIBILITY OF SUCH DAMAGE.
+.\" ====================================================================
+.\"
+.\" This software consists of voluntary contributions made by many
+.\" individuals on behalf of the Apache Group and was originally based
+.\" on public domain software written at the National Center for
+.\" Supercomputing Applications, University of Illinois, Urbana-Champaign.
+.\" For more information on the Apache Group and the Apache HTTP server
+.\" project, please see <http://www.apache.org/>.
+.SH NAME
+ab \- Apache HTTP server benchmarking tool
+.SH SYNOPSIS
+.B ab
+[
+.B \-k
+] [
+.BI \-n " requests"
+] [
+.BI \-t " timelimit"
+] [
+.BI \-c " concurrency"
+] [
+.BI \-p " POST file"
+] [
+.BI \-A " Authenticate username:password"
+] [
+.BI \-P " Proxy Authenticate username:password"
+] [
+.BI \-H " Custom header"
+] [
+.BI \-C " Cookie name=value"
+] [
+.BI \-T " content-type"
+] [
+.BI \-v " verbosity"
+]
+] [
+.BI \-w " output HTML"
+]
+] [
+.BI \-x " <table> attributes"
+]
+] [
+.BI \-y " <tr> attributes"
+]
+] [
+.BI \-z " <td> attributes"
+]
+.I [http://]hostname[:port]/path
+
+.B ab
+[
+.B \-V
+] [
+.B \-h
+]
+.PP
+.SH DESCRIPTION
+.B ab
+is a tool for benchmarking your Apache HyperText Transfer Protocol (HTTP)
+server. It is designed to give you an impression on how performant is your
+current Apache installation. This especially shows you how much requests per
+time your Apache installation is capable to serve.
+.PP
+.SH OPTIONS
+.TP 12
+.B \-k
+Enable the HTTP KeepAlive feature, i.e. perform multiple requests within one
+HTTP session instead. Default is no KeepAlive.
+.TP 12
+.BI \-n " requests"
+Number of requests to perform for the benchmarking session. The default is to
+just perform one single request which usually leads to not very representative
+benchmarking results.
+.TP 12
+.BI \-t " timelimit"
+Seconds to max. spend for benchmarking. This implies
+a
+.B \-n
+.B 50000
+internally. Use this to benchmark the server within a fixed total amount of
+time. Per default there is no timelimit.
+.TP 12
+.BI \-c " concurrency"
+Number of multiple requests per time to perform.
+Default is one request per time.
+
+.TP 12
+.BI \-p " POST file"
+File containing data to POST.
+
+.TP 12
+.BI \-A " Authorization username:password"
+Supply BASIC Authentification credentials to the server. The username
+and password are separated by a single ':' and send on the wire uuencoded.
+The string is send regardless of wether the server needs it; (i.e. has
+send an 401. Authentifcation needed).
+
+.TP 12
+.BI \-p " Proxy-Authorization username:password"
+Supply BASIC Authentification credentials to a proxy en-route. The username
+and password are separated by a single ':' and send on the wire uuencoded.
+The string is send regardless of wether the proxy needs it; (i.e. has
+send an 407 Proxy authentifcation needed).
+
+.TP 12
+.BI \-C " Cookie name=value"
+Add a 'Cookie:' line to the request. The argument is typically in the form
+of a 'name=value' pair. This field is repeatable.
+
+.TP 12
+.BI \-p " Header string"
+Postfix extra headers to the request. The argument is typically in the form
+of a valid header line; containing a colon separated field value pair. (i.e.
+'Accept-Encoding: zip/zop;8bit').
+
+.TP 12
+.BI \-T " content-type"
+Content-type header to use for POST data.
+
+.TP 12
+.B \-v
+Set verbosity level - 4 and above prints information on headers, 3 and
+above prints response codes (404, 200, etc.), 2 and above prints
+warnings and info.
+
+.TP 12
+.BI \-w
+Print out results in HTML tables. Default table is two columns wide,
+with a white background.
+.TP 12
+.BI \-x " attributes"
+String to use as attributes for <table>. Attributes are inserted
+<table
+.B here
+>
+.TP 12
+.BI \-y " attributes"
+String to use as attributes for <tr>.
+.TP 12
+.BI \-z " attributes"
+String to use as attributes for <td>.
+.TP 12
+.B \-V
+Display version number and exit.
+.TP 12
+.B \-h
+Display usage information.
+.PD
+.SH BUGS
+There are various statically declared buffers of fixed length. Combined
+with the lazy parsing of the command line arguments, the response headers
+from the server and other external inputs this might bite you.
+.P
+It does not implement HTTP/1.x fully; only accepts some 'expected' forms
+of responses. The rather heavy use of
+.BR strstr(3)
+shows up top in profile,
+which might indicate a performance problem; i.e. you would measure the
+.BR ab
+performance rather than the server's.
+
+.SH SEE ALSO
+.BR httpd(8)
+.
--- /dev/null
+.TH apachectl 1 "September 1997"
+.\" Copyright (c) 1997-1999 The Apache Group. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\"
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\"
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in
+.\" the documentation and/or other materials provided with the
+.\" distribution.
+.\"
+.\" 3. All advertising materials mentioning features or use of this
+.\" software must display the following acknowledgment:
+.\" "This product includes software developed by the Apache Group
+.\" for use in the Apache HTTP server project (http://www.apache.org/)."
+.\"
+.\" 4. The names "Apache Server" and "Apache Group" must not be used to
+.\" endorse or promote products derived from this software without
+.\" prior written permission.
+.\"
+.\" 5. Products derived from this software may not be called "Apache"
+.\" nor may "Apache" appear in their names without prior written
+.\" permission of the Apache Group.
+.\"
+.\" 6. Redistributions of any form whatsoever must retain the following
+.\" acknowledgment:
+.\" "This product includes software developed by the Apache Group
+.\" for use in the Apache HTTP server project (http://www.apache.org/)."
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+.\" EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
+.\" ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+.\" SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+.\" LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+.\" STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+.\" OF THE POSSIBILITY OF SUCH DAMAGE.
+.\" ====================================================================
+.\"
+.\" This software consists of voluntary contributions made by many
+.\" individuals on behalf of the Apache Group and was originally based
+.\" on public domain software written at the National Center for
+.\" Supercomputing Applications, University of Illinois, Urbana-Champaign.
+.\" For more information on the Apache Group and the Apache HTTP server
+.\" project, please see <http://www.apache.org/>.
+.SH NAME
+apachectl \- Apache HTTP server control interface
+.SH SYNOPSIS
+.B apachectl
+\fIcommand\fP [...]
+.SH DESCRIPTION
+.B apachectl
+is a front end to the Apache HyperText Transfer Protocol (HTTP)
+server. It is designed to help the administrator control the
+functioning of the Apache
+.B httpd
+daemon.
+.PP
+.B NOTE:
+If your Apache installation uses non-standard paths, you will need to
+edit the
+.B apachectl
+script to set the appropriate paths to your PID file and your
+.B httpd
+binary. See the comments in the script for details.
+.PP
+The
+.B apachectl
+script returns a 0 exit value on success, and >0 if an error
+occurs. For more details, view the comments in the script.
+.PP
+Full documentation for Apache is available at
+.B http://www.apache.org/
+.
+.SH OPTIONS
+The \fIcommand\fP can be any one or more of the following options:
+.TP 12
+.BI start
+Start the Apache daemon. Gives an error if it is already running.
+.TP
+.BI stop
+Stops the Apache daemon.
+.TP
+.BI restart
+Restarts the Apache daemon by sending it a SIGHUP. If the daemon
+is not running, it is started.
+This command automatically checks the configuration files via
+.BI configtest
+before initiating the restart to make sure Apache doesn't die.
+.TP
+.BI fullstatus
+Displays a full status report from
+.B mod_status.
+For this to work, you need to have mod_status enabled on your server
+and a text-based browser such as \fIlynx\fP available on your system. The
+URL used to access the status report can be set by editing the
+.B STATUSURL
+variable in the script.
+.TP
+.BI status
+Displays a brief status report. Similar to the fullstatus option,
+except that the list of requests currently being served is omitted.
+.TP
+.BI graceful
+Gracefully restarts the Apache daemon by sending it a SIGUSR1. If
+the daemon is not running, it is started. This differs from a
+normal restart in that currently open connections are not aborted.
+A side effect is that old log files will not be closed immediately.
+This means that if used in a log rotation script, a substantial delay may be
+necessary to ensure that the old log files are closed before processing them.
+This command automatically checks the configuration files via
+.BI configtest
+before initiating the restart to make sure Apache doesn't die.
+.TP
+.BI configtest
+Run a configuration file syntax test. It parses the configuration
+files and either reports
+.B "Syntax Ok"
+or detailed information about the particular syntax error.
+.TP
+.BI help
+Displays a short help message.
+.SH SEE ALSO
+.BR httpd(8)
+.
--- /dev/null
+.TH apxs 8 "April 1998"
+.\" Copyright (c) 1998-1999 The Apache Group. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\"
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\"
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in
+.\" the documentation and/or other materials provided with the
+.\" distribution.
+.\"
+.\" 3. All advertising materials mentioning features or use of this
+.\" software must display the following acknowledgment:
+.\" "This product includes software developed by the Apache Group
+.\" for use in the Apache HTTP server project (http://www.apache.org/)."
+.\"
+.\" 4. The names "Apache Server" and "Apache Group" must not be used to
+.\" endorse or promote products derived from this software without
+.\" prior written permission.
+.\"
+.\" 5. Products derived from this software may not be called "Apache"
+.\" nor may "Apache" appear in their names without prior written
+.\" permission of the Apache Group.
+.\"
+.\" 6. Redistributions of any form whatsoever must retain the following
+.\" acknowledgment:
+.\" "This product includes software developed by the Apache Group
+.\" for use in the Apache HTTP server project (http://www.apache.org/)."
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+.\" EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
+.\" ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+.\" SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+.\" LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+.\" STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+.\" OF THE POSSIBILITY OF SUCH DAMAGE.
+.\" ====================================================================
+.\"
+.\" This software consists of voluntary contributions made by many
+.\" individuals on behalf of the Apache Group and was originally based
+.\" on public domain software written at the National Center for
+.\" Supercomputing Applications, University of Illinois, Urbana-Champaign.
+.\" For more information on the Apache Group and the Apache HTTP server
+.\" project, please see <http://www.apache.org/>.
+.SH NAME
+apxs \- APache eXtenSion tool
+.SH SYNOPSIS
+.B apxs
+.B \-g
+[
+.BI \-S " name=value
+]
+.BI \-n " modname"
+
+.B apxs
+.B \-q
+[
+.BI \-S " name=value
+]
+.IR query " ..."
+
+.B apxs
+.B \-c
+[
+.BI \-S " name=value
+]
+[
+.BI \-o " dsofile"
+]
+[
+.BI \-I " incdir"
+]
+[
+.BI \-D " name=value"
+]
+[
+.BI \-L " libdir"
+]
+[
+.BI \-l " libname"
+]
+[
+.BI \-Wc, "compiler-flags"
+]
+[
+.BI \-Wl, "linker-flags"
+]
+.IR files " ..."
+
+.B apxs
+.B \-i
+[
+.BI \-S " name=value
+]
+[
+.BI \-n " modname"
+]
+[
+.B \-a
+]
+[
+.B \-A
+]
+.IR dsofile " ..."
+
+.B apxs
+.B \-e
+[
+.BI \-S " name=value
+]
+[
+.BI \-n " modname"
+]
+[
+.B \-a
+]
+[
+.B \-A
+]
+.IR dsofile " ..."
+.PP
+.SH DESCRIPTION
+.B apxs
+is a tool for building and installing extension modules for the Apache
+HyperText Transfer Protocol (HTTP) server. This is achieved by building a
+dynamic shared object (DSO) from one or more source or object
+.I files
+which then can be loaded into
+the Apache server under runtime via the
+.B LoadModule
+directive from
+.BR mod_so.
+
+So to use this extension mechanism your platform has
+to support the DSO feature and your
+Apache
+.B httpd
+binary has to be built with the
+.B mod_so
+module.
+The
+.B apxs
+tool automatically complains if this is not the case.
+You can check this yourself by manually running the command
+
+.nf
+ $ httpd -l
+.fi
+
+The module
+.B mod_so
+should be part of the displayed list.
+If these requirements are fulfilled you can easily extend
+your Apache server's functionality by installing your own
+modules with the DSO mechanism by the help of this
+.B apxs
+tool:
+
+.nf
+ $ apxs -i -a -c mod_foo.c
+ gcc -fpic -DSHARED_MODULE -I/path/to/apache/include -c mod_foo.c
+ ld -Bshareable -o mod_foo.so mod_foo.o
+ cp mod_foo.so /path/to/apache/libexec/mod_foo.so
+ chmod 755 /path/to/apache/libexec/mod_foo.so
+ [activating module `foo' in /path/to/apache/etc/httpd.conf]
+ $ apachectl restart
+ /path/to/apache/sbin/apachectl restart: httpd not running, trying to start
+ [Tue Mar 31 11:27:55 1998] [debug] mod_so.c(303): loaded module foo_module
+ /path/to/apache/sbin/apachectl restart: httpd started
+ $ _
+.fi
+
+The arguments
+.I files
+can be any C source file (.c), a object file (.o) or
+even a library archive (.a). The
+.B apxs
+tool automatically recognizes these extensions and automtaically used the C
+source files for compilation while just using the object and archive files for
+the linking phase. But when using such pre-compiled objects make sure they are
+compiled for position independend code (PIC) to be able to use them for a
+dynamically loaded shared object.
+For instance with GCC you always just have to use
+.BR -fpic .
+For other
+C compilers consult its manual
+page or at watch for the flags
+.B apxs
+uses to compile the object files.
+
+For more details about DSO support in Apache read the documentation
+of
+.B mod_so
+or perhaps even read the
+.B src/modules/standard/mod_so.c
+source file.
+
+.PP
+.SH OPTIONS
+Common options:
+.TP 12
+.BI \-n " modname"
+This explicitly sets the module name for the
+.B \-i
+(install)
+and
+.B \-g
+(template generation) option. Use this to explicitly specify the module name.
+For option
+.B \-g
+this is required, for option
+.B \-i
+the
+.B apxs
+tool tries to determine the name from the source or (as a fallback) at least
+by guessing it from the filename.
+.PP
+Query options:
+.TP 12
+.B \-q
+Performs a query for
+.BR apxs 's
+knowledge about certain settings. The
+.I query
+parameters can be one or more of the following strings:
+.nf
+ CC TARGET
+ CFLAGS SBINDIR
+ CFLAGS_SHLIB INCLUDEDIR
+ LD_SHLIB LIBEXECDIR
+ LDFLAGS_SHLIB SYSCONFDIR
+ LIBS_SHLIB
+.fi
+Use this for manually determining settings. For instance use
+.nf
+ INC=-I`apxs -q INCLUDEDIR`
+.fi
+inside your own Makefiles if you need manual access
+to Apache's C header files.
+.PP
+Configuration options:
+.TP 12
+.BI \-S " name=value"
+This option changes the apxs settings described above.
+.PP
+Template Generation options:
+.TP 12
+.B \-g
+This generates a subdirectory
+.I name
+(see option
+.BR \-n ")"
+and there two files: A sample module source file named
+.BI mod_ name.c
+which can be used as a template for creating your own modules or
+as a quick start for playing with the APXS mechanism.
+And a corresponding
+.B Makefile
+for even easier build and installing of this module.
+.PP
+DSO compilation options:
+.TP 12
+.B \-c
+This indicates the compilation operation. It first compiles the C source
+files (.c) of
+.I files
+into corresponding object files (.o) and then builds a dynamically shared object in
+.I dsofile
+by linking these object files plus the remaining
+object files (.o and .a) of
+.I files
+If no
+.B \-o
+option is specified
+the output file is guessed from the first filename in
+.I files
+and thus usually defaults to
+.BI mod_ name.so
+.TP 12
+.BI \-o " dsofile"
+Explicitly specifies the filename of the created dynamically shared object. If
+not specified and the name cannot be guessed from the
+.I files
+list, the fallback name
+.B mod_unknown.so
+is used.
+.TP 12
+.BI \-D " name=value"
+This option is directly passed through to the compilation command(s).
+Use this to add your own defines to the build process.
+.TP 12
+.BI \-I " incdir"
+This option is directly passed through to the compilation command(s).
+Use this to add your own include directories to search to the build process.
+.TP 12
+.BI \-L " libdir"
+This option is directly passed through to the linker command.
+Use this to add your own library directories to search to the build process.
+.TP 12
+.BI \-l " libname"
+This option is directly passed through to the linker command.
+Use this to add your own libraries to search to the build process.
+.TP 12
+.BI \-Wc, "compiler-flags"
+This option passes
+.I compiler-flags
+as additional flags to the compiler command.
+Use this to add local compiler-specific options.
+.TP 12
+.BI \-Wl, "linker-flags"
+This option passes
+.I linker-flags
+as additional flags to the linker command.
+Use this to add local linker-specific options.
+.PP
+DSO installation and configuration options:
+.TP 12
+.B \-i
+This indicates the installation operation and installs one or more
+dynamically shared objects into the
+server's
+.I libexec
+directory.
+.TP 12
+.B \-a
+This activates the module by automatically adding a corresponding
+.B LoadModule
+line to Apache's
+.B httpd.conf
+configuration file, or by enabling it if it already exists.
+.TP 12
+.B \-A
+Same as option
+.B \-a
+but the created
+.B LoadModule
+directive is prefixed with a hash sign (#), i.e. the module is
+just prepared for later activation but initially disabled.
+.TP 12
+.B \-e
+This indicates the editing operation, which can be used with the
+.B \-a
+and
+.B \-A
+options similarly to the
+.B \-i
+operation to edit Apache's
+.B httpd.conf
+configuration file without attempting to install the module.
+.PD
+.SH EXAMPLES
+Assume you have an Apache module named mod_foo.c available which should extend
+Apache's server functionality. To accomplish this you first have to compile
+the C source into a shared object suitable for loading into the Apache server
+under runtime via the following command:
+
+.nf
+ $ apxs -c mod_foo.c
+ gcc -fpic -DSHARED_MODULE -I/path/to/apache/include -c mod_foo.c
+ ld -Bshareable -o mod_foo.so mod_foo.o
+ $ _
+.fi
+
+Then you have to update the Apache configuration by making sure a
+.B LoadModule
+directive is present to load this shared object. To simplify this
+step
+.B apxs
+provides an automatic way to install the shared object in its
+"libexec" directory and updating the
+.B httpd.conf
+file accordingly. This can be achieved by running:
+
+.nf
+ $ apxs -i -a mod_foo.c
+ cp mod_foo.so /path/to/apache/libexec/mod_foo.so
+ chmod 755 /path/to/apache/libexec/mod_foo.so
+ [activating module `foo' in /path/to/apache/etc/httpd.conf]
+ $ _
+.fi
+
+This way a line named
+
+.nf
+ LoadModule foo_module libexec/mod_foo.so
+.fi
+
+is added to the configuration file if still not present.
+If you want to have this this disabled per default use the
+.B \-A
+option, i.e.
+
+.nf
+ $ apxs -i -A mod_foo.c
+.fi
+
+For a quick test of the APXS mechanism you can create a sample Apache module
+template plus a corresponding Makefile via:
+
+.nf
+ $ apxs -g -n foo
+ Creating [DIR] foo
+ Creating [FILE] foo/Makefile
+ Creating [FILE] foo/mod_foo.c
+ $ _
+.fi
+
+Then you can immediately compile this sample module into a shared object and
+load it into the Apache server:
+
+.nf
+ $ cd foo
+ $ make all reload
+ apxs -c mod_foo.c
+ gcc -fpic -DSHARED_MODULE -I/path/to/apache/include -c mod_foo.c
+ ld -Bshareable -o mod_foo.so mod_foo.o
+ apxs -i -a -n "foo" mod_foo.so
+ cp mod_foo.so /path/to/apache/libexec/mod_foo.so
+ chmod 755 /path/to/apache/libexec/mod_foo.so
+ [activating module `foo' in /path/to/apache/etc/httpd.conf]
+ apachectl restart
+ /path/to/apache/sbin/apachectl restart: httpd not running, trying to start
+ [Tue Mar 31 11:27:55 1998] [debug] mod_so.c(303): loaded module foo_module
+ /path/to/apache/sbin/apachectl restart: httpd started
+ $ _
+.fi
+
+You can even use
+.B apxs
+to compile complex modules outside the Apache source tree, like PHP3:
+
+.nf
+ $ cd php3
+ $ ./configure --with-shared-apache=../apache-1.3
+ $ apxs -c -o libphp3.so mod_php3.c libmodphp3-so.a
+ gcc -fpic -DSHARED_MODULE -I/tmp/apache/include -c mod_php3.c
+ ld -Bshareable -o libphp3.so mod_php3.o libmodphp3-so.a
+ $ _
+.fi
+
+because
+.B apxs
+automatically recognized C source files and object files. Only C source files
+are compiled while remaining object files are used for the linking phase.
+
+.PD
+.SH SEE ALSO
+.BR apachectl(1),
+.BR httpd(8).
+.
--- /dev/null
+.TH dbmmanage 1 "March 1998"
+.\" Copyright (c) 1998-1999 The Apache Group. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\"
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\"
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in
+.\" the documentation and/or other materials provided with the
+.\" distribution.
+.\"
+.\" 3. All advertising materials mentioning features or use of this
+.\" software must display the following acknowledgment:
+.\" "This product includes software developed by the Apache Group
+.\" for use in the Apache HTTP server project (http://www.apache.org/)."
+.\"
+.\" 4. The names "Apache Server" and "Apache Group" must not be used to
+.\" endorse or promote products derived from this software without
+.\" prior written permission.
+.\"
+.\" 5. Products derived from this software may not be called "Apache"
+.\" nor may "Apache" appear in their names without prior written
+.\" permission of the Apache Group.
+.\"
+.\" 6. Redistributions of any form whatsoever must retain the following
+.\" acknowledgment:
+.\" "This product includes software developed by the Apache Group
+.\" for use in the Apache HTTP server project (http://www.apache.org/)."
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+.\" EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
+.\" ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+.\" SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+.\" LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+.\" STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+.\" OF THE POSSIBILITY OF SUCH DAMAGE.
+.\" ====================================================================
+.\"
+.\" This software consists of voluntary contributions made by many
+.\" individuals on behalf of the Apache Group and was originally based
+.\" on public domain software written at the National Center for
+.\" Supercomputing Applications, University of Illinois, Urbana-Champaign.
+.\" For more information on the Apache Group and the Apache HTTP server
+.\" project, please see <http://www.apache.org/>.
+.SH NAME
+dbmmanage \- Create and update user authentication files in DBM format
+.SH SYNOPSIS
+.B dbmmanage
+.I filename
+[
+.I command
+] [
+.I username
+[
+.I encpasswd
+] ]
+.PP
+.SH DESCRIPTION
+.B dbmmanage
+is used to create and update the DBM format files used to store
+usernames and password for basic authentication of HTTP users.
+Resources available from the
+.B httpd
+Apache web server can be restricted to just the users listed
+in the files created by
+.B dbmmanage.
+This program can only be used
+when the usernames are stored in a DBM file. To use a
+flat-file database see
+\fBhtpasswd\fP.
+.PP
+This manual page only lists the command line arguments. For details of
+the directives necessary to configure user authentication in
+.B httpd
+see
+the Apache manual, which is part of the Apache distribution or can be
+found at http://www.apache.org/.
+.SH OPTIONS
+.IP \fB\fIfilename\fP
+The filename of the DBM format file. Usually without the
+extension .db, .pag, or .dir.
+.IP \fB\fIcommand\fP
+This selects the operation to perform:
+.TP 12
+.B add
+Adds an entry for \fIusername\fP to \fIfilename\fP using the encrypted
+password \fIencpassword\fP.
+.TP 12
+.B adduser
+Asks for a password and then adds an entry for \fIusername\fP to
+\fIfilename\fP .
+.TP 12
+.B check
+Asks for a password and then checks if
+\fIusername\fP is in \fIfilename\fP and if it's password matches
+the specified one.
+.TP 12
+.B delete
+Deletes the \fIusername\fP entry from \fIfilename\fP.
+.TP 12
+.B import
+Reads username:password entries (one per line) from STDIN and adds them to
+\fIfilename\fP. The passwords already has to be crypted.
+.TP 12
+.B update
+Same as the "adduser" command, except that it makes sure \fIusername\fP
+already exists in \fIfilename\fP.
+.TP 12
+.B view
+Just displays the complete contents of the DBM file.
+.IP \fB\fIusername\fP
+The user for which the update operation is performed.
+.PD
+.SH BUGS
+.PP
+One should be aware that there are a number of different DBM file
+formats in existance, and with all likelihood, libraries for more than
+one format may exist on your system. The three primary examples are
+NDBM, the GNU project's GDBM, and Berkeley DB 2. Unfortunately, all
+these libraries use different file formats, and you must make sure
+that the file format used by
+.I filename
+is the same format that
+.B dbmmanage
+expects to see.
+.B dbmmanage
+currently has no way of determining what type of DBM file it is
+looking at. If used against the wrong format,
+.dbmmanage
+will simply return nothing, or may create a different DBM file with a
+different name, or at worst, it may corrupt the DBM file if you were
+attempting to write to it.
+.PP
+.B dbmmanage
+has a list of DBM format preferences, defined by the
+.B @AnyDBM::ISA
+array near the beginning of the program. Since we prefer the Berkeley
+DB 2 file format, the order in which
+.B dbmmanage
+will look for system libraries is Berkeley DB 2, then NDBM, and then
+GDBM. The first library found will be the library
+.B dbmmanage
+will attempt to use for all DBM file transactions. This ordering is
+slightly different than the standard
+.B @AnyDBM::ISA
+ordering in perl, as well as the ordering used by the simple dbmopen()
+call in Perl, so if you use any other utilities to manage your DBM
+files, they must also follow this preference ordering. Similar care
+must be taken if using programs in other languages, like C, to
+access these files.
+.PP
+Apache's
+.B mod_auth_db.c
+module corresponds to Berkeley DB 2 library, while
+.B mod_auth_dbm.c
+corresponds to the NDBM library. Also, one can usually use the
+.B file
+program supplied with most Unix systems to see what format a DBM file is in.
+.PD
+.SH SEE ALSO
+.BR httpd(8)
+.
--- /dev/null
+.TH htdigest 1 "March 1998"
+.\" Copyright (c) 1997-1999 The Apache Group. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\"
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\"
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in
+.\" the documentation and/or other materials provided with the
+.\" distribution.
+.\"
+.\" 3. All advertising materials mentioning features or use of this
+.\" software must display the following acknowledgment:
+.\" "This product includes software developed by the Apache Group
+.\" for use in the Apache HTTP server project (http://www.apache.org/)."
+.\"
+.\" 4. The names "Apache Server" and "Apache Group" must not be used to
+.\" endorse or promote products derived from this software without
+.\" prior written permission. For written permission, please contact
+.\" apache@apache.org.
+.\"
+.\" 5. Products derived from this software may not be called "Apache"
+.\" nor may "Apache" appear in their names without prior written
+.\" permission of the Apache Group.
+.\"
+.\" 6. Redistributions of any form whatsoever must retain the following
+.\" acknowledgment:
+.\" "This product includes software developed by the Apache Group
+.\" for use in the Apache HTTP server project (http://www.apache.org/)."
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+.\" EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
+.\" ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+.\" SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+.\" LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+.\" STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+.\" OF THE POSSIBILITY OF SUCH DAMAGE.
+.\" ====================================================================
+.\"
+.\" This software consists of voluntary contributions made by many
+.\" individuals on behalf of the Apache Group and was originally based
+.\" on public domain software written at the National Center for
+.\" Supercomputing Applications, University of Illinois, Urbana-Champaign.
+.\" For more information on the Apache Group and the Apache HTTP server
+.\" project, please see <http://www.apache.org/>.
+.SH NAME
+htdigest \- Create and update user authentication files
+.SH SYNOPSIS
+.B htdigest
+[
+.B \-c
+]
+.I passwdfile
+.I realm
+.I username
+.SH DESCRIPTION
+.B htdigest
+is used to create and update the flat-files used to store
+usernames, realm and password for digest authentication of HTTP users.
+Resources available from the
+.B httpd
+Apache web server can be restricted to just the users listed
+in the files created by
+.B htdigest.
+.PP
+This manual page only lists the command line arguments. For details of
+the directives necessary to configure digest authentication in
+.B httpd
+see
+the Apache manual, which is part of the Apache distribution or can be
+found at http://www.apache.org/.
+.SH OPTIONS
+.IP \-c
+Create the \fIpasswdfile\fP. If \fIpasswdfile\fP already exists, it
+is deleted first.
+.IP \fB\fIpasswdfile\fP
+Name of the file to contain the username, realm and password. If \-c
+is given, this file is created if it does not already exist,
+or deleted and recreated if it does exist.
+.IP \fB\fIrealm\fP
+The realm name to which the user name belongs to.
+.IP \fB\fIusername\fP
+The user name to create or update in \fBpasswdfile\fP. If
+\fIusername\fP does not exist is this file, an entry is added. If it
+does exist, the password is changed.
+.SH SEE ALSO
+.BR httpd(8)
+.
--- /dev/null
+.TH htpasswd 1 "February 1997"
+.\" Copyright (c) 1997-1999 The Apache Group. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\"
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\"
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in
+.\" the documentation and/or other materials provided with the
+.\" distribution.
+.\"
+.\" 3. All advertising materials mentioning features or use of this
+.\" software must display the following acknowledgment:
+.\" "This product includes software developed by the Apache Group
+.\" for use in the Apache HTTP server project (http://www.apache.org/)."
+.\"
+.\" 4. The names "Apache Server" and "Apache Group" must not be used to
+.\" endorse or promote products derived from this software without
+.\" prior written permission. For written permission, please contact
+.\" apache@apache.org.
+.\"
+.\" 5. Products derived from this software may not be called "Apache"
+.\" nor may "Apache" appear in their names without prior written
+.\" permission of the Apache Group.
+.\"
+.\" 6. Redistributions of any form whatsoever must retain the following
+.\" acknowledgment:
+.\" "This product includes software developed by the Apache Group
+.\" for use in the Apache HTTP server project (http://www.apache.org/)."
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+.\" EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
+.\" ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+.\" SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+.\" LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+.\" STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+.\" OF THE POSSIBILITY OF SUCH DAMAGE.
+.\" ====================================================================
+.\"
+.\" This software consists of voluntary contributions made by many
+.\" individuals on behalf of the Apache Group and was originally based
+.\" on public domain software written at the National Center for
+.\" Supercomputing Applications, University of Illinois, Urbana-Champaign.
+.\" For more information on the Apache Group and the Apache HTTP server
+.\" project, please see <http://www.apache.org/>.
+.SH NAME
+htpasswd \- Create and update user authentication files
+.SH SYNOPSIS
+.B htpasswd
+[
+.B \-c
+]
+[
+.B \-m
+]
+.I passwdfile
+.I username
+.br
+.B htpasswd
+.B \-b
+[
+.B \-c
+]
+[
+.B \-m
+.B \-d
+.B \-p
+.B \-s
+]
+.I passwdfile
+.I username
+.I password
+.SH DESCRIPTION
+.B htpasswd
+is used to create and update the flat-files used to store
+usernames and password for basic authentication of HTTP users.
+If
+.B htpasswd
+cannot access a file, such as not being able to write to the output
+file or not being able to read the file in order to update it,
+it returns an error status and makes no changes.
+.PP
+Resources available from the
+.B httpd
+Apache web server can be restricted to just the users listed
+in the files created by
+.B htpasswd.
+This program can only be used
+when the usernames are stored in a flat-file. To use a
+DBM database see
+\fBdbmmanage\fP.
+.PP
+.B htpasswd
+encrypts passwords using either a version of MD5 modified for Apache,
+or the system's \fIcrypt()\fP routine. Files managed by
+.B htpasswd
+may contain both types of passwords; some user records may have
+MD5-encrypted passwords while others in the same file may have passwords
+encrypted with \fIcrypt()\fP.
+.PP
+This manual page only lists the command line arguments. For details of
+the directives necessary to configure user authentication in
+.B httpd
+see
+the Apache manual, which is part of the Apache distribution or can be
+found at <URL:http://www.apache.org/>.
+.SH OPTIONS
+.IP \-b
+Use batch mode; \fIi.e.\fP, get the password from the command line
+rather than prompting for it. \fBThis option should be used with
+extreme care, since the password is clearly visible on the command
+line.\fP
+.IP \-c
+Create the \fIpasswdfile\fP. If \fIpasswdfile\fP already exists, it
+is rewritten and truncated.
+.IP \-m
+Use MD5 encryption for passwords. On Windows and TPF, this is the default.
+.IP \-d
+Use crypt() encryption for passwords. The default on all platforms but
+Windows and TPF. Though possibly supported by
+.B htpasswd
+onm all platforms, it is not supported by the
+.B httpd
+server on Windows and TPF.
+.IP \-s
+Use SHA encryption for passwords. Faciliates migration from/to Netscape
+servers using the LDAP Directory Interchange Format (ldif).
+.IP \-p
+Use plaintext passwords. Though
+.B htpasswd
+will support creation on all platofrms, the
+.B httpd
+deamon will only accept plain text passwords on Windows and TPF.
+.IP \fB\fIpasswdfile\fP
+Name of the file to contain the user name and password. If \-c
+is given, this file is created if it does not already exist,
+or rewritten and truncated if it does exist.
+.IP \fB\fIusername\fP
+The username to create or update in \fBpasswdfile\fP. If
+\fIusername\fP does not exist in this file, an entry is added. If it
+does exist, the password is changed.
+.IP \fB\fIpassword\fP
+The plaintext password to be encrypted and stored in the file. Only used
+with the \fI-b\fP flag.
+.SH EXIT STATUS
+.B htpasswd
+returns a zero status ("true") if the username and password have
+been successfully added or updated in the \fIpasswdfile\fP.
+.B htpasswd
+returns 1 if it encounters some problem accessing files, 2 if there
+was a syntax problem with the command line, 3 if the password was
+entered interactively and the verification entry didn't match, 4 if
+its operation was interrupted, 5 if a value is too long (username,
+filename, password, or final computed record), and 6 if the username
+contains illegal characters (see the \fBRESTRICTIONS\fP section).
+.SH EXAMPLES
+\fBhtpasswd /usr/local/etc/apache/.htpasswd-users jsmith\fP
+.IP
+Adds or modifies the password for user \fIjsmith\fP.
+The user is prompted for the password. If executed
+on a Windows system, the password will be encrypted using the
+modified Apache MD5 algorithm; otherwise, the system's
+\fIcrypt()\fP routine will be used. If the file does not
+exist,
+.B htpasswd
+will do nothing except return an error.
+.LP
+\fBhtpasswd -c /home/doe/public_html/.htpasswd jane\fP
+.IP
+Creates a new file and stores a record in it for user \fIjane\fP.
+The user is prompted for the password.
+If the file exists and cannot be read, or cannot be written,
+it is not altered and
+.B htpasswd
+will display a message and return an error status.
+.LP
+\fBhtpasswd -mb /usr/web/.htpasswd-all jones Pwd4Steve\fP
+.IP
+Encrypts the password from the command line (\fIPwd4Steve\fP) using
+the MD5 algorithm, and stores it in the specified file.
+.LP
+.SH SECURITY CONSIDERATIONS
+Web password files such as those managed by
+.B htpasswd
+should \fBnot\fP be within the Web server's URI space -- that is,
+they should not be fetchable with a browser.
+.PP
+The use of the \fI-b\fP option is discouraged, since when it is
+used the unencrypted password appears on the command line.
+.SH RESTRICTIONS
+On the Windows and MPE platforms, passwords encrypted with
+.B htpasswd
+are limited to no more than 255 characters in length. Longer
+passwords will be truncated to 255 characters.
+.PP
+The MD5 algorithm used by
+.B htpasswd
+is specific to the Apache software; passwords encrypted using it will not be
+usable with other Web servers.
+.PP
+Usernames are limited to 255 bytes and may not include the character ':'.
+.SH SEE ALSO
+.BR httpd(8)
+and the scripts in support/SHA1 which come with the distribution.
--- /dev/null
+.TH httpd 8 "February 1997"
+.\" Copyright (c) 1995-1997 David Robinson. All rights reserved.
+.\" Copyright (c) 1997-1999 The Apache Group. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\"
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\"
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in
+.\" the documentation and/or other materials provided with the
+.\" distribution.
+.\"
+.\" 3. All advertising materials mentioning features or use of this
+.\" software must display the following acknowledgment:
+.\" "This product includes software developed by the Apache Group
+.\" for use in the Apache HTTP server project (http://www.apache.org/)."
+.\"
+.\" 4. The names "Apache Server" and "Apache Group" must not be used to
+.\" endorse or promote products derived from this software without
+.\" prior written permission. For written permission, please contact
+.\" apache@apache.org.
+.\"
+.\" 5. Products derived from this software may not be called "Apache"
+.\" nor may "Apache" appear in their names without prior written
+.\" permission of the Apache Group.
+.\"
+.\" 6. Redistributions of any form whatsoever must retain the following
+.\" acknowledgment:
+.\" "This product includes software developed by the Apache Group
+.\" for use in the Apache HTTP server project (http://www.apache.org/)."
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+.\" EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
+.\" ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+.\" SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+.\" LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+.\" STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+.\" OF THE POSSIBILITY OF SUCH DAMAGE.
+.\" ====================================================================
+.\"
+.\" This software consists of voluntary contributions made by many
+.\" individuals on behalf of the Apache Group and was originally based
+.\" on public domain software written at the National Center for
+.\" Supercomputing Applications, University of Illinois, Urbana-Champaign.
+.\" For more information on the Apache Group and the Apache HTTP server
+.\" project, please see <http://www.apache.org/>.
+.SH NAME
+httpd \- Apache hypertext transfer protocol server
+.SH SYNOPSIS
+.B httpd
+[
+.B \-X
+] [
+.BI \-R " libexecdir"
+] [
+.BI \-d " serverroot"
+] [
+.BI \-f " config"
+] [
+.BI \-C " directive"
+] [
+.BI \-c " directive"
+] [
+.BI \-D " parameter"
+]
+
+.B httpd
+[
+.B \-h
+]
+[
+.B \-l
+]
+[
+.B \-L
+]
+[
+.B \-v
+]
+[
+.B \-V
+]
+[
+.B \-S
+]
+[
+.B \-t
+]
+[
+.B \-T
+]
+
+.SH DESCRIPTION
+.B httpd
+is the Apache HyperText Transfer Protocol (HTTP) server program. It is
+designed to be run as a standalone daemon process. When used like this
+it will create a pool of child processes to handle requests. To stop
+it, send a TERM signal to the initial (parent) process. The PID of
+this process is written to a file as given in the configuration file.
+Alternatively
+.B httpd
+may be invoked by the Internet daemon inetd(8) each
+time a connection to the HTTP service is made.
+.PP
+This manual page only lists the command line arguments. For details
+of the directives necessary to configure
+.B httpd
+see the Apache manual,
+which is part of the Apache distribution or can be found at
+http://www.apache.org/. Paths in this manual may not reflect those
+compiled into
+.B httpd.
+.SH OPTIONS
+.TP 12
+.BI \-R " libexecdir"
+This option is only available if Apache was built with
+the
+.I SHARED_CORE
+rule enabled which forces the Apache core code to be
+placed into a dynamic shared object (DSO) file. This file
+is searched in a hardcoded path under ServerRoot per default. Use this
+option if you want to override it.
+.TP 12
+.BI \-d " serverroot"
+Set the initial value for the ServerRoot directive to \fIserverroot\fP. This
+can be overridden by the ServerRoot command in the configuration file. The
+default is \fB/usr/local/apache\fP.
+.TP
+.BI \-f " config"
+Execute the commands in the file \fIconfig\fP on startup. If \fIconfig\fP
+does not begin with a /, then it is taken to be a path relative to
+the ServerRoot. The default is \fBconf/httpd.conf\fP.
+.TP
+.BI \-C " directive"
+Process the configuration \fIdirective\fP before reading config files.
+.TP
+.BI \-c " directive"
+Process the configuration \fIdirective\fP after reading config files.
+.TP
+.BI \-D " parameter"
+Sets a configuration \fIparameter\fP which can be used with
+<IfDefine>...</IfDefine> sections in the configuration files
+to conditionally skip or process commands.
+.TP
+.B \-h
+Output a short summary of available command line options.
+.TP
+.B \-l
+Output a list of modules compiled into the server.
+.TP
+.B \-L
+Output a list of directives together with expected arguments and
+places where the directive is valid.
+.TP
+.B \-S
+Show the settings as parsed from the config file (currently only shows the
+virtualhost settings).
+.TP
+.B \-t
+Run syntax tests for configuration files only. The program immediately exits
+after these syntax parsing with either a return code of 0 (Syntax OK) or
+return code not equal to 0 (Syntax Error).
+.TP
+.B \-T
+Same as option
+.B \-t
+but does not check the configured document roots.
+.TP
+.B \-X
+Run in single-process mode, for internal debugging purposes only; the daemon
+does not detach from the terminal or fork any children. Do NOT use this mode
+to provide ordinary web service.
+.TP
+.B \-v
+Print the version of
+.B httpd
+, and then exit.
+.TP
+.B \-V
+Print the version and build parameters of
+.B httpd
+, and then exit.
+.SH FILES
+.PD 0
+.B /usr/local/apache/conf/httpd.conf
+.br
+.B /usr/local/apache/conf/srm.conf
+.br
+.B /usr/local/apache/conf/access.conf
+.br
+.B /usr/local/apache/conf/mime.types
+.br
+.B /usr/local/apache/conf/magic
+.br
+.B /usr/local/apache/logs/error_log
+.br
+.B /usr/local/apache/logs/access_log
+.br
+.B /usr/local/apache/logs/httpd.pid
+.PD
+.SH SEE ALSO
+.BR inetd (8).
--- /dev/null
+.TH logresolve 8 "March 1998"
+.\" Copyright (c) 1998-1999 The Apache Group. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\"
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\"
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in
+.\" the documentation and/or other materials provided with the
+.\" distribution.
+.\"
+.\" 3. All advertising materials mentioning features or use of this
+.\" software must display the following acknowledgment:
+.\" "This product includes software developed by the Apache Group
+.\" for use in the Apache HTTP server project (http://www.apache.org/)."
+.\"
+.\" 4. The names "Apache Server" and "Apache Group" must not be used to
+.\" endorse or promote products derived from this software without
+.\" prior written permission.
+.\"
+.\" 5. Products derived from this software may not be called "Apache"
+.\" nor may "Apache" appear in their names without prior written
+.\" permission of the Apache Group.
+.\"
+.\" 6. Redistributions of any form whatsoever must retain the following
+.\" acknowledgment:
+.\" "This product includes software developed by the Apache Group
+.\" for use in the Apache HTTP server project (http://www.apache.org/)."
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+.\" EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
+.\" ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+.\" SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+.\" LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+.\" STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+.\" OF THE POSSIBILITY OF SUCH DAMAGE.
+.\" ====================================================================
+.\"
+.\" This software consists of voluntary contributions made by many
+.\" individuals on behalf of the Apache Group and was originally based
+.\" on public domain software written at the National Center for
+.\" Supercomputing Applications, University of Illinois, Urbana-Champaign.
+.\" For more information on the Apache Group and the Apache HTTP server
+.\" project, please see <http://www.apache.org/>.
+.SH NAME
+logresolve \- resolve hostnames for IP-adresses in Apache logfiles
+.SH SYNOPSIS
+.B logresolve
+[
+.BI \-s " filename"
+] [
+.B \-c
+] <
+.I access_log
+>
+.I access_log.new
+.PP
+.SH DESCRIPTION
+.B logresolve
+is a post-processing program to resolve IP-adresses in Apache's access
+logfiles. To minimize impact on your nameserver, logresolve has its very own
+internal hash-table cache. This means that each IP number will only be looked
+up the first time it is found in the log file.
+.SH OPTIONS
+.TP 12
+.BI \-s " filename"
+Specifies a filename to record statistics.
+.TP 12
+.B \-c
+This causes
+.B logresolve
+to apply some DNS checks: after finding the hostname from the IP address, it
+looks up the IP addresses for the hostname and checks that one of these
+matches the original address.
+.PD
+.SH SEE ALSO
+.BR httpd(8)
+.
--- /dev/null
+.TH rotatelogs 8 "March 1998"
+.\" Copyright (c) 1998-1999 The Apache Group. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\"
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\"
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in
+.\" the documentation and/or other materials provided with the
+.\" distribution.
+.\"
+.\" 3. All advertising materials mentioning features or use of this
+.\" software must display the following acknowledgment:
+.\" "This product includes software developed by the Apache Group
+.\" for use in the Apache HTTP server project (http://www.apache.org/)."
+.\"
+.\" 4. The names "Apache Server" and "Apache Group" must not be used to
+.\" endorse or promote products derived from this software without
+.\" prior written permission.
+.\"
+.\" 5. Products derived from this software may not be called "Apache"
+.\" nor may "Apache" appear in their names without prior written
+.\" permission of the Apache Group.
+.\"
+.\" 6. Redistributions of any form whatsoever must retain the following
+.\" acknowledgment:
+.\" "This product includes software developed by the Apache Group
+.\" for use in the Apache HTTP server project (http://www.apache.org/)."
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+.\" EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
+.\" ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+.\" SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+.\" LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+.\" STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+.\" OF THE POSSIBILITY OF SUCH DAMAGE.
+.\" ====================================================================
+.\"
+.\" This software consists of voluntary contributions made by many
+.\" individuals on behalf of the Apache Group and was originally based
+.\" on public domain software written at the National Center for
+.\" Supercomputing Applications, University of Illinois, Urbana-Champaign.
+.\" For more information on the Apache Group and the Apache HTTP server
+.\" project, please see <http://www.apache.org/>.
+.SH NAME
+rotatelogs \- rotate Apache logs without having to kill the server
+.SH SYNOPSIS
+.B rotatelogs
+.I logfile
+.I rotationtime
+.PP
+.SH DESCRIPTION
+.B rotatelogs
+is a simple program for use in conjunction with Apache's piped logfile
+feature which can be used like this:
+
+.fi
+ TransferLog "|rotatelogs /path/to/logs/access_log 86400"
+.mf
+
+This creates the files /path/to/logs/access_log.nnnn where nnnn is the system
+time at which the log nominally starts (this time will always be a multiple of
+the rotation time, so you can synchronize cron scripts with it). At the end
+of each rotation time (here after 24 hours) a new log is started.
+.SH OPTIONS
+.IP \fB\fIlogfile\fP
+The path plus basename of the logfile. The suffix .nnnn is automatically
+added.
+.IP \fB\fIrotationtime\fP
+The rotation time in seconds.
+.PD
+.SH SEE ALSO
+.BR httpd(8)
+.
--- /dev/null
+.TH suexec 8 "March 1998"
+.\" Copyright (c) 1998-1999 The Apache Group. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\"
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\"
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in
+.\" the documentation and/or other materials provided with the
+.\" distribution.
+.\"
+.\" 3. All advertising materials mentioning features or use of this
+.\" software must display the following acknowledgment:
+.\" "This product includes software developed by the Apache Group
+.\" for use in the Apache HTTP server project (http://www.apache.org/)."
+.\"
+.\" 4. The names "Apache Server" and "Apache Group" must not be used to
+.\" endorse or promote products derived from this software without
+.\" prior written permission.
+.\"
+.\" 5. Products derived from this software may not be called "Apache"
+.\" nor may "Apache" appear in their names without prior written
+.\" permission of the Apache Group.
+.\"
+.\" 6. Redistributions of any form whatsoever must retain the following
+.\" acknowledgment:
+.\" "This product includes software developed by the Apache Group
+.\" for use in the Apache HTTP server project (http://www.apache.org/)."
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+.\" EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
+.\" ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+.\" SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+.\" LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+.\" STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+.\" OF THE POSSIBILITY OF SUCH DAMAGE.
+.\" ====================================================================
+.\"
+.\" This software consists of voluntary contributions made by many
+.\" individuals on behalf of the Apache Group and was originally based
+.\" on public domain software written at the National Center for
+.\" Supercomputing Applications, University of Illinois, Urbana-Champaign.
+.\" For more information on the Apache Group and the Apache HTTP server
+.\" project, please see <http://www.apache.org/>.
+.SH NAME
+suexec \- Switch User For Exec
+.SH SYNOPSIS
+No synopsis for usage, because this program
+is used internally by Apache only.
+.PP
+.SH DESCRIPTION
+.B suexec
+is the "wrapper" support program for the suEXEC behaviour for Apache.
+It is run from within Apache automatically to switch the user when
+an external program has to be run under a different user. For more
+information about suEXEC see the document `Apache suEXEC Support'
+under http://www.apache.org/docs/suexec.html .
+.PD
+.SH SEE ALSO
+.BR httpd(8)
+.
--- /dev/null
+-i4 -npsl -di0 -br -nce -d0 -cli0 -npcs -nfc1
+-TBUFF
+-TFILE
+-TTRANS
+-TUINT4
+-T_trans
+-Tallow_options_t
+-Tapache_sfio
+-Tarray_header
+-Tbool_int
+-Tbuf_area
+-Tbuff_struct
+-Tbuffy
+-Tcmd_how
+-Tcmd_parms
+-Tcommand_rec
+-Tcommand_struct
+-Tconn_rec
+-Tcore_dir_config
+-Tcore_server_config
+-Tdir_maker_func
+-Tevent
+-Tglobals_s
+-Thandler_func
+-Thandler_rec
+-Tjoblist_s
+-Tlisten_rec
+-Tmerger_func
+-Tmode_t
+-Tmodule
+-Tmodule_struct
+-Tmutex
+-Tn_long
+-Tother_child_rec
+-Toverrides_t
+-Tparent_score
+-Tpid_t
+-Tpiped_log
+-Tpool
+-Trequest_rec
+-Trequire_line
+-Trlim_t
+-Tscoreboard
+-Tsemaphore
+-Tserver_addr_rec
+-Tserver_rec
+-Tserver_rec_chain
+-Tshort_score
+-Ttable
+-Ttable_entry
+-Tthread
+-Tu_wide_int
+-Tvtime_t
+-Twide_int
--- /dev/null
+# Microsoft Developer Studio Project File - Name="ApacheModuleAuthAnon" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 5.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=ApacheModuleAuthAnon - 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 "ApacheModuleAuthAnon.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 "ApacheModuleAuthAnon.mak"\
+ CFG="ApacheModuleAuthAnon - Win32 Release"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "ApacheModuleAuthAnon - Win32 Release" (based on\
+ "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "ApacheModuleAuthAnon - Win32 Debug" (based on\
+ "Win32 (x86) Dynamic-Link Library")
+!MESSAGE
+
+# Begin Project
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "ApacheModuleAuthAnon - 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 ".\ApacheModuleAuthAnonR"
+# PROP Intermediate_Dir ".\ApacheModuleAuthAnonR"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /c
+# ADD CPP /nologo /MD /W3 /GX /O2 /I "..\..\include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "SHARED_MODULE" /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "NDEBUG"
+# ADD RSC /l 0x809 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386
+# ADD LINK32 ..\..\CoreR\ApacheCore.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib /nologo /subsystem:windows /dll /machine:I386
+
+!ELSEIF "$(CFG)" == "ApacheModuleAuthAnon - 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 ".\ApacheModuleAuthAnonD"
+# PROP Intermediate_Dir ".\ApacheModuleAuthAnonD"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /c
+# ADD CPP /nologo /MDd /W3 /Gm /GX /Zi /Od /I "..\..\include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "SHARED_MODULE" /YX /FD /c
+# ADD BASE MTL /nologo /D "_DEBUG" /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "_DEBUG"
+# ADD RSC /l 0x809 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386
+# ADD LINK32 ..\..\CoreD\ApacheCore.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib /nologo /subsystem:windows /dll /debug /machine:I386
+
+!ENDIF
+
+# Begin Target
+
+# Name "ApacheModuleAuthAnon - Win32 Release"
+# Name "ApacheModuleAuthAnon - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90"
+# Begin Source File
+
+SOURCE=..\..\modules\standard\mod_auth_anon.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl;fi;fd"
+# Begin Source File
+
+SOURCE=.\readdir.h
+# End Source File
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;cnt;rtf;gif;jpg;jpeg;jpe"
+# End Group
+# End Target
+# End Project
--- /dev/null
+/* ====================================================================
+ * Copyright (c) 1995-1999 The Apache Group. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * 4. The names "Apache Server" and "Apache Group" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission. For written permission, please contact
+ * apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ * nor may "Apache" appear in their names without prior written
+ * permission of the Apache Group.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Group and was originally based
+ * on public domain software written at the National Center for
+ * Supercomputing Applications, University of Illinois, Urbana-Champaign.
+ * For more information on the Apache Group and the Apache HTTP server
+ * project, please see <http://www.apache.org/>.
+ *
+ */
+
+/*
+ * mod_isapi.c - Internet Server Application (ISA) module for Apache
+ * by Alexei Kosut <akosut@apache.org>
+ *
+ * This module implements Microsoft's ISAPI, allowing Apache (when running
+ * under Windows) to load Internet Server Applications (ISAPI extensions).
+ * It implements all of the ISAPI 2.0 specification, except for the
+ * "Microsoft-only" extensions dealing with asynchronous I/O. All ISAPI
+ * extensions that use only synchronous I/O and are compatible with the
+ * ISAPI 2.0 specification should work (most ISAPI 1.0 extensions should
+ * function as well).
+ *
+ * To load, simply place the ISA in a location in the document tree.
+ * Then add an "AddHandler isapi-isa dll" into your config file.
+ * You should now be able to load ISAPI DLLs just be reffering to their
+ * URLs. Make sure the ExecCGI option is active in the directory
+ * the ISA is in.
+ */
+
+#include "httpd.h"
+#include "http_config.h"
+#include "http_core.h"
+#include "http_protocol.h"
+#include "http_request.h"
+#include "http_log.h"
+#include "util_script.h"
+
+/* We use the exact same header file as the original */
+#include <HttpExt.h>
+
+/* Seems IIS does not enforce the requirement for \r\n termination on HSE_REQ_SEND_RESPONSE_HEADER,
+ define this to conform */
+#define RELAX_HEADER_RULE
+
+module isapi_module;
+
+/* Our "Connection ID" structure */
+
+typedef struct {
+ LPEXTENSION_CONTROL_BLOCK ecb;
+ request_rec *r;
+ int status;
+} isapi_cid;
+
+/* Declare the ISAPI functions */
+
+BOOL WINAPI GetServerVariable (HCONN hConn, LPSTR lpszVariableName,
+ LPVOID lpvBuffer, LPDWORD lpdwSizeofBuffer);
+BOOL WINAPI WriteClient (HCONN ConnID, LPVOID Buffer, LPDWORD lpwdwBytes,
+ DWORD dwReserved);
+BOOL WINAPI ReadClient (HCONN ConnID, LPVOID lpvBuffer, LPDWORD lpdwSize);
+BOOL WINAPI ServerSupportFunction (HCONN hConn, DWORD dwHSERequest,
+ LPVOID lpvBuffer, LPDWORD lpdwSize,
+ LPDWORD lpdwDataType);
+
+/*
+ The optimiser blows it totally here. What happens is that autos are addressed relative to the
+ stack pointer, which, of course, moves around. The optimiser seems to lose track of it somewhere
+ between setting isapi_entry and calling through it. We work around the problem by forcing it to
+ use frame pointers.
+*/
+#pragma optimize("y",off)
+
+int isapi_handler (request_rec *r) {
+ LPEXTENSION_CONTROL_BLOCK ecb =
+ ap_pcalloc(r->pool, sizeof(struct _EXTENSION_CONTROL_BLOCK));
+ HSE_VERSION_INFO *pVer = ap_pcalloc(r->pool, sizeof(HSE_VERSION_INFO));
+
+ HINSTANCE isapi_handle;
+ BOOL (*isapi_version)(HSE_VERSION_INFO *); /* entry point 1 */
+ DWORD (*isapi_entry)(LPEXTENSION_CONTROL_BLOCK); /* entry point 2 */
+ BOOL (*isapi_term)(DWORD); /* optional entry point 3 */
+
+ isapi_cid *cid = ap_pcalloc(r->pool, sizeof(isapi_cid));
+ table *e = r->subprocess_env;
+ int retval;
+
+ /* Use similar restrictions as CGIs */
+
+ if (!(ap_allow_options(r) & OPT_EXECCGI))
+ return FORBIDDEN;
+
+ if (r->finfo.st_mode == 0)
+ return NOT_FOUND;
+
+ if (S_ISDIR(r->finfo.st_mode))
+ return FORBIDDEN;
+
+ /* Load the module */
+
+ if (!(isapi_handle = LoadLibraryEx(r->filename, NULL,
+ LOAD_WITH_ALTERED_SEARCH_PATH))) {
+ ap_log_rerror(APLOG_MARK, APLOG_ALERT, r,
+ "Could not load DLL: %s", r->filename);
+ return SERVER_ERROR;
+ }
+
+ if (!(isapi_version =
+ (void *)(GetProcAddress(isapi_handle, "GetExtensionVersion")))) {
+ ap_log_rerror(APLOG_MARK, APLOG_ALERT, r,
+ "DLL could not load GetExtensionVersion(): %s", r->filename);
+ FreeLibrary(isapi_handle);
+ return SERVER_ERROR;
+ }
+
+ if (!(isapi_entry =
+ (void *)(GetProcAddress(isapi_handle, "HttpExtensionProc")))) {
+ ap_log_rerror(APLOG_MARK, APLOG_ALERT, r,
+ "DLL could not load HttpExtensionProc(): %s", r->filename);
+ FreeLibrary(isapi_handle);
+ return SERVER_ERROR;
+ }
+
+ isapi_term = (void *)(GetProcAddress(isapi_handle, "TerminateExtension"));
+
+ /* Run GetExtensionVersion() */
+
+ if ((*isapi_version)(pVer) != TRUE) {
+ ap_log_rerror(APLOG_MARK, APLOG_ALERT, r,
+ "ISAPI GetExtensionVersion() failed: %s", r->filename);
+ FreeLibrary(isapi_handle);
+ return SERVER_ERROR;
+ }
+
+ /* Set up variables */
+ ap_add_common_vars(r);
+ ap_add_cgi_vars(r);
+
+ /* Set up connection ID */
+ ecb->ConnID = (HCONN)cid;
+ cid->ecb = ecb;
+ cid->r = r;
+ cid->status = 0;
+
+ ecb->cbSize = sizeof(struct _EXTENSION_CONTROL_BLOCK);
+ ecb->dwVersion = MAKELONG(0, 2);
+ ecb->dwHttpStatusCode = 0;
+ strcpy(ecb->lpszLogData, "");
+ ecb->lpszMethod = r->method;
+ ecb->lpszQueryString = ap_table_get(e, "QUERY_STRING");
+ ecb->lpszPathInfo = ap_table_get(e, "PATH_INFO");
+ ecb->lpszPathTranslated = ap_table_get(e, "PATH_TRANSLATED");
+ ecb->lpszContentType = ap_table_get(e, "CONTENT_TYPE");
+
+ /* Set up client input */
+ if ((retval = ap_setup_client_block(r, REQUEST_CHUNKED_ERROR))) {
+ if (isapi_term) (*isapi_term)(HSE_TERM_MUST_UNLOAD);
+ FreeLibrary(isapi_handle);
+ return retval;
+ }
+
+ if (ap_should_client_block(r)) {
+ /* Unlike IIS, which limits this to 48k, we read the whole
+ * sucker in. I suppose this could be bad for memory if someone
+ * uploaded the complete works of Shakespeare. Well, WebSite
+ * does the same thing.
+ */
+ long to_read = atol(ap_table_get(e, "CONTENT_LENGTH"));
+ long read;
+
+ /* Actually, let's cap it at 48k, until we figure out what
+ * to do with this... we don't want a Content-Length: 1000000000
+ * taking out the machine.
+ */
+
+ if (to_read > 49152) {
+ if (isapi_term) (*isapi_term)(HSE_TERM_MUST_UNLOAD);
+ FreeLibrary(isapi_handle);
+ return HTTP_REQUEST_ENTITY_TOO_LARGE;
+ }
+
+ ecb->lpbData = ap_pcalloc(r->pool, 1 + to_read);
+
+ if ((read = ap_get_client_block(r, ecb->lpbData, to_read)) < 0) {
+ if (isapi_term) (*isapi_term)(HSE_TERM_MUST_UNLOAD);
+ FreeLibrary(isapi_handle);
+ return SERVER_ERROR;
+ }
+
+ /* Although its not to spec, IIS seems to null-terminate
+ * its lpdData string. So we will too. To make sure
+ * cbAvailable matches cbTotalBytes, we'll up the latter
+ * and equalize them.
+ */
+ ecb->cbAvailable = ecb->cbTotalBytes = read + 1;
+ ecb->lpbData[read] = '\0';
+ }
+ else {
+ ecb->cbTotalBytes = 0;
+ ecb->cbAvailable = 0;
+ ecb->lpbData = NULL;
+ }
+
+ /* Set up the callbacks */
+
+ ecb->GetServerVariable = &GetServerVariable;
+ ecb->WriteClient = &WriteClient;
+ ecb->ReadClient = &ReadClient;
+ ecb->ServerSupportFunction = &ServerSupportFunction;
+
+ /* All right... try and load the sucker */
+ retval = (*isapi_entry)(ecb);
+
+ /* Set the status (for logging) */
+ if (ecb->dwHttpStatusCode)
+ r->status = ecb->dwHttpStatusCode;
+
+ /* Check for a log message - and log it */
+ if (ecb->lpszLogData && strcmp(ecb->lpszLogData, ""))
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
+ "%s: %s", ecb->lpszLogData, r->filename);
+
+ /* All done with the DLL... get rid of it */
+ if (isapi_term) (*isapi_term)(HSE_TERM_MUST_UNLOAD);
+ FreeLibrary(isapi_handle);
+
+ switch(retval) {
+ case HSE_STATUS_SUCCESS:
+ case HSE_STATUS_SUCCESS_AND_KEEP_CONN:
+ /* Ignore the keepalive stuff; Apache handles it just fine without
+ * the ISA's "advice".
+ */
+
+ if (cid->status) /* We have a special status to return */
+ return cid->status;
+
+ return OK;
+ case HSE_STATUS_PENDING: /* We don't support this */
+ ap_log_rerror(APLOG_MARK, APLOG_WARNING, r,
+ "ISAPI asynchronous I/O not supported: %s", r->filename);
+ case HSE_STATUS_ERROR:
+ default:
+ return SERVER_ERROR;
+ }
+
+}
+#pragma optimize("",on)
+
+BOOL WINAPI GetServerVariable (HCONN hConn, LPSTR lpszVariableName,
+ LPVOID lpvBuffer, LPDWORD lpdwSizeofBuffer) {
+ request_rec *r = ((isapi_cid *)hConn)->r;
+ table *e = r->subprocess_env;
+ const char *result;
+
+ /* Mostly, we just grab it from the environment, but there are
+ * a couple of special cases
+ */
+
+ if (!strcasecmp(lpszVariableName, "UNMAPPED_REMOTE_USER")) {
+ /* We don't support NT users, so this is always the same as
+ * REMOTE_USER
+ */
+ result = ap_table_get(e, "REMOTE_USER");
+ }
+ else if (!strcasecmp(lpszVariableName, "SERVER_PORT_SECURE")) {
+ /* Apache doesn't support secure requests inherently, so
+ * we have no way of knowing. We'll be conservative, and say
+ * all requests are insecure.
+ */
+ result = "0";
+ }
+ else if (!strcasecmp(lpszVariableName, "URL")) {
+ result = r->uri;
+ }
+ else {
+ result = ap_table_get(e, lpszVariableName);
+ }
+
+ if (result) {
+ if (strlen(result) > *lpdwSizeofBuffer) {
+ *lpdwSizeofBuffer = strlen(result);
+ SetLastError(ERROR_INSUFFICIENT_BUFFER);
+ return FALSE;
+ }
+ strncpy(lpvBuffer, result, *lpdwSizeofBuffer);
+ return TRUE;
+ }
+
+ /* Didn't find it */
+ SetLastError(ERROR_INVALID_INDEX);
+ return FALSE;
+}
+
+BOOL WINAPI WriteClient (HCONN ConnID, LPVOID Buffer, LPDWORD lpwdwBytes,
+ DWORD dwReserved) {
+ request_rec *r = ((isapi_cid *)ConnID)->r;
+ int writ; /* written, actually, but why shouldn't I make up words? */
+
+ /* We only support synchronous writing */
+ if (dwReserved && dwReserved != HSE_IO_SYNC) {
+ ap_log_rerror(APLOG_MARK, APLOG_WARNING, r,
+ "ISAPI asynchronous I/O not supported: %s", r->filename);
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return FALSE;
+ }
+
+ if ((writ = ap_rwrite(Buffer, *lpwdwBytes, r)) == EOF) {
+ SetLastError(ERROR); /* XXX: Find the right error code */
+ return FALSE;
+ }
+
+ *lpwdwBytes = writ;
+ return TRUE;
+}
+
+BOOL WINAPI ReadClient (HCONN ConnID, LPVOID lpvBuffer, LPDWORD lpdwSize) {
+ /* Doesn't need to do anything; we've read all the data already */
+ return TRUE;
+}
+
+/* XXX: There is an O(n^2) attack possible here. */
+BOOL WINAPI ServerSupportFunction (HCONN hConn, DWORD dwHSERequest,
+ LPVOID lpvBuffer, LPDWORD lpdwSize,
+ LPDWORD lpdwDataType) {
+ isapi_cid *cid = (isapi_cid *)hConn;
+ request_rec *subreq, *r = cid->r;
+ char *data;
+
+ switch (dwHSERequest) {
+ case HSE_REQ_SEND_URL_REDIRECT_RESP:
+ /* Set the status to be returned when the HttpExtensionProc()
+ * is done.
+ */
+ ap_table_set (r->headers_out, "Location", lpvBuffer);
+ cid->status = cid->r->status = cid->ecb->dwHttpStatusCode = REDIRECT;
+ return TRUE;
+
+ case HSE_REQ_SEND_URL:
+ /* Read any additional input */
+
+ if (r->remaining > 0) {
+ char argsbuffer[HUGE_STRING_LEN];
+
+ while (ap_get_client_block(r, argsbuffer, HUGE_STRING_LEN));
+ }
+
+ /* Reset the method to GET */
+ r->method = ap_pstrdup(r->pool, "GET");
+ r->method_number = M_GET;
+
+ /* Don't let anyone think there's still data */
+ ap_table_unset(r->headers_in, "Content-Length");
+
+ ap_internal_redirect((char *)lpvBuffer, r);
+ return TRUE;
+
+ case HSE_REQ_SEND_RESPONSE_HEADER:
+ r->status_line = lpvBuffer ? lpvBuffer : ap_pstrdup(r->pool, "200 OK");
+ sscanf(r->status_line, "%d", &r->status);
+ cid->ecb->dwHttpStatusCode = r->status;
+
+ /* Now fill in the HTTP headers, and the rest of it. Ick.
+ * lpdwDataType contains a string that has headers (in MIME
+ * format), a blank like, then (possibly) data. We need
+ * to parse it.
+ *
+ * Easy case first:
+ */
+ if (!lpdwDataType) {
+ ap_send_http_header(r);
+ return TRUE;
+ }
+
+ /* Make a copy - don't disturb the original */
+ data = ap_pstrdup(r->pool, (char *)lpdwDataType);
+
+ /* We *should* break before this while loop ends */
+ while (*data) {
+ char *value, *lf = strchr(data, '\n');
+ int p;
+
+#ifdef RELAX_HEADER_RULE
+ if (lf)
+ *lf = '\0';
+#else
+ if (!lf) { /* Huh? Invalid data, I think */
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
+ "ISA sent invalid headers: %s", r->filename);
+ SetLastError(ERROR); /* XXX: Find right error */
+ return FALSE;
+ }
+
+ /* Get rid of \n and \r */
+ *lf = '\0';
+#endif
+ p = strlen(data);
+ if (p > 0 && data[p-1] == '\r') data[p-1] = '\0';
+
+ /* End of headers */
+ if (*data == '\0') {
+#ifdef RELAX_HEADER_RULE
+ if (lf)
+#endif
+ data = lf + 1; /* Reset data */
+ break;
+ }
+
+ if (!(value = strchr(data, ':'))) {
+ SetLastError(ERROR); /* XXX: Find right error */
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
+ "ISA sent invalid headers", r->filename);
+ return FALSE;
+ }
+
+ *value++ = '\0';
+ while (*value && ap_isspace(*value)) ++value;
+
+ /* Check all the special-case headers. Similar to what
+ * ap_scan_script_header_err() does (see that function for
+ * more detail)
+ */
+
+ if (!strcasecmp(data, "Content-Type")) {
+ char *tmp;
+ /* Nuke trailing whitespace */
+
+ char *endp = value + strlen(value) - 1;
+ while (endp > value && ap_isspace(*endp)) *endp-- = '\0';
+
+ tmp = ap_pstrdup (r->pool, value);
+ ap_str_tolower(tmp);
+ r->content_type = tmp;
+ }
+ else if (!strcasecmp(data, "Content-Length")) {
+ ap_table_set(r->headers_out, data, value);
+ }
+ else if (!strcasecmp(data, "Transfer-Encoding")) {
+ ap_table_set(r->headers_out, data, value);
+ }
+ else if (!strcasecmp(data, "Set-Cookie")) {
+ ap_table_add(r->err_headers_out, data, value);
+ }
+ else {
+ ap_table_merge(r->err_headers_out, data, value);
+ }
+
+ /* Reset data */
+#ifdef RELAX_HEADER_RULE
+ if (!lf) {
+ data += p;
+ break;
+ }
+#endif
+ data = lf + 1;
+ }
+
+ /* All the headers should be set now */
+
+ ap_send_http_header(r);
+
+ /* Any data left should now be sent directly */
+ ap_rputs(data, r);
+
+ return TRUE;
+
+ case HSE_REQ_MAP_URL_TO_PATH:
+ /* Map a URL to a filename */
+ subreq = ap_sub_req_lookup_uri(ap_pstrndup(r->pool, (char *)lpvBuffer,
+ *lpdwSize), r);
+
+ GetFullPathName(subreq->filename, *lpdwSize - 1, (char *)lpvBuffer, NULL);
+
+ /* IIS puts a trailing slash on directories, Apache doesn't */
+
+ if (S_ISDIR (subreq->finfo.st_mode)) {
+ int l = strlen((char *)lpvBuffer);
+
+ ((char *)lpvBuffer)[l] = '\\';
+ ((char *)lpvBuffer)[l + 1] = '\0';
+ }
+
+ return TRUE;
+
+ case HSE_REQ_DONE_WITH_SESSION:
+ /* Do nothing... since we don't support async I/O, they'll
+ * return from HttpExtensionProc soon
+ */
+ return TRUE;
+
+ /* We don't support all this async I/O, Microsoft-specific stuff */
+ case HSE_REQ_IO_COMPLETION:
+ case HSE_REQ_TRANSMIT_FILE:
+ ap_log_rerror(APLOG_MARK, APLOG_WARNING, r,
+ "ISAPI asynchronous I/O not supported: %s", r->filename);
+ default:
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return FALSE;
+ }
+}
+
+handler_rec isapi_handlers[] = {
+{ "isapi-isa", isapi_handler },
+{ NULL}
+};
+
+module isapi_module = {
+ STANDARD_MODULE_STUFF,
+ NULL, /* initializer */
+ NULL, /* create per-dir config */
+ NULL, /* merge per-dir config */
+ NULL, /* server config */
+ NULL, /* merge server config */
+ NULL, /* command table */
+ isapi_handlers, /* handlers */
+ NULL, /* filename translation */
+ NULL, /* check_user_id */
+ NULL, /* check auth */
+ NULL, /* check access */
+ NULL, /* type_checker */
+ NULL, /* logger */
+ NULL /* header parser */
+};
--- /dev/null
+-i4 -npsl -di0 -br -nce -d0 -cli0 -npcs -nfc1
+-TBUFF
+-TFILE
+-TTRANS
+-TUINT4
+-T_trans
+-Tallow_options_t
+-Tapache_sfio
+-Tarray_header
+-Tbool_int
+-Tbuf_area
+-Tbuff_struct
+-Tbuffy
+-Tcmd_how
+-Tcmd_parms
+-Tcommand_rec
+-Tcommand_struct
+-Tconn_rec
+-Tcore_dir_config
+-Tcore_server_config
+-Tdir_maker_func
+-Tevent
+-Tglobals_s
+-Thandler_func
+-Thandler_rec
+-Tjoblist_s
+-Tlisten_rec
+-Tmerger_func
+-Tmode_t
+-Tmodule
+-Tmodule_struct
+-Tmutex
+-Tn_long
+-Tother_child_rec
+-Toverrides_t
+-Tparent_score
+-Tpid_t
+-Tpiped_log
+-Tpool
+-Trequest_rec
+-Trequire_line
+-Trlim_t
+-Tscoreboard
+-Tsemaphore
+-Tserver_addr_rec
+-Tserver_rec
+-Tserver_rec_chain
+-Tshort_score
+-Ttable
+-Ttable_entry
+-Tthread
+-Tu_wide_int
+-Tvtime_t
+-Twide_int
--- /dev/null
+-i4 -npsl -di0 -br -nce -d0 -cli0 -npcs -nfc1
+-TBUFF
+-TFILE
+-TTRANS
+-TUINT4
+-T_trans
+-Tallow_options_t
+-Tapache_sfio
+-Tarray_header
+-Tbool_int
+-Tbuf_area
+-Tbuff_struct
+-Tbuffy
+-Tcmd_how
+-Tcmd_parms
+-Tcommand_rec
+-Tcommand_struct
+-Tconn_rec
+-Tcore_dir_config
+-Tcore_server_config
+-Tdir_maker_func
+-Tevent
+-Tglobals_s
+-Thandler_func
+-Thandler_rec
+-Tjoblist_s
+-Tlisten_rec
+-Tmerger_func
+-Tmode_t
+-Tmodule
+-Tmodule_struct
+-Tmutex
+-Tn_long
+-Tother_child_rec
+-Toverrides_t
+-Tparent_score
+-Tpid_t
+-Tpiped_log
+-Tpool
+-Trequest_rec
+-Trequire_line
+-Trlim_t
+-Tscoreboard
+-Tsemaphore
+-Tserver_addr_rec
+-Tserver_rec
+-Tserver_rec_chain
+-Tshort_score
+-Ttable
+-Ttable_entry
+-Tthread
+-Tu_wide_int
+-Tvtime_t
+-Twide_int
--- /dev/null
+-i4 -npsl -di0 -br -nce -d0 -cli0 -npcs -nfc1
+-TBUFF
+-TFILE
+-TTRANS
+-TUINT4
+-T_trans
+-Tallow_options_t
+-Tapache_sfio
+-Tarray_header
+-Tbool_int
+-Tbuf_area
+-Tbuff_struct
+-Tbuffy
+-Tcmd_how
+-Tcmd_parms
+-Tcommand_rec
+-Tcommand_struct
+-Tconn_rec
+-Tcore_dir_config
+-Tcore_server_config
+-Tdir_maker_func
+-Tevent
+-Tglobals_s
+-Thandler_func
+-Thandler_rec
+-Tjoblist_s
+-Tlisten_rec
+-Tmerger_func
+-Tmode_t
+-Tmodule
+-Tmodule_struct
+-Tmutex
+-Tn_long
+-Tother_child_rec
+-Toverrides_t
+-Tparent_score
+-Tpid_t
+-Tpiped_log
+-Tpool
+-Trequest_rec
+-Trequire_line
+-Trlim_t
+-Tscoreboard
+-Tsemaphore
+-Tserver_addr_rec
+-Tserver_rec
+-Tserver_rec_chain
+-Tshort_score
+-Ttable
+-Ttable_entry
+-Tthread
+-Tu_wide_int
+-Tvtime_t
+-Twide_int
--- /dev/null
+-i4 -npsl -di0 -br -nce -d0 -cli0 -npcs -nfc1
+-TBUFF
+-TFILE
+-TTRANS
+-TUINT4
+-T_trans
+-Tallow_options_t
+-Tapache_sfio
+-Tarray_header
+-Tbool_int
+-Tbuf_area
+-Tbuff_struct
+-Tbuffy
+-Tcmd_how
+-Tcmd_parms
+-Tcommand_rec
+-Tcommand_struct
+-Tconn_rec
+-Tcore_dir_config
+-Tcore_server_config
+-Tdir_maker_func
+-Tevent
+-Tglobals_s
+-Thandler_func
+-Thandler_rec
+-Tjoblist_s
+-Tlisten_rec
+-Tmerger_func
+-Tmode_t
+-Tmodule
+-Tmodule_struct
+-Tmutex
+-Tn_long
+-Tother_child_rec
+-Toverrides_t
+-Tparent_score
+-Tpid_t
+-Tpiped_log
+-Tpool
+-Trequest_rec
+-Trequire_line
+-Trlim_t
+-Tscoreboard
+-Tsemaphore
+-Tserver_addr_rec
+-Tserver_rec
+-Tserver_rec_chain
+-Tshort_score
+-Ttable
+-Ttable_entry
+-Tthread
+-Tu_wide_int
+-Tvtime_t
+-Twide_int
--- /dev/null
+-i4 -npsl -di0 -br -nce -d0 -cli0 -npcs -nfc1
+-TBUFF
+-TFILE
+-TTRANS
+-TUINT4
+-T_trans
+-Tallow_options_t
+-Tapache_sfio
+-Tarray_header
+-Tbool_int
+-Tbuf_area
+-Tbuff_struct
+-Tbuffy
+-Tcmd_how
+-Tcmd_parms
+-Tcommand_rec
+-Tcommand_struct
+-Tconn_rec
+-Tcore_dir_config
+-Tcore_server_config
+-Tdir_maker_func
+-Tevent
+-Tglobals_s
+-Thandler_func
+-Thandler_rec
+-Tjoblist_s
+-Tlisten_rec
+-Tmerger_func
+-Tmode_t
+-Tmodule
+-Tmodule_struct
+-Tmutex
+-Tn_long
+-Tother_child_rec
+-Toverrides_t
+-Tparent_score
+-Tpid_t
+-Tpiped_log
+-Tpool
+-Trequest_rec
+-Trequire_line
+-Trlim_t
+-Tscoreboard
+-Tsemaphore
+-Tserver_addr_rec
+-Tserver_rec
+-Tserver_rec_chain
+-Tshort_score
+-Ttable
+-Ttable_entry
+-Tthread
+-Tu_wide_int
+-Tvtime_t
+-Twide_int
--- /dev/null
+# Microsoft Developer Studio Project File - Name="ApacheModuleInfo" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 5.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=ApacheModuleInfo - 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 "ApacheModuleInfo.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 "ApacheModuleInfo.mak" CFG="ApacheModuleInfo - Win32 Release"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "ApacheModuleInfo - Win32 Release" (based on\
+ "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "ApacheModuleInfo - Win32 Debug" (based on\
+ "Win32 (x86) Dynamic-Link Library")
+!MESSAGE
+
+# Begin Project
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "ApacheModuleInfo - 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 ".\ApacheModuleInfoR"
+# PROP Intermediate_Dir ".\ApacheModuleInfoR"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /c
+# ADD CPP /nologo /MD /W3 /GX /O2 /I "..\..\include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "SHARED_MODULE" /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "NDEBUG"
+# ADD RSC /l 0x809 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386
+# ADD LINK32 ..\..\CoreR\ApacheCore.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib /nologo /subsystem:windows /dll /machine:I386
+
+!ELSEIF "$(CFG)" == "ApacheModuleInfo - 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 ".\ApacheModuleInfoD"
+# PROP Intermediate_Dir ".\ApacheModuleInfoD"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /c
+# ADD CPP /nologo /MDd /W3 /Gm /GX /Zi /Od /I "..\..\include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "SHARED_MODULE" /YX /FD /c
+# ADD BASE MTL /nologo /D "_DEBUG" /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "_DEBUG"
+# ADD RSC /l 0x809 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386
+# ADD LINK32 ..\..\CoreD\ApacheCore.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib /nologo /subsystem:windows /dll /debug /machine:I386
+
+!ENDIF
+
+# Begin Target
+
+# Name "ApacheModuleInfo - Win32 Release"
+# Name "ApacheModuleInfo - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90"
+# Begin Source File
+
+SOURCE=..\..\modules\standard\mod_info.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl;fi;fd"
+# Begin Source File
+
+SOURCE=.\readdir.h
+# End Source File
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;cnt;rtf;gif;jpg;jpeg;jpe"
+# End Group
+# End Target
+# End Project
--- /dev/null
+# Microsoft Developer Studio Project File - Name="ApacheModuleStatus" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 5.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=ApacheModuleStatus - 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 "ApacheModuleStatus.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 "ApacheModuleStatus.mak"\
+ CFG="ApacheModuleStatus - Win32 Release"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "ApacheModuleStatus - Win32 Release" (based on\
+ "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "ApacheModuleStatus - Win32 Debug" (based on\
+ "Win32 (x86) Dynamic-Link Library")
+!MESSAGE
+
+# Begin Project
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "ApacheModuleStatus - 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 ".\ApacheModuleStatusR"
+# PROP Intermediate_Dir ".\ApacheModuleStatusR"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /c
+# ADD CPP /nologo /MD /W3 /GX /O2 /I "..\..\include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "SHARED_MODULE" /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "NDEBUG"
+# ADD RSC /l 0x809 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386
+# ADD LINK32 ..\..\CoreR\ApacheCore.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib /nologo /subsystem:windows /dll /machine:I386
+
+!ELSEIF "$(CFG)" == "ApacheModuleStatus - 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 ".\ApacheModuleStatusD"
+# PROP Intermediate_Dir ".\ApacheModuleStatusD"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /c
+# ADD CPP /nologo /MDd /W3 /Gm /GX /Zi /Od /I "..\..\include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "SHARED_MODULE" /YX /FD /c
+# ADD BASE MTL /nologo /D "_DEBUG" /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "_DEBUG"
+# ADD RSC /l 0x809 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386
+# ADD LINK32 ..\..\CoreD\ApacheCore.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib /nologo /subsystem:windows /dll /debug /machine:I386
+
+!ENDIF
+
+# Begin Target
+
+# Name "ApacheModuleStatus - Win32 Release"
+# Name "ApacheModuleStatus - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90"
+# Begin Source File
+
+SOURCE=..\..\modules\standard\mod_status.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl;fi;fd"
+# Begin Source File
+
+SOURCE=.\readdir.h
+# End Source File
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;cnt;rtf;gif;jpg;jpeg;jpe"
+# End Group
+# End Target
+# End Project
--- /dev/null
+-i4 -npsl -di0 -br -nce -d0 -cli0 -npcs -nfc1
+-TBUFF
+-TFILE
+-TTRANS
+-TUINT4
+-T_trans
+-Tallow_options_t
+-Tapache_sfio
+-Tarray_header
+-Tbool_int
+-Tbuf_area
+-Tbuff_struct
+-Tbuffy
+-Tcmd_how
+-Tcmd_parms
+-Tcommand_rec
+-Tcommand_struct
+-Tconn_rec
+-Tcore_dir_config
+-Tcore_server_config
+-Tdir_maker_func
+-Tevent
+-Tglobals_s
+-Thandler_func
+-Thandler_rec
+-Tjoblist_s
+-Tlisten_rec
+-Tmerger_func
+-Tmode_t
+-Tmodule
+-Tmodule_struct
+-Tmutex
+-Tn_long
+-Tother_child_rec
+-Toverrides_t
+-Tparent_score
+-Tpid_t
+-Tpiped_log
+-Tpool
+-Trequest_rec
+-Trequire_line
+-Trlim_t
+-Tscoreboard
+-Tsemaphore
+-Tserver_addr_rec
+-Tserver_rec
+-Tserver_rec_chain
+-Tshort_score
+-Ttable
+-Ttable_entry
+-Tthread
+-Tu_wide_int
+-Tvtime_t
+-Twide_int
--- /dev/null
+/* ====================================================================
+ * Copyright (c) 1995-1999 The Apache Group. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * 4. The names "Apache Server" and "Apache Group" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission. For written permission, please contact
+ * apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ * nor may "Apache" appear in their names without prior written
+ * permission of the Apache Group.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Group and was originally based
+ * on public domain software written at the National Center for
+ * Supercomputing Applications, University of Illinois, Urbana-Champaign.
+ * For more information on the Apache Group and the Apache HTTP server
+ * project, please see <http://www.apache.org/>.
+ *
+ */
+
+#define CORE_PRIVATE
+#include "httpd.h"
+#include "http_config.h"
+#include "http_core.h"
+#include "http_protocol.h" /* For index_of_response(). Grump. */
+#include "http_request.h"
+#include "http_conf_globals.h"
+#include "http_vhost.h"
+#include "http_main.h" /* For the default_handler below... */
+#include "http_log.h"
+#include "rfc1413.h"
+#include "util_md5.h"
+#include "scoreboard.h"
+#include "fnmatch.h"
+
+#ifdef USE_MMAP_FILES
+#include <sys/mman.h>
+
+/* mmap support for static files based on ideas from John Heidemann's
+ * patch against 1.0.5. See
+ * <http://www.isi.edu/~johnh/SOFTWARE/APACHE/index.html>.
+ */
+
+/* Files have to be at least this big before they're mmap()d. This is to deal
+ * with systems where the expense of doing an mmap() and an munmap() outweighs
+ * the benefit for small files. It shouldn't be set lower than 1.
+ */
+#ifndef MMAP_THRESHOLD
+#ifdef SUNOS4
+#define MMAP_THRESHOLD (8*1024)
+#else
+#define MMAP_THRESHOLD 1
+#endif
+#endif
+#endif
+#ifndef MMAP_LIMIT
+#define MMAP_LIMIT (4*1024*1024)
+#endif
+
+/* Server core module... This module provides support for really basic
+ * server operations, including options and commands which control the
+ * operation of other modules. Consider this the bureaucracy module.
+ *
+ * The core module also defines handlers, etc., do handle just enough
+ * to allow a server with the core module ONLY to actually serve documents
+ * (though it slaps DefaultType on all of 'em); this was useful in testing,
+ * but may not be worth preserving.
+ *
+ * This file could almost be mod_core.c, except for the stuff which affects
+ * the http_conf_globals.
+ */
+
+static void *create_core_dir_config(pool *a, char *dir)
+{
+ core_dir_config *conf;
+
+ conf = (core_dir_config *)ap_pcalloc(a, sizeof(core_dir_config));
+ if (!dir || dir[strlen(dir) - 1] == '/') {
+ conf->d = dir;
+ }
+ else if (strncmp(dir, "proxy:", 6) == 0) {
+ conf->d = ap_pstrdup(a, dir);
+ }
+ else {
+ conf->d = ap_pstrcat(a, dir, "/", NULL);
+ }
+ conf->d_is_fnmatch = conf->d ? (ap_is_fnmatch(conf->d) != 0) : 0;
+ conf->d_components = conf->d ? ap_count_dirs(conf->d) : 0;
+
+ conf->opts = dir ? OPT_UNSET : OPT_UNSET|OPT_ALL;
+ conf->opts_add = conf->opts_remove = OPT_NONE;
+ conf->override = dir ? OR_UNSET : OR_UNSET|OR_ALL;
+
+ conf->content_md5 = 2;
+
+ conf->use_canonical_name = USE_CANONICAL_NAME_UNSET;
+
+ conf->hostname_lookups = HOSTNAME_LOOKUP_UNSET;
+ conf->do_rfc1413 = DEFAULT_RFC1413 | 2; /* set bit 1 to indicate default */
+ conf->satisfy = SATISFY_NOSPEC;
+
+#ifdef RLIMIT_CPU
+ conf->limit_cpu = NULL;
+#endif
+#if defined(RLIMIT_DATA) || defined(RLIMIT_VMEM) || defined(RLIMIT_AS)
+ conf->limit_mem = NULL;
+#endif
+#ifdef RLIMIT_NPROC
+ conf->limit_nproc = NULL;
+#endif
+
+ conf->limit_req_body = 0;
+ conf->sec = ap_make_array(a, 2, sizeof(void *));
+#ifdef WIN32
+ conf->script_interpreter_source = INTERPRETER_SOURCE_UNSET;
+#endif
+
+ conf->server_signature = srv_sig_unset;
+
+ return (void *)conf;
+}
+
+static void *merge_core_dir_configs(pool *a, void *basev, void *newv)
+{
+ core_dir_config *base = (core_dir_config *)basev;
+ core_dir_config *new = (core_dir_config *)newv;
+ core_dir_config *conf;
+ int i;
+
+ conf = (core_dir_config *)ap_palloc(a, sizeof(core_dir_config));
+ memcpy((char *)conf, (const char *)base, sizeof(core_dir_config));
+ if (base->response_code_strings) {
+ conf->response_code_strings =
+ ap_palloc(a, sizeof(*conf->response_code_strings)
+ * RESPONSE_CODES);
+ memcpy(conf->response_code_strings, base->response_code_strings,
+ sizeof(*conf->response_code_strings) * RESPONSE_CODES);
+ }
+
+ conf->d = new->d;
+ conf->d_is_fnmatch = new->d_is_fnmatch;
+ conf->d_components = new->d_components;
+ conf->r = new->r;
+
+ if (new->opts & OPT_UNSET) {
+ /* there was no explicit setting of new->opts, so we merge
+ * preserve the invariant (opts_add & opts_remove) == 0
+ */
+ conf->opts_add = (conf->opts_add & ~new->opts_remove) | new->opts_add;
+ conf->opts_remove = (conf->opts_remove & ~new->opts_add)
+ | new->opts_remove;
+ conf->opts = (conf->opts & ~conf->opts_remove) | conf->opts_add;
+ if ((base->opts & OPT_INCNOEXEC) && (new->opts & OPT_INCLUDES)) {
+ conf->opts = (conf->opts & ~OPT_INCNOEXEC) | OPT_INCLUDES;
+ }
+ }
+ else {
+ /* otherwise we just copy, because an explicit opts setting
+ * overrides all earlier +/- modifiers
+ */
+ conf->opts = new->opts;
+ conf->opts_add = new->opts_add;
+ conf->opts_remove = new->opts_remove;
+ }
+
+ if (!(new->override & OR_UNSET)) {
+ conf->override = new->override;
+ }
+ if (new->ap_default_type) {
+ conf->ap_default_type = new->ap_default_type;
+ }
+
+ if (new->ap_auth_type) {
+ conf->ap_auth_type = new->ap_auth_type;
+ }
+ if (new->ap_auth_name) {
+ conf->ap_auth_name = new->ap_auth_name;
+ }
+ if (new->ap_requires) {
+ conf->ap_requires = new->ap_requires;
+ }
+
+ if (new->response_code_strings) {
+ if (conf->response_code_strings == NULL) {
+ conf->response_code_strings = ap_palloc(a,
+ sizeof(*conf->response_code_strings) * RESPONSE_CODES);
+ memcpy(conf->response_code_strings, new->response_code_strings,
+ sizeof(*conf->response_code_strings) * RESPONSE_CODES);
+ }
+ else {
+ for (i = 0; i < RESPONSE_CODES; ++i) {
+ if (new->response_code_strings[i] != NULL) {
+ conf->response_code_strings[i]
+ = new->response_code_strings[i];
+ }
+ }
+ }
+ }
+ if (new->hostname_lookups != HOSTNAME_LOOKUP_UNSET) {
+ conf->hostname_lookups = new->hostname_lookups;
+ }
+ if ((new->do_rfc1413 & 2) == 0) {
+ conf->do_rfc1413 = new->do_rfc1413;
+ }
+ if ((new->content_md5 & 2) == 0) {
+ conf->content_md5 = new->content_md5;
+ }
+ if (new->use_canonical_name != USE_CANONICAL_NAME_UNSET) {
+ conf->use_canonical_name = new->use_canonical_name;
+ }
+
+#ifdef RLIMIT_CPU
+ if (new->limit_cpu) {
+ conf->limit_cpu = new->limit_cpu;
+ }
+#endif
+#if defined(RLIMIT_DATA) || defined(RLIMIT_VMEM) || defined(RLIMIT_AS)
+ if (new->limit_mem) {
+ conf->limit_mem = new->limit_mem;
+ }
+#endif
+#ifdef RLIMIT_NPROC
+ if (new->limit_nproc) {
+ conf->limit_nproc = new->limit_nproc;
+ }
+#endif
+
+ if (new->limit_req_body) {
+ conf->limit_req_body = new->limit_req_body;
+ }
+ conf->sec = ap_append_arrays(a, base->sec, new->sec);
+
+ if (new->satisfy != SATISFY_NOSPEC) {
+ conf->satisfy = new->satisfy;
+ }
+
+#ifdef WIN32
+ if (new->script_interpreter_source != INTERPRETER_SOURCE_UNSET) {
+ conf->script_interpreter_source = new->script_interpreter_source;
+ }
+#endif
+
+ if (new->server_signature != srv_sig_unset) {
+ conf->server_signature = new->server_signature;
+ }
+
+ return (void*)conf;
+}
+
+static void *create_core_server_config(pool *a, server_rec *s)
+{
+ core_server_config *conf;
+ int is_virtual = s->is_virtual;
+
+ conf = (core_server_config *)ap_pcalloc(a, sizeof(core_server_config));
+#ifdef GPROF
+ conf->gprof_dir = NULL;
+#endif
+ conf->access_name = is_virtual ? NULL : DEFAULT_ACCESS_FNAME;
+ conf->ap_document_root = is_virtual ? NULL : DOCUMENT_LOCATION;
+ conf->sec = ap_make_array(a, 40, sizeof(void *));
+ conf->sec_url = ap_make_array(a, 40, sizeof(void *));
+
+ return (void *)conf;
+}
+
+static void *merge_core_server_configs(pool *p, void *basev, void *virtv)
+{
+ core_server_config *base = (core_server_config *)basev;
+ core_server_config *virt = (core_server_config *)virtv;
+ core_server_config *conf;
+
+ conf = (core_server_config *)ap_pcalloc(p, sizeof(core_server_config));
+ *conf = *virt;
+ if (!conf->access_name) {
+ conf->access_name = base->access_name;
+ }
+ if (!conf->ap_document_root) {
+ conf->ap_document_root = base->ap_document_root;
+ }
+ conf->sec = ap_append_arrays(p, base->sec, virt->sec);
+ conf->sec_url = ap_append_arrays(p, base->sec_url, virt->sec_url);
+
+ return conf;
+}
+
+/* Add per-directory configuration entry (for <directory> section);
+ * these are part of the core server config.
+ */
+
+CORE_EXPORT(void) ap_add_per_dir_conf(server_rec *s, void *dir_config)
+{
+ core_server_config *sconf = ap_get_module_config(s->module_config,
+ &core_module);
+ void **new_space = (void **)ap_push_array(sconf->sec);
+
+ *new_space = dir_config;
+}
+
+CORE_EXPORT(void) ap_add_per_url_conf(server_rec *s, void *url_config)
+{
+ core_server_config *sconf = ap_get_module_config(s->module_config,
+ &core_module);
+ void **new_space = (void **)ap_push_array(sconf->sec_url);
+
+ *new_space = url_config;
+}
+
+CORE_EXPORT(void) ap_add_file_conf(core_dir_config *conf, void *url_config)
+{
+ void **new_space = (void **)ap_push_array(conf->sec);
+
+ *new_space = url_config;
+}
+
+/* core_reorder_directories reorders the directory sections such that the
+ * 1-component sections come first, then the 2-component, and so on, finally
+ * followed by the "special" sections. A section is "special" if it's a regex,
+ * or if it doesn't start with / -- consider proxy: matching. All movements
+ * are in-order to preserve the ordering of the sections from the config files.
+ * See directory_walk().
+ */
+
+#ifdef HAVE_DRIVE_LETTERS
+#define IS_SPECIAL(entry_core) \
+ ((entry_core)->r != NULL \
+ || ((entry_core)->d[0] != '/' && (entry_core)->d[1] != ':'))
+#else
+#define IS_SPECIAL(entry_core) \
+ ((entry_core)->r != NULL || (entry_core)->d[0] != '/')
+#endif
+
+/* We need to do a stable sort, qsort isn't stable. So to make it stable
+ * we'll be maintaining the original index into the list, and using it
+ * as the minor key during sorting. The major key is the number of
+ * components (where a "special" section has infinite components).
+ */
+struct reorder_sort_rec {
+ void *elt;
+ int orig_index;
+};
+
+static int reorder_sorter(const void *va, const void *vb)
+{
+ const struct reorder_sort_rec *a = va;
+ const struct reorder_sort_rec *b = vb;
+ core_dir_config *core_a;
+ core_dir_config *core_b;
+
+ core_a = (core_dir_config *)ap_get_module_config(a->elt, &core_module);
+ core_b = (core_dir_config *)ap_get_module_config(b->elt, &core_module);
+ if (IS_SPECIAL(core_a)) {
+ if (!IS_SPECIAL(core_b)) {
+ return 1;
+ }
+ }
+ else if (IS_SPECIAL(core_b)) {
+ return -1;
+ }
+ else {
+ /* we know they're both not special */
+ if (core_a->d_components < core_b->d_components) {
+ return -1;
+ }
+ else if (core_a->d_components > core_b->d_components) {
+ return 1;
+ }
+ }
+ /* Either they're both special, or they're both not special and have the
+ * same number of components. In any event, we now have to compare
+ * the minor key. */
+ return a->orig_index - b->orig_index;
+}
+
+void ap_core_reorder_directories(pool *p, server_rec *s)
+{
+ core_server_config *sconf;
+ array_header *sec;
+ struct reorder_sort_rec *sortbin;
+ int nelts;
+ void **elts;
+ int i;
+ pool *tmp;
+
+ sconf = ap_get_module_config(s->module_config, &core_module);
+ sec = sconf->sec;
+ nelts = sec->nelts;
+ elts = (void **)sec->elts;
+
+ /* we have to allocate tmp space to do a stable sort */
+ tmp = ap_make_sub_pool(p);
+ sortbin = ap_palloc(tmp, sec->nelts * sizeof(*sortbin));
+ for (i = 0; i < nelts; ++i) {
+ sortbin[i].orig_index = i;
+ sortbin[i].elt = elts[i];
+ }
+
+ qsort(sortbin, nelts, sizeof(*sortbin), reorder_sorter);
+
+ /* and now copy back to the original array */
+ for (i = 0; i < nelts; ++i) {
+ elts[i] = sortbin[i].elt;
+ }
+
+ ap_destroy_pool(tmp);
+}
+
+/*****************************************************************
+ *
+ * There are some elements of the core config structures in which
+ * other modules have a legitimate interest (this is ugly, but necessary
+ * to preserve NCSA back-compatibility). So, we have a bunch of accessors
+ * here...
+ */
+
+API_EXPORT(int) ap_allow_options(request_rec *r)
+{
+ core_dir_config *conf =
+ (core_dir_config *)ap_get_module_config(r->per_dir_config, &core_module);
+
+ return conf->opts;
+}
+
+API_EXPORT(int) ap_allow_overrides(request_rec *r)
+{
+ core_dir_config *conf;
+ conf = (core_dir_config *)ap_get_module_config(r->per_dir_config,
+ &core_module);
+
+ return conf->override;
+}
+
+API_EXPORT(const char *) ap_auth_type(request_rec *r)
+{
+ core_dir_config *conf;
+
+ conf = (core_dir_config *)ap_get_module_config(r->per_dir_config,
+ &core_module);
+ return conf->ap_auth_type;
+}
+
+API_EXPORT(const char *) ap_auth_name(request_rec *r)
+{
+ core_dir_config *conf;
+
+ conf = (core_dir_config *)ap_get_module_config(r->per_dir_config,
+ &core_module);
+ return conf->ap_auth_name;
+}
+
+API_EXPORT(const char *) ap_default_type(request_rec *r)
+{
+ core_dir_config *conf;
+
+ conf = (core_dir_config *)ap_get_module_config(r->per_dir_config,
+ &core_module);
+ return conf->ap_default_type
+ ? conf->ap_default_type
+ : DEFAULT_CONTENT_TYPE;
+}
+
+API_EXPORT(const char *) ap_document_root(request_rec *r) /* Don't use this! */
+{
+ core_server_config *conf;
+
+ conf = (core_server_config *)ap_get_module_config(r->server->module_config,
+ &core_module);
+ return conf->ap_document_root;
+}
+
+API_EXPORT(const array_header *) ap_requires(request_rec *r)
+{
+ core_dir_config *conf;
+
+ conf = (core_dir_config *)ap_get_module_config(r->per_dir_config,
+ &core_module);
+ return conf->ap_requires;
+}
+
+API_EXPORT(int) ap_satisfies(request_rec *r)
+{
+ core_dir_config *conf;
+
+ conf = (core_dir_config *)ap_get_module_config(r->per_dir_config,
+ &core_module);
+
+ return conf->satisfy;
+}
+
+/* Should probably just get rid of this... the only code that cares is
+ * part of the core anyway (and in fact, it isn't publicised to other
+ * modules).
+ */
+
+char *ap_response_code_string(request_rec *r, int error_index)
+{
+ core_dir_config *conf;
+
+ conf = (core_dir_config *)ap_get_module_config(r->per_dir_config,
+ &core_module);
+
+ if (conf->response_code_strings == NULL) {
+ return NULL;
+ }
+ return conf->response_code_strings[error_index];
+}
+
+
+/* Code from Harald Hanche-Olsen <hanche@imf.unit.no> */
+static ap_inline void do_double_reverse (conn_rec *conn)
+{
+ struct hostent *hptr;
+
+ if (conn->double_reverse) {
+ /* already done */
+ return;
+ }
+ if (conn->remote_host == NULL || conn->remote_host[0] == '\0') {
+ /* single reverse failed, so don't bother */
+ conn->double_reverse = -1;
+ return;
+ }
+ hptr = gethostbyname(conn->remote_host);
+ if (hptr) {
+ char **haddr;
+
+ for (haddr = hptr->h_addr_list; *haddr; haddr++) {
+ if (((struct in_addr *)(*haddr))->s_addr
+ == conn->remote_addr.sin_addr.s_addr) {
+ conn->double_reverse = 1;
+ return;
+ }
+ }
+ }
+ conn->double_reverse = -1;
+}
+
+API_EXPORT(const char *) ap_get_remote_host(conn_rec *conn, void *dir_config,
+ int type)
+{
+ struct in_addr *iaddr;
+ struct hostent *hptr;
+ int hostname_lookups;
+ int old_stat = SERVER_DEAD; /* we shouldn't ever be in this state */
+
+ /* If we haven't checked the host name, and we want to */
+ if (dir_config) {
+ hostname_lookups =
+ ((core_dir_config *)ap_get_module_config(dir_config, &core_module))
+ ->hostname_lookups;
+ if (hostname_lookups == HOSTNAME_LOOKUP_UNSET) {
+ hostname_lookups = HOSTNAME_LOOKUP_OFF;
+ }
+ }
+ else {
+ /* the default */
+ hostname_lookups = HOSTNAME_LOOKUP_OFF;
+ }
+
+ if (type != REMOTE_NOLOOKUP
+ && conn->remote_host == NULL
+ && (type == REMOTE_DOUBLE_REV
+ || hostname_lookups != HOSTNAME_LOOKUP_OFF)) {
+ old_stat = ap_update_child_status(conn->child_num, SERVER_BUSY_DNS,
+ (request_rec*)NULL);
+ iaddr = &(conn->remote_addr.sin_addr);
+ hptr = gethostbyaddr((char *)iaddr, sizeof(struct in_addr), AF_INET);
+ if (hptr != NULL) {
+ conn->remote_host = ap_pstrdup(conn->pool, (void *)hptr->h_name);
+ ap_str_tolower(conn->remote_host);
+
+ if (hostname_lookups == HOSTNAME_LOOKUP_DOUBLE) {
+ do_double_reverse(conn);
+ if (conn->double_reverse != 1) {
+ conn->remote_host = NULL;
+ }
+ }
+ }
+ /* if failed, set it to the NULL string to indicate error */
+ if (conn->remote_host == NULL) {
+ conn->remote_host = "";
+ }
+ }
+ if (type == REMOTE_DOUBLE_REV) {
+ do_double_reverse(conn);
+ if (conn->double_reverse == -1) {
+ return NULL;
+ }
+ }
+ if (old_stat != SERVER_DEAD) {
+ (void)ap_update_child_status(conn->child_num, old_stat,
+ (request_rec*)NULL);
+ }
+
+/*
+ * Return the desired information; either the remote DNS name, if found,
+ * or either NULL (if the hostname was requested) or the IP address
+ * (if any identifier was requested).
+ */
+ if (conn->remote_host != NULL && conn->remote_host[0] != '\0') {
+ return conn->remote_host;
+ }
+ else {
+ if (type == REMOTE_HOST || type == REMOTE_DOUBLE_REV) {
+ return NULL;
+ }
+ else {
+ return conn->remote_ip;
+ }
+ }
+}
+
+API_EXPORT(const char *) ap_get_remote_logname(request_rec *r)
+{
+ core_dir_config *dir_conf;
+
+ if (r->connection->remote_logname != NULL) {
+ return r->connection->remote_logname;
+ }
+
+/* If we haven't checked the identity, and we want to */
+ dir_conf = (core_dir_config *)ap_get_module_config(r->per_dir_config,
+ &core_module);
+
+ if (dir_conf->do_rfc1413 & 1) {
+ return ap_rfc1413(r->connection, r->server);
+ }
+ else {
+ return NULL;
+ }
+}
+
+/* There are two options regarding what the "name" of a server is. The
+ * "canonical" name as defined by ServerName and Port, or the "client's
+ * name" as supplied by a possible Host: header or full URI. We never
+ * trust the port passed in the client's headers, we always use the
+ * port of the actual socket.
+ *
+ * The DNS option to UseCanonicalName causes this routine to do a
+ * reverse lookup on the local IP address of the connectiona and use
+ * that for the ServerName. This makes its value more reliable while
+ * at the same time allowing Demon's magic virtual hosting to work.
+ * The assumption is that DNS lookups are sufficiently quick...
+ * -- fanf 1998-10-03
+ */
+API_EXPORT(const char *) ap_get_server_name(request_rec *r)
+{
+ conn_rec *conn = r->connection;
+ core_dir_config *d;
+
+ d = (core_dir_config *)ap_get_module_config(r->per_dir_config,
+ &core_module);
+
+ if (d->use_canonical_name == USE_CANONICAL_NAME_OFF) {
+ return r->hostname ? r->hostname : r->server->server_hostname;
+ }
+ if (d->use_canonical_name == USE_CANONICAL_NAME_DNS) {
+ if (conn->local_host == NULL) {
+ struct in_addr *iaddr;
+ struct hostent *hptr;
+ int old_stat;
+ old_stat = ap_update_child_status(conn->child_num,
+ SERVER_BUSY_DNS, r);
+ iaddr = &(conn->local_addr.sin_addr);
+ hptr = gethostbyaddr((char *)iaddr, sizeof(struct in_addr),
+ AF_INET);
+ if (hptr != NULL) {
+ conn->local_host = ap_pstrdup(conn->pool,
+ (void *)hptr->h_name);
+ ap_str_tolower(conn->local_host);
+ }
+ else {
+ conn->local_host = ap_pstrdup(conn->pool,
+ r->server->server_hostname);
+ }
+ (void) ap_update_child_status(conn->child_num, old_stat, r);
+ }
+ return conn->local_host;
+ }
+ /* default */
+ return r->server->server_hostname;
+}
+
+API_EXPORT(unsigned) ap_get_server_port(const request_rec *r)
+{
+ unsigned port;
+ core_dir_config *d =
+ (core_dir_config *)ap_get_module_config(r->per_dir_config, &core_module);
+
+ port = r->server->port ? r->server->port : ap_default_port(r);
+
+ if (d->use_canonical_name == USE_CANONICAL_NAME_OFF
+ || d->use_canonical_name == USE_CANONICAL_NAME_DNS) {
+ return r->hostname ? ntohs(r->connection->local_addr.sin_port)
+ : port;
+ }
+ /* default */
+ return port;
+}
+
+API_EXPORT(char *) ap_construct_url(pool *p, const char *uri,
+ request_rec *r)
+{
+ unsigned port = ap_get_server_port(r);
+ const char *host = ap_get_server_name(r);
+
+ if (ap_is_default_port(port, r)) {
+ return ap_pstrcat(p, ap_http_method(r), "://", host, uri, NULL);
+ }
+ return ap_psprintf(p, "%s://%s:%u%s", ap_http_method(r), host, port, uri);
+}
+
+API_EXPORT(unsigned long) ap_get_limit_req_body(const request_rec *r)
+{
+ core_dir_config *d =
+ (core_dir_config *)ap_get_module_config(r->per_dir_config, &core_module);
+
+ return d->limit_req_body;
+}
+
+#ifdef WIN32
+static char* get_interpreter_from_win32_registry(pool *p, const char* ext)
+{
+ char extension_path[] = "SOFTWARE\\Classes\\";
+ char executable_path[] = "\\SHELL\\OPEN\\COMMAND";
+
+ HKEY hkeyOpen;
+ DWORD type;
+ int size;
+ int result;
+ char *keyName;
+ char *buffer;
+ char *s;
+
+ if (!ext)
+ return NULL;
+ /*
+ * Future optimization:
+ * When the registry is successfully searched, store the interpreter
+ * string in a table to make subsequent look-ups faster
+ */
+
+ /* Open the key associated with the script extension */
+ keyName = ap_pstrcat(p, extension_path, ext, NULL);
+
+ result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, keyName, 0, KEY_QUERY_VALUE,
+ &hkeyOpen);
+
+ if (result != ERROR_SUCCESS)
+ return NULL;
+
+ /* Read to NULL buffer to find value size */
+ size = 0;
+ result = RegQueryValueEx(hkeyOpen, "", NULL, &type, NULL, &size);
+
+ if (result == ERROR_SUCCESS) {
+ buffer = ap_palloc(p, size);
+ result = RegQueryValueEx(hkeyOpen, "", NULL, &type, buffer, &size);
+ }
+
+ RegCloseKey(hkeyOpen);
+
+ if (result != ERROR_SUCCESS)
+ return NULL;
+
+ /* Open the key associated with the interpreter path */
+ keyName = ap_pstrcat(p, extension_path, buffer, executable_path, NULL);
+
+ result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, keyName, 0, KEY_QUERY_VALUE,
+ &hkeyOpen);
+
+ if (result != ERROR_SUCCESS)
+ return NULL;
+
+ /* Read to NULL buffer to find value size */
+ size = 0;
+ result = RegQueryValueEx(hkeyOpen, "", 0, &type, NULL, &size);
+
+ if (result == ERROR_SUCCESS) {
+ buffer = ap_palloc(p, size);
+ result = RegQueryValueEx(hkeyOpen, "", 0, &type, buffer, &size);
+ }
+
+ RegCloseKey(hkeyOpen);
+
+ if (result != ERROR_SUCCESS)
+ return NULL;
+
+ /*
+ * The canonical way shell command entries are entered in the Win32
+ * registry is as follows:
+ * shell [options] "%1"
+ * where
+ * shell - full path name to interpreter or shell to run.
+ * E.g., c:\usr\local\ntreskit\perl\bin\perl.exe
+ * options - optional switches
+ * E.g., \C
+ * "%1" - Place holder for file to run the shell against.
+ * Typically quoted.
+ *
+ * If we find a %1 or a quoted %1, lop it off.
+ */
+ if (buffer && *buffer) {
+ if ((s = strstr(buffer, "\"%1")))
+ *s = '\0';
+ else if ((s = strstr(buffer, "%1")))
+ *s = '\0';
+ }
+
+ return buffer;
+}
+
+API_EXPORT (file_type_e) ap_get_win32_interpreter(const request_rec *r,
+ char** interpreter )
+{
+ HANDLE hFile;
+ DWORD nBytesRead;
+ BOOLEAN bResult;
+ char buffer[1024];
+ core_dir_config *d;
+ int i;
+ file_type_e fileType = eFileTypeUNKNOWN;
+ char *ext = NULL;
+ char *exename = NULL;
+
+ d = (core_dir_config *)ap_get_module_config(r->per_dir_config,
+ &core_module);
+
+ /* Find the file extension */
+ exename = strrchr(r->filename, '/');
+ if (!exename) {
+ exename = strrchr(r->filename, '\\');
+ }
+ if (!exename) {
+ exename = r->filename;
+ }
+ else {
+ exename++;
+ }
+ ext = strrchr(exename, '.');
+
+ if (ext && (!strcasecmp(ext,".bat") || !strcasecmp(ext,".cmd"))) {
+ return eFileTypeEXE32;
+ }
+
+ /* If the file has an extension and it is not .com and not .exe and
+ * we've been instructed to search the registry, then do it!
+ */
+ if (ext && strcasecmp(ext,".exe") && strcasecmp(ext,".com") &&
+ d->script_interpreter_source == INTERPRETER_SOURCE_REGISTRY) {
+ /* Check the registry */
+ *interpreter = get_interpreter_from_win32_registry(r->pool, ext);
+ if (*interpreter)
+ return eFileTypeSCRIPT;
+ else {
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, r->server,
+ "ScriptInterpreterSource config directive set to \"registry\".\n\t"
+ "Registry was searched but interpreter not found. Trying the shebang line.");
+ }
+ }
+
+ /* Need to peek into the file figure out what it really is... */
+ hFile = CreateFile(r->filename, GENERIC_READ, FILE_SHARE_READ, NULL,
+ OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (hFile == INVALID_HANDLE_VALUE) {
+ return eFileTypeUNKNOWN;
+ }
+ bResult = ReadFile(hFile, (void*) &buffer, sizeof(buffer) - 1,
+ &nBytesRead, NULL);
+ if (!bResult || (nBytesRead == 0)) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
+ "ReadFile(%s) failed", r->filename);
+ CloseHandle(hFile);
+ return eFileTypeUNKNOWN;
+ }
+ CloseHandle(hFile);
+ buffer[nBytesRead] = '\0';
+
+ /* Script or executable, that is the question... */
+ if ((buffer[0] == '#') && (buffer[1] == '!')) {
+ /* Assuming file is a script since it starts with a shebang */
+ fileType = eFileTypeSCRIPT;
+ for (i = 2; i < sizeof(buffer); i++) {
+ if ((buffer[i] == '\r')
+ || (buffer[i] == '\n')) {
+ break;
+ }
+ }
+ buffer[i] = '\0';
+ for (i = 2; buffer[i] == ' ' ; ++i)
+ ;
+ *interpreter = ap_pstrdup(r->pool, buffer + i );
+ }
+ else {
+ /* Not a script, is it an executable? */
+ IMAGE_DOS_HEADER *hdr = (IMAGE_DOS_HEADER*)buffer;
+ if ((nBytesRead >= sizeof(IMAGE_DOS_HEADER)) && (hdr->e_magic == IMAGE_DOS_SIGNATURE)) {
+ if (hdr->e_lfarlc < 0x40)
+ fileType = eFileTypeEXE16;
+ else
+ fileType = eFileTypeEXE32;
+ }
+ else
+ fileType = eFileTypeUNKNOWN;
+ }
+
+ return fileType;
+}
+#endif
+
+/*****************************************************************
+ *
+ * Commands... this module handles almost all of the NCSA httpd.conf
+ * commands, but most of the old srm.conf is in the the modules.
+ */
+
+static const char end_directory_section[] = "</Directory>";
+static const char end_directorymatch_section[] = "</DirectoryMatch>";
+static const char end_location_section[] = "</Location>";
+static const char end_locationmatch_section[] = "</LocationMatch>";
+static const char end_files_section[] = "</Files>";
+static const char end_filesmatch_section[] = "</FilesMatch>";
+static const char end_virtualhost_section[] = "</VirtualHost>";
+static const char end_ifmodule_section[] = "</IfModule>";
+static const char end_ifdefine_section[] = "</IfDefine>";
+
+
+API_EXPORT(const char *) ap_check_cmd_context(cmd_parms *cmd,
+ unsigned forbidden)
+{
+ const char *gt = (cmd->cmd->name[0] == '<'
+ && cmd->cmd->name[strlen(cmd->cmd->name)-1] != '>')
+ ? ">" : "";
+
+ if ((forbidden & NOT_IN_VIRTUALHOST) && cmd->server->is_virtual) {
+ return ap_pstrcat(cmd->pool, cmd->cmd->name, gt,
+ " cannot occur within <VirtualHost> section", NULL);
+ }
+
+ if ((forbidden & NOT_IN_LIMIT) && cmd->limited != -1) {
+ return ap_pstrcat(cmd->pool, cmd->cmd->name, gt,
+ " cannot occur within <Limit> section", NULL);
+ }
+
+ if ((forbidden & NOT_IN_DIR_LOC_FILE) == NOT_IN_DIR_LOC_FILE
+ && cmd->path != NULL) {
+ return ap_pstrcat(cmd->pool, cmd->cmd->name, gt,
+ " cannot occur within <Directory/Location/Files> "
+ "section", NULL);
+ }
+
+ if (((forbidden & NOT_IN_DIRECTORY)
+ && (cmd->end_token == end_directory_section
+ || cmd->end_token == end_directorymatch_section))
+ || ((forbidden & NOT_IN_LOCATION)
+ && (cmd->end_token == end_location_section
+ || cmd->end_token == end_locationmatch_section))
+ || ((forbidden & NOT_IN_FILES)
+ && (cmd->end_token == end_files_section
+ || cmd->end_token == end_filesmatch_section))) {
+ return ap_pstrcat(cmd->pool, cmd->cmd->name, gt,
+ " cannot occur within <", cmd->end_token+2,
+ " section", NULL);
+ }
+
+ return NULL;
+}
+
+static const char *set_access_name(cmd_parms *cmd, void *dummy, char *arg)
+{
+ void *sconf = cmd->server->module_config;
+ core_server_config *conf = ap_get_module_config(sconf, &core_module);
+
+ const char *err = ap_check_cmd_context(cmd,
+ NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT);
+ if (err != NULL) {
+ return err;
+ }
+
+ conf->access_name = ap_pstrdup(cmd->pool, arg);
+ return NULL;
+}
+
+#ifdef GPROF
+static const char *set_gprof_dir(cmd_parms *cmd, void *dummy, char *arg)
+{
+ void *sconf = cmd->server->module_config;
+ core_server_config *conf = ap_get_module_config(sconf, &core_module);
+
+ const char *err = ap_check_cmd_context(cmd,
+ NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT);
+ if (err != NULL) {
+ return err;
+ }
+
+ conf->gprof_dir = ap_pstrdup(cmd->pool, arg);
+ return NULL;
+}
+#endif /*GPROF*/
+
+static const char *set_document_root(cmd_parms *cmd, void *dummy, char *arg)
+{
+ void *sconf = cmd->server->module_config;
+ core_server_config *conf = ap_get_module_config(sconf, &core_module);
+
+ const char *err = ap_check_cmd_context(cmd,
+ NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT);
+ if (err != NULL) {
+ return err;
+ }
+
+ arg = ap_os_canonical_filename(cmd->pool, arg);
+ if (ap_configtestonly && ap_docrootcheck && !ap_is_directory(arg)) {
+ if (cmd->server->is_virtual) {
+ fprintf(stderr, "Warning: DocumentRoot [%s] does not exist\n",
+ arg);
+ }
+ else {
+ return "DocumentRoot must be a directory";
+ }
+ }
+
+ conf->ap_document_root = arg;
+ return NULL;
+}
+
+API_EXPORT(void) ap_custom_response(request_rec *r, int status, char *string)
+{
+ core_dir_config *conf =
+ ap_get_module_config(r->per_dir_config, &core_module);
+ int idx;
+
+ if(conf->response_code_strings == NULL) {
+ conf->response_code_strings =
+ ap_pcalloc(r->pool,
+ sizeof(*conf->response_code_strings) *
+ RESPONSE_CODES);
+ }
+
+ idx = ap_index_of_response(status);
+
+ conf->response_code_strings[idx] =
+ ((ap_is_url(string) || (*string == '/')) && (*string != '"')) ?
+ ap_pstrdup(r->pool, string) : ap_pstrcat(r->pool, "\"", string, NULL);
+}
+
+static const char *set_error_document(cmd_parms *cmd, core_dir_config *conf,
+ char *line)
+{
+ int error_number, index_number, idx500;
+ char *w;
+
+ const char *err = ap_check_cmd_context(cmd, NOT_IN_LIMIT);
+ if (err != NULL) {
+ return err;
+ }
+
+ /* 1st parameter should be a 3 digit number, which we recognize;
+ * convert it into an array index
+ */
+
+ w = ap_getword_conf_nc(cmd->pool, &line);
+ error_number = atoi(w);
+
+ idx500 = ap_index_of_response(HTTP_INTERNAL_SERVER_ERROR);
+
+ if (error_number == HTTP_INTERNAL_SERVER_ERROR) {
+ index_number = idx500;
+ }
+ else if ((index_number = ap_index_of_response(error_number)) == idx500) {
+ return ap_pstrcat(cmd->pool, "Unsupported HTTP response code ",
+ w, NULL);
+ }
+
+ /* The entry should be ignored if it is a full URL for a 401 error */
+
+ if (error_number == 401 &&
+ line[0] != '/' && line[0] != '"') { /* Ignore it... */
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, cmd->server,
+ "cannot use a full URL in a 401 ErrorDocument "
+ "directive --- ignoring!");
+ }
+ else { /* Store it... */
+ if (conf->response_code_strings == NULL) {
+ conf->response_code_strings =
+ ap_pcalloc(cmd->pool,
+ sizeof(*conf->response_code_strings) * RESPONSE_CODES);
+ }
+ conf->response_code_strings[index_number] = ap_pstrdup(cmd->pool, line);
+ }
+
+ return NULL;
+}
+
+/* access.conf commands...
+ *
+ * The *only* thing that can appear in access.conf at top level is a
+ * <Directory> section. NB we need to have a way to cut the srm_command_loop
+ * invoked by dirsection (i.e., <Directory>) short when </Directory> is seen.
+ * We do that by returning an error, which dirsection itself recognizes and
+ * discards as harmless. Cheesy, but it works.
+ */
+
+static const char *set_override(cmd_parms *cmd, core_dir_config *d,
+ const char *l)
+{
+ char *w;
+
+ const char *err = ap_check_cmd_context(cmd, NOT_IN_LIMIT);
+ if (err != NULL) {
+ return err;
+ }
+
+ d->override = OR_NONE;
+ while (l[0]) {
+ w = ap_getword_conf(cmd->pool, &l);
+ if (!strcasecmp(w, "Limit")) {
+ d->override |= OR_LIMIT;
+ }
+ else if (!strcasecmp(w, "Options")) {
+ d->override |= OR_OPTIONS;
+ }
+ else if (!strcasecmp(w, "FileInfo")) {
+ d->override |= OR_FILEINFO;
+ }
+ else if (!strcasecmp(w, "AuthConfig")) {
+ d->override |= OR_AUTHCFG;
+ }
+ else if (!strcasecmp(w, "Indexes")) {
+ d->override |= OR_INDEXES;
+ }
+ else if (!strcasecmp(w, "None")) {
+ d->override = OR_NONE;
+ }
+ else if (!strcasecmp(w, "All")) {
+ d->override = OR_ALL;
+ }
+ else {
+ return ap_pstrcat(cmd->pool, "Illegal override option ", w, NULL);
+ }
+ d->override &= ~OR_UNSET;
+ }
+
+ return NULL;
+}
+
+static const char *set_options(cmd_parms *cmd, core_dir_config *d,
+ const char *l)
+{
+ allow_options_t opt;
+ int first = 1;
+ char action;
+
+ while (l[0]) {
+ char *w = ap_getword_conf(cmd->pool, &l);
+ action = '\0';
+
+ if (*w == '+' || *w == '-') {
+ action = *(w++);
+ }
+ else if (first) {
+ d->opts = OPT_NONE;
+ first = 0;
+ }
+
+ if (!strcasecmp(w, "Indexes")) {
+ opt = OPT_INDEXES;
+ }
+ else if (!strcasecmp(w, "Includes")) {
+ opt = OPT_INCLUDES;
+ }
+ else if (!strcasecmp(w, "IncludesNOEXEC")) {
+ opt = (OPT_INCLUDES | OPT_INCNOEXEC);
+ }
+ else if (!strcasecmp(w, "FollowSymLinks")) {
+ opt = OPT_SYM_LINKS;
+ }
+ else if (!strcasecmp(w, "SymLinksIfOwnerMatch")) {
+ opt = OPT_SYM_OWNER;
+ }
+ else if (!strcasecmp(w, "execCGI")) {
+ opt = OPT_EXECCGI;
+ }
+ else if (!strcasecmp(w, "MultiViews")) {
+ opt = OPT_MULTI;
+ }
+ else if (!strcasecmp(w, "RunScripts")) { /* AI backcompat. Yuck */
+ opt = OPT_MULTI|OPT_EXECCGI;
+ }
+ else if (!strcasecmp(w, "None")) {
+ opt = OPT_NONE;
+ }
+ else if (!strcasecmp(w, "All")) {
+ opt = OPT_ALL;
+ }
+ else {
+ return ap_pstrcat(cmd->pool, "Illegal option ", w, NULL);
+ }
+
+ /* we ensure the invariant (d->opts_add & d->opts_remove) == 0 */
+ if (action == '-') {
+ d->opts_remove |= opt;
+ d->opts_add &= ~opt;
+ d->opts &= ~opt;
+ }
+ else if (action == '+') {
+ d->opts_add |= opt;
+ d->opts_remove &= ~opt;
+ d->opts |= opt;
+ }
+ else {
+ d->opts |= opt;
+ }
+ }
+
+ return NULL;
+}
+
+static const char *satisfy(cmd_parms *cmd, core_dir_config *c, char *arg)
+{
+ if (!strcasecmp(arg, "all")) {
+ c->satisfy = SATISFY_ALL;
+ }
+ else if (!strcasecmp(arg, "any")) {
+ c->satisfy = SATISFY_ANY;
+ }
+ else {
+ return "Satisfy either 'any' or 'all'.";
+ }
+ return NULL;
+}
+
+static const char *require(cmd_parms *cmd, core_dir_config *c, char *arg)
+{
+ require_line *r;
+
+ if (!c->ap_requires) {
+ c->ap_requires = ap_make_array(cmd->pool, 2, sizeof(require_line));
+ }
+ r = (require_line *)ap_push_array(c->ap_requires);
+ r->requirement = ap_pstrdup(cmd->pool, arg);
+ r->method_mask = cmd->limited;
+ return NULL;
+}
+
+CORE_EXPORT_NONSTD(const char *) ap_limit_section(cmd_parms *cmd, void *dummy,
+ const char *arg)
+{
+ const char *limited_methods = ap_getword(cmd->pool, &arg, '>');
+ void *tog = cmd->cmd->cmd_data;
+ int limited = 0;
+
+ const char *err = ap_check_cmd_context(cmd, NOT_IN_LIMIT);
+ if (err != NULL) {
+ return err;
+ }
+
+ /* XXX: NB: Currently, we have no way of checking
+ * whether <Limit> or <LimitExcept> sections are closed properly.
+ * (If we would add a srm_command_loop() here we might...)
+ */
+
+ while (limited_methods[0]) {
+ char *method = ap_getword_conf(cmd->pool, &limited_methods);
+ int methnum = ap_method_number_of(method);
+
+ if (methnum == M_TRACE && !tog) {
+ return "TRACE cannot be controlled by <Limit>";
+ }
+ else if (methnum == M_INVALID) {
+ return ap_pstrcat(cmd->pool, "unknown method \"", method,
+ "\" in <Limit", tog ? "Except>" : ">", NULL);
+ }
+ else {
+ limited |= (1 << methnum);
+ }
+ }
+
+ /* Killing two features with one function,
+ * if (tog == NULL) <Limit>, else <LimitExcept>
+ */
+ cmd->limited = tog ? ~limited : limited;
+ return NULL;
+}
+
+static const char *endlimit_section(cmd_parms *cmd, void *dummy, void *dummy2)
+{
+ void *tog = cmd->cmd->cmd_data;
+
+ if (cmd->limited == -1) {
+ return tog ? "</LimitExcept> unexpected" : "</Limit> unexpected";
+ }
+
+ cmd->limited = -1;
+ return NULL;
+}
+
+/*
+ * When a section is not closed properly when end-of-file is reached,
+ * then an error message should be printed:
+ */
+static const char *missing_endsection(cmd_parms *cmd, int nest)
+{
+ if (nest < 2) {
+ return ap_psprintf(cmd->pool, "Missing %s directive at end-of-file",
+ cmd->end_token);
+ }
+ return ap_psprintf(cmd->pool, "%d missing %s directives at end-of-file",
+ nest, cmd->end_token);
+}
+
+/* We use this in <DirectoryMatch> and <FilesMatch>, to ensure that
+ * people don't get bitten by wrong-cased regex matches
+ */
+
+#ifdef WIN32
+#define USE_ICASE REG_ICASE
+#else
+#define USE_ICASE 0
+#endif
+
+static const char *end_nested_section(cmd_parms *cmd, void *dummy)
+{
+ if (cmd->end_token == NULL) {
+ return ap_pstrcat(cmd->pool, cmd->cmd->name,
+ " without matching <", cmd->cmd->name + 2,
+ " section", NULL);
+ }
+ /*
+ * This '!=' may look weird on a string comparison, but it's correct --
+ * it's been set up so that checking for two pointers to the same datum
+ * is valid here. And faster.
+ */
+ if (cmd->cmd->name != cmd->end_token) {
+ return ap_pstrcat(cmd->pool, "Expected ", cmd->end_token, " but saw ",
+ cmd->cmd->name, NULL);
+ }
+ return cmd->end_token;
+}
+
+/*
+ * Report a missing-'>' syntax error.
+ */
+static char *unclosed_directive(cmd_parms *cmd)
+{
+ return ap_pstrcat(cmd->pool, cmd->cmd->name,
+ "> directive missing closing '>'", NULL);
+}
+
+static const char *dirsection(cmd_parms *cmd, void *dummy, const char *arg)
+{
+ const char *errmsg;
+ char *endp = strrchr(arg, '>');
+ int old_overrides = cmd->override;
+ char *old_path = cmd->path;
+ core_dir_config *conf;
+ void *new_dir_conf = ap_create_per_dir_config(cmd->pool);
+ regex_t *r = NULL;
+ const char *old_end_token;
+ const command_rec *thiscmd = cmd->cmd;
+
+ const char *err = ap_check_cmd_context(cmd,
+ NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT);
+ if (err != NULL) {
+ return err;
+ }
+
+ if (endp == NULL) {
+ return unclosed_directive(cmd);
+ }
+
+ *endp = '\0';
+
+ cmd->path = ap_getword_conf(cmd->pool, &arg);
+ cmd->override = OR_ALL|ACCESS_CONF;
+
+ if (thiscmd->cmd_data) { /* <DirectoryMatch> */
+ r = ap_pregcomp(cmd->pool, cmd->path, REG_EXTENDED|USE_ICASE);
+ }
+ else if (!strcmp(cmd->path, "~")) {
+ cmd->path = ap_getword_conf(cmd->pool, &arg);
+ r = ap_pregcomp(cmd->pool, cmd->path, REG_EXTENDED|USE_ICASE);
+ }
+ else {
+ /* Ensure that the pathname is canonical */
+ cmd->path = ap_os_canonical_filename(cmd->pool, cmd->path);
+ }
+
+ old_end_token = cmd->end_token;
+ cmd->end_token = thiscmd->cmd_data ? end_directorymatch_section : end_directory_section;
+ errmsg = ap_srm_command_loop(cmd, new_dir_conf);
+ if (errmsg == NULL) {
+ errmsg = missing_endsection(cmd, 1);
+ }
+ cmd->end_token = old_end_token;
+ if (errmsg != (thiscmd->cmd_data
+ ? end_directorymatch_section
+ : end_directory_section)) {
+ return errmsg;
+ }
+
+ conf = (core_dir_config *)ap_get_module_config(new_dir_conf, &core_module);
+ conf->r = r;
+
+ ap_add_per_dir_conf(cmd->server, new_dir_conf);
+
+ if (*arg != '\0') {
+ return ap_pstrcat(cmd->pool, "Multiple ", thiscmd->name,
+ "> arguments not (yet) supported.", NULL);
+ }
+
+ cmd->path = old_path;
+ cmd->override = old_overrides;
+
+ return NULL;
+}
+
+static const char *urlsection(cmd_parms *cmd, void *dummy, const char *arg)
+{
+ const char *errmsg;
+ char *endp = strrchr(arg, '>');
+ int old_overrides = cmd->override;
+ char *old_path = cmd->path;
+ core_dir_config *conf;
+ regex_t *r = NULL;
+ const char *old_end_token;
+ const command_rec *thiscmd = cmd->cmd;
+
+ void *new_url_conf = ap_create_per_dir_config(cmd->pool);
+
+ const char *err = ap_check_cmd_context(cmd,
+ NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT);
+ if (err != NULL) {
+ return err;
+ }
+
+ if (endp == NULL) {
+ return unclosed_directive(cmd);
+ }
+
+ *endp = '\0';
+
+ cmd->path = ap_getword_conf(cmd->pool, &arg);
+ cmd->override = OR_ALL|ACCESS_CONF;
+
+ if (thiscmd->cmd_data) { /* <LocationMatch> */
+ r = ap_pregcomp(cmd->pool, cmd->path, REG_EXTENDED);
+ }
+ else if (!strcmp(cmd->path, "~")) {
+ cmd->path = ap_getword_conf(cmd->pool, &arg);
+ r = ap_pregcomp(cmd->pool, cmd->path, REG_EXTENDED);
+ }
+
+ old_end_token = cmd->end_token;
+ cmd->end_token = thiscmd->cmd_data ? end_locationmatch_section
+ : end_location_section;
+ errmsg = ap_srm_command_loop(cmd, new_url_conf);
+ if (errmsg == NULL) {
+ errmsg = missing_endsection(cmd, 1);
+ }
+ cmd->end_token = old_end_token;
+ if (errmsg != (thiscmd->cmd_data
+ ? end_locationmatch_section
+ : end_location_section)) {
+ return errmsg;
+ }
+
+ conf = (core_dir_config *)ap_get_module_config(new_url_conf, &core_module);
+ conf->d = ap_pstrdup(cmd->pool, cmd->path); /* No mangling, please */
+ conf->d_is_fnmatch = ap_is_fnmatch(conf->d) != 0;
+ conf->r = r;
+
+ ap_add_per_url_conf(cmd->server, new_url_conf);
+
+ if (*arg != '\0') {
+ return ap_pstrcat(cmd->pool, "Multiple ", thiscmd->name,
+ "> arguments not (yet) supported.", NULL);
+ }
+
+ cmd->path = old_path;
+ cmd->override = old_overrides;
+
+ return NULL;
+}
+
+static const char *filesection(cmd_parms *cmd, core_dir_config *c,
+ const char *arg)
+{
+ const char *errmsg;
+ char *endp = strrchr(arg, '>');
+ int old_overrides = cmd->override;
+ char *old_path = cmd->path;
+ core_dir_config *conf;
+ regex_t *r = NULL;
+ const char *old_end_token;
+ const command_rec *thiscmd = cmd->cmd;
+
+ void *new_file_conf = ap_create_per_dir_config(cmd->pool);
+
+ const char *err = ap_check_cmd_context(cmd, NOT_IN_LIMIT|NOT_IN_LOCATION);
+ if (err != NULL) {
+ return err;
+ }
+
+ if (endp == NULL) {
+ return unclosed_directive(cmd);
+ }
+
+ *endp = '\0';
+
+ cmd->path = ap_getword_conf(cmd->pool, &arg);
+ /* Only if not an .htaccess file */
+ if (!old_path) {
+ cmd->override = OR_ALL|ACCESS_CONF;
+ }
+
+ if (thiscmd->cmd_data) { /* <FilesMatch> */
+ r = ap_pregcomp(cmd->pool, cmd->path, REG_EXTENDED|USE_ICASE);
+ }
+ else if (!strcmp(cmd->path, "~")) {
+ cmd->path = ap_getword_conf(cmd->pool, &arg);
+ r = ap_pregcomp(cmd->pool, cmd->path, REG_EXTENDED|USE_ICASE);
+ }
+ else {
+ /* Ensure that the pathname is canonical */
+ cmd->path = ap_os_canonical_filename(cmd->pool, cmd->path);
+ }
+
+ old_end_token = cmd->end_token;
+ cmd->end_token = thiscmd->cmd_data ? end_filesmatch_section : end_files_section;
+ errmsg = ap_srm_command_loop(cmd, new_file_conf);
+ if (errmsg == NULL) {
+ errmsg = missing_endsection(cmd, 1);
+ }
+ cmd->end_token = old_end_token;
+ if (errmsg != (thiscmd->cmd_data
+ ? end_filesmatch_section
+ : end_files_section)) {
+ return errmsg;
+ }
+
+ conf = (core_dir_config *)ap_get_module_config(new_file_conf,
+ &core_module);
+ conf->d = cmd->path;
+ conf->d_is_fnmatch = ap_is_fnmatch(conf->d) != 0;
+ conf->r = r;
+
+ ap_add_file_conf(c, new_file_conf);
+
+ if (*arg != '\0') {
+ return ap_pstrcat(cmd->pool, "Multiple ", thiscmd->name,
+ "> arguments not (yet) supported.", NULL);
+ }
+
+ cmd->path = old_path;
+ cmd->override = old_overrides;
+
+ return NULL;
+}
+
+/* XXX: NB: Currently, we have no way of checking
+ * whether <IfModule> sections are closed properly.
+ * Extra (redundant, unpaired) </IfModule> directives are
+ * simply silently ignored.
+ */
+static const char *end_ifmod(cmd_parms *cmd, void *dummy)
+{
+ return NULL;
+}
+
+static const char *start_ifmod(cmd_parms *cmd, void *dummy, char *arg)
+{
+ char *endp = strrchr(arg, '>');
+ char l[MAX_STRING_LEN];
+ int not = (arg[0] == '!');
+ module *found;
+ int nest = 1;
+
+ if (endp == NULL) {
+ return unclosed_directive(cmd);
+ }
+
+ *endp = '\0';
+
+ if (not) {
+ arg++;
+ }
+
+ found = ap_find_linked_module(arg);
+
+ if ((!not && found) || (not && !found)) {
+ return NULL;
+ }
+
+ while (nest && !(ap_cfg_getline(l, MAX_STRING_LEN, cmd->config_file))) {
+ if (!strncasecmp(l, "<IfModule", 9)) {
+ nest++;
+ }
+ if (!strcasecmp(l, "</IfModule>")) {
+ nest--;
+ }
+ }
+
+ if (nest) {
+ cmd->end_token = end_ifmodule_section;
+ return missing_endsection(cmd, nest);
+ }
+ return NULL;
+}
+
+API_EXPORT(int) ap_exists_config_define(char *name)
+{
+ char **defines;
+ int i;
+
+ defines = (char **)ap_server_config_defines->elts;
+ for (i = 0; i < ap_server_config_defines->nelts; i++) {
+ if (strcmp(defines[i], name) == 0) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static const char *end_ifdefine(cmd_parms *cmd, void *dummy)
+{
+ return NULL;
+}
+
+static const char *start_ifdefine(cmd_parms *cmd, void *dummy, char *arg)
+{
+ char *endp;
+ char l[MAX_STRING_LEN];
+ int defined;
+ int not = 0;
+ int nest = 1;
+
+ endp = strrchr(arg, '>');
+ if (endp == NULL) {
+ return unclosed_directive(cmd);
+ }
+
+ *endp = '\0';
+
+ if (arg[0] == '!') {
+ not = 1;
+ arg++;
+ }
+
+ defined = ap_exists_config_define(arg);
+
+ if ((!not && defined) || (not && !defined)) {
+ return NULL;
+ }
+
+ while (nest && !(ap_cfg_getline(l, MAX_STRING_LEN, cmd->config_file))) {
+ if (!strncasecmp(l, "<IfDefine", 9)) {
+ nest++;
+ }
+ if (!strcasecmp(l, "</IfDefine>")) {
+ nest--;
+ }
+ }
+ if (nest) {
+ cmd->end_token = end_ifdefine_section;
+ return missing_endsection(cmd, nest);
+ }
+ return NULL;
+}
+
+/* httpd.conf commands... beginning with the <VirtualHost> business */
+
+static const char *virtualhost_section(cmd_parms *cmd, void *dummy, char *arg)
+{
+ server_rec *main_server = cmd->server, *s;
+ const char *errmsg;
+ char *endp = strrchr(arg, '>');
+ pool *p = cmd->pool, *ptemp = cmd->temp_pool;
+ const char *old_end_token;
+
+ const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+ if (err != NULL) {
+ return err;
+ }
+
+ if (endp == NULL) {
+ return unclosed_directive(cmd);
+ }
+
+ *endp = '\0';
+
+ /* FIXME: There's another feature waiting to happen here -- since you
+ can now put multiple addresses/names on a single <VirtualHost>
+ you might want to use it to group common definitions and then
+ define other "subhosts" with their individual differences. But
+ personally I'd rather just do it with a macro preprocessor. -djg */
+ if (main_server->is_virtual) {
+ return "<VirtualHost> doesn't nest!";
+ }
+
+ errmsg = ap_init_virtual_host(p, arg, main_server, &s);
+ if (errmsg) {
+ return errmsg;
+ }
+
+ s->next = main_server->next;
+ main_server->next = s;
+
+ s->defn_name = cmd->config_file->name;
+ s->defn_line_number = cmd->config_file->line_number;
+
+ old_end_token = cmd->end_token;
+ cmd->end_token = end_virtualhost_section;
+ cmd->server = s;
+ errmsg = ap_srm_command_loop(cmd, s->lookup_defaults);
+ cmd->server = main_server;
+ if (errmsg == NULL) {
+ errmsg = missing_endsection(cmd, 1);
+ }
+ cmd->end_token = old_end_token;
+
+ if (s->srm_confname) {
+ ap_process_resource_config(s, s->srm_confname, p, ptemp);
+ }
+
+ if (s->access_confname) {
+ ap_process_resource_config(s, s->access_confname, p, ptemp);
+ }
+
+ if (errmsg == end_virtualhost_section) {
+ return NULL;
+ }
+ return errmsg;
+}
+
+static const char *set_server_alias(cmd_parms *cmd, void *dummy,
+ const char *arg)
+{
+ if (!cmd->server->names) {
+ return "ServerAlias only used in <VirtualHost>";
+ }
+ while (*arg) {
+ char **item, *name = ap_getword_conf(cmd->pool, &arg);
+ if (ap_is_matchexp(name)) {
+ item = (char **)ap_push_array(cmd->server->wild_names);
+ }
+ else {
+ item = (char **)ap_push_array(cmd->server->names);
+ }
+ *item = name;
+ }
+ return NULL;
+}
+
+static const char *add_module_command(cmd_parms *cmd, void *dummy, char *arg)
+{
+ const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+ if (err != NULL) {
+ return err;
+ }
+
+ if (!ap_add_named_module(arg)) {
+ return ap_pstrcat(cmd->pool, "Cannot add module via name '", arg,
+ "': not in list of loaded modules", NULL);
+ }
+ return NULL;
+}
+
+static const char *clear_module_list_command(cmd_parms *cmd, void *dummy)
+{
+ const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+ if (err != NULL) {
+ return err;
+ }
+
+ ap_clear_module_list();
+ return NULL;
+}
+
+static const char *set_server_string_slot(cmd_parms *cmd, void *dummy,
+ char *arg)
+{
+ /* This one's pretty generic... */
+
+ int offset = (int)(long)cmd->info;
+ char *struct_ptr = (char *)cmd->server;
+
+ const char *err = ap_check_cmd_context(cmd,
+ NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT);
+ if (err != NULL) {
+ return err;
+ }
+
+ *(char **)(struct_ptr + offset) = arg;
+ return NULL;
+}
+
+static const char *server_type(cmd_parms *cmd, void *dummy, char *arg)
+{
+ const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+ if (err != NULL) {
+ return err;
+ }
+
+ if (!strcasecmp(arg, "inetd")) {
+ ap_standalone = 0;
+ }
+ else if (!strcasecmp(arg, "standalone")) {
+ ap_standalone = 1;
+ }
+ else {
+ return "ServerType must be either 'inetd' or 'standalone'";
+ }
+
+ return NULL;
+}
+
+static const char *server_port(cmd_parms *cmd, void *dummy, char *arg)
+{
+ const char *err = ap_check_cmd_context(cmd, NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT);
+ int port;
+
+ if (err != NULL) {
+ return err;
+ }
+ port = atoi(arg);
+ if (port <= 0 || port >= 65536) { /* 65536 == 1<<16 */
+ return ap_pstrcat(cmd->temp_pool, "The port number \"", arg,
+ "\" is outside the appropriate range "
+ "(i.e., 1..65535).", NULL);
+ }
+ cmd->server->port = port;
+ return NULL;
+}
+
+static const char *set_signature_flag(cmd_parms *cmd, core_dir_config *d,
+ char *arg)
+{
+ const char *err = ap_check_cmd_context(cmd, NOT_IN_LIMIT);
+ if (err != NULL) {
+ return err;
+ }
+
+ if (strcasecmp(arg, "On") == 0) {
+ d->server_signature = srv_sig_on;
+ }
+ else if (strcasecmp(arg, "Off") == 0) {
+ d->server_signature = srv_sig_off;
+ }
+ else if (strcasecmp(arg, "EMail") == 0) {
+ d->server_signature = srv_sig_withmail;
+ }
+ else {
+ return "ServerSignature: use one of: off | on | email";
+ }
+ return NULL;
+}
+
+static const char *set_send_buffer_size(cmd_parms *cmd, void *dummy, char *arg)
+{
+ int s = atoi(arg);
+ const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+ if (err != NULL) {
+ return err;
+ }
+
+ if (s < 512 && s != 0) {
+ return "SendBufferSize must be >= 512 bytes, or 0 for system default.";
+ }
+ cmd->server->send_buffer_size = s;
+ return NULL;
+}
+
+static const char *set_user(cmd_parms *cmd, void *dummy, char *arg)
+{
+#ifdef WIN32
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, cmd->server,
+ "User directive has no affect on Win32");
+ cmd->server->server_uid = ap_user_id = 1;
+#else
+ const char *err = ap_check_cmd_context(cmd, NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT);
+ if (err != NULL) {
+ return err;
+ }
+
+ if (!cmd->server->is_virtual) {
+ ap_user_name = arg;
+ cmd->server->server_uid = ap_user_id = ap_uname2id(arg);
+ }
+ else {
+ if (ap_suexec_enabled) {
+ cmd->server->server_uid = ap_uname2id(arg);
+ }
+ else {
+ cmd->server->server_uid = ap_user_id;
+ fprintf(stderr,
+ "Warning: User directive in <VirtualHost> "
+ "requires SUEXEC wrapper.\n");
+ }
+ }
+#if !defined (BIG_SECURITY_HOLE) && !defined (OS2)
+ if (cmd->server->server_uid == 0) {
+ fprintf(stderr,
+ "Error:\tApache has not been designed to serve pages while\n"
+ "\trunning as root. There are known race conditions that\n"
+ "\twill allow any local user to read any file on the system.\n"
+ "\tIf you still desire to serve pages as root then\n"
+ "\tadd -DBIG_SECURITY_HOLE to the EXTRA_CFLAGS line in your\n"
+ "\tsrc/Configuration file and rebuild the server. It is\n"
+ "\tstrongly suggested that you instead modify the User\n"
+ "\tdirective in your httpd.conf file to list a non-root\n"
+ "\tuser.\n");
+ exit (1);
+ }
+#endif
+#endif /* WIN32 */
+
+ return NULL;
+}
+
+static const char *set_group(cmd_parms *cmd, void *dummy, char *arg)
+{
+ const char *err = ap_check_cmd_context(cmd, NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT);
+ if (err != NULL) {
+ return err;
+ }
+
+ if (!cmd->server->is_virtual) {
+ cmd->server->server_gid = ap_group_id = ap_gname2id(arg);
+ }
+ else {
+ if (ap_suexec_enabled) {
+ cmd->server->server_gid = ap_gname2id(arg);
+ }
+ else {
+ cmd->server->server_gid = ap_group_id;
+ fprintf(stderr,
+ "Warning: Group directive in <VirtualHost> requires "
+ "SUEXEC wrapper.\n");
+ }
+ }
+
+ return NULL;
+}
+
+static const char *set_server_root(cmd_parms *cmd, void *dummy, char *arg)
+{
+ const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+
+ if (err != NULL) {
+ return err;
+ }
+
+ arg = ap_os_canonical_filename(cmd->pool, arg);
+
+ if (!ap_is_directory(arg)) {
+ return "ServerRoot must be a valid directory";
+ }
+ ap_cpystrn(ap_server_root, arg,
+ sizeof(ap_server_root));
+ return NULL;
+}
+
+static const char *set_timeout(cmd_parms *cmd, void *dummy, char *arg)
+{
+ const char *err = ap_check_cmd_context(cmd, NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT);
+ if (err != NULL) {
+ return err;
+ }
+
+ cmd->server->timeout = atoi(arg);
+ return NULL;
+}
+
+static const char *set_keep_alive_timeout(cmd_parms *cmd, void *dummy,
+ char *arg)
+{
+ const char *err = ap_check_cmd_context(cmd, NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT);
+ if (err != NULL) {
+ return err;
+ }
+
+ cmd->server->keep_alive_timeout = atoi(arg);
+ return NULL;
+}
+
+static const char *set_keep_alive(cmd_parms *cmd, void *dummy, char *arg)
+{
+ const char *err = ap_check_cmd_context(cmd, NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT);
+ if (err != NULL) {
+ return err;
+ }
+
+ /* We've changed it to On/Off, but used to use numbers
+ * so we accept anything but "Off" or "0" as "On"
+ */
+ if (!strcasecmp(arg, "off") || !strcmp(arg, "0")) {
+ cmd->server->keep_alive = 0;
+ }
+ else {
+ cmd->server->keep_alive = 1;
+ }
+ return NULL;
+}
+
+static const char *set_keep_alive_max(cmd_parms *cmd, void *dummy, char *arg)
+{
+ const char *err = ap_check_cmd_context(cmd, NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT);
+ if (err != NULL) {
+ return err;
+ }
+
+ cmd->server->keep_alive_max = atoi(arg);
+ return NULL;
+}
+
+static const char *set_pidfile(cmd_parms *cmd, void *dummy, char *arg)
+{
+ const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+ if (err != NULL) {
+ return err;
+ }
+
+ if (cmd->server->is_virtual) {
+ return "PidFile directive not allowed in <VirtualHost>";
+ }
+ ap_pid_fname = arg;
+ return NULL;
+}
+
+static const char *set_scoreboard(cmd_parms *cmd, void *dummy, char *arg)
+{
+ const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+ if (err != NULL) {
+ return err;
+ }
+
+ ap_scoreboard_fname = arg;
+ return NULL;
+}
+
+static const char *set_lockfile(cmd_parms *cmd, void *dummy, char *arg)
+{
+ const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+ if (err != NULL) {
+ return err;
+ }
+
+ ap_lock_fname = arg;
+ return NULL;
+}
+
+static const char *set_idcheck(cmd_parms *cmd, core_dir_config *d, int arg)
+{
+ const char *err = ap_check_cmd_context(cmd, NOT_IN_LIMIT);
+ if (err != NULL) {
+ return err;
+ }
+
+ d->do_rfc1413 = arg != 0;
+ return NULL;
+}
+
+static const char *set_hostname_lookups(cmd_parms *cmd, core_dir_config *d,
+ char *arg)
+{
+ const char *err = ap_check_cmd_context(cmd, NOT_IN_LIMIT);
+ if (err != NULL) {
+ return err;
+ }
+
+ if (!strcasecmp(arg, "on")) {
+ d->hostname_lookups = HOSTNAME_LOOKUP_ON;
+ }
+ else if (!strcasecmp(arg, "off")) {
+ d->hostname_lookups = HOSTNAME_LOOKUP_OFF;
+ }
+ else if (!strcasecmp(arg, "double")) {
+ d->hostname_lookups = HOSTNAME_LOOKUP_DOUBLE;
+ }
+ else {
+ return "parameter must be 'on', 'off', or 'double'";
+ }
+ return NULL;
+}
+
+static const char *set_serverpath(cmd_parms *cmd, void *dummy, char *arg)
+{
+ const char *err = ap_check_cmd_context(cmd, NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT);
+ if (err != NULL) {
+ return err;
+ }
+
+ cmd->server->path = arg;
+ cmd->server->pathlen = strlen(arg);
+ return NULL;
+}
+
+static const char *set_content_md5(cmd_parms *cmd, core_dir_config *d, int arg)
+{
+ const char *err = ap_check_cmd_context(cmd, NOT_IN_LIMIT);
+ if (err != NULL) {
+ return err;
+ }
+
+ d->content_md5 = arg != 0;
+ return NULL;
+}
+
+static const char *set_use_canonical_name(cmd_parms *cmd, core_dir_config *d,
+ char *arg)
+{
+ const char *err = ap_check_cmd_context(cmd, NOT_IN_LIMIT);
+ if (err != NULL) {
+ return err;
+ }
+
+ if (strcasecmp(arg, "on") == 0) {
+ d->use_canonical_name = USE_CANONICAL_NAME_ON;
+ }
+ else if (strcasecmp(arg, "off") == 0) {
+ d->use_canonical_name = USE_CANONICAL_NAME_OFF;
+ }
+ else if (strcasecmp(arg, "dns") == 0) {
+ d->use_canonical_name = USE_CANONICAL_NAME_DNS;
+ }
+ else {
+ return "parameter must be 'on', 'off', or 'dns'";
+ }
+ return NULL;
+}
+
+static const char *set_daemons_to_start(cmd_parms *cmd, void *dummy, char *arg)
+{
+#ifdef WIN32
+ fprintf(stderr, "WARNING: StartServers has no effect on Win32\n");
+#else
+ const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+ if (err != NULL) {
+ return err;
+ }
+
+ ap_daemons_to_start = atoi(arg);
+#endif
+ return NULL;
+}
+
+static const char *set_min_free_servers(cmd_parms *cmd, void *dummy, char *arg)
+{
+ const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+ if (err != NULL) {
+ return err;
+ }
+
+ ap_daemons_min_free = atoi(arg);
+ if (ap_daemons_min_free <= 0) {
+ fprintf(stderr, "WARNING: detected MinSpareServers set to non-positive.\n");
+ fprintf(stderr, "Resetting to 1 to avoid almost certain Apache failure.\n");
+ fprintf(stderr, "Please read the documentation.\n");
+ ap_daemons_min_free = 1;
+ }
+
+ return NULL;
+}
+
+static const char *set_max_free_servers(cmd_parms *cmd, void *dummy, char *arg)
+{
+ const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+ if (err != NULL) {
+ return err;
+ }
+
+ ap_daemons_max_free = atoi(arg);
+ return NULL;
+}
+
+static const char *set_server_limit (cmd_parms *cmd, void *dummy, char *arg)
+{
+ const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+ if (err != NULL) {
+ return err;
+ }
+
+ ap_daemons_limit = atoi(arg);
+ if (ap_daemons_limit > HARD_SERVER_LIMIT) {
+ fprintf(stderr, "WARNING: MaxClients of %d exceeds compile time limit "
+ "of %d servers,\n", ap_daemons_limit, HARD_SERVER_LIMIT);
+ fprintf(stderr, " lowering MaxClients to %d. To increase, please "
+ "see the\n", HARD_SERVER_LIMIT);
+ fprintf(stderr, " HARD_SERVER_LIMIT define in src/include/httpd.h.\n");
+ ap_daemons_limit = HARD_SERVER_LIMIT;
+ }
+ else if (ap_daemons_limit < 1) {
+ fprintf(stderr, "WARNING: Require MaxClients > 0, setting to 1\n");
+ ap_daemons_limit = 1;
+ }
+ return NULL;
+}
+
+static const char *set_max_requests(cmd_parms *cmd, void *dummy, char *arg)
+{
+ const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+ if (err != NULL) {
+ return err;
+ }
+
+ ap_max_requests_per_child = atoi(arg);
+ return NULL;
+}
+
+static const char *set_threads(cmd_parms *cmd, void *dummy, char *arg) {
+ const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+ if (err != NULL) {
+ return err;
+ }
+
+ ap_threads_per_child = atoi(arg);
+ if (ap_threads_per_child > HARD_SERVER_LIMIT) {
+ fprintf(stderr, "WARNING: ThreadsPerChild of %d exceeds compile time limit "
+ "of %d threads,\n", ap_threads_per_child, HARD_SERVER_LIMIT);
+ fprintf(stderr, " lowering ThreadsPerChild to %d. To increase, please "
+ "see the\n", HARD_SERVER_LIMIT);
+ fprintf(stderr, " HARD_SERVER_LIMIT define in src/include/httpd.h.\n");
+ ap_threads_per_child = HARD_SERVER_LIMIT;
+ }
+ else if (ap_threads_per_child < 1) {
+ fprintf(stderr, "WARNING: Require ThreadsPerChild > 0, setting to 1\n");
+ ap_threads_per_child = 1;
+ }
+
+ return NULL;
+}
+
+static const char *set_excess_requests(cmd_parms *cmd, void *dummy, char *arg)
+{
+ const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+ if (err != NULL) {
+ return err;
+ }
+
+ ap_excess_requests_per_child = atoi(arg);
+ return NULL;
+}
+
+
+#if defined(RLIMIT_CPU) || defined(RLIMIT_DATA) || defined(RLIMIT_VMEM) || defined(RLIMIT_NPROC) || defined(RLIMIT_AS)
+static void set_rlimit(cmd_parms *cmd, struct rlimit **plimit, const char *arg,
+ const char * arg2, int type)
+{
+ char *str;
+ struct rlimit *limit;
+ /* If your platform doesn't define rlim_t then typedef it in ap_config.h */
+ rlim_t cur = 0;
+ rlim_t max = 0;
+
+ *plimit = (struct rlimit *)ap_pcalloc(cmd->pool, sizeof(**plimit));
+ limit = *plimit;
+ if ((getrlimit(type, limit)) != 0) {
+ *plimit = NULL;
+ ap_log_error(APLOG_MARK, APLOG_ERR, cmd->server,
+ "%s: getrlimit failed", cmd->cmd->name);
+ return;
+ }
+
+ if ((str = ap_getword_conf(cmd->pool, &arg))) {
+ if (!strcasecmp(str, "max")) {
+ cur = limit->rlim_max;
+ }
+ else {
+ cur = atol(str);
+ }
+ }
+ else {
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, cmd->server,
+ "Invalid parameters for %s", cmd->cmd->name);
+ return;
+ }
+
+ if (arg2 && (str = ap_getword_conf(cmd->pool, &arg2))) {
+ max = atol(str);
+ }
+
+ /* if we aren't running as root, cannot increase max */
+ if (geteuid()) {
+ limit->rlim_cur = cur;
+ if (max) {
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, cmd->server,
+ "Must be uid 0 to raise maximum %s", cmd->cmd->name);
+ }
+ }
+ else {
+ if (cur) {
+ limit->rlim_cur = cur;
+ }
+ if (max) {
+ limit->rlim_max = max;
+ }
+ }
+}
+#endif
+
+#if !defined (RLIMIT_CPU) || !(defined (RLIMIT_DATA) || defined (RLIMIT_VMEM) || defined(RLIMIT_AS)) || !defined (RLIMIT_NPROC)
+static const char *no_set_limit(cmd_parms *cmd, core_dir_config *conf,
+ char *arg, char *arg2)
+{
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, cmd->server,
+ "%s not supported on this platform", cmd->cmd->name);
+ return NULL;
+}
+#endif
+
+#ifdef RLIMIT_CPU
+static const char *set_limit_cpu(cmd_parms *cmd, core_dir_config *conf,
+ char *arg, char *arg2)
+{
+ set_rlimit(cmd, &conf->limit_cpu, arg, arg2, RLIMIT_CPU);
+ return NULL;
+}
+#endif
+
+#if defined (RLIMIT_DATA) || defined (RLIMIT_VMEM) || defined(RLIMIT_AS)
+static const char *set_limit_mem(cmd_parms *cmd, core_dir_config *conf,
+ char *arg, char * arg2)
+{
+#if defined(RLIMIT_AS)
+ set_rlimit(cmd, &conf->limit_mem, arg, arg2 ,RLIMIT_AS);
+#elif defined(RLIMIT_DATA)
+ set_rlimit(cmd, &conf->limit_mem, arg, arg2, RLIMIT_DATA);
+#elif defined(RLIMIT_VMEM)
+ set_rlimit(cmd, &conf->limit_mem, arg, arg2, RLIMIT_VMEM);
+#endif
+ return NULL;
+}
+#endif
+
+#ifdef RLIMIT_NPROC
+static const char *set_limit_nproc(cmd_parms *cmd, core_dir_config *conf,
+ char *arg, char * arg2)
+{
+ set_rlimit(cmd, &conf->limit_nproc, arg, arg2, RLIMIT_NPROC);
+ return NULL;
+}
+#endif
+
+static const char *set_bind_address(cmd_parms *cmd, void *dummy, char *arg)
+{
+ const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+ if (err != NULL) {
+ return err;
+ }
+
+ ap_bind_address.s_addr = ap_get_virthost_addr(arg, NULL);
+ return NULL;
+}
+
+static const char *set_listener(cmd_parms *cmd, void *dummy, char *ips)
+{
+ listen_rec *new;
+ char *ports;
+ unsigned short port;
+
+ const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+ if (err != NULL) {
+ return err;
+ }
+
+ ports = strchr(ips, ':');
+ if (ports != NULL) {
+ if (ports == ips) {
+ return "Missing IP address";
+ }
+ else if (ports[1] == '\0') {
+ return "Address must end in :<port-number>";
+ }
+ *(ports++) = '\0';
+ }
+ else {
+ ports = ips;
+ }
+
+ new=ap_pcalloc(cmd->pool, sizeof(listen_rec));
+ new->local_addr.sin_family = AF_INET;
+ if (ports == ips) { /* no address */
+ new->local_addr.sin_addr.s_addr = htonl(INADDR_ANY);
+ }
+ else {
+ new->local_addr.sin_addr.s_addr = ap_get_virthost_addr(ips, NULL);
+ }
+ port = atoi(ports);
+ if (!port) {
+ return "Port must be numeric";
+ }
+ new->local_addr.sin_port = htons(port);
+ new->fd = -1;
+ new->used = 0;
+ new->next = ap_listeners;
+ ap_listeners = new;
+ return NULL;
+}
+
+static const char *set_listenbacklog(cmd_parms *cmd, void *dummy, char *arg)
+{
+ int b;
+
+ const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+ if (err != NULL) {
+ return err;
+ }
+
+ b = atoi(arg);
+ if (b < 1) {
+ return "ListenBacklog must be > 0";
+ }
+ ap_listenbacklog = b;
+ return NULL;
+}
+
+static const char *set_coredumpdir (cmd_parms *cmd, void *dummy, char *arg)
+{
+ struct stat finfo;
+ const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+ if (err != NULL) {
+ return err;
+ }
+
+ arg = ap_server_root_relative(cmd->pool, arg);
+ if ((stat(arg, &finfo) == -1) || !S_ISDIR(finfo.st_mode)) {
+ return ap_pstrcat(cmd->pool, "CoreDumpDirectory ", arg,
+ " does not exist or is not a directory", NULL);
+ }
+ ap_cpystrn(ap_coredump_dir, arg, sizeof(ap_coredump_dir));
+ return NULL;
+}
+
+static const char *include_config (cmd_parms *cmd, void *dummy, char *name)
+{
+ name = ap_server_root_relative(cmd->pool, name);
+
+ ap_process_resource_config(cmd->server, name, cmd->pool, cmd->temp_pool);
+
+ return NULL;
+}
+
+static const char *set_loglevel(cmd_parms *cmd, void *dummy, const char *arg)
+{
+ char *str;
+
+ const char *err = ap_check_cmd_context(cmd,
+ NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT);
+ if (err != NULL) {
+ return err;
+ }
+
+ if ((str = ap_getword_conf(cmd->pool, &arg))) {
+ if (!strcasecmp(str, "emerg")) {
+ cmd->server->loglevel = APLOG_EMERG;
+ }
+ else if (!strcasecmp(str, "alert")) {
+ cmd->server->loglevel = APLOG_ALERT;
+ }
+ else if (!strcasecmp(str, "crit")) {
+ cmd->server->loglevel = APLOG_CRIT;
+ }
+ else if (!strcasecmp(str, "error")) {
+ cmd->server->loglevel = APLOG_ERR;
+ }
+ else if (!strcasecmp(str, "warn")) {
+ cmd->server->loglevel = APLOG_WARNING;
+ }
+ else if (!strcasecmp(str, "notice")) {
+ cmd->server->loglevel = APLOG_NOTICE;
+ }
+ else if (!strcasecmp(str, "info")) {
+ cmd->server->loglevel = APLOG_INFO;
+ }
+ else if (!strcasecmp(str, "debug")) {
+ cmd->server->loglevel = APLOG_DEBUG;
+ }
+ else {
+ return "LogLevel requires level keyword: one of "
+ "emerg/alert/crit/error/warn/notice/info/debug";
+ }
+ }
+ else {
+ return "LogLevel requires level keyword";
+ }
+
+ return NULL;
+}
+
+API_EXPORT(const char *) ap_psignature(const char *prefix, request_rec *r)
+{
+ char sport[20];
+ core_dir_config *conf;
+
+ conf = (core_dir_config *)ap_get_module_config(r->per_dir_config,
+ &core_module);
+ if ((conf->server_signature == srv_sig_off)
+ || (conf->server_signature == srv_sig_unset)) {
+ return "";
+ }
+
+ ap_snprintf(sport, sizeof sport, "%u", (unsigned) ap_get_server_port(r));
+
+ if (conf->server_signature == srv_sig_withmail) {
+ return ap_pstrcat(r->pool, prefix, "<ADDRESS>" SERVER_BASEVERSION
+ " Server at <A HREF=\"mailto:",
+ r->server->server_admin, "\">",
+ ap_get_server_name(r), "</A> Port ", sport,
+ "</ADDRESS>\n", NULL);
+ }
+ return ap_pstrcat(r->pool, prefix, "<ADDRESS>" SERVER_BASEVERSION
+ " Server at ", ap_get_server_name(r), " Port ", sport,
+ "</ADDRESS>\n", NULL);
+}
+
+/*
+ * Load an authorisation realm into our location configuration, applying the
+ * usual rules that apply to realms.
+ */
+static const char *set_authname(cmd_parms *cmd, void *mconfig, char *word1)
+{
+ core_dir_config *aconfig = (core_dir_config *)mconfig;
+
+ aconfig->ap_auth_name = ap_escape_quotes(cmd->pool, word1);
+ return NULL;
+}
+
+#ifdef _OSD_POSIX /* BS2000 Logon Passwd file */
+static const char *set_bs2000_account(cmd_parms *cmd, void *dummy, char *name)
+{
+ const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+ if (err != NULL) {
+ return err;
+ }
+
+ return os_set_account(cmd->pool, name);
+}
+#endif /*_OSD_POSIX*/
+
+/*
+ * Handle a request to include the server's OS platform in the Server
+ * response header field (the ServerTokens directive). Unfortunately
+ * this requires a new global in order to communicate the setting back to
+ * http_main so it can insert the information in the right place in the
+ * string.
+ */
+
+static const char *set_serv_tokens(cmd_parms *cmd, void *dummy, char *arg)
+{
+ const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+ if (err != NULL) {
+ return err;
+ }
+
+ if (!strcasecmp(arg, "OS")) {
+ ap_server_tokens = SrvTk_OS;
+ }
+ else if (!strcasecmp(arg, "Min") || !strcasecmp(arg, "Minimal")) {
+ ap_server_tokens = SrvTk_MIN;
+ }
+ else {
+ ap_server_tokens = SrvTk_FULL;
+ }
+ return NULL;
+}
+
+static const char *set_limit_req_line(cmd_parms *cmd, void *dummy, char *arg)
+{
+ const char *err = ap_check_cmd_context(cmd,
+ NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT);
+ int lim;
+
+ if (err != NULL) {
+ return err;
+ }
+ lim = atoi(arg);
+ if (lim < 0) {
+ return ap_pstrcat(cmd->temp_pool, "LimitRequestLine \"", arg,
+ "\" must be a non-negative integer", NULL);
+ }
+ if (lim > DEFAULT_LIMIT_REQUEST_LINE) {
+ return ap_psprintf(cmd->temp_pool, "LimitRequestLine \"%s\" "
+ "must not exceed the precompiled maximum of %d",
+ arg, DEFAULT_LIMIT_REQUEST_LINE);
+ }
+ cmd->server->limit_req_line = lim;
+ return NULL;
+}
+
+static const char *set_limit_req_fieldsize(cmd_parms *cmd, void *dummy,
+ char *arg)
+{
+ const char *err = ap_check_cmd_context(cmd,
+ NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT);
+ int lim;
+
+ if (err != NULL) {
+ return err;
+ }
+ lim = atoi(arg);
+ if (lim < 0) {
+ return ap_pstrcat(cmd->temp_pool, "LimitRequestFieldsize \"", arg,
+ "\" must be a non-negative integer (0 = no limit)",
+ NULL);
+ }
+ if (lim > DEFAULT_LIMIT_REQUEST_FIELDSIZE) {
+ return ap_psprintf(cmd->temp_pool, "LimitRequestFieldsize \"%s\" "
+ "must not exceed the precompiled maximum of %d",
+ arg, DEFAULT_LIMIT_REQUEST_FIELDSIZE);
+ }
+ cmd->server->limit_req_fieldsize = lim;
+ return NULL;
+}
+
+static const char *set_limit_req_fields(cmd_parms *cmd, void *dummy, char *arg)
+{
+ const char *err = ap_check_cmd_context(cmd,
+ NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT);
+ int lim;
+
+ if (err != NULL) {
+ return err;
+ }
+ lim = atoi(arg);
+ if (lim < 0) {
+ return ap_pstrcat(cmd->temp_pool, "LimitRequestFields \"", arg,
+ "\" must be a non-negative integer (0 = no limit)",
+ NULL);
+ }
+ cmd->server->limit_req_fields = lim;
+ return NULL;
+}
+
+static const char *set_limit_req_body(cmd_parms *cmd, core_dir_config *conf,
+ char *arg)
+{
+ const char *err = ap_check_cmd_context(cmd, NOT_IN_LIMIT);
+ if (err != NULL) {
+ return err;
+ }
+
+ /* WTF: If strtoul is not portable, then write a replacement.
+ * Instead we have an idiotic define in httpd.h that prevents
+ * it from being used even when it is available. Sheesh.
+ */
+ conf->limit_req_body = (unsigned long)strtol(arg, (char **)NULL, 10);
+ return NULL;
+}
+
+#ifdef WIN32
+static const char *set_interpreter_source(cmd_parms *cmd, core_dir_config *d,
+ char *arg)
+{
+ if (!strcasecmp(arg, "registry")) {
+ d->script_interpreter_source = INTERPRETER_SOURCE_REGISTRY;
+ } else if (!strcasecmp(arg, "script")) {
+ d->script_interpreter_source = INTERPRETER_SOURCE_SHEBANG;
+ } else {
+ d->script_interpreter_source = INTERPRETER_SOURCE_SHEBANG;
+ }
+ return NULL;
+}
+#endif
+
+/* Note --- ErrorDocument will now work from .htaccess files.
+ * The AllowOverride of Fileinfo allows webmasters to turn it off
+ */
+
+static const command_rec core_cmds[] = {
+
+/* Old access config file commands */
+
+{ "<Directory", dirsection, NULL, RSRC_CONF, RAW_ARGS,
+ "Container for directives affecting resources located in the specified "
+ "directories" },
+{ end_directory_section, end_nested_section, NULL, ACCESS_CONF, NO_ARGS,
+ "Marks end of <Directory>" },
+{ "<Location", urlsection, NULL, RSRC_CONF, RAW_ARGS,
+ "Container for directives affecting resources accessed through the "
+ "specified URL paths" },
+{ end_location_section, end_nested_section, NULL, ACCESS_CONF, NO_ARGS,
+ "Marks end of <Location>" },
+{ "<VirtualHost", virtualhost_section, NULL, RSRC_CONF, RAW_ARGS,
+ "Container to map directives to a particular virtual host, takes one or "
+ "more host addresses" },
+{ end_virtualhost_section, end_nested_section, NULL, RSRC_CONF, NO_ARGS,
+ "Marks end of <VirtualHost>" },
+{ "<Files", filesection, NULL, OR_ALL, RAW_ARGS, "Container for directives "
+ "affecting files matching specified patterns" },
+{ end_files_section, end_nested_section, NULL, OR_ALL, NO_ARGS,
+ "Marks end of <Files>" },
+{ "<Limit", ap_limit_section, NULL, OR_ALL, RAW_ARGS, "Container for "
+ "authentication directives when accessed using specified HTTP methods" },
+{ "</Limit>", endlimit_section, NULL, OR_ALL, NO_ARGS,
+ "Marks end of <Limit>" },
+{ "<LimitExcept", ap_limit_section, (void*)1, OR_ALL, RAW_ARGS,
+ "Container for authentication directives to be applied when any HTTP "
+ "method other than those specified is used to access the resource" },
+{ "</LimitExcept>", endlimit_section, (void*)1, OR_ALL, NO_ARGS,
+ "Marks end of <LimitExcept>" },
+{ "<IfModule", start_ifmod, NULL, OR_ALL, TAKE1,
+ "Container for directives based on existance of specified modules" },
+{ end_ifmodule_section, end_ifmod, NULL, OR_ALL, NO_ARGS,
+ "Marks end of <IfModule>" },
+{ "<IfDefine", start_ifdefine, NULL, OR_ALL, TAKE1,
+ "Container for directives based on existance of command line defines" },
+{ end_ifdefine_section, end_ifdefine, NULL, OR_ALL, NO_ARGS,
+ "Marks end of <IfDefine>" },
+{ "<DirectoryMatch", dirsection, (void*)1, RSRC_CONF, RAW_ARGS,
+ "Container for directives affecting resources located in the "
+ "specified directories" },
+{ end_directorymatch_section, end_nested_section, NULL, ACCESS_CONF, NO_ARGS,
+ "Marks end of <DirectoryMatch>" },
+{ "<LocationMatch", urlsection, (void*)1, RSRC_CONF, RAW_ARGS,
+ "Container for directives affecting resources accessed through the "
+ "specified URL paths" },
+{ end_locationmatch_section, end_nested_section, NULL, ACCESS_CONF, NO_ARGS,
+ "Marks end of <LocationMatch>" },
+{ "<FilesMatch", filesection, (void*)1, OR_ALL, RAW_ARGS,
+ "Container for directives affecting files matching specified patterns" },
+{ end_filesmatch_section, end_nested_section, NULL, OR_ALL, NO_ARGS,
+ "Marks end of <FilesMatch>" },
+{ "AuthType", ap_set_string_slot,
+ (void*)XtOffsetOf(core_dir_config, ap_auth_type), OR_AUTHCFG, TAKE1,
+ "An HTTP authorization type (e.g., \"Basic\")" },
+{ "AuthName", set_authname, NULL, OR_AUTHCFG, TAKE1,
+ "The authentication realm (e.g. \"Members Only\")" },
+{ "Require", require, NULL, OR_AUTHCFG, RAW_ARGS,
+ "Selects which authenticated users or groups may access a protected space" },
+{ "Satisfy", satisfy, NULL, OR_AUTHCFG, TAKE1,
+ "access policy if both allow and require used ('all' or 'any')" },
+#ifdef GPROF
+{ "GprofDir", set_gprof_dir, NULL, RSRC_CONF, TAKE1,
+ "Directory to plop gmon.out files" },
+#endif
+
+/* Old resource config file commands */
+
+{ "AccessFileName", set_access_name, NULL, RSRC_CONF, RAW_ARGS,
+ "Name(s) of per-directory config files (default: .htaccess)" },
+{ "DocumentRoot", set_document_root, NULL, RSRC_CONF, TAKE1,
+ "Root directory of the document tree" },
+{ "ErrorDocument", set_error_document, NULL, OR_FILEINFO, RAW_ARGS,
+ "Change responses for HTTP errors" },
+{ "AllowOverride", set_override, NULL, ACCESS_CONF, RAW_ARGS,
+ "Controls what groups of directives can be configured by per-directory "
+ "config files" },
+{ "Options", set_options, NULL, OR_OPTIONS, RAW_ARGS,
+ "Set a number of attributes for a given directory" },
+{ "DefaultType", ap_set_string_slot,
+ (void*)XtOffsetOf (core_dir_config, ap_default_type),
+ OR_FILEINFO, TAKE1, "the default MIME type for untypable files" },
+
+/* Old server config file commands */
+
+{ "ServerType", server_type, NULL, RSRC_CONF, TAKE1,
+ "'inetd' or 'standalone'"},
+{ "Port", server_port, NULL, RSRC_CONF, TAKE1, "A TCP port number"},
+{ "HostnameLookups", set_hostname_lookups, NULL, ACCESS_CONF|RSRC_CONF, TAKE1,
+ "\"on\" to enable, \"off\" to disable reverse DNS lookups, or \"double\" to "
+ "enable double-reverse DNS lookups" },
+{ "User", set_user, NULL, RSRC_CONF, TAKE1,
+ "Effective user id for this server"},
+{ "Group", set_group, NULL, RSRC_CONF, TAKE1,
+ "Effective group id for this server"},
+{ "ServerAdmin", set_server_string_slot,
+ (void *)XtOffsetOf (server_rec, server_admin), RSRC_CONF, TAKE1,
+ "The email address of the server administrator" },
+{ "ServerName", set_server_string_slot,
+ (void *)XtOffsetOf (server_rec, server_hostname), RSRC_CONF, TAKE1,
+ "The hostname of the server" },
+{ "ServerSignature", set_signature_flag, NULL, OR_ALL, TAKE1,
+ "En-/disable server signature (on|off|email)" },
+{ "ServerRoot", set_server_root, NULL, RSRC_CONF, TAKE1,
+ "Common directory of server-related files (logs, confs, etc.)" },
+{ "ErrorLog", set_server_string_slot,
+ (void *)XtOffsetOf (server_rec, error_fname), RSRC_CONF, TAKE1,
+ "The filename of the error log" },
+{ "PidFile", set_pidfile, NULL, RSRC_CONF, TAKE1,
+ "A file for logging the server process ID"},
+{ "ScoreBoardFile", set_scoreboard, NULL, RSRC_CONF, TAKE1,
+ "A file for Apache to maintain runtime process management information"},
+{ "LockFile", set_lockfile, NULL, RSRC_CONF, TAKE1,
+ "The lockfile used when Apache needs to lock the accept() call"},
+{ "AccessConfig", set_server_string_slot,
+ (void *)XtOffsetOf (server_rec, access_confname), RSRC_CONF, TAKE1,
+ "The filename of the access config file" },
+{ "ResourceConfig", set_server_string_slot,
+ (void *)XtOffsetOf (server_rec, srm_confname), RSRC_CONF, TAKE1,
+ "The filename of the resource config file" },
+{ "ServerAlias", set_server_alias, NULL, RSRC_CONF, RAW_ARGS,
+ "A name or names alternately used to access the server" },
+{ "ServerPath", set_serverpath, NULL, RSRC_CONF, TAKE1,
+ "The pathname the server can be reached at" },
+{ "Timeout", set_timeout, NULL, RSRC_CONF, TAKE1, "Timeout duration (sec)" },
+{ "KeepAliveTimeout", set_keep_alive_timeout, NULL, RSRC_CONF, TAKE1,
+ "Keep-Alive timeout duration (sec)"},
+{ "MaxKeepAliveRequests", set_keep_alive_max, NULL, RSRC_CONF, TAKE1,
+ "Maximum number of Keep-Alive requests per connection, or 0 for infinite" },
+{ "KeepAlive", set_keep_alive, NULL, RSRC_CONF, TAKE1,
+ "Whether persistent connections should be On or Off" },
+{ "IdentityCheck", set_idcheck, NULL, RSRC_CONF|ACCESS_CONF, FLAG,
+ "Enable identd (RFC 1413) user lookups - SLOW" },
+{ "ContentDigest", set_content_md5, NULL, OR_OPTIONS,
+ FLAG, "whether or not to send a Content-MD5 header with each request" },
+{ "UseCanonicalName", set_use_canonical_name, NULL,
+ RSRC_CONF, TAKE1,
+ "How to work out the ServerName : Port when constructing URLs" },
+{ "StartServers", set_daemons_to_start, NULL, RSRC_CONF, TAKE1,
+ "Number of child processes launched at server startup" },
+{ "MinSpareServers", set_min_free_servers, NULL, RSRC_CONF, TAKE1,
+ "Minimum number of idle children, to handle request spikes" },
+{ "MaxSpareServers", set_max_free_servers, NULL, RSRC_CONF, TAKE1,
+ "Maximum number of idle children" },
+{ "MaxServers", set_max_free_servers, NULL, RSRC_CONF, TAKE1,
+ "Deprecated equivalent to MaxSpareServers" },
+{ "ServersSafetyLimit", set_server_limit, NULL, RSRC_CONF, TAKE1,
+ "Deprecated equivalent to MaxClients" },
+{ "MaxClients", set_server_limit, NULL, RSRC_CONF, TAKE1,
+ "Maximum number of children alive at the same time" },
+{ "MaxRequestsPerChild", set_max_requests, NULL, RSRC_CONF, TAKE1,
+ "Maximum number of requests a particular child serves before dying." },
+{ "RLimitCPU",
+#ifdef RLIMIT_CPU
+ set_limit_cpu, (void*)XtOffsetOf(core_dir_config, limit_cpu),
+#else
+ no_set_limit, NULL,
+#endif
+ OR_ALL, TAKE12, "Soft/hard limits for max CPU usage in seconds" },
+{ "RLimitMEM",
+#if defined (RLIMIT_DATA) || defined (RLIMIT_VMEM) || defined (RLIMIT_AS)
+ set_limit_mem, (void*)XtOffsetOf(core_dir_config, limit_mem),
+#else
+ no_set_limit, NULL,
+#endif
+ OR_ALL, TAKE12, "Soft/hard limits for max memory usage per process" },
+{ "RLimitNPROC",
+#ifdef RLIMIT_NPROC
+ set_limit_nproc, (void*)XtOffsetOf(core_dir_config, limit_nproc),
+#else
+ no_set_limit, NULL,
+#endif
+ OR_ALL, TAKE12, "soft/hard limits for max number of processes per uid" },
+{ "BindAddress", set_bind_address, NULL, RSRC_CONF, TAKE1,
+ "'*', a numeric IP address, or the name of a host with a unique IP address"},
+{ "Listen", set_listener, NULL, RSRC_CONF, TAKE1,
+ "A port number or a numeric IP address and a port number"},
+{ "SendBufferSize", set_send_buffer_size, NULL, RSRC_CONF, TAKE1,
+ "Send buffer size in bytes"},
+{ "AddModule", add_module_command, NULL, RSRC_CONF, ITERATE,
+ "The name of a module" },
+{ "ClearModuleList", clear_module_list_command, NULL, RSRC_CONF, NO_ARGS,
+ NULL },
+{ "ThreadsPerChild", set_threads, NULL, RSRC_CONF, TAKE1,
+ "Number of threads a child creates" },
+{ "ExcessRequestsPerChild", set_excess_requests, NULL, RSRC_CONF, TAKE1,
+ "Maximum number of requests a particular child serves after it is ready "
+ "to die." },
+{ "ListenBacklog", set_listenbacklog, NULL, RSRC_CONF, TAKE1,
+ "Maximum length of the queue of pending connections, as used by listen(2)" },
+{ "CoreDumpDirectory", set_coredumpdir, NULL, RSRC_CONF, TAKE1,
+ "The location of the directory Apache changes to before dumping core" },
+{ "Include", include_config, NULL, (RSRC_CONF | ACCESS_CONF), TAKE1,
+ "Name of the config file to be included" },
+{ "LogLevel", set_loglevel, NULL, RSRC_CONF, TAKE1,
+ "Level of verbosity in error logging" },
+{ "NameVirtualHost", ap_set_name_virtual_host, NULL, RSRC_CONF, TAKE1,
+ "A numeric IP address:port, or the name of a host" },
+#ifdef _OSD_POSIX
+{ "BS2000Account", set_bs2000_account, NULL, RSRC_CONF, TAKE1,
+ "Name of server User's bs2000 logon account name" },
+#endif
+#ifdef WIN32
+{ "ScriptInterpreterSource", set_interpreter_source, NULL, OR_FILEINFO, TAKE1,
+ "Where to find interpreter to run Win32 scripts (Registry or script shebang line)" },
+#endif
+{ "ServerTokens", set_serv_tokens, NULL, RSRC_CONF, TAKE1,
+ "Determine tokens displayed in the Server: header - Min(imal), OS or Full" },
+{ "LimitRequestLine", set_limit_req_line, NULL, RSRC_CONF, TAKE1,
+ "Limit on maximum size of an HTTP request line"},
+{ "LimitRequestFieldsize", set_limit_req_fieldsize, NULL, RSRC_CONF, TAKE1,
+ "Limit on maximum size of an HTTP request header field"},
+{ "LimitRequestFields", set_limit_req_fields, NULL, RSRC_CONF, TAKE1,
+ "Limit (0 = unlimited) on max number of header fields in a request message"},
+{ "LimitRequestBody", set_limit_req_body,
+ (void*)XtOffsetOf(core_dir_config, limit_req_body),
+ OR_ALL, TAKE1,
+ "Limit (in bytes) on maximum size of request message body" },
+{ NULL }
+};
+
+/*****************************************************************
+ *
+ * Core handlers for various phases of server operation...
+ */
+
+static int core_translate(request_rec *r)
+{
+ void *sconf = r->server->module_config;
+ core_server_config *conf = ap_get_module_config(sconf, &core_module);
+
+ if (r->proxyreq) {
+ return HTTP_FORBIDDEN;
+ }
+ if ((r->uri[0] != '/') && strcmp(r->uri, "*")) {
+ ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
+ "Invalid URI in request %s", r->the_request);
+ return BAD_REQUEST;
+ }
+
+ if (r->server->path
+ && !strncmp(r->uri, r->server->path, r->server->pathlen)
+ && (r->server->path[r->server->pathlen - 1] == '/'
+ || r->uri[r->server->pathlen] == '/'
+ || r->uri[r->server->pathlen] == '\0')) {
+ r->filename = ap_pstrcat(r->pool, conf->ap_document_root,
+ (r->uri + r->server->pathlen), NULL);
+ }
+ else {
+ /*
+ * Make sure that we do not mess up the translation by adding two
+ * /'s in a row. This happens under windows when the document
+ * root ends with a /
+ */
+ if ((conf->ap_document_root[strlen(conf->ap_document_root)-1] == '/')
+ && (*(r->uri) == '/')) {
+ r->filename = ap_pstrcat(r->pool, conf->ap_document_root, r->uri+1,
+ NULL);
+ }
+ else {
+ r->filename = ap_pstrcat(r->pool, conf->ap_document_root, r->uri,
+ NULL);
+ }
+ }
+
+ return OK;
+}
+
+static int do_nothing(request_rec *r) { return OK; }
+
+#ifdef USE_MMAP_FILES
+struct mmap_rec {
+ void *mm;
+ size_t length;
+};
+
+static void mmap_cleanup(void *mmv)
+{
+ struct mmap_rec *mmd = mmv;
+
+ if (munmap(mmd->mm, mmd->length) == -1) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, NULL,
+ "Failed to munmap memory of length %ld at 0x%lx",
+ (long) mmd->length, (long) mmd->mm);
+ }
+}
+#endif
+
+/*
+ * Default handler for MIME types without other handlers. Only GET
+ * and OPTIONS at this point... anyone who wants to write a generic
+ * handler for PUT or POST is free to do so, but it seems unwise to provide
+ * any defaults yet... So, for now, we assume that this will always be
+ * the last handler called and return 405 or 501.
+ */
+
+static int default_handler(request_rec *r)
+{
+ core_dir_config *d =
+ (core_dir_config *)ap_get_module_config(r->per_dir_config, &core_module);
+ int rangestatus, errstatus;
+ FILE *f;
+#ifdef USE_MMAP_FILES
+ caddr_t mm;
+#endif
+#ifdef CHARSET_EBCDIC
+ int convert_flag;
+#endif
+
+ /* This handler has no use for a request body (yet), but we still
+ * need to read and discard it if the client sent one.
+ */
+ if ((errstatus = ap_discard_request_body(r)) != OK) {
+ return errstatus;
+ }
+
+ r->allowed |= (1 << M_GET) | (1 << M_OPTIONS);
+
+ if (r->method_number == M_INVALID) {
+ ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
+ "Invalid method in request %s", r->the_request);
+ return NOT_IMPLEMENTED;
+ }
+ if (r->method_number == M_OPTIONS) {
+ return ap_send_http_options(r);
+ }
+ if (r->method_number == M_PUT) {
+ return METHOD_NOT_ALLOWED;
+ }
+
+ if (r->finfo.st_mode == 0 || (r->path_info && *r->path_info)) {
+ char *emsg;
+
+ emsg = "File does not exist: ";
+ if (r->path_info == NULL) {
+ emsg = ap_pstrcat(r->pool, emsg, r->filename, NULL);
+ }
+ else {
+ emsg = ap_pstrcat(r->pool, emsg, r->filename, r->path_info, NULL);
+ }
+ ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, r, "%s", emsg);
+ return HTTP_NOT_FOUND;
+ }
+ if (r->method_number != M_GET) {
+ return METHOD_NOT_ALLOWED;
+ }
+
+#if defined(OS2) || defined(WIN32)
+ /* Need binary mode for OS/2 */
+ f = ap_pfopen(r->pool, r->filename, "rb");
+#else
+ f = ap_pfopen(r->pool, r->filename, "r");
+#endif
+
+ if (f == NULL) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
+ "file permissions deny server access: %s", r->filename);
+ return FORBIDDEN;
+ }
+
+ ap_update_mtime(r, r->finfo.st_mtime);
+ ap_set_last_modified(r);
+ ap_set_etag(r);
+ ap_table_setn(r->headers_out, "Accept-Ranges", "bytes");
+ if (((errstatus = ap_meets_conditions(r)) != OK)
+ || (errstatus = ap_set_content_length(r, r->finfo.st_size))) {
+ return errstatus;
+ }
+
+#ifdef USE_MMAP_FILES
+ ap_block_alarms();
+ if ((r->finfo.st_size >= MMAP_THRESHOLD)
+ && (r->finfo.st_size < MMAP_LIMIT)
+ && (!r->header_only || (d->content_md5 & 1))) {
+ /* we need to protect ourselves in case we die while we've got the
+ * file mmapped */
+ mm = mmap(NULL, r->finfo.st_size, PROT_READ, MAP_PRIVATE,
+ fileno(f), 0);
+ if (mm == (caddr_t)-1) {
+ ap_log_rerror(APLOG_MARK, APLOG_CRIT, r,
+ "default_handler: mmap failed: %s", r->filename);
+ }
+ }
+ else {
+ mm = (caddr_t)-1;
+ }
+
+ if (mm == (caddr_t)-1) {
+ ap_unblock_alarms();
+#endif
+
+#ifdef CHARSET_EBCDIC
+ /* To make serving of "raw ASCII text" files easy (they serve faster
+ * since they don't have to be converted from EBCDIC), a new
+ * "magic" type prefix was invented: text/x-ascii-{plain,html,...}
+ * If we detect one of these content types here, we simply correct
+ * the type to the real text/{plain,html,...} type. Otherwise, we
+ * set a flag that translation is required later on.
+ */
+ convert_flag = ap_checkconv(r);
+ if (d->content_md5 & 1) {
+ ap_table_setn(r->headers_out, "Content-MD5",
+ ap_md5digest(r->pool, f, convert_flag));
+ }
+#else
+ if (d->content_md5 & 1) {
+ ap_table_setn(r->headers_out, "Content-MD5",
+ ap_md5digest(r->pool, f));
+ }
+#endif /* CHARSET_EBCDIC */
+
+ rangestatus = ap_set_byterange(r);
+
+ ap_send_http_header(r);
+
+ if (!r->header_only) {
+ if (!rangestatus) {
+ ap_send_fd(f, r);
+ }
+ else {
+ long offset, length;
+ while (ap_each_byterange(r, &offset, &length)) {
+ /*
+ * Non zero returns are more portable than checking
+ * for a return of -1.
+ */
+ if (fseek(f, offset, SEEK_SET)) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, r->server,
+ "Failed to fseek for byterange (%ld, %ld)",
+ offset, length);
+ }
+ else {
+ ap_send_fd_length(f, r, length);
+ }
+ }
+ }
+ }
+
+#ifdef USE_MMAP_FILES
+ }
+ else {
+ struct mmap_rec *mmd;
+
+ mmd = ap_palloc(r->pool, sizeof(*mmd));
+ mmd->mm = mm;
+ mmd->length = r->finfo.st_size;
+ ap_register_cleanup(r->pool, (void *)mmd, mmap_cleanup, mmap_cleanup);
+ ap_unblock_alarms();
+
+ if (d->content_md5 & 1) {
+ AP_MD5_CTX context;
+
+ ap_MD5Init(&context);
+ ap_MD5Update(&context, (void *)mm, (unsigned int)r->finfo.st_size);
+ ap_table_setn(r->headers_out, "Content-MD5",
+ ap_md5contextTo64(r->pool, &context));
+ }
+
+ rangestatus = ap_set_byterange(r);
+ ap_send_http_header(r);
+
+ if (!r->header_only) {
+ if (!rangestatus) {
+ ap_send_mmap(mm, r, 0, r->finfo.st_size);
+ }
+ else {
+ long offset, length;
+ while (ap_each_byterange(r, &offset, &length)) {
+ ap_send_mmap(mm, r, offset, length);
+ }
+ }
+ }
+ }
+#endif
+
+ ap_pfclose(r->pool, f);
+ return OK;
+}
+
+static const handler_rec core_handlers[] = {
+{ "*/*", default_handler },
+{ "default-handler", default_handler },
+{ NULL, NULL }
+};
+
+API_VAR_EXPORT module core_module = {
+ STANDARD_MODULE_STUFF,
+ NULL, /* initializer */
+ create_core_dir_config, /* create per-directory config structure */
+ merge_core_dir_configs, /* merge per-directory config structures */
+ create_core_server_config, /* create per-server config structure */
+ merge_core_server_configs, /* merge per-server config structures */
+ core_cmds, /* command table */
+ core_handlers, /* handlers */
+ core_translate, /* translate_handler */
+ NULL, /* check_user_id */
+ NULL, /* check auth */
+ do_nothing, /* check access */
+ do_nothing, /* type_checker */
+ NULL, /* pre-run fixups */
+ NULL, /* logger */
+ NULL, /* header parser */
+ NULL, /* child_init */
+ NULL, /* child_exit */
+ NULL /* post_read_request */
+};
--- /dev/null
+/* ====================================================================
+ * Copyright (c) 1995-1999 The Apache Group. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * 4. The names "Apache Server" and "Apache Group" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission. For written permission, please contact
+ * apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ * nor may "Apache" appear in their names without prior written
+ * permission of the Apache Group.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Group and was originally based
+ * on public domain software written at the National Center for
+ * Supercomputing Applications, University of Illinois, Urbana-Champaign.
+ * For more information on the Apache Group and the Apache HTTP server
+ * project, please see <http://www.apache.org/>.
+ *
+ */
+
+/*
+ * http_protocol.c --- routines which directly communicate with the client.
+ *
+ * Code originally by Rob McCool; much redone by Robert S. Thau
+ * and the Apache Group.
+ */
+
+#define CORE_PRIVATE
+#include "httpd.h"
+#include "http_config.h"
+#include "http_core.h"
+#include "http_protocol.h"
+#include "http_main.h"
+#include "http_request.h"
+#include "http_vhost.h"
+#include "http_log.h" /* For errors detected in basic auth common
+ * support code... */
+#include "util_date.h" /* For parseHTTPdate and BAD_DATE */
+#include <stdarg.h>
+#include "http_conf_globals.h"
+
+#define SET_BYTES_SENT(r) \
+ do { if (r->sent_bodyct) \
+ ap_bgetopt (r->connection->client, BO_BYTECT, &r->bytes_sent); \
+ } while (0)
+
+
+static int parse_byterange(char *range, long clength, long *start, long *end)
+{
+ char *dash = strchr(range, '-');
+
+ if (!dash)
+ return 0;
+
+ if ((dash == range)) {
+ /* In the form "-5" */
+ *start = clength - atol(dash + 1);
+ *end = clength - 1;
+ }
+ else {
+ *dash = '\0';
+ dash++;
+ *start = atol(range);
+ if (*dash)
+ *end = atol(dash);
+ else /* "5-" */
+ *end = clength - 1;
+ }
+
+ if (*start < 0)
+ *start = 0;
+
+ if (*end >= clength)
+ *end = clength - 1;
+
+ if (*start > *end)
+ return 0;
+
+ return (*start > 0 || *end < clength - 1);
+}
+
+static int internal_byterange(int, long *, request_rec *, const char **, long *,
+ long *);
+
+API_EXPORT(int) ap_set_byterange(request_rec *r)
+{
+ const char *range, *if_range, *match;
+ long range_start, range_end;
+
+ if (!r->clength || r->assbackwards)
+ return 0;
+
+ /* Check for Range request-header (HTTP/1.1) or Request-Range for
+ * backwards-compatibility with second-draft Luotonen/Franks
+ * byte-ranges (e.g. Netscape Navigator 2-3).
+ *
+ * We support this form, with Request-Range, and (farther down) we
+ * send multipart/x-byteranges instead of multipart/byteranges for
+ * Request-Range based requests to work around a bug in Netscape
+ * Navigator 2-3 and MSIE 3.
+ */
+
+ if (!(range = ap_table_get(r->headers_in, "Range")))
+ range = ap_table_get(r->headers_in, "Request-Range");
+
+ if (!range || strncasecmp(range, "bytes=", 6)) {
+ return 0;
+ }
+
+ /* Check the If-Range header for Etag or Date.
+ * Note that this check will return false (as required) if either
+ * of the two etags are weak.
+ */
+ if ((if_range = ap_table_get(r->headers_in, "If-Range"))) {
+ if (if_range[0] == '"') {
+ if (!(match = ap_table_get(r->headers_out, "Etag")) ||
+ (strcmp(if_range, match) != 0))
+ return 0;
+ }
+ else if (!(match = ap_table_get(r->headers_out, "Last-Modified")) ||
+ (strcmp(if_range, match) != 0))
+ return 0;
+ }
+
+ if (!strchr(range, ',')) {
+ /* A single range */
+ if (!parse_byterange(ap_pstrdup(r->pool, range + 6), r->clength,
+ &range_start, &range_end))
+ return 0;
+
+ r->byterange = 1;
+
+ ap_table_setn(r->headers_out, "Content-Range",
+ ap_psprintf(r->pool, "bytes %ld-%ld/%ld",
+ range_start, range_end, r->clength));
+ ap_table_setn(r->headers_out, "Content-Length",
+ ap_psprintf(r->pool, "%ld", range_end - range_start + 1));
+ }
+ else {
+ /* a multiple range */
+ const char *r_range = ap_pstrdup(r->pool, range + 6);
+ long tlength = 0;
+
+ r->byterange = 2;
+ r->boundary = ap_psprintf(r->pool, "%lx%lx",
+ r->request_time, (long) getpid());
+ while (internal_byterange(0, &tlength, r, &r_range, NULL, NULL));
+ ap_table_setn(r->headers_out, "Content-Length",
+ ap_psprintf(r->pool, "%ld", tlength));
+ }
+
+ r->status = PARTIAL_CONTENT;
+ r->range = range + 6;
+
+ return 1;
+}
+
+API_EXPORT(int) ap_each_byterange(request_rec *r, long *offset, long *length)
+{
+ return internal_byterange(1, NULL, r, &r->range, offset, length);
+}
+
+/* If this function is called with realreq=1, it will spit out
+ * the correct headers for a byterange chunk, and set offset and
+ * length to the positions they should be.
+ *
+ * If it is called with realreq=0, it will add to tlength the length
+ * it *would* have used with realreq=1.
+ *
+ * Either case will return 1 if it should be called again, and 0
+ * when done.
+ */
+static int internal_byterange(int realreq, long *tlength, request_rec *r,
+ const char **r_range, long *offset, long *length)
+{
+ long range_start, range_end;
+ char *range;
+
+ if (!**r_range) {
+ if (r->byterange > 1) {
+ if (realreq)
+ ap_rvputs(r, "\015\012--", r->boundary, "--\015\012", NULL);
+ else
+ *tlength += 4 + strlen(r->boundary) + 4;
+ }
+ return 0;
+ }
+
+ range = ap_getword(r->pool, r_range, ',');
+ if (!parse_byterange(range, r->clength, &range_start, &range_end))
+ /* Skip this one */
+ return internal_byterange(realreq, tlength, r, r_range, offset,
+ length);
+
+ if (r->byterange > 1) {
+ const char *ct = r->content_type ? r->content_type : ap_default_type(r);
+ char ts[MAX_STRING_LEN];
+
+ ap_snprintf(ts, sizeof(ts), "%ld-%ld/%ld", range_start, range_end,
+ r->clength);
+ if (realreq)
+ ap_rvputs(r, "\015\012--", r->boundary, "\015\012Content-type: ",
+ ct, "\015\012Content-range: bytes ", ts, "\015\012\015\012",
+ NULL);
+ else
+ *tlength += 4 + strlen(r->boundary) + 16 + strlen(ct) + 23 +
+ strlen(ts) + 4;
+ }
+
+ if (realreq) {
+ *offset = range_start;
+ *length = range_end - range_start + 1;
+ }
+ else {
+ *tlength += range_end - range_start + 1;
+ }
+ return 1;
+}
+
+API_EXPORT(int) ap_set_content_length(request_rec *r, long clength)
+{
+ r->clength = clength;
+ ap_table_setn(r->headers_out, "Content-Length", ap_psprintf(r->pool, "%ld", clength));
+ return 0;
+}
+
+API_EXPORT(int) ap_set_keepalive(request_rec *r)
+{
+ int ka_sent = 0;
+ int wimpy = ap_find_token(r->pool,
+ ap_table_get(r->headers_out, "Connection"), "close");
+ const char *conn = ap_table_get(r->headers_in, "Connection");
+
+ /* The following convoluted conditional determines whether or not
+ * the current connection should remain persistent after this response
+ * (a.k.a. HTTP Keep-Alive) and whether or not the output message
+ * body should use the HTTP/1.1 chunked transfer-coding. In English,
+ *
+ * IF we have not marked this connection as errored;
+ * and the response body has a defined length due to the status code
+ * being 304 or 204, the request method being HEAD, already
+ * having defined Content-Length or Transfer-Encoding: chunked, or
+ * the request version being HTTP/1.1 and thus capable of being set
+ * as chunked [we know the (r->chunked = 1) side-effect is ugly];
+ * and the server configuration enables keep-alive;
+ * and the server configuration has a reasonable inter-request timeout;
+ * and there is no maximum # requests or the max hasn't been reached;
+ * and the response status does not require a close;
+ * and the response generator has not already indicated close;
+ * and the client did not request non-persistence (Connection: close);
+ * and we haven't been configured to ignore the buggy twit
+ * or they're a buggy twit coming through a HTTP/1.1 proxy
+ * and the client is requesting an HTTP/1.0-style keep-alive
+ * or the client claims to be HTTP/1.1 compliant (perhaps a proxy);
+ * THEN we can be persistent, which requires more headers be output.
+ *
+ * Note that the condition evaluation order is extremely important.
+ */
+ if ((r->connection->keepalive != -1) &&
+ ((r->status == HTTP_NOT_MODIFIED) ||
+ (r->status == HTTP_NO_CONTENT) ||
+ r->header_only ||
+ ap_table_get(r->headers_out, "Content-Length") ||
+ ap_find_last_token(r->pool,
+ ap_table_get(r->headers_out, "Transfer-Encoding"),
+ "chunked") ||
+ ((r->proto_num >= HTTP_VERSION(1,1)) &&
+ (r->chunked = 1))) && /* THIS CODE IS CORRECT, see comment above. */
+ r->server->keep_alive &&
+ (r->server->keep_alive_timeout > 0) &&
+ ((r->server->keep_alive_max == 0) ||
+ (r->server->keep_alive_max > r->connection->keepalives)) &&
+ !ap_status_drops_connection(r->status) &&
+ !wimpy &&
+ !ap_find_token(r->pool, conn, "close") &&
+ (!ap_table_get(r->subprocess_env, "nokeepalive") ||
+ ap_table_get(r->headers_in, "Via")) &&
+ ((ka_sent = ap_find_token(r->pool, conn, "keep-alive")) ||
+ (r->proto_num >= HTTP_VERSION(1,1)))
+ ) {
+ int left = r->server->keep_alive_max - r->connection->keepalives;
+
+ r->connection->keepalive = 1;
+ r->connection->keepalives++;
+
+ /* If they sent a Keep-Alive token, send one back */
+ if (ka_sent) {
+ if (r->server->keep_alive_max)
+ ap_table_setn(r->headers_out, "Keep-Alive",
+ ap_psprintf(r->pool, "timeout=%d, max=%d",
+ r->server->keep_alive_timeout, left));
+ else
+ ap_table_setn(r->headers_out, "Keep-Alive",
+ ap_psprintf(r->pool, "timeout=%d",
+ r->server->keep_alive_timeout));
+ ap_table_mergen(r->headers_out, "Connection", "Keep-Alive");
+ }
+
+ return 1;
+ }
+
+ /* Otherwise, we need to indicate that we will be closing this
+ * connection immediately after the current response.
+ *
+ * We only really need to send "close" to HTTP/1.1 clients, but we
+ * always send it anyway, because a broken proxy may identify itself
+ * as HTTP/1.0, but pass our request along with our HTTP/1.1 tag
+ * to a HTTP/1.1 client. Better safe than sorry.
+ */
+ if (!wimpy)
+ ap_table_mergen(r->headers_out, "Connection", "close");
+
+ r->connection->keepalive = 0;
+
+ return 0;
+}
+
+/*
+ * Return the latest rational time from a request/mtime (modification time)
+ * pair. We return the mtime unless it's in the future, in which case we
+ * return the current time. We use the request time as a reference in order
+ * to limit the number of calls to time(). We don't check for futurosity
+ * unless the mtime is at least as new as the reference.
+ */
+API_EXPORT(time_t) ap_rationalize_mtime(request_rec *r, time_t mtime)
+{
+ time_t now;
+
+ /* For all static responses, it's almost certain that the file was
+ * last modified before the beginning of the request. So there's
+ * no reason to call time(NULL) again. But if the response has been
+ * created on demand, then it might be newer than the time the request
+ * started. In this event we really have to call time(NULL) again
+ * so that we can give the clients the most accurate Last-Modified. If we
+ * were given a time in the future, we return the current time - the
+ * Last-Modified can't be in the future.
+ */
+ now = (mtime < r->request_time) ? r->request_time : time(NULL);
+ return (mtime > now) ? now : mtime;
+}
+
+API_EXPORT(int) ap_meets_conditions(request_rec *r)
+{
+ const char *etag = ap_table_get(r->headers_out, "ETag");
+ const char *if_match, *if_modified_since, *if_unmodified, *if_nonematch;
+ time_t mtime;
+
+ /* Check for conditional requests --- note that we only want to do
+ * this if we are successful so far and we are not processing a
+ * subrequest or an ErrorDocument.
+ *
+ * The order of the checks is important, since ETag checks are supposed
+ * to be more accurate than checks relative to the modification time.
+ * However, not all documents are guaranteed to *have* ETags, and some
+ * might have Last-Modified values w/o ETags, so this gets a little
+ * complicated.
+ */
+
+ if (!ap_is_HTTP_SUCCESS(r->status) || r->no_local_copy) {
+ return OK;
+ }
+
+ mtime = (r->mtime != 0) ? r->mtime : time(NULL);
+
+ /* If an If-Match request-header field was given
+ * AND the field value is not "*" (meaning match anything)
+ * AND if our strong ETag does not match any entity tag in that field,
+ * respond with a status of 412 (Precondition Failed).
+ */
+ if ((if_match = ap_table_get(r->headers_in, "If-Match")) != NULL) {
+ if (if_match[0] != '*' &&
+ (etag == NULL || etag[0] == 'W' ||
+ !ap_find_list_item(r->pool, if_match, etag))) {
+ return HTTP_PRECONDITION_FAILED;
+ }
+ }
+ else {
+ /* Else if a valid If-Unmodified-Since request-header field was given
+ * AND the requested resource has been modified since the time
+ * specified in this field, then the server MUST
+ * respond with a status of 412 (Precondition Failed).
+ */
+ if_unmodified = ap_table_get(r->headers_in, "If-Unmodified-Since");
+ if (if_unmodified != NULL) {
+ time_t ius = ap_parseHTTPdate(if_unmodified);
+
+ if ((ius != BAD_DATE) && (mtime > ius)) {
+ return HTTP_PRECONDITION_FAILED;
+ }
+ }
+ }
+
+ /* If an If-None-Match request-header field was given
+ * AND the field value is "*" (meaning match anything)
+ * OR our ETag matches any of the entity tags in that field, fail.
+ *
+ * If the request method was GET or HEAD, failure means the server
+ * SHOULD respond with a 304 (Not Modified) response.
+ * For all other request methods, failure means the server MUST
+ * respond with a status of 412 (Precondition Failed).
+ *
+ * GET or HEAD allow weak etag comparison, all other methods require
+ * strong comparison. We can only use weak if it's not a range request.
+ */
+ if_nonematch = ap_table_get(r->headers_in, "If-None-Match");
+ if (if_nonematch != NULL) {
+ if (r->method_number == M_GET) {
+ if (if_nonematch[0] == '*')
+ return HTTP_NOT_MODIFIED;
+ if (etag != NULL) {
+ if (ap_table_get(r->headers_in, "Range")) {
+ if (etag[0] != 'W' &&
+ ap_find_list_item(r->pool, if_nonematch, etag)) {
+ return HTTP_NOT_MODIFIED;
+ }
+ }
+ else if (strstr(if_nonematch, etag)) {
+ return HTTP_NOT_MODIFIED;
+ }
+ }
+ }
+ else if (if_nonematch[0] == '*' ||
+ (etag != NULL &&
+ ap_find_list_item(r->pool, if_nonematch, etag))) {
+ return HTTP_PRECONDITION_FAILED;
+ }
+ }
+ /* Else if a valid If-Modified-Since request-header field was given
+ * AND it is a GET or HEAD request
+ * AND the requested resource has not been modified since the time
+ * specified in this field, then the server MUST
+ * respond with a status of 304 (Not Modified).
+ * A date later than the server's current request time is invalid.
+ */
+ else if ((r->method_number == M_GET)
+ && ((if_modified_since =
+ ap_table_get(r->headers_in, "If-Modified-Since")) != NULL)) {
+ time_t ims = ap_parseHTTPdate(if_modified_since);
+
+ if ((ims >= mtime) && (ims <= r->request_time)) {
+ return HTTP_NOT_MODIFIED;
+ }
+ }
+ return OK;
+}
+
+/*
+ * Construct an entity tag (ETag) from resource information. If it's a real
+ * file, build in some of the file characteristics. If the modification time
+ * is newer than (request-time minus 1 second), mark the ETag as weak - it
+ * could be modified again in as short an interval. We rationalize the
+ * modification time we're given to keep it from being in the future.
+ */
+API_EXPORT(char *) ap_make_etag(request_rec *r, int force_weak)
+{
+ char *etag;
+ char *weak;
+
+ /*
+ * Make an ETag header out of various pieces of information. We use
+ * the last-modified date and, if we have a real file, the
+ * length and inode number - note that this doesn't have to match
+ * the content-length (i.e. includes), it just has to be unique
+ * for the file.
+ *
+ * If the request was made within a second of the last-modified date,
+ * we send a weak tag instead of a strong one, since it could
+ * be modified again later in the second, and the validation
+ * would be incorrect.
+ */
+
+ weak = ((r->request_time - r->mtime > 1) && !force_weak) ? "" : "W/";
+
+ if (r->finfo.st_mode != 0) {
+ etag = ap_psprintf(r->pool,
+ "%s\"%lx-%lx-%lx\"", weak,
+ (unsigned long) r->finfo.st_ino,
+ (unsigned long) r->finfo.st_size,
+ (unsigned long) r->mtime);
+ }
+ else {
+ etag = ap_psprintf(r->pool, "%s\"%lx\"", weak,
+ (unsigned long) r->mtime);
+ }
+
+ return etag;
+}
+
+API_EXPORT(void) ap_set_etag(request_rec *r)
+{
+ char *etag;
+ char *variant_etag, *vlv;
+ int vlv_weak;
+
+ if (!r->vlist_validator) {
+ etag = ap_make_etag(r, 0);
+ }
+ else {
+ /* If we have a variant list validator (vlv) due to the
+ * response being negotiated, then we create a structured
+ * entity tag which merges the variant etag with the variant
+ * list validator (vlv). This merging makes revalidation
+ * somewhat safer, ensures that caches which can deal with
+ * Vary will (eventually) be updated if the set of variants is
+ * changed, and is also a protocol requirement for transparent
+ * content negotiation.
+ */
+
+ /* if the variant list validator is weak, we make the whole
+ * structured etag weak. If we would not, then clients could
+ * have problems merging range responses if we have different
+ * variants with the same non-globally-unique strong etag.
+ */
+
+ vlv = r->vlist_validator;
+ vlv_weak = (vlv[0] == 'W');
+
+ variant_etag = ap_make_etag(r, vlv_weak);
+
+ /* merge variant_etag and vlv into a structured etag */
+
+ variant_etag[strlen(variant_etag) - 1] = '\0';
+ if (vlv_weak)
+ vlv += 3;
+ else
+ vlv++;
+ etag = ap_pstrcat(r->pool, variant_etag, ";", vlv, NULL);
+ }
+
+ ap_table_setn(r->headers_out, "ETag", etag);
+}
+
+/*
+ * This function sets the Last-Modified output header field to the value
+ * of the mtime field in the request structure - rationalized to keep it from
+ * being in the future.
+ */
+API_EXPORT(void) ap_set_last_modified(request_rec *r)
+{
+ time_t mod_time = ap_rationalize_mtime(r, r->mtime);
+
+ ap_table_setn(r->headers_out, "Last-Modified",
+ ap_gm_timestr_822(r->pool, mod_time));
+}
+
+/* Get the method number associated with the given string, assumed to
+ * contain an HTTP method. Returns M_INVALID if not recognized.
+ *
+ * This is the first step toward placing method names in a configurable
+ * list. Hopefully it (and other routines) can eventually be moved to
+ * something like a mod_http_methods.c, complete with config stuff.
+ */
+API_EXPORT(int) ap_method_number_of(const char *method)
+{
+ switch (*method) {
+ case 'H':
+ if (strcmp(method, "HEAD") == 0)
+ return M_GET; /* see header_only in request_rec */
+ break;
+ case 'G':
+ if (strcmp(method, "GET") == 0)
+ return M_GET;
+ break;
+ case 'P':
+ if (strcmp(method, "POST") == 0)
+ return M_POST;
+ if (strcmp(method, "PUT") == 0)
+ return M_PUT;
+ if (strcmp(method, "PATCH") == 0)
+ return M_PATCH;
+ if (strcmp(method, "PROPFIND") == 0)
+ return M_PROPFIND;
+ if (strcmp(method, "PROPPATCH") == 0)
+ return M_PROPPATCH;
+ break;
+ case 'D':
+ if (strcmp(method, "DELETE") == 0)
+ return M_DELETE;
+ break;
+ case 'C':
+ if (strcmp(method, "CONNECT") == 0)
+ return M_CONNECT;
+ if (strcmp(method, "COPY") == 0)
+ return M_COPY;
+ break;
+ case 'M':
+ if (strcmp(method, "MKCOL") == 0)
+ return M_MKCOL;
+ if (strcmp(method, "MOVE") == 0)
+ return M_MOVE;
+ break;
+ case 'O':
+ if (strcmp(method, "OPTIONS") == 0)
+ return M_OPTIONS;
+ break;
+ case 'T':
+ if (strcmp(method, "TRACE") == 0)
+ return M_TRACE;
+ break;
+ case 'L':
+ if (strcmp(method, "LOCK") == 0)
+ return M_LOCK;
+ break;
+ case 'U':
+ if (strcmp(method, "UNLOCK") == 0)
+ return M_UNLOCK;
+ break;
+ }
+ return M_INVALID;
+}
+
+/* Get a line of protocol input, including any continuation lines
+ * caused by MIME folding (or broken clients) if fold != 0, and place it
+ * in the buffer s, of size n bytes, without the ending newline.
+ *
+ * Returns -1 on error, or the length of s.
+ *
+ * Note: Because bgets uses 1 char for newline and 1 char for NUL,
+ * the most we can get is (n - 2) actual characters if it
+ * was ended by a newline, or (n - 1) characters if the line
+ * length exceeded (n - 1). So, if the result == (n - 1),
+ * then the actual input line exceeded the buffer length,
+ * and it would be a good idea for the caller to puke 400 or 414.
+ */
+static int getline(char *s, int n, BUFF *in, int fold)
+{
+ char *pos, next;
+ int retval;
+ int total = 0;
+
+ pos = s;
+
+ do {
+ retval = ap_bgets(pos, n, in); /* retval == -1 if error, 0 if EOF */
+
+ if (retval <= 0)
+ return ((retval < 0) && (total == 0)) ? -1 : total;
+
+ /* retval is the number of characters read, not including NUL */
+
+ n -= retval; /* Keep track of how much of s is full */
+ pos += (retval - 1); /* and where s ends */
+ total += retval; /* and how long s has become */
+
+ if (*pos == '\n') { /* Did we get a full line of input? */
+ /*
+ * Trim any extra trailing spaces or tabs except for the first
+ * space or tab at the beginning of a blank string. This makes
+ * it much easier to check field values for exact matches, and
+ * saves memory as well. Terminate string at end of line.
+ */
+ while (pos > (s + 1) && (*(pos - 1) == ' ' || *(pos - 1) == '\t')) {
+ --pos; /* trim extra trailing spaces or tabs */
+ --total; /* but not one at the beginning of line */
+ ++n;
+ }
+ *pos = '\0';
+ --total;
+ ++n;
+ }
+ else
+ return total; /* if not, input line exceeded buffer size */
+
+ /* Continue appending if line folding is desired and
+ * the last line was not empty and we have room in the buffer and
+ * the next line begins with a continuation character.
+ */
+ } while (fold && (retval != 1) && (n > 1)
+ && (ap_blookc(&next, in) == 1)
+ && ((next == ' ') || (next == '\t')));
+
+ return total;
+}
+
+/* parse_uri: break apart the uri
+ * Side Effects:
+ * - sets r->args to rest after '?' (or NULL if no '?')
+ * - sets r->uri to request uri (without r->args part)
+ * - sets r->hostname (if not set already) from request (scheme://host:port)
+ */
+CORE_EXPORT(void) ap_parse_uri(request_rec *r, const char *uri)
+{
+ int status = HTTP_OK;
+
+ r->unparsed_uri = ap_pstrdup(r->pool, uri);
+
+ if (r->method_number == M_CONNECT) {
+ status = ap_parse_hostinfo_components(r->pool, uri, &r->parsed_uri);
+ } else {
+ /* Simple syntax Errors in URLs are trapped by parse_uri_components(). */
+ status = ap_parse_uri_components(r->pool, uri, &r->parsed_uri);
+ }
+
+ if (ap_is_HTTP_SUCCESS(status)) {
+ /* if it has a scheme we may need to do absoluteURI vhost stuff */
+ if (r->parsed_uri.scheme
+ && !strcasecmp(r->parsed_uri.scheme, ap_http_method(r))) {
+ r->hostname = r->parsed_uri.hostname;
+ } else if (r->method_number == M_CONNECT) {
+ r->hostname = r->parsed_uri.hostname;
+ }
+ r->args = r->parsed_uri.query;
+ r->uri = r->parsed_uri.path ? r->parsed_uri.path
+ : ap_pstrdup(r->pool, "/");
+#if defined(OS2) || defined(WIN32)
+ /* Handle path translations for OS/2 and plug security hole.
+ * This will prevent "http://www.wherever.com/..\..\/" from
+ * returning a directory for the root drive.
+ */
+ {
+ char *x;
+
+ for (x = r->uri; (x = strchr(x, '\\')) != NULL; )
+ *x = '/';
+ }
+#endif /* OS2 || WIN32 */
+ }
+ else {
+ r->args = NULL;
+ r->hostname = NULL;
+ r->status = status; /* set error status */
+ r->uri = ap_pstrdup(r->pool, uri);
+ }
+}
+
+static int read_request_line(request_rec *r)
+{
+ char l[DEFAULT_LIMIT_REQUEST_LINE + 2]; /* getline's two extra for \n\0 */
+ const char *ll = l;
+ const char *uri;
+ conn_rec *conn = r->connection;
+ int major = 1, minor = 0; /* Assume HTTP/1.0 if non-"HTTP" protocol */
+ int len;
+
+ /* Read past empty lines until we get a real request line,
+ * a read error, the connection closes (EOF), or we timeout.
+ *
+ * We skip empty lines because browsers have to tack a CRLF on to the end
+ * of POSTs to support old CERN webservers. But note that we may not
+ * have flushed any previous response completely to the client yet.
+ * We delay the flush as long as possible so that we can improve
+ * performance for clients that are pipelining requests. If a request
+ * is pipelined then we won't block during the (implicit) read() below.
+ * If the requests aren't pipelined, then the client is still waiting
+ * for the final buffer flush from us, and we will block in the implicit
+ * read(). B_SAFEREAD ensures that the BUFF layer flushes if it will
+ * have to block during a read.
+ */
+ ap_bsetflag(conn->client, B_SAFEREAD, 1);
+ while ((len = getline(l, sizeof(l), conn->client, 0)) <= 0) {
+ if ((len < 0) || ap_bgetflag(conn->client, B_EOF)) {
+ ap_bsetflag(conn->client, B_SAFEREAD, 0);
+ /* this is a hack to make sure that request time is set,
+ * it's not perfect, but it's better than nothing
+ */
+ r->request_time = time(0);
+ return 0;
+ }
+ }
+ /* we've probably got something to do, ignore graceful restart requests */
+#ifdef SIGUSR1
+ signal(SIGUSR1, SIG_IGN);
+#endif
+
+ ap_bsetflag(conn->client, B_SAFEREAD, 0);
+
+ r->request_time = time(NULL);
+ r->the_request = ap_pstrdup(r->pool, l);
+ r->method = ap_getword_white(r->pool, &ll);
+ uri = ap_getword_white(r->pool, &ll);
+
+ /* Provide quick information about the request method as soon as known */
+
+ r->method_number = ap_method_number_of(r->method);
+ if (r->method_number == M_GET && r->method[0] == 'H') {
+ r->header_only = 1;
+ }
+
+ ap_parse_uri(r, uri);
+
+ /* getline returns (size of max buffer - 1) if it fills up the
+ * buffer before finding the end-of-line. This is only going to
+ * happen if it exceeds the configured limit for a request-line.
+ */
+ if (len > r->server->limit_req_line) {
+ r->status = HTTP_REQUEST_URI_TOO_LARGE;
+ r->proto_num = HTTP_VERSION(1,0);
+ r->protocol = ap_pstrdup(r->pool, "HTTP/1.0");
+ return 0;
+ }
+
+ r->assbackwards = (ll[0] == '\0');
+ r->protocol = ap_pstrdup(r->pool, ll[0] ? ll : "HTTP/0.9");
+
+ if (2 == sscanf(r->protocol, "HTTP/%u.%u", &major, &minor)
+ && minor < HTTP_VERSION(1,0)) /* don't allow HTTP/0.1000 */
+ r->proto_num = HTTP_VERSION(major, minor);
+ else
+ r->proto_num = HTTP_VERSION(1,0);
+
+ return 1;
+}
+
+static void get_mime_headers(request_rec *r)
+{
+ char field[DEFAULT_LIMIT_REQUEST_FIELDSIZE + 2]; /* getline's two extra */
+ conn_rec *c = r->connection;
+ char *value;
+ char *copy;
+ int len;
+ unsigned int fields_read = 0;
+ table *tmp_headers;
+
+ /* We'll use ap_overlap_tables later to merge these into r->headers_in. */
+ tmp_headers = ap_make_table(r->pool, 50);
+
+ /*
+ * Read header lines until we get the empty separator line, a read error,
+ * the connection closes (EOF), reach the server limit, or we timeout.
+ */
+ while ((len = getline(field, sizeof(field), c->client, 1)) > 0) {
+
+ if (r->server->limit_req_fields &&
+ (++fields_read > r->server->limit_req_fields)) {
+ r->status = HTTP_BAD_REQUEST;
+ ap_table_setn(r->notes, "error-notes",
+ "The number of request header fields exceeds "
+ "this server's limit.<P>\n");
+ return;
+ }
+ /* getline returns (size of max buffer - 1) if it fills up the
+ * buffer before finding the end-of-line. This is only going to
+ * happen if it exceeds the configured limit for a field size.
+ */
+ if (len > r->server->limit_req_fieldsize) {
+ r->status = HTTP_BAD_REQUEST;
+ ap_table_setn(r->notes, "error-notes", ap_pstrcat(r->pool,
+ "Size of a request header field exceeds server limit.<P>\n"
+ "<PRE>\n", field, "</PRE>\n", NULL));
+ return;
+ }
+ copy = ap_palloc(r->pool, len + 1);
+ memcpy(copy, field, len + 1);
+
+ if (!(value = strchr(copy, ':'))) { /* Find the colon separator */
+ r->status = HTTP_BAD_REQUEST; /* or abort the bad request */
+ ap_table_setn(r->notes, "error-notes", ap_pstrcat(r->pool,
+ "Request header field is missing colon separator.<P>\n"
+ "<PRE>\n", copy, "</PRE>\n", NULL));
+ return;
+ }
+
+ *value = '\0';
+ ++value;
+ while (*value == ' ' || *value == '\t')
+ ++value; /* Skip to start of value */
+
+ ap_table_addn(tmp_headers, copy, value);
+ }
+
+ ap_overlap_tables(r->headers_in, tmp_headers, AP_OVERLAP_TABLES_MERGE);
+}
+
+request_rec *ap_read_request(conn_rec *conn)
+{
+ request_rec *r;
+ pool *p;
+ const char *expect;
+ int access_status;
+
+ p = ap_make_sub_pool(conn->pool);
+ r = ap_pcalloc(p, sizeof(request_rec));
+ r->pool = p;
+ r->connection = conn;
+ conn->server = conn->base_server;
+ r->server = conn->server;
+
+ conn->keptalive = conn->keepalive == 1;
+ conn->keepalive = 0;
+
+ conn->user = NULL;
+ conn->ap_auth_type = NULL;
+
+ r->headers_in = ap_make_table(r->pool, 50);
+ r->subprocess_env = ap_make_table(r->pool, 50);
+ r->headers_out = ap_make_table(r->pool, 12);
+ r->err_headers_out = ap_make_table(r->pool, 5);
+ r->notes = ap_make_table(r->pool, 5);
+
+ r->request_config = ap_create_request_config(r->pool);
+ r->per_dir_config = r->server->lookup_defaults;
+
+ r->sent_bodyct = 0; /* bytect isn't for body */
+
+ r->read_length = 0;
+ r->read_body = REQUEST_NO_BODY;
+
+ r->status = HTTP_REQUEST_TIME_OUT; /* Until we get a request */
+ r->the_request = NULL;
+
+#ifdef CHARSET_EBCDIC
+ ap_bsetflag(r->connection->client, B_ASCII2EBCDIC|B_EBCDIC2ASCII, 1);
+#endif
+
+ /* Get the request... */
+
+ ap_keepalive_timeout("read request line", r);
+ if (!read_request_line(r)) {
+ ap_kill_timeout(r);
+ if (r->status == HTTP_REQUEST_URI_TOO_LARGE) {
+
+ ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
+ "request failed: URI too long");
+ ap_send_error_response(r, 0);
+ ap_log_transaction(r);
+ return r;
+ }
+ return NULL;
+ }
+ if (!r->assbackwards) {
+ ap_hard_timeout("read request headers", r);
+ get_mime_headers(r);
+ ap_kill_timeout(r);
+ if (r->status != HTTP_REQUEST_TIME_OUT) {
+ ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
+ "request failed: error reading the headers");
+ ap_send_error_response(r, 0);
+ ap_log_transaction(r);
+ return r;
+ }
+ }
+ else {
+ ap_kill_timeout(r);
+
+ if (r->header_only) {
+ /*
+ * Client asked for headers only with HTTP/0.9, which doesn't send
+ * headers! Have to dink things just to make sure the error message
+ * comes through...
+ */
+ ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
+ "client sent invalid HTTP/0.9 request: HEAD %s",
+ r->uri);
+ r->header_only = 0;
+ r->status = HTTP_BAD_REQUEST;
+ ap_send_error_response(r, 0);
+ ap_log_transaction(r);
+ return r;
+ }
+ }
+
+ r->status = HTTP_OK; /* Until further notice. */
+
+ /* update what we think the virtual host is based on the headers we've
+ * now read
+ */
+ ap_update_vhost_from_headers(r);
+
+ /* we may have switched to another server */
+ r->per_dir_config = r->server->lookup_defaults;
+
+ conn->keptalive = 0; /* We now have a request to play with */
+
+ if ((!r->hostname && (r->proto_num >= HTTP_VERSION(1,1))) ||
+ ((r->proto_num == HTTP_VERSION(1,1)) &&
+ !ap_table_get(r->headers_in, "Host"))) {
+ /*
+ * Client sent us an HTTP/1.1 or later request without telling us the
+ * hostname, either with a full URL or a Host: header. We therefore
+ * need to (as per the 1.1 spec) send an error. As a special case,
+ * HTTP/1.1 mentions twice (S9, S14.23) that a request MUST contain
+ * a Host: header, and the server MUST respond with 400 if it doesn't.
+ */
+ r->status = HTTP_BAD_REQUEST;
+ ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
+ "client sent HTTP/1.1 request without hostname "
+ "(see RFC2068 section 9, and 14.23): %s", r->uri);
+ ap_send_error_response(r, 0);
+ ap_log_transaction(r);
+ return r;
+ }
+ if (((expect = ap_table_get(r->headers_in, "Expect")) != NULL) &&
+ (expect[0] != '\0')) {
+ /*
+ * The Expect header field was added to HTTP/1.1 after RFC 2068
+ * as a means to signal when a 100 response is desired and,
+ * unfortunately, to signal a poor man's mandatory extension that
+ * the server must understand or return 417 Expectation Failed.
+ */
+ if (strcasecmp(expect, "100-continue") == 0) {
+ r->expecting_100 = 1;
+ }
+ else {
+ r->status = HTTP_EXPECTATION_FAILED;
+ ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, r,
+ "client sent an unrecognized expectation value of "
+ "Expect: %s", expect);
+ ap_send_error_response(r, 0);
+ (void) ap_discard_request_body(r);
+ ap_log_transaction(r);
+ return r;
+ }
+ }
+
+ if ((access_status = ap_run_post_read_request(r))) {
+ ap_die(access_status, r);
+ ap_log_transaction(r);
+ return NULL;
+ }
+
+ return r;
+}
+
+/*
+ * A couple of other functions which initialize some of the fields of
+ * a request structure, as appropriate for adjuncts of one kind or another
+ * to a request in progress. Best here, rather than elsewhere, since
+ * *someone* has to set the protocol-specific fields...
+ */
+
+void ap_set_sub_req_protocol(request_rec *rnew, const request_rec *r)
+{
+ rnew->the_request = r->the_request; /* Keep original request-line */
+
+ rnew->assbackwards = 1; /* Don't send headers from this. */
+ rnew->no_local_copy = 1; /* Don't try to send USE_LOCAL_COPY for a
+ * fragment. */
+ rnew->method = "GET";
+ rnew->method_number = M_GET;
+ rnew->protocol = "INCLUDED";
+
+ rnew->status = HTTP_OK;
+
+ rnew->headers_in = r->headers_in;
+ rnew->subprocess_env = ap_copy_table(rnew->pool, r->subprocess_env);
+ rnew->headers_out = ap_make_table(rnew->pool, 5);
+ rnew->err_headers_out = ap_make_table(rnew->pool, 5);
+ rnew->notes = ap_make_table(rnew->pool, 5);
+
+ rnew->expecting_100 = r->expecting_100;
+ rnew->read_length = r->read_length;
+ rnew->read_body = REQUEST_NO_BODY;
+
+ rnew->main = (request_rec *) r;
+}
+
+void ap_finalize_sub_req_protocol(request_rec *sub)
+{
+ SET_BYTES_SENT(sub->main);
+}
+
+/*
+ * Support for the Basic authentication protocol, and a bit for Digest.
+ */
+
+API_EXPORT(void) ap_note_auth_failure(request_rec *r)
+{
+ if (!strcasecmp(ap_auth_type(r), "Basic"))
+ ap_note_basic_auth_failure(r);
+ else if (!strcasecmp(ap_auth_type(r), "Digest"))
+ ap_note_digest_auth_failure(r);
+}
+
+API_EXPORT(void) ap_note_basic_auth_failure(request_rec *r)
+{
+ if (strcasecmp(ap_auth_type(r), "Basic"))
+ ap_note_auth_failure(r);
+ else
+ ap_table_setn(r->err_headers_out,
+ r->proxyreq ? "Proxy-Authenticate" : "WWW-Authenticate",
+ ap_pstrcat(r->pool, "Basic realm=\"", ap_auth_name(r), "\"",
+ NULL));
+}
+
+API_EXPORT(void) ap_note_digest_auth_failure(request_rec *r)
+{
+ ap_table_setn(r->err_headers_out,
+ r->proxyreq ? "Proxy-Authenticate" : "WWW-Authenticate",
+ ap_psprintf(r->pool, "Digest realm=\"%s\", nonce=\"%lu\"",
+ ap_auth_name(r), r->request_time));
+}
+
+API_EXPORT(int) ap_get_basic_auth_pw(request_rec *r, const char **pw)
+{
+ const char *auth_line = ap_table_get(r->headers_in,
+ r->proxyreq ? "Proxy-Authorization"
+ : "Authorization");
+ const char *t;
+
+ if (!(t = ap_auth_type(r)) || strcasecmp(t, "Basic"))
+ return DECLINED;
+
+ if (!ap_auth_name(r)) {
+ ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR,
+ r, "need AuthName: %s", r->uri);
+ return SERVER_ERROR;
+ }
+
+ if (!auth_line) {
+ ap_note_basic_auth_failure(r);
+ return AUTH_REQUIRED;
+ }
+
+ if (strcasecmp(ap_getword(r->pool, &auth_line, ' '), "Basic")) {
+ /* Client tried to authenticate using wrong auth scheme */
+ ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
+ "client used wrong authentication scheme: %s", r->uri);
+ ap_note_basic_auth_failure(r);
+ return AUTH_REQUIRED;
+ }
+
+ /* CHARSET_EBCDIC Issue's here ?!? Compare with 32/9 instead
+ * as we are operating on an octed stream ?
+ */
+ while (*auth_line== ' ' || *auth_line== '\t')
+ auth_line++;
+
+ t = ap_pbase64decode(r->pool, auth_line);
+ /* Note that this allocation has to be made from r->connection->pool
+ * because it has the lifetime of the connection. The other allocations
+ * are temporary and can be tossed away any time.
+ */
+ r->connection->user = ap_getword_nulls (r->connection->pool, &t, ':');
+ r->connection->ap_auth_type = "Basic";
+
+ *pw = t;
+
+ return OK;
+}
+
+/* New Apache routine to map status codes into array indicies
+ * e.g. 100 -> 0, 101 -> 1, 200 -> 2 ...
+ * The number of status lines must equal the value of RESPONSE_CODES (httpd.h)
+ * and must be listed in order.
+ */
+
+#ifdef UTS21
+/* The second const triggers an assembler bug on UTS 2.1.
+ * Another workaround is to move some code out of this file into another,
+ * but this is easier. Dave Dykstra, 3/31/99
+ */
+static const char * status_lines[RESPONSE_CODES] =
+#else
+static const char * const status_lines[RESPONSE_CODES] =
+#endif
+{
+ "100 Continue",
+ "101 Switching Protocols",
+ "102 Processing",
+#define LEVEL_200 3
+ "200 OK",
+ "201 Created",
+ "202 Accepted",
+ "203 Non-Authoritative Information",
+ "204 No Content",
+ "205 Reset Content",
+ "206 Partial Content",
+ "207 Multi-Status",
+#define LEVEL_300 11
+ "300 Multiple Choices",
+ "301 Moved Permanently",
+ "302 Found",
+ "303 See Other",
+ "304 Not Modified",
+ "305 Use Proxy",
+ "306 unused",
+ "307 Temporary Redirect",
+#define LEVEL_400 19
+ "400 Bad Request",
+ "401 Authorization Required",
+ "402 Payment Required",
+ "403 Forbidden",
+ "404 Not Found",
+ "405 Method Not Allowed",
+ "406 Not Acceptable",
+ "407 Proxy Authentication Required",
+ "408 Request Time-out",
+ "409 Conflict",
+ "410 Gone",
+ "411 Length Required",
+ "412 Precondition Failed",
+ "413 Request Entity Too Large",
+ "414 Request-URI Too Large",
+ "415 Unsupported Media Type",
+ "416 Requested Range Not Satisfiable",
+ "417 Expectation Failed",
+ "418 unused",
+ "419 unused",
+ "420 unused",
+ "421 unused",
+ "422 Unprocessable Entity",
+ "423 Locked",
+ "424 Failed Dependency",
+#define LEVEL_500 44
+ "500 Internal Server Error",
+ "501 Method Not Implemented",
+ "502 Bad Gateway",
+ "503 Service Temporarily Unavailable",
+ "504 Gateway Time-out",
+ "505 HTTP Version Not Supported",
+ "506 Variant Also Negotiates",
+ "507 Insufficient Storage",
+ "508 unused",
+ "509 unused",
+ "510 Not Extended"
+};
+
+/* The index is found by its offset from the x00 code of each level.
+ * Although this is fast, it will need to be replaced if some nutcase
+ * decides to define a high-numbered code before the lower numbers.
+ * If that sad event occurs, replace the code below with a linear search
+ * from status_lines[shortcut[i]] to status_lines[shortcut[i+1]-1];
+ */
+API_EXPORT(int) ap_index_of_response(int status)
+{
+ static int shortcut[6] = {0, LEVEL_200, LEVEL_300, LEVEL_400,
+ LEVEL_500, RESPONSE_CODES};
+ int i, pos;
+
+ if (status < 100) /* Below 100 is illegal for HTTP status */
+ return LEVEL_500;
+
+ for (i = 0; i < 5; i++) {
+ status -= 100;
+ if (status < 100) {
+ pos = (status + shortcut[i]);
+ if (pos < shortcut[i + 1])
+ return pos;
+ else
+ return LEVEL_500; /* status unknown (falls in gap) */
+ }
+ }
+ return LEVEL_500; /* 600 or above is also illegal */
+}
+
+/* Send a single HTTP header field to the client. Note that this function
+ * is used in calls to table_do(), so their interfaces are co-dependent.
+ * In other words, don't change this one without checking table_do in alloc.c.
+ * It returns true unless there was a write error of some kind.
+ */
+API_EXPORT_NONSTD(int) ap_send_header_field(request_rec *r,
+ const char *fieldname, const char *fieldval)
+{
+ return (0 < ap_rvputs(r, fieldname, ": ", fieldval, "\015\012", NULL));
+}
+
+API_EXPORT(void) ap_basic_http_header(request_rec *r)
+{
+ char *protocol;
+#ifdef CHARSET_EBCDIC
+ int convert = ap_bgetflag(r->connection->client, B_EBCDIC2ASCII);
+#endif /*CHARSET_EBCDIC*/
+
+ if (r->assbackwards)
+ return;
+
+ if (!r->status_line)
+ r->status_line = status_lines[ap_index_of_response(r->status)];
+
+ /* mod_proxy is only HTTP/1.0, so avoid sending HTTP/1.1 error response;
+ * kluge around broken browsers when indicated by force-response-1.0
+ */
+ if (r->proxyreq
+ || (r->proto_num == HTTP_VERSION(1,0)
+ && ap_table_get(r->subprocess_env, "force-response-1.0"))) {
+
+ protocol = "HTTP/1.0";
+ r->connection->keepalive = -1;
+ }
+ else
+ protocol = SERVER_PROTOCOL;
+
+#ifdef CHARSET_EBCDIC
+ ap_bsetflag(r->connection->client, B_EBCDIC2ASCII, 1);
+#endif /*CHARSET_EBCDIC*/
+
+ /* Output the HTTP/1.x Status-Line and the Date and Server fields */
+
+ ap_rvputs(r, protocol, " ", r->status_line, "\015\012", NULL);
+
+ ap_send_header_field(r, "Date", ap_gm_timestr_822(r->pool, r->request_time));
+ ap_send_header_field(r, "Server", ap_get_server_version());
+
+ ap_table_unset(r->headers_out, "Date"); /* Avoid bogosity */
+ ap_table_unset(r->headers_out, "Server");
+#ifdef CHARSET_EBCDIC
+ if (!convert)
+ ap_bsetflag(r->connection->client, B_EBCDIC2ASCII, convert);
+#endif /*CHARSET_EBCDIC*/
+}
+
+/* Navigator versions 2.x, 3.x and 4.0 betas up to and including 4.0b2
+ * have a header parsing bug. If the terminating \r\n occur starting
+ * at offset 256, 257 or 258 of output then it will not properly parse
+ * the headers. Curiously it doesn't exhibit this problem at 512, 513.
+ * We are guessing that this is because their initial read of a new request
+ * uses a 256 byte buffer, and subsequent reads use a larger buffer.
+ * So the problem might exist at different offsets as well.
+ *
+ * This should also work on keepalive connections assuming they use the
+ * same small buffer for the first read of each new request.
+ *
+ * At any rate, we check the bytes written so far and, if we are about to
+ * tickle the bug, we instead insert a bogus padding header. Since the bug
+ * manifests as a broken image in Navigator, users blame the server. :(
+ * It is more expensive to check the User-Agent than it is to just add the
+ * bytes, so we haven't used the BrowserMatch feature here.
+ */
+static void terminate_header(BUFF *client)
+{
+ long int bs;
+
+ ap_bgetopt(client, BO_BYTECT, &bs);
+ if (bs >= 255 && bs <= 257)
+ ap_bputs("X-Pad: avoid browser bug\015\012", client);
+
+ ap_bputs("\015\012", client); /* Send the terminating empty line */
+}
+
+/* Build the Allow field-value from the request handler method mask.
+ * Note that we always allow TRACE, since it is handled below.
+ */
+static char *make_allow(request_rec *r)
+{
+ return 2 + ap_pstrcat(r->pool,
+ (r->allowed & (1 << M_GET)) ? ", GET, HEAD" : "",
+ (r->allowed & (1 << M_POST)) ? ", POST" : "",
+ (r->allowed & (1 << M_PUT)) ? ", PUT" : "",
+ (r->allowed & (1 << M_DELETE)) ? ", DELETE" : "",
+ (r->allowed & (1 << M_CONNECT)) ? ", CONNECT" : "",
+ (r->allowed & (1 << M_OPTIONS)) ? ", OPTIONS" : "",
+ (r->allowed & (1 << M_PATCH)) ? ", PATCH" : "",
+ (r->allowed & (1 << M_PROPFIND)) ? ", PROPFIND" : "",
+ (r->allowed & (1 << M_PROPPATCH)) ? ", PROPPATCH" : "",
+ (r->allowed & (1 << M_MKCOL)) ? ", MKCOL" : "",
+ (r->allowed & (1 << M_COPY)) ? ", COPY" : "",
+ (r->allowed & (1 << M_MOVE)) ? ", MOVE" : "",
+ (r->allowed & (1 << M_LOCK)) ? ", LOCK" : "",
+ (r->allowed & (1 << M_UNLOCK)) ? ", UNLOCK" : "",
+ ", TRACE",
+ NULL);
+}
+
+API_EXPORT(int) ap_send_http_trace(request_rec *r)
+{
+ int rv;
+
+ /* Get the original request */
+ while (r->prev)
+ r = r->prev;
+
+ if ((rv = ap_setup_client_block(r, REQUEST_NO_BODY)))
+ return rv;
+
+ ap_hard_timeout("send TRACE", r);
+
+ r->content_type = "message/http";
+ ap_send_http_header(r);
+
+ /* Now we recreate the request, and echo it back */
+
+ ap_rvputs(r, r->the_request, "\015\012", NULL);
+
+ ap_table_do((int (*) (void *, const char *, const char *))
+ ap_send_header_field, (void *) r, r->headers_in, NULL);
+ ap_rputs("\015\012", r);
+
+ ap_kill_timeout(r);
+ return OK;
+}
+
+int ap_send_http_options(request_rec *r)
+{
+ const long int zero = 0L;
+
+ if (r->assbackwards)
+ return DECLINED;
+
+ ap_hard_timeout("send OPTIONS", r);
+
+ ap_basic_http_header(r);
+
+ ap_table_setn(r->headers_out, "Content-Length", "0");
+ ap_table_setn(r->headers_out, "Allow", make_allow(r));
+ ap_set_keepalive(r);
+
+ ap_table_do((int (*) (void *, const char *, const char *)) ap_send_header_field,
+ (void *) r, r->headers_out, NULL);
+
+ terminate_header(r->connection->client);
+
+ ap_kill_timeout(r);
+ ap_bsetopt(r->connection->client, BO_BYTECT, &zero);
+
+ return OK;
+}
+
+/*
+ * Here we try to be compatible with clients that want multipart/x-byteranges
+ * instead of multipart/byteranges (also see above), as per HTTP/1.1. We
+ * look for the Request-Range header (e.g. Netscape 2 and 3) as an indication
+ * that the browser supports an older protocol. We also check User-Agent
+ * for Microsoft Internet Explorer 3, which needs this as well.
+ */
+static int use_range_x(request_rec *r)
+{
+ const char *ua;
+ return (ap_table_get(r->headers_in, "Request-Range") ||
+ ((ua = ap_table_get(r->headers_in, "User-Agent"))
+ && strstr(ua, "MSIE 3")));
+}
+
+/* This routine is called by ap_table_do and merges all instances of
+ * the passed field values into a single array that will be further
+ * processed by some later routine. Originally intended to help split
+ * and recombine multiple Vary fields, though it is generic to any field
+ * consisting of comma/space-separated tokens.
+ */
+static int uniq_field_values(void *d, const char *key, const char *val)
+{
+ array_header *values;
+ char *start;
+ char *e;
+ char **strpp;
+ int i;
+
+ values = (array_header *)d;
+
+ e = ap_pstrdup(values->pool, val);
+
+ do {
+ /* Find a non-empty fieldname */
+
+ while (*e == ',' || ap_isspace(*e)) {
+ ++e;
+ }
+ if (*e == '\0') {
+ break;
+ }
+ start = e;
+ while (*e != '\0' && *e != ',' && !ap_isspace(*e)) {
+ ++e;
+ }
+ if (*e != '\0') {
+ *e++ = '\0';
+ }
+
+ /* Now add it to values if it isn't already represented.
+ * Could be replaced by a ap_array_strcasecmp() if we had one.
+ */
+ for (i = 0, strpp = (char **) values->elts; i < values->nelts;
+ ++i, ++strpp) {
+ if (*strpp && strcasecmp(*strpp, start) == 0) {
+ break;
+ }
+ }
+ if (i == values->nelts) { /* if not found */
+ *(char **)ap_push_array(values) = start;
+ }
+ } while (*e != '\0');
+
+ return 1;
+}
+
+/*
+ * Since some clients choke violently on multiple Vary fields, or
+ * Vary fields with duplicate tokens, combine any multiples and remove
+ * any duplicates.
+ */
+static void fixup_vary(request_rec *r)
+{
+ array_header *varies;
+
+ varies = ap_make_array(r->pool, 5, sizeof(char *));
+
+ /* Extract all Vary fields from the headers_out, separate each into
+ * its comma-separated fieldname values, and then add them to varies
+ * if not already present in the array.
+ */
+ ap_table_do((int (*)(void *, const char *, const char *))uniq_field_values,
+ (void *) varies, r->headers_out, "Vary", NULL);
+
+ /* If we found any, replace old Vary fields with unique-ified value */
+
+ if (varies->nelts > 0) {
+ ap_table_setn(r->headers_out, "Vary",
+ ap_array_pstrcat(r->pool, varies, ','));
+ }
+}
+
+API_EXPORT(void) ap_send_http_header(request_rec *r)
+{
+ int i;
+ const long int zero = 0L;
+#ifdef CHARSET_EBCDIC
+ int convert = ap_bgetflag(r->connection->client, B_EBCDIC2ASCII);
+#endif /*CHARSET_EBCDIC*/
+
+ if (r->assbackwards) {
+ if (!r->main)
+ ap_bsetopt(r->connection->client, BO_BYTECT, &zero);
+ r->sent_bodyct = 1;
+ return;
+ }
+
+ /*
+ * Now that we are ready to send a response, we need to combine the two
+ * header field tables into a single table. If we don't do this, our
+ * later attempts to set or unset a given fieldname might be bypassed.
+ */
+ if (!ap_is_empty_table(r->err_headers_out))
+ r->headers_out = ap_overlay_tables(r->pool, r->err_headers_out,
+ r->headers_out);
+
+ /*
+ * Remove the 'Vary' header field if the client can't handle it.
+ * Since this will have nasty effects on HTTP/1.1 caches, force
+ * the response into HTTP/1.0 mode.
+ */
+ if (ap_table_get(r->subprocess_env, "force-no-vary") != NULL) {
+ ap_table_unset(r->headers_out, "Vary");
+ r->proto_num = HTTP_VERSION(1,0);
+ ap_table_set(r->subprocess_env, "force-response-1.0", "1");
+ }
+ else {
+ fixup_vary(r);
+ }
+
+ ap_hard_timeout("send headers", r);
+
+ ap_basic_http_header(r);
+
+#ifdef CHARSET_EBCDIC
+ ap_bsetflag(r->connection->client, B_EBCDIC2ASCII, 1);
+#endif /*CHARSET_EBCDIC*/
+
+ ap_set_keepalive(r);
+
+ if (r->chunked) {
+ ap_table_mergen(r->headers_out, "Transfer-Encoding", "chunked");
+ ap_table_unset(r->headers_out, "Content-Length");
+ }
+
+ if (r->byterange > 1)
+ ap_table_setn(r->headers_out, "Content-Type",
+ ap_pstrcat(r->pool, "multipart", use_range_x(r) ? "/x-" : "/",
+ "byteranges; boundary=", r->boundary, NULL));
+ else if (r->content_type)
+ ap_table_setn(r->headers_out, "Content-Type", r->content_type);
+ else
+ ap_table_setn(r->headers_out, "Content-Type", ap_default_type(r));
+
+ if (r->content_encoding)
+ ap_table_setn(r->headers_out, "Content-Encoding", r->content_encoding);
+
+ if (r->content_languages && r->content_languages->nelts) {
+ for (i = 0; i < r->content_languages->nelts; ++i) {
+ ap_table_mergen(r->headers_out, "Content-Language",
+ ((char **) (r->content_languages->elts))[i]);
+ }
+ }
+ else if (r->content_language)
+ ap_table_setn(r->headers_out, "Content-Language", r->content_language);
+
+ /*
+ * Control cachability for non-cachable responses if not already set by
+ * some other part of the server configuration.
+ */
+ if (r->no_cache && !ap_table_get(r->headers_out, "Expires"))
+ ap_table_addn(r->headers_out, "Expires",
+ ap_gm_timestr_822(r->pool, r->request_time));
+
+ /* Send the entire table of header fields, terminated by an empty line. */
+
+ ap_table_do((int (*) (void *, const char *, const char *)) ap_send_header_field,
+ (void *) r, r->headers_out, NULL);
+
+ terminate_header(r->connection->client);
+
+ ap_kill_timeout(r);
+
+ ap_bsetopt(r->connection->client, BO_BYTECT, &zero);
+ r->sent_bodyct = 1; /* Whatever follows is real body stuff... */
+
+ /* Set buffer flags for the body */
+ if (r->chunked)
+ ap_bsetflag(r->connection->client, B_CHUNK, 1);
+#ifdef CHARSET_EBCDIC
+ if (!convert)
+ ap_bsetflag(r->connection->client, B_EBCDIC2ASCII, convert);
+#endif /*CHARSET_EBCDIC*/
+}
+
+/* finalize_request_protocol is called at completion of sending the
+ * response. It's sole purpose is to send the terminating protocol
+ * information for any wrappers around the response message body
+ * (i.e., transfer encodings). It should have been named finalize_response.
+ */
+API_EXPORT(void) ap_finalize_request_protocol(request_rec *r)
+{
+ if (r->chunked && !r->connection->aborted) {
+ /*
+ * Turn off chunked encoding --- we can only do this once.
+ */
+ r->chunked = 0;
+ ap_bsetflag(r->connection->client, B_CHUNK, 0);
+
+ ap_soft_timeout("send ending chunk", r);
+ ap_rputs("0\015\012", r);
+ /* If we had footer "headers", we'd send them now */
+ ap_rputs("\015\012", r);
+ ap_kill_timeout(r);
+ }
+}
+
+/* Here we deal with getting the request message body from the client.
+ * Whether or not the request contains a body is signaled by the presence
+ * of a non-zero Content-Length or by a Transfer-Encoding: chunked.
+ *
+ * Note that this is more complicated than it was in Apache 1.1 and prior
+ * versions, because chunked support means that the module does less.
+ *
+ * The proper procedure is this:
+ *
+ * 1. Call setup_client_block() near the beginning of the request
+ * handler. This will set up all the necessary properties, and will
+ * return either OK, or an error code. If the latter, the module should
+ * return that error code. The second parameter selects the policy to
+ * apply if the request message indicates a body, and how a chunked
+ * transfer-coding should be interpreted. Choose one of
+ *
+ * REQUEST_NO_BODY Send 413 error if message has any body
+ * REQUEST_CHUNKED_ERROR Send 411 error if body without Content-Length
+ * REQUEST_CHUNKED_DECHUNK If chunked, remove the chunks for me.
+ * REQUEST_CHUNKED_PASS Pass the chunks to me without removal.
+ *
+ * In order to use the last two options, the caller MUST provide a buffer
+ * large enough to hold a chunk-size line, including any extensions.
+ *
+ * 2. When you are ready to read a body (if any), call should_client_block().
+ * This will tell the module whether or not to read input. If it is 0,
+ * the module should assume that there is no message body to read.
+ * This step also sends a 100 Continue response to HTTP/1.1 clients,
+ * so should not be called until the module is *definitely* ready to
+ * read content. (otherwise, the point of the 100 response is defeated).
+ * Never call this function more than once.
+ *
+ * 3. Finally, call get_client_block in a loop. Pass it a buffer and its size.
+ * It will put data into the buffer (not necessarily a full buffer), and
+ * return the length of the input block. When it is done reading, it will
+ * return 0 if EOF, or -1 if there was an error.
+ * If an error occurs on input, we force an end to keepalive.
+ */
+
+API_EXPORT(int) ap_setup_client_block(request_rec *r, int read_policy)
+{
+ const char *tenc = ap_table_get(r->headers_in, "Transfer-Encoding");
+ const char *lenp = ap_table_get(r->headers_in, "Content-Length");
+ unsigned long max_body;
+
+ r->read_body = read_policy;
+ r->read_chunked = 0;
+ r->remaining = 0;
+
+ if (tenc) {
+ if (strcasecmp(tenc, "chunked")) {
+ ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
+ "Unknown Transfer-Encoding %s", tenc);
+ return HTTP_NOT_IMPLEMENTED;
+ }
+ if (r->read_body == REQUEST_CHUNKED_ERROR) {
+ ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
+ "chunked Transfer-Encoding forbidden: %s", r->uri);
+ return (lenp) ? HTTP_BAD_REQUEST : HTTP_LENGTH_REQUIRED;
+ }
+
+ r->read_chunked = 1;
+ }
+ else if (lenp) {
+ const char *pos = lenp;
+
+ while (ap_isdigit(*pos) || ap_isspace(*pos))
+ ++pos;
+ if (*pos != '\0') {
+ ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
+ "Invalid Content-Length %s", lenp);
+ return HTTP_BAD_REQUEST;
+ }
+
+ r->remaining = atol(lenp);
+ }
+
+ if ((r->read_body == REQUEST_NO_BODY) &&
+ (r->read_chunked || (r->remaining > 0))) {
+ ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
+ "%s with body is not allowed for %s", r->method, r->uri);
+ return HTTP_REQUEST_ENTITY_TOO_LARGE;
+ }
+
+ max_body = ap_get_limit_req_body(r);
+ if (max_body && (r->remaining > max_body)) {
+ ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
+ "Request content-length of %s is larger than the configured "
+ "limit of %lu", lenp, max_body);
+ return HTTP_REQUEST_ENTITY_TOO_LARGE;
+ }
+
+ return OK;
+}
+
+API_EXPORT(int) ap_should_client_block(request_rec *r)
+{
+ /* First check if we have already read the request body */
+
+ if (r->read_length || (!r->read_chunked && (r->remaining <= 0)))
+ return 0;
+
+ if (r->expecting_100 && r->proto_num >= HTTP_VERSION(1,1)) {
+ /* sending 100 Continue interim response */
+ ap_rvputs(r, SERVER_PROTOCOL, " ", status_lines[0], "\015\012\015\012",
+ NULL);
+ ap_rflush(r);
+ }
+
+ return 1;
+}
+
+static long get_chunk_size(char *b)
+{
+ long chunksize = 0;
+
+ while (ap_isxdigit(*b)) {
+ int xvalue = 0;
+
+ if (*b >= '0' && *b <= '9')
+ xvalue = *b - '0';
+ else if (*b >= 'A' && *b <= 'F')
+ xvalue = *b - 'A' + 0xa;
+ else if (*b >= 'a' && *b <= 'f')
+ xvalue = *b - 'a' + 0xa;
+
+ chunksize = (chunksize << 4) | xvalue;
+ ++b;
+ }
+
+ return chunksize;
+}
+
+/* get_client_block is called in a loop to get the request message body.
+ * This is quite simple if the client includes a content-length
+ * (the normal case), but gets messy if the body is chunked. Note that
+ * r->remaining is used to maintain state across calls and that
+ * r->read_length is the total number of bytes given to the caller
+ * across all invocations. It is messy because we have to be careful not
+ * to read past the data provided by the client, since these reads block.
+ * Returns 0 on End-of-body, -1 on error or premature chunk end.
+ *
+ * Reading the chunked encoding requires a buffer size large enough to
+ * hold a chunk-size line, including any extensions. For now, we'll leave
+ * that to the caller, at least until we can come up with a better solution.
+ */
+API_EXPORT(long) ap_get_client_block(request_rec *r, char *buffer, int bufsiz)
+{
+ int c;
+ long len_read, len_to_read;
+ long chunk_start = 0;
+ unsigned long max_body;
+
+ if (!r->read_chunked) { /* Content-length read */
+ len_to_read = (r->remaining > bufsiz) ? bufsiz : r->remaining;
+ len_read = ap_bread(r->connection->client, buffer, len_to_read);
+ if (len_read <= 0) {
+ if (len_read < 0)
+ r->connection->keepalive = -1;
+ return len_read;
+ }
+ r->read_length += len_read;
+ r->remaining -= len_read;
+ return len_read;
+ }
+
+ /*
+ * Handle chunked reading Note: we are careful to shorten the input
+ * bufsiz so that there will always be enough space for us to add a CRLF
+ * (if necessary).
+ */
+ if (r->read_body == REQUEST_CHUNKED_PASS)
+ bufsiz -= 2;
+ if (bufsiz <= 0)
+ return -1; /* Cannot read chunked with a small buffer */
+
+ /* Check to see if we have already read too much request data.
+ * For efficiency reasons, we only check this at the top of each
+ * caller read pass, since the limit exists just to stop infinite
+ * length requests and nobody cares if it goes over by one buffer.
+ */
+ max_body = ap_get_limit_req_body(r);
+ if (max_body && (r->read_length > max_body)) {
+ ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
+ "Chunked request body is larger than the configured limit of %lu",
+ max_body);
+ r->connection->keepalive = -1;
+ return -1;
+ }
+
+ if (r->remaining == 0) { /* Start of new chunk */
+
+ chunk_start = getline(buffer, bufsiz, r->connection->client, 0);
+ if ((chunk_start <= 0) || (chunk_start >= (bufsiz - 1))
+ || !ap_isxdigit(*buffer)) {
+ r->connection->keepalive = -1;
+ return -1;
+ }
+
+ len_to_read = get_chunk_size(buffer);
+
+ if (len_to_read == 0) { /* Last chunk indicated, get footers */
+ if (r->read_body == REQUEST_CHUNKED_DECHUNK) {
+ get_mime_headers(r);
+ ap_snprintf(buffer, bufsiz, "%ld", r->read_length);
+ ap_table_unset(r->headers_in, "Transfer-Encoding");
+ ap_table_setn(r->headers_in, "Content-Length",
+ ap_pstrdup(r->pool, buffer));
+ return 0;
+ }
+ r->remaining = -1; /* Indicate footers in-progress */
+ }
+ else {
+ r->remaining = len_to_read;
+ }
+ if (r->read_body == REQUEST_CHUNKED_PASS) {
+ buffer[chunk_start++] = CR; /* Restore chunk-size line end */
+ buffer[chunk_start++] = LF;
+ buffer += chunk_start; /* and pass line on to caller */
+ bufsiz -= chunk_start;
+ }
+ else {
+ /* REQUEST_CHUNKED_DECHUNK -- do not include the length of the
+ * header in the return value
+ */
+ chunk_start = 0;
+ }
+ }
+ /* When REQUEST_CHUNKED_PASS, we are */
+ if (r->remaining == -1) { /* reading footers until empty line */
+ len_read = chunk_start;
+
+ while ((bufsiz > 1) && ((len_read =
+ getline(buffer, bufsiz, r->connection->client, 1)) > 0)) {
+
+ if (len_read != (bufsiz - 1)) {
+ buffer[len_read++] = CR; /* Restore footer line end */
+ buffer[len_read++] = LF;
+ }
+ chunk_start += len_read;
+ buffer += len_read;
+ bufsiz -= len_read;
+ }
+ if (len_read < 0) {
+ r->connection->keepalive = -1;
+ return -1;
+ }
+
+ if (len_read == 0) { /* Indicates an empty line */
+ buffer[0] = CR;
+ buffer[1] = LF;
+ chunk_start += 2;
+ r->remaining = -2;
+ }
+ r->read_length += chunk_start;
+ return chunk_start;
+ }
+ /* When REQUEST_CHUNKED_PASS, we */
+ if (r->remaining == -2) { /* finished footers when last called */
+ r->remaining = 0; /* so now we must signal EOF */
+ return 0;
+ }
+
+ /* Otherwise, we are in the midst of reading a chunk of data */
+
+ len_to_read = (r->remaining > bufsiz) ? bufsiz : r->remaining;
+
+ len_read = ap_bread(r->connection->client, buffer, len_to_read);
+ if (len_read <= 0) {
+ r->connection->keepalive = -1;
+ return -1;
+ }
+
+ r->remaining -= len_read;
+
+ if (r->remaining == 0) { /* End of chunk, get trailing CRLF */
+ if ((c = ap_bgetc(r->connection->client)) == CR) {
+ c = ap_bgetc(r->connection->client);
+ }
+ if (c != LF) {
+ r->connection->keepalive = -1;
+ return -1;
+ }
+ if (r->read_body == REQUEST_CHUNKED_PASS) {
+ buffer[len_read++] = CR;
+ buffer[len_read++] = LF;
+ }
+ }
+ r->read_length += (chunk_start + len_read);
+
+ return (chunk_start + len_read);
+}
+
+/* In HTTP/1.1, any method can have a body. However, most GET handlers
+ * wouldn't know what to do with a request body if they received one.
+ * This helper routine tests for and reads any message body in the request,
+ * simply discarding whatever it receives. We need to do this because
+ * failing to read the request body would cause it to be interpreted
+ * as the next request on a persistent connection.
+ *
+ * Since we return an error status if the request is malformed, this
+ * routine should be called at the beginning of a no-body handler, e.g.,
+ *
+ * if ((retval = ap_discard_request_body(r)) != OK)
+ * return retval;
+ */
+API_EXPORT(int) ap_discard_request_body(request_rec *r)
+{
+ int rv;
+
+ if ((rv = ap_setup_client_block(r, REQUEST_CHUNKED_PASS)))
+ return rv;
+
+ /* In order to avoid sending 100 Continue when we already know the
+ * final response status, and yet not kill the connection if there is
+ * no request body to be read, we need to duplicate the test from
+ * ap_should_client_block() here negated rather than call it directly.
+ */
+ if ((r->read_length == 0) && (r->read_chunked || (r->remaining > 0))) {
+ char dumpbuf[HUGE_STRING_LEN];
+
+ if (r->expecting_100) {
+ r->connection->keepalive = -1;
+ return OK;
+ }
+ ap_hard_timeout("reading request body", r);
+ while ((rv = ap_get_client_block(r, dumpbuf, HUGE_STRING_LEN)) > 0)
+ continue;
+ ap_kill_timeout(r);
+
+ if (rv < 0)
+ return HTTP_BAD_REQUEST;
+ }
+ return OK;
+}
+
+/*
+ * Send the body of a response to the client.
+ */
+API_EXPORT(long) ap_send_fd(FILE *f, request_rec *r)
+{
+ return ap_send_fd_length(f, r, -1);
+}
+
+API_EXPORT(long) ap_send_fd_length(FILE *f, request_rec *r, long length)
+{
+ char buf[IOBUFSIZE];
+ long total_bytes_sent = 0;
+ register int n, w, o, len;
+
+ if (length == 0)
+ return 0;
+
+ ap_soft_timeout("send body", r);
+
+ while (!r->connection->aborted) {
+ if ((length > 0) && (total_bytes_sent + IOBUFSIZE) > length)
+ len = length - total_bytes_sent;
+ else
+ len = IOBUFSIZE;
+
+ while ((n = fread(buf, sizeof(char), len, f)) < 1
+ && ferror(f) && errno == EINTR && !r->connection->aborted)
+ continue;
+
+ if (n < 1) {
+ break;
+ }
+ o = 0;
+
+ while (n && !r->connection->aborted) {
+ w = ap_bwrite(r->connection->client, &buf[o], n);
+ if (w > 0) {
+ ap_reset_timeout(r); /* reset timeout after successful write */
+ total_bytes_sent += w;
+ n -= w;
+ o += w;
+ }
+ else if (w < 0) {
+ if (!r->connection->aborted) {
+ ap_log_rerror(APLOG_MARK, APLOG_INFO, r,
+ "client stopped connection before send body completed");
+ ap_bsetflag(r->connection->client, B_EOUT, 1);
+ r->connection->aborted = 1;
+ }
+ break;
+ }
+ }
+ }
+
+ ap_kill_timeout(r);
+ SET_BYTES_SENT(r);
+ return total_bytes_sent;
+}
+
+/*
+ * Send the body of a response to the client.
+ */
+API_EXPORT(long) ap_send_fb(BUFF *fb, request_rec *r)
+{
+ return ap_send_fb_length(fb, r, -1);
+}
+
+API_EXPORT(long) ap_send_fb_length(BUFF *fb, request_rec *r, long length)
+{
+ char buf[IOBUFSIZE];
+ long total_bytes_sent = 0;
+ register int n, w, o, len, fd;
+ fd_set fds;
+
+ if (length == 0)
+ return 0;
+
+ /* Make fb unbuffered and non-blocking */
+ ap_bsetflag(fb, B_RD, 0);
+#ifndef TPF
+ ap_bnonblock(fb, B_RD);
+#endif
+ fd = ap_bfileno(fb, B_RD);
+#ifdef CHECK_FD_SETSIZE
+ if (fd >= FD_SETSIZE) {
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, NULL,
+ "send body: filedescriptor (%u) larger than FD_SETSIZE (%u) "
+ "found, you probably need to rebuild Apache with a "
+ "larger FD_SETSIZE", fd, FD_SETSIZE);
+ return 0;
+ }
+#endif
+
+ ap_soft_timeout("send body", r);
+
+ FD_ZERO(&fds);
+ while (!r->connection->aborted) {
+#ifdef NDELAY_PIPE_RETURNS_ZERO
+ /* Contributed by dwd@bell-labs.com for UTS 2.1.2, where the fcntl */
+ /* O_NDELAY flag causes read to return 0 when there's nothing */
+ /* available when reading from a pipe. That makes it tricky */
+ /* to detect end-of-file :-(. This stupid bug is even documented */
+ /* in the read(2) man page where it says that everything but */
+ /* pipes return -1 and EAGAIN. That makes it a feature, right? */
+ int afterselect = 0;
+#endif
+ if ((length > 0) && (total_bytes_sent + IOBUFSIZE) > length)
+ len = length - total_bytes_sent;
+ else
+ len = IOBUFSIZE;
+
+ do {
+ n = ap_bread(fb, buf, len);
+#ifdef NDELAY_PIPE_RETURNS_ZERO
+ if ((n > 0) || (n == 0 && afterselect))
+ break;
+#else
+ if (n >= 0)
+ break;
+#endif
+ if (r->connection->aborted)
+ break;
+ if (n < 0 && errno != EAGAIN)
+ break;
+
+ /* we need to block, so flush the output first */
+ if (ap_bflush(r->connection->client) < 0) {
+ ap_log_rerror(APLOG_MARK, APLOG_INFO, r,
+ "client stopped connection before send body completed");
+ ap_bsetflag(r->connection->client, B_EOUT, 1);
+ r->connection->aborted = 1;
+ break;
+ }
+ FD_SET(fd, &fds);
+ /*
+ * we don't care what select says, we might as well loop back
+ * around and try another read
+ */
+ ap_select(fd + 1, &fds, NULL, NULL, NULL);
+#ifdef NDELAY_PIPE_RETURNS_ZERO
+ afterselect = 1;
+#endif
+ } while (!r->connection->aborted);
+
+ if (n < 1 || r->connection->aborted) {
+ break;
+ }
+ o = 0;
+
+ while (n && !r->connection->aborted) {
+ w = ap_bwrite(r->connection->client, &buf[o], n);
+ if (w > 0) {
+ ap_reset_timeout(r); /* reset timeout after successful write */
+ total_bytes_sent += w;
+ n -= w;
+ o += w;
+ }
+ else if (w < 0) {
+ if (!r->connection->aborted) {
+ ap_log_rerror(APLOG_MARK, APLOG_INFO, r,
+ "client stopped connection before send body completed");
+ ap_bsetflag(r->connection->client, B_EOUT, 1);
+ r->connection->aborted = 1;
+ }
+ break;
+ }
+ }
+ }
+
+ ap_kill_timeout(r);
+ SET_BYTES_SENT(r);
+ return total_bytes_sent;
+}
+
+
+
+/* The code writes MMAP_SEGMENT_SIZE bytes at a time. This is due to Apache's
+ * timeout model, which is a timeout per-write rather than a time for the
+ * entire transaction to complete. Essentially this should be small enough
+ * so that in one Timeout period, your slowest clients should be reasonably
+ * able to receive this many bytes.
+ *
+ * To take advantage of zero-copy TCP under Solaris 2.6 this should be a
+ * multiple of 16k. (And you need a SunATM2.0 network card.)
+ */
+#ifndef MMAP_SEGMENT_SIZE
+#define MMAP_SEGMENT_SIZE 32768
+#endif
+
+/* send data from an in-memory buffer */
+API_EXPORT(size_t) ap_send_mmap(void *mm, request_rec *r, size_t offset,
+ size_t length)
+{
+ size_t total_bytes_sent = 0;
+ int n, w;
+
+ if (length == 0)
+ return 0;
+
+ ap_soft_timeout("send mmap", r);
+
+ length += offset;
+ while (!r->connection->aborted && offset < length) {
+ if (length - offset > MMAP_SEGMENT_SIZE) {
+ n = MMAP_SEGMENT_SIZE;
+ }
+ else {
+ n = length - offset;
+ }
+
+ while (n && !r->connection->aborted) {
+ w = ap_bwrite(r->connection->client, (char *) mm + offset, n);
+ if (w > 0) {
+ ap_reset_timeout(r); /* reset timeout after successful write */
+ total_bytes_sent += w;
+ n -= w;
+ offset += w;
+ }
+ else if (w < 0) {
+ if (!r->connection->aborted) {
+ ap_log_rerror(APLOG_MARK, APLOG_INFO, r,
+ "client stopped connection before send mmap completed");
+ ap_bsetflag(r->connection->client, B_EOUT, 1);
+ r->connection->aborted = 1;
+ }
+ break;
+ }
+ }
+ }
+
+ ap_kill_timeout(r);
+ SET_BYTES_SENT(r);
+ return total_bytes_sent;
+}
+
+API_EXPORT(int) ap_rputc(int c, request_rec *r)
+{
+ if (r->connection->aborted)
+ return EOF;
+
+ if (ap_bputc(c, r->connection->client) < 0) {
+ if (!r->connection->aborted) {
+ ap_log_rerror(APLOG_MARK, APLOG_INFO, r,
+ "client stopped connection before rputc completed");
+ ap_bsetflag(r->connection->client, B_EOUT, 1);
+ r->connection->aborted = 1;
+ }
+ return EOF;
+ }
+ SET_BYTES_SENT(r);
+ return c;
+}
+
+API_EXPORT(int) ap_rputs(const char *str, request_rec *r)
+{
+ int rcode;
+
+ if (r->connection->aborted)
+ return EOF;
+
+ rcode = ap_bputs(str, r->connection->client);
+ if (rcode < 0) {
+ if (!r->connection->aborted) {
+ ap_log_rerror(APLOG_MARK, APLOG_INFO, r,
+ "client stopped connection before rputs completed");
+ ap_bsetflag(r->connection->client, B_EOUT, 1);
+ r->connection->aborted = 1;
+ }
+ return EOF;
+ }
+ SET_BYTES_SENT(r);
+ return rcode;
+}
+
+API_EXPORT(int) ap_rwrite(const void *buf, int nbyte, request_rec *r)
+{
+ int n;
+
+ if (r->connection->aborted)
+ return -1;
+
+ n = ap_bwrite(r->connection->client, buf, nbyte);
+ if (n < 0) {
+ if (!r->connection->aborted) {
+ ap_log_rerror(APLOG_MARK, APLOG_INFO, r,
+ "client stopped connection before rwrite completed");
+ ap_bsetflag(r->connection->client, B_EOUT, 1);
+ r->connection->aborted = 1;
+ }
+ return -1;
+ }
+ SET_BYTES_SENT(r);
+ return n;
+}
+
+API_EXPORT(int) ap_vrprintf(request_rec *r, const char *fmt, va_list ap)
+{
+ int n;
+
+ if (r->connection->aborted)
+ return -1;
+
+ n = ap_vbprintf(r->connection->client, fmt, ap);
+
+ if (n < 0) {
+ if (!r->connection->aborted) {
+ ap_log_rerror(APLOG_MARK, APLOG_INFO, r,
+ "client stopped connection before vrprintf completed");
+ ap_bsetflag(r->connection->client, B_EOUT, 1);
+ r->connection->aborted = 1;
+ }
+ return -1;
+ }
+ SET_BYTES_SENT(r);
+ return n;
+}
+
+API_EXPORT(int) ap_rprintf(request_rec *r, const char *fmt,...)
+{
+ va_list vlist;
+ int n;
+
+ if (r->connection->aborted)
+ return -1;
+
+ va_start(vlist, fmt);
+ n = ap_vbprintf(r->connection->client, fmt, vlist);
+ va_end(vlist);
+
+ if (n < 0) {
+ if (!r->connection->aborted) {
+ ap_log_rerror(APLOG_MARK, APLOG_INFO, r,
+ "client stopped connection before rprintf completed");
+ ap_bsetflag(r->connection->client, B_EOUT, 1);
+ r->connection->aborted = 1;
+ }
+ return -1;
+ }
+ SET_BYTES_SENT(r);
+ return n;
+}
+
+API_EXPORT_NONSTD(int) ap_rvputs(request_rec *r,...)
+{
+ va_list args;
+ int i, j, k;
+ const char *x;
+ BUFF *fb = r->connection->client;
+
+ if (r->connection->aborted)
+ return EOF;
+
+ va_start(args, r);
+ for (k = 0;;) {
+ x = va_arg(args, const char *);
+ if (x == NULL)
+ break;
+ j = strlen(x);
+ i = ap_bwrite(fb, x, j);
+ if (i != j) {
+ va_end(args);
+ if (!r->connection->aborted) {
+ ap_log_rerror(APLOG_MARK, APLOG_INFO, r,
+ "client stopped connection before rvputs completed");
+ ap_bsetflag(r->connection->client, B_EOUT, 1);
+ r->connection->aborted = 1;
+ }
+ return EOF;
+ }
+ k += i;
+ }
+ va_end(args);
+
+ SET_BYTES_SENT(r);
+ return k;
+}
+
+API_EXPORT(int) ap_rflush(request_rec *r)
+{
+ if (ap_bflush(r->connection->client) < 0) {
+ if (!r->connection->aborted) {
+ ap_log_rerror(APLOG_MARK, APLOG_INFO, r,
+ "client stopped connection before rflush completed");
+ ap_bsetflag(r->connection->client, B_EOUT, 1);
+ r->connection->aborted = 1;
+ }
+ return EOF;
+ }
+ return 0;
+}
+
+/* We should have named this send_canned_response, since it is used for any
+ * response that can be generated by the server from the request record.
+ * This includes all 204 (no content), 3xx (redirect), 4xx (client error),
+ * and 5xx (server error) messages that have not been redirected to another
+ * handler via the ErrorDocument feature.
+ */
+void ap_send_error_response(request_rec *r, int recursive_error)
+{
+ int status = r->status;
+ int idx = ap_index_of_response(status);
+ char *custom_response;
+ const char *location = ap_table_get(r->headers_out, "Location");
+
+ /*
+ * It's possible that the Location field might be in r->err_headers_out
+ * instead of r->headers_out; use the latter if possible, else the
+ * former.
+ */
+ if (location == NULL) {
+ location = ap_table_get(r->err_headers_out, "Location");
+ }
+ /* We need to special-case the handling of 204 and 304 responses,
+ * since they have specific HTTP requirements and do not include a
+ * message body. Note that being assbackwards here is not an option.
+ */
+ if (status == HTTP_NOT_MODIFIED) {
+ if (!ap_is_empty_table(r->err_headers_out))
+ r->headers_out = ap_overlay_tables(r->pool, r->err_headers_out,
+ r->headers_out);
+ ap_hard_timeout("send 304", r);
+
+ ap_basic_http_header(r);
+ ap_set_keepalive(r);
+
+ ap_table_do((int (*)(void *, const char *, const char *)) ap_send_header_field,
+ (void *) r, r->headers_out,
+ "Connection",
+ "Keep-Alive",
+ "ETag",
+ "Content-Location",
+ "Expires",
+ "Cache-Control",
+ "Vary",
+ "Warning",
+ "WWW-Authenticate",
+ "Proxy-Authenticate",
+ NULL);
+
+ terminate_header(r->connection->client);
+
+ ap_kill_timeout(r);
+ return;
+ }
+
+ if (status == HTTP_NO_CONTENT) {
+ ap_send_http_header(r);
+ ap_finalize_request_protocol(r);
+ return;
+ }
+
+ if (!r->assbackwards) {
+ table *tmp = r->headers_out;
+
+ /* For all HTTP/1.x responses for which we generate the message,
+ * we need to avoid inheriting the "normal status" header fields
+ * that may have been set by the request handler before the
+ * error or redirect, except for Location on external redirects.
+ */
+ r->headers_out = r->err_headers_out;
+ r->err_headers_out = tmp;
+ ap_clear_table(r->err_headers_out);
+
+ if (ap_is_HTTP_REDIRECT(status) || (status == HTTP_CREATED)) {
+ if ((location != NULL) && *location) {
+ ap_table_setn(r->headers_out, "Location", location);
+ }
+ else {
+ location = ""; /* avoids coredump when printing, below */
+ }
+ }
+
+ r->content_language = NULL;
+ r->content_languages = NULL;
+ r->content_encoding = NULL;
+ r->clength = 0;
+ r->content_type = "text/html";
+
+ if ((status == METHOD_NOT_ALLOWED) || (status == NOT_IMPLEMENTED))
+ ap_table_setn(r->headers_out, "Allow", make_allow(r));
+
+ ap_send_http_header(r);
+
+ if (r->header_only) {
+ ap_finalize_request_protocol(r);
+ ap_rflush(r);
+ return;
+ }
+ }
+
+ ap_hard_timeout("send error body", r);
+
+ if ((custom_response = ap_response_code_string(r, idx))) {
+ /*
+ * We have a custom response output. This should only be
+ * a text-string to write back. But if the ErrorDocument
+ * was a local redirect and the requested resource failed
+ * for any reason, the custom_response will still hold the
+ * redirect URL. We don't really want to output this URL
+ * as a text message, so first check the custom response
+ * string to ensure that it is a text-string (using the
+ * same test used in ap_die(), i.e. does it start with a ").
+ * If it doesn't, we've got a recursive error, so find
+ * the original error and output that as well.
+ */
+ if (custom_response[0] == '\"') {
+ ap_rputs(custom_response + 1, r);
+ ap_kill_timeout(r);
+ ap_finalize_request_protocol(r);
+ ap_rflush(r);
+ return;
+ }
+ /*
+ * Redirect failed, so get back the original error
+ */
+ while (r->prev && (r->prev->status != HTTP_OK))
+ r = r->prev;
+ }
+ {
+ const char *title = status_lines[idx];
+ const char *h1;
+ const char *error_notes;
+
+ /* Accept a status_line set by a module, but only if it begins
+ * with the 3 digit status code
+ */
+ if (r->status_line != NULL
+ && strlen(r->status_line) > 4 /* long enough */
+ && ap_isdigit(r->status_line[0])
+ && ap_isdigit(r->status_line[1])
+ && ap_isdigit(r->status_line[2])
+ && ap_isspace(r->status_line[3])
+ && ap_isalnum(r->status_line[4])) {
+ title = r->status_line;
+ }
+
+ /* folks decided they didn't want the error code in the H1 text */
+ h1 = &title[4];
+
+ ap_rvputs(r,
+ DOCTYPE_HTML_2_0
+ "<HTML><HEAD>\n<TITLE>", title,
+ "</TITLE>\n</HEAD><BODY>\n<H1>", h1, "</H1>\n",
+ NULL);
+
+ switch (status) {
+ case HTTP_MOVED_PERMANENTLY:
+ case HTTP_MOVED_TEMPORARILY:
+ case HTTP_TEMPORARY_REDIRECT:
+ ap_rvputs(r, "The document has moved <A HREF=\"",
+ ap_escape_html(r->pool, location), "\">here</A>.<P>\n",
+ NULL);
+ break;
+ case HTTP_SEE_OTHER:
+ ap_rvputs(r, "The answer to your request is located <A HREF=\"",
+ ap_escape_html(r->pool, location), "\">here</A>.<P>\n",
+ NULL);
+ break;
+ case HTTP_USE_PROXY:
+ ap_rvputs(r, "This resource is only accessible "
+ "through the proxy\n",
+ ap_escape_html(r->pool, location),
+ "<BR>\nYou will need to ",
+ "configure your client to use that proxy.<P>\n", NULL);
+ break;
+ case HTTP_PROXY_AUTHENTICATION_REQUIRED:
+ case AUTH_REQUIRED:
+ ap_rputs("This server could not verify that you\n"
+ "are authorized to access the document\n"
+ "requested. Either you supplied the wrong\n"
+ "credentials (e.g., bad password), or your\n"
+ "browser doesn't understand how to supply\n"
+ "the credentials required.<P>\n", r);
+ break;
+ case BAD_REQUEST:
+ ap_rputs("Your browser sent a request that "
+ "this server could not understand.<P>\n", r);
+ if ((error_notes = ap_table_get(r->notes, "error-notes")) != NULL) {
+ ap_rvputs(r, error_notes, "<P>\n", NULL);
+ }
+ break;
+ case HTTP_FORBIDDEN:
+ ap_rvputs(r, "You don't have permission to access ",
+ ap_escape_html(r->pool, r->uri),
+ "\non this server.<P>\n", NULL);
+ break;
+ case NOT_FOUND:
+ ap_rvputs(r, "The requested URL ",
+ ap_escape_html(r->pool, r->uri),
+ " was not found on this server.<P>\n", NULL);
+ break;
+ case METHOD_NOT_ALLOWED:
+ ap_rvputs(r, "The requested method ", r->method,
+ " is not allowed "
+ "for the URL ", ap_escape_html(r->pool, r->uri),
+ ".<P>\n", NULL);
+ break;
+ case NOT_ACCEPTABLE:
+ ap_rvputs(r,
+ "An appropriate representation of the "
+ "requested resource ",
+ ap_escape_html(r->pool, r->uri),
+ " could not be found on this server.<P>\n", NULL);
+ /* fall through */
+ case MULTIPLE_CHOICES:
+ {
+ const char *list;
+ if ((list = ap_table_get(r->notes, "variant-list")))
+ ap_rputs(list, r);
+ }
+ break;
+ case LENGTH_REQUIRED:
+ ap_rvputs(r, "A request of the requested method ", r->method,
+ " requires a valid Content-length.<P>\n", NULL);
+ if ((error_notes = ap_table_get(r->notes, "error-notes")) != NULL) {
+ ap_rvputs(r, error_notes, "<P>\n", NULL);
+ }
+ break;
+ case PRECONDITION_FAILED:
+ ap_rvputs(r, "The precondition on the request for the URL ",
+ ap_escape_html(r->pool, r->uri),
+ " evaluated to false.<P>\n", NULL);
+ break;
+ case HTTP_NOT_IMPLEMENTED:
+ ap_rvputs(r, ap_escape_html(r->pool, r->method), " to ",
+ ap_escape_html(r->pool, r->uri),
+ " not supported.<P>\n", NULL);
+ if ((error_notes = ap_table_get(r->notes, "error-notes")) != NULL) {
+ ap_rvputs(r, error_notes, "<P>\n", NULL);
+ }
+ break;
+ case BAD_GATEWAY:
+ ap_rputs("The proxy server received an invalid\015\012"
+ "response from an upstream server.<P>\015\012", r);
+ if ((error_notes = ap_table_get(r->notes, "error-notes")) != NULL) {
+ ap_rvputs(r, error_notes, "<P>\n", NULL);
+ }
+ break;
+ case VARIANT_ALSO_VARIES:
+ ap_rvputs(r, "A variant for the requested resource\n<PRE>\n",
+ ap_escape_html(r->pool, r->uri),
+ "\n</PRE>\nis itself a negotiable resource. "
+ "This indicates a configuration error.<P>\n", NULL);
+ break;
+ case HTTP_REQUEST_TIME_OUT:
+ ap_rputs("I'm tired of waiting for your request.\n", r);
+ break;
+ case HTTP_GONE:
+ ap_rvputs(r, "The requested resource<BR>",
+ ap_escape_html(r->pool, r->uri),
+ "<BR>\nis no longer available on this server ",
+ "and there is no forwarding address.\n",
+ "Please remove all references to this resource.\n",
+ NULL);
+ break;
+ case HTTP_REQUEST_ENTITY_TOO_LARGE:
+ ap_rvputs(r, "The requested resource<BR>",
+ ap_escape_html(r->pool, r->uri), "<BR>\n",
+ "does not allow request data with ", r->method,
+ " requests, or the amount of data provided in\n",
+ "the request exceeds the capacity limit.\n", NULL);
+ break;
+ case HTTP_REQUEST_URI_TOO_LARGE:
+ ap_rputs("The requested URL's length exceeds the capacity\n"
+ "limit for this server.<P>\n", r);
+ if ((error_notes = ap_table_get(r->notes, "error-notes")) != NULL) {
+ ap_rvputs(r, error_notes, "<P>\n", NULL);
+ }
+ break;
+ case HTTP_UNSUPPORTED_MEDIA_TYPE:
+ ap_rputs("The supplied request data is not in a format\n"
+ "acceptable for processing by this resource.\n", r);
+ break;
+ case HTTP_RANGE_NOT_SATISFIABLE:
+ ap_rputs("None of the range-specifier values in the Range\n"
+ "request-header field overlap the current extent\n"
+ "of the selected resource.\n", r);
+ break;
+ case HTTP_EXPECTATION_FAILED:
+ ap_rvputs(r, "The expectation given in the Expect request-header"
+ "\nfield could not be met by this server.<P>\n"
+ "The client sent<PRE>\n Expect: ",
+ ap_table_get(r->headers_in, "Expect"), "\n</PRE>\n"
+ "but we only allow the 100-continue expectation.\n",
+ NULL);
+ break;
+ case HTTP_UNPROCESSABLE_ENTITY:
+ ap_rputs("The server understands the media type of the\n"
+ "request entity, but was unable to process the\n"
+ "contained instructions.\n", r);
+ break;
+ case HTTP_LOCKED:
+ ap_rputs("The requested resource is currently locked.\n"
+ "The lock must be released or proper identification\n"
+ "given before the method can be applied.\n", r);
+ break;
+ case HTTP_FAILED_DEPENDENCY:
+ ap_rputs("The method could not be performed on the resource\n"
+ "because the requested action depended on another\n"
+ "action and that other action failed.\n", r);
+ break;
+ case HTTP_INSUFFICIENT_STORAGE:
+ ap_rputs("The method could not be performed on the resource\n"
+ "because the server is unable to store the\n"
+ "representation needed to successfully complete the\n"
+ "request. There is insufficient free space left in\n"
+ "your storage allocation.\n", r);
+ break;
+ case HTTP_SERVICE_UNAVAILABLE:
+ ap_rputs("The server is temporarily unable to service your\n"
+ "request due to maintenance downtime or capacity\n"
+ "problems. Please try again later.\n", r);
+ break;
+ case HTTP_GATEWAY_TIME_OUT:
+ ap_rputs("The proxy server did not receive a timely response\n"
+ "from the upstream server.\n", r);
+ break;
+ case HTTP_NOT_EXTENDED:
+ ap_rputs("A mandatory extension policy in the request is not\n"
+ "accepted by the server for this resource.\n", r);
+ break;
+ default: /* HTTP_INTERNAL_SERVER_ERROR */
+ /*
+ * This comparison to expose error-notes could be modified to
+ * use a configuration directive and export based on that
+ * directive. For now "*" is used to designate an error-notes
+ * that is totally safe for any user to see (ie lacks paths,
+ * database passwords, etc.)
+ */
+ if (((error_notes = ap_table_get(r->notes, "error-notes")) != NULL)
+ && (h1 = ap_table_get(r->notes, "verbose-error-to")) != NULL
+ && (strcmp(h1, "*") == 0)) {
+ ap_rvputs(r, error_notes, "<P>\n", NULL);
+ }
+ else {
+ ap_rvputs(r, "The server encountered an internal error or\n"
+ "misconfiguration and was unable to complete\n"
+ "your request.<P>\n"
+ "Please contact the server administrator,\n ",
+ ap_escape_html(r->pool, r->server->server_admin),
+ " and inform them of the time the error occurred,\n"
+ "and anything you might have done that may have\n"
+ "caused the error.<P>\n"
+ "More information about this error may be available\n"
+ "in the server error log.<P>\n", NULL);
+ }
+ /*
+ * It would be nice to give the user the information they need to
+ * fix the problem directly since many users don't have access to
+ * the error_log (think University sites) even though they can easily
+ * get this error by misconfiguring an htaccess file. However, the
+ * error notes tend to include the real file pathname in this case,
+ * which some people consider to be a breach of privacy. Until we
+ * can figure out a way to remove the pathname, leave this commented.
+ *
+ * if ((error_notes = ap_table_get(r->notes, "error-notes")) != NULL) {
+ * ap_rvputs(r, error_notes, "<P>\n", NULL);
+ * }
+ */
+ break;
+ }
+
+ if (recursive_error) {
+ ap_rvputs(r, "<P>Additionally, a ",
+ status_lines[ap_index_of_response(recursive_error)],
+ "\nerror was encountered while trying to use an "
+ "ErrorDocument to handle the request.\n", NULL);
+ }
+ ap_rputs(ap_psignature("<HR>\n", r), r);
+ ap_rputs("</BODY></HTML>\n", r);
+ }
+ ap_kill_timeout(r);
+ ap_finalize_request_protocol(r);
+ ap_rflush(r);
+}
--- /dev/null
+/* ====================================================================
+ * Copyright (c) 1995-1999 The Apache Group. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * 4. The names "Apache Server" and "Apache Group" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission. For written permission, please contact
+ * apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ * nor may "Apache" appear in their names without prior written
+ * permission of the Apache Group.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Group and was originally based
+ * on public domain software written at the National Center for
+ * Supercomputing Applications, University of Illinois, Urbana-Champaign.
+ * For more information on the Apache Group and the Apache HTTP server
+ * project, please see <http://www.apache.org/>.
+ *
+ */
+
+/*
+ * http_request.c: functions to get and process requests
+ *
+ * Rob McCool 3/21/93
+ *
+ * Thoroughly revamped by rst for Apache. NB this file reads
+ * best from the bottom up.
+ *
+ */
+
+#define CORE_PRIVATE
+#include "httpd.h"
+#include "http_config.h"
+#include "http_request.h"
+#include "http_core.h"
+#include "http_protocol.h"
+#include "http_conf_globals.h" /* for ap_extended_status */
+#include "http_log.h"
+#include "http_main.h"
+#include "scoreboard.h"
+#include "fnmatch.h"
+
+/*****************************************************************
+ *
+ * Getting and checking directory configuration. Also checks the
+ * FollowSymlinks and FollowSymOwner stuff, since this is really the
+ * only place that can happen (barring a new mid_dir_walk callout).
+ *
+ * We can't do it as an access_checker module function which gets
+ * called with the final per_dir_config, since we could have a directory
+ * with FollowSymLinks disabled, which contains a symlink to another
+ * with a .htaccess file which turns FollowSymLinks back on --- and
+ * access in such a case must be denied. So, whatever it is that
+ * checks FollowSymLinks needs to know the state of the options as
+ * they change, all the way down.
+ */
+
+/*
+ * We don't want people able to serve up pipes, or unix sockets, or other
+ * scary things. Note that symlink tests are performed later.
+ */
+static int check_safe_file(request_rec *r)
+{
+ if (r->finfo.st_mode == 0 /* doesn't exist */
+ || S_ISDIR(r->finfo.st_mode)
+ || S_ISREG(r->finfo.st_mode)
+ || S_ISLNK(r->finfo.st_mode)) {
+ return OK;
+ }
+ ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
+ "object is not a file, directory or symlink: %s",
+ r->filename);
+ return HTTP_FORBIDDEN;
+}
+
+
+static int check_symlinks(char *d, int opts)
+{
+#if defined(OS2) || defined(WIN32)
+ /* OS/2 doesn't have symlinks */
+ return OK;
+#else
+ struct stat lfi, fi;
+ char *lastp;
+ int res;
+
+ if (opts & OPT_SYM_LINKS)
+ return OK;
+
+ /*
+ * Strip trailing '/', if any, off what we're checking; trailing slashes
+ * make some systems follow symlinks to directories even in lstat().
+ * After we've done the lstat, put it back. Also, don't bother checking
+ * '/' at all...
+ *
+ * Note that we don't have to worry about multiple slashes here because of
+ * no2slash() below...
+ */
+
+ lastp = d + strlen(d) - 1;
+ if (lastp == d)
+ return OK; /* Root directory, '/' */
+
+ if (*lastp == '/')
+ *lastp = '\0';
+ else
+ lastp = NULL;
+
+ res = lstat(d, &lfi);
+
+ if (lastp)
+ *lastp = '/';
+
+ /*
+ * Note that we don't reject accesses to nonexistent files (multiviews or
+ * the like may cons up a way to run the transaction anyway)...
+ */
+
+ if (!(res >= 0) || !S_ISLNK(lfi.st_mode))
+ return OK;
+
+ /* OK, it's a symlink. May still be OK with OPT_SYM_OWNER */
+
+ if (!(opts & OPT_SYM_OWNER))
+ return HTTP_FORBIDDEN;
+
+ if (stat(d, &fi) < 0)
+ return HTTP_FORBIDDEN;
+
+ return (fi.st_uid == lfi.st_uid) ? OK : HTTP_FORBIDDEN;
+
+#endif
+}
+
+/* Dealing with the file system to get PATH_INFO
+ */
+static int get_path_info(request_rec *r)
+{
+ char *cp;
+ char *path = r->filename;
+ char *end = &path[strlen(path)];
+ char *last_cp = NULL;
+ int rv;
+#ifdef HAVE_DRIVE_LETTERS
+ char bStripSlash=1;
+#endif
+
+ if (r->finfo.st_mode) {
+ /* assume path_info already set */
+ return OK;
+ }
+
+#ifdef HAVE_DRIVE_LETTERS
+ /* If the directory is x:\, then we don't want to strip
+ * the trailing slash since x: is not a valid directory.
+ */
+ if (strlen(path) == 3 && path[1] == ':' && path[2] == '/')
+ bStripSlash = 0;
+
+
+ /* If UNC name == //machine/share/, do not
+ * advance over the trailing slash. Any other
+ * UNC name is OK to strip the slash.
+ */
+ cp = end;
+ if (strlen(path) > 2 && path[0] == '/' && path[1] == '/' &&
+ path[2] != '/' && cp[-1] == '/') {
+ char *p;
+ int iCount=0;
+ p = path;
+ while (p = strchr(p,'/')) {
+ p++;
+ iCount++;
+ }
+
+ if (iCount == 4)
+ bStripSlash = 0;
+ }
+
+ if (bStripSlash)
+#endif
+ /* Advance over trailing slashes ... NOT part of filename
+ * if file is not a UNC name (Win32 only).
+ */
+ for (cp = end; cp > path && cp[-1] == '/'; --cp)
+ continue;
+
+
+ while (cp > path) {
+
+ /* See if the pathname ending here exists... */
+
+ *cp = '\0';
+
+ /* We must not stat() filenames that may cause os-specific system
+ * problems, such as "/file/aux" on DOS-abused filesystems.
+ * So pretend that they do not exist by returning an ENOENT error.
+ * This will force us to drop that part of the path and keep
+ * looking back for a "real" file that exists, while still allowing
+ * the "invalid" path parts within the PATH_INFO.
+ */
+ if (!ap_os_is_filename_valid(path)) {
+ errno = ENOENT;
+ rv = -1;
+ }
+ else {
+ errno = 0;
+ rv = stat(path, &r->finfo);
+ }
+
+ if (cp != end)
+ *cp = '/';
+
+ if (!rv) {
+
+ /*
+ * Aha! Found something. If it was a directory, we will search
+ * contents of that directory for a multi_match, so the PATH_INFO
+ * argument starts with the component after that.
+ */
+
+ if (S_ISDIR(r->finfo.st_mode) && last_cp) {
+ r->finfo.st_mode = 0; /* No such file... */
+ cp = last_cp;
+ }
+
+ r->path_info = ap_pstrdup(r->pool, cp);
+ *cp = '\0';
+ return OK;
+ }
+ /* must set this to zero, some stat()s may have corrupted it
+ * even if they returned an error.
+ */
+ r->finfo.st_mode = 0;
+#if defined(ENOENT) && defined(ENOTDIR)
+ if (errno == ENOENT || errno == ENOTDIR) {
+ last_cp = cp;
+
+ while (--cp > path && *cp != '/')
+ continue;
+
+ while (cp > path && cp[-1] == '/')
+ --cp;
+ }
+ else {
+#if defined(EACCES)
+ if (errno != EACCES)
+#endif
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
+ "access to %s failed", r->uri);
+ return HTTP_FORBIDDEN;
+ }
+#else
+#error ENOENT || ENOTDIR not defined; please see the
+#error comments at this line in the source for a workaround.
+ /*
+ * If ENOENT || ENOTDIR is not defined in one of the your OS's
+ * include files, Apache does not know how to check to see why the
+ * stat() of the index file failed; there are cases where it can fail
+ * even though the file exists. This means that it is possible for
+ * someone to get a directory listing of a directory even though
+ * there is an index (eg. index.html) file in it. If you do not have
+ * a problem with this, delete the above #error lines and start the
+ * compile again. If you need to do this, please submit a bug report
+ * from http://www.apache.org/bug_report.html letting us know that
+ * you needed to do this. Please be sure to include the operating
+ * system you are using.
+ */
+ last_cp = cp;
+
+ while (--cp > path && *cp != '/')
+ continue;
+
+ while (cp > path && cp[-1] == '/')
+ --cp;
+#endif /* ENOENT && ENOTDIR */
+ }
+ return OK;
+}
+
+static int directory_walk(request_rec *r)
+{
+ core_server_config *sconf = ap_get_module_config(r->server->module_config,
+ &core_module);
+ void *per_dir_defaults = r->server->lookup_defaults;
+ void **sec = (void **) sconf->sec->elts;
+ int num_sec = sconf->sec->nelts;
+ char *test_filename;
+ char *test_dirname;
+ int res;
+ unsigned i, num_dirs, iStart;
+ int j, test_filename_len;
+
+ /*
+ * Are we dealing with a file? If not, we can (hopefuly) safely assume we
+ * have a handler that doesn't require one, but for safety's sake, and so
+ * we have something find_types() can get something out of, fake one. But
+ * don't run through the directory entries.
+ */
+
+ if (r->filename == NULL) {
+ r->filename = ap_pstrdup(r->pool, r->uri);
+ r->finfo.st_mode = 0; /* Not really a file... */
+ r->per_dir_config = per_dir_defaults;
+
+ return OK;
+ }
+
+ /*
+ * Go down the directory hierarchy. Where we have to check for symlinks,
+ * do so. Where a .htaccess file has permission to override anything,
+ * try to find one. If either of these things fails, we could poke
+ * around, see why, and adjust the lookup_rec accordingly --- this might
+ * save us a call to get_path_info (with the attendant stat()s); however,
+ * for the moment, that's not worth the trouble.
+ *
+ * Fake filenames (i.e. proxy:) only match Directory sections.
+ */
+
+ if (!ap_os_is_path_absolute(r->filename))
+ {
+ void *this_conf, *entry_config;
+ core_dir_config *entry_core;
+ char *entry_dir;
+
+ for (j = 0; j < num_sec; ++j) {
+
+ entry_config = sec[j];
+
+ entry_core = (core_dir_config *)
+ ap_get_module_config(entry_config, &core_module);
+ entry_dir = entry_core->d;
+
+ this_conf = NULL;
+ if (entry_core->r) {
+ if (!ap_regexec(entry_core->r, r->filename, 0, NULL, 0))
+ this_conf = entry_config;
+ }
+ else if (entry_core->d_is_fnmatch) {
+ if (!ap_fnmatch(entry_dir, r->filename, 0))
+ this_conf = entry_config;
+ }
+ else if (!strncmp(r->filename, entry_dir, strlen(entry_dir)))
+ this_conf = entry_config;
+
+ if (this_conf)
+ per_dir_defaults = ap_merge_per_dir_configs(r->pool,
+ per_dir_defaults,
+ this_conf);
+ }
+
+ r->per_dir_config = per_dir_defaults;
+
+ return OK;
+ }
+
+ r->filename = ap_os_case_canonical_filename(r->pool, r->filename);
+
+ res = get_path_info(r);
+ if (res != OK) {
+ return res;
+ }
+
+ r->filename = ap_os_canonical_filename(r->pool, r->filename);
+
+ test_filename = ap_pstrdup(r->pool, r->filename);
+
+ ap_no2slash(test_filename);
+ num_dirs = ap_count_dirs(test_filename);
+
+ if (!ap_os_is_filename_valid(r->filename)) {
+ ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
+ "Filename is not valid: %s", r->filename);
+ return HTTP_FORBIDDEN;
+ }
+
+ if ((res = check_safe_file(r))) {
+ return res;
+ }
+
+ test_filename_len = strlen(test_filename);
+ if (test_filename[test_filename_len - 1] == '/')
+ --num_dirs;
+
+ if (S_ISDIR(r->finfo.st_mode))
+ ++num_dirs;
+
+ /*
+ * We will use test_dirname as scratch space while we build directory
+ * names during the walk. Profiling shows directory_walk to be a busy
+ * function so we try to avoid allocating lots of extra memory here.
+ * We need 2 extra bytes, one for trailing \0 and one because
+ * make_dirstr_prefix will add potentially one extra /.
+ */
+ test_dirname = ap_palloc(r->pool, test_filename_len + 2);
+
+ iStart = 1;
+#ifdef WIN32
+ /* If the name is a UNC name, then do not walk through the
+ * machine and share name (e.g. \\machine\share\)
+ */
+ if (num_dirs > 3 && test_filename[0] == '/' && test_filename[1] == '/')
+ iStart = 4;
+#endif
+
+ /* j keeps track of which section we're on, see core_reorder_directories */
+ j = 0;
+ for (i = iStart; i <= num_dirs; ++i) {
+ int overrides_here;
+ core_dir_config *core_dir = (core_dir_config *)
+ ap_get_module_config(per_dir_defaults, &core_module);
+
+ /*
+ * XXX: this could be made faster by only copying the next component
+ * rather than copying the entire thing all over.
+ */
+ ap_make_dirstr_prefix(test_dirname, test_filename, i);
+
+ /*
+ * Do symlink checks first, because they are done with the
+ * permissions appropriate to the *parent* directory...
+ */
+
+ if ((res = check_symlinks(test_dirname, core_dir->opts))) {
+ ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
+ "Symbolic link not allowed: %s", test_dirname);
+ return res;
+ }
+
+ /*
+ * Begin *this* level by looking for matching <Directory> sections
+ * from access.conf.
+ */
+
+ for (; j < num_sec; ++j) {
+ void *entry_config = sec[j];
+ core_dir_config *entry_core;
+ char *entry_dir;
+ void *this_conf;
+
+ entry_core = (core_dir_config *)
+ ap_get_module_config(entry_config, &core_module);
+ entry_dir = entry_core->d;
+
+ if (entry_core->r
+ || !ap_os_is_path_absolute(entry_dir)
+ || entry_core->d_components > i)
+ break;
+
+ this_conf = NULL;
+ if (entry_core->d_is_fnmatch) {
+ if (!ap_fnmatch(entry_dir, test_dirname, FNM_PATHNAME)) {
+ this_conf = entry_config;
+ }
+ }
+ else if (!strcmp(test_dirname, entry_dir))
+ this_conf = entry_config;
+
+ if (this_conf) {
+ per_dir_defaults = ap_merge_per_dir_configs(r->pool,
+ per_dir_defaults,
+ this_conf);
+ core_dir = (core_dir_config *)
+ ap_get_module_config(per_dir_defaults, &core_module);
+ }
+ }
+ overrides_here = core_dir->override;
+
+ /* If .htaccess files are enabled, check for one. */
+
+ if (overrides_here) {
+ void *htaccess_conf = NULL;
+
+ res = ap_parse_htaccess(&htaccess_conf, r, overrides_here,
+ ap_pstrdup(r->pool, test_dirname),
+ sconf->access_name);
+ if (res)
+ return res;
+
+ if (htaccess_conf) {
+ per_dir_defaults = ap_merge_per_dir_configs(r->pool,
+ per_dir_defaults,
+ htaccess_conf);
+ r->per_dir_config = per_dir_defaults;
+ }
+ }
+ }
+
+ /*
+ * There's two types of IS_SPECIAL sections (see http_core.c), and we've
+ * already handled the proxy:-style stuff. Now we'll deal with the
+ * regexes.
+ */
+ for (; j < num_sec; ++j) {
+ void *entry_config = sec[j];
+ core_dir_config *entry_core;
+
+ entry_core = (core_dir_config *)
+ ap_get_module_config(entry_config, &core_module);
+
+ if (entry_core->r) {
+ if (!ap_regexec(entry_core->r, test_dirname, 0, NULL, REG_NOTEOL)) {
+ per_dir_defaults =
+ ap_merge_per_dir_configs(r->pool, per_dir_defaults,
+ entry_config);
+ }
+ }
+ }
+ r->per_dir_config = per_dir_defaults;
+
+ /*
+ * Symlink permissions are determined by the parent. If the request is
+ * for a directory then applying the symlink test here would use the
+ * permissions of the directory as opposed to its parent. Consider a
+ * symlink pointing to a dir with a .htaccess disallowing symlinks. If
+ * you access /symlink (or /symlink/) you would get a 403 without this
+ * S_ISDIR test. But if you accessed /symlink/index.html, for example,
+ * you would *not* get the 403.
+ */
+ if (!S_ISDIR(r->finfo.st_mode)
+ && (res = check_symlinks(r->filename, ap_allow_options(r)))) {
+ ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
+ "Symbolic link not allowed: %s", r->filename);
+ return res;
+ }
+ return OK; /* Can only "fail" if access denied by the
+ * symlink goop. */
+}
+
+static int location_walk(request_rec *r)
+{
+ core_server_config *sconf = ap_get_module_config(r->server->module_config,
+ &core_module);
+ void *per_dir_defaults = r->per_dir_config;
+ void **url = (void **) sconf->sec_url->elts;
+ int len, num_url = sconf->sec_url->nelts;
+ char *test_location;
+ void *this_conf, *entry_config;
+ core_dir_config *entry_core;
+ char *entry_url;
+ int j;
+
+ if (!num_url) {
+ return OK;
+ }
+
+ /* Location and LocationMatch differ on their behaviour w.r.t. multiple
+ * slashes. Location matches multiple slashes with a single slash,
+ * LocationMatch doesn't. An exception, for backwards brokenness is
+ * absoluteURIs... in which case neither match multiple slashes.
+ */
+ if (r->uri[0] != '/') {
+ test_location = r->uri;
+ }
+ else {
+ test_location = ap_pstrdup(r->pool, r->uri);
+ ap_no2slash(test_location);
+ }
+
+ /* Go through the location entries, and check for matches. */
+
+ /* we apply the directive sections in some order;
+ * should really try them with the most general first.
+ */
+ for (j = 0; j < num_url; ++j) {
+
+ entry_config = url[j];
+
+ entry_core = (core_dir_config *)
+ ap_get_module_config(entry_config, &core_module);
+ entry_url = entry_core->d;
+
+ len = strlen(entry_url);
+
+ this_conf = NULL;
+
+ if (entry_core->r) {
+ if (!ap_regexec(entry_core->r, r->uri, 0, NULL, 0))
+ this_conf = entry_config;
+ }
+ else if (entry_core->d_is_fnmatch) {
+ if (!ap_fnmatch(entry_url, test_location, FNM_PATHNAME)) {
+ this_conf = entry_config;
+ }
+ }
+ else if (!strncmp(test_location, entry_url, len) &&
+ (entry_url[len - 1] == '/' ||
+ test_location[len] == '/' || test_location[len] == '\0'))
+ this_conf = entry_config;
+
+ if (this_conf)
+ per_dir_defaults = ap_merge_per_dir_configs(r->pool,
+ per_dir_defaults, this_conf);
+ }
+ r->per_dir_config = per_dir_defaults;
+
+ return OK;
+}
+
+static int file_walk(request_rec *r)
+{
+ core_dir_config *conf = ap_get_module_config(r->per_dir_config, &core_module);
+ void *per_dir_defaults = r->per_dir_config;
+ void **file = (void **) conf->sec->elts;
+ int num_files = conf->sec->nelts;
+ char *test_file;
+
+ /* get the basename */
+ test_file = strrchr(r->filename, '/');
+ if (test_file == NULL) {
+ test_file = r->filename;
+ }
+ else {
+ ++test_file;
+ }
+
+ /* Go through the file entries, and check for matches. */
+
+ if (num_files) {
+ void *this_conf, *entry_config;
+ core_dir_config *entry_core;
+ char *entry_file;
+ int j;
+
+ /* we apply the directive sections in some order;
+ * should really try them with the most general first.
+ */
+ for (j = 0; j < num_files; ++j) {
+
+ entry_config = file[j];
+
+ entry_core = (core_dir_config *)
+ ap_get_module_config(entry_config, &core_module);
+ entry_file = entry_core->d;
+
+ this_conf = NULL;
+
+ if (entry_core->r) {
+ if (!ap_regexec(entry_core->r, test_file, 0, NULL, 0))
+ this_conf = entry_config;
+ }
+ else if (entry_core->d_is_fnmatch) {
+ if (!ap_fnmatch(entry_file, test_file, FNM_PATHNAME)) {
+ this_conf = entry_config;
+ }
+ }
+ else if (!strcmp(test_file, entry_file)) {
+ this_conf = entry_config;
+ }
+
+ if (this_conf)
+ per_dir_defaults = ap_merge_per_dir_configs(r->pool,
+ per_dir_defaults,
+ this_conf);
+ }
+ r->per_dir_config = per_dir_defaults;
+ }
+ return OK;
+}
+
+/*****************************************************************
+ *
+ * The sub_request mechanism.
+ *
+ * Fns to look up a relative URI from, e.g., a map file or SSI document.
+ * These do all access checks, etc., but don't actually run the transaction
+ * ... use run_sub_req below for that. Also, be sure to use destroy_sub_req
+ * as appropriate if you're likely to be creating more than a few of these.
+ * (An early Apache version didn't destroy the sub_reqs used in directory
+ * indexing. The result, when indexing a directory with 800-odd files in
+ * it, was massively excessive storage allocation).
+ *
+ * Note more manipulation of protocol-specific vars in the request
+ * structure...
+ */
+
+static request_rec *make_sub_request(const request_rec *r)
+{
+ pool *rrp = ap_make_sub_pool(r->pool);
+ request_rec *rr = ap_pcalloc(rrp, sizeof(request_rec));
+
+ rr->pool = rrp;
+ return rr;
+}
+
+API_EXPORT(request_rec *) ap_sub_req_method_uri(const char *method,
+ const char *new_file,
+ const request_rec *r)
+{
+ request_rec *rnew;
+ int res;
+ char *udir;
+
+ rnew = make_sub_request(r);
+ rnew->hostname = r->hostname;
+ rnew->request_time = r->request_time;
+ rnew->connection = r->connection;
+ rnew->server = r->server;
+ rnew->request_config = ap_create_request_config(rnew->pool);
+ rnew->htaccess = r->htaccess;
+ rnew->per_dir_config = r->server->lookup_defaults;
+
+ ap_set_sub_req_protocol(rnew, r);
+
+ /* would be nicer to pass "method" to ap_set_sub_req_protocol */
+ rnew->method = method;
+ rnew->method_number = ap_method_number_of(method);
+
+ if (new_file[0] == '/')
+ ap_parse_uri(rnew, new_file);
+ else {
+ udir = ap_make_dirstr_parent(rnew->pool, r->uri);
+ udir = ap_escape_uri(rnew->pool, udir); /* re-escape it */
+ ap_parse_uri(rnew, ap_make_full_path(rnew->pool, udir, new_file));
+ }
+
+ res = ap_unescape_url(rnew->uri);
+ if (res) {
+ rnew->status = res;
+ return rnew;
+ }
+
+ ap_getparents(rnew->uri);
+
+ if ((res = location_walk(rnew))) {
+ rnew->status = res;
+ return rnew;
+ }
+
+ res = ap_translate_name(rnew);
+ if (res) {
+ rnew->status = res;
+ return rnew;
+ }
+
+ /*
+ * We could be clever at this point, and avoid calling directory_walk,
+ * etc. However, we'd need to test that the old and new filenames contain
+ * the same directory components, so it would require duplicating the
+ * start of translate_name. Instead we rely on the cache of .htaccess
+ * results.
+ *
+ * NB: directory_walk() clears the per_dir_config, so we don't inherit
+ * from location_walk() above
+ */
+
+ if ((res = directory_walk(rnew))
+ || (res = file_walk(rnew))
+ || (res = location_walk(rnew))
+ || ((ap_satisfies(rnew) == SATISFY_ALL
+ || ap_satisfies(rnew) == SATISFY_NOSPEC)
+ ? ((res = ap_check_access(rnew))
+ || (ap_some_auth_required(rnew)
+ && ((res = ap_check_user_id(rnew))
+ || (res = ap_check_auth(rnew)))))
+ : ((res = ap_check_access(rnew))
+ && (!ap_some_auth_required(rnew)
+ || ((res = ap_check_user_id(rnew))
+ || (res = ap_check_auth(rnew)))))
+ )
+ || (res = ap_find_types(rnew))
+ || (res = ap_run_fixups(rnew))
+ ) {
+ rnew->status = res;
+ }
+ return rnew;
+}
+
+API_EXPORT(request_rec *) ap_sub_req_lookup_uri(const char *new_file,
+ const request_rec *r)
+{
+ return ap_sub_req_method_uri("GET", new_file, r);
+}
+
+API_EXPORT(request_rec *) ap_sub_req_lookup_file(const char *new_file,
+ const request_rec *r)
+{
+ request_rec *rnew;
+ int res;
+ char *fdir;
+
+ rnew = make_sub_request(r);
+ rnew->hostname = r->hostname;
+ rnew->request_time = r->request_time;
+ rnew->connection = r->connection;
+ rnew->server = r->server;
+ rnew->request_config = ap_create_request_config(rnew->pool);
+ rnew->htaccess = r->htaccess;
+
+ ap_set_sub_req_protocol(rnew, r);
+ fdir = ap_make_dirstr_parent(rnew->pool, r->filename);
+
+ /*
+ * Check for a special case... if there are no '/' characters in new_file
+ * at all, then we are looking at a relative lookup in the same
+ * directory. That means we won't have to redo directory_walk, and we may
+ * not even have to redo access checks.
+ */
+
+ if (strchr(new_file, '/') == NULL) {
+ char *udir = ap_make_dirstr_parent(rnew->pool, r->uri);
+
+ rnew->uri = ap_make_full_path(rnew->pool, udir, new_file);
+ rnew->filename = ap_make_full_path(rnew->pool, fdir, new_file);
+ ap_parse_uri(rnew, rnew->uri); /* fill in parsed_uri values */
+ if (stat(rnew->filename, &rnew->finfo) < 0) {
+ rnew->finfo.st_mode = 0;
+ }
+
+ if ((res = check_safe_file(rnew))) {
+ rnew->status = res;
+ return rnew;
+ }
+
+ rnew->per_dir_config = r->per_dir_config;
+
+ /*
+ * no matter what, if it's a subdirectory, we need to re-run
+ * directory_walk
+ */
+ if (S_ISDIR(rnew->finfo.st_mode)) {
+ res = directory_walk(rnew);
+ if (!res) {
+ res = file_walk(rnew);
+ }
+ }
+ else {
+ if ((res = check_symlinks(rnew->filename, ap_allow_options(rnew)))) {
+ ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, rnew,
+ "Symbolic link not allowed: %s", rnew->filename);
+ rnew->status = res;
+ return rnew;
+ }
+ /*
+ * do a file_walk, if it doesn't change the per_dir_config then
+ * we know that we don't have to redo all the access checks
+ */
+ if ((res = file_walk(rnew))) {
+ rnew->status = res;
+ return rnew;
+ }
+ if (rnew->per_dir_config == r->per_dir_config) {
+ if ((res = ap_find_types(rnew)) || (res = ap_run_fixups(rnew))) {
+ rnew->status = res;
+ }
+ return rnew;
+ }
+ }
+ }
+ else {
+ /* XXX: @@@: What should be done with the parsed_uri values? */
+ ap_parse_uri(rnew, new_file); /* fill in parsed_uri values */
+ /*
+ * XXX: this should be set properly like it is in the same-dir case
+ * but it's actually sometimes to impossible to do it... because the
+ * file may not have a uri associated with it -djg
+ */
+ rnew->uri = "INTERNALLY GENERATED file-relative req";
+ rnew->filename = ((ap_os_is_path_absolute(new_file)) ?
+ ap_pstrdup(rnew->pool, new_file) :
+ ap_make_full_path(rnew->pool, fdir, new_file));
+ rnew->per_dir_config = r->server->lookup_defaults;
+ res = directory_walk(rnew);
+ if (!res) {
+ res = file_walk(rnew);
+ }
+ }
+
+ if (res
+ || ((ap_satisfies(rnew) == SATISFY_ALL
+ || ap_satisfies(rnew) == SATISFY_NOSPEC)
+ ? ((res = ap_check_access(rnew))
+ || (ap_some_auth_required(rnew)
+ && ((res = ap_check_user_id(rnew))
+ || (res = ap_check_auth(rnew)))))
+ : ((res = ap_check_access(rnew))
+ && (!ap_some_auth_required(rnew)
+ || ((res = ap_check_user_id(rnew))
+ || (res = ap_check_auth(rnew)))))
+ )
+ || (res = ap_find_types(rnew))
+ || (res = ap_run_fixups(rnew))
+ ) {
+ rnew->status = res;
+ }
+ return rnew;
+}
+
+API_EXPORT(int) ap_run_sub_req(request_rec *r)
+{
+#ifndef CHARSET_EBCDIC
+ int retval = ap_invoke_handler(r);
+#else /*CHARSET_EBCDIC*/
+ /* Save the EBCDIC conversion setting of the caller across subrequests */
+ int convert = ap_bgetflag(r->connection->client, B_EBCDIC2ASCII);
+ int retval = ap_invoke_handler(r);
+ ap_bsetflag(r->connection->client, B_EBCDIC2ASCII, convert);
+#endif /*CHARSET_EBCDIC*/
+ ap_finalize_sub_req_protocol(r);
+ return retval;
+}
+
+API_EXPORT(void) ap_destroy_sub_req(request_rec *r)
+{
+ /* Reclaim the space */
+ ap_destroy_pool(r->pool);
+}
+
+/*****************************************************************
+ *
+ * Mainline request processing...
+ */
+
+API_EXPORT(void) ap_die(int type, request_rec *r)
+{
+ int error_index = ap_index_of_response(type);
+ char *custom_response = ap_response_code_string(r, error_index);
+ int recursive_error = 0;
+
+ if (type == DONE) {
+ ap_finalize_request_protocol(r);
+ return;
+ }
+
+ /*
+ * The following takes care of Apache redirects to custom response URLs
+ * Note that if we are already dealing with the response to some other
+ * error condition, we just report on the original error, and give up on
+ * any attempt to handle the other thing "intelligently"...
+ */
+
+ if (r->status != HTTP_OK) {
+ recursive_error = type;
+
+ while (r->prev && (r->prev->status != HTTP_OK))
+ r = r->prev; /* Get back to original error */
+
+ type = r->status;
+ custom_response = NULL; /* Do NOT retry the custom thing! */
+ }
+
+ r->status = type;
+
+ /*
+ * This test is done here so that none of the auth modules needs to know
+ * about proxy authentication. They treat it like normal auth, and then
+ * we tweak the status.
+ */
+ if (r->status == AUTH_REQUIRED && r->proxyreq) {
+ r->status = HTTP_PROXY_AUTHENTICATION_REQUIRED;
+ }
+
+ /*
+ * If we want to keep the connection, be sure that the request body
+ * (if any) has been read.
+ */
+ if ((r->status != HTTP_NOT_MODIFIED) && (r->status != HTTP_NO_CONTENT)
+ && !ap_status_drops_connection(r->status)
+ && r->connection && (r->connection->keepalive != -1)) {
+
+ (void) ap_discard_request_body(r);
+ }
+
+ /*
+ * Two types of custom redirects --- plain text, and URLs. Plain text has
+ * a leading '"', so the URL code, here, is triggered on its absence
+ */
+
+ if (custom_response && custom_response[0] != '"') {
+
+ if (ap_is_url(custom_response)) {
+ /*
+ * The URL isn't local, so lets drop through the rest of this
+ * apache code, and continue with the usual REDIRECT handler.
+ * But note that the client will ultimately see the wrong
+ * status...
+ */
+ r->status = REDIRECT;
+ ap_table_setn(r->headers_out, "Location", custom_response);
+ }
+ else if (custom_response[0] == '/') {
+ const char *error_notes;
+ r->no_local_copy = 1; /* Do NOT send USE_LOCAL_COPY for
+ * error documents! */
+ /*
+ * This redirect needs to be a GET no matter what the original
+ * method was.
+ */
+ ap_table_setn(r->subprocess_env, "REQUEST_METHOD", r->method);
+
+ /*
+ * Provide a special method for modules to communicate
+ * more informative (than the plain canned) messages to us.
+ * Propagate them to ErrorDocuments via the ERROR_NOTES variable:
+ */
+ if ((error_notes = ap_table_get(r->notes, "error-notes")) != NULL) {
+ ap_table_setn(r->subprocess_env, "ERROR_NOTES", error_notes);
+ }
+ r->method = ap_pstrdup(r->pool, "GET");
+ r->method_number = M_GET;
+ ap_internal_redirect(custom_response, r);
+ return;
+ }
+ else {
+ /*
+ * Dumb user has given us a bad url to redirect to --- fake up
+ * dying with a recursive server error...
+ */
+ recursive_error = SERVER_ERROR;
+ ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
+ "Invalid error redirection directive: %s",
+ custom_response);
+ }
+ }
+ ap_send_error_response(r, recursive_error);
+}
+
+static void decl_die(int status, char *phase, request_rec *r)
+{
+ if (status == DECLINED) {
+ ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_CRIT, r,
+ "configuration error: couldn't %s: %s", phase, r->uri);
+ ap_die(SERVER_ERROR, r);
+ }
+ else
+ ap_die(status, r);
+}
+
+API_EXPORT(int) ap_some_auth_required(request_rec *r)
+{
+ /* Is there a require line configured for the type of *this* req? */
+
+ const array_header *reqs_arr = ap_requires(r);
+ require_line *reqs;
+ int i;
+
+ if (!reqs_arr)
+ return 0;
+
+ reqs = (require_line *) reqs_arr->elts;
+
+ for (i = 0; i < reqs_arr->nelts; ++i)
+ if (reqs[i].method_mask & (1 << r->method_number))
+ return 1;
+
+ return 0;
+}
+
+static void process_request_internal(request_rec *r)
+{
+ int access_status;
+
+ /* Ignore embedded %2F's in path for proxy requests */
+ if (!r->proxyreq && r->parsed_uri.path) {
+ access_status = ap_unescape_url(r->parsed_uri.path);
+ if (access_status) {
+ ap_die(access_status, r);
+ return;
+ }
+ }
+
+ ap_getparents(r->uri); /* OK --- shrinking transformations... */
+
+ if ((access_status = location_walk(r))) {
+ ap_die(access_status, r);
+ return;
+ }
+
+ if ((access_status = ap_translate_name(r))) {
+ decl_die(access_status, "translate", r);
+ return;
+ }
+
+ if (!r->proxyreq) {
+ /*
+ * We don't want TRACE to run through the normal handler set, we
+ * handle it specially.
+ */
+ if (r->method_number == M_TRACE) {
+ if ((access_status = ap_send_http_trace(r)))
+ ap_die(access_status, r);
+ else
+ ap_finalize_request_protocol(r);
+ return;
+ }
+ }
+
+ if (r->proto_num > HTTP_VERSION(1,0) && ap_table_get(r->subprocess_env, "downgrade-1.0")) {
+ r->proto_num = HTTP_VERSION(1,0);
+ }
+
+ /*
+ * NB: directory_walk() clears the per_dir_config, so we don't inherit
+ * from location_walk() above
+ */
+
+ if ((access_status = directory_walk(r))) {
+ ap_die(access_status, r);
+ return;
+ }
+
+ if ((access_status = file_walk(r))) {
+ ap_die(access_status, r);
+ return;
+ }
+
+ if ((access_status = location_walk(r))) {
+ ap_die(access_status, r);
+ return;
+ }
+
+ if ((access_status = ap_header_parse(r))) {
+ ap_die(access_status, r);
+ return;
+ }
+
+ switch (ap_satisfies(r)) {
+ case SATISFY_ALL:
+ case SATISFY_NOSPEC:
+ if ((access_status = ap_check_access(r)) != 0) {
+ decl_die(access_status, "check access", r);
+ return;
+ }
+ if (ap_some_auth_required(r)) {
+ if (((access_status = ap_check_user_id(r)) != 0) || !ap_auth_type(r)) {
+ decl_die(access_status, ap_auth_type(r)
+ ? "check user. No user file?"
+ : "perform authentication. AuthType not set!", r);
+ return;
+ }
+ if (((access_status = ap_check_auth(r)) != 0) || !ap_auth_type(r)) {
+ decl_die(access_status, ap_auth_type(r)
+ ? "check access. No groups file?"
+ : "perform authentication. AuthType not set!", r);
+ return;
+ }
+ }
+ break;
+ case SATISFY_ANY:
+ if (((access_status = ap_check_access(r)) != 0) || !ap_auth_type(r)) {
+ if (!ap_some_auth_required(r)) {
+ decl_die(access_status, ap_auth_type(r)
+ ? "check access"
+ : "perform authentication. AuthType not set!", r);
+ return;
+ }
+ if (((access_status = ap_check_user_id(r)) != 0) || !ap_auth_type(r)) {
+ decl_die(access_status, ap_auth_type(r)
+ ? "check user. No user file?"
+ : "perform authentication. AuthType not set!", r);
+ return;
+ }
+ if (((access_status = ap_check_auth(r)) != 0) || !ap_auth_type(r)) {
+ decl_die(access_status, ap_auth_type(r)
+ ? "check access. No groups file?"
+ : "perform authentication. AuthType not set!", r);
+ return;
+ }
+ }
+ break;
+ }
+
+ if (! (r->proxyreq
+ && r->parsed_uri.scheme != NULL
+ && strcmp(r->parsed_uri.scheme, "http") == 0) ) {
+ if ((access_status = ap_find_types(r)) != 0) {
+ decl_die(access_status, "find types", r);
+ return;
+ }
+ }
+
+ if ((access_status = ap_run_fixups(r)) != 0) {
+ ap_die(access_status, r);
+ return;
+ }
+
+ if ((access_status = ap_invoke_handler(r)) != 0) {
+ ap_die(access_status, r);
+ return;
+ }
+
+ /* Take care of little things that need to happen when we're done */
+ ap_finalize_request_protocol(r);
+}
+
+void ap_process_request(request_rec *r)
+{
+ int old_stat;
+
+ if (ap_extended_status)
+ ap_time_process_request(r->connection->child_num, START_PREQUEST);
+
+ process_request_internal(r);
+
+ old_stat = ap_update_child_status(r->connection->child_num,
+ SERVER_BUSY_LOG, r);
+
+ /*
+ * We want to flush the last packet if this isn't a pipelining connection
+ * *before* we start into logging. Suppose that the logging causes a DNS
+ * lookup to occur, which may have a high latency. If we hold off on
+ * this packet, then it'll appear like the link is stalled when really
+ * it's the application that's stalled.
+ */
+ ap_bhalfduplex(r->connection->client);
+ ap_log_transaction(r);
+
+ (void) ap_update_child_status(r->connection->child_num, old_stat, r);
+ if (ap_extended_status)
+ ap_time_process_request(r->connection->child_num, STOP_PREQUEST);
+}
+
+static table *rename_original_env(pool *p, table *t)
+{
+ array_header *env_arr = ap_table_elts(t);
+ table_entry *elts = (table_entry *) env_arr->elts;
+ table *new = ap_make_table(p, env_arr->nalloc);
+ int i;
+
+ for (i = 0; i < env_arr->nelts; ++i) {
+ if (!elts[i].key)
+ continue;
+ ap_table_setn(new, ap_pstrcat(p, "REDIRECT_", elts[i].key, NULL),
+ elts[i].val);
+ }
+
+ return new;
+}
+
+static request_rec *internal_internal_redirect(const char *new_uri, request_rec *r)
+{
+ int access_status;
+ request_rec *new = (request_rec *) ap_pcalloc(r->pool, sizeof(request_rec));
+
+ new->connection = r->connection;
+ new->server = r->server;
+ new->pool = r->pool;
+
+ /*
+ * A whole lot of this really ought to be shared with http_protocol.c...
+ * another missing cleanup. It's particularly inappropriate to be
+ * setting header_only, etc., here.
+ */
+
+ new->method = r->method;
+ new->method_number = r->method_number;
+ ap_parse_uri(new, new_uri);
+ new->request_config = ap_create_request_config(r->pool);
+ new->per_dir_config = r->server->lookup_defaults;
+
+ new->prev = r;
+ r->next = new;
+
+ /* Inherit the rest of the protocol info... */
+
+ new->the_request = r->the_request;
+
+ new->allowed = r->allowed;
+
+ new->status = r->status;
+ new->assbackwards = r->assbackwards;
+ new->header_only = r->header_only;
+ new->protocol = r->protocol;
+ new->proto_num = r->proto_num;
+ new->hostname = r->hostname;
+ new->request_time = r->request_time;
+ new->main = r->main;
+
+ new->headers_in = r->headers_in;
+ new->headers_out = ap_make_table(r->pool, 12);
+ new->err_headers_out = r->err_headers_out;
+ new->subprocess_env = rename_original_env(r->pool, r->subprocess_env);
+ new->notes = ap_make_table(r->pool, 5);
+
+ new->htaccess = r->htaccess;
+ new->no_cache = r->no_cache;
+ new->expecting_100 = r->expecting_100;
+ new->no_local_copy = r->no_local_copy;
+ new->read_length = r->read_length; /* We can only read it once */
+ new->vlist_validator = r->vlist_validator;
+
+ ap_table_setn(new->subprocess_env, "REDIRECT_STATUS",
+ ap_psprintf(r->pool, "%d", r->status));
+
+ /*
+ * XXX: hmm. This is because mod_setenvif and mod_unique_id really need
+ * to do their thing on internal redirects as well. Perhaps this is a
+ * misnamed function.
+ */
+ if ((access_status = ap_run_post_read_request(new))) {
+ ap_die(access_status, new);
+ return NULL;
+ }
+
+ return new;
+}
+
+API_EXPORT(void) ap_internal_redirect(const char *new_uri, request_rec *r)
+{
+ request_rec *new = internal_internal_redirect(new_uri, r);
+ process_request_internal(new);
+}
+
+/* This function is designed for things like actions or CGI scripts, when
+ * using AddHandler, and you want to preserve the content type across
+ * an internal redirect.
+ */
+API_EXPORT(void) ap_internal_redirect_handler(const char *new_uri, request_rec *r)
+{
+ request_rec *new = internal_internal_redirect(new_uri, r);
+ if (r->handler)
+ new->content_type = r->content_type;
+ process_request_internal(new);
+}
+
+/*
+ * Is it the initial main request, which we only get *once* per HTTP request?
+ */
+API_EXPORT(int) ap_is_initial_req(request_rec *r)
+{
+ return
+ (r->main == NULL) /* otherwise, this is a sub-request */
+ &&
+ (r->prev == NULL); /* otherwise, this is an internal redirect */
+}
+
+/*
+ * Function to set the r->mtime field to the specified value if it's later
+ * than what's already there.
+ */
+API_EXPORT(time_t) ap_update_mtime(request_rec *r, time_t dependency_mtime)
+{
+ if (r->mtime < dependency_mtime) {
+ r->mtime = dependency_mtime;
+ }
+ return r->mtime;
+}
--- /dev/null
+-i4 -npsl -di0 -br -nce -d0 -cli0 -npcs -nfc1
+-TBUFF
+-TFILE
+-TTRANS
+-TUINT4
+-T_trans
+-Tallow_options_t
+-Tapache_sfio
+-Tarray_header
+-Tbool_int
+-Tbuf_area
+-Tbuff_struct
+-Tbuffy
+-Tcmd_how
+-Tcmd_parms
+-Tcommand_rec
+-Tcommand_struct
+-Tconn_rec
+-Tcore_dir_config
+-Tcore_server_config
+-Tdir_maker_func
+-Tevent
+-Tglobals_s
+-Thandler_func
+-Thandler_rec
+-Tjoblist_s
+-Tlisten_rec
+-Tmerger_func
+-Tmode_t
+-Tmodule
+-Tmodule_struct
+-Tmutex
+-Tn_long
+-Tother_child_rec
+-Toverrides_t
+-Tparent_score
+-Tpid_t
+-Tpiped_log
+-Tpool
+-Trequest_rec
+-Trequire_line
+-Trlim_t
+-Tscoreboard
+-Tsemaphore
+-Tserver_addr_rec
+-Tserver_rec
+-Tserver_rec_chain
+-Tshort_score
+-Ttable
+-Ttable_entry
+-Tthread
+-Tu_wide_int
+-Tvtime_t
+-Twide_int
--- /dev/null
+-i4 -npsl -di0 -br -nce -d0 -cli0 -npcs -nfc1
+-TBUFF
+-TFILE
+-TTRANS
+-TUINT4
+-T_trans
+-Tallow_options_t
+-Tapache_sfio
+-Tarray_header
+-Tbool_int
+-Tbuf_area
+-Tbuff_struct
+-Tbuffy
+-Tcmd_how
+-Tcmd_parms
+-Tcommand_rec
+-Tcommand_struct
+-Tconn_rec
+-Tcore_dir_config
+-Tcore_server_config
+-Tdir_maker_func
+-Tevent
+-Tglobals_s
+-Thandler_func
+-Thandler_rec
+-Tjoblist_s
+-Tlisten_rec
+-Tmerger_func
+-Tmode_t
+-Tmodule
+-Tmodule_struct
+-Tmutex
+-Tn_long
+-Tother_child_rec
+-Toverrides_t
+-Tparent_score
+-Tpid_t
+-Tpiped_log
+-Tpool
+-Trequest_rec
+-Trequire_line
+-Trlim_t
+-Tscoreboard
+-Tsemaphore
+-Tserver_addr_rec
+-Tserver_rec
+-Tserver_rec_chain
+-Tshort_score
+-Ttable
+-Ttable_entry
+-Tthread
+-Tu_wide_int
+-Tvtime_t
+-Twide_int
--- /dev/null
+# Microsoft Developer Studio Project File - Name="ApacheModuleRewrite" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 5.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=ApacheModuleRewrite - 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 "ApacheModuleRewrite.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 "ApacheModuleRewrite.mak"\
+ CFG="ApacheModuleRewrite - Win32 Release"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "ApacheModuleRewrite - Win32 Release" (based on\
+ "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "ApacheModuleRewrite - Win32 Debug" (based on\
+ "Win32 (x86) Dynamic-Link Library")
+!MESSAGE
+
+# Begin Project
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "ApacheModuleRewrite - 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 ".\ApacheModuleRewriteR"
+# PROP Intermediate_Dir ".\ApacheModuleRewriteR"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /c
+# ADD CPP /nologo /MD /W3 /GX /O2 /I "..\..\include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "NO_DBM_REWRITEMAP" /D "SHARED_MODULE" /D "WIN32_LEAN_AND_MEAN" /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "NDEBUG"
+# ADD RSC /l 0x809 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386
+# ADD LINK32 ..\..\CoreR\ApacheCore.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ws2_32.lib /nologo /subsystem:windows /dll /machine:I386
+
+!ELSEIF "$(CFG)" == "ApacheModuleRewrite - 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 ".\ApacheModuleRewriteD"
+# PROP Intermediate_Dir ".\ApacheModuleRewriteD"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /c
+# ADD CPP /nologo /MDd /W3 /Gm /GX /Zi /Od /I "..\..\include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "NO_DBM_REWRITEMAP" /D "SHARED_MODULE" /D "WIN32_LEAN_AND_MEAN" /YX /FD /c
+# ADD BASE MTL /nologo /D "_DEBUG" /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "_DEBUG"
+# ADD RSC /l 0x809 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386
+# ADD LINK32 ..\..\CoreD\ApacheCore.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ws2_32.lib /nologo /subsystem:windows /dll /debug /machine:I386
+
+!ENDIF
+
+# Begin Target
+
+# Name "ApacheModuleRewrite - Win32 Release"
+# Name "ApacheModuleRewrite - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90"
+# Begin Source File
+
+SOURCE=..\..\modules\standard\mod_rewrite.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\passwd.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl;fi;fd"
+# Begin Source File
+
+SOURCE=.\readdir.h
+# End Source File
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;cnt;rtf;gif;jpg;jpeg;jpe"
+# End Group
+# End Target
+# End Project
--- /dev/null
+# Microsoft Developer Studio Project File - Name="ApacheModuleSpeling" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 5.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=ApacheModuleSpeling - 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 "ApacheModuleSpeling.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 "ApacheModuleSpeling.mak"\
+ CFG="ApacheModuleSpeling - Win32 Release"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "ApacheModuleSpeling - Win32 Release" (based on\
+ "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "ApacheModuleSpeling - Win32 Debug" (based on\
+ "Win32 (x86) Dynamic-Link Library")
+!MESSAGE
+
+# Begin Project
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "ApacheModuleSpeling - 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 ".\ApacheModuleSpelingR"
+# PROP Intermediate_Dir ".\ApacheModuleSpelingR"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /c
+# ADD CPP /nologo /MD /W3 /GX /O2 /I "..\..\include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "SHARED_MODULE" /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "NDEBUG"
+# ADD RSC /l 0x809 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386
+# ADD LINK32 ..\..\CoreR\ApacheCore.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib /nologo /subsystem:windows /dll /machine:I386
+
+!ELSEIF "$(CFG)" == "ApacheModuleSpeling - 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 ".\ApacheModuleSpelingD"
+# PROP Intermediate_Dir ".\ApacheModuleSpelingD"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /c
+# ADD CPP /nologo /MDd /W3 /Gm /GX /Zi /Od /I "..\..\include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "SHARED_MODULE" /YX /FD /c
+# ADD BASE MTL /nologo /D "_DEBUG" /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "_DEBUG"
+# ADD RSC /l 0x809 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386
+# ADD LINK32 ..\..\CoreD\ApacheCore.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib /nologo /subsystem:windows /dll /debug /machine:I386
+
+!ENDIF
+
+# Begin Target
+
+# Name "ApacheModuleSpeling - Win32 Release"
+# Name "ApacheModuleSpeling - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90"
+# Begin Source File
+
+SOURCE=..\..\modules\standard\mod_speling.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl;fi;fd"
+# Begin Source File
+
+SOURCE=.\readdir.h
+# End Source File
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;cnt;rtf;gif;jpg;jpeg;jpe"
+# End Group
+# End Target
+# End Project
--- /dev/null
+-i4 -npsl -di0 -br -nce -d0 -cli0 -npcs -nfc1
+-TBUFF
+-TFILE
+-TTRANS
+-TUINT4
+-T_trans
+-Tallow_options_t
+-Tapache_sfio
+-Tarray_header
+-Tbool_int
+-Tbuf_area
+-Tbuff_struct
+-Tbuffy
+-Tcmd_how
+-Tcmd_parms
+-Tcommand_rec
+-Tcommand_struct
+-Tconn_rec
+-Tcore_dir_config
+-Tcore_server_config
+-Tdir_maker_func
+-Tevent
+-Tglobals_s
+-Thandler_func
+-Thandler_rec
+-Tjoblist_s
+-Tlisten_rec
+-Tmerger_func
+-Tmode_t
+-Tmodule
+-Tmodule_struct
+-Tmutex
+-Tn_long
+-Tother_child_rec
+-Toverrides_t
+-Tparent_score
+-Tpid_t
+-Tpiped_log
+-Tpool
+-Trequest_rec
+-Trequire_line
+-Trlim_t
+-Tscoreboard
+-Tsemaphore
+-Tserver_addr_rec
+-Tserver_rec
+-Tserver_rec_chain
+-Tshort_score
+-Ttable
+-Ttable_entry
+-Tthread
+-Tu_wide_int
+-Tvtime_t
+-Twide_int
--- /dev/null
+# Microsoft Developer Studio Project File - Name="ApacheModuleCERNMeta" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 5.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=ApacheModuleCERNMeta - 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 "ApacheModuleCERNMeta.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 "ApacheModuleCERNMeta.mak"\
+ CFG="ApacheModuleCERNMeta - Win32 Release"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "ApacheModuleCERNMeta - Win32 Release" (based on\
+ "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "ApacheModuleCERNMeta - Win32 Debug" (based on\
+ "Win32 (x86) Dynamic-Link Library")
+!MESSAGE
+
+# Begin Project
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "ApacheModuleCERNMeta - 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 ".\ApacheModuleCERNMetaR"
+# PROP Intermediate_Dir ".\ApacheModuleCERNMetaR"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /c
+# ADD CPP /nologo /MD /W3 /GX /O2 /I "..\..\include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "SHARED_MODULE" /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "NDEBUG"
+# ADD RSC /l 0x809 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386
+# ADD LINK32 ..\..\CoreR\ApacheCore.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib /nologo /subsystem:windows /dll /machine:I386
+
+!ELSEIF "$(CFG)" == "ApacheModuleCERNMeta - 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 ".\ApacheModuleCERNMetaD"
+# PROP Intermediate_Dir ".\ApacheModuleCERNMetaD"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /c
+# ADD CPP /nologo /MDd /W3 /Gm /GX /Zi /Od /I "..\..\include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "SHARED_MODULE" /YX /FD /c
+# ADD BASE MTL /nologo /D "_DEBUG" /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "_DEBUG"
+# ADD RSC /l 0x809 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386
+# ADD LINK32 ..\..\CoreD\ApacheCore.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib /nologo /subsystem:windows /dll /debug /machine:I386
+
+!ENDIF
+
+# Begin Target
+
+# Name "ApacheModuleCERNMeta - Win32 Release"
+# Name "ApacheModuleCERNMeta - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90"
+# Begin Source File
+
+SOURCE=..\..\modules\standard\mod_cern_meta.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl;fi;fd"
+# Begin Source File
+
+SOURCE=.\readdir.h
+# End Source File
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;cnt;rtf;gif;jpg;jpeg;jpe"
+# End Group
+# End Target
+# End Project
--- /dev/null
+# Microsoft Developer Studio Project File - Name="ApacheModuleExpires" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 5.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=ApacheModuleExpires - 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 "ApacheModuleExpires.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 "ApacheModuleExpires.mak"\
+ CFG="ApacheModuleExpires - Win32 Release"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "ApacheModuleExpires - Win32 Release" (based on\
+ "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "ApacheModuleExpires - Win32 Debug" (based on\
+ "Win32 (x86) Dynamic-Link Library")
+!MESSAGE
+
+# Begin Project
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "ApacheModuleExpires - 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 ".\ApacheModuleExpiresR"
+# PROP Intermediate_Dir ".\ApacheModuleExpiresR"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /c
+# ADD CPP /nologo /MD /W3 /GX /O2 /I "..\..\include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "SHARED_MODULE" /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "NDEBUG"
+# ADD RSC /l 0x809 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386
+# ADD LINK32 ..\..\CoreR\ApacheCore.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib /nologo /subsystem:windows /dll /machine:I386
+
+!ELSEIF "$(CFG)" == "ApacheModuleExpires - 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 ".\ApacheModuleExpiresD"
+# PROP Intermediate_Dir ".\ApacheModuleExpiresD"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /c
+# ADD CPP /nologo /MDd /W3 /Gm /GX /Zi /Od /I "..\..\include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "SHARED_MODULE" /YX /FD /c
+# ADD BASE MTL /nologo /D "_DEBUG" /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "_DEBUG"
+# ADD RSC /l 0x809 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386
+# ADD LINK32 ..\..\CoreD\ApacheCore.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib /nologo /subsystem:windows /dll /debug /machine:I386
+
+!ENDIF
+
+# Begin Target
+
+# Name "ApacheModuleExpires - Win32 Release"
+# Name "ApacheModuleExpires - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90"
+# Begin Source File
+
+SOURCE=..\..\modules\standard\mod_expires.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl;fi;fd"
+# Begin Source File
+
+SOURCE=.\readdir.h
+# End Source File
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;cnt;rtf;gif;jpg;jpeg;jpe"
+# End Group
+# End Target
+# End Project
--- /dev/null
+# Microsoft Developer Studio Project File - Name="ApacheModuleHeaders" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 5.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=ApacheModuleHeaders - 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 "ApacheModuleHeaders.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 "ApacheModuleHeaders.mak"\
+ CFG="ApacheModuleHeaders - Win32 Release"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "ApacheModuleHeaders - Win32 Release" (based on\
+ "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "ApacheModuleHeaders - Win32 Debug" (based on\
+ "Win32 (x86) Dynamic-Link Library")
+!MESSAGE
+
+# Begin Project
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "ApacheModuleHeaders - 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 ".\ApacheModuleHeadersR"
+# PROP Intermediate_Dir ".\ApacheModuleHeadersR"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /c
+# ADD CPP /nologo /MD /W3 /GX /O2 /I "..\..\include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "SHARED_MODULE" /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "NDEBUG"
+# ADD RSC /l 0x809 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386
+# ADD LINK32 ..\..\CoreR\ApacheCore.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib /nologo /subsystem:windows /dll /machine:I386
+
+!ELSEIF "$(CFG)" == "ApacheModuleHeaders - 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 ".\ApacheModuleHeadersD"
+# PROP Intermediate_Dir ".\ApacheModuleHeadersD"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /c
+# ADD CPP /nologo /MDd /W3 /Gm /GX /Zi /Od /I "..\..\include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "SHARED_MODULE" /YX /FD /c
+# ADD BASE MTL /nologo /D "_DEBUG" /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "_DEBUG"
+# ADD RSC /l 0x809 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386
+# ADD LINK32 ..\..\CoreD\ApacheCore.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib /nologo /subsystem:windows /dll /debug /machine:I386
+
+!ENDIF
+
+# Begin Target
+
+# Name "ApacheModuleHeaders - Win32 Release"
+# Name "ApacheModuleHeaders - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90"
+# Begin Source File
+
+SOURCE=..\..\modules\standard\mod_headers.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl;fi;fd"
+# Begin Source File
+
+SOURCE=.\readdir.h
+# End Source File
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;cnt;rtf;gif;jpg;jpeg;jpe"
+# End Group
+# End Target
+# End Project
--- /dev/null
+# Microsoft Developer Studio Project File - Name="ApacheModuleUserTrack" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 5.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=ApacheModuleUserTrack - 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 "ApacheModuleUserTrack.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 "ApacheModuleUserTrack.mak"\
+ CFG="ApacheModuleUserTrack - Win32 Release"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "ApacheModuleUserTrack - Win32 Release" (based on\
+ "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "ApacheModuleUserTrack - Win32 Debug" (based on\
+ "Win32 (x86) Dynamic-Link Library")
+!MESSAGE
+
+# Begin Project
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "ApacheModuleUserTrack - 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 ".\ApacheModuleUserTrackR"
+# PROP Intermediate_Dir ".\ApacheModuleUserTrackR"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /c
+# ADD CPP /nologo /MD /W3 /GX /O2 /I "..\..\include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "SHARED_MODULE" /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "NDEBUG"
+# ADD RSC /l 0x809 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386
+# ADD LINK32 ..\..\CoreR\ApacheCore.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib /nologo /subsystem:windows /dll /machine:I386
+
+!ELSEIF "$(CFG)" == "ApacheModuleUserTrack - 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 ".\ApacheModuleUserTrackD"
+# PROP Intermediate_Dir ".\ApacheModuleUserTrackD"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /c
+# ADD CPP /nologo /MDd /W3 /Gm /GX /Zi /Od /I "..\..\include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "SHARED_MODULE" /YX /FD /c
+# ADD BASE MTL /nologo /D "_DEBUG" /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x809 /d "_DEBUG"
+# ADD RSC /l 0x809 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386
+# ADD LINK32 ..\..\CoreD\ApacheCore.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib /nologo /subsystem:windows /dll /debug /machine:I386
+
+!ENDIF
+
+# Begin Target
+
+# Name "ApacheModuleUserTrack - Win32 Release"
+# Name "ApacheModuleUserTrack - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90"
+# Begin Source File
+
+SOURCE=..\..\modules\standard\mod_usertrack.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl;fi;fd"
+# Begin Source File
+
+SOURCE=.\readdir.h
+# End Source File
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;cnt;rtf;gif;jpg;jpeg;jpe"
+# End Group
+# End Target
+# End Project
--- /dev/null
+-i4 -npsl -di0 -br -nce -d0 -cli0 -npcs -nfc1
+-TBUFF
+-TFILE
+-TTRANS
+-TUINT4
+-T_trans
+-Tallow_options_t
+-Tapache_sfio
+-Tarray_header
+-Tbool_int
+-Tbuf_area
+-Tbuff_struct
+-Tbuffy
+-Tcmd_how
+-Tcmd_parms
+-Tcommand_rec
+-Tcommand_struct
+-Tconn_rec
+-Tcore_dir_config
+-Tcore_server_config
+-Tdir_maker_func
+-Tevent
+-Tglobals_s
+-Thandler_func
+-Thandler_rec
+-Tjoblist_s
+-Tlisten_rec
+-Tmerger_func
+-Tmode_t
+-Tmodule
+-Tmodule_struct
+-Tmutex
+-Tn_long
+-Tother_child_rec
+-Toverrides_t
+-Tparent_score
+-Tpid_t
+-Tpiped_log
+-Tpool
+-Trequest_rec
+-Trequire_line
+-Trlim_t
+-Tscoreboard
+-Tsemaphore
+-Tserver_addr_rec
+-Tserver_rec
+-Tserver_rec_chain
+-Tshort_score
+-Ttable
+-Ttable_entry
+-Tthread
+-Tu_wide_int
+-Tvtime_t
+-Twide_int
--- /dev/null
+-i4 -npsl -di0 -br -nce -d0 -cli0 -npcs -nfc1
+-TBUFF
+-TFILE
+-TTRANS
+-TUINT4
+-T_trans
+-Tallow_options_t
+-Tapache_sfio
+-Tarray_header
+-Tbool_int
+-Tbuf_area
+-Tbuff_struct
+-Tbuffy
+-Tcmd_how
+-Tcmd_parms
+-Tcommand_rec
+-Tcommand_struct
+-Tconn_rec
+-Tcore_dir_config
+-Tcore_server_config
+-Tdir_maker_func
+-Tevent
+-Tglobals_s
+-Thandler_func
+-Thandler_rec
+-Tjoblist_s
+-Tlisten_rec
+-Tmerger_func
+-Tmode_t
+-Tmodule
+-Tmodule_struct
+-Tmutex
+-Tn_long
+-Tother_child_rec
+-Toverrides_t
+-Tparent_score
+-Tpid_t
+-Tpiped_log
+-Tpool
+-Trequest_rec
+-Trequire_line
+-Trlim_t
+-Tscoreboard
+-Tsemaphore
+-Tserver_addr_rec
+-Tserver_rec
+-Tserver_rec_chain
+-Tshort_score
+-Ttable
+-Ttable_entry
+-Tthread
+-Tu_wide_int
+-Tvtime_t
+-Twide_int
--- /dev/null
+/* ====================================================================
+ * Copyright (c) 1995-1999 The Apache Group. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * 4. The names "Apache Server" and "Apache Group" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission. For written permission, please contact
+ * apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ * nor may "Apache" appear in their names without prior written
+ * permission of the Apache Group.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Group and was originally based
+ * on public domain software written at the National Center for
+ * Supercomputing Applications, University of Illinois, Urbana-Champaign.
+ * For more information on the Apache Group and the Apache HTTP server
+ * project, please see <http://www.apache.org/>.
+ *
+ */
+
+#ifdef _OSD_POSIX
+#include "httpd.h"
+#include "http_config.h"
+#include "http_log.h"
+#include <ctype.h>
+#include <sys/utsname.h>
+
+#define ACCT_LEN 8
+#define USER_LEN 8
+
+static const char *bs2000_account = NULL;
+typedef enum
+{
+ bs2_unknown, /* not initialized yet. */
+ bs2_noFORK, /* no fork() because -X flag was specified */
+ bs2_FORK, /* only fork() because uid != 0 */
+ bs2_FORK_RINI, /* prior to A17, regular fork() and _rini() was used. */
+ bs2_RFORK_RINI, /* for A17, use of _rfork() and _rini() was required */
+ bs2_UFORK /* As of A18, the new ufork() is used. */
+} bs2_ForkType;
+
+static bs2_ForkType forktype = bs2_unknown;
+
+
+static void ap_pad(char *dest, size_t size, char ch)
+{
+ int i = strlen(dest); /* Leave space for trailing '\0' */
+
+ while (i < size-1)
+ dest[i++] = ch;
+
+ dest[size-1] = '\0'; /* Guarantee for trailing '\0' */
+}
+
+static void ap_str_toupper(char *str)
+{
+ while (*str) {
+ *str = ap_toupper(*str);
+ ++str;
+ }
+}
+
+/* Determine the method for forking off a child in such a way as to
+ * set both the POSIX and BS2000 user id's to the unprivileged user.
+ */
+static bs2_ForkType os_forktype(void)
+{
+ struct utsname os_version;
+
+ /* have we checked the OS version before? If yes return the previous
+ * result - the OS release isn't going to change suddenly!
+ */
+ if (forktype != bs2_unknown) {
+ return forktype;
+ }
+
+ /* If the user is unprivileged, use the normal fork() only. */
+ if (getuid() != 0) {
+ return forktype = bs2_FORK;
+ }
+
+ if (uname(&os_version) < 0)
+ {
+ ap_log_error(APLOG_MARK, APLOG_ALERT, NULL,
+ "uname() failed - aborting.");
+ exit(APEXIT_CHILDFATAL);
+ }
+
+ /*
+ * Old BS2000/OSD versions (before XPG4 SPEC1170) don't work with Apache.
+ * Anyway, simply return a fork().
+ */
+ if (strcmp(os_version.release, "01.0A") == 0 ||
+ strcmp(os_version.release, "02.0A") == 0 ||
+ strcmp(os_version.release, "02.1A") == 0)
+ {
+ ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, NULL,
+ "Error: unsupported OS version. "
+ "You may encounter problems.");
+ forktype = bs2_FORK;
+ }
+
+ /* The following versions are special:
+ * OS versions before A17 needs regular fork() and _rini().
+ * A17 requires _rfork() and _rini(),
+ * and later versions need ufork().
+ */
+ else if (strcmp(os_version.release, "01.1A") == 0 ||
+ strcmp(os_version.release, "03.0A") == 0 ||
+ strcmp(os_version.release, "03.1A") == 0 ||
+ strcmp(os_version.release, "04.0A") == 0)
+ {
+ if (strcmp (os_version.version, "A18") >= 0)
+ forktype = bs2_UFORK;
+
+ else if (strcmp (os_version.version, "A17") < 0)
+ forktype = bs2_FORK_RINI;
+
+ else
+ forktype = bs2_RFORK_RINI;
+ }
+
+ /* All later OS versions will hopefully use ufork() only ;-) */
+ else
+ forktype = bs2_UFORK;
+
+ return forktype;
+}
+
+
+
+/* This routine is called by http_core for the BS2000Account directive */
+/* It stores the account name for later use */
+const char *os_set_account(pool *p, const char *account)
+{
+ char account_temp[ACCT_LEN+1];
+
+ ap_cpystrn(account_temp, account, sizeof account_temp);
+
+ /* Make account all upper case */
+ ap_str_toupper(account_temp);
+
+ /* Pad to length 8 */
+ ap_pad(account_temp, sizeof account_temp, ' ');
+
+ bs2000_account = ap_pstrdup(p, account_temp);
+ return NULL;
+}
+
+/* This routine complements the setuid() call: it causes the BS2000 job
+ * environment to be switched to the target user's user id.
+ * That is important if CGI scripts try to execute native BS2000 commands.
+ */
+int os_init_job_environment(server_rec *server, const char *user_name, int one_process)
+{
+ _rini_struct inittask;
+ char username[USER_LEN+1];
+ int save_errno;
+ bs2_ForkType type = os_forktype();
+
+ /* We can be sure that no change to uid==0 is possible because of
+ * the checks in http_core.c:set_user()
+ */
+
+ /* The _rini() function works only after a prior _rfork().
+ * In the case of one_process, it would fail.
+ */
+ if (one_process) {
+
+ type = forktype = bs2_noFORK;
+
+ ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, server,
+ "The debug mode of Apache should only "
+ "be started by an unprivileged user!");
+ return 0;
+ }
+
+ /* If no _rini() is required, then return quickly. */
+ if (type != bs2_RFORK_RINI && type != bs2_FORK_RINI)
+ return 0;
+
+ /* An Account is required for _rini() */
+ if (bs2000_account == NULL)
+ {
+ ap_log_error(APLOG_MARK, APLOG_ALERT|APLOG_NOERRNO, server,
+ "No BS2000Account configured - cannot switch to User %s",
+ user_name);
+ exit(APEXIT_CHILDFATAL);
+ }
+
+ ap_cpystrn(username, user_name, sizeof username);
+
+ /* Make user name all upper case */
+ ap_str_toupper(username);
+
+ /* Pad to length 8 */
+ ap_pad(username, sizeof username, ' ');
+
+ inittask.username = username;
+ inittask.account = bs2000_account;
+ inittask.processor_name = " ";
+
+ /* Switch to the new logon user (setuid() and setgid() are done later) */
+ /* Only the super user can switch identities. */
+ if (_rini(&inittask) != 0) {
+
+ ap_log_error(APLOG_MARK, APLOG_ALERT, server,
+ "_rini: BS2000 auth failed for user \"%s\" acct \"%s\"",
+ inittask.username, inittask.account);
+
+ exit(APEXIT_CHILDFATAL);
+ }
+
+ return 0;
+}
+
+/* BS2000 requires a "special" version of fork() before a setuid()/_rini() call */
+pid_t os_fork(const char *user)
+{
+ pid_t pid;
+ char username[USER_LEN+1];
+
+ switch (os_forktype()) {
+ case bs2_FORK:
+ case bs2_FORK_RINI:
+ pid = fork();
+ break;
+
+ case bs2_RFORK_RINI:
+ pid = _rfork();
+ break;
+
+ case bs2_UFORK:
+ ap_cpystrn(username, user, sizeof username);
+
+ /* Make user name all upper case - for some versions of ufork() */
+ ap_str_toupper(username);
+
+ pid = ufork(username);
+ if (pid == -1 && errno == EPERM) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG,
+ NULL, "ufork: Possible mis-configuration "
+ "for user %s - Aborting.", user);
+ exit(1);
+ }
+ break;
+
+ default:
+ pid = 0;
+ break;
+ }
+
+ return pid;
+}
+
+#else /* _OSD_POSIX */
+void bs2login_is_not_here()
+{
+}
+#endif /* _OSD_POSIX */
--- /dev/null
+/* ====================================================================
+ * Copyright (c) 1998-1999 The Apache Group. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * 4. The names "Apache Server" and "Apache Group" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission. For written permission, please contact
+ * apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ * nor may "Apache" appear in their names without prior written
+ * permission of the Apache Group.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Group and was originally based
+ * on public domain software written at the National Center for
+ * Supercomputing Applications, University of Illinois, Urbana-Champaign.
+ * For more information on the Apache Group and the Apache HTTP server
+ * project, please see <http://www.apache.org/>.
+ *
+ */
+
+
+#ifdef CHARSET_EBCDIC
+#include "ap_config.h"
+#include "ebcdic.h"
+/*
+ Initial Port for Apache-1.3 by <Martin.Kraemer@Mch.SNI.De>
+
+"BS2000 OSD" is a POSIX on a main frame. It is made by Siemens AG, Germany.
+Within the POSIX subsystem, the same character set was chosen as in
+"native BS2000", namely EBCDIC.
+
+EBCDIC Table. (Yes, in EBCDIC, the letters 'a'..'z' are not contiguous!)
+This table is bijective, i.e. there are no ambigous or duplicate characters
+00 00 01 02 03 85 09 86 7f 87 8d 8e 0b 0c 0d 0e 0f *................*
+10 10 11 12 13 8f 0a 08 97 18 19 9c 9d 1c 1d 1e 1f *................*
+20 80 81 82 83 84 92 17 1b 88 89 8a 8b 8c 05 06 07 *................*
+30 90 91 16 93 94 95 96 04 98 99 9a 9b 14 15 9e 1a *................*
+40 20 a0 e2 e4 e0 e1 e3 e5 e7 f1 60 2e 3c 28 2b 7c * .........`.<(+|*
+50 26 e9 ea eb e8 ed ee ef ec df 21 24 2a 29 3b 9f *&.........!$*);.*
+60 2d 2f c2 c4 c0 c1 c3 c5 c7 d1 5e 2c 25 5f 3e 3f *-/........^,%_>?*
+70 f8 c9 ca cb c8 cd ce cf cc a8 3a 23 40 27 3d 22 *..........:#@'="*
+80 d8 61 62 63 64 65 66 67 68 69 ab bb f0 fd fe b1 *.abcdefghi......*
+90 b0 6a 6b 6c 6d 6e 6f 70 71 72 aa ba e6 b8 c6 a4 *.jklmnopqr......*
+a0 b5 af 73 74 75 76 77 78 79 7a a1 bf d0 dd de ae *..stuvwxyz......*
+b0 a2 a3 a5 b7 a9 a7 b6 bc bd be ac 5b 5c 5d b4 d7 *...........[\]..*
+c0 f9 41 42 43 44 45 46 47 48 49 ad f4 f6 f2 f3 f5 *.ABCDEFGHI......*
+d0 a6 4a 4b 4c 4d 4e 4f 50 51 52 b9 fb fc db fa ff *.JKLMNOPQR......*
+e0 d9 f7 53 54 55 56 57 58 59 5a b2 d4 d6 d2 d3 d5 *..STUVWXYZ......*
+f0 30 31 32 33 34 35 36 37 38 39 b3 7b dc 7d da 7e *0123456789.{.}.~*
+*/
+
+/* The bijective ebcdic-to-ascii table: */
+const unsigned char os_toascii_strictly[256] = {
+/*00*/ 0x00, 0x01, 0x02, 0x03, 0x85, 0x09, 0x86, 0x7f,
+ 0x87, 0x8d, 0x8e, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /*................*/
+/*10*/ 0x10, 0x11, 0x12, 0x13, 0x8f, 0x0a, 0x08, 0x97,
+ 0x18, 0x19, 0x9c, 0x9d, 0x1c, 0x1d, 0x1e, 0x1f, /*................*/
+/*20*/ 0x80, 0x81, 0x82, 0x83, 0x84, 0x92, 0x17, 0x1b,
+ 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x05, 0x06, 0x07, /*................*/
+/*30*/ 0x90, 0x91, 0x16, 0x93, 0x94, 0x95, 0x96, 0x04,
+ 0x98, 0x99, 0x9a, 0x9b, 0x14, 0x15, 0x9e, 0x1a, /*................*/
+/*40*/ 0x20, 0xa0, 0xe2, 0xe4, 0xe0, 0xe1, 0xe3, 0xe5,
+ 0xe7, 0xf1, 0x60, 0x2e, 0x3c, 0x28, 0x2b, 0x7c, /* .........`.<(+|*/
+/*50*/ 0x26, 0xe9, 0xea, 0xeb, 0xe8, 0xed, 0xee, 0xef,
+ 0xec, 0xdf, 0x21, 0x24, 0x2a, 0x29, 0x3b, 0x9f, /*&.........!$*);.*/
+/*60*/ 0x2d, 0x2f, 0xc2, 0xc4, 0xc0, 0xc1, 0xc3, 0xc5,
+ 0xc7, 0xd1, 0x5e, 0x2c, 0x25, 0x5f, 0x3e, 0x3f, /*-/........^,%_>?*/
+/*70*/ 0xf8, 0xc9, 0xca, 0xcb, 0xc8, 0xcd, 0xce, 0xcf,
+ 0xcc, 0xa8, 0x3a, 0x23, 0x40, 0x27, 0x3d, 0x22, /*..........:#@'="*/
+/*80*/ 0xd8, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
+ 0x68, 0x69, 0xab, 0xbb, 0xf0, 0xfd, 0xfe, 0xb1, /*.abcdefghi......*/
+/*90*/ 0xb0, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70,
+ 0x71, 0x72, 0xaa, 0xba, 0xe6, 0xb8, 0xc6, 0xa4, /*.jklmnopqr......*/
+/*a0*/ 0xb5, 0xaf, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
+ 0x79, 0x7a, 0xa1, 0xbf, 0xd0, 0xdd, 0xde, 0xae, /*..stuvwxyz......*/
+/*b0*/ 0xa2, 0xa3, 0xa5, 0xb7, 0xa9, 0xa7, 0xb6, 0xbc,
+ 0xbd, 0xbe, 0xac, 0x5b, 0x5c, 0x5d, 0xb4, 0xd7, /*...........[\]..*/
+/*c0*/ 0xf9, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+ 0x48, 0x49, 0xad, 0xf4, 0xf6, 0xf2, 0xf3, 0xf5, /*.ABCDEFGHI......*/
+/*d0*/ 0xa6, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50,
+ 0x51, 0x52, 0xb9, 0xfb, 0xfc, 0xdb, 0xfa, 0xff, /*.JKLMNOPQR......*/
+/*e0*/ 0xd9, 0xf7, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
+ 0x59, 0x5a, 0xb2, 0xd4, 0xd6, 0xd2, 0xd3, 0xd5, /*..STUVWXYZ......*/
+/*f0*/ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+ 0x38, 0x39, 0xb3, 0x7b, 0xdc, 0x7d, 0xda, 0x7e /*0123456789.{.}.~*/
+};
+
+/* This table is (almost) identical to the previous one. The only difference
+ * is the fact that it maps every EBCDIC *except 0x0A* to its ASCII
+ * equivalent. The reason for this table is simple: Throughout the
+ * server, protocol strings are used in the form
+ * "Content-Type: text/plain\015\012". Now all the characters in the string
+ * are stored as EBCDIC, only the semantics of \012 is completely
+ * different from LF (look it up in the table above). \015 happens to be
+ * mapped to \015 anyway, so there's no special case for it.
+ *
+ * In THIS table, EBCDIC-\012 is mapped to ASCII-\012.
+ * This table is therefore used wherever an EBCDIC to ASCII conversion is
+ * needed in the server.
+ */
+/* ebcdic-to-ascii with \012 mapped to ASCII-\n */
+const unsigned char os_toascii[256] = {
+/*00*/ 0x00, 0x01, 0x02, 0x03, 0x85, 0x09, 0x86, 0x7f,
+ 0x87, 0x8d, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /*................*/
+/*10*/ 0x10, 0x11, 0x12, 0x13, 0x8f, 0x0a, 0x08, 0x97,
+ 0x18, 0x19, 0x9c, 0x9d, 0x1c, 0x1d, 0x1e, 0x1f, /*................*/
+/*20*/ 0x80, 0x81, 0x82, 0x83, 0x84, 0x92, 0x17, 0x1b,
+ 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x05, 0x06, 0x07, /*................*/
+/*30*/ 0x90, 0x91, 0x16, 0x93, 0x94, 0x95, 0x96, 0x04,
+ 0x98, 0x99, 0x9a, 0x9b, 0x14, 0x15, 0x9e, 0x1a, /*................*/
+/*40*/ 0x20, 0xa0, 0xe2, 0xe4, 0xe0, 0xe1, 0xe3, 0xe5,
+ 0xe7, 0xf1, 0x60, 0x2e, 0x3c, 0x28, 0x2b, 0x7c, /* .........`.<(+|*/
+/*50*/ 0x26, 0xe9, 0xea, 0xeb, 0xe8, 0xed, 0xee, 0xef,
+ 0xec, 0xdf, 0x21, 0x24, 0x2a, 0x29, 0x3b, 0x9f, /*&.........!$*);.*/
+/*60*/ 0x2d, 0x2f, 0xc2, 0xc4, 0xc0, 0xc1, 0xc3, 0xc5,
+ 0xc7, 0xd1, 0x5e, 0x2c, 0x25, 0x5f, 0x3e, 0x3f, /*-/........^,%_>?*/
+/*70*/ 0xf8, 0xc9, 0xca, 0xcb, 0xc8, 0xcd, 0xce, 0xcf,
+ 0xcc, 0xa8, 0x3a, 0x23, 0x40, 0x27, 0x3d, 0x22, /*..........:#@'="*/
+/*80*/ 0xd8, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
+ 0x68, 0x69, 0xab, 0xbb, 0xf0, 0xfd, 0xfe, 0xb1, /*.abcdefghi......*/
+/*90*/ 0xb0, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70,
+ 0x71, 0x72, 0xaa, 0xba, 0xe6, 0xb8, 0xc6, 0xa4, /*.jklmnopqr......*/
+/*a0*/ 0xb5, 0xaf, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
+ 0x79, 0x7a, 0xa1, 0xbf, 0xd0, 0xdd, 0xde, 0xae, /*..stuvwxyz......*/
+/*b0*/ 0xa2, 0xa3, 0xa5, 0xb7, 0xa9, 0xa7, 0xb6, 0xbc,
+ 0xbd, 0xbe, 0xac, 0x5b, 0x5c, 0x5d, 0xb4, 0xd7, /*...........[\]..*/
+/*c0*/ 0xf9, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+ 0x48, 0x49, 0xad, 0xf4, 0xf6, 0xf2, 0xf3, 0xf5, /*.ABCDEFGHI......*/
+/*d0*/ 0xa6, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50,
+ 0x51, 0x52, 0xb9, 0xfb, 0xfc, 0xdb, 0xfa, 0xff, /*.JKLMNOPQR......*/
+/*e0*/ 0xd9, 0xf7, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
+ 0x59, 0x5a, 0xb2, 0xd4, 0xd6, 0xd2, 0xd3, 0xd5, /*..STUVWXYZ......*/
+/*f0*/ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+ 0x38, 0x39, 0xb3, 0x7b, 0xdc, 0x7d, 0xda, 0x7e /*0123456789.{.}.~*/
+};
+
+/* The ascii-to-ebcdic table:
+00 00 01 02 03 37 2d 2e 2f 16 05 15 0b 0c 0d 0e 0f *................*
+10 10 11 12 13 3c 3d 32 26 18 19 3f 27 1c 1d 1e 1f *................*
+20 40 5a 7f 7b 5b 6c 50 7d 4d 5d 5c 4e 6b 60 4b 61 * !"#$%&'()*+,-./
+30 f0 f1 f2 f3 f4 f5 f6 f7 f8 f9 7a 5e 4c 7e 6e 6f *0123456789:;<=>?*
+40 7c c1 c2 c3 c4 c5 c6 c7 c8 c9 d1 d2 d3 d4 d5 d6 *@ABCDEFGHIJKLMNO*
+50 d7 d8 d9 e2 e3 e4 e5 e6 e7 e8 e9 bb bc bd 6a 6d *PQRSTUVWXYZ[\]^_*
+60 4a 81 82 83 84 85 86 87 88 89 91 92 93 94 95 96 *`abcdefghijklmno*
+70 97 98 99 a2 a3 a4 a5 a6 a7 a8 a9 fb 4f fd ff 07 *pqrstuvwxyz{|}~.*
+80 20 21 22 23 24 04 06 08 28 29 2a 2b 2c 09 0a 14 *................*
+90 30 31 25 33 34 35 36 17 38 39 3a 3b 1a 1b 3e 5f *................*
+a0 41 aa b0 b1 9f b2 d0 b5 79 b4 9a 8a ba ca af a1 *................*
+b0 90 8f ea fa be a0 b6 b3 9d da 9b 8b b7 b8 b9 ab *................*
+c0 64 65 62 66 63 67 9e 68 74 71 72 73 78 75 76 77 *................*
+d0 ac 69 ed ee eb ef ec bf 80 e0 fe dd fc ad ae 59 *................*
+e0 44 45 42 46 43 47 9c 48 54 51 52 53 58 55 56 57 *................*
+f0 8c 49 cd ce cb cf cc e1 70 c0 de db dc 8d 8e df *................*
+*/
+const unsigned char os_toebcdic[256] = {
+/*00*/ 0x00, 0x01, 0x02, 0x03, 0x37, 0x2d, 0x2e, 0x2f,
+ 0x16, 0x05, 0x15, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /*................*/
+/*10*/ 0x10, 0x11, 0x12, 0x13, 0x3c, 0x3d, 0x32, 0x26,
+ 0x18, 0x19, 0x3f, 0x27, 0x1c, 0x1d, 0x1e, 0x1f, /*................*/
+/*20*/ 0x40, 0x5a, 0x7f, 0x7b, 0x5b, 0x6c, 0x50, 0x7d,
+ 0x4d, 0x5d, 0x5c, 0x4e, 0x6b, 0x60, 0x4b, 0x61, /* !"#$%&'()*+,-./ */
+/*30*/ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
+ 0xf8, 0xf9, 0x7a, 0x5e, 0x4c, 0x7e, 0x6e, 0x6f, /*0123456789:;<=>?*/
+/*40*/ 0x7c, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
+ 0xc8, 0xc9, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, /*@ABCDEFGHIJKLMNO*/
+/*50*/ 0xd7, 0xd8, 0xd9, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6,
+ 0xe7, 0xe8, 0xe9, 0xbb, 0xbc, 0xbd, 0x6a, 0x6d, /*PQRSTUVWXYZ[\]^_*/
+/*60*/ 0x4a, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
+ 0x88, 0x89, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, /*`abcdefghijklmno*/
+/*70*/ 0x97, 0x98, 0x99, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6,
+ 0xa7, 0xa8, 0xa9, 0xfb, 0x4f, 0xfd, 0xff, 0x07, /*pqrstuvwxyz{|}~.*/
+/*80*/ 0x20, 0x21, 0x22, 0x23, 0x24, 0x04, 0x06, 0x08,
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x09, 0x0a, 0x14, /*................*/
+/*90*/ 0x30, 0x31, 0x25, 0x33, 0x34, 0x35, 0x36, 0x17,
+ 0x38, 0x39, 0x3a, 0x3b, 0x1a, 0x1b, 0x3e, 0x5f, /*................*/
+/*a0*/ 0x41, 0xaa, 0xb0, 0xb1, 0x9f, 0xb2, 0xd0, 0xb5,
+ 0x79, 0xb4, 0x9a, 0x8a, 0xba, 0xca, 0xaf, 0xa1, /*................*/
+/*b0*/ 0x90, 0x8f, 0xea, 0xfa, 0xbe, 0xa0, 0xb6, 0xb3,
+ 0x9d, 0xda, 0x9b, 0x8b, 0xb7, 0xb8, 0xb9, 0xab, /*................*/
+/*c0*/ 0x64, 0x65, 0x62, 0x66, 0x63, 0x67, 0x9e, 0x68,
+ 0x74, 0x71, 0x72, 0x73, 0x78, 0x75, 0x76, 0x77, /*................*/
+/*d0*/ 0xac, 0x69, 0xed, 0xee, 0xeb, 0xef, 0xec, 0xbf,
+ 0x80, 0xe0, 0xfe, 0xdd, 0xfc, 0xad, 0xae, 0x59, /*................*/
+/*e0*/ 0x44, 0x45, 0x42, 0x46, 0x43, 0x47, 0x9c, 0x48,
+ 0x54, 0x51, 0x52, 0x53, 0x58, 0x55, 0x56, 0x57, /*................*/
+/*f0*/ 0x8c, 0x49, 0xcd, 0xce, 0xcb, 0xcf, 0xcc, 0xe1,
+ 0x70, 0xc0, 0xde, 0xdb, 0xdc, 0x8d, 0x8e, 0xdf /*................*/
+};
+
+/* Translate a memory block from EBCDIC (host charset) to ASCII (net charset)
+ * dest and srce may be identical, or separate memory blocks, but
+ * should not overlap.
+ */
+void
+ebcdic2ascii(unsigned char *dest, const unsigned char *srce, size_t count)
+{
+ while (count-- != 0) {
+ *dest++ = os_toascii[*srce++];
+ }
+}
+void
+ebcdic2ascii_strictly(unsigned char *dest, const unsigned char *srce, size_t count)
+{
+ while (count-- != 0) {
+ *dest++ = os_toascii_strictly[*srce++];
+ }
+}
+void
+ascii2ebcdic(unsigned char *dest, const unsigned char *srce, size_t count)
+{
+ while (count-- != 0) {
+ *dest++ = os_toebcdic[*srce++];
+ }
+}
+#endif /*CHARSET_EBCDIC*/
--- /dev/null
+#include <sys/types.h>
+
+extern const unsigned char os_toascii[256];
+extern const unsigned char os_toebcdic[256];
+void ebcdic2ascii(unsigned char *dest, const unsigned char *srce, size_t count);
+void ebcdic2ascii_strictly(unsigned char *dest, const unsigned char *srce, size_t count);
+void ascii2ebcdic(unsigned char *dest, const unsigned char *srce, size_t count);
+
--- /dev/null
+/*
+ * This file contains functions which can be inlined if the compiler
+ * has an "inline" modifier. Because of this, this file is both a
+ * header file and a compilable module.
+ *
+ * Only inlineable functions should be defined in here. They must all
+ * include the INLINE modifier.
+ *
+ * If the compiler supports inline, this file will be #included as a
+ * header file from os.h to create all the inline function
+ * definitions. INLINE will be defined to whatever is required on
+ * function definitions to make them inline declarations.
+ *
+ * If the compiler does not support inline, this file will be compiled
+ * as a normal C file into libos.a (along with os.c). In this case
+ * INLINE will _not_ be set so we can use this to test if we are
+ * compiling this source file.
+ */
+
+#ifndef INLINE
+#define INLINE
+
+/* Anything required only when compiling */
+#include "ap_config.h"
+
+#endif
+
+INLINE int ap_os_is_path_absolute(const char *file)
+{
+ return (file && file[0] == '/' ? 1 : 0);
+}
--- /dev/null
+/* ====================================================================
+ * Copyright (c) 1998-1999 The Apache Group. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * 4. The names "Apache Server" and "Apache Group" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission. For written permission, please contact
+ * apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ * nor may "Apache" appear in their names without prior written
+ * permission of the Apache Group.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Group and was originally based
+ * on public domain software written at the National Center for
+ * Supercomputing Applications, University of Illinois, Urbana-Champaign.
+ * For more information on the Apache Group and the Apache HTTP server
+ * project, please see <http://www.apache.org/>.
+ *
+ */
+
+/*
+ * This file will include OS specific functions which are not inlineable.
+ * Any inlineable functions should be defined in os-inline.c instead.
+ */
+
+#include "httpd.h"
+#include "http_core.h"
+#include "os.h"
+
+/* Check the Content-Type to decide if conversion is needed */
+int ap_checkconv(struct request_rec *r)
+{
+ int convert_to_ascii;
+ const char *type;
+
+ /* To make serving of "raw ASCII text" files easy (they serve faster
+ * since they don't have to be converted from EBCDIC), a new
+ * "magic" type prefix was invented: text/x-ascii-{plain,html,...}
+ * If we detect one of these content types here, we simply correct
+ * the type to the real text/{plain,html,...} type. Otherwise, we
+ * set a flag that translation is required later on.
+ */
+
+ type = (r->content_type == NULL) ? ap_default_type(r) : r->content_type;
+
+ /* If no content type is set then treat it as (ebcdic) text/plain */
+ convert_to_ascii = (type == NULL);
+
+ /* Conversion is applied to text/ files only, if ever. */
+ if (type && (strncasecmp(type, "text/", 5) == 0 ||
+ strncasecmp(type, "message/", 8) == 0)) {
+ if (strncasecmp(type, ASCIITEXT_MAGIC_TYPE_PREFIX,
+ sizeof(ASCIITEXT_MAGIC_TYPE_PREFIX)-1) == 0)
+ r->content_type = ap_pstrcat(r->pool, "text/",
+ type+sizeof(ASCIITEXT_MAGIC_TYPE_PREFIX)-1,
+ NULL);
+ else
+ /* translate EBCDIC to ASCII */
+ convert_to_ascii = 1;
+ }
+ /* Enable conversion if it's a text document */
+ ap_bsetflag(r->connection->client, B_EBCDIC2ASCII, convert_to_ascii);
+
+ return convert_to_ascii;
+}
+
--- /dev/null
+#ifndef APACHE_OS_H
+#define APACHE_OS_H
+
+#define PLATFORM "BS2000"
+
+/*
+ * This file in included in all Apache source code. It contains definitions
+ * of facilities available on _this_ operating system (HAVE_* macros),
+ * and prototypes of OS specific functions defined in os.c or os-inline.c
+ */
+
+#if !defined(INLINE) && defined(USE_GNU_INLINE)
+/* Compiler supports inline, so include the inlineable functions as
+ * part of the header
+ */
+#define INLINE extern ap_inline
+
+INLINE int ap_os_is_path_absolute(const char *file);
+
+#include "os-inline.c"
+#endif
+
+#ifndef INLINE
+/* Compiler does not support inline, so prototype the inlineable functions
+ * as normal
+ */
+extern int ap_os_is_path_absolute(const char *file);
+#endif
+
+/* Other ap_os_ routines not used by this platform */
+
+#define ap_os_is_filename_valid(f) (1)
+#define ap_os_kill(pid, sig) kill(pid, sig)
+
+#if !defined(_POSIX_SOURCE) && !defined(_XOPEN_SOURCE)
+typedef struct {
+ char *username;
+ char *account;
+ char *processor_name;
+} _rini_struct;
+
+extern int _rini(_rini_struct *);
+#endif /* !defined(_POSIX_SOURCE) && !defined(_XOPEN_SOURCE) */
+
+/* Sorry if this is ugly, but the include order doesn't allow me
+ * to use request_rec here... */
+struct request_rec;
+extern int ap_checkconv(struct request_rec *r);
+extern pid_t os_fork(const char *user);
+
+#endif /*! APACHE_OS_H*/
--- /dev/null
+/*
+ * This file contains functions which can be inlined if the compiler
+ * has an "inline" modifier. Because of this, this file is both a
+ * header file and a compilable module.
+ *
+ * Only inlineable functions should be defined in here. They must all
+ * include the INLINE modifier.
+ *
+ * If the compiler supports inline, this file will be #included as a
+ * header file from os.h to create all the inline function
+ * definitions. INLINE will be defined to whatever is required on
+ * function definitions to make them inline declarations.
+ *
+ * If the compiler does not support inline, this file will be compiled
+ * as a normal C file into libos.a (along with os.c). In this case
+ * INLINE will _not_ be set so we can use this to test if we are
+ * compiling this source file.
+ */
+
+#ifndef INLINE
+#define INLINE
+
+/* Anything required only when compiling */
+#include "ap_config.h"
+
+#endif
+
+INLINE int ap_os_is_path_absolute(const char *file)
+{
+ /* For now, just do the same check that http_request.c and mod_alias.c
+ * do.
+ */
+ return file && (file[0] == '/' || file[1] == ':');
+}
--- /dev/null
+#ifndef APACHE_OS_H
+#define APACHE_OS_H
+
+#define PLATFORM "OS/2"
+#define HAVE_CANONICAL_FILENAME
+#define HAVE_DRIVE_LETTERS
+
+/*
+ * This file in included in all Apache source code. It contains definitions
+ * of facilities available on _this_ operating system (HAVE_* macros),
+ * and prototypes of OS specific functions defined in os.c or os-inline.c
+ */
+
+#if defined(__GNUC__) && !defined(INLINE)
+/* Compiler supports inline, so include the inlineable functions as
+ * part of the header
+ */
+#define INLINE extern __inline__
+
+INLINE int ap_os_is_path_absolute(const char *file);
+
+#include "os-inline.c"
+#endif
+
+#ifndef INLINE
+/* Compiler does not support inline, so prototype the inlineable functions
+ * as normal
+ */
+extern int ap_os_is_path_absolute(const char *file);
+#endif
+
+/* FIXME: the following should be implemented on this platform */
+#define ap_os_is_filename_valid(f) (1)
+
+/* Use a specialized kill() function */
+int ap_os_kill(int pid, int sig);
+
+/* Maps an OS error code to an error message */
+char *ap_os_error_message(int err);
+
+/* OS/2 doesn't have symlinks so S_ISLNK is always false */
+#define S_ISLNK(m) 0
+
+/* Dynamic loading functions */
+#define ap_os_dso_handle_t unsigned long
+void ap_os_dso_init(void);
+ap_os_dso_handle_t ap_os_dso_load(const char *);
+void ap_os_dso_unload(ap_os_dso_handle_t);
+void * ap_os_dso_sym(ap_os_dso_handle_t, const char *);
+const char *ap_os_dso_error(void);
+
+#endif /* ! APACHE_OS_H */
--- /dev/null
+#define INCL_DOS
+#define INCL_DOSERRORS
+#include <os2.h>
+#include "httpd.h"
+#include "http_log.h"
+
+
+API_EXPORT(char *)ap_os_canonical_filename(pool *pPool, const char *szFile)
+{
+ char buf[HUGE_STRING_LEN];
+ char buf2[HUGE_STRING_LEN];
+ int rc, len;
+ char *pos;
+
+/* Remove trailing slash unless it's a root directory */
+ strcpy(buf, szFile);
+ len = strlen(buf);
+
+ if (len > 3 && buf[len-1] == '/')
+ buf[--len] = 0;
+
+ rc = DosQueryPathInfo(buf, FIL_QUERYFULLNAME, buf2, HUGE_STRING_LEN);
+
+ if (rc) {
+ if ( rc != ERROR_INVALID_NAME ) {
+ ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, NULL, "OS/2 error %d for file %s", rc, szFile);
+ return ap_pstrdup(pPool, "");
+ } else {
+ return ap_pstrdup(pPool, szFile);
+ }
+ }
+
+ strlwr(buf2);
+
+/* Switch backslashes to forward */
+ for (pos=buf2; *pos; pos++)
+ if (*pos == '\\')
+ *pos = '/';
+
+ return ap_pstrdup(pPool, buf2);
+}
+
+
+
+int ap_os_kill(pid_t pid, int sig)
+{
+/* SIGTERM's don't work too well in OS/2 (only affects other EMX programs).
+ CGIs may not be, esp. REXX scripts, so use a native call instead */
+
+ int rc;
+
+ if ( sig == SIGTERM ) {
+ rc = DosSendSignalException( pid, XCPT_SIGNAL_BREAK );
+
+ if ( rc ) {
+ errno = ESRCH;
+ rc = -1;
+ }
+ } else {
+ rc = kill(pid, sig);
+ }
+
+ return rc;
+}
+
+
+
+char *ap_os_error_message(int err)
+{
+ static char result[200];
+ char message[HUGE_STRING_LEN];
+ ULONG len;
+ char *pos;
+ int c;
+
+ if (DosGetMessage(NULL, 0, message, HUGE_STRING_LEN, err, "OSO001.MSG", &len) == 0) {
+ len--;
+ message[len] = 0;
+ pos = result;
+
+ if (len >= sizeof(result))
+ len = sizeof(result-1);
+
+ for (c=0; c<len; c++) {
+ while (isspace(message[c]) && isspace(message[c+1])) /* skip multiple whitespace */
+ c++;
+ *(pos++) = isspace(message[c]) ? ' ' : message[c];
+ }
+
+ *pos = 0;
+ } else {
+ sprintf(result, "OS/2 error %d", err);
+ }
+
+ return result;
+}
--- /dev/null
+#!/bin/sh
+echo " Setting TPF/c89 environment variables"
+export _C89_CCMODE=1
+# replace the following with the location of your TPF include files
+export _C89_INCDIRS="/u/tpf41/currentmaint/include /u/tpf41/currentmaint/include/oco"
+export TPF=YES
+echo "Done"
--- /dev/null
+/* ====================================================================
+ * Copyright (c) 1998-1999 The Apache Group. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * 4. The names "Apache Server" and "Apache Group" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission. For written permission, please contact
+ * apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ * nor may "Apache" appear in their names without prior written
+ * permission of the Apache Group.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Group and was originally based
+ * on public domain software written at the National Center for
+ * Supercomputing Applications, University of Illinois, Urbana-Champaign.
+ * For more information on the Apache Group and the Apache HTTP server
+ * project, please see <http://www.apache.org/>.
+ *
+ */
+
+
+#ifdef CHARSET_EBCDIC
+#include "ap_config.h"
+#include "ebcdic.h"
+/*
+This code does basic character mapping for IBM's TPF operating system.
+It is a modified version of <Martin.Kraemer@Mch.SNI.De>'s code for
+the BS2000 (apache/src/os/bs2000/ebcdic.c).
+*/
+
+/*
+Bijective EBCDIC (character set IBM-1047) to US-ASCII table:
+This table is bijective - there are no ambigous or duplicate characters.
+*/
+const unsigned char os_toascii_strictly[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x85, 0x09, 0x86, 0x7f, /* 00-0f: */
+ 0x87, 0x8d, 0x8e, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* ................ */
+ 0x10, 0x11, 0x12, 0x13, 0x8f, 0x0a, 0x08, 0x97, /* 10-1f: */
+ 0x18, 0x19, 0x9c, 0x9d, 0x1c, 0x1d, 0x1e, 0x1f, /* ................ */
+ 0x80, 0x81, 0x82, 0x83, 0x84, 0x92, 0x17, 0x1b, /* 20-2f: */
+ 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x05, 0x06, 0x07, /* ................ */
+ 0x90, 0x91, 0x16, 0x93, 0x94, 0x95, 0x96, 0x04, /* 30-3f: */
+ 0x98, 0x99, 0x9a, 0x9b, 0x14, 0x15, 0x9e, 0x1a, /* ................ */
+ 0x20, 0xa0, 0xe2, 0xe4, 0xe0, 0xe1, 0xe3, 0xe5, /* 40-4f: */
+ 0xe7, 0xf1, 0xa2, 0x2e, 0x3c, 0x28, 0x2b, 0x7c, /* ...........<(+| */
+ 0x26, 0xe9, 0xea, 0xeb, 0xe8, 0xed, 0xee, 0xef, /* 50-5f: */
+ 0xec, 0xdf, 0x21, 0x24, 0x2a, 0x29, 0x3b, 0x5e, /* &.........!$*);^ */
+ 0x2d, 0x2f, 0xc2, 0xc4, 0xc0, 0xc1, 0xc3, 0xc5, /* 60-6f: */
+ 0xc7, 0xd1, 0xa6, 0x2c, 0x25, 0x5f, 0x3e, 0x3f, /* -/.........,%_>? */
+ 0xf8, 0xc9, 0xca, 0xcb, 0xc8, 0xcd, 0xce, 0xcf, /* 70-7f: */
+ 0xcc, 0x60, 0x3a, 0x23, 0x40, 0x27, 0x3d, 0x22, /* .........`:#@'=" */
+ 0xd8, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 80-8f: */
+ 0x68, 0x69, 0xab, 0xbb, 0xf0, 0xfd, 0xfe, 0xb1, /* .abcdefghi...... */
+ 0xb0, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, /* 90-9f: */
+ 0x71, 0x72, 0xaa, 0xba, 0xe6, 0xb8, 0xc6, 0xa4, /* .jklmnopqr...... */
+ 0xb5, 0x7e, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, /* a0-af: */
+ 0x79, 0x7a, 0xa1, 0xbf, 0xd0, 0x5b, 0xde, 0xae, /* .~stuvwxyz...[.. */
+ 0xac, 0xa3, 0xa5, 0xb7, 0xa9, 0xa7, 0xb6, 0xbc, /* b0-bf: */
+ 0xbd, 0xbe, 0xdd, 0xa8, 0xaf, 0x5d, 0xb4, 0xd7, /* .............].. */
+ 0x7b, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* c0-cf: */
+ 0x48, 0x49, 0xad, 0xf4, 0xf6, 0xf2, 0xf3, 0xf5, /* {ABCDEFGHI...... */
+ 0x7d, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, /* d0-df: */
+ 0x51, 0x52, 0xb9, 0xfb, 0xfc, 0xf9, 0xfa, 0xff, /* }JKLMNOPQR...... */
+ 0x5c, 0xf7, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, /* e0-ef: */
+ 0x59, 0x5a, 0xb2, 0xd4, 0xd6, 0xd2, 0xd3, 0xd5, /* \.STUVWXYZ...... */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* f0-ff: */
+ 0x38, 0x39, 0xb3, 0xdb, 0xdc, 0xd9, 0xda, 0x9f /* 0123456789...... */
+};
+
+/*
+Server EBCDIC (character set IBM-1047) to US-ASCII table:
+This table is a copy of the os_toascii_strictly bijective table above.
+The only change is that hex 0a (\012 octal) is mapped to hex 0a
+(ASCII's line feed) instead of hex 8e. This is done because throughout
+Apache, protocol string definitions hardcode the linefeed as \012 (octal):
+"Content-Type: text/plain\015\012". Without this kludge all protocol
+string definitions would need to be changed from ...\012 to ...\025.
+*/
+const unsigned char os_toascii[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x85, 0x09, 0x86, 0x7f, /* 00-0f: */
+ 0x87, 0x8d, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* ................ */
+ 0x10, 0x11, 0x12, 0x13, 0x8f, 0x0a, 0x08, 0x97, /* 10-1f: */
+ 0x18, 0x19, 0x9c, 0x9d, 0x1c, 0x1d, 0x1e, 0x1f, /* ................ */
+ 0x80, 0x81, 0x82, 0x83, 0x84, 0x92, 0x17, 0x1b, /* 20-2f: */
+ 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x05, 0x06, 0x07, /* ................ */
+ 0x90, 0x91, 0x16, 0x93, 0x94, 0x95, 0x96, 0x04, /* 30-3f: */
+ 0x98, 0x99, 0x9a, 0x9b, 0x14, 0x15, 0x9e, 0x1a, /* ................ */
+ 0x20, 0xa0, 0xe2, 0xe4, 0xe0, 0xe1, 0xe3, 0xe5, /* 40-4f: */
+ 0xe7, 0xf1, 0xa2, 0x2e, 0x3c, 0x28, 0x2b, 0x7c, /* ...........<(+| */
+ 0x26, 0xe9, 0xea, 0xeb, 0xe8, 0xed, 0xee, 0xef, /* 50-5f: */
+ 0xec, 0xdf, 0x21, 0x24, 0x2a, 0x29, 0x3b, 0x5e, /* &.........!$*);^ */
+ 0x2d, 0x2f, 0xc2, 0xc4, 0xc0, 0xc1, 0xc3, 0xc5, /* 60-6f: */
+ 0xc7, 0xd1, 0xa6, 0x2c, 0x25, 0x5f, 0x3e, 0x3f, /* -/.........,%_>? */
+ 0xf8, 0xc9, 0xca, 0xcb, 0xc8, 0xcd, 0xce, 0xcf, /* 70-7f: */
+ 0xcc, 0x60, 0x3a, 0x23, 0x40, 0x27, 0x3d, 0x22, /* .........`:#@'=" */
+ 0xd8, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 80-8f: */
+ 0x68, 0x69, 0xab, 0xbb, 0xf0, 0xfd, 0xfe, 0xb1, /* .abcdefghi...... */
+ 0xb0, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, /* 90-9f: */
+ 0x71, 0x72, 0xaa, 0xba, 0xe6, 0xb8, 0xc6, 0xa4, /* .jklmnopqr...... */
+ 0xb5, 0x7e, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, /* a0-af: */
+ 0x79, 0x7a, 0xa1, 0xbf, 0xd0, 0x5b, 0xde, 0xae, /* .~stuvwxyz...[.. */
+ 0xac, 0xa3, 0xa5, 0xb7, 0xa9, 0xa7, 0xb6, 0xbc, /* b0-bf: */
+ 0xbd, 0xbe, 0xdd, 0xa8, 0xaf, 0x5d, 0xb4, 0xd7, /* .............].. */
+ 0x7b, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* c0-cf: */
+ 0x48, 0x49, 0xad, 0xf4, 0xf6, 0xf2, 0xf3, 0xf5, /* {ABCDEFGHI...... */
+ 0x7d, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, /* d0-df: */
+ 0x51, 0x52, 0xb9, 0xfb, 0xfc, 0xf9, 0xfa, 0xff, /* }JKLMNOPQR...... */
+ 0x5c, 0xf7, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, /* e0-ef: */
+ 0x59, 0x5a, 0xb2, 0xd4, 0xd6, 0xd2, 0xd3, 0xd5, /* \.STUVWXYZ...... */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* f0-ff: */
+ 0x38, 0x39, 0xb3, 0xdb, 0xdc, 0xd9, 0xda, 0x9f /* 0123456789...... */
+};
+
+/*
+The US-ASCII to EBCDIC (character set IBM-1047) table:
+This table is bijective (no ambiguous or duplicate characters)
+*/
+const unsigned char os_toebcdic[256] = {
+ 0x00, 0x01, 0x02, 0x03, 0x37, 0x2d, 0x2e, 0x2f, /* 00-0f: */
+ 0x16, 0x05, 0x15, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* ................ */
+ 0x10, 0x11, 0x12, 0x13, 0x3c, 0x3d, 0x32, 0x26, /* 10-1f: */
+ 0x18, 0x19, 0x3f, 0x27, 0x1c, 0x1d, 0x1e, 0x1f, /* ................ */
+ 0x40, 0x5a, 0x7f, 0x7b, 0x5b, 0x6c, 0x50, 0x7d, /* 20-2f: */
+ 0x4d, 0x5d, 0x5c, 0x4e, 0x6b, 0x60, 0x4b, 0x61, /* !"#$%&'()*+,-./ */
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 30-3f: */
+ 0xf8, 0xf9, 0x7a, 0x5e, 0x4c, 0x7e, 0x6e, 0x6f, /* 0123456789:;<=>? */
+ 0x7c, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* 40-4f: */
+ 0xc8, 0xc9, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, /* @ABCDEFGHIJKLMNO */
+ 0xd7, 0xd8, 0xd9, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, /* 50-5f: */
+ 0xe7, 0xe8, 0xe9, 0xad, 0xe0, 0xbd, 0x5f, 0x6d, /* PQRSTUVWXYZ[\]^_ */
+ 0x79, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, /* 60-6f: */
+ 0x88, 0x89, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, /* `abcdefghijklmno */
+ 0x97, 0x98, 0x99, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, /* 70-7f: */
+ 0xa7, 0xa8, 0xa9, 0xc0, 0x4f, 0xd0, 0xa1, 0x07, /* pqrstuvwxyz{|}~. */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x04, 0x06, 0x08, /* 80-8f: */
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x09, 0x0a, 0x14, /* ................ */
+ 0x30, 0x31, 0x25, 0x33, 0x34, 0x35, 0x36, 0x17, /* 90-9f: */
+ 0x38, 0x39, 0x3a, 0x3b, 0x1a, 0x1b, 0x3e, 0xff, /* ................ */
+ 0x41, 0xaa, 0x4a, 0xb1, 0x9f, 0xb2, 0x6a, 0xb5, /* a0-af: */
+ 0xbb, 0xb4, 0x9a, 0x8a, 0xb0, 0xca, 0xaf, 0xbc, /* ................ */
+ 0x90, 0x8f, 0xea, 0xfa, 0xbe, 0xa0, 0xb6, 0xb3, /* b0-bf: */
+ 0x9d, 0xda, 0x9b, 0x8b, 0xb7, 0xb8, 0xb9, 0xab, /* ................ */
+ 0x64, 0x65, 0x62, 0x66, 0x63, 0x67, 0x9e, 0x68, /* c0-cf: */
+ 0x74, 0x71, 0x72, 0x73, 0x78, 0x75, 0x76, 0x77, /* ................ */
+ 0xac, 0x69, 0xed, 0xee, 0xeb, 0xef, 0xec, 0xbf, /* d0-df: */
+ 0x80, 0xfd, 0xfe, 0xfb, 0xfc, 0xba, 0xae, 0x59, /* ................ */
+ 0x44, 0x45, 0x42, 0x46, 0x43, 0x47, 0x9c, 0x48, /* e0-ef: */
+ 0x54, 0x51, 0x52, 0x53, 0x58, 0x55, 0x56, 0x57, /* ................ */
+ 0x8c, 0x49, 0xcd, 0xce, 0xcb, 0xcf, 0xcc, 0xe1, /* f0-ff: */
+ 0x70, 0xdd, 0xde, 0xdb, 0xdc, 0x8d, 0x8e, 0xdf /* ................ */
+};
+
+/* Translate a memory block from EBCDIC (host charset) to ASCII (net charset)
+ * dest and srce may be identical, or separate memory blocks, but
+ * should not overlap.
+ */
+void
+ebcdic2ascii(void *dest, const void *srce, size_t count)
+{
+ unsigned char *udest = dest;
+ const unsigned char *usrce = srce;
+ while (count-- != 0) {
+ *udest++ = os_toascii[*usrce++];
+ }
+}
+void
+ebcdic2ascii_strictly(unsigned char *dest, const unsigned char *srce, size_t count)
+{
+ while (count-- != 0) {
+ *dest++ = os_toascii_strictly[*srce++];
+ }
+}
+void
+ascii2ebcdic(void *dest, const void *srce, size_t count)
+{
+ unsigned char *udest = dest;
+ const unsigned char *usrce = srce;
+
+ while (count-- != 0) {
+ *udest++ = os_toebcdic[*usrce++];
+ }
+}
+#endif /*CHARSET_EBCDIC*/
+
--- /dev/null
+#include <sys/types.h>
+
+extern const unsigned char os_toascii[256];
+extern const unsigned char os_toebcdic[256];
+void ebcdic2ascii(void *dest, const void *srce, size_t count);
+void ebcdic2ascii_strictly(unsigned char *dest, const unsigned char *srce, size_t count);
+void ascii2ebcdic(void *dest, const void *srce, size_t count);
+
--- /dev/null
+/*
+ * This file contains functions which can be inlined if the compiler
+ * has an "inline" modifier. Because of this, this file is both a
+ * header file and a compilable module.
+ *
+ * Only inlineable functions should be defined in here. They must all
+ * include the INLINE modifier.
+ *
+ * If the compiler supports inline, this file will be #included as a
+ * header file from os.h to create all the inline function
+ * definitions. INLINE will be defined to whatever is required on
+ * function definitions to make them inline declarations.
+ *
+ * If the compiler does not support inline, this file will be compiled
+ * as a normal C file into libos.a (along with os.c). In this case
+ * INLINE will _not_ be set so we can use this to test if we are
+ * compiling this source file.
+ */
+
+#ifndef INLINE
+#define INLINE
+
+/* Anything required only when compiling */
+#include "ap_config.h"
+
+#endif
+
+INLINE int ap_os_is_path_absolute(const char *file)
+{
+ return (file && file[0] == '/' ? 1 : 0);
+}
--- /dev/null
+/* ====================================================================
+ * Copyright (c) 1998-1999 The Apache Group. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * 4. The names "Apache Server" and "Apache Group" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission. For written permission, please contact
+ * apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ * nor may "Apache" appear in their names without prior written
+ * permission of the Apache Group.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Group and was originally based
+ * on public domain software written at the National Center for
+ * Supercomputing Applications, University of Illinois, Urbana-Champaign.
+ * For more information on the Apache Group and the Apache HTTP server
+ * project, please see <http://www.apache.org/>.
+ *
+ */
+
+/*
+ * This file will include OS specific functions which are not inlineable.
+ * Any inlineable functions should be defined in os-inline.c instead.
+ */
+
+#include "httpd.h"
+#include "http_core.h"
+#include "os.h"
+#include "scoreboard.h"
+#include "http_log.h"
+#include "http_conf_globals.h"
+
+static FILE *sock_fp;
+
+/* Check the Content-Type to decide if conversion is needed */
+int ap_checkconv(struct request_rec *r)
+{
+ int convert_to_ascii;
+ const char *type;
+
+ /* To make serving of "raw ASCII text" files easy (they serve faster
+ * since they don't have to be converted from EBCDIC), a new
+ * "magic" type prefix was invented: text/x-ascii-{plain,html,...}
+ * If we detect one of these content types here, we simply correct
+ * the type to the real text/{plain,html,...} type. Otherwise, we
+ * set a flag that translation is required later on.
+ */
+
+ type = (r->content_type == NULL) ? ap_default_type(r) : r->content_type;
+
+ /* If no content type is set then treat it as (ebcdic) text/plain */
+ convert_to_ascii = (type == NULL);
+
+ /* Conversion is applied to text/ files only, if ever. */
+ if (type && (strncasecmp(type, "text/", 5) == 0 ||
+ strncasecmp(type, "message/", 8) == 0)) {
+ if (strncasecmp(type, ASCIITEXT_MAGIC_TYPE_PREFIX,
+ sizeof(ASCIITEXT_MAGIC_TYPE_PREFIX)-1) == 0){
+ r->content_type = ap_pstrcat(r->pool, "text/",
+ type+sizeof(ASCIITEXT_MAGIC_TYPE_PREFIX)-1, NULL);
+ if (r->method_number == M_PUT)
+ ap_bsetflag(r->connection->client, B_ASCII2EBCDIC, 0);
+ }
+
+ else
+ /* translate EBCDIC to ASCII */
+ convert_to_ascii = 1;
+ }
+ else{
+ if (r->method_number == M_PUT)
+ ap_bsetflag(r->connection->client, B_ASCII2EBCDIC, 0);
+ /* don't translate non-text files to EBCDIC */
+ }
+ /* Enable conversion if it's a text document */
+ ap_bsetflag(r->connection->client, B_EBCDIC2ASCII, convert_to_ascii);
+
+ return convert_to_ascii;
+}
+
+int tpf_select(int maxfds, fd_set *reads, fd_set *writes, fd_set *excepts, struct timeval *tv)
+{
+/* We're going to force our way through select. We're only interested reads and TPF allows
+ 2billion+ socket descriptors for we don't want an fd_set that big. Just assume that maxfds-1
+ contains the socket descriptor we're interested in. If it's 0, leave it alone. */
+
+ int sockets[1];
+ int no_reads = 0;
+ int no_writes = 0;
+ int no_excepts = 0;
+ int timeout = 0;
+ int rv;
+
+ if(maxfds) {
+ if(tv)
+ timeout = tv->tv_sec * 1000 + tv->tv_usec;
+ sockets[0] = maxfds-1;
+ no_reads++;
+ }
+ else
+ sockets[0] = 0;
+
+ ap_check_signals();
+ rv = select(sockets, no_reads, no_writes, no_excepts, timeout);
+ ap_check_signals();
+
+ return rv;
+
+}
+
+int tpf_accept(int sockfd, struct sockaddr *peer, int *paddrlen)
+{
+ int socks[1];
+ int rv;
+
+ ap_check_signals();
+ socks[0] = sockfd;
+ rv = select(socks, 1, 0, 0, 1000);
+ errno = sock_errno();
+ if(rv>0) {
+ ap_check_signals();
+ rv = accept(sockfd, peer, paddrlen);
+ errno = sock_errno();
+ }
+ return rv;
+}
+
+/* the getpass function is not usable on TPF */
+char *getpass(const char* prompt)
+{
+ errno = EIO;
+ return((char *)NULL);
+}
+
+#ifndef __PIPE_
+int pipe(int fildes[2])
+{
+ errno = ENOSYS;
+ return(-1);
+}
+#endif
+
+/* fork and exec functions are not defined on
+ TPF due to the implementation of tpf_fork() */
+
+pid_t fork(void)
+{
+ errno = ENOSYS;
+ return(-1);
+}
+
+int execl(const char *path, const char *arg0, ...)
+{
+ errno = ENOSYS;
+ return(-1);
+}
+
+int execle(const char *path, const char *arg0, ...)
+{
+ errno = ENOSYS;
+ return(-1);
+}
+
+int execve(const char *path, char *const argv[], char *const envp[])
+{
+ errno = ENOSYS;
+ return(-1);
+}
+
+int execvp(const char *file, char *const argv[])
+{
+ errno = ENOSYS;
+ return(-1);
+}
+
+
+
+int ap_tpf_spawn_child(pool *p, int (*func) (void *, child_info *),
+ void *data, enum kill_conditions kill_how,
+ int *pipe_in, int *pipe_out, int *pipe_err,
+ int out_fds[], int in_fds[], int err_fds[])
+
+{
+
+ int i, temp_out, temp_in, temp_err, save_errno, pid, result=0;
+ int fd_flags_out, fd_flags_in, fd_flags_err;
+ struct tpf_fork_input fork_input;
+ TPF_FORK_CHILD *cld = (TPF_FORK_CHILD *) data;
+ array_header *env_arr = ap_table_elts ((array_header *) cld->subprocess_env);
+ table_entry *elts = (table_entry *) env_arr->elts;
+
+
+
+ if (func) {
+ if (result=func(data, NULL)) {
+ return 0; /* error from child function */
+ }
+ }
+
+ if (pipe_out) {
+ fd_flags_out = fcntl(out_fds[0], F_GETFD);
+ fcntl(out_fds[0], F_SETFD, FD_CLOEXEC);
+ temp_out = dup(STDOUT_FILENO);
+ fcntl(temp_out, F_SETFD, FD_CLOEXEC);
+ dup2(out_fds[1], STDOUT_FILENO);
+ }
+
+
+ if (pipe_in) {
+ fd_flags_in = fcntl(in_fds[1], F_GETFD);
+ fcntl(in_fds[1], F_SETFD, FD_CLOEXEC);
+ temp_in = dup(STDIN_FILENO);
+ fcntl(temp_in, F_SETFD, FD_CLOEXEC);
+ dup2(in_fds[0], STDIN_FILENO);
+ }
+
+ if (pipe_err) {
+ fd_flags_err = fcntl(err_fds[0], F_GETFD);
+ fcntl(err_fds[0], F_SETFD, FD_CLOEXEC);
+ temp_err = dup(STDERR_FILENO);
+ fcntl(temp_err, F_SETFD, FD_CLOEXEC);
+ dup2(err_fds[1], STDERR_FILENO);
+ }
+
+ if (cld->subprocess_env) {
+ for (i = 0; i < env_arr->nelts; ++i) {
+ if (!elts[i].key)
+ continue;
+ setenv (elts[i].key, elts[i].val, 1);
+ }
+ }
+
+ fork_input.program = (const char*) cld->filename;
+ fork_input.prog_type = cld->prog_type;
+ fork_input.istream = TPF_FORK_IS_BALANCE;
+ fork_input.ebw_data_length = 0;
+ fork_input.ebw_data = NULL;
+ fork_input.parm_data = NULL;
+
+
+ if ((pid = tpf_fork(&fork_input)) < 0) {
+ save_errno = errno;
+ if (pipe_out) {
+ close(out_fds[0]);
+ }
+ if (pipe_in) {
+ close(in_fds[1]);
+ }
+ if (pipe_err) {
+ close(err_fds[0]);
+ }
+ errno = save_errno;
+ pid = 0;
+ }
+
+ if (cld->subprocess_env) {
+ for (i = 0; i < env_arr->nelts; ++i) {
+ if (!elts[i].key)
+ continue;
+ unsetenv (elts[i].key);
+ }
+ }
+
+ if (pipe_out) {
+ close(out_fds[1]);
+ dup2(temp_out, STDOUT_FILENO);
+ close(temp_out);
+ fcntl(out_fds[0], F_SETFD, fd_flags_out);
+ }
+
+ if (pipe_in) {
+ close(in_fds[0]);
+ dup2(temp_in, STDIN_FILENO);
+ close(temp_in);
+ fcntl(in_fds[1], F_SETFD, fd_flags_in);
+ }
+
+
+ if (pipe_err) {
+ close(err_fds[1]);
+ dup2(temp_err, STDERR_FILENO);
+ close(temp_err);
+ fcntl(err_fds[0], F_SETFD, fd_flags_err);
+ }
+
+
+ if (pid) {
+
+ ap_note_subprocess(p, pid, kill_how);
+
+ if (pipe_out) {
+ *pipe_out = out_fds[0];
+ }
+ if (pipe_in) {
+ *pipe_in = in_fds[1];
+ }
+ if (pipe_err) {
+ *pipe_err = err_fds[0];
+ }
+ }
+
+ return pid;
+
+}
+
+pid_t os_fork(server_rec *s, int slot)
+{
+ struct tpf_fork_input fork_input;
+ APACHE_TPF_INPUT input_parms;
+ int count;
+ listen_rec *lr;
+
+ fflush(stdin);
+ if (dup2(fileno(sock_fp), STDIN_FILENO) == -1)
+ ap_log_error(APLOG_MARK, APLOG_CRIT, s,
+ "unable to replace stdin with sock device driver");
+ fflush(stdout);
+ if (dup2(fileno(sock_fp), STDOUT_FILENO) == -1)
+ ap_log_error(APLOG_MARK, APLOG_CRIT, s,
+ "unable to replace stdout with sock device driver");
+ input_parms.generation = ap_my_generation;
+#ifdef SCOREBOARD_FILE
+ input_parms.scoreboard_fd = scoreboard_fd;
+#else /* must be USE_TPF_SCOREBOARD or USE_SHMGET_SCOREBOARD */
+ input_parms.scoreboard_heap = ap_scoreboard_image;
+#endif
+
+ lr = ap_listeners;
+ count = 0;
+ do {
+ input_parms.listeners[count] = lr->fd;
+ lr = lr->next;
+ count++;
+ } while(lr != ap_listeners);
+
+ input_parms.slot = slot;
+ input_parms.restart_time = ap_restart_time;
+ fork_input.ebw_data = &input_parms;
+ fork_input.program = ap_server_argv0;
+ fork_input.prog_type = TPF_FORK_NAME;
+ fork_input.istream = TPF_FORK_IS_BALANCE;
+ fork_input.ebw_data_length = sizeof(input_parms);
+ fork_input.parm_data = "-x";
+ return tpf_fork(&fork_input);
+}
+
+int os_check_server(char *server) {
+ #ifndef USE_TPF_DAEMON
+ int rv;
+ int *current_acn;
+ if((rv = inetd_getServerStatus(server)) == INETD_SERVER_STATUS_INACTIVE)
+ return 1;
+ else {
+ current_acn = (int *)cinfc_fast(CINFC_CMMACNUM);
+ if(ecbp2()->ce2acn != *current_acn)
+ return 1;
+ }
+ #endif
+ return 0;
+}
+
+void os_note_additional_cleanups(pool *p, int sd) {
+ char sockfilename[50];
+ /* write the socket to file so that TPF socket device driver will close socket in case
+ we happen to abend. */
+ sprintf(sockfilename, "/dev/tpf.socket.file/%.8X", sd);
+ sock_fp = fopen(sockfilename, "r+");
+ ap_note_cleanups_for_file(p, sock_fp); /* arrange to close on exec or restart */
+ fcntl(sd,F_SETFD,FD_CLOEXEC);
+}
+
+void os_tpf_child(APACHE_TPF_INPUT *input_parms) {
+ tpf_child = 1;
+ ap_my_generation = input_parms->generation;
+ ap_restart_time = input_parms->restart_time;
+}
+
+
--- /dev/null
+#ifndef APACHE_OS_H
+#define APACHE_OS_H
+
+#define PLATFORM "TPF"
+
+#ifdef errno
+#undef errno
+#endif
+
+/*
+ * This file in included in all Apache source code. It contains definitions
+ * of facilities available on _this_ operating system (HAVE_* macros),
+ * and prototypes of OS specific functions defined in os.c or os-inline.c
+ */
+
+#include "ap_config.h"
+
+#if !defined(INLINE) && defined(USE_GNU_INLINE)
+/* Compiler supports inline, so include the inlineable functions as
+ * part of the header
+ */
+#define INLINE extern ap_inline
+#include "os-inline.c"
+#endif
+
+#ifndef INLINE
+/* Compiler does not support inline, so prototype the inlineable functions
+ * as normal
+ */
+extern int ap_os_is_path_absolute(const char *f);
+#endif
+
+/* Other ap_os_ routines not used by this platform */
+
+#define ap_os_is_filename_valid(f) (1)
+#define ap_os_kill(pid, sig) kill(pid, sig)
+
+/* Sorry if this is ugly, but the include order doesn't allow me
+ * to use request_rec here... */
+struct request_rec;
+extern int ap_checkconv(struct request_rec *r);
+
+#include <strings.h>
+#ifndef __strings_h
+
+#define FD_SETSIZE 2048
+
+typedef long fd_mask;
+
+#define NBBY 8 /* number of bits in a byte */
+#define NFDBITS (sizeof(fd_mask) * NBBY)
+#define howmany(x, y) (((x)+((y)-1))/(y))
+
+typedef struct fd_set {
+ fd_mask fds_bits [howmany(FD_SETSIZE, NFDBITS)];
+} fd_set;
+
+#define FD_CLR(n, p)((p)->fds_bits[(n)/NFDBITS] &= ~(1 << ((n) % NFDBITS)))
+#define FD_ISSET(n, p)((p)->fds_bits[(n)/NFDBITS] & (1 <<((n) % NFDBITS)))
+#define FD_ZERO(p) memset((char *)(p), 0, sizeof(*(p)))
+#endif
+
+#ifdef FD_SET
+#undef FD_SET
+#define FD_SET(n, p) (0)
+#endif
+
+#define RESOURCE_KEY ((void*) 0xC1C2C1C3)
+
+/* TPF doesn't have, or need, tzset (it is used in mod_expires.c) */
+#define tzset()
+
+#include <i$netd.h>
+struct apache_input {
+ INETD_SERVER_INPUT inetd_server;
+ void *scoreboard_heap; /* scoreboard system heap address */
+ int scoreboard_fd; /* scoreboard file descriptor */
+ int slot; /* child number */
+ int generation; /* server generation number */
+ int listeners[10];
+ time_t restart_time;
+};
+
+typedef struct apache_input APACHE_TPF_INPUT;
+
+typedef struct tpf_fork_child {
+ char *filename;
+ enum { FORK_NAME = 1, FORK_FILE = 2 } prog_type;
+ void *subprocess_env;
+}TPF_FORK_CHILD;
+
+int tpf_accept(int sockfd, struct sockaddr *peer, int *paddrlen);
+extern int tpf_child;
+
+struct server_rec;
+pid_t os_fork(struct server_rec *s, int slot);
+int os_check_server(char *server);
+char *getpass(const char *prompt);
+extern char *ap_server_argv0;
+extern int scoreboard_fd;
+#include <signal.h>
+#ifndef SIGPIPE
+#define SIGPIPE 14
+#endif
+#ifdef NSIG
+#undef NSIG
+#endif
+#endif /*! APACHE_OS_H*/
--- /dev/null
+//APACH JOB MSGLEVEL=(1,1),CLASS=A,MSGCLASS=A\r
+/*ROUTE PRINT XXXXXX.XXXXXX\r
+/*ROUTE PUNCH XXXXXX.XXXXXX\r
+/*NOTIFY XXXXXX.XXXXXX\r
+//CCLE JCLLIB ORDER=(SYS1.CBC.SCBCPRC,SYS1.CEE.SCEEPROC)\r
+//PRELINK EXEC EDCPL,COND.LKED=(0,NE),\r
+// PPARM='OMVS,DLLNAME(pppp)',\r
+// LREGSIZ='2048K',\r
+// LPARM='AMODE=31,RMODE=ANY,LIST,XREF'\r
+//PLKED.SYSLIB DD DISP=SHR,DSN=FSE0000.DEVP.STUB.OB\r
+// DD DISP=SHR,DSN=FSE0000.DEVP.CLIB.OB\r
+// DD DISP=SHR,DSN=ACP.CLIB.RLSE46.WEB\r
+// DD DISP=SHR,DSN=ACP.STUB.RLSE46.WEB\r
+// DD DISP=SHR,DSN=ACP.CLIB.RLSE40\r
+// DD DISP=SHR,DSN=ACP.STUB.RLSE40\r
+//PLKED.SYSDEFSD DD DSN=APA0000.DEVP.IMPORTS.DSD(ppppvv),DISP=SHR\r
+//PLKED.DSD DD DSN=APA0000.DEVP.IMPORTS.DSD,DISP=SHR\r
+//PLKED.OBJLIB DD DISP=SHR,DSN=FSE0000.DEVP.TEST.OB\r
+// DD DISP=SHR,DSN=ACP.OBJ.RLSE46.WEB\r
+// DD DISP=SHR,DSN=ACP.OBJ.INTG98.NBS\r
+// DD DISP=SHR,DSN=ACP.MAIN.SYST.OBBSS\r
+// DD DISP=SHR,DSN=ACP.DF.MAIN.SYST.OBBSS\r
+// DD DISP=SHR,DSN=ACP.OBJ.RLSE40.BSS\r
+//PLKED.OBJ1 DD PATH='/usr/local/apache/src/ap/ap_cpystrn.o'\r
+//PLKED.OBJ2 DD PATH='/usr/local/apache/src/ap/ap_execve.o'\r
+//PLKED.OBJ3 DD PATH='/usr/local/apache/src/ap/ap_signal.o'\r
+//PLKED.OBJ4 DD PATH='/usr/local/apache/src/ap/ap_slack.o'\r
+//PLKED.OBJ5 DD PATH='/usr/local/apache/src/ap/ap_snprintf.o'\r
+//PLKED.OBJ6 DD PATH='/usr/local/apache/src/ap/ap_strings.o'\r
+//PLKED.OBJ7 DD PATH='/usr/local/apache/src/os/tpf/ebcdic.o'\r
+//PLKED.OBJ8 DD PATH='/usr/local/apache/src/os/tpf/os.o'\r
+//PLKED.OBJ9 DD PATH='/usr/local/apache/src/os/tpf/os-inline.o'\r
+//PLKED.OBJ10 DD PATH='/usr/local/apache/src/regex/regcomp.o'\r
+//PLKED.OBJ11 DD PATH='/usr/local/apache/src/regex/regerror.o'\r
+//PLKED.OBJ12 DD PATH='/usr/local/apache/src/regex/regexec.o'\r
+//PLKED.OBJ13 DD PATH='/usr/local/apache/src/regex/regfree.o'\r
+//PLKED.OBJ14 DD PATH='/usr/local/apache/src/main/alloc.o'\r
+//PLKED.OBJ15 DD PATH='/usr/local/apache/src/main/buff.o'\r
+//PLKED.OBJ16 DD PATH='/usr/local/apache/src/main/fnmatch.o'\r
+//PLKED.OBJ17 DD PATH='/usr/local/apache/src/main/http_config.o'\r
+//PLKED.OBJ18 DD PATH='/usr/local/apache/src/main/http_core.o'\r
+//PLKED.OBJ19 DD PATH='/usr/local/apache/src/main/http_log.o'\r
+//PLKED.OBJ20 DD PATH='/usr/local/apache/src/main/http_main.o'\r
+//PLKED.OBJ21 DD PATH='/usr/local/apache/src/main/http_protocol.o'\r
+//PLKED.OBJ22 DD PATH='/usr/local/apache/src/main/http_request.o'\r
+//PLKED.OBJ23 DD PATH='/usr/local/apache/src/main/http_vhost.o'\r
+//PLKED.OBJ24 DD PATH='/usr/local/apache/src/main/md5c.o'\r
+//PLKED.OBJ25 DD PATH='/usr/local/apache/src/main/rfc1413.o'\r
+//PLKED.OBJ26 DD PATH='/usr/local/apache/src/main/util.o'\r
+//PLKED.OBJ27 DD PATH='/usr/local/apache/src/main/util_date.o'\r
+//PLKED.OBJ28 DD PATH='/usr/local/apache/src/main/util_md5.o'\r
+//PLKED.OBJ29 DD PATH='/usr/local/apache/src/main/util_script.o'\r
+//PLKED.OBJ30 DD PATH='/usr/local/apache/src/main/util_uri.o'\r
+//PLKED.OBJ31 DD PATH='/usr/local/apache/src/modules.o'\r
+//PLKED.OBJ32 DD PATH='/usr/local/apache/src/buildmark.o'\r
+//PLKED.OBJ33 DD PATH='/usr/local/apache/src/modules/standard/mod_auto\\r
+// index.o'\r
+//PLKED.OBJ34 DD PATH='/usr/local/apache/src/modules/standard/mod_dir.\\r
+// o'\r
+//PLKED.OBJ35 DD PATH='/usr/local/apache/src/modules/standard/mod_mime\\r
+// .o'\r
+//PLKED.OBJ36 DD PATH='/usr/local/apache/src/modules/standard/mod_sete\\r
+// nvif.o'\r
+//PLKED.OBJ37 DD PATH='/usr/local/apache/src/modules/standard/mod_alia\\r
+// s.o'\r
+//PLKED.OBJ38 DD PATH='/usr/local/apache/src/modules/standard/mod_acce\\r
+// ss.o'\r
+//PLKED.OBJ39 DD PATH='/usr/local/apache/src/modules/standard/mod_user\\r
+// dir.o'\r
+//PLKED.OBJ40 DD PATH='/usr/local/apache/src/modules/standard/mod_spel\\r
+// ing.o'\r
+//PLKED.OBJ41 DD PATH='/usr/local/apache/src/modules/standard/mod_nego\\r
+// tiation.o'\r
+//PLKED.SYSIN DD *\r
+ ORDER @@DLMHDR\r
+ INCLUDE OBJLIB(CSTRTD40)\r
+ INCLUDE OBJ1\r
+ INCLUDE OBJ2\r
+ INCLUDE OBJ3\r
+ INCLUDE OBJ4\r
+ INCLUDE OBJ5\r
+ INCLUDE OBJ6\r
+ INCLUDE OBJ7\r
+ INCLUDE OBJ8\r
+ INCLUDE OBJ9\r
+ INCLUDE OBJ10\r
+ INCLUDE OBJ11\r
+ INCLUDE OBJ12\r
+ INCLUDE OBJ13\r
+ INCLUDE OBJ14\r
+ INCLUDE OBJ15\r
+ INCLUDE OBJ16\r
+ INCLUDE OBJ17\r
+ INCLUDE OBJ18\r
+ INCLUDE OBJ19\r
+ INCLUDE OBJ20\r
+ INCLUDE OBJ21\r
+ INCLUDE OBJ22\r
+ INCLUDE OBJ23\r
+ INCLUDE OBJ24\r
+ INCLUDE OBJ25\r
+ INCLUDE OBJ26\r
+ INCLUDE OBJ27\r
+ INCLUDE OBJ28\r
+ INCLUDE OBJ29\r
+ INCLUDE OBJ30\r
+ INCLUDE OBJ31\r
+ INCLUDE OBJ32\r
+ INCLUDE OBJ33\r
+ INCLUDE OBJ34\r
+ INCLUDE OBJ35\r
+ INCLUDE OBJ36\r
+ INCLUDE OBJ37\r
+ INCLUDE OBJ38\r
+ INCLUDE OBJ39\r
+ INCLUDE OBJ40\r
+ INCLUDE OBJ41\r
+/*\r
+//*** WARNING *** NEVER change .LK to .OB in SYSLMOD!!!\r
+//LKED.SYSLMOD DD DISP=OLD,DSN=xxxxxx.xxxx(ppppvv)\r
+//\r
--- /dev/null
+//OLDRWEB JOB MSGLEVEL=1,CLASS=A,MSGCLASS=S\r
+//JOBCAT DD DSN=ICFCAT.ESAWK2,DISP=SHR\r
+/*ROUTE PRINT xxxxxx.xxxxxxx\r
+/*ROUTE PUNCH xxxxxx.xxxxxxx\r
+//TLDR EXEC PGM=TPFLDRCA,REGION=8M,\r
+// PARM='OLDR,SYS=ACP,CLMSIZE=8000000'\r
+//STEPLIB DD DSN=ACP.LINK.RLSE46.WEB,DISP=SHR\r
+// DD DSN=ACP.LINK.RLSE40.BSS,DISP=SHR\r
+// DD DSN=VIS0000.DEVP.TEST.LK,DISP=SHR\r
+// DD DSN=SYS1.CEE.SCEERUN,DISP=SHR\r
+//SALTB DD DSN=ACP.SALTBL.RLSE46.WEB,DISP=SHR\r
+// DD DSN=ACP.SALTBL.INTG46.WEB,DISP=SHR\r
+//OBJLIB DD DSN=FSE0000.DEVP.TEST.OB,DISP=SHR\r
+// DD DSN=APA0000.DEVP.TEST.OB,DISP=SHR\r
+// DD DSN=ACP.DRVE.TEST.OB,DISP=SHR\r
+// DD DSN=ACP.OBJ.RLSE46.WEB,DISP=SHR\r
+// DD DSN=ACP.OBJ.INTG36.DRV,DISP=SHR\r
+// DD DSN=ACP.OBJ.INTG46.WEB,DISP=SHR\r
+// DD DSN=ACP.OBJ.INTG40.BSS,DISP=SHR\r
+//LOADMOD DD DSN=FSE0000.DEVP.TEST.LK,DISP=SHR\r
+// DD DSN=APA0000.DEVP.TEST.LK,DISP=SHR\r
+// DD DSN=CWEISS.LINK,DISP=SHR\r
+// DD DSN=ACP.DRVE.TEST.LK,DISP=SHR\r
+// DD DSN=ACP.LINK.RLSE46.WEB,DISP=SHR\r
+// DD DSN=ACP.LINK.INTG98.NBS,DISP=SHR\r
+// DD DSN=ACP.LINK.INTG46.WEB,DISP=SHR\r
+// DD DSN=ACP.LINK.INTG36.DRV,DISP=SHR\r
+// DD DSN=ACP.LINK.INTG40.BSS,DISP=SHR\r
+//LOADSUM DD DSN=&&LOADSUM,DISP=(NEW,PASS),UNIT=SYSDA,\r
+// LRECL=133,SPACE=(TRK,(10,10)),RECFM=FBA\r
+//CPRTEMP DD UNIT=SYSDA,\r
+// DSN=&&CPRTEMP,SPACE=(TRK,(100,20)),\r
+// DCB=(RECFM=FB,BLKSIZE=4095,LRECL=4095),\r
+// DISP=(NEW,DELETE)\r
+//PROGTEMP DD UNIT=SYSDA,\r
+// DSN=&&PRTEMP,SPACE=(TRK,(100,20)),\r
+// DCB=(RECFM=FB,BLKSIZE=4095,LRECL=4095),\r
+// DISP=(NEW,DELETE)\r
+//OUTPUT DD DSN=&&VRDROUT,DISP=(NEW,PASS),UNIT=SYSDA,\r
+// DCB=(RECFM=F,BLKSIZE=4095,LRECL=4095)\r
+//SYSUDUMP DD DUMMY\r
+//SYSABEND DD DUMMY\r
+//SYSOUT DD SYSOUT=A\r
+//SYSPRINT DD SYSOUT=A\r
+//PRINTER DD SYSOUT=A\r
+//CEEDUMP DD SYSOUT=A\r
+//SYSIN DD *\r
+SYSID=BSS\r
+PATVERS=NONE\r
+SALVERS=40\r
+LOADER LOADSET lllllll\r
+LOADER CALL PROG ppppvv\r
+/*\r
+//TRANSMIT EXEC PGM=IKJEFT01,\r
+// PARM='TRANSMIT xxxxxx.xxxxxx DDNAME(SYSTSIN) NOLOG NONOTIFY SEQ'\r
+//SYSTSIN DD UNIT=SYSDA,\r
+// DSN=&&VRDROUT,DISP=(OLD,DELETE)\r
+//SYSTSPRT DD DUMMY\r
--- /dev/null
+/*
+ * This file contains functions which can be inlined if the compiler
+ * has an "inline" modifier. Because of this, this file is both a
+ * header file and a compilable module.
+ *
+ * Only inlineable functions should be defined in here. They must all
+ * include the INLINE modifier.
+ *
+ * If the compiler supports inline, this file will be #included as a
+ * header file from os.h to create all the inline function
+ * definitions. INLINE will be defined to whatever is required on
+ * function definitions to make them inline declarations.
+ *
+ * If the compiler does not support inline, this file will be compiled
+ * as a normal C file into libos.a (along with os.c). In this case
+ * INLINE will _not_ be set so we can use this to test if we are
+ * compiling this source file.
+ */
+
+#ifndef INLINE
+#define INLINE
+
+/* Anything required only when compiling */
+#include "ap_config.h"
+
+#endif
+
+INLINE int ap_os_is_path_absolute(const char *file)
+{
+ return file[0] == '/';
+}
--- /dev/null
+/* ====================================================================
+ * Copyright (c) 1998-1999 The Apache Group. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * 4. The names "Apache Server" and "Apache Group" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission. For written permission, please contact
+ * apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ * nor may "Apache" appear in their names without prior written
+ * permission of the Apache Group.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Group and was originally based
+ * on public domain software written at the National Center for
+ * Supercomputing Applications, University of Illinois, Urbana-Champaign.
+ * For more information on the Apache Group and the Apache HTTP server
+ * project, please see <http://www.apache.org/>.
+ *
+ */
+
+#ifndef APACHE_OS_H
+#define APACHE_OS_H
+
+#include "ap_config.h"
+
+#ifndef PLATFORM
+#define PLATFORM "Unix"
+#endif
+
+/*
+ * This file in included in all Apache source code. It contains definitions
+ * of facilities available on _this_ operating system (HAVE_* macros),
+ * and prototypes of OS specific functions defined in os.c or os-inline.c
+ */
+
+#if !defined(INLINE) && defined(USE_GNU_INLINE)
+/* Compiler supports inline, so include the inlineable functions as
+ * part of the header
+ */
+#define INLINE extern ap_inline
+
+INLINE int ap_os_is_path_absolute(const char *file);
+
+#include "os-inline.c"
+
+#else
+
+/* Compiler does not support inline, so prototype the inlineable functions
+ * as normal
+ */
+extern int ap_os_is_path_absolute(const char *file);
+#endif
+
+/* Other ap_os_ routines not used by this platform */
+
+#define ap_os_is_filename_valid(f) (1)
+#define ap_os_kill(pid, sig) kill(pid, sig)
+
+/*
+ * Abstraction layer for loading
+ * Apache modules under run-time via
+ * dynamic shared object (DSO) mechanism
+ */
+
+#ifdef HAVE_DL_H
+#include <dl.h>
+#endif
+
+/*
+ * Do not use native AIX DSO support
+ */
+#ifdef AIX
+#undef HAVE_DLFCN_H
+#endif
+
+#ifdef HAVE_DLFCN_H
+#include <dlfcn.h>
+#else
+void *dlopen(const char *, int);
+int dlclose(void *);
+void *dlsym(void *, const char *);
+const char *dlerror(void);
+#endif
+
+/* probably on an older system that doesn't support RTLD_NOW or RTLD_LAZY.
+ * The below define is a lie since we are really doing RTLD_LAZY since the
+ * system doesn't support RTLD_NOW.
+ */
+#ifndef RTLD_NOW
+#define RTLD_NOW 1
+#endif
+
+#ifndef RTLD_GLOBAL
+#define RTLD_GLOBAL 0
+#endif
+
+#if (defined(__FreeBSD__) ||\
+ defined(__OpenBSD__) ||\
+ defined(__NetBSD__) ) && !defined(__ELF__)
+#define DLSYM_NEEDS_UNDERSCORE
+#endif
+
+#define ap_os_dso_handle_t void *
+void ap_os_dso_init(void);
+void * ap_os_dso_load(const char *);
+void ap_os_dso_unload(void *);
+void * ap_os_dso_sym(void *, const char *);
+const char *ap_os_dso_error(void);
+
+#endif /* !APACHE_OS_H */
--- /dev/null
+*.mdp
+*.ncb
+*.opt
+*.plg
+*.dsw
+ApacheModuleAuthAnonD
+ApacheModuleAuthAnonR
+ApacheModuleCERNMetaD
+ApacheModuleCERNMetaR
+ApacheModuleDigestD
+ApacheModuleDigestR
+ApacheModuleExpiresD
+ApacheModuleExpiresR
+ApacheModuleHeadersD
+ApacheModuleHeadersR
+ApacheModuleInfoD
+ApacheModuleInfoR
+ApacheModuleRewriteD
+ApacheModuleRewriteR
+ApacheModuleSpelingD
+ApacheModuleSpelingR
+ApacheModuleStatusD
+ApacheModuleStatusR
+ApacheModuleUserTrackD
+ApacheModuleUserTrackR
+ApacheOSR
+ApacheOSD
--- /dev/null
+#include <fstream.h>
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+void MakeMake(const char *szModule,const char *szSource)
+ {
+ ifstream ifs("Module.mak.tmpl",ios::nocreate);
+ assert(ifs.good());
+
+ char buf[1024];
+ sprintf(buf,"%s.mak",szModule);
+ ofstream ofs(buf,ios::trunc);
+ for( ; ; )
+ {
+ ifs.getline(buf,sizeof buf);
+ if(ifs.eof())
+ break;
+ for(char *s=buf ; *s ; )
+ {
+ char *p=strchr(s,'%');
+ if(!p)
+ {
+ ofs << s << '\n';
+ break;
+ }
+ if(!strncmp(p,"%Module%",8))
+ {
+ ofs.write(s,p-s);
+ ofs << szModule;
+ s=p+8;
+ }
+ else if(!strncmp(p,"%Source%",8))
+ {
+ ofs.write(s,p-s);
+ ofs << szSource;
+ s=p+8;
+ }
+ else
+ {
+ ofs.write(s,p-s+1);
+ s=p+1;
+ }
+ }
+ }
+ }
+
+void main(int argc,char **argv)
+ {
+ if(argc < 2 || (argc%2) != 1)
+ {
+ cerr << argv[0] << " [<module name> <source file>]+\n";
+ exit(1);
+ }
+ for(int n=1 ; n < argc ; n+=2)
+ MakeMake(argv[n],argv[n+1]);
+ }
+
--- /dev/null
+# Microsoft Developer Studio Generated NMAKE File, Format Version 4.20
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+!IF "$(CFG)" == ""
+CFG=%Module% - Win32 Debug
+!MESSAGE No configuration specified. Defaulting to %Module% - Win32\
+ Debug.
+!ENDIF
+
+!IF "$(CFG)" != "%Module% - Win32 Release" && "$(CFG)" !=\
+ "%Module% - Win32 Debug"
+!MESSAGE Invalid configuration "$(CFG)" specified.
+!MESSAGE You can specify a configuration when running NMAKE on this makefile
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "%Module%.mak"\
+ CFG="%Module% - Win32 Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "%Module% - Win32 Release" (based on\
+ "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "%Module% - 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
+################################################################################
+# Begin Project
+# PROP Target_Last_Scanned "%Module% - Win32 Debug"
+MTL=mktyplib.exe
+CPP=cl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "%Module% - 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 "%Module%R"
+# PROP Intermediate_Dir "%Module%R"
+# PROP Target_Dir ""
+OUTDIR=.\%Module%R
+INTDIR=.\%Module%R
+
+ALL : "$(OUTDIR)\%Module%.dll"
+
+CLEAN :
+ -@erase "$(INTDIR)\%Source%.obj"
+ -@erase "$(OUTDIR)\%Module%.dll"
+ -@erase "$(OUTDIR)\%Module%.exp"
+ -@erase "$(OUTDIR)\%Module%.lib"
+
+"$(OUTDIR)" :
+ if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /c
+# ADD CPP /nologo /MD /W3 /GX /O2 /I "..\regex" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /c
+CPP_PROJ=/nologo /MD /W3 /GX /O2 /I "..\regex" /D "WIN32" /D "NDEBUG" /D\
+ "_WINDOWS" /Fp"$(INTDIR)/%Module%.pch" /YX /Fo"$(INTDIR)/" /c
+CPP_OBJS=.\%Module%R/
+CPP_SBRS=.\.
+# ADD BASE MTL /nologo /D "NDEBUG" /win32
+# ADD MTL /nologo /D "NDEBUG" /win32
+MTL_PROJ=/nologo /D "NDEBUG" /win32
+# ADD BASE RSC /l 0x809 /d "NDEBUG"
+# ADD RSC /l 0x809 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+BSC32_FLAGS=/nologo /o"$(OUTDIR)/%Module%.bsc"
+BSC32_SBRS= \
+
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386
+# ADD LINK32 ..\CoreR\ApacheCore.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386
+LINK32_FLAGS=..\CoreR\ApacheCore.lib kernel32.lib user32.lib gdi32.lib\
+ winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib\
+ uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll\
+ /incremental:no /pdb:"$(OUTDIR)/%Module%.pdb" /machine:I386\
+ /out:"$(OUTDIR)/%Module%.dll"\
+ /implib:"$(OUTDIR)/%Module%.lib"
+LINK32_OBJS= \
+ "$(INTDIR)\%Source%.obj"
+
+"$(OUTDIR)\%Module%.dll" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+ $(LINK32) @<<
+ $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+!ELSEIF "$(CFG)" == "%Module% - 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 "%Module%D"
+# PROP Intermediate_Dir "%Module%D"
+# PROP Target_Dir ""
+OUTDIR=.\%Module%D
+INTDIR=.\%Module%D
+
+ALL : "$(OUTDIR)\%Module%.dll"
+
+CLEAN :
+ -@erase "$(INTDIR)\%Source%.obj"
+ -@erase "$(INTDIR)\vc40.idb"
+ -@erase "$(INTDIR)\vc40.pdb"
+ -@erase "$(OUTDIR)\%Module%.dll"
+ -@erase "$(OUTDIR)\%Module%.exp"
+ -@erase "$(OUTDIR)\%Module%.ilk"
+ -@erase "$(OUTDIR)\%Module%.lib"
+ -@erase "$(OUTDIR)\%Module%.pdb"
+
+"$(OUTDIR)" :
+ if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /c
+# ADD CPP /nologo /MDd /W3 /Gm /GX /Zi /Od /I "..\regex" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /c
+CPP_PROJ=/nologo /MDd /W3 /Gm /GX /Zi /Od /I "..\regex" /D "WIN32" /D "_DEBUG"\
+ /D "_WINDOWS" /Fp"$(INTDIR)/%Module%.pch" /YX /Fo"$(INTDIR)/"\
+ /Fd"$(INTDIR)/" /c
+CPP_OBJS=.\%Module%D/
+CPP_SBRS=.\.
+# ADD BASE MTL /nologo /D "_DEBUG" /win32
+# ADD MTL /nologo /D "_DEBUG" /win32
+MTL_PROJ=/nologo /D "_DEBUG" /win32
+# ADD BASE RSC /l 0x809 /d "_DEBUG"
+# ADD RSC /l 0x809 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+BSC32_FLAGS=/nologo /o"$(OUTDIR)/%Module%.bsc"
+BSC32_SBRS= \
+
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386
+# ADD LINK32 ..\CoreD\ApacheCore.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386
+LINK32_FLAGS=..\CoreD\ApacheCore.lib kernel32.lib user32.lib gdi32.lib\
+ winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib\
+ uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll\
+ /incremental:yes /pdb:"$(OUTDIR)/%Module%.pdb" /debug /machine:I386\
+ /out:"$(OUTDIR)/%Module%.dll"\
+ /implib:"$(OUTDIR)/%Module%.lib"
+LINK32_OBJS= \
+ "$(INTDIR)\%Source%.obj"
+
+"$(OUTDIR)\%Module%.dll" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+ $(LINK32) @<<
+ $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+!ENDIF
+
+.c{$(CPP_OBJS)}.obj:
+ $(CPP) $(CPP_PROJ) $<
+
+.cpp{$(CPP_OBJS)}.obj:
+ $(CPP) $(CPP_PROJ) $<
+
+.cxx{$(CPP_OBJS)}.obj:
+ $(CPP) $(CPP_PROJ) $<
+
+.c{$(CPP_SBRS)}.sbr:
+ $(CPP) $(CPP_PROJ) $<
+
+.cpp{$(CPP_SBRS)}.sbr:
+ $(CPP) $(CPP_PROJ) $<
+
+.cxx{$(CPP_SBRS)}.sbr:
+ $(CPP) $(CPP_PROJ) $<
+
+################################################################################
+# Begin Target
+
+# Name "%Module% - Win32 Release"
+# Name "%Module% - Win32 Debug"
+
+!IF "$(CFG)" == "%Module% - Win32 Release"
+
+!ELSEIF "$(CFG)" == "%Module% - Win32 Debug"
+
+!ENDIF
+
+################################################################################
+# Begin Source File
+
+SOURCE=\work\apache\src\%Source%.c
+DEP_CPP_MOD_A=\
+ "..\alloc.h"\
+ "..\buff.h"\
+ "..\conf.h"\
+ "..\http_config.h"\
+ "..\http_core.h"\
+ "..\http_log.h"\
+ "..\http_request.h"\
+ "..\httpd.h"\
+ "..\regex\regex.h"\
+ "..\ap_mmn.h"\
+ ".\readdir.h"\
+ {$(INCLUDE)}"\sys\stat.h"\
+ {$(INCLUDE)}"\sys\types.h"\
+
+NODEP_CPP_MOD_A=\
+ "..\sfio.h"\
+
+
+"$(INTDIR)\%Source%.obj" : $(SOURCE) $(DEP_CPP_MOD_A) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+# End Source File
+# End Target
+# End Project
+################################################################################
--- /dev/null
+/* ====================================================================
+ * Copyright (c) 1995-1999 The Apache Group. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * 4. The names "Apache Server" and "Apache Group" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission. For written permission, please contact
+ * apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ * nor may "Apache" appear in their names without prior written
+ * permission of the Apache Group.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Group and was originally based
+ * on public domain software written at the National Center for
+ * Supercomputing Applications, University of Illinois, Urbana-Champaign.
+ * For more information on the Apache Group and the Apache HTTP server
+ * project, please see <http://www.apache.org/>.
+ *
+ */
+
+/*
+ * mod_isapi.c - Internet Server Application (ISA) module for Apache
+ * by Alexei Kosut <akosut@apache.org>
+ *
+ * This module implements Microsoft's ISAPI, allowing Apache (when running
+ * under Windows) to load Internet Server Applications (ISAPI extensions).
+ * It implements all of the ISAPI 2.0 specification, except for the
+ * "Microsoft-only" extensions dealing with asynchronous I/O. All ISAPI
+ * extensions that use only synchronous I/O and are compatible with the
+ * ISAPI 2.0 specification should work (most ISAPI 1.0 extensions should
+ * function as well).
+ *
+ * To load, simply place the ISA in a location in the document tree.
+ * Then add an "AddHandler isapi-isa dll" into your config file.
+ * You should now be able to load ISAPI DLLs just be reffering to their
+ * URLs. Make sure the ExecCGI option is active in the directory
+ * the ISA is in.
+ */
+
+#include "httpd.h"
+#include "http_config.h"
+#include "http_core.h"
+#include "http_protocol.h"
+#include "http_request.h"
+#include "http_log.h"
+#include "util_script.h"
+
+/* We use the exact same header file as the original */
+#include <HttpExt.h>
+
+/* Seems IIS does not enforce the requirement for \r\n termination on HSE_REQ_SEND_RESPONSE_HEADER,
+ define this to conform */
+#define RELAX_HEADER_RULE
+
+module isapi_module;
+
+/* Our "Connection ID" structure */
+
+typedef struct {
+ LPEXTENSION_CONTROL_BLOCK ecb;
+ request_rec *r;
+ int status;
+} isapi_cid;
+
+/* Declare the ISAPI functions */
+
+BOOL WINAPI GetServerVariable (HCONN hConn, LPSTR lpszVariableName,
+ LPVOID lpvBuffer, LPDWORD lpdwSizeofBuffer);
+BOOL WINAPI WriteClient (HCONN ConnID, LPVOID Buffer, LPDWORD lpwdwBytes,
+ DWORD dwReserved);
+BOOL WINAPI ReadClient (HCONN ConnID, LPVOID lpvBuffer, LPDWORD lpdwSize);
+BOOL WINAPI ServerSupportFunction (HCONN hConn, DWORD dwHSERequest,
+ LPVOID lpvBuffer, LPDWORD lpdwSize,
+ LPDWORD lpdwDataType);
+
+/*
+ The optimiser blows it totally here. What happens is that autos are addressed relative to the
+ stack pointer, which, of course, moves around. The optimiser seems to lose track of it somewhere
+ between setting isapi_entry and calling through it. We work around the problem by forcing it to
+ use frame pointers.
+*/
+#pragma optimize("y",off)
+
+int isapi_handler (request_rec *r) {
+ LPEXTENSION_CONTROL_BLOCK ecb =
+ ap_pcalloc(r->pool, sizeof(struct _EXTENSION_CONTROL_BLOCK));
+ HSE_VERSION_INFO *pVer = ap_pcalloc(r->pool, sizeof(HSE_VERSION_INFO));
+
+ HINSTANCE isapi_handle;
+ BOOL (*isapi_version)(HSE_VERSION_INFO *); /* entry point 1 */
+ DWORD (*isapi_entry)(LPEXTENSION_CONTROL_BLOCK); /* entry point 2 */
+ BOOL (*isapi_term)(DWORD); /* optional entry point 3 */
+
+ isapi_cid *cid = ap_pcalloc(r->pool, sizeof(isapi_cid));
+ table *e = r->subprocess_env;
+ int retval;
+
+ /* Use similar restrictions as CGIs */
+
+ if (!(ap_allow_options(r) & OPT_EXECCGI))
+ return FORBIDDEN;
+
+ if (r->finfo.st_mode == 0)
+ return NOT_FOUND;
+
+ if (S_ISDIR(r->finfo.st_mode))
+ return FORBIDDEN;
+
+ /* Load the module */
+
+ if (!(isapi_handle = LoadLibraryEx(r->filename, NULL,
+ LOAD_WITH_ALTERED_SEARCH_PATH))) {
+ ap_log_rerror(APLOG_MARK, APLOG_ALERT, r,
+ "Could not load DLL: %s", r->filename);
+ return SERVER_ERROR;
+ }
+
+ if (!(isapi_version =
+ (void *)(GetProcAddress(isapi_handle, "GetExtensionVersion")))) {
+ ap_log_rerror(APLOG_MARK, APLOG_ALERT, r,
+ "DLL could not load GetExtensionVersion(): %s", r->filename);
+ FreeLibrary(isapi_handle);
+ return SERVER_ERROR;
+ }
+
+ if (!(isapi_entry =
+ (void *)(GetProcAddress(isapi_handle, "HttpExtensionProc")))) {
+ ap_log_rerror(APLOG_MARK, APLOG_ALERT, r,
+ "DLL could not load HttpExtensionProc(): %s", r->filename);
+ FreeLibrary(isapi_handle);
+ return SERVER_ERROR;
+ }
+
+ isapi_term = (void *)(GetProcAddress(isapi_handle, "TerminateExtension"));
+
+ /* Run GetExtensionVersion() */
+
+ if ((*isapi_version)(pVer) != TRUE) {
+ ap_log_rerror(APLOG_MARK, APLOG_ALERT, r,
+ "ISAPI GetExtensionVersion() failed: %s", r->filename);
+ FreeLibrary(isapi_handle);
+ return SERVER_ERROR;
+ }
+
+ /* Set up variables */
+ ap_add_common_vars(r);
+ ap_add_cgi_vars(r);
+
+ /* Set up connection ID */
+ ecb->ConnID = (HCONN)cid;
+ cid->ecb = ecb;
+ cid->r = r;
+ cid->status = 0;
+
+ ecb->cbSize = sizeof(struct _EXTENSION_CONTROL_BLOCK);
+ ecb->dwVersion = MAKELONG(0, 2);
+ ecb->dwHttpStatusCode = 0;
+ strcpy(ecb->lpszLogData, "");
+ ecb->lpszMethod = r->method;
+ ecb->lpszQueryString = ap_table_get(e, "QUERY_STRING");
+ ecb->lpszPathInfo = ap_table_get(e, "PATH_INFO");
+ ecb->lpszPathTranslated = ap_table_get(e, "PATH_TRANSLATED");
+ ecb->lpszContentType = ap_table_get(e, "CONTENT_TYPE");
+
+ /* Set up client input */
+ if ((retval = ap_setup_client_block(r, REQUEST_CHUNKED_ERROR))) {
+ if (isapi_term) (*isapi_term)(HSE_TERM_MUST_UNLOAD);
+ FreeLibrary(isapi_handle);
+ return retval;
+ }
+
+ if (ap_should_client_block(r)) {
+ /* Unlike IIS, which limits this to 48k, we read the whole
+ * sucker in. I suppose this could be bad for memory if someone
+ * uploaded the complete works of Shakespeare. Well, WebSite
+ * does the same thing.
+ */
+ long to_read = atol(ap_table_get(e, "CONTENT_LENGTH"));
+ long read;
+
+ /* Actually, let's cap it at 48k, until we figure out what
+ * to do with this... we don't want a Content-Length: 1000000000
+ * taking out the machine.
+ */
+
+ if (to_read > 49152) {
+ if (isapi_term) (*isapi_term)(HSE_TERM_MUST_UNLOAD);
+ FreeLibrary(isapi_handle);
+ return HTTP_REQUEST_ENTITY_TOO_LARGE;
+ }
+
+ ecb->lpbData = ap_pcalloc(r->pool, 1 + to_read);
+
+ if ((read = ap_get_client_block(r, ecb->lpbData, to_read)) < 0) {
+ if (isapi_term) (*isapi_term)(HSE_TERM_MUST_UNLOAD);
+ FreeLibrary(isapi_handle);
+ return SERVER_ERROR;
+ }
+
+ /* Although its not to spec, IIS seems to null-terminate
+ * its lpdData string. So we will too. To make sure
+ * cbAvailable matches cbTotalBytes, we'll up the latter
+ * and equalize them.
+ */
+ ecb->cbAvailable = ecb->cbTotalBytes = read + 1;
+ ecb->lpbData[read] = '\0';
+ }
+ else {
+ ecb->cbTotalBytes = 0;
+ ecb->cbAvailable = 0;
+ ecb->lpbData = NULL;
+ }
+
+ /* Set up the callbacks */
+
+ ecb->GetServerVariable = &GetServerVariable;
+ ecb->WriteClient = &WriteClient;
+ ecb->ReadClient = &ReadClient;
+ ecb->ServerSupportFunction = &ServerSupportFunction;
+
+ /* All right... try and load the sucker */
+ retval = (*isapi_entry)(ecb);
+
+ /* Set the status (for logging) */
+ if (ecb->dwHttpStatusCode)
+ r->status = ecb->dwHttpStatusCode;
+
+ /* Check for a log message - and log it */
+ if (ecb->lpszLogData && strcmp(ecb->lpszLogData, ""))
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
+ "%s: %s", ecb->lpszLogData, r->filename);
+
+ /* All done with the DLL... get rid of it */
+ if (isapi_term) (*isapi_term)(HSE_TERM_MUST_UNLOAD);
+ FreeLibrary(isapi_handle);
+
+ switch(retval) {
+ case HSE_STATUS_SUCCESS:
+ case HSE_STATUS_SUCCESS_AND_KEEP_CONN:
+ /* Ignore the keepalive stuff; Apache handles it just fine without
+ * the ISA's "advice".
+ */
+
+ if (cid->status) /* We have a special status to return */
+ return cid->status;
+
+ return OK;
+ case HSE_STATUS_PENDING: /* We don't support this */
+ ap_log_rerror(APLOG_MARK, APLOG_WARNING, r,
+ "ISAPI asynchronous I/O not supported: %s", r->filename);
+ case HSE_STATUS_ERROR:
+ default:
+ return SERVER_ERROR;
+ }
+
+}
+#pragma optimize("",on)
+
+BOOL WINAPI GetServerVariable (HCONN hConn, LPSTR lpszVariableName,
+ LPVOID lpvBuffer, LPDWORD lpdwSizeofBuffer) {
+ request_rec *r = ((isapi_cid *)hConn)->r;
+ table *e = r->subprocess_env;
+ const char *result;
+
+ /* Mostly, we just grab it from the environment, but there are
+ * a couple of special cases
+ */
+
+ if (!strcasecmp(lpszVariableName, "UNMAPPED_REMOTE_USER")) {
+ /* We don't support NT users, so this is always the same as
+ * REMOTE_USER
+ */
+ result = ap_table_get(e, "REMOTE_USER");
+ }
+ else if (!strcasecmp(lpszVariableName, "SERVER_PORT_SECURE")) {
+ /* Apache doesn't support secure requests inherently, so
+ * we have no way of knowing. We'll be conservative, and say
+ * all requests are insecure.
+ */
+ result = "0";
+ }
+ else if (!strcasecmp(lpszVariableName, "URL")) {
+ result = r->uri;
+ }
+ else {
+ result = ap_table_get(e, lpszVariableName);
+ }
+
+ if (result) {
+ if (strlen(result) > *lpdwSizeofBuffer) {
+ *lpdwSizeofBuffer = strlen(result);
+ SetLastError(ERROR_INSUFFICIENT_BUFFER);
+ return FALSE;
+ }
+ strncpy(lpvBuffer, result, *lpdwSizeofBuffer);
+ return TRUE;
+ }
+
+ /* Didn't find it */
+ SetLastError(ERROR_INVALID_INDEX);
+ return FALSE;
+}
+
+BOOL WINAPI WriteClient (HCONN ConnID, LPVOID Buffer, LPDWORD lpwdwBytes,
+ DWORD dwReserved) {
+ request_rec *r = ((isapi_cid *)ConnID)->r;
+ int writ; /* written, actually, but why shouldn't I make up words? */
+
+ /* We only support synchronous writing */
+ if (dwReserved && dwReserved != HSE_IO_SYNC) {
+ ap_log_rerror(APLOG_MARK, APLOG_WARNING, r,
+ "ISAPI asynchronous I/O not supported: %s", r->filename);
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return FALSE;
+ }
+
+ if ((writ = ap_rwrite(Buffer, *lpwdwBytes, r)) == EOF) {
+ SetLastError(ERROR); /* XXX: Find the right error code */
+ return FALSE;
+ }
+
+ *lpwdwBytes = writ;
+ return TRUE;
+}
+
+BOOL WINAPI ReadClient (HCONN ConnID, LPVOID lpvBuffer, LPDWORD lpdwSize) {
+ /* Doesn't need to do anything; we've read all the data already */
+ return TRUE;
+}
+
+/* XXX: There is an O(n^2) attack possible here. */
+BOOL WINAPI ServerSupportFunction (HCONN hConn, DWORD dwHSERequest,
+ LPVOID lpvBuffer, LPDWORD lpdwSize,
+ LPDWORD lpdwDataType) {
+ isapi_cid *cid = (isapi_cid *)hConn;
+ request_rec *subreq, *r = cid->r;
+ char *data;
+
+ switch (dwHSERequest) {
+ case HSE_REQ_SEND_URL_REDIRECT_RESP:
+ /* Set the status to be returned when the HttpExtensionProc()
+ * is done.
+ */
+ ap_table_set (r->headers_out, "Location", lpvBuffer);
+ cid->status = cid->r->status = cid->ecb->dwHttpStatusCode = REDIRECT;
+ return TRUE;
+
+ case HSE_REQ_SEND_URL:
+ /* Read any additional input */
+
+ if (r->remaining > 0) {
+ char argsbuffer[HUGE_STRING_LEN];
+
+ while (ap_get_client_block(r, argsbuffer, HUGE_STRING_LEN));
+ }
+
+ /* Reset the method to GET */
+ r->method = ap_pstrdup(r->pool, "GET");
+ r->method_number = M_GET;
+
+ /* Don't let anyone think there's still data */
+ ap_table_unset(r->headers_in, "Content-Length");
+
+ ap_internal_redirect((char *)lpvBuffer, r);
+ return TRUE;
+
+ case HSE_REQ_SEND_RESPONSE_HEADER:
+ r->status_line = lpvBuffer ? lpvBuffer : ap_pstrdup(r->pool, "200 OK");
+ sscanf(r->status_line, "%d", &r->status);
+ cid->ecb->dwHttpStatusCode = r->status;
+
+ /* Now fill in the HTTP headers, and the rest of it. Ick.
+ * lpdwDataType contains a string that has headers (in MIME
+ * format), a blank like, then (possibly) data. We need
+ * to parse it.
+ *
+ * Easy case first:
+ */
+ if (!lpdwDataType) {
+ ap_send_http_header(r);
+ return TRUE;
+ }
+
+ /* Make a copy - don't disturb the original */
+ data = ap_pstrdup(r->pool, (char *)lpdwDataType);
+
+ /* We *should* break before this while loop ends */
+ while (*data) {
+ char *value, *lf = strchr(data, '\n');
+ int p;
+
+#ifdef RELAX_HEADER_RULE
+ if (lf)
+ *lf = '\0';
+#else
+ if (!lf) { /* Huh? Invalid data, I think */
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
+ "ISA sent invalid headers: %s", r->filename);
+ SetLastError(ERROR); /* XXX: Find right error */
+ return FALSE;
+ }
+
+ /* Get rid of \n and \r */
+ *lf = '\0';
+#endif
+ p = strlen(data);
+ if (p > 0 && data[p-1] == '\r') data[p-1] = '\0';
+
+ /* End of headers */
+ if (*data == '\0') {
+#ifdef RELAX_HEADER_RULE
+ if (lf)
+#endif
+ data = lf + 1; /* Reset data */
+ break;
+ }
+
+ if (!(value = strchr(data, ':'))) {
+ SetLastError(ERROR); /* XXX: Find right error */
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
+ "ISA sent invalid headers", r->filename);
+ return FALSE;
+ }
+
+ *value++ = '\0';
+ while (*value && ap_isspace(*value)) ++value;
+
+ /* Check all the special-case headers. Similar to what
+ * ap_scan_script_header_err() does (see that function for
+ * more detail)
+ */
+
+ if (!strcasecmp(data, "Content-Type")) {
+ char *tmp;
+ /* Nuke trailing whitespace */
+
+ char *endp = value + strlen(value) - 1;
+ while (endp > value && ap_isspace(*endp)) *endp-- = '\0';
+
+ tmp = ap_pstrdup (r->pool, value);
+ ap_str_tolower(tmp);
+ r->content_type = tmp;
+ }
+ else if (!strcasecmp(data, "Content-Length")) {
+ ap_table_set(r->headers_out, data, value);
+ }
+ else if (!strcasecmp(data, "Transfer-Encoding")) {
+ ap_table_set(r->headers_out, data, value);
+ }
+ else if (!strcasecmp(data, "Set-Cookie")) {
+ ap_table_add(r->err_headers_out, data, value);
+ }
+ else {
+ ap_table_merge(r->err_headers_out, data, value);
+ }
+
+ /* Reset data */
+#ifdef RELAX_HEADER_RULE
+ if (!lf) {
+ data += p;
+ break;
+ }
+#endif
+ data = lf + 1;
+ }
+
+ /* All the headers should be set now */
+
+ ap_send_http_header(r);
+
+ /* Any data left should now be sent directly */
+ ap_rputs(data, r);
+
+ return TRUE;
+
+ case HSE_REQ_MAP_URL_TO_PATH:
+ /* Map a URL to a filename */
+ subreq = ap_sub_req_lookup_uri(ap_pstrndup(r->pool, (char *)lpvBuffer,
+ *lpdwSize), r);
+
+ GetFullPathName(subreq->filename, *lpdwSize - 1, (char *)lpvBuffer, NULL);
+
+ /* IIS puts a trailing slash on directories, Apache doesn't */
+
+ if (S_ISDIR (subreq->finfo.st_mode)) {
+ int l = strlen((char *)lpvBuffer);
+
+ ((char *)lpvBuffer)[l] = '\\';
+ ((char *)lpvBuffer)[l + 1] = '\0';
+ }
+
+ return TRUE;
+
+ case HSE_REQ_DONE_WITH_SESSION:
+ /* Do nothing... since we don't support async I/O, they'll
+ * return from HttpExtensionProc soon
+ */
+ return TRUE;
+
+ /* We don't support all this async I/O, Microsoft-specific stuff */
+ case HSE_REQ_IO_COMPLETION:
+ case HSE_REQ_TRANSMIT_FILE:
+ ap_log_rerror(APLOG_MARK, APLOG_WARNING, r,
+ "ISAPI asynchronous I/O not supported: %s", r->filename);
+ default:
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return FALSE;
+ }
+}
+
+handler_rec isapi_handlers[] = {
+{ "isapi-isa", isapi_handler },
+{ NULL}
+};
+
+module isapi_module = {
+ STANDARD_MODULE_STUFF,
+ NULL, /* initializer */
+ NULL, /* create per-dir config */
+ NULL, /* merge per-dir config */
+ NULL, /* server config */
+ NULL, /* merge server config */
+ NULL, /* command table */
+ isapi_handlers, /* handlers */
+ NULL, /* filename translation */
+ NULL, /* check_user_id */
+ NULL, /* check auth */
+ NULL, /* check access */
+ NULL, /* type_checker */
+ NULL, /* logger */
+ NULL /* header parser */
+};
--- /dev/null
+/* modules.c --- major modules compiled into Apache for Win32.
+ * Only insert an entry for a module if it must be compiled into
+ * the core server
+ */
+
+#include "httpd.h"
+#include "http_config.h"
+
+extern module core_module;
+extern module so_module;
+extern module mime_module;
+extern module access_module;
+extern module auth_module;
+extern module negotiation_module;
+extern module includes_module;
+extern module autoindex_module;
+extern module dir_module;
+extern module cgi_module;
+extern module userdir_module;
+extern module alias_module;
+extern module env_module;
+extern module config_log_module;
+extern module asis_module;
+extern module imap_module;
+extern module action_module;
+extern module setenvif_module;
+extern module isapi_module;
+
+module *ap_prelinked_modules[] = {
+ &core_module,
+ &so_module,
+ &mime_module,
+ &access_module,
+ &auth_module,
+ &negotiation_module,
+ &includes_module,
+ &autoindex_module,
+ &dir_module,
+ &cgi_module,
+ &userdir_module,
+ &alias_module,
+ &env_module,
+ &config_log_module,
+ &asis_module,
+ &imap_module,
+ &action_module,
+ &setenvif_module,
+ &isapi_module,
+ NULL
+};
+module *ap_preloaded_modules[] = {
+ &core_module,
+ &so_module,
+ &mime_module,
+ &access_module,
+ &auth_module,
+ &negotiation_module,
+ &includes_module,
+ &autoindex_module,
+ &dir_module,
+ &cgi_module,
+ &userdir_module,
+ &alias_module,
+ &env_module,
+ &config_log_module,
+ &asis_module,
+ &imap_module,
+ &action_module,
+ &setenvif_module,
+ &isapi_module,
+ NULL
+};
--- /dev/null
+#ifndef APACHE_OS_H
+#define APACHE_OS_H
+
+#define PLATFORM "Win32"
+
+/*
+ * This file in included in all Apache source code. It contains definitions
+ * of facilities available on _this_ operating system (HAVE_* macros),
+ * and prototypes of OS specific functions defined in os.c
+ */
+
+/* temporarily replace crypt */
+/* char *crypt(const char *pw, const char *salt); */
+#define crypt(buf,salt) (buf)
+
+/* Although DIR_TYPE is dirent (see nt/readdir.h) we need direct.h for
+ chdir() */
+#include <direct.h>
+
+#define STATUS
+/*#define WIN32_LEAN_AND_MEAN Now defined in project files */
+#ifndef STRICT
+ #define STRICT
+#endif
+#define CASE_BLIND_FILESYSTEM
+#define NO_WRITEV
+#define NO_SETSID
+#define NO_USE_SIGACTION
+#define NO_TIMES
+#define NO_GETTIMEOFDAY
+//#define NEED_PROCESS_H although we do, this is specially handled in ap_config.h
+#define USE_LONGJMP
+#define HAVE_MMAP
+#define USE_MMAP_SCOREBOARD
+#define MULTITHREAD
+#define HAVE_CANONICAL_FILENAME
+#define HAVE_DRIVE_LETTERS
+typedef int uid_t;
+typedef int gid_t;
+typedef int pid_t;
+typedef int mode_t;
+typedef char * caddr_t;
+
+/*
+Define export types. API_EXPORT_NONSTD is a nasty hack to avoid having to declare
+every configuration function as __stdcall.
+*/
+
+#ifdef SHARED_MODULE
+# define API_VAR_EXPORT __declspec(dllimport)
+# define API_EXPORT(type) __declspec(dllimport) type __stdcall
+# define API_EXPORT_NONSTD(type) __declspec(dllimport) type
+#else
+# define API_VAR_EXPORT __declspec(dllexport)
+# define API_EXPORT(type) __declspec(dllexport) type __stdcall
+# define API_EXPORT_NONSTD(type) __declspec(dllexport) type
+#endif
+#define MODULE_VAR_EXPORT __declspec(dllexport)
+
+#define strcasecmp(s1, s2) stricmp(s1, s2)
+#define strncasecmp(s1, s2, n) strnicmp(s1, s2, n)
+#define lstat(x, y) stat(x, y)
+#define S_ISLNK(m) (0)
+#define S_ISREG(m) ((m & _S_IFREG) == _S_IFREG)
+#ifndef S_ISDIR
+#define S_ISDIR(m) (((m) & S_IFDIR) == S_IFDIR)
+#endif
+#ifndef S_ISREG
+#define S_ISREG(m) (((m)&(S_IFREG)) == (S_IFREG))
+#endif
+#define STDIN_FILENO 0
+#define STDOUT_FILENO 1
+#define STDERR_FILENO 2
+#define JMP_BUF jmp_buf
+#define sleep(t) Sleep(t*1000)
+#define O_CREAT _O_CREAT
+#define O_RDWR _O_RDWR
+#define SIGPIPE 17
+/* Seems Windows is not a subgenius */
+#define NO_SLACK
+#include <stddef.h>
+
+#define NO_OTHER_CHILD
+#define NO_RELIABLE_PIPED_LOGS
+
+__inline int ap_os_is_path_absolute(const char *file)
+{
+ /* For now, just do the same check that http_request.c and mod_alias.c
+ * do.
+ */
+ return file[0] == '/' || file[1] == ':';
+}
+
+#define stat(f,ps) os_stat(f,ps)
+API_EXPORT(int) os_stat(const char *szPath,struct stat *pStat);
+
+API_EXPORT(int) os_strftime(char *s, size_t max, const char *format, const struct tm *tm);
+
+#define _spawnv(mode,cmdname,argv) os_spawnv(mode,cmdname,argv)
+#define spawnv(mode,cmdname,argv) os_spawnv(mode,cmdname,argv)
+API_EXPORT(int) os_spawnv(int mode,const char *cmdname,const char *const *argv);
+#define _spawnve(mode,cmdname,argv,envp) os_spawnve(mode,cmdname,argv,envp)
+#define spawnve(mode,cmdname,argv,envp) os_spawnve(mode,cmdname,argv,envp)
+API_EXPORT(int) os_spawnve(int mode,const char *cmdname,const char *const *argv,const char *const *envp);
+#define _spawnle os_spawnle
+#define spawnle os_spawnle
+API_EXPORT(int) os_spawnle(int mode,const char *cmdname,...);
+
+/* OS-dependent filename routines in util_win32.c */
+
+API_EXPORT(int) ap_os_is_filename_valid(const char *file);
+
+/* Abstractions for dealing with shared object files (DLLs on Win32).
+ * These are used by mod_so.c
+ */
+#define ap_os_dso_handle_t HINSTANCE
+#define ap_os_dso_init()
+#define ap_os_dso_load(l) LoadLibraryEx(l, NULL, LOAD_WITH_ALTERED_SEARCH_PATH)
+#define ap_os_dso_unload(l) FreeLibrary(l)
+#define ap_os_dso_sym(h,s) GetProcAddress(h,s)
+#define ap_os_dso_error() "" /* for now */
+
+/* Other ap_os_ routines not used by this platform */
+#define ap_os_kill(pid, sig) kill(pid, sig)
+
+#endif /* ! APACHE_OS_H */
--- /dev/null
+#include <windows.h>
+#include <sys/stat.h>
+#include <stdarg.h>
+#include <time.h>
+#include <stdlib.h>
+
+#include "httpd.h"
+#include "http_log.h"
+
+/* Returns TRUE if the input string is a string
+ * of one or more '.' characters.
+ */
+static BOOL OnlyDots(char *pString)
+{
+ char *c;
+
+ if (*pString == '\0')
+ return FALSE;
+
+ for (c = pString;*c;c++)
+ if (*c != '.')
+ return FALSE;
+
+ return TRUE;
+}
+
+/* Accepts as input a pathname, and tries to match it to an
+ * existing path and return the pathname in the case that
+ * is present on the existing path. This routine also
+ * converts alias names to long names.
+ */
+API_EXPORT(char *) ap_os_systemcase_filename(pool *pPool,
+ const char *szFile)
+{
+ char buf[HUGE_STRING_LEN];
+ char *pInputName;
+ char *p, *q;
+ BOOL bDone = FALSE;
+ BOOL bFileExists = TRUE;
+ HANDLE hFind;
+ WIN32_FIND_DATA wfd;
+
+ if (!szFile || strlen(szFile) == 0 || strlen(szFile) >= sizeof(buf))
+ return ap_pstrdup(pPool, "");
+
+ buf[0] = '\0';
+ pInputName = ap_pstrdup(pPool, szFile);
+
+ /* First convert all slashes to \ so Win32 calls work OK */
+ for (p = pInputName; *p; p++) {
+ if (*p == '/')
+ *p = '\\';
+ }
+
+ p = pInputName;
+ /* If there is drive information, copy it over. */
+ if (pInputName[1] == ':') {
+ buf[0] = tolower(*p++);
+ buf[1] = *p++;
+ buf[2] = '\0';
+
+ /* If all we have is a drive letter, then we are done */
+ if (strlen(pInputName) == 2)
+ bDone = TRUE;
+ }
+
+ q = p;
+ if (*p == '\\') {
+ p++;
+ if (*p == '\\') /* Possible UNC name */
+ {
+ p++;
+ /* Get past the machine name. FindFirstFile */
+ /* will not find a machine name only */
+ p = strchr(p, '\\');
+ if (p)
+ {
+ p++;
+ /* Get past the share name. FindFirstFile */
+ /* will not find a \\machine\share name only */
+ p = strchr(p, '\\');
+ if (p) {
+ strncat(buf,q,p-q);
+ q = p;
+ p++;
+ }
+ }
+
+ if (!p)
+ p = q;
+ }
+ }
+
+ p = strchr(p, '\\');
+
+ while (!bDone) {
+ if (p)
+ *p = '\0';
+
+ if (strchr(q, '*') || strchr(q, '?'))
+ bFileExists = FALSE;
+
+ /* If the path exists so far, call FindFirstFile
+ * again. However, if this portion of the path contains
+ * only '.' charaters, skip the call to FindFirstFile
+ * since it will convert '.' and '..' to actual names.
+ * Note: in the call to OnlyDots, we may have to skip
+ * a leading slash.
+ */
+ if (bFileExists && !OnlyDots((*q == '.' ? q : q+1))) {
+ hFind = FindFirstFile(pInputName, &wfd);
+
+ if (hFind == INVALID_HANDLE_VALUE) {
+ bFileExists = FALSE;
+ }
+ else {
+ FindClose(hFind);
+
+ if (*q == '\\')
+ strcat(buf,"\\");
+ strcat(buf, wfd.cFileName);
+ }
+ }
+
+ if (!bFileExists || OnlyDots((*q == '.' ? q : q+1))) {
+ strcat(buf, q);
+ }
+
+ if (p) {
+ q = p;
+ *p++ = '\\';
+ p = strchr(p, '\\');
+ }
+ else {
+ bDone = TRUE;
+ }
+ }
+
+ /* First convert all slashes to / so server code handles it ok */
+ for (p = buf; *p; p++) {
+ if (*p == '\\')
+ *p = '/';
+ }
+
+ return ap_pstrdup(pPool, buf);
+}
+
+
+/* Perform canonicalization with the exception that the
+ * input case is preserved.
+ */
+API_EXPORT(char *) ap_os_case_canonical_filename(pool *pPool,
+ const char *szFile)
+{
+ char *pNewStr;
+ char *s;
+ char *p;
+ char *q;
+
+ if (szFile == NULL || strlen(szFile) == 0)
+ return ap_pstrdup(pPool, "");
+
+ pNewStr = ap_pstrdup(pPool, szFile);
+
+ /* Change all '\' characters to '/' characters.
+ * While doing this, remove any trailing '.'.
+ * Also, blow away any directories with 3 or
+ * more '.'
+ */
+ for (p = pNewStr,s = pNewStr; *s; s++,p++) {
+ if (*s == '\\' || *s == '/') {
+
+ q = p;
+ while (p > pNewStr && *(p-1) == '.')
+ p--;
+
+ if (p == pNewStr && q-p <= 2 && *p == '.')
+ p = q;
+ else if (p > pNewStr && p < q && *(p-1) == '/') {
+ if (q-p > 2)
+ p--;
+ else
+ p = q;
+ }
+
+ *p = '/';
+ }
+ else {
+ *p = *s;
+ }
+ }
+ *p = '\0';
+
+ /* Blow away any final trailing '.' since on Win32
+ * foo.bat == foo.bat. == foo.bat... etc.
+ * Also blow away any trailing spaces since
+ * "filename" == "filename "
+ */
+ q = p;
+ while (p > pNewStr && (*(p-1) == '.' || *(p-1) == ' '))
+ p--;
+ if ((p > pNewStr) ||
+ (p == pNewStr && q-p > 2))
+ *p = '\0';
+
+
+ /* One more security issue to deal with. Win32 allows
+ * you to create long filenames. However, alias filenames
+ * are always created so that the filename will
+ * conform to 8.3 rules. According to the Microsoft
+ * Developer's network CD (1/98)
+ * "Automatically generated aliases are composed of the
+ * first six characters of the filename plus ~n
+ * (where n is a number) and the first three characters
+ * after the last period."
+ * Here, we attempt to detect and decode these names.
+ */
+ p = strchr(pNewStr, '~');
+ if (p != NULL) {
+ char *pConvertedName, *pQstr, *pPstr;
+ char buf[HUGE_STRING_LEN];
+ /* We potentially have a short name. Call
+ * ap_os_systemcase_filename to examine the filesystem
+ * and possibly extract the long name.
+ */
+ pConvertedName = ap_os_systemcase_filename(pPool, pNewStr);
+
+ /* Since we want to preserve the incoming case as much
+ * as we can, compare for differences in the string and
+ * only substitute in the path names that changed.
+ */
+ if (stricmp(pNewStr, pConvertedName)) {
+ buf[0] = '\0';
+
+ q = pQstr = pConvertedName;
+ p = pPstr = pNewStr;
+ do {
+ q = strchr(q,'/');
+ p = strchr(p,'/');
+
+ if (p != NULL) {
+ *q = '\0';
+ *p = '\0';
+ }
+
+ if (stricmp(pQstr, pPstr))
+ strcat(buf, pQstr); /* Converted name */
+ else
+ strcat(buf, pPstr); /* Original name */
+
+
+ if (p != NULL) {
+ pQstr = q;
+ pPstr = p;
+ *q++ = '/';
+ *p++ = '/';
+ }
+
+ } while (p != NULL);
+
+ pNewStr = ap_pstrdup(pPool, buf);
+ }
+ }
+
+
+ return pNewStr;
+}
+
+/* Perform complete canonicalization.
+ */
+API_EXPORT(char *) ap_os_canonical_filename(pool *pPool, const char *szFile)
+{
+ char *pNewName;
+ pNewName = ap_os_case_canonical_filename(pPool, szFile);
+ strlwr(pNewName);
+ return pNewName;
+}
+
+/* Win95 doesn't like trailing /s. NT and Unix don't mind. This works
+ * around the problem.
+ * Errr... except if it is UNC and we are referring to the root of
+ * the UNC, we MUST have a trailing \ and we can't use /s. Jeez.
+ * Not sure if this refers to all UNCs or just roots,
+ * but I'm going to fix it for all cases for now. (Ben)
+ */
+
+#undef stat
+API_EXPORT(int) os_stat(const char *szPath, struct stat *pStat)
+{
+ int n;
+
+ if (strlen(szPath) == 0) {
+ return -1;
+ }
+
+ if (szPath[0] == '/' && szPath[1] == '/') {
+ char buf[_MAX_PATH];
+ char *s;
+ int nSlashes = 0;
+
+ ap_assert(strlen(szPath) < _MAX_PATH);
+ strcpy(buf, szPath);
+ for (s = buf; *s; ++s) {
+ if (*s == '/') {
+ *s = '\\';
+ ++nSlashes;
+ }
+ }
+ /* then we need to add one more to get \\machine\share\ */
+ if (nSlashes == 3) {
+ *s++ = '\\';
+ }
+ *s = '\0';
+ return stat(buf, pStat);
+ }
+
+ /*
+ * Below removes the trailing /, however, do not remove
+ * it in the case of 'x:/' or stat will fail
+ */
+ n = strlen(szPath);
+ if ((szPath[n - 1] == '\\' || szPath[n - 1] == '/') &&
+ !(n == 3 && szPath[1] == ':')) {
+ char buf[_MAX_PATH];
+
+ ap_assert(n < _MAX_PATH);
+ strcpy(buf, szPath);
+ buf[n - 1] = '\0';
+
+ return stat(buf, pStat);
+ }
+ return stat(szPath, pStat);
+}
+
+/* Fix two really crap problems with Win32 spawn[lv]e*:
+ *
+ * 1. Win32 doesn't deal with spaces in argv.
+ * 2. Win95 doesn't like / in cmdname.
+ */
+
+#undef _spawnv
+API_EXPORT(int) os_spawnv(int mode, const char *cmdname,
+ const char *const *argv)
+{
+ int n;
+ char **aszArgs;
+ const char *szArg;
+ char *szCmd;
+ char *s;
+
+ szCmd = _alloca(strlen(cmdname)+1);
+ strcpy(szCmd, cmdname);
+ for (s = szCmd; *s; ++s) {
+ if (*s == '/') {
+ *s = '\\';
+ }
+ }
+
+ for (n = 0; argv[n]; ++n)
+ ;
+
+ aszArgs = _alloca((n + 1) * sizeof(const char *));
+
+ for (n = 0; szArg = argv[n]; ++n) {
+ if (strchr(szArg, ' ')) {
+ int l = strlen(szArg);
+
+ aszArgs[n] = _alloca(l + 2 + 1);
+ aszArgs[n][0] = '"';
+ strcpy(&aszArgs[n][1], szArg);
+ aszArgs[n][l + 1] = '"';
+ aszArgs[n][l + 2] = '\0';
+ }
+ else {
+ aszArgs[n] = (char *)szArg;
+ }
+ }
+
+ aszArgs[n] = NULL;
+
+ return _spawnv(mode, szCmd, aszArgs);
+}
+
+#undef _spawnve
+API_EXPORT(int) os_spawnve(int mode, const char *cmdname,
+ const char *const *argv, const char *const *envp)
+{
+ int n;
+ char **aszArgs;
+ const char *szArg;
+ char *szCmd;
+ char *s;
+
+ szCmd = _alloca(strlen(cmdname)+1);
+ strcpy(szCmd, cmdname);
+ for (s = szCmd; *s; ++s) {
+ if (*s == '/') {
+ *s = '\\';
+ }
+ }
+
+ for (n = 0; argv[n]; ++n)
+ ;
+
+ aszArgs = _alloca((n + 1)*sizeof(const char *));
+
+ for (n = 0; szArg = argv[n]; ++n){
+ if (strchr(szArg, ' ')) {
+ int l = strlen(szArg);
+
+ aszArgs[n] = _alloca(l + 2 + 1);
+ aszArgs[n][0] = '"';
+ strcpy(&aszArgs[n][1], szArg);
+ aszArgs[n][l + 1] = '"';
+ aszArgs[n][l + 2] = '\0';
+ }
+ else {
+ aszArgs[n] = (char *)szArg;
+ }
+ }
+
+ aszArgs[n] = NULL;
+
+ return _spawnve(mode, szCmd, aszArgs, envp);
+}
+
+API_EXPORT(int) os_spawnle(int mode, const char *cmdname, ...)
+{
+ int n;
+ va_list vlist;
+ char **aszArgs;
+ const char *szArg;
+ const char *const *aszEnv;
+ char *szCmd;
+ char *s;
+
+ szCmd = _alloca(strlen(cmdname)+1);
+ strcpy(szCmd, cmdname);
+ for (s = szCmd; *s; ++s) {
+ if (*s == '/') {
+ *s = '\\';
+ }
+ }
+
+ va_start(vlist, cmdname);
+ for (n = 0; va_arg(vlist, const char *); ++n)
+ ;
+ va_end(vlist);
+
+ aszArgs = _alloca((n + 1) * sizeof(const char *));
+
+ va_start(vlist, cmdname);
+ for (n = 0; szArg = va_arg(vlist, const char *); ++n) {
+ if (strchr(szArg, ' ')) {
+ int l = strlen(szArg);
+
+ aszArgs[n] = _alloca(l + 2 + 1);
+ aszArgs[n][0] = '"';
+ strcpy(&aszArgs[n][1], szArg);
+ aszArgs[n][l + 1] = '"';
+ aszArgs[n][l + 2] = '\0';
+ }
+ else {
+ aszArgs[n] = (char *)szArg;
+ }
+ }
+
+ aszArgs[n] = NULL;
+
+ aszEnv = va_arg(vlist, const char *const *);
+ va_end(vlist);
+
+ return _spawnve(mode, szCmd, aszArgs, aszEnv);
+}
+
+#undef strftime
+
+/* Partial replacement for strftime. This adds certain expandos to the
+ * Windows version
+ */
+
+API_EXPORT(int) os_strftime(char *s, size_t max, const char *format,
+ const struct tm *tm) {
+ /* If the new format string is bigger than max, the result string probably
+ * won't fit anyway. When %-expandos are added, made sure the padding below
+ * is enough.
+ */
+ char *new_format = (char *) _alloca(max + 11);
+ size_t i, j, format_length = strlen(format);
+ int return_value;
+ int length_written;
+
+ for (i = 0, j = 0; (i < format_length && j < max);) {
+ if (format[i] != '%') {
+ new_format[j++] = format[i++];
+ continue;
+ }
+ switch (format[i+1]) {
+ case 'D':
+ /* Is this locale dependent? Shouldn't be...
+ Also note the year 2000 exposure here */
+ memcpy(new_format + j, "%m/%d/%y", 8);
+ i += 2;
+ j += 8;
+ break;
+ case 'r':
+ memcpy(new_format + j, "%I:%M:%S %p", 11);
+ i += 2;
+ j += 11;
+ break;
+ case 'T':
+ memcpy(new_format + j, "%H:%M:%S", 8);
+ i += 2;
+ j += 8;
+ break;
+ case 'e':
+ length_written = ap_snprintf(new_format + j, max - j, "%2d",
+ tm->tm_mday);
+ j = (length_written == -1) ? max : (j + length_written);
+ i += 2;
+ break;
+ default:
+ /* We know we can advance two characters forward here. */
+ new_format[j++] = format[i++];
+ new_format[j++] = format[i++];
+ }
+ }
+ if (j >= max) {
+ *s = '\0'; /* Defensive programming, okay since output is undefined */
+ return_value = 0;
+ } else {
+ new_format[j] = '\0';
+ return_value = strftime(s, max, new_format, tm);
+ }
+ return return_value;
+}
+
+/*
+ * ap_os_is_filename_valid is given a filename, and returns 0 if the filename
+ * is not valid for use on this system. On Windows, this means it fails any
+ * of the tests below. Otherwise returns 1.
+ *
+ * Test for filename validity on Win32. This is of tests come in part from
+ * the MSDN article at "Technical Articles, Windows Platform, Base Services,
+ * Guidelines, Making Room for Long Filenames" although the information
+ * in MSDN about filename testing is incomplete or conflicting. There is a
+ * similar set of tests in "Technical Articles, Windows Platform, Base Services,
+ * Guidelines, Moving Unix Applications to Windows NT".
+ *
+ * The tests are:
+ *
+ * 1) total path length greater than MAX_PATH
+ *
+ * 2) anything using the octets 0-31 or characters " < > | :
+ * (these are reserved for Windows use in filenames. In addition
+ * each file system has its own additional characters that are
+ * invalid. See KB article Q100108 for more details).
+ *
+ * 3) anything ending in "." (no matter how many)
+ * (filename doc, doc. and doc... all refer to the same file)
+ *
+ * 4) any segment in which the basename (before first period) matches
+ * one of the DOS device names
+ * (the list comes from KB article Q100108 although some people
+ * reports that additional names such as "COM5" are also special
+ * devices).
+ *
+ * If the path fails ANY of these tests, the result must be to deny access.
+ */
+
+API_EXPORT(int) ap_os_is_filename_valid(const char *file)
+{
+ const char *segstart;
+ unsigned int seglength;
+ const char *pos;
+ static const char * const invalid_characters = "?\"<>*|:";
+ static const char * const invalid_filenames[] = {
+ "CON", "AUX", "COM1", "COM2", "COM3",
+ "COM4", "LPT1", "LPT2", "LPT3", "PRN", "NUL", NULL
+ };
+
+ /* Test 1 */
+ if (strlen(file) > MAX_PATH) {
+ /* Path too long for Windows. Note that this test is not valid
+ * if the path starts with //?/ or \\?\. */
+ return 0;
+ }
+
+ pos = file;
+
+ /* Skip any leading non-path components. This can be either a
+ * drive letter such as C:, or a UNC path such as \\SERVER\SHARE\.
+ * We continue and check the rest of the path based on the rules above.
+ * This means we could eliminate valid filenames from servers which
+ * are not running NT (such as Samba).
+ */
+
+ if (pos[0] && pos[1] == ':') {
+ /* Skip leading drive letter */
+ pos += 2;
+ }
+ else {
+ if ((pos[0] == '\\' || pos[0] == '/') &&
+ (pos[1] == '\\' || pos[1] == '/')) {
+ /* Is a UNC, so skip the server name and share name */
+ pos += 2;
+ while (*pos && *pos != '/' && *pos != '\\')
+ pos++;
+ if (!*pos) {
+ /* No share name */
+ return 0;
+ }
+ pos++; /* Move to start of share name */
+ while (*pos && *pos != '/' && *pos != '\\')
+ pos++;
+ if (!*pos) {
+ /* No path information */
+ return 0;
+ }
+ }
+ }
+
+ while (*pos) {
+ unsigned int idx;
+ unsigned int baselength;
+
+ while (*pos == '/' || *pos == '\\') {
+ pos++;
+ }
+ if (*pos == '\0') {
+ break;
+ }
+ segstart = pos; /* start of segment */
+ while (*pos && *pos != '/' && *pos != '\\') {
+ pos++;
+ }
+ seglength = pos - segstart;
+ /*
+ * Now we have a segment of the path, starting at position "segstart"
+ * and length "seglength"
+ */
+
+ /* Test 2 */
+ for (idx = 0; idx < seglength; idx++) {
+ if ((segstart[idx] > 0 && segstart[idx] < 32) ||
+ strchr(invalid_characters, segstart[idx])) {
+ return 0;
+ }
+ }
+
+ /* Test 3 */
+ if (segstart[seglength-1] == '.') {
+ return 0;
+ }
+
+ /* Test 4 */
+ for (baselength = 0; baselength < seglength; baselength++) {
+ if (segstart[baselength] == '.') {
+ break;
+ }
+ }
+
+ /* baselength is the number of characters in the base path of
+ * the segment (which could be the same as the whole segment length,
+ * if it does not include any dot characters). */
+ if (baselength == 3 || baselength == 4) {
+ for (idx = 0; invalid_filenames[idx]; idx++) {
+ if (strlen(invalid_filenames[idx]) == baselength &&
+ !strnicmp(invalid_filenames[idx], segstart, baselength)) {
+ return 0;
+ }
+ }
+ }
+ }
+
+ return 1;
+}
--- /dev/null
+Makefile
+uri_delims.h
+gen_uri_delims
+test_char.h
+gen_test_char
+gen_uri_delims_R
+gen_test_char_R
+gen_uri_delims_D
+gen_uri_delims.ilk
+gen_uri_delims.pdb
+gen_test_char_D
+gen_test_char.ilk
+gen_test_char.pdb
--- /dev/null
+-i4 -npsl -di0 -br -nce -d0 -cli0 -npcs -nfc1
+-TBUFF
+-TFILE
+-TTRANS
+-TUINT4
+-T_trans
+-Tallow_options_t
+-Tapache_sfio
+-Tarray_header
+-Tbool_int
+-Tbuf_area
+-Tbuff_struct
+-Tbuffy
+-Tcmd_how
+-Tcmd_parms
+-Tcommand_rec
+-Tcommand_struct
+-Tconn_rec
+-Tcore_dir_config
+-Tcore_server_config
+-Tdir_maker_func
+-Tevent
+-Tglobals_s
+-Thandler_func
+-Thandler_rec
+-Tjoblist_s
+-Tlisten_rec
+-Tmerger_func
+-Tmode_t
+-Tmodule
+-Tmodule_struct
+-Tmutex
+-Tn_long
+-Tother_child_rec
+-Toverrides_t
+-Tparent_score
+-Tpid_t
+-Tpiped_log
+-Tpool
+-Trequest_rec
+-Trequire_line
+-Trlim_t
+-Tscoreboard
+-Tsemaphore
+-Tserver_addr_rec
+-Tserver_rec
+-Tserver_rec_chain
+-Tshort_score
+-Ttable
+-Ttable_entry
+-Tthread
+-Tu_wide_int
+-Tvtime_t
+-Twide_int
--- /dev/null
+/* ====================================================================
+ * Copyright (c) 1995-1999 The Apache Group. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * 4. The names "Apache Server" and "Apache Group" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission. For written permission, please contact
+ * apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ * nor may "Apache" appear in their names without prior written
+ * permission of the Apache Group.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Group and was originally based
+ * on public domain software written at the National Center for
+ * Supercomputing Applications, University of Illinois, Urbana-Champaign.
+ * For more information on the Apache Group and the Apache HTTP server
+ * project, please see <http://www.apache.org/>.
+ *
+ */
+
+/*
+ * http_config.c: once was auxillary functions for reading httpd's config
+ * file and converting filenames into a namespace
+ *
+ * Rob McCool
+ *
+ * Wall-to-wall rewrite for Apache... commands which are part of the
+ * server core can now be found next door in "http_core.c". Now contains
+ * general command loop, and functions which do bookkeeping for the new
+ * Apache config stuff (modules and configuration vectors).
+ *
+ * rst
+ *
+ */
+
+#define CORE_PRIVATE
+
+#include "httpd.h"
+#include "http_config.h"
+#include "http_core.h"
+#include "http_log.h" /* for errors in parse_htaccess */
+#include "http_request.h" /* for default_handler (see invoke_handler) */
+#include "http_conf_globals.h" /* Sigh... */
+#include "http_vhost.h"
+#include "explain.h"
+
+DEF_Explain
+
+/****************************************************************
+ *
+ * We begin with the functions which deal with the linked list
+ * of modules which control just about all of the server operation.
+ */
+
+/* total_modules is the number of modules that have been linked
+ * into the server.
+ */
+static int total_modules = 0;
+/* dynamic_modules is the number of modules that have been added
+ * after the pre-loaded ones have been set up. It shouldn't be larger
+ * than DYNAMIC_MODULE_LIMIT.
+ */
+static int dynamic_modules = 0;
+API_VAR_EXPORT module *top_module = NULL;
+API_VAR_EXPORT module **ap_loaded_modules=NULL;
+
+typedef int (*handler_func) (request_rec *);
+typedef void *(*dir_maker_func) (pool *, char *);
+typedef void *(*merger_func) (pool *, void *, void *);
+
+/* Dealing with config vectors. These are associated with per-directory,
+ * per-server, and per-request configuration, and have a void* pointer for
+ * each modules. The nature of the structure pointed to is private to the
+ * module in question... the core doesn't (and can't) know. However, there
+ * are defined interfaces which allow it to create instances of its private
+ * per-directory and per-server structures, and to merge the per-directory
+ * structures of a directory and its subdirectory (producing a new one in
+ * which the defaults applying to the base directory have been properly
+ * overridden).
+ */
+
+#ifndef ap_get_module_config
+API_EXPORT(void *) ap_get_module_config(void *conf_vector, module *m)
+{
+ void **confv = (void **) conf_vector;
+ return confv[m->module_index];
+}
+#endif
+
+#ifndef ap_set_module_config
+API_EXPORT(void) ap_set_module_config(void *conf_vector, module *m, void *val)
+{
+ void **confv = (void **) conf_vector;
+ confv[m->module_index] = val;
+}
+#endif
+
+static void *create_empty_config(pool *p)
+{
+ void **conf_vector = (void **) ap_pcalloc(p, sizeof(void *) *
+ (total_modules + DYNAMIC_MODULE_LIMIT));
+ return (void *) conf_vector;
+}
+
+static void *create_default_per_dir_config(pool *p)
+{
+ void **conf_vector = (void **) ap_pcalloc(p, sizeof(void *) * (total_modules + DYNAMIC_MODULE_LIMIT));
+ module *modp;
+
+ for (modp = top_module; modp; modp = modp->next) {
+ dir_maker_func df = modp->create_dir_config;
+
+ if (df)
+ conf_vector[modp->module_index] = (*df) (p, NULL);
+ }
+
+ return (void *) conf_vector;
+}
+
+void *
+ ap_merge_per_dir_configs(pool *p, void *base, void *new)
+{
+ void **conf_vector = (void **) ap_palloc(p, sizeof(void *) * total_modules);
+ void **base_vector = (void **) base;
+ void **new_vector = (void **) new;
+ module *modp;
+
+ for (modp = top_module; modp; modp = modp->next) {
+ merger_func df = modp->merge_dir_config;
+ int i = modp->module_index;
+
+ if (df && new_vector[i])
+ conf_vector[i] = (*df) (p, base_vector[i], new_vector[i]);
+ else
+ conf_vector[i] = new_vector[i] ? new_vector[i] : base_vector[i];
+ }
+
+ return (void *) conf_vector;
+}
+
+static void *create_server_config(pool *p, server_rec *s)
+{
+ void **conf_vector = (void **) ap_pcalloc(p, sizeof(void *) * (total_modules + DYNAMIC_MODULE_LIMIT));
+ module *modp;
+
+ for (modp = top_module; modp; modp = modp->next) {
+ if (modp->create_server_config)
+ conf_vector[modp->module_index] = (*modp->create_server_config) (p, s);
+ }
+
+ return (void *) conf_vector;
+}
+
+static void merge_server_configs(pool *p, void *base, void *virt)
+{
+ /* Can reuse the 'virt' vector for the spine of it, since we don't
+ * have to deal with the moral equivalent of .htaccess files here...
+ */
+
+ void **base_vector = (void **) base;
+ void **virt_vector = (void **) virt;
+ module *modp;
+
+ for (modp = top_module; modp; modp = modp->next) {
+ merger_func df = modp->merge_server_config;
+ int i = modp->module_index;
+
+ if (!virt_vector[i])
+ virt_vector[i] = base_vector[i];
+ else if (df)
+ virt_vector[i] = (*df) (p, base_vector[i], virt_vector[i]);
+ }
+}
+
+void *ap_create_request_config(pool *p)
+{
+ return create_empty_config(p);
+}
+
+CORE_EXPORT(void *) ap_create_per_dir_config(pool *p)
+{
+ return create_empty_config(p);
+}
+
+#ifdef EXPLAIN
+
+struct {
+ int offset;
+ char *method;
+} aMethods[] =
+
+{
+#define m(meth) { XtOffsetOf(module,meth),#meth }
+ m(translate_handler),
+ m(ap_check_user_id),
+ m(auth_checker),
+ m(type_checker),
+ m(fixer_upper),
+ m(logger),
+ { -1, "?" },
+#undef m
+};
+
+char *ShowMethod(module *modp, int offset)
+{
+ int n;
+ static char buf[200];
+
+ for (n = 0; aMethods[n].offset >= 0; ++n)
+ if (aMethods[n].offset == offset)
+ break;
+ ap_snprintf(buf, sizeof(buf), "%s:%s", modp->name, aMethods[n].method);
+ return buf;
+}
+#else
+#define ShowMethod(modp,offset)
+#endif
+
+/****************************************************************
+ *
+ * Dispatch through the modules to find handlers for various phases
+ * of request handling. These are invoked by http_request.c to actually
+ * do the dirty work of slogging through the module structures.
+ */
+
+/*
+ * Optimized run_method routines. The observation here is that many modules
+ * have NULL for most of the methods. So we build optimized lists of
+ * everything. If you think about it, this is really just like a sparse array
+ * implementation to avoid scanning the zero entries.
+ */
+static const int method_offsets[] =
+{
+ XtOffsetOf(module, translate_handler),
+ XtOffsetOf(module, ap_check_user_id),
+ XtOffsetOf(module, auth_checker),
+ XtOffsetOf(module, access_checker),
+ XtOffsetOf(module, type_checker),
+ XtOffsetOf(module, fixer_upper),
+ XtOffsetOf(module, logger),
+ XtOffsetOf(module, header_parser),
+ XtOffsetOf(module, post_read_request)
+};
+#define NMETHODS (sizeof (method_offsets)/sizeof (method_offsets[0]))
+
+static struct {
+ int translate_handler;
+ int ap_check_user_id;
+ int auth_checker;
+ int access_checker;
+ int type_checker;
+ int fixer_upper;
+ int logger;
+ int header_parser;
+ int post_read_request;
+} offsets_into_method_ptrs;
+
+/*
+ * This is just one big array of method_ptrs. It's constructed such that,
+ * for example, method_ptrs[ offsets_into_method_ptrs.logger ] is the first
+ * logger function. You go one-by-one from there until you hit a NULL.
+ * This structure was designed to hopefully maximize cache-coolness.
+ */
+static handler_func *method_ptrs;
+
+/* routine to reconstruct all these shortcuts... called after every
+ * add_module.
+ * XXX: this breaks if modules dink with their methods pointers
+ */
+static void build_method_shortcuts(void)
+{
+ module *modp;
+ int how_many_ptrs;
+ int i;
+ int next_ptr;
+ handler_func fp;
+
+ if (method_ptrs) {
+ /* free up any previous set of method_ptrs */
+ free(method_ptrs);
+ }
+
+ /* first we count how many functions we have */
+ how_many_ptrs = 0;
+ for (modp = top_module; modp; modp = modp->next) {
+ for (i = 0; i < NMETHODS; ++i) {
+ if (*(handler_func *) (method_offsets[i] + (char *) modp)) {
+ ++how_many_ptrs;
+ }
+ }
+ }
+ method_ptrs = malloc((how_many_ptrs + NMETHODS) * sizeof(handler_func));
+ if (method_ptrs == NULL) {
+ fprintf(stderr, "Ouch! Out of memory in build_method_shortcuts()!\n");
+ }
+ next_ptr = 0;
+ for (i = 0; i < NMETHODS; ++i) {
+ /* XXX: This is an itsy bit presumptuous about the alignment
+ * constraints on offsets_into_method_ptrs. I can't remember if
+ * ANSI says this has to be true... -djg */
+ ((int *) &offsets_into_method_ptrs)[i] = next_ptr;
+ for (modp = top_module; modp; modp = modp->next) {
+ fp = *(handler_func *) (method_offsets[i] + (char *) modp);
+ if (fp) {
+ method_ptrs[next_ptr++] = fp;
+ }
+ }
+ method_ptrs[next_ptr++] = NULL;
+ }
+}
+
+
+static int run_method(request_rec *r, int offset, int run_all)
+{
+ int i;
+
+ for (i = offset; method_ptrs[i]; ++i) {
+ handler_func mod_handler = method_ptrs[i];
+
+ if (mod_handler) {
+ int result;
+
+ result = (*mod_handler) (r);
+
+ if (result != DECLINED && (!run_all || result != OK))
+ return result;
+ }
+ }
+
+ return run_all ? OK : DECLINED;
+}
+
+int ap_translate_name(request_rec *r)
+{
+ return run_method(r, offsets_into_method_ptrs.translate_handler, 0);
+}
+
+int ap_check_access(request_rec *r)
+{
+ return run_method(r, offsets_into_method_ptrs.access_checker, 1);
+}
+
+int ap_find_types(request_rec *r)
+{
+ return run_method(r, offsets_into_method_ptrs.type_checker, 0);
+}
+
+int ap_run_fixups(request_rec *r)
+{
+ return run_method(r, offsets_into_method_ptrs.fixer_upper, 1);
+}
+
+int ap_log_transaction(request_rec *r)
+{
+ return run_method(r, offsets_into_method_ptrs.logger, 1);
+}
+
+int ap_header_parse(request_rec *r)
+{
+ return run_method(r, offsets_into_method_ptrs.header_parser, 1);
+}
+
+int ap_run_post_read_request(request_rec *r)
+{
+ return run_method(r, offsets_into_method_ptrs.post_read_request, 1);
+}
+
+/* Auth stuff --- anything that defines one of these will presumably
+ * want to define something for the other. Note that check_auth is
+ * separate from check_access to make catching some config errors easier.
+ */
+
+int ap_check_user_id(request_rec *r)
+{
+ return run_method(r, offsets_into_method_ptrs.ap_check_user_id, 0);
+}
+
+int ap_check_auth(request_rec *r)
+{
+ return run_method(r, offsets_into_method_ptrs.auth_checker, 0);
+}
+
+/*
+ * For speed/efficiency we generate a compact list of all the handlers
+ * and wildcard handlers. This means we won't have to scan the entire
+ * module list looking for handlers... where we'll find a whole whack
+ * of NULLs.
+ */
+typedef struct {
+ handler_rec hr;
+ size_t len;
+} fast_handler_rec;
+
+static fast_handler_rec *handlers;
+static fast_handler_rec *wildhandlers;
+
+static void init_handlers(pool *p)
+{
+ module *modp;
+ int nhandlers = 0;
+ int nwildhandlers = 0;
+ const handler_rec *handp;
+ fast_handler_rec *ph, *pw;
+ char *starp;
+
+ for (modp = top_module; modp; modp = modp->next) {
+ if (!modp->handlers)
+ continue;
+ for (handp = modp->handlers; handp->content_type; ++handp) {
+ if (strchr(handp->content_type, '*')) {
+ nwildhandlers ++;
+ } else {
+ nhandlers ++;
+ }
+ }
+ }
+ ph = handlers = ap_palloc(p, sizeof(*ph)*(nhandlers + 1));
+ pw = wildhandlers = ap_palloc(p, sizeof(*pw)*(nwildhandlers + 1));
+ for (modp = top_module; modp; modp = modp->next) {
+ if (!modp->handlers)
+ continue;
+ for (handp = modp->handlers; handp->content_type; ++handp) {
+ if ((starp = strchr(handp->content_type, '*'))) {
+ pw->hr.content_type = handp->content_type;
+ pw->hr.handler = handp->handler;
+ pw->len = starp - handp->content_type;
+ pw ++;
+ } else {
+ ph->hr.content_type = handp->content_type;
+ ph->hr.handler = handp->handler;
+ ph->len = strlen(handp->content_type);
+ ph ++;
+ }
+ }
+ }
+ pw->hr.content_type = NULL;
+ pw->hr.handler = NULL;
+ ph->hr.content_type = NULL;
+ ph->hr.handler = NULL;
+}
+
+int ap_invoke_handler(request_rec *r)
+{
+ fast_handler_rec *handp;
+ const char *handler;
+ char *p;
+ size_t handler_len;
+ int result = HTTP_INTERNAL_SERVER_ERROR;
+
+ if (r->handler) {
+ handler = r->handler;
+ handler_len = strlen(handler);
+ }
+ else {
+ handler = r->content_type ? r->content_type : ap_default_type(r);
+ if ((p = strchr(handler, ';')) != NULL) { /* MIME type arguments */
+ while (p > handler && p[-1] == ' ')
+ --p; /* strip trailing spaces */
+ handler_len = p - handler;
+ }
+ else {
+ handler_len = strlen(handler);
+ }
+ }
+
+ /* Pass one --- direct matches */
+
+ for (handp = handlers; handp->hr.content_type; ++handp) {
+ if (handler_len == handp->len
+ && !strncmp(handler, handp->hr.content_type, handler_len)) {
+ result = (*handp->hr.handler) (r);
+
+ if (result != DECLINED)
+ return result;
+ }
+ }
+
+ if (result == HTTP_INTERNAL_SERVER_ERROR && r->handler) {
+ ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, r,
+ "handler \"%s\" not found for: %s", r->handler, r->filename);
+ }
+
+ /* Pass two --- wildcard matches */
+
+ for (handp = wildhandlers; handp->hr.content_type; ++handp) {
+ if (handler_len >= handp->len
+ && !strncmp(handler, handp->hr.content_type, handp->len)) {
+ result = (*handp->hr.handler) (r);
+
+ if (result != DECLINED)
+ return result;
+ }
+ }
+
+ return HTTP_INTERNAL_SERVER_ERROR;
+}
+
+/* One-time setup for precompiled modules --- NOT to be done on restart */
+
+API_EXPORT(void) ap_add_module(module *m)
+{
+ /* This could be called from an AddModule httpd.conf command,
+ * after the file has been linked and the module structure within it
+ * teased out...
+ */
+
+ if (m->version != MODULE_MAGIC_NUMBER_MAJOR) {
+ fprintf(stderr, "%s: module \"%s\" is not compatible with this "
+ "version of Apache.\n", ap_server_argv0, m->name);
+ fprintf(stderr, "Please contact the vendor for the correct version.\n");
+ exit(1);
+ }
+
+ if (m->next == NULL) {
+ m->next = top_module;
+ top_module = m;
+ }
+ if (m->module_index == -1) {
+ m->module_index = total_modules++;
+ dynamic_modules++;
+
+ if (dynamic_modules > DYNAMIC_MODULE_LIMIT) {
+ fprintf(stderr, "%s: module \"%s\" could not be loaded, because"
+ " the dynamic\n", ap_server_argv0, m->name);
+ fprintf(stderr, "module limit was reached. Please increase "
+ "DYNAMIC_MODULE_LIMIT and recompile.\n");
+ exit(1);
+ }
+ }
+
+ /* Some C compilers put a complete path into __FILE__, but we want
+ * only the filename (e.g. mod_includes.c). So check for path
+ * components (Unix and DOS), and remove them.
+ */
+
+ if (strrchr(m->name, '/'))
+ m->name = 1 + strrchr(m->name, '/');
+ if (strrchr(m->name, '\\'))
+ m->name = 1 + strrchr(m->name, '\\');
+
+#ifdef _OSD_POSIX /* __FILE__="*POSIX(/home/martin/apache/src/modules/standard/mod_info.c)" */
+ /* We cannot fix the string in-place, because it's const */
+ if (m->name[strlen(m->name)-1]==')') {
+ char *tmp = strdup(m->name); /* FIXME:memory leak, albeit a small one */
+ tmp[strlen(tmp)-1] = '\0';
+ m->name = tmp;
+ }
+#endif /*_OSD_POSIX*/
+}
+
+/*
+ * remove_module undoes what add_module did. There are some caveats:
+ * when the module is removed, its slot is lost so all the current
+ * per-dir and per-server configurations are invalid. So we should
+ * only ever call this function when you are invalidating almost
+ * all our current data. I.e. when doing a restart.
+ */
+
+API_EXPORT(void) ap_remove_module(module *m)
+{
+ module *modp;
+
+ modp = top_module;
+ if (modp == m) {
+ /* We are the top module, special case */
+ top_module = modp->next;
+ m->next = NULL;
+ }
+ else {
+ /* Not the top module, find use. When found modp will
+ * point to the module _before_ us in the list
+ */
+
+ while (modp && modp->next != m) {
+ modp = modp->next;
+ }
+ if (!modp) {
+ /* Uh-oh, this module doesn't exist */
+ ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, NULL,
+ "Cannot remove module %s: not found in module list",
+ m->name);
+ return;
+ }
+ /* Eliminate us from the module list */
+ modp->next = modp->next->next;
+ }
+
+ m->module_index = -1; /* simulate being unloaded, should
+ * be unnecessary */
+ dynamic_modules--;
+ total_modules--;
+}
+
+API_EXPORT(void) ap_add_loaded_module(module *mod)
+{
+ module **m;
+
+ /*
+ * Add module pointer to top of chained module list
+ */
+ ap_add_module(mod);
+
+ /*
+ * And module pointer to list of loaded modules
+ *
+ * Notes: 1. ap_add_module() would already complain if no more space
+ * exists for adding a dynamically loaded module
+ * 2. ap_add_module() accepts double-inclusion, so we have
+ * to accept this, too.
+ */
+ for (m = ap_loaded_modules; *m != NULL; m++)
+ ;
+ *m++ = mod;
+ *m = NULL;
+}
+
+API_EXPORT(void) ap_remove_loaded_module(module *mod)
+{
+ module **m;
+ module **m2;
+ int done;
+
+ /*
+ * Remove module pointer from chained module list
+ */
+ ap_remove_module(mod);
+
+ /*
+ * Remove module pointer from list of loaded modules
+ *
+ * Note: 1. We cannot determine if the module was successfully
+ * removed by ap_remove_module().
+ * 2. We have not to complain explicity when the module
+ * is not found because ap_remove_module() did it
+ * for us already.
+ */
+ for (m = m2 = ap_loaded_modules, done = 0; *m2 != NULL; m2++) {
+ if (*m2 == mod && done == 0)
+ done = 1;
+ else
+ *m++ = *m2;
+ }
+ *m = NULL;
+}
+
+void ap_setup_prelinked_modules()
+{
+ module **m;
+ module **m2;
+
+ /*
+ * Initialise total_modules variable and module indices
+ */
+ total_modules = 0;
+ for (m = ap_preloaded_modules; *m != NULL; m++)
+ (*m)->module_index = total_modules++;
+
+ /*
+ * Initialise list of loaded modules
+ */
+ ap_loaded_modules = (module **)malloc(
+ sizeof(module *)*(total_modules+DYNAMIC_MODULE_LIMIT+1));
+ if (ap_loaded_modules == NULL) {
+ fprintf(stderr, "Ouch! Out of memory in ap_setup_prelinked_modules()!\n");
+ }
+ for (m = ap_preloaded_modules, m2 = ap_loaded_modules; *m != NULL; )
+ *m2++ = *m++;
+ *m2 = NULL;
+
+ /*
+ * Initialize chain of linked (=activate) modules
+ */
+ for (m = ap_prelinked_modules; *m != NULL; m++)
+ ap_add_module(*m);
+}
+
+API_EXPORT(const char *) ap_find_module_name(module *m)
+{
+ return m->name;
+}
+
+API_EXPORT(module *) ap_find_linked_module(const char *name)
+{
+ module *modp;
+
+ for (modp = top_module; modp; modp = modp->next) {
+ if (strcmp(modp->name, name) == 0)
+ return modp;
+ }
+ return NULL;
+}
+
+/* Add a named module. Returns 1 if module found, 0 otherwise. */
+API_EXPORT(int) ap_add_named_module(const char *name)
+{
+ module *modp;
+ int i = 0;
+
+ for (modp = ap_loaded_modules[i]; modp; modp = ap_loaded_modules[++i]) {
+ if (strcmp(modp->name, name) == 0) {
+ /* Only add modules that are not already enabled. */
+ if (modp->next == NULL) {
+ ap_add_module(modp);
+ }
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/* Clear the internal list of modules, in preparation for starting over. */
+API_EXPORT(void) ap_clear_module_list()
+{
+ module **m = &top_module;
+ module **next_m;
+
+ while (*m) {
+ next_m = &((*m)->next);
+ *m = NULL;
+ m = next_m;
+ }
+
+ /* This is required; so we add it always. */
+ ap_add_named_module("http_core.c");
+}
+
+/*****************************************************************
+ *
+ * Resource, access, and .htaccess config files now parsed by a common
+ * command loop.
+ *
+ * Let's begin with the basics; parsing the line and
+ * invoking the function...
+ */
+
+static const char *invoke_cmd(const command_rec *cmd, cmd_parms *parms,
+ void *mconfig, const char *args)
+{
+ char *w, *w2, *w3;
+ const char *errmsg;
+
+ if ((parms->override & cmd->req_override) == 0)
+ return ap_pstrcat(parms->pool, cmd->name, " not allowed here", NULL);
+
+ parms->info = cmd->cmd_data;
+ parms->cmd = cmd;
+
+ switch (cmd->args_how) {
+ case RAW_ARGS:
+ return ((const char *(*)(cmd_parms *, void *, const char *))
+ (cmd->func)) (parms, mconfig, args);
+
+ case NO_ARGS:
+ if (*args != 0)
+ return ap_pstrcat(parms->pool, cmd->name, " takes no arguments",
+ NULL);
+
+ return ((const char *(*)(cmd_parms *, void *))
+ (cmd->func)) (parms, mconfig);
+
+ case TAKE1:
+ w = ap_getword_conf(parms->pool, &args);
+
+ if (*w == '\0' || *args != 0)
+ return ap_pstrcat(parms->pool, cmd->name, " takes one argument",
+ cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL);
+
+ return ((const char *(*)(cmd_parms *, void *, const char *))
+ (cmd->func)) (parms, mconfig, w);
+
+ case TAKE2:
+
+ w = ap_getword_conf(parms->pool, &args);
+ w2 = ap_getword_conf(parms->pool, &args);
+
+ if (*w == '\0' || *w2 == '\0' || *args != 0)
+ return ap_pstrcat(parms->pool, cmd->name, " takes two arguments",
+ cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL);
+
+ return ((const char *(*)(cmd_parms *, void *, const char *,
+ const char *)) (cmd->func)) (parms, mconfig, w, w2);
+
+ case TAKE12:
+
+ w = ap_getword_conf(parms->pool, &args);
+ w2 = ap_getword_conf(parms->pool, &args);
+
+ if (*w == '\0' || *args != 0)
+ return ap_pstrcat(parms->pool, cmd->name, " takes 1-2 arguments",
+ cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL);
+
+ return ((const char *(*)(cmd_parms *, void *, const char *,
+ const char *)) (cmd->func)) (parms, mconfig, w,
+ *w2 ? w2 : NULL);
+
+ case TAKE3:
+
+ w = ap_getword_conf(parms->pool, &args);
+ w2 = ap_getword_conf(parms->pool, &args);
+ w3 = ap_getword_conf(parms->pool, &args);
+
+ if (*w == '\0' || *w2 == '\0' || *w3 == '\0' || *args != 0)
+ return ap_pstrcat(parms->pool, cmd->name, " takes three arguments",
+ cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL);
+
+ return ((const char *(*)(cmd_parms *, void *, const char *,
+ const char *, const char *)) (cmd->func)) (parms,
+ mconfig, w, w2, w3);
+
+ case TAKE23:
+
+ w = ap_getword_conf(parms->pool, &args);
+ w2 = ap_getword_conf(parms->pool, &args);
+ w3 = *args ? ap_getword_conf(parms->pool, &args) : NULL;
+
+ if (*w == '\0' || *w2 == '\0' || *args != 0)
+ return ap_pstrcat(parms->pool, cmd->name,
+ " takes two or three arguments",
+ cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL);
+
+ return ((const char *(*)(cmd_parms *, void *, const char *,
+ const char *, const char *)) (cmd->func)) (parms,
+ mconfig, w, w2, w3);
+
+ case TAKE123:
+
+ w = ap_getword_conf(parms->pool, &args);
+ w2 = *args ? ap_getword_conf(parms->pool, &args) : NULL;
+ w3 = *args ? ap_getword_conf(parms->pool, &args) : NULL;
+
+ if (*w == '\0' || *args != 0)
+ return ap_pstrcat(parms->pool, cmd->name,
+ " takes one, two or three arguments",
+ cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL);
+
+ return ((const char *(*)(cmd_parms *, void *, const char *,
+ const char *, const char *)) (cmd->func)) (parms,
+ mconfig, w, w2, w3);
+
+ case TAKE13:
+
+ w = ap_getword_conf(parms->pool, &args);
+ w2 = *args ? ap_getword_conf(parms->pool, &args) : NULL;
+ w3 = *args ? ap_getword_conf(parms->pool, &args) : NULL;
+
+ if (*w == '\0' || (*w2 && !w3) || *args != 0)
+ return ap_pstrcat(parms->pool, cmd->name,
+ " takes one or three arguments",
+ cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL);
+
+ return ((const char *(*)(cmd_parms *, void *, const char *,
+ const char *, const char *)) (cmd->func)) (parms,
+ mconfig, w, w2, w3);
+
+ case ITERATE:
+
+ while (*(w = ap_getword_conf(parms->pool, &args)) != '\0')
+ if ((errmsg = ((const char *(*)(cmd_parms *, void *,
+ const char *)) (cmd->func)) (parms, mconfig, w)))
+ return errmsg;
+
+ return NULL;
+
+ case ITERATE2:
+
+ w = ap_getword_conf(parms->pool, &args);
+
+ if (*w == '\0' || *args == 0)
+ return ap_pstrcat(parms->pool, cmd->name,
+ " requires at least two arguments",
+ cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL);
+
+
+ while (*(w2 = ap_getword_conf(parms->pool, &args)) != '\0')
+ if ((errmsg = ((const char *(*)(cmd_parms *, void *,
+ const char *, const char *)) (cmd->func)) (parms,
+ mconfig, w, w2)))
+ return errmsg;
+
+ return NULL;
+
+ case FLAG:
+
+ w = ap_getword_conf(parms->pool, &args);
+
+ if (*w == '\0' || (strcasecmp(w, "on") && strcasecmp(w, "off")))
+ return ap_pstrcat(parms->pool, cmd->name, " must be On or Off",
+ NULL);
+
+ return ((const char *(*)(cmd_parms *, void *, int))
+ (cmd->func)) (parms, mconfig, strcasecmp(w, "off") != 0);
+
+ default:
+
+ return ap_pstrcat(parms->pool, cmd->name,
+ " is improperly configured internally (server bug)",
+ NULL);
+ }
+}
+
+CORE_EXPORT(const command_rec *) ap_find_command(const char *name, const command_rec *cmds)
+{
+ while (cmds->name)
+ if (!strcasecmp(name, cmds->name))
+ return cmds;
+ else
+ ++cmds;
+
+ return NULL;
+}
+
+CORE_EXPORT(const command_rec *) ap_find_command_in_modules(const char *cmd_name, module **mod)
+{
+ const command_rec *cmdp;
+ module *modp;
+
+ for (modp = *mod; modp; modp = modp->next)
+ if (modp->cmds && (cmdp = ap_find_command(cmd_name, modp->cmds))) {
+ *mod = modp;
+ return cmdp;
+ }
+
+ return NULL;
+}
+
+CORE_EXPORT(void *) ap_set_config_vectors(cmd_parms *parms, void *config, module *mod)
+{
+ void *mconfig = ap_get_module_config(config, mod);
+ void *sconfig = ap_get_module_config(parms->server->module_config, mod);
+
+ if (!mconfig && mod->create_dir_config) {
+ mconfig = (*mod->create_dir_config) (parms->pool, parms->path);
+ ap_set_module_config(config, mod, mconfig);
+ }
+
+ if (!sconfig && mod->create_server_config) {
+ sconfig = (*mod->create_server_config) (parms->pool, parms->server);
+ ap_set_module_config(parms->server->module_config, mod, sconfig);
+ }
+ return mconfig;
+}
+
+CORE_EXPORT(const char *) ap_handle_command(cmd_parms *parms, void *config, const char *l)
+{
+ void *oldconfig;
+ const char *args, *cmd_name, *retval;
+ const command_rec *cmd;
+ module *mod = top_module;
+
+ if ((l[0] == '#') || (!l[0]))
+ return NULL;
+
+ args = l;
+ cmd_name = ap_getword_conf(parms->temp_pool, &args);
+ if (*cmd_name == '\0')
+ return NULL;
+
+ oldconfig = parms->context;
+ parms->context = config;
+ do {
+ if (!(cmd = ap_find_command_in_modules(cmd_name, &mod))) {
+ errno = EINVAL;
+ return ap_pstrcat(parms->pool, "Invalid command '", cmd_name,
+ "', perhaps mis-spelled or defined by a module "
+ "not included in the server configuration", NULL);
+ }
+ else {
+ void *mconfig = ap_set_config_vectors(parms,config, mod);
+
+ retval = invoke_cmd(cmd, parms, mconfig, args);
+ mod = mod->next; /* Next time around, skip this one */
+ }
+ } while (retval && !strcmp(retval, DECLINE_CMD));
+ parms->context = oldconfig;
+
+ return retval;
+}
+
+API_EXPORT(const char *) ap_srm_command_loop(cmd_parms *parms, void *config)
+{
+ char l[MAX_STRING_LEN];
+
+ while (!(ap_cfg_getline(l, MAX_STRING_LEN, parms->config_file))) {
+ const char *errmsg = ap_handle_command(parms, config, l);
+ if (errmsg) {
+ return errmsg;
+ }
+ }
+
+ return NULL;
+}
+
+/*
+ * Generic command functions...
+ */
+
+API_EXPORT_NONSTD(const char *) ap_set_string_slot(cmd_parms *cmd,
+ char *struct_ptr, char *arg)
+{
+ /* This one's pretty generic... */
+
+ int offset = (int) (long) cmd->info;
+ *(char **) (struct_ptr + offset) = arg;
+ return NULL;
+}
+
+API_EXPORT_NONSTD(const char *) ap_set_string_slot_lower(cmd_parms *cmd,
+ char *struct_ptr, char *arg)
+{
+ /* This one's pretty generic... */
+
+ int offset = (int) (long) cmd->info;
+ ap_str_tolower(arg);
+ *(char **) (struct_ptr + offset) = arg;
+ return NULL;
+}
+
+API_EXPORT_NONSTD(const char *) ap_set_flag_slot(cmd_parms *cmd,
+ char *struct_ptr, int arg)
+{
+ /* This one's pretty generic too... */
+
+ int offset = (int) (long) cmd->info;
+ *(int *) (struct_ptr + offset) = arg ? 1 : 0;
+ return NULL;
+}
+
+API_EXPORT_NONSTD(const char *) ap_set_file_slot(cmd_parms *cmd, char *struct_ptr, char *arg)
+{
+ /* Prepend server_root to relative arg.
+ This allows .htaccess to be independent of server_root,
+ so the server can be moved or mirrored with less pain. */
+ char *p;
+ int offset = (int) (long) cmd->info;
+ if (ap_os_is_path_absolute(arg))
+ p = arg;
+ else
+ p = ap_make_full_path(cmd->pool, ap_server_root, arg);
+ *(char **) (struct_ptr + offset) = p;
+ return NULL;
+}
+
+/*****************************************************************
+ *
+ * Reading whole config files...
+ */
+
+static cmd_parms default_parms =
+{NULL, 0, -1, NULL, NULL, NULL, NULL, NULL, NULL, NULL};
+
+API_EXPORT(char *) ap_server_root_relative(pool *p, char *file)
+{
+ if(ap_os_is_path_absolute(file))
+ return file;
+ return ap_make_full_path(p, ap_server_root, file);
+}
+
+
+/* This structure and the following functions are needed for the
+ * table-based config file reading. They are passed to the
+ * cfg_open_custom() routine.
+ */
+
+/* Structure to be passed to cfg_open_custom(): it contains an
+ * index which is incremented from 0 to nelts on each call to
+ * cfg_getline() (which in turn calls arr_elts_getstr())
+ * and an array_header pointer for the string array.
+ */
+typedef struct {
+ array_header *array;
+ int curr_idx;
+} arr_elts_param_t;
+
+
+/* arr_elts_getstr() returns the next line from the string array. */
+static void *arr_elts_getstr(void *buf, size_t bufsiz, void *param)
+{
+ arr_elts_param_t *arr_param = (arr_elts_param_t *) param;
+
+ /* End of array reached? */
+ if (++arr_param->curr_idx > arr_param->array->nelts)
+ return NULL;
+
+ /* return the line */
+ ap_cpystrn(buf, ((char **) arr_param->array->elts)[arr_param->curr_idx - 1], bufsiz);
+
+ return buf;
+}
+
+
+/* arr_elts_close(): dummy close routine (makes sure no more lines can be read) */
+static int arr_elts_close(void *param)
+{
+ arr_elts_param_t *arr_param = (arr_elts_param_t *) param;
+ arr_param->curr_idx = arr_param->array->nelts;
+ return 0;
+}
+
+static void process_command_config(server_rec *s, array_header *arr, pool *p,
+ pool *ptemp)
+{
+ const char *errmsg;
+ cmd_parms parms;
+ arr_elts_param_t arr_parms;
+
+ arr_parms.curr_idx = 0;
+ arr_parms.array = arr;
+
+ parms = default_parms;
+ parms.pool = p;
+ parms.temp_pool = ptemp;
+ parms.server = s;
+ parms.override = (RSRC_CONF | OR_ALL) & ~(OR_AUTHCFG | OR_LIMIT);
+ parms.config_file = ap_pcfg_open_custom(p, "-c/-C directives",
+ &arr_parms, NULL,
+ arr_elts_getstr, arr_elts_close);
+
+ errmsg = ap_srm_command_loop(&parms, s->lookup_defaults);
+
+ if (errmsg) {
+ fprintf(stderr, "Syntax error in -C/-c directive:\n%s\n", errmsg);
+ exit(1);
+ }
+
+ ap_cfg_closefile(parms.config_file);
+}
+
+void ap_process_resource_config(server_rec *s, char *fname, pool *p, pool *ptemp)
+{
+ const char *errmsg;
+ cmd_parms parms;
+ struct stat finfo;
+
+ fname = ap_server_root_relative(p, fname);
+
+ if (!(strcmp(fname, ap_server_root_relative(p, RESOURCE_CONFIG_FILE))) ||
+ !(strcmp(fname, ap_server_root_relative(p, ACCESS_CONFIG_FILE)))) {
+ if (stat(fname, &finfo) == -1)
+ return;
+ }
+
+ /* don't require conf/httpd.conf if we have a -C or -c switch */
+ if((ap_server_pre_read_config->nelts || ap_server_post_read_config->nelts) &&
+ !(strcmp(fname, ap_server_root_relative(p, SERVER_CONFIG_FILE)))) {
+ if (stat(fname, &finfo) == -1)
+ return;
+ }
+
+ /* GCC's initialization extensions are soooo nice here... */
+
+ parms = default_parms;
+ parms.pool = p;
+ parms.temp_pool = ptemp;
+ parms.server = s;
+ parms.override = (RSRC_CONF | OR_ALL) & ~(OR_AUTHCFG | OR_LIMIT);
+
+ if (!(parms.config_file = ap_pcfg_openfile(p,fname))) {
+ perror("fopen");
+ fprintf(stderr, "%s: could not open document config file %s\n",
+ ap_server_argv0, fname);
+ exit(1);
+ }
+
+ errmsg = ap_srm_command_loop(&parms, s->lookup_defaults);
+
+ if (errmsg) {
+ fprintf(stderr, "Syntax error on line %d of %s:\n",
+ parms.config_file->line_number, parms.config_file->name);
+ fprintf(stderr, "%s\n", errmsg);
+ exit(1);
+ }
+
+ ap_cfg_closefile(parms.config_file);
+}
+
+
+int ap_parse_htaccess(void **result, request_rec *r, int override,
+ const char *d, const char *access_name)
+{
+ configfile_t *f = NULL;
+ cmd_parms parms;
+ const char *errmsg;
+ char *filename = NULL;
+ const struct htaccess_result *cache;
+ struct htaccess_result *new;
+ void *dc = NULL;
+
+/* firstly, search cache */
+ for (cache = r->htaccess; cache != NULL; cache = cache->next)
+ if (cache->override == override && strcmp(cache->dir, d) == 0) {
+ if (cache->htaccess != NULL)
+ *result = cache->htaccess;
+ return OK;
+ }
+
+ parms = default_parms;
+ parms.override = override;
+ parms.pool = r->pool;
+ parms.temp_pool = r->pool;
+ parms.server = r->server;
+ parms.path = ap_pstrdup(r->pool, d);
+
+ /* loop through the access names and find the first one */
+
+ while (access_name[0]) {
+ filename = ap_make_full_path(r->pool, d,
+ ap_getword_conf(r->pool, &access_name));
+
+ if ((f = ap_pcfg_openfile(r->pool, filename)) != NULL) {
+
+ dc = ap_create_per_dir_config(r->pool);
+
+ parms.config_file = f;
+
+ errmsg = ap_srm_command_loop(&parms, dc);
+
+ ap_cfg_closefile(f);
+
+ if (errmsg) {
+ ap_log_rerror(APLOG_MARK, APLOG_ALERT|APLOG_NOERRNO, r,
+ "%s: %s", filename, errmsg);
+ return HTTP_INTERNAL_SERVER_ERROR;
+ }
+ *result = dc;
+ break;
+ }
+ else if (errno != ENOENT && errno != ENOTDIR) {
+ ap_log_rerror(APLOG_MARK, APLOG_CRIT, r,
+ "%s pcfg_openfile: unable to check htaccess file, "
+ "ensure it is readable",
+ filename);
+ ap_table_setn(r->notes, "error-notes",
+ "Server unable to read htaccess file, denying "
+ "access to be safe");
+ return HTTP_FORBIDDEN;
+ }
+ }
+
+/* cache it */
+ new = ap_palloc(r->pool, sizeof(struct htaccess_result));
+ new->dir = parms.path;
+ new->override = override;
+ new->htaccess = dc;
+/* add to head of list */
+ new->next = r->htaccess;
+ r->htaccess = new;
+
+ return OK;
+}
+
+
+CORE_EXPORT(const char *) ap_init_virtual_host(pool *p, const char *hostname,
+ server_rec *main_server, server_rec **ps)
+{
+ server_rec *s = (server_rec *) ap_pcalloc(p, sizeof(server_rec));
+
+#ifdef RLIMIT_NOFILE
+ struct rlimit limits;
+
+ getrlimit(RLIMIT_NOFILE, &limits);
+ if (limits.rlim_cur < limits.rlim_max) {
+ limits.rlim_cur += 2;
+ if (setrlimit(RLIMIT_NOFILE, &limits) < 0) {
+ perror("setrlimit(RLIMIT_NOFILE)");
+ fprintf(stderr, "Cannot exceed hard limit for open files");
+ }
+ }
+#endif
+
+ s->server_admin = NULL;
+ s->server_hostname = NULL;
+ s->error_fname = NULL;
+ s->srm_confname = NULL;
+ s->access_confname = NULL;
+ s->timeout = 0;
+ s->keep_alive_timeout = 0;
+ s->keep_alive = -1;
+ s->keep_alive_max = -1;
+ s->error_log = main_server->error_log;
+ s->loglevel = main_server->loglevel;
+ /* useful default, otherwise we get a port of 0 on redirects */
+ s->port = main_server->port;
+ s->next = NULL;
+
+ s->is_virtual = 1;
+ s->names = ap_make_array(p, 4, sizeof(char **));
+ s->wild_names = ap_make_array(p, 4, sizeof(char **));
+
+ s->module_config = create_empty_config(p);
+ s->lookup_defaults = ap_create_per_dir_config(p);
+
+ s->server_uid = ap_user_id;
+ s->server_gid = ap_group_id;
+
+ s->limit_req_line = main_server->limit_req_line;
+ s->limit_req_fieldsize = main_server->limit_req_fieldsize;
+ s->limit_req_fields = main_server->limit_req_fields;
+
+ *ps = s;
+
+ return ap_parse_vhost_addrs(p, hostname, s);
+}
+
+
+static void fixup_virtual_hosts(pool *p, server_rec *main_server)
+{
+ server_rec *virt;
+
+ for (virt = main_server->next; virt; virt = virt->next) {
+ merge_server_configs(p, main_server->module_config,
+ virt->module_config);
+
+ virt->lookup_defaults =
+ ap_merge_per_dir_configs(p, main_server->lookup_defaults,
+ virt->lookup_defaults);
+
+ if (virt->server_admin == NULL)
+ virt->server_admin = main_server->server_admin;
+
+ if (virt->srm_confname == NULL)
+ virt->srm_confname = main_server->srm_confname;
+
+ if (virt->access_confname == NULL)
+ virt->access_confname = main_server->access_confname;
+
+ if (virt->timeout == 0)
+ virt->timeout = main_server->timeout;
+
+ if (virt->keep_alive_timeout == 0)
+ virt->keep_alive_timeout = main_server->keep_alive_timeout;
+
+ if (virt->keep_alive == -1)
+ virt->keep_alive = main_server->keep_alive;
+
+ if (virt->keep_alive_max == -1)
+ virt->keep_alive_max = main_server->keep_alive_max;
+
+ if (virt->send_buffer_size == 0)
+ virt->send_buffer_size = main_server->send_buffer_size;
+
+ /* XXX: this is really something that should be dealt with by a
+ * post-config api phase */
+ ap_core_reorder_directories(p, virt);
+ }
+ ap_core_reorder_directories(p, main_server);
+}
+
+/*****************************************************************
+ *
+ * Getting *everything* configured...
+ */
+
+static void init_config_globals(pool *p)
+{
+ /* ServerRoot, server_confname set in httpd.c */
+
+ ap_standalone = 1;
+ ap_user_name = DEFAULT_USER;
+ ap_user_id = ap_uname2id(DEFAULT_USER);
+ ap_group_id = ap_gname2id(DEFAULT_GROUP);
+ ap_daemons_to_start = DEFAULT_START_DAEMON;
+ ap_daemons_min_free = DEFAULT_MIN_FREE_DAEMON;
+ ap_daemons_max_free = DEFAULT_MAX_FREE_DAEMON;
+ ap_daemons_limit = HARD_SERVER_LIMIT;
+ ap_pid_fname = DEFAULT_PIDLOG;
+ ap_scoreboard_fname = DEFAULT_SCOREBOARD;
+ ap_lock_fname = DEFAULT_LOCKFILE;
+ ap_max_requests_per_child = DEFAULT_MAX_REQUESTS_PER_CHILD;
+ ap_bind_address.s_addr = htonl(INADDR_ANY);
+ ap_listeners = NULL;
+ ap_listenbacklog = DEFAULT_LISTENBACKLOG;
+ ap_extended_status = 0;
+
+ /* Global virtual host hash bucket pointers. Init to null. */
+ ap_init_vhost_config(p);
+
+ ap_cpystrn(ap_coredump_dir, ap_server_root, sizeof(ap_coredump_dir));
+}
+
+static server_rec *init_server_config(pool *p)
+{
+ server_rec *s = (server_rec *) ap_pcalloc(p, sizeof(server_rec));
+
+ s->port = 0;
+ s->server_admin = DEFAULT_ADMIN;
+ s->server_hostname = NULL;
+ s->error_fname = DEFAULT_ERRORLOG;
+ s->error_log = stderr;
+ s->loglevel = DEFAULT_LOGLEVEL;
+ s->srm_confname = RESOURCE_CONFIG_FILE;
+ s->access_confname = ACCESS_CONFIG_FILE;
+ s->limit_req_line = DEFAULT_LIMIT_REQUEST_LINE;
+ s->limit_req_fieldsize = DEFAULT_LIMIT_REQUEST_FIELDSIZE;
+ s->limit_req_fields = DEFAULT_LIMIT_REQUEST_FIELDS;
+ s->timeout = DEFAULT_TIMEOUT;
+ s->keep_alive_timeout = DEFAULT_KEEPALIVE_TIMEOUT;
+ s->keep_alive_max = DEFAULT_KEEPALIVE;
+ s->keep_alive = 1;
+ s->next = NULL;
+ s->addrs = ap_pcalloc(p, sizeof(server_addr_rec));
+ /* NOT virtual host; don't match any real network interface */
+ s->addrs->host_addr.s_addr = htonl(INADDR_ANY);
+ s->addrs->host_port = 0; /* matches any port */
+ s->addrs->virthost = ""; /* must be non-NULL */
+ s->names = s->wild_names = NULL;
+
+ s->module_config = create_server_config(p, s);
+ s->lookup_defaults = create_default_per_dir_config(p);
+
+ return s;
+}
+
+
+static void default_listeners(pool *p, server_rec *s)
+{
+ listen_rec *new;
+
+ if (ap_listeners != NULL) {
+ return;
+ }
+ /* allocate a default listener */
+ new = ap_pcalloc(p, sizeof(listen_rec));
+ new->local_addr.sin_family = AF_INET;
+ new->local_addr.sin_addr = ap_bind_address;
+ new->local_addr.sin_port = htons(s->port ? s->port : DEFAULT_HTTP_PORT);
+ new->fd = -1;
+ new->used = 0;
+ new->next = NULL;
+ ap_listeners = new;
+}
+
+
+server_rec *ap_read_config(pool *p, pool *ptemp, char *confname)
+{
+ server_rec *s = init_server_config(p);
+
+ init_config_globals(p);
+
+ /* All server-wide config files now have the SAME syntax... */
+
+ process_command_config(s, ap_server_pre_read_config, p, ptemp);
+
+ ap_process_resource_config(s, confname, p, ptemp);
+ ap_process_resource_config(s, s->srm_confname, p, ptemp);
+ ap_process_resource_config(s, s->access_confname, p, ptemp);
+
+ process_command_config(s, ap_server_post_read_config, p, ptemp);
+
+ fixup_virtual_hosts(p, s);
+ default_listeners(p, s);
+ ap_fini_vhost_config(p, s);
+
+ return s;
+}
+
+void ap_single_module_configure(pool *p, server_rec *s, module *m)
+{
+ if (m->create_server_config)
+ ap_set_module_config(s->module_config, m,
+ (*m->create_server_config)(p, s));
+ if (m->create_dir_config)
+ ap_set_module_config(s->lookup_defaults, m,
+ (*m->create_dir_config)(p, NULL));
+}
+
+void ap_init_modules(pool *p, server_rec *s)
+{
+ module *m;
+
+ for (m = top_module; m; m = m->next)
+ if (m->init)
+ (*m->init) (s, p);
+ build_method_shortcuts();
+ init_handlers(p);
+}
+
+void ap_child_init_modules(pool *p, server_rec *s)
+{
+ module *m;
+
+ for (m = top_module; m; m = m->next)
+ if (m->child_init)
+ (*m->child_init) (s, p);
+}
+
+void ap_child_exit_modules(pool *p, server_rec *s)
+{
+ module *m;
+
+#ifdef SIGHUP
+ signal(SIGHUP, SIG_IGN);
+#endif
+#ifdef SIGUSR1
+ signal(SIGUSR1, SIG_IGN);
+#endif
+
+ for (m = top_module; m; m = m->next)
+ if (m->child_exit)
+ (*m->child_exit) (s, p);
+
+}
+
+/********************************************************************
+ * Configuration directives are restricted in terms of where they may
+ * appear in the main configuration files and/or .htaccess files according
+ * to the bitmask req_override in the command_rec structure.
+ * If any of the overrides set in req_override are also allowed in the
+ * context in which the command is read, then the command is allowed.
+ * The context is determined as follows:
+ *
+ * inside *.conf --> override = (RSRC_CONF|OR_ALL)&~(OR_AUTHCFG|OR_LIMIT);
+ * within <Directory> or <Location> --> override = OR_ALL|ACCESS_CONF;
+ * within .htaccess --> override = AllowOverride for current directory;
+ *
+ * the result is, well, a rather confusing set of possibilities for when
+ * a particular directive is allowed to be used. This procedure prints
+ * in English where the given (pc) directive can be used.
+ */
+static void show_overrides(const command_rec *pc, module *pm)
+{
+ int n = 0;
+
+ printf("\tAllowed in *.conf ");
+ if ((pc->req_override & (OR_OPTIONS | OR_FILEINFO | OR_INDEXES)) ||
+ ((pc->req_override & RSRC_CONF) &&
+ ((pc->req_override & (ACCESS_CONF | OR_AUTHCFG | OR_LIMIT)))))
+ printf("anywhere");
+ else if (pc->req_override & RSRC_CONF)
+ printf("only outside <Directory>, <Files> or <Location>");
+ else
+ printf("only inside <Directory>, <Files> or <Location>");
+
+ /* Warn if the directive is allowed inside <Directory> or .htaccess
+ * but module doesn't support per-dir configuration */
+
+ if ((pc->req_override & (OR_ALL | ACCESS_CONF)) && !pm->create_dir_config)
+ printf(" [no per-dir config]");
+
+ if (pc->req_override & OR_ALL) {
+ printf(" and in .htaccess\n\twhen AllowOverride");
+
+ if ((pc->req_override & OR_ALL) == OR_ALL)
+ printf(" isn't None");
+ else {
+ printf(" includes ");
+
+ if (pc->req_override & OR_AUTHCFG) {
+ if (n++)
+ printf(" or ");
+ printf("AuthConfig");
+ }
+ if (pc->req_override & OR_LIMIT) {
+ if (n++)
+ printf(" or ");
+ printf("Limit");
+ }
+ if (pc->req_override & OR_OPTIONS) {
+ if (n++)
+ printf(" or ");
+ printf("Options");
+ }
+ if (pc->req_override & OR_FILEINFO) {
+ if (n++)
+ printf(" or ");
+ printf("FileInfo");
+ }
+ if (pc->req_override & OR_INDEXES) {
+ if (n++)
+ printf(" or ");
+ printf("Indexes");
+ }
+ }
+ }
+ printf("\n");
+}
+
+/* Show the preloaded configuration directives, the help string explaining
+ * the directive arguments, in what module they are handled, and in
+ * what parts of the configuration they are allowed. Used for httpd -h.
+ */
+void ap_show_directives()
+{
+ const command_rec *pc;
+ int n;
+
+ for (n = 0; ap_loaded_modules[n]; ++n)
+ for (pc = ap_loaded_modules[n]->cmds; pc && pc->name; ++pc) {
+ printf("%s (%s)\n", pc->name, ap_loaded_modules[n]->name);
+ if (pc->errmsg)
+ printf("\t%s\n", pc->errmsg);
+ show_overrides(pc, ap_loaded_modules[n]);
+ }
+}
+
+/* Show the preloaded module names. Used for httpd -l. */
+void ap_show_modules()
+{
+ int n;
+
+ printf("Compiled-in modules:\n");
+ for (n = 0; ap_loaded_modules[n]; ++n)
+ printf(" %s\n", ap_loaded_modules[n]->name);
+}
--- /dev/null
+/* we need some of the portability definitions... for strchr */
+#include "httpd.h"
+
+/* A bunch of functions in util.c scan strings looking for certain characters.
+ * To make that more efficient we encode a lookup table.
+ */
+#define T_ESCAPE_SHELL_CMD (0x01)
+#define T_ESCAPE_PATH_SEGMENT (0x02)
+#define T_OS_ESCAPE_PATH (0x04)
+#define T_HTTP_TOKEN_STOP (0x08)
+
+int main(int argc, char *argv[])
+{
+ unsigned c;
+ unsigned char flags;
+
+ printf(
+"/* this file is automatically generated by gen_test_char, do not edit */\n"
+"#define T_ESCAPE_SHELL_CMD (%u)\n"
+"#define T_ESCAPE_PATH_SEGMENT (%u)\n"
+"#define T_OS_ESCAPE_PATH (%u)\n"
+"#define T_HTTP_TOKEN_STOP (%u)\n"
+"\n"
+"static const unsigned char test_char_table[256] = {\n"
+" 0,",
+ T_ESCAPE_SHELL_CMD,
+ T_ESCAPE_PATH_SEGMENT,
+ T_OS_ESCAPE_PATH,
+ T_HTTP_TOKEN_STOP);
+
+ /* we explicitly dealt with NUL above
+ * in case some strchr() do bogosity with it */
+
+ for (c = 1; c < 256; ++c) {
+ flags = 0;
+ if (c % 20 == 0)
+ printf("\n ");
+
+ /* escape_shell_cmd */
+ if (strchr("&;`'\"|*?~<>^()[]{}$\\\n", c)) {
+ flags |= T_ESCAPE_SHELL_CMD;
+ }
+
+ if (!ap_isalnum(c) && !strchr("$-_.+!*'(),:@&=~", c)) {
+ flags |= T_ESCAPE_PATH_SEGMENT;
+ }
+
+ if (!ap_isalnum(c) && !strchr("$-_.+!*'(),:@&=/~", c)) {
+ flags |= T_OS_ESCAPE_PATH;
+ }
+
+ /* these are the "tspecials" from RFC2068 */
+ if (ap_iscntrl(c) || strchr(" \t()<>@,;:\\/[]?={}", c)) {
+ flags |= T_HTTP_TOKEN_STOP;
+ }
+ printf("%u%c", flags, (c < 255) ? ',' : ' ');
+
+ }
+ printf("\n};\n");
+
+ return 0;
+}
--- /dev/null
+# Microsoft Developer Studio Project File - Name="gen_test_char" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 5.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Console Application" 0x0103
+
+CFG=gen_test_char - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "gen_test_char.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 "gen_test_char.mak" CFG="gen_test_char - Win32 Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "gen_test_char - Win32 Release" (based on\
+ "Win32 (x86) Console Application")
+!MESSAGE "gen_test_char - Win32 Debug" (based on\
+ "Win32 (x86) Console Application")
+!MESSAGE
+
+# Begin Project
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "gen_test_char - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "gen_test"
+# PROP BASE Intermediate_Dir "gen_test"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir ""
+# PROP Intermediate_Dir "gen_test_char_R"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
+# ADD CPP /nologo /W3 /GX /O2 /I "..\include" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
+# ADD BASE RSC /l 0x809 /d "NDEBUG"
+# ADD RSC /l 0x809 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib /nologo /subsystem:console /machine:I386
+# Begin Special Build Tool
+SOURCE=$(InputPath)
+PostBuild_Desc=Create test_char.h
+PostBuild_Cmds=.\gen_test_char > test_char.h
+# End Special Build Tool
+
+!ELSEIF "$(CFG)" == "gen_test_char - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "gen_tes0"
+# PROP BASE Intermediate_Dir "gen_tes0"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir ""
+# PROP Intermediate_Dir "gen_test_char_D"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
+# ADD CPP /nologo /W3 /Gm /GX /Zi /Od /I "..\include" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
+# ADD BASE RSC /l 0x809 /d "_DEBUG"
+# ADD RSC /l 0x809 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
+# Begin Special Build Tool
+SOURCE=$(InputPath)
+PostBuild_Desc=Create test_char.h
+PostBuild_Cmds=.\gen_test_char > test_char.h
+# End Special Build Tool
+
+!ENDIF
+
+# Begin Target
+
+# Name "gen_test_char - Win32 Release"
+# Name "gen_test_char - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=.\gen_test_char.c
+# End Source File
+# End Group
+# End Target
+# End Project
--- /dev/null
+#include <stdio.h>
+
+/* generate a table of 256 values, where certain characters are
+ * marked "interesting"... for the uri parsing process.
+ */
+
+int main(int argc, char *argv[])
+{
+ int i;
+ char *value;
+
+ printf("/* this file is automatically generated by "
+ "gen_uri_delims, do not edit */\n");
+ printf("static const unsigned char uri_delims[256] = {");
+ for (i = 0; i < 256; ++i) {
+ if (i % 20 == 0)
+ printf("\n ");
+ switch (i) {
+ case ':': value = "T_COLON"; break;
+ case '/': value = "T_SLASH"; break;
+ case '?': value = "T_QUESTION"; break;
+ case '#': value = "T_HASH"; break;
+ case '\0': value = "T_NUL"; break;
+ default: value = "0"; break;
+ }
+ printf("%s%c", value, (i < 255) ? ',' : ' ');
+ }
+ printf("\n};\n");
+
+ return 0;
+}
--- /dev/null
+# Microsoft Developer Studio Project File - Name="gen_uri_delims" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 5.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Console Application" 0x0103
+
+CFG=gen_uri_delims - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "gen_uri_delims.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 "gen_uri_delims.mak" CFG="gen_uri_delims - Win32 Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "gen_uri_delims - Win32 Release" (based on\
+ "Win32 (x86) Console Application")
+!MESSAGE "gen_uri_delims - Win32 Debug" (based on\
+ "Win32 (x86) Console Application")
+!MESSAGE
+
+# Begin Project
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "gen_uri_delims - 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 ""
+# PROP Intermediate_Dir "gen_uri_delims_R"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
+# ADD CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
+# ADD BASE RSC /l 0x809 /d "NDEBUG"
+# ADD RSC /l 0x809 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib /nologo /subsystem:console /machine:I386
+# Begin Special Build Tool
+SOURCE=$(InputPath)
+PostBuild_Desc=Create uri_delims.h
+PostBuild_Cmds=.\gen_uri_delims > uri_delims.h
+# End Special Build Tool
+
+!ELSEIF "$(CFG)" == "gen_uri_delims - 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 ""
+# PROP Intermediate_Dir "gen_uri_delims_D"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
+# ADD CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
+# ADD BASE RSC /l 0x809 /d "_DEBUG"
+# ADD RSC /l 0x809 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
+# Begin Special Build Tool
+SOURCE=$(InputPath)
+PostBuild_Desc=Create uri_delims.h
+PostBuild_Cmds=.\gen_uri_delims > uri_delims.h
+# End Special Build Tool
+
+!ENDIF
+
+# Begin Target
+
+# Name "gen_uri_delims - Win32 Release"
+# Name "gen_uri_delims - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=.\gen_uri_delims.c
+# End Source File
+# End Group
+# End Target
+# End Project
--- /dev/null
+/* ====================================================================
+ * Copyright (c) 1995-1999 The Apache Group. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * 4. The names "Apache Server" and "Apache Group" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission. For written permission, please contact
+ * apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ * nor may "Apache" appear in their names without prior written
+ * permission of the Apache Group.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Group and was originally based
+ * on public domain software written at the National Center for
+ * Supercomputing Applications, University of Illinois, Urbana-Champaign.
+ * For more information on the Apache Group and the Apache HTTP server
+ * project, please see <http://www.apache.org/>.
+ *
+ */
+
+/*
+ * http_log.c: Dealing with the logs and errors
+ *
+ * Rob McCool
+ *
+ */
+
+
+#define CORE_PRIVATE
+#include "httpd.h"
+#include "http_conf_globals.h"
+#include "http_config.h"
+#include "http_core.h"
+#include "http_log.h"
+#include "http_main.h"
+
+#include <stdarg.h>
+
+typedef struct {
+ char *t_name;
+ int t_val;
+} TRANS;
+
+#ifdef HAVE_SYSLOG
+
+static const TRANS facilities[] = {
+ {"auth", LOG_AUTH},
+#ifdef LOG_AUTHPRIV
+ {"authpriv",LOG_AUTHPRIV},
+#endif
+#ifdef LOG_CRON
+ {"cron", LOG_CRON},
+#endif
+#ifdef LOG_DAEMON
+ {"daemon", LOG_DAEMON},
+#endif
+#ifdef LOG_FTP
+ {"ftp", LOG_FTP},
+#endif
+#ifdef LOG_KERN
+ {"kern", LOG_KERN},
+#endif
+#ifdef LOG_LPR
+ {"lpr", LOG_LPR},
+#endif
+#ifdef LOG_MAIL
+ {"mail", LOG_MAIL},
+#endif
+#ifdef LOG_NEWS
+ {"news", LOG_NEWS},
+#endif
+#ifdef LOG_SYSLOG
+ {"syslog", LOG_SYSLOG},
+#endif
+#ifdef LOG_USER
+ {"user", LOG_USER},
+#endif
+#ifdef LOG_UUCP
+ {"uucp", LOG_UUCP},
+#endif
+#ifdef LOG_LOCAL0
+ {"local0", LOG_LOCAL0},
+#endif
+#ifdef LOG_LOCAL1
+ {"local1", LOG_LOCAL1},
+#endif
+#ifdef LOG_LOCAL2
+ {"local2", LOG_LOCAL2},
+#endif
+#ifdef LOG_LOCAL3
+ {"local3", LOG_LOCAL3},
+#endif
+#ifdef LOG_LOCAL4
+ {"local4", LOG_LOCAL4},
+#endif
+#ifdef LOG_LOCAL5
+ {"local5", LOG_LOCAL5},
+#endif
+#ifdef LOG_LOCAL6
+ {"local6", LOG_LOCAL6},
+#endif
+#ifdef LOG_LOCAL7
+ {"local7", LOG_LOCAL7},
+#endif
+ {NULL, -1},
+};
+#endif
+
+static const TRANS priorities[] = {
+ {"emerg", APLOG_EMERG},
+ {"alert", APLOG_ALERT},
+ {"crit", APLOG_CRIT},
+ {"error", APLOG_ERR},
+ {"warn", APLOG_WARNING},
+ {"notice", APLOG_NOTICE},
+ {"info", APLOG_INFO},
+ {"debug", APLOG_DEBUG},
+ {NULL, -1},
+};
+
+static int error_log_child(void *cmd, child_info *pinfo)
+{
+ /* Child process code for 'ErrorLog "|..."';
+ * may want a common framework for this, since I expect it will
+ * be common for other foo-loggers to want this sort of thing...
+ */
+ int child_pid = 0;
+
+ ap_cleanup_for_exec();
+#ifdef SIGHUP
+ /* No concept of a child process on Win32 */
+ signal(SIGHUP, SIG_IGN);
+#endif /* ndef SIGHUP */
+#if defined(WIN32)
+ child_pid = spawnl(_P_NOWAIT, SHELL_PATH, SHELL_PATH, "/c", (char *)cmd, NULL);
+ return(child_pid);
+#elif defined(OS2)
+ /* For OS/2 we need to use a '/' and spawn the child rather than exec as
+ * we haven't forked */
+ child_pid = spawnl(P_NOWAIT, SHELL_PATH, SHELL_PATH, "/c", (char *)cmd, NULL);
+ return(child_pid);
+#else
+ execl(SHELL_PATH, SHELL_PATH, "-c", (char *)cmd, NULL);
+#endif
+ exit(1);
+ /* NOT REACHED */
+ return(child_pid);
+}
+
+static void open_error_log(server_rec *s, pool *p)
+{
+ char *fname;
+
+ if (*s->error_fname == '|') {
+ FILE *dummy;
+#ifdef TPF
+ TPF_FORK_CHILD cld;
+ cld.filename = s->error_fname+1;
+ cld.subprocess_env = NULL;
+ cld.prog_type = FORK_NAME;
+ if (!ap_spawn_child(p, NULL, &cld,
+ kill_after_timeout, &dummy, NULL, NULL)) {
+#else
+ if (!ap_spawn_child(p, error_log_child, (void *)(s->error_fname+1),
+ kill_after_timeout, &dummy, NULL, NULL)) {
+#endif /* TPF */
+ perror("ap_spawn_child");
+ fprintf(stderr, "Couldn't fork child for ErrorLog process\n");
+ exit(1);
+ }
+
+ s->error_log = dummy;
+ }
+
+#ifdef HAVE_SYSLOG
+ else if (!strncasecmp(s->error_fname, "syslog", 6)) {
+ if ((fname = strchr(s->error_fname, ':'))) {
+ const TRANS *fac;
+
+ fname++;
+ for (fac = facilities; fac->t_name; fac++) {
+ if (!strcasecmp(fname, fac->t_name)) {
+ openlog(ap_server_argv0, LOG_NDELAY|LOG_CONS|LOG_PID,
+ fac->t_val);
+ s->error_log = NULL;
+ return;
+ }
+ }
+ }
+ else
+ openlog(ap_server_argv0, LOG_NDELAY|LOG_CONS|LOG_PID, LOG_LOCAL7);
+
+ s->error_log = NULL;
+ }
+#endif
+ else {
+ fname = ap_server_root_relative(p, s->error_fname);
+ if (!(s->error_log = ap_pfopen(p, fname, "a"))) {
+ perror("fopen");
+ fprintf(stderr, "%s: could not open error log file %s.\n",
+ ap_server_argv0, fname);
+ exit(1);
+ }
+ }
+}
+
+void ap_open_logs(server_rec *s_main, pool *p)
+{
+ server_rec *virt, *q;
+ int replace_stderr;
+
+ open_error_log(s_main, p);
+
+ replace_stderr = 1;
+ if (s_main->error_log) {
+ /* replace stderr with this new log */
+ fflush(stderr);
+ if (dup2(fileno(s_main->error_log), STDERR_FILENO) == -1) {
+ ap_log_error(APLOG_MARK, APLOG_CRIT, s_main,
+ "unable to replace stderr with error_log");
+ } else {
+ replace_stderr = 0;
+ }
+ }
+ /* note that stderr may still need to be replaced with something
+ * because it points to the old error log, or back to the tty
+ * of the submitter.
+ */
+ if (replace_stderr && freopen("/dev/null", "w", stderr) == NULL) {
+ ap_log_error(APLOG_MARK, APLOG_CRIT, s_main,
+ "unable to replace stderr with /dev/null");
+ }
+
+ for (virt = s_main->next; virt; virt = virt->next) {
+ if (virt->error_fname)
+ {
+ for (q=s_main; q != virt; q = q->next)
+ if (q->error_fname != NULL &&
+ strcmp(q->error_fname, virt->error_fname) == 0)
+ break;
+ if (q == virt)
+ open_error_log(virt, p);
+ else
+ virt->error_log = q->error_log;
+ }
+ else
+ virt->error_log = s_main->error_log;
+ }
+}
+
+API_EXPORT(void) ap_error_log2stderr(server_rec *s) {
+ if ( s->error_log != NULL
+ && fileno(s->error_log) != STDERR_FILENO)
+ dup2(fileno(s->error_log), STDERR_FILENO);
+}
+
+static void log_error_core(const char *file, int line, int level,
+ const server_rec *s, const request_rec *r,
+ const char *fmt, va_list args)
+{
+ char errstr[MAX_STRING_LEN];
+ size_t len;
+ int save_errno = errno;
+ FILE *logf;
+
+ if (s == NULL) {
+ /*
+ * If we are doing stderr logging (startup), don't log messages that are
+ * above the default server log level unless it is a startup/shutdown
+ * notice
+ */
+ if (((level & APLOG_LEVELMASK) != APLOG_NOTICE) &&
+ ((level & APLOG_LEVELMASK) > DEFAULT_LOGLEVEL))
+ return;
+ logf = stderr;
+ }
+ else if (s->error_log) {
+ /*
+ * If we are doing normal logging, don't log messages that are
+ * above the server log level unless it is a startup/shutdown notice
+ */
+ if (((level & APLOG_LEVELMASK) != APLOG_NOTICE) &&
+ ((level & APLOG_LEVELMASK) > s->loglevel))
+ return;
+ logf = s->error_log;
+ }
+#ifdef TPF
+ else if (tpf_child) {
+ /*
+ * If we are doing normal logging, don't log messages that are
+ * above the server log level unless it is a startup/shutdown notice
+ */
+ if (((level & APLOG_LEVELMASK) != APLOG_NOTICE) &&
+ ((level & APLOG_LEVELMASK) > s->loglevel))
+ return;
+ logf = stderr;
+ }
+#endif /* TPF */
+ else {
+ /*
+ * If we are doing syslog logging, don't log messages that are
+ * above the server log level (including a startup/shutdown notice)
+ */
+ if ((level & APLOG_LEVELMASK) > s->loglevel)
+ return;
+ logf = NULL;
+ }
+
+ if (logf) {
+ len = ap_snprintf(errstr, sizeof(errstr), "[%s] ", ap_get_time());
+ } else {
+ len = 0;
+ }
+
+ len += ap_snprintf(errstr + len, sizeof(errstr) - len,
+ "[%s] ", priorities[level & APLOG_LEVELMASK].t_name);
+
+#ifndef TPF
+ if (file && (level & APLOG_LEVELMASK) == APLOG_DEBUG) {
+#ifdef _OSD_POSIX
+ char tmp[256];
+ char *e = strrchr(file, '/');
+
+ /* In OSD/POSIX, the compiler returns for __FILE__
+ * a string like: __FILE__="*POSIX(/usr/include/stdio.h)"
+ * (it even returns an absolute path for sources in
+ * the current directory). Here we try to strip this
+ * down to the basename.
+ */
+ if (e != NULL && e[1] != '\0') {
+ ap_snprintf(tmp, sizeof(tmp), "%s", &e[1]);
+ e = &tmp[strlen(tmp)-1];
+ if (*e == ')')
+ *e = '\0';
+ file = tmp;
+ }
+#endif /*_OSD_POSIX*/
+ len += ap_snprintf(errstr + len, sizeof(errstr) - len,
+ "%s(%d): ", file, line);
+ }
+#endif /* TPF */
+ if (r) {
+ /* XXX: TODO: add a method of selecting whether logged client
+ * addresses are in dotted quad or resolved form... dotted
+ * quad is the most secure, which is why I'm implementing it
+ * first. -djg
+ */
+ len += ap_snprintf(errstr + len, sizeof(errstr) - len,
+ "[client %s] ", r->connection->remote_ip);
+ }
+ if (!(level & APLOG_NOERRNO)
+ && (save_errno != 0)
+#ifdef WIN32
+ && !(level & APLOG_WIN32ERROR)
+#endif
+ ) {
+ len += ap_snprintf(errstr + len, sizeof(errstr) - len,
+ "(%d)%s: ", save_errno, strerror(save_errno));
+ }
+#ifdef WIN32
+ if (level & APLOG_WIN32ERROR) {
+ int nChars;
+ int nErrorCode;
+
+ nErrorCode = GetLastError();
+ len += ap_snprintf(errstr + len, sizeof(errstr) - len,
+ "(%d)", nErrorCode);
+
+ nChars = FormatMessage(
+ FORMAT_MESSAGE_FROM_SYSTEM,
+ NULL,
+ nErrorCode,
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
+ (LPTSTR) errstr + len,
+ sizeof(errstr) - len,
+ NULL
+ );
+ len += nChars;
+ if (nChars == 0) {
+ /* Um, error occurred, but we can't recurse to log it again
+ * (and it would probably only fail anyway), so lets just
+ * log the numeric value.
+ */
+ nErrorCode = GetLastError();
+ len += ap_snprintf(errstr + len, sizeof(errstr) - len,
+ "(FormatMessage failed with code %d): ",
+ nErrorCode);
+ }
+ else {
+ /* FormatMessage put the message in the buffer, but it may
+ * have appended a newline (\r\n). So remove it and use
+ * ": " instead like the Unix errors. The error may also
+ * end with a . before the return - if so, trash it.
+ */
+ if (len > 1 && errstr[len-2] == '\r' && errstr[len-1] == '\n') {
+ if (len > 2 && errstr[len-3] == '.')
+ len--;
+ errstr[len-2] = ':';
+ errstr[len-1] = ' ';
+ }
+ }
+ }
+#endif
+
+ len += ap_vsnprintf(errstr + len, sizeof(errstr) - len, fmt, args);
+
+ /* NULL if we are logging to syslog */
+ if (logf) {
+ fputs(errstr, logf);
+ fputc('\n', logf);
+ fflush(logf);
+ }
+#ifdef HAVE_SYSLOG
+ else {
+ syslog(level & APLOG_LEVELMASK, "%s", errstr);
+ }
+#endif
+}
+
+API_EXPORT(void) ap_log_error(const char *file, int line, int level,
+ const server_rec *s, const char *fmt, ...)
+{
+ va_list args;
+
+ va_start(args, fmt);
+ log_error_core(file, line, level, s, NULL, fmt, args);
+ va_end(args);
+}
+
+API_EXPORT(void) ap_log_rerror(const char *file, int line, int level,
+ const request_rec *r, const char *fmt, ...)
+{
+ va_list args;
+
+ va_start(args, fmt);
+ log_error_core(file, line, level, r->server, r, fmt, args);
+ /*
+ * IF the error level is 'warning' or more severe,
+ * AND there isn't already error text associated with this request,
+ * THEN make the message text available to ErrorDocument and
+ * other error processors. This can be disabled by stuffing
+ * something, even an empty string, into the "error-notes" cell
+ * before calling this routine.
+ */
+ va_end(args);
+ va_start(args,fmt);
+ if (((level & APLOG_LEVELMASK) <= APLOG_WARNING)
+ && (ap_table_get(r->notes, "error-notes") == NULL)) {
+ ap_table_setn(r->notes, "error-notes",
+ ap_pvsprintf(r->pool, fmt, args));
+ }
+ va_end(args);
+}
+
+void ap_log_pid(pool *p, char *fname)
+{
+ FILE *pid_file;
+ struct stat finfo;
+ static pid_t saved_pid = -1;
+ pid_t mypid;
+
+ if (!fname)
+ return;
+
+ fname = ap_server_root_relative(p, fname);
+ mypid = getpid();
+ if (mypid != saved_pid && stat(fname, &finfo) == 0) {
+ /* USR1 and HUP call this on each restart.
+ * Only warn on first time through for this pid.
+ *
+ * XXX: Could just write first time through too, although
+ * that may screw up scripts written to do something
+ * based on the last modification time of the pid file.
+ */
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, NULL,
+ ap_psprintf(p,
+ "pid file %s overwritten -- Unclean shutdown of previous Apache run?",
+ fname)
+ );
+ }
+
+ if(!(pid_file = fopen(fname, "w"))) {
+ perror("fopen");
+ fprintf(stderr, "%s: could not log pid to file %s\n",
+ ap_server_argv0, fname);
+ exit(1);
+ }
+ fprintf(pid_file, "%ld\n", (long)mypid);
+ fclose(pid_file);
+ saved_pid = mypid;
+}
+
+API_EXPORT(void) ap_log_error_old(const char *err, server_rec *s)
+{
+ ap_log_error(APLOG_MARK, APLOG_ERR, s, "%s", err);
+}
+
+API_EXPORT(void) ap_log_unixerr(const char *routine, const char *file,
+ const char *msg, server_rec *s)
+{
+ ap_log_error(file, 0, APLOG_ERR, s, "%s", msg);
+}
+
+API_EXPORT(void) ap_log_printf(const server_rec *s, const char *fmt, ...)
+{
+ va_list args;
+
+ va_start(args, fmt);
+ log_error_core(APLOG_MARK, APLOG_ERR, s, NULL, fmt, args);
+ va_end(args);
+}
+
+API_EXPORT(void) ap_log_reason(const char *reason, const char *file, request_rec *r)
+{
+ ap_log_error(APLOG_MARK, APLOG_ERR, r->server,
+ "access to %s failed for %s, reason: %s",
+ file,
+ ap_get_remote_host(r->connection, r->per_dir_config, REMOTE_NAME),
+ reason);
+}
+
+API_EXPORT(void) ap_log_assert(const char *szExp, const char *szFile, int nLine)
+{
+ fprintf(stderr, "[%s] file %s, line %d, assertion \"%s\" failed\n",
+ ap_get_time(), szFile, nLine, szExp);
+#ifndef WIN32
+ /* unix assert does an abort leading to a core dump */
+ abort();
+#else
+ exit(1);
+#endif
+}
+
+/* piped log support */
+
+#ifndef NO_RELIABLE_PIPED_LOGS
+/* forward declaration */
+static void piped_log_maintenance(int reason, void *data, ap_wait_t status);
+
+static int piped_log_spawn(piped_log *pl)
+{
+ int pid;
+
+ ap_block_alarms();
+ pid = fork();
+ if (pid == 0) {
+ /* XXX: this needs porting to OS2 and WIN32 */
+ /* XXX: need to check what open fds the logger is actually passed,
+ * XXX: and CGIs for that matter ... cleanup_for_exec *should*
+ * XXX: close all the relevant stuff, but hey, it could be broken. */
+ RAISE_SIGSTOP(PIPED_LOG_SPAWN);
+ /* we're now in the child */
+ close(STDIN_FILENO);
+ dup2(pl->fds[0], STDIN_FILENO);
+
+ ap_cleanup_for_exec();
+ signal(SIGCHLD, SIG_DFL); /* for HPUX */
+ signal(SIGHUP, SIG_IGN);
+ execl(SHELL_PATH, SHELL_PATH, "-c", pl->program, NULL);
+ fprintf(stderr,
+ "piped_log_spawn: unable to exec %s -c '%s': %s\n",
+ SHELL_PATH, pl->program, strerror (errno));
+ exit(1);
+ }
+ if (pid == -1) {
+ fprintf(stderr,
+ "piped_log_spawn: unable to fork(): %s\n", strerror (errno));
+ ap_unblock_alarms();
+ return -1;
+ }
+ ap_unblock_alarms();
+ pl->pid = pid;
+ ap_register_other_child(pid, piped_log_maintenance, pl, pl->fds[1]);
+ return 0;
+}
+
+
+static void piped_log_maintenance(int reason, void *data, ap_wait_t status)
+{
+ piped_log *pl = data;
+
+ switch (reason) {
+ case OC_REASON_DEATH:
+ case OC_REASON_LOST:
+ pl->pid = -1;
+ ap_unregister_other_child(pl);
+ if (pl->program == NULL) {
+ /* during a restart */
+ break;
+ }
+ if (piped_log_spawn(pl) == -1) {
+ /* what can we do? This could be the error log we're having
+ * problems opening up... */
+ fprintf(stderr,
+ "piped_log_maintenance: unable to respawn '%s': %s\n",
+ pl->program, strerror(errno));
+ }
+ break;
+
+ case OC_REASON_UNWRITABLE:
+ if (pl->pid != -1) {
+ kill(pl->pid, SIGTERM);
+ }
+ break;
+
+ case OC_REASON_RESTART:
+ pl->program = NULL;
+ if (pl->pid != -1) {
+ kill(pl->pid, SIGTERM);
+ }
+ break;
+
+ case OC_REASON_UNREGISTER:
+ break;
+ }
+}
+
+
+static void piped_log_cleanup(void *data)
+{
+ piped_log *pl = data;
+
+ if (pl->pid != -1) {
+ kill(pl->pid, SIGTERM);
+ }
+ ap_unregister_other_child(pl);
+ close(pl->fds[0]);
+ close(pl->fds[1]);
+}
+
+
+static void piped_log_cleanup_for_exec(void *data)
+{
+ piped_log *pl = data;
+
+ close(pl->fds[0]);
+ close(pl->fds[1]);
+}
+
+
+API_EXPORT(piped_log *) ap_open_piped_log(pool *p, const char *program)
+{
+ piped_log *pl;
+
+ pl = ap_palloc(p, sizeof (*pl));
+ pl->p = p;
+ pl->program = ap_pstrdup(p, program);
+ pl->pid = -1;
+ ap_block_alarms ();
+ if (pipe(pl->fds) == -1) {
+ int save_errno = errno;
+ ap_unblock_alarms();
+ errno = save_errno;
+ return NULL;
+ }
+ ap_register_cleanup(p, pl, piped_log_cleanup, piped_log_cleanup_for_exec);
+ if (piped_log_spawn(pl) == -1) {
+ int save_errno = errno;
+ ap_kill_cleanup(p, pl, piped_log_cleanup);
+ close(pl->fds[0]);
+ close(pl->fds[1]);
+ ap_unblock_alarms();
+ errno = save_errno;
+ return NULL;
+ }
+ ap_unblock_alarms();
+ return pl;
+}
+
+API_EXPORT(void) ap_close_piped_log(piped_log *pl)
+{
+ ap_block_alarms();
+ piped_log_cleanup(pl);
+ ap_kill_cleanup(pl->p, pl, piped_log_cleanup);
+ ap_unblock_alarms();
+}
+
+#else
+static int piped_log_child(void *cmd, child_info *pinfo)
+{
+ /* Child process code for 'TransferLog "|..."';
+ * may want a common framework for this, since I expect it will
+ * be common for other foo-loggers to want this sort of thing...
+ */
+ int child_pid = 1;
+
+ ap_cleanup_for_exec();
+#ifdef SIGHUP
+ signal(SIGHUP, SIG_IGN);
+#endif
+#if defined(WIN32)
+ child_pid = spawnl(_P_NOWAIT, SHELL_PATH, SHELL_PATH, "/c", (char *)cmd, NULL);
+ return(child_pid);
+#elif defined(OS2)
+ /* For OS/2 we need to use a '/' and spawn the child rather than exec as
+ * we haven't forked */
+ child_pid = spawnl(P_NOWAIT, SHELL_PATH, SHELL_PATH, "/c", (char *)cmd, NULL);
+ return(child_pid);
+#else
+ execl (SHELL_PATH, SHELL_PATH, "-c", (char *)cmd, NULL);
+#endif
+ perror("exec");
+ fprintf(stderr, "Exec of shell for logging failed!!!\n");
+ return(child_pid);
+}
+
+
+API_EXPORT(piped_log *) ap_open_piped_log(pool *p, const char *program)
+{
+ piped_log *pl;
+ FILE *dummy;
+#ifdef TPF
+ TPF_FORK_CHILD cld;
+ cld.filename = (char *)program;
+ cld.subprocess_env = NULL;
+ cld.prog_type = FORK_NAME;
+
+ if (!ap_spawn_child (p, NULL, &cld,
+ kill_after_timeout, &dummy, NULL, NULL)){
+#else
+ if (!ap_spawn_child(p, piped_log_child, (void *)program,
+ kill_after_timeout, &dummy, NULL, NULL)) {
+#endif /* TPF */
+ perror("ap_spawn_child");
+ fprintf(stderr, "Couldn't fork child for piped log process\n");
+ exit (1);
+ }
+ pl = ap_palloc(p, sizeof (*pl));
+ pl->p = p;
+ pl->write_f = dummy;
+
+ return pl;
+}
+
+
+API_EXPORT(void) ap_close_piped_log(piped_log *pl)
+{
+ ap_pfclose(pl->p, pl->write_f);
+}
+#endif
--- /dev/null
+/* ====================================================================
+ * Copyright (c) 1995-1999 The Apache Group. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * 4. The names "Apache Server" and "Apache Group" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission. For written permission, please contact
+ * apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ * nor may "Apache" appear in their names without prior written
+ * permission of the Apache Group.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Group and was originally based
+ * on public domain software written at the National Center for
+ * Supercomputing Applications, University of Illinois, Urbana-Champaign.
+ * For more information on the Apache Group and the Apache HTTP server
+ * project, please see <http://www.apache.org/>.
+ *
+ */
+
+/*
+ * httpd.c: simple http daemon for answering WWW file requests
+ *
+ *
+ * 03-21-93 Rob McCool wrote original code (up to NCSA HTTPd 1.3)
+ *
+ * 03-06-95 blong
+ * changed server number for child-alone processes to 0 and changed name
+ * of processes
+ *
+ * 03-10-95 blong
+ * Added numerous speed hacks proposed by Robert S. Thau (rst@ai.mit.edu)
+ * including set group before fork, and call gettime before to fork
+ * to set up libraries.
+ *
+ * 04-14-95 rst / rh
+ * Brandon's code snarfed from NCSA 1.4, but tinkered to work with the
+ * Apache server, and also to have child processes do accept() directly.
+ *
+ * April-July '95 rst
+ * Extensive rework for Apache.
+ */
+
+#ifndef SHARED_CORE_BOOTSTRAP
+#ifndef SHARED_CORE_TIESTATIC
+
+#ifdef SHARED_CORE
+#define REALMAIN ap_main
+int ap_main(int argc, char *argv[]);
+#else
+#define REALMAIN main
+#endif
+
+#define CORE_PRIVATE
+
+#include "httpd.h"
+#include "http_main.h"
+#include "http_log.h"
+#include "http_config.h" /* for read_config */
+#include "http_protocol.h" /* for read_request */
+#include "http_request.h" /* for process_request */
+#include "http_conf_globals.h"
+#include "http_core.h" /* for get_remote_host */
+#include "http_vhost.h"
+#include "util_script.h" /* to force util_script.c linking */
+#include "util_uri.h"
+#include "scoreboard.h"
+#include "multithread.h"
+#include <sys/stat.h>
+#ifdef USE_SHMGET_SCOREBOARD
+#include <sys/types.h>
+#include <sys/ipc.h>
+#include <sys/shm.h>
+#endif
+#ifdef SecureWare
+#include <sys/security.h>
+#include <sys/audit.h>
+#include <prot.h>
+#endif
+#ifdef WIN32
+#include "../os/win32/getopt.h"
+#elif !defined(BEOS) && !defined(TPF)
+#include <netinet/tcp.h>
+#endif
+
+#ifdef HAVE_BSTRING_H
+#include <bstring.h> /* for IRIX, FD_SET calls bzero() */
+#endif
+
+#ifdef MULTITHREAD
+/* special debug stuff -- PCS */
+
+/* Set this non-zero if you are prepared to put up with more than one log entry per second */
+#define SEVERELY_VERBOSE 0
+
+ /* APD1() to APD5() are macros to help us debug. They can either
+ * log to the screen or the error_log file. In release builds, these
+ * macros do nothing. In debug builds, they send messages at priority
+ * "debug" to the error log file, or if DEBUG_TO_CONSOLE is defined,
+ * to the console.
+ */
+
+# ifdef _DEBUG
+# ifndef DEBUG_TO_CONSOLE
+# define APD1(a) ap_log_error(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,server_conf,a)
+# define APD2(a,b) ap_log_error(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,server_conf,a,b)
+# define APD3(a,b,c) ap_log_error(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,server_conf,a,b,c)
+# define APD4(a,b,c,d) ap_log_error(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,server_conf,a,b,c,d)
+# define APD5(a,b,c,d,e) ap_log_error(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,server_conf,a,b,c,d,e)
+# else
+# define APD1(a) printf("%s\n",a)
+# define APD2(a,b) do { printf(a,b);putchar('\n'); } while(0);
+# define APD3(a,b,c) do { printf(a,b,c);putchar('\n'); } while(0);
+# define APD4(a,b,c,d) do { printf(a,b,c,d);putchar('\n'); } while(0);
+# define APD5(a,b,c,d,e) do { printf(a,b,c,d,e);putchar('\n'); } while(0);
+# endif
+# else /* !_DEBUG */
+# define APD1(a)
+# define APD2(a,b)
+# define APD3(a,b,c)
+# define APD4(a,b,c,d)
+# define APD5(a,b,c,d,e)
+# endif /* _DEBUG */
+#endif /* MULTITHREAD */
+
+/* This next function is never used. It is here to ensure that if we
+ * make all the modules into shared libraries that core httpd still
+ * includes the full Apache API. Without this function the objects in
+ * main/util_script.c would not be linked into a minimal httpd.
+ * And the extra prototype is to make gcc -Wmissing-prototypes quiet.
+ */
+extern void ap_force_library_loading(void);
+void ap_force_library_loading(void) {
+ ap_add_cgi_vars(NULL);
+}
+
+#include "explain.h"
+
+#if !defined(max)
+#define max(a,b) (a > b ? a : b)
+#endif
+
+#ifdef WIN32
+#include "../os/win32/service.h"
+#include "../os/win32/registry.h"
+#define DEFAULTSERVICENAME "Apache"
+#define PATHSEPARATOR '\\'
+#else
+#define PATHSEPARATOR '/'
+#endif
+
+
+#ifdef MINT
+long _stksize = 32768;
+#endif
+
+#ifdef USE_OS2_SCOREBOARD
+ /* Add MMAP style functionality to OS/2 */
+#define INCL_DOSMEMMGR
+#define INCL_DOSEXCEPTIONS
+#define INCL_DOSSEMAPHORES
+#include <os2.h>
+#include <umalloc.h>
+#include <stdio.h>
+caddr_t create_shared_heap(const char *, size_t);
+caddr_t get_shared_heap(const char *);
+#endif
+
+DEF_Explain
+
+/* Defining GPROF when compiling uses the moncontrol() function to
+ * disable gprof profiling in the parent, and enable it only for
+ * request processing in children (or in one_process mode). It's
+ * absolutely required to get useful gprof results under linux
+ * because the profile itimers and such are disabled across a
+ * fork(). It's probably useful elsewhere as well.
+ */
+#ifdef GPROF
+extern void moncontrol(int);
+#define MONCONTROL(x) moncontrol(x)
+#else
+#define MONCONTROL(x)
+#endif
+
+#ifndef MULTITHREAD
+/* this just need to be anything non-NULL */
+void *ap_dummy_mutex = &ap_dummy_mutex;
+#endif
+
+/*
+ * Actual definitions of config globals... here because this is
+ * for the most part the only code that acts on 'em. (Hmmm... mod_main.c?)
+ */
+
+int ap_standalone=0;
+int ap_configtestonly=0;
+int ap_docrootcheck=1;
+uid_t ap_user_id=0;
+char *ap_user_name=NULL;
+gid_t ap_group_id=0;
+#ifdef MULTIPLE_GROUPS
+gid_t group_id_list[NGROUPS_MAX];
+#endif
+int ap_max_requests_per_child=0;
+int ap_threads_per_child=0;
+int ap_excess_requests_per_child=0;
+char *ap_pid_fname=NULL;
+char *ap_scoreboard_fname=NULL;
+char *ap_lock_fname;
+char *ap_server_argv0=NULL;
+struct in_addr ap_bind_address;
+int ap_daemons_to_start=0;
+int ap_daemons_min_free=0;
+int ap_daemons_max_free=0;
+int ap_daemons_limit=0;
+time_t ap_restart_time=0;
+int ap_suexec_enabled = 0;
+int ap_listenbacklog;
+int ap_dump_settings = 0;
+API_VAR_EXPORT int ap_extended_status = 0;
+
+/*
+ * The max child slot ever assigned, preserved across restarts. Necessary
+ * to deal with MaxClients changes across SIGUSR1 restarts. We use this
+ * value to optimize routines that have to scan the entire scoreboard.
+ */
+static int max_daemons_limit = -1;
+
+/*
+ * During config time, listeners is treated as a NULL-terminated list.
+ * child_main previously would start at the beginning of the list each time
+ * through the loop, so a socket early on in the list could easily starve out
+ * sockets later on in the list. The solution is to start at the listener
+ * after the last one processed. But to do that fast/easily in child_main it's
+ * way more convenient for listeners to be a ring that loops back on itself.
+ * The routine setup_listeners() is called after config time to both open up
+ * the sockets and to turn the NULL-terminated list into a ring that loops back
+ * on itself.
+ *
+ * head_listener is used by each child to keep track of what they consider
+ * to be the "start" of the ring. It is also set by make_child to ensure
+ * that new children also don't starve any sockets.
+ *
+ * Note that listeners != NULL is ensured by read_config().
+ */
+listen_rec *ap_listeners;
+static listen_rec *head_listener;
+
+API_VAR_EXPORT char ap_server_root[MAX_STRING_LEN]="";
+char ap_server_confname[MAX_STRING_LEN]="";
+char ap_coredump_dir[MAX_STRING_LEN];
+
+array_header *ap_server_pre_read_config;
+array_header *ap_server_post_read_config;
+array_header *ap_server_config_defines;
+
+/* *Non*-shared http_main globals... */
+
+static server_rec *server_conf;
+static JMP_BUF APACHE_TLS jmpbuffer;
+static int sd;
+static fd_set listenfds;
+static int listenmaxfd;
+static pid_t pgrp;
+
+/* one_process --- debugging mode variable; can be set from the command line
+ * with the -X flag. If set, this gets you the child_main loop running
+ * in the process which originally started up (no detach, no make_child),
+ * which is a pretty nice debugging environment. (You'll get a SIGHUP
+ * early in standalone_main; just continue through. This is the server
+ * trying to kill off any child processes which it might have lying
+ * around --- Apache doesn't keep track of their pids, it just sends
+ * SIGHUP to the process group, ignoring it in the root process.
+ * Continue through and you'll be fine.).
+ */
+
+static int one_process = 0;
+
+/* set if timeouts are to be handled by the children and not by the parent.
+ * i.e. child_timeouts = !standalone || one_process.
+ */
+static int child_timeouts;
+
+#ifdef DEBUG_SIGSTOP
+int raise_sigstop_flags;
+#endif
+
+#ifndef NO_OTHER_CHILD
+/* used to maintain list of children which aren't part of the scoreboard */
+typedef struct other_child_rec other_child_rec;
+struct other_child_rec {
+ other_child_rec *next;
+ int pid;
+ void (*maintenance) (int, void *, ap_wait_t);
+ void *data;
+ int write_fd;
+};
+static other_child_rec *other_children;
+#endif
+
+static pool *pglobal; /* Global pool */
+static pool *pconf; /* Pool for config stuff */
+static pool *plog; /* Pool for error-logging files */
+static pool *ptrans; /* Pool for per-transaction stuff */
+static pool *pchild; /* Pool for httpd child stuff */
+static pool *pcommands; /* Pool for -C and -c switches */
+
+static int APACHE_TLS my_pid; /* it seems silly to call getpid all the time */
+#ifndef MULTITHREAD
+static int my_child_num;
+#endif
+
+#ifdef TPF
+int tpf_child = 0;
+char tpf_server_name[INETD_SERVNAME_LENGTH+1];
+#endif /* TPF */
+
+scoreboard *ap_scoreboard_image = NULL;
+
+/*
+ * Pieces for managing the contents of the Server response header
+ * field.
+ */
+static char *server_version = NULL;
+static int version_locked = 0;
+
+/* Global, alas, so http_core can talk to us */
+enum server_token_type ap_server_tokens = SrvTk_FULL;
+
+/*
+ * This routine is called when the pconf pool is vacuumed. It resets the
+ * server version string to a known value and [re]enables modifications
+ * (which are disabled by configuration completion).
+ */
+static void reset_version(void *dummy)
+{
+ version_locked = 0;
+ ap_server_tokens = SrvTk_FULL;
+ server_version = NULL;
+}
+
+API_EXPORT(const char *) ap_get_server_version(void)
+{
+ return (server_version ? server_version : SERVER_BASEVERSION);
+}
+
+API_EXPORT(void) ap_add_version_component(const char *component)
+{
+ if (! version_locked) {
+ /*
+ * If the version string is null, register our cleanup to reset the
+ * pointer on pool destruction. We also know that, if NULL,
+ * we are adding the original SERVER_BASEVERSION string.
+ */
+ if (server_version == NULL) {
+ ap_register_cleanup(pconf, NULL, (void (*)(void *))reset_version,
+ ap_null_cleanup);
+ server_version = ap_pstrdup(pconf, component);
+ }
+ else {
+ /*
+ * Tack the given component identifier to the end of
+ * the existing string.
+ */
+ server_version = ap_pstrcat(pconf, server_version, " ",
+ component, NULL);
+ }
+ }
+}
+
+/*
+ * This routine adds the real server base identity to the version string,
+ * and then locks out changes until the next reconfig.
+ */
+static void ap_set_version(void)
+{
+ if (ap_server_tokens == SrvTk_MIN) {
+ ap_add_version_component(SERVER_BASEVERSION);
+ }
+ else {
+ ap_add_version_component(SERVER_BASEVERSION " (" PLATFORM ")");
+ }
+ /*
+ * Lock the server_version string if we're not displaying
+ * the full set of tokens
+ */
+ if (ap_server_tokens != SrvTk_FULL) {
+ version_locked++;
+ }
+}
+
+static APACHE_TLS int volatile exit_after_unblock = 0;
+
+#ifdef GPROF
+/*
+ * change directory for gprof to plop the gmon.out file
+ * configure in httpd.conf:
+ * GprofDir logs/ -> $ServerRoot/logs/gmon.out
+ * GprofDir logs/% -> $ServerRoot/logs/gprof.$pid/gmon.out
+ */
+static void chdir_for_gprof(void)
+{
+ core_server_config *sconf =
+ ap_get_module_config(server_conf->module_config, &core_module);
+ char *dir = sconf->gprof_dir;
+
+ if(dir) {
+ char buf[512];
+ int len = strlen(sconf->gprof_dir) - 1;
+ if(*(dir + len) == '%') {
+ dir[len] = '\0';
+ ap_snprintf(buf, sizeof(buf), "%sgprof.%d", dir, (int)getpid());
+ }
+ dir = ap_server_root_relative(pconf, buf[0] ? buf : dir);
+ if(mkdir(dir, 0755) < 0 && errno != EEXIST) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, server_conf,
+ "gprof: error creating directory %s", dir);
+ }
+ }
+ else {
+ dir = ap_server_root_relative(pconf, "logs");
+ }
+
+ chdir(dir);
+}
+#else
+#define chdir_for_gprof()
+#endif
+
+/* a clean exit from a child with proper cleanup */
+static void clean_child_exit(int code) __attribute__ ((noreturn));
+static void clean_child_exit(int code)
+{
+ if (pchild) {
+ ap_child_exit_modules(pchild, server_conf);
+ ap_destroy_pool(pchild);
+ }
+ chdir_for_gprof();
+ exit(code);
+}
+
+#if defined(USE_FCNTL_SERIALIZED_ACCEPT) || defined(USE_FLOCK_SERIALIZED_ACCEPT)
+static void expand_lock_fname(pool *p)
+{
+ /* XXXX possibly bogus cast */
+ ap_lock_fname = ap_psprintf(p, "%s.%lu",
+ ap_server_root_relative(p, ap_lock_fname), (unsigned long)getpid());
+}
+#endif
+
+#if defined (USE_USLOCK_SERIALIZED_ACCEPT)
+
+#include <ulocks.h>
+
+static ulock_t uslock = NULL;
+
+#define accept_mutex_child_init(x)
+
+static void accept_mutex_init(pool *p)
+{
+ ptrdiff_t old;
+ usptr_t *us;
+
+
+ /* default is 8, allocate enough for all the children plus the parent */
+ if ((old = usconfig(CONF_INITUSERS, HARD_SERVER_LIMIT + 1)) == -1) {
+ perror("usconfig(CONF_INITUSERS)");
+ exit(-1);
+ }
+
+ if ((old = usconfig(CONF_LOCKTYPE, US_NODEBUG)) == -1) {
+ perror("usconfig(CONF_LOCKTYPE)");
+ exit(-1);
+ }
+ if ((old = usconfig(CONF_ARENATYPE, US_SHAREDONLY)) == -1) {
+ perror("usconfig(CONF_ARENATYPE)");
+ exit(-1);
+ }
+ if ((us = usinit("/dev/zero")) == NULL) {
+ perror("usinit");
+ exit(-1);
+ }
+
+ if ((uslock = usnewlock(us)) == NULL) {
+ perror("usnewlock");
+ exit(-1);
+ }
+}
+
+static void accept_mutex_on(void)
+{
+ switch (ussetlock(uslock)) {
+ case 1:
+ /* got lock */
+ break;
+ case 0:
+ fprintf(stderr, "didn't get lock\n");
+ clean_child_exit(APEXIT_CHILDFATAL);
+ case -1:
+ perror("ussetlock");
+ clean_child_exit(APEXIT_CHILDFATAL);
+ }
+}
+
+static void accept_mutex_off(void)
+{
+ if (usunsetlock(uslock) == -1) {
+ perror("usunsetlock");
+ clean_child_exit(APEXIT_CHILDFATAL);
+ }
+}
+
+#elif defined (USE_PTHREAD_SERIALIZED_ACCEPT)
+
+/* This code probably only works on Solaris ... but it works really fast
+ * on Solaris. Note that pthread mutexes are *NOT* released when a task
+ * dies ... the task has to free it itself. So we block signals and
+ * try to be nice about releasing the mutex.
+ */
+
+#include <pthread.h>
+
+static pthread_mutex_t *accept_mutex = (void *)(caddr_t) -1;
+static int have_accept_mutex;
+static sigset_t accept_block_mask;
+static sigset_t accept_previous_mask;
+
+static void accept_mutex_child_cleanup(void *foo)
+{
+ if (accept_mutex != (void *)(caddr_t)-1
+ && have_accept_mutex) {
+ pthread_mutex_unlock(accept_mutex);
+ }
+}
+
+static void accept_mutex_child_init(pool *p)
+{
+ ap_register_cleanup(p, NULL, accept_mutex_child_cleanup, ap_null_cleanup);
+}
+
+static void accept_mutex_cleanup(void *foo)
+{
+ if (accept_mutex != (void *)(caddr_t)-1
+ && munmap((caddr_t) accept_mutex, sizeof(*accept_mutex))) {
+ perror("munmap");
+ }
+ accept_mutex = (void *)(caddr_t)-1;
+}
+
+static void accept_mutex_init(pool *p)
+{
+ pthread_mutexattr_t mattr;
+ int fd;
+
+ fd = open("/dev/zero", O_RDWR);
+ if (fd == -1) {
+ perror("open(/dev/zero)");
+ exit(APEXIT_INIT);
+ }
+ accept_mutex = (pthread_mutex_t *) mmap((caddr_t) 0, sizeof(*accept_mutex),
+ PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ if (accept_mutex == (void *) (caddr_t) - 1) {
+ perror("mmap");
+ exit(APEXIT_INIT);
+ }
+ close(fd);
+ if ((errno = pthread_mutexattr_init(&mattr))) {
+ perror("pthread_mutexattr_init");
+ exit(APEXIT_INIT);
+ }
+ if ((errno = pthread_mutexattr_setpshared(&mattr,
+ PTHREAD_PROCESS_SHARED))) {
+ perror("pthread_mutexattr_setpshared");
+ exit(APEXIT_INIT);
+ }
+ if ((errno = pthread_mutex_init(accept_mutex, &mattr))) {
+ perror("pthread_mutex_init");
+ exit(APEXIT_INIT);
+ }
+ sigfillset(&accept_block_mask);
+ sigdelset(&accept_block_mask, SIGHUP);
+ sigdelset(&accept_block_mask, SIGTERM);
+ sigdelset(&accept_block_mask, SIGUSR1);
+ ap_register_cleanup(p, NULL, accept_mutex_cleanup, ap_null_cleanup);
+}
+
+static void accept_mutex_on(void)
+{
+ int err;
+
+ if (sigprocmask(SIG_BLOCK, &accept_block_mask, &accept_previous_mask)) {
+ perror("sigprocmask(SIG_BLOCK)");
+ clean_child_exit(APEXIT_CHILDFATAL);
+ }
+ if ((err = pthread_mutex_lock(accept_mutex))) {
+ errno = err;
+ perror("pthread_mutex_lock");
+ clean_child_exit(APEXIT_CHILDFATAL);
+ }
+ have_accept_mutex = 1;
+}
+
+static void accept_mutex_off(void)
+{
+ int err;
+
+ if ((err = pthread_mutex_unlock(accept_mutex))) {
+ errno = err;
+ perror("pthread_mutex_unlock");
+ clean_child_exit(APEXIT_CHILDFATAL);
+ }
+ /* There is a slight race condition right here... if we were to die right
+ * now, we'd do another pthread_mutex_unlock. Now, doing that would let
+ * another process into the mutex. pthread mutexes are designed to be
+ * fast, as such they don't have protection for things like testing if the
+ * thread owning a mutex is actually unlocking it (or even any way of
+ * testing who owns the mutex).
+ *
+ * If we were to unset have_accept_mutex prior to releasing the mutex
+ * then the race could result in the server unable to serve hits. Doing
+ * it this way means that the server can continue, but an additional
+ * child might be in the critical section ... at least it's still serving
+ * hits.
+ */
+ have_accept_mutex = 0;
+ if (sigprocmask(SIG_SETMASK, &accept_previous_mask, NULL)) {
+ perror("sigprocmask(SIG_SETMASK)");
+ clean_child_exit(1);
+ }
+}
+
+#elif defined (USE_SYSVSEM_SERIALIZED_ACCEPT)
+
+#include <sys/types.h>
+#include <sys/ipc.h>
+#include <sys/sem.h>
+
+#ifdef NEED_UNION_SEMUN
+/* it makes no sense, but this isn't defined on solaris */
+union semun {
+ long val;
+ struct semid_ds *buf;
+ ushort *array;
+};
+
+#endif
+
+static int sem_id = -1;
+static struct sembuf op_on;
+static struct sembuf op_off;
+
+/* We get a random semaphore ... the lame sysv semaphore interface
+ * means we have to be sure to clean this up or else we'll leak
+ * semaphores.
+ */
+static void accept_mutex_cleanup(void *foo)
+{
+ union semun ick;
+
+ if (sem_id < 0)
+ return;
+ /* this is ignored anyhow */
+ ick.val = 0;
+ semctl(sem_id, 0, IPC_RMID, ick);
+}
+
+#define accept_mutex_child_init(x)
+
+static void accept_mutex_init(pool *p)
+{
+ union semun ick;
+ struct semid_ds buf;
+
+ /* acquire the semaphore */
+ sem_id = semget(IPC_PRIVATE, 1, IPC_CREAT | 0600);
+ if (sem_id < 0) {
+ perror("semget");
+ exit(APEXIT_INIT);
+ }
+ ick.val = 1;
+ if (semctl(sem_id, 0, SETVAL, ick) < 0) {
+ perror("semctl(SETVAL)");
+ exit(APEXIT_INIT);
+ }
+ if (!getuid()) {
+ /* restrict it to use only by the appropriate user_id ... not that this
+ * stops CGIs from acquiring it and dinking around with it.
+ */
+ buf.sem_perm.uid = ap_user_id;
+ buf.sem_perm.gid = ap_group_id;
+ buf.sem_perm.mode = 0600;
+ ick.buf = &buf;
+ if (semctl(sem_id, 0, IPC_SET, ick) < 0) {
+ perror("semctl(IPC_SET)");
+ exit(APEXIT_INIT);
+ }
+ }
+ ap_register_cleanup(p, NULL, accept_mutex_cleanup, ap_null_cleanup);
+
+ /* pre-initialize these */
+ op_on.sem_num = 0;
+ op_on.sem_op = -1;
+ op_on.sem_flg = SEM_UNDO;
+ op_off.sem_num = 0;
+ op_off.sem_op = 1;
+ op_off.sem_flg = SEM_UNDO;
+}
+
+static void accept_mutex_on(void)
+{
+ while (semop(sem_id, &op_on, 1) < 0) {
+ if (errno != EINTR) {
+ perror("accept_mutex_on");
+ clean_child_exit(APEXIT_CHILDFATAL);
+ }
+ }
+}
+
+static void accept_mutex_off(void)
+{
+ while (semop(sem_id, &op_off, 1) < 0) {
+ if (errno != EINTR) {
+ perror("accept_mutex_off");
+ clean_child_exit(APEXIT_CHILDFATAL);
+ }
+ }
+}
+
+#elif defined(USE_FCNTL_SERIALIZED_ACCEPT)
+static struct flock lock_it;
+static struct flock unlock_it;
+
+static int lock_fd = -1;
+
+#define accept_mutex_child_init(x)
+
+/*
+ * Initialize mutex lock.
+ * Must be safe to call this on a restart.
+ */
+static void accept_mutex_init(pool *p)
+{
+
+ lock_it.l_whence = SEEK_SET; /* from current point */
+ lock_it.l_start = 0; /* -"- */
+ lock_it.l_len = 0; /* until end of file */
+ lock_it.l_type = F_WRLCK; /* set exclusive/write lock */
+ lock_it.l_pid = 0; /* pid not actually interesting */
+ unlock_it.l_whence = SEEK_SET; /* from current point */
+ unlock_it.l_start = 0; /* -"- */
+ unlock_it.l_len = 0; /* until end of file */
+ unlock_it.l_type = F_UNLCK; /* set exclusive/write lock */
+ unlock_it.l_pid = 0; /* pid not actually interesting */
+
+ expand_lock_fname(p);
+ lock_fd = ap_popenf(p, ap_lock_fname, O_CREAT | O_WRONLY | O_EXCL, 0644);
+ if (lock_fd == -1) {
+ perror("open");
+ fprintf(stderr, "Cannot open lock file: %s\n", ap_lock_fname);
+ exit(APEXIT_INIT);
+ }
+ unlink(ap_lock_fname);
+}
+
+static void accept_mutex_on(void)
+{
+ int ret;
+
+ while ((ret = fcntl(lock_fd, F_SETLKW, &lock_it)) < 0 && errno == EINTR) {
+ /* nop */
+ }
+
+ if (ret < 0) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, server_conf,
+ "fcntl: F_SETLKW: Error getting accept lock, exiting! "
+ "Perhaps you need to use the LockFile directive to place "
+ "your lock file on a local disk!");
+ clean_child_exit(APEXIT_CHILDFATAL);
+ }
+}
+
+static void accept_mutex_off(void)
+{
+ int ret;
+
+ while ((ret = fcntl(lock_fd, F_SETLKW, &unlock_it)) < 0 && errno == EINTR) {
+ /* nop */
+ }
+ if (ret < 0) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, server_conf,
+ "fcntl: F_SETLKW: Error freeing accept lock, exiting! "
+ "Perhaps you need to use the LockFile directive to place "
+ "your lock file on a local disk!");
+ clean_child_exit(APEXIT_CHILDFATAL);
+ }
+}
+
+#elif defined(USE_FLOCK_SERIALIZED_ACCEPT)
+
+static int lock_fd = -1;
+
+static void accept_mutex_cleanup(void *foo)
+{
+ unlink(ap_lock_fname);
+}
+
+/*
+ * Initialize mutex lock.
+ * Done by each child at it's birth
+ */
+static void accept_mutex_child_init(pool *p)
+{
+
+ lock_fd = ap_popenf(p, ap_lock_fname, O_WRONLY, 0600);
+ if (lock_fd == -1) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, server_conf,
+ "Child cannot open lock file: %s", ap_lock_fname);
+ clean_child_exit(APEXIT_CHILDINIT);
+ }
+}
+
+/*
+ * Initialize mutex lock.
+ * Must be safe to call this on a restart.
+ */
+static void accept_mutex_init(pool *p)
+{
+ expand_lock_fname(p);
+ unlink(ap_lock_fname);
+ lock_fd = ap_popenf(p, ap_lock_fname, O_CREAT | O_WRONLY | O_EXCL, 0600);
+ if (lock_fd == -1) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, server_conf,
+ "Parent cannot open lock file: %s", ap_lock_fname);
+ exit(APEXIT_INIT);
+ }
+ ap_register_cleanup(p, NULL, accept_mutex_cleanup, ap_null_cleanup);
+}
+
+static void accept_mutex_on(void)
+{
+ int ret;
+
+ while ((ret = flock(lock_fd, LOCK_EX)) < 0 && errno == EINTR)
+ continue;
+
+ if (ret < 0) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, server_conf,
+ "flock: LOCK_EX: Error getting accept lock. Exiting!");
+ clean_child_exit(APEXIT_CHILDFATAL);
+ }
+}
+
+static void accept_mutex_off(void)
+{
+ if (flock(lock_fd, LOCK_UN) < 0) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, server_conf,
+ "flock: LOCK_UN: Error freeing accept lock. Exiting!");
+ clean_child_exit(APEXIT_CHILDFATAL);
+ }
+}
+
+#elif defined(USE_OS2SEM_SERIALIZED_ACCEPT)
+
+static HMTX lock_sem = -1;
+
+static void accept_mutex_cleanup(void *foo)
+{
+ DosReleaseMutexSem(lock_sem);
+ DosCloseMutexSem(lock_sem);
+}
+
+/*
+ * Initialize mutex lock.
+ * Done by each child at it's birth
+ */
+static void accept_mutex_child_init(pool *p)
+{
+ int rc = DosOpenMutexSem(NULL, &lock_sem);
+
+ if (rc != 0) {
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_EMERG, server_conf,
+ "Child cannot open lock semaphore, rc=%d", rc);
+ clean_child_exit(APEXIT_CHILDINIT);
+ } else {
+ ap_register_cleanup(p, NULL, accept_mutex_cleanup, ap_null_cleanup);
+ }
+}
+
+/*
+ * Initialize mutex lock.
+ * Must be safe to call this on a restart.
+ */
+static void accept_mutex_init(pool *p)
+{
+ int rc = DosCreateMutexSem(NULL, &lock_sem, DC_SEM_SHARED, FALSE);
+
+ if (rc != 0) {
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_EMERG, server_conf,
+ "Parent cannot create lock semaphore, rc=%d", rc);
+ exit(APEXIT_INIT);
+ }
+
+ ap_register_cleanup(p, NULL, accept_mutex_cleanup, ap_null_cleanup);
+}
+
+static void accept_mutex_on(void)
+{
+ int rc = DosRequestMutexSem(lock_sem, SEM_INDEFINITE_WAIT);
+
+ if (rc != 0) {
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_EMERG, server_conf,
+ "OS2SEM: Error %d getting accept lock. Exiting!", rc);
+ clean_child_exit(APEXIT_CHILDFATAL);
+ }
+}
+
+static void accept_mutex_off(void)
+{
+ int rc = DosReleaseMutexSem(lock_sem);
+
+ if (rc != 0) {
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_EMERG, server_conf,
+ "OS2SEM: Error %d freeing accept lock. Exiting!", rc);
+ clean_child_exit(APEXIT_CHILDFATAL);
+ }
+}
+
+#elif defined(USE_TPF_CORE_SERIALIZED_ACCEPT)
+
+static int tpf_core_held;
+
+static void accept_mutex_cleanup(void *foo)
+{
+ if(tpf_core_held)
+ coruc(RESOURCE_KEY);
+}
+
+#define accept_mutex_init(x)
+
+static void accept_mutex_child_init(pool *p)
+{
+ ap_register_cleanup(p, NULL, accept_mutex_cleanup, ap_null_cleanup);
+ tpf_core_held = 0;
+}
+
+static void accept_mutex_on(void)
+{
+ corhc(RESOURCE_KEY);
+ tpf_core_held = 1;
+ ap_check_signals();
+}
+
+static void accept_mutex_off(void)
+{
+ coruc(RESOURCE_KEY);
+ tpf_core_held = 0;
+ ap_check_signals();
+}
+
+#else
+/* Default --- no serialization. Other methods *could* go here,
+ * as #elifs...
+ */
+#if !defined(MULTITHREAD)
+/* Multithreaded systems don't complete between processes for
+ * the sockets. */
+#define NO_SERIALIZED_ACCEPT
+#define accept_mutex_child_init(x)
+#define accept_mutex_init(x)
+#define accept_mutex_on()
+#define accept_mutex_off()
+#endif
+#endif
+
+/* On some architectures it's safe to do unserialized accept()s in the single
+ * Listen case. But it's never safe to do it in the case where there's
+ * multiple Listen statements. Define SINGLE_LISTEN_UNSERIALIZED_ACCEPT
+ * when it's safe in the single Listen case.
+ */
+#ifdef SINGLE_LISTEN_UNSERIALIZED_ACCEPT
+#define SAFE_ACCEPT(stmt) do {if(ap_listeners->next != ap_listeners) {stmt;}} while(0)
+#else
+#define SAFE_ACCEPT(stmt) do {stmt;} while(0)
+#endif
+
+static void usage(char *bin)
+{
+ char pad[MAX_STRING_LEN];
+ unsigned i;
+
+ for (i = 0; i < strlen(bin); i++)
+ pad[i] = ' ';
+ pad[i] = '\0';
+#ifdef SHARED_CORE
+ fprintf(stderr, "Usage: %s [-R directory] [-D name] [-d directory] [-f file]\n", bin);
+#else
+ fprintf(stderr, "Usage: %s [-D name] [-d directory] [-f file]\n", bin);
+#endif
+ fprintf(stderr, " %s [-C \"directive\"] [-c \"directive\"]\n", pad);
+ fprintf(stderr, " %s [-v] [-V] [-h] [-l] [-L] [-S] [-t] [-T]\n", pad);
+#ifdef WIN32
+ fprintf(stderr, " %s [-n service] [-k signal] [-i] [-u]\n", pad);
+#endif
+ fprintf(stderr, "Options:\n");
+#ifdef SHARED_CORE
+ fprintf(stderr, " -R directory : specify an alternate location for shared object files\n");
+#endif
+ fprintf(stderr, " -D name : define a name for use in <IfDefine name> directives\n");
+ fprintf(stderr, " -d directory : specify an alternate initial ServerRoot\n");
+ fprintf(stderr, " -f file : specify an alternate ServerConfigFile\n");
+ fprintf(stderr, " -C \"directive\" : process directive before reading config files\n");
+ fprintf(stderr, " -c \"directive\" : process directive after reading config files\n");
+ fprintf(stderr, " -v : show version number\n");
+ fprintf(stderr, " -V : show compile settings\n");
+ fprintf(stderr, " -h : list available command line options (this page)\n");
+ fprintf(stderr, " -l : list compiled-in modules\n");
+ fprintf(stderr, " -L : list available configuration directives\n");
+ fprintf(stderr, " -S : show parsed settings (currently only vhost settings)\n");
+ fprintf(stderr, " -t : run syntax check for config files (with docroot check)\n");
+ fprintf(stderr, " -T : run syntax check for config files (without docroot check)\n");
+#ifdef WIN32
+ fprintf(stderr, " -n name : set service name and use its ServerConfigFile\n");
+ fprintf(stderr, " -k shutdown : tell running Apache to shutdown\n");
+ fprintf(stderr, " -k restart : tell running Apache to do a graceful restart\n");
+ fprintf(stderr, " -k start : tell Apache to start\n");
+ fprintf(stderr, " -i : install an Apache service\n");
+ fprintf(stderr, " -u : uninstall an Apache service\n");
+#endif
+ exit(1);
+}
+
+/*****************************************************************
+ *
+ * Timeout handling. DISTINCTLY not thread-safe, but all this stuff
+ * has to change for threads anyway. Note that this code allows only
+ * one timeout in progress at a time...
+ */
+
+static APACHE_TLS conn_rec *volatile current_conn;
+static APACHE_TLS request_rec *volatile timeout_req;
+static APACHE_TLS const char *volatile timeout_name = NULL;
+static APACHE_TLS int volatile alarms_blocked = 0;
+static APACHE_TLS int volatile alarm_pending = 0;
+
+static void timeout(int sig)
+{
+ void *dirconf;
+
+ if (alarms_blocked) {
+ alarm_pending = 1;
+ return;
+ }
+ if (exit_after_unblock) {
+ clean_child_exit(0);
+ }
+
+ if (!current_conn) {
+ ap_longjmp(jmpbuffer, 1);
+ }
+
+ if (timeout_req != NULL)
+ dirconf = timeout_req->per_dir_config;
+ else
+ dirconf = current_conn->server->lookup_defaults;
+ if (!current_conn->keptalive) {
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO,
+ current_conn->server, "[client %s] %s timed out",
+ current_conn->remote_ip,
+ timeout_name ? timeout_name : "request");
+ }
+
+ if (timeout_req) {
+ /* Someone has asked for this transaction to just be aborted
+ * if it times out...
+ */
+
+ request_rec *log_req = timeout_req;
+ request_rec *save_req = timeout_req;
+
+ /* avoid looping... if ap_log_transaction started another
+ * timer (say via rfc1413.c) we could loop...
+ */
+ timeout_req = NULL;
+
+ while (log_req->main || log_req->prev) {
+ /* Get back to original request... */
+ if (log_req->main)
+ log_req = log_req->main;
+ else
+ log_req = log_req->prev;
+ }
+
+ if (!current_conn->keptalive) {
+ /* in some cases we come here before setting the time */
+ if (log_req->request_time == 0) {
+ log_req->request_time = time(0);
+ }
+ ap_log_transaction(log_req);
+ }
+
+ ap_bsetflag(save_req->connection->client, B_EOUT, 1);
+ ap_bclose(save_req->connection->client);
+
+ if (!ap_standalone)
+ exit(0);
+
+ ap_longjmp(jmpbuffer, 1);
+ }
+ else { /* abort the connection */
+ ap_bsetflag(current_conn->client, B_EOUT, 1);
+ ap_bclose(current_conn->client);
+ current_conn->aborted = 1;
+ }
+}
+
+#ifndef TPF
+/*
+ * These two called from alloc.c to protect its critical sections...
+ * Note that they can nest (as when destroying the sub_pools of a pool
+ * which is itself being cleared); we have to support that here.
+ */
+
+API_EXPORT(void) ap_block_alarms(void)
+{
+ ++alarms_blocked;
+}
+
+API_EXPORT(void) ap_unblock_alarms(void)
+{
+ --alarms_blocked;
+ if (alarms_blocked == 0) {
+ if (exit_after_unblock) {
+ /* We have a couple race conditions to deal with here, we can't
+ * allow a timeout that comes in this small interval to allow
+ * the child to jump back to the main loop. Instead we block
+ * alarms again, and then note that exit_after_unblock is
+ * being dealt with. We choose this way to solve this so that
+ * the common path through unblock_alarms() is really short.
+ */
+ ++alarms_blocked;
+ exit_after_unblock = 0;
+ clean_child_exit(0);
+ }
+ if (alarm_pending) {
+ alarm_pending = 0;
+ timeout(0);
+ }
+ }
+}
+#endif /* TPF */
+
+static APACHE_TLS void (*volatile alarm_fn) (int) = NULL;
+#ifdef WIN32
+static APACHE_TLS unsigned int alarm_expiry_time = 0;
+#endif /* WIN32 */
+
+#ifndef WIN32
+static void alrm_handler(int sig)
+{
+ if (alarm_fn) {
+ (*alarm_fn) (sig);
+ }
+}
+#endif
+
+unsigned int ap_set_callback_and_alarm(void (*fn) (int), int x)
+{
+ unsigned int old;
+
+#ifdef WIN32
+ old = alarm_expiry_time;
+ if (old)
+ old -= time(0);
+ if (x == 0) {
+ alarm_fn = NULL;
+ alarm_expiry_time = 0;
+ }
+ else {
+ alarm_fn = fn;
+ alarm_expiry_time = time(NULL) + x;
+ }
+#else
+ if (alarm_fn && x && fn != alarm_fn) {
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, NULL,
+ "ap_set_callback_and_alarm: possible nested timer!");
+ }
+ alarm_fn = fn;
+#ifndef OPTIMIZE_TIMEOUTS
+ old = alarm(x);
+#else
+ if (child_timeouts) {
+ old = alarm(x);
+ }
+ else {
+ /* Just note the timeout in our scoreboard, no need to call the system.
+ * We also note that the virtual time has gone forward.
+ */
+ ap_check_signals();
+ old = ap_scoreboard_image->servers[my_child_num].timeout_len;
+ ap_scoreboard_image->servers[my_child_num].timeout_len = x;
+ ++ap_scoreboard_image->servers[my_child_num].cur_vtime;
+ }
+#endif
+#endif
+ return (old);
+}
+
+
+#ifdef WIN32
+API_EXPORT(int) ap_check_alarm(void)
+{
+ if (alarm_expiry_time) {
+ unsigned int t;
+
+ t = time(NULL);
+ if (t >= alarm_expiry_time) {
+ alarm_expiry_time = 0;
+ (*alarm_fn) (0);
+ return (-1);
+ }
+ else {
+ return (alarm_expiry_time - t);
+ }
+ }
+ else
+ return (0);
+}
+#endif /* WIN32 */
+
+
+
+/* reset_timeout (request_rec *) resets the timeout in effect,
+ * as long as it hasn't expired already.
+ */
+
+API_EXPORT(void) ap_reset_timeout(request_rec *r)
+{
+ int i;
+
+ if (timeout_name) { /* timeout has been set */
+ i = ap_set_callback_and_alarm(alarm_fn, r->server->timeout);
+ if (i == 0) /* timeout already expired, so set it back to 0 */
+ ap_set_callback_and_alarm(alarm_fn, 0);
+ }
+}
+
+
+
+
+void ap_keepalive_timeout(char *name, request_rec *r)
+{
+ unsigned int to;
+
+ timeout_req = r;
+ timeout_name = name;
+
+ if (r->connection->keptalive)
+ to = r->server->keep_alive_timeout;
+ else
+ to = r->server->timeout;
+ ap_set_callback_and_alarm(timeout, to);
+
+}
+
+API_EXPORT(void) ap_hard_timeout(char *name, request_rec *r)
+{
+ timeout_req = r;
+ timeout_name = name;
+
+ ap_set_callback_and_alarm(timeout, r->server->timeout);
+
+}
+
+API_EXPORT(void) ap_soft_timeout(char *name, request_rec *r)
+{
+ timeout_name = name;
+
+ ap_set_callback_and_alarm(timeout, r->server->timeout);
+
+}
+
+API_EXPORT(void) ap_kill_timeout(request_rec *dummy)
+{
+ ap_check_signals();
+ ap_set_callback_and_alarm(NULL, 0);
+ timeout_req = NULL;
+ timeout_name = NULL;
+}
+
+
+/*
+ * More machine-dependent networking gooo... on some systems,
+ * you've got to be *really* sure that all the packets are acknowledged
+ * before closing the connection, since the client will not be able
+ * to see the last response if their TCP buffer is flushed by a RST
+ * packet from us, which is what the server's TCP stack will send
+ * if it receives any request data after closing the connection.
+ *
+ * In an ideal world, this function would be accomplished by simply
+ * setting the socket option SO_LINGER and handling it within the
+ * server's TCP stack while the process continues on to the next request.
+ * Unfortunately, it seems that most (if not all) operating systems
+ * block the server process on close() when SO_LINGER is used.
+ * For those that don't, see USE_SO_LINGER below. For the rest,
+ * we have created a home-brew lingering_close.
+ *
+ * Many operating systems tend to block, puke, or otherwise mishandle
+ * calls to shutdown only half of the connection. You should define
+ * NO_LINGCLOSE in ap_config.h if such is the case for your system.
+ */
+#ifndef MAX_SECS_TO_LINGER
+#define MAX_SECS_TO_LINGER 30
+#endif
+
+#ifdef USE_SO_LINGER
+#define NO_LINGCLOSE /* The two lingering options are exclusive */
+
+static void sock_enable_linger(int s)
+{
+ struct linger li;
+
+ li.l_onoff = 1;
+ li.l_linger = MAX_SECS_TO_LINGER;
+
+ if (setsockopt(s, SOL_SOCKET, SO_LINGER,
+ (char *) &li, sizeof(struct linger)) < 0) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf,
+ "setsockopt: (SO_LINGER)");
+ /* not a fatal error */
+ }
+}
+
+#else
+#define sock_enable_linger(s) /* NOOP */
+#endif /* USE_SO_LINGER */
+
+#ifndef NO_LINGCLOSE
+
+/* Special version of timeout for lingering_close */
+
+static void lingerout(int sig)
+{
+ if (alarms_blocked) {
+ alarm_pending = 1;
+ return;
+ }
+
+ if (!current_conn) {
+ ap_longjmp(jmpbuffer, 1);
+ }
+ ap_bsetflag(current_conn->client, B_EOUT, 1);
+ current_conn->aborted = 1;
+}
+
+static void linger_timeout(void)
+{
+ timeout_name = "lingering close";
+
+ ap_set_callback_and_alarm(lingerout, MAX_SECS_TO_LINGER);
+}
+
+/* Since many clients will abort a connection instead of closing it,
+ * attempting to log an error message from this routine will only
+ * confuse the webmaster. There doesn't seem to be any portable way to
+ * distinguish between a dropped connection and something that might be
+ * worth logging.
+ */
+static void lingering_close(request_rec *r)
+{
+ char dummybuf[512];
+ struct timeval tv;
+ fd_set lfds;
+ int select_rv;
+ int lsd;
+
+ /* Prevent a slow-drip client from holding us here indefinitely */
+
+ linger_timeout();
+
+ /* Send any leftover data to the client, but never try to again */
+
+ if (ap_bflush(r->connection->client) == -1) {
+ ap_kill_timeout(r);
+ ap_bclose(r->connection->client);
+ return;
+ }
+ ap_bsetflag(r->connection->client, B_EOUT, 1);
+
+ /* Close our half of the connection --- send the client a FIN */
+
+ lsd = r->connection->client->fd;
+
+ if ((shutdown(lsd, 1) != 0) || r->connection->aborted) {
+ ap_kill_timeout(r);
+ ap_bclose(r->connection->client);
+ return;
+ }
+
+ /* Set up to wait for readable data on socket... */
+
+ FD_ZERO(&lfds);
+
+ /* Wait for readable data or error condition on socket;
+ * slurp up any data that arrives... We exit when we go for an
+ * interval of tv length without getting any more data, get an error
+ * from select(), get an error or EOF on a read, or the timer expires.
+ */
+
+ do {
+ /* We use a 2 second timeout because current (Feb 97) browsers
+ * fail to close a connection after the server closes it. Thus,
+ * to avoid keeping the child busy, we are only lingering long enough
+ * for a client that is actively sending data on a connection.
+ * This should be sufficient unless the connection is massively
+ * losing packets, in which case we might have missed the RST anyway.
+ * These parameters are reset on each pass, since they might be
+ * changed by select.
+ */
+ FD_SET(lsd, &lfds);
+ tv.tv_sec = 2;
+ tv.tv_usec = 0;
+
+ select_rv = ap_select(lsd + 1, &lfds, NULL, NULL, &tv);
+
+ } while ((select_rv > 0) &&
+ (read(lsd, dummybuf, sizeof dummybuf) > 0));
+
+ /* Should now have seen final ack. Safe to finally kill socket */
+
+ ap_bclose(r->connection->client);
+
+ ap_kill_timeout(r);
+}
+#endif /* ndef NO_LINGCLOSE */
+
+/*****************************************************************
+ * dealing with other children
+ */
+
+#ifndef NO_OTHER_CHILD
+API_EXPORT(void) ap_register_other_child(int pid,
+ void (*maintenance) (int reason, void *, ap_wait_t status),
+ void *data, int write_fd)
+{
+ other_child_rec *ocr;
+
+ ocr = ap_palloc(pconf, sizeof(*ocr));
+ ocr->pid = pid;
+ ocr->maintenance = maintenance;
+ ocr->data = data;
+ ocr->write_fd = write_fd;
+ ocr->next = other_children;
+ other_children = ocr;
+}
+
+/* note that since this can be called by a maintenance function while we're
+ * scanning the other_children list, all scanners should protect themself
+ * by loading ocr->next before calling any maintenance function.
+ */
+API_EXPORT(void) ap_unregister_other_child(void *data)
+{
+ other_child_rec **pocr, *nocr;
+
+ for (pocr = &other_children; *pocr; pocr = &(*pocr)->next) {
+ if ((*pocr)->data == data) {
+ nocr = (*pocr)->next;
+ (*(*pocr)->maintenance) (OC_REASON_UNREGISTER, (*pocr)->data, -1);
+ *pocr = nocr;
+ /* XXX: um, well we've just wasted some space in pconf ? */
+ return;
+ }
+ }
+}
+
+/* test to ensure that the write_fds are all still writable, otherwise
+ * invoke the maintenance functions as appropriate */
+static void probe_writable_fds(void)
+{
+ fd_set writable_fds;
+ int fd_max;
+ other_child_rec *ocr, *nocr;
+ struct timeval tv;
+ int rc;
+
+ if (other_children == NULL)
+ return;
+
+ fd_max = 0;
+ FD_ZERO(&writable_fds);
+ do {
+ for (ocr = other_children; ocr; ocr = ocr->next) {
+ if (ocr->write_fd == -1)
+ continue;
+ FD_SET(ocr->write_fd, &writable_fds);
+ if (ocr->write_fd > fd_max) {
+ fd_max = ocr->write_fd;
+ }
+ }
+ if (fd_max == 0)
+ return;
+
+ tv.tv_sec = 0;
+ tv.tv_usec = 0;
+ rc = ap_select(fd_max + 1, NULL, &writable_fds, NULL, &tv);
+ } while (rc == -1 && errno == EINTR);
+
+ if (rc == -1) {
+ /* XXX: uhh this could be really bad, we could have a bad file
+ * descriptor due to a bug in one of the maintenance routines */
+ ap_log_unixerr("probe_writable_fds", "select",
+ "could not probe writable fds", server_conf);
+ return;
+ }
+ if (rc == 0)
+ return;
+
+ for (ocr = other_children; ocr; ocr = nocr) {
+ nocr = ocr->next;
+ if (ocr->write_fd == -1)
+ continue;
+ if (FD_ISSET(ocr->write_fd, &writable_fds))
+ continue;
+ (*ocr->maintenance) (OC_REASON_UNWRITABLE, ocr->data, -1);
+ }
+}
+
+/* possibly reap an other_child, return 0 if yes, -1 if not */
+static int reap_other_child(int pid, ap_wait_t status)
+{
+ other_child_rec *ocr, *nocr;
+
+ for (ocr = other_children; ocr; ocr = nocr) {
+ nocr = ocr->next;
+ if (ocr->pid != pid)
+ continue;
+ ocr->pid = -1;
+ (*ocr->maintenance) (OC_REASON_DEATH, ocr->data, status);
+ return 0;
+ }
+ return -1;
+}
+#endif
+
+/*****************************************************************
+ *
+ * Dealing with the scoreboard... a lot of these variables are global
+ * only to avoid getting clobbered by the longjmp() that happens when
+ * a hard timeout expires...
+ *
+ * We begin with routines which deal with the file itself...
+ */
+
+#ifdef MULTITHREAD
+/*
+ * In the multithreaded mode, have multiple threads - not multiple
+ * processes that need to talk to each other. Just use a simple
+ * malloc. But let the routines that follow, think that you have
+ * shared memory (so they use memcpy etc.)
+ */
+
+static void reinit_scoreboard(pool *p)
+{
+ ap_assert(!ap_scoreboard_image);
+ ap_scoreboard_image = (scoreboard *) malloc(SCOREBOARD_SIZE);
+ if (ap_scoreboard_image == NULL) {
+ fprintf(stderr, "Ouch! Out of memory reiniting scoreboard!\n");
+ }
+ memset(ap_scoreboard_image, 0, SCOREBOARD_SIZE);
+}
+
+void cleanup_scoreboard(void)
+{
+ ap_assert(ap_scoreboard_image);
+ free(ap_scoreboard_image);
+ ap_scoreboard_image = NULL;
+}
+
+API_EXPORT(void) ap_sync_scoreboard_image(void)
+{
+}
+
+
+#else /* MULTITHREAD */
+#if defined(USE_OS2_SCOREBOARD)
+
+/* The next two routines are used to access shared memory under OS/2. */
+/* This requires EMX v09c to be installed. */
+
+caddr_t create_shared_heap(const char *name, size_t size)
+{
+ ULONG rc;
+ void *mem;
+ Heap_t h;
+
+ rc = DosAllocSharedMem(&mem, name, size,
+ PAG_COMMIT | PAG_READ | PAG_WRITE);
+ if (rc != 0)
+ return NULL;
+ h = _ucreate(mem, size, !_BLOCK_CLEAN, _HEAP_REGULAR | _HEAP_SHARED,
+ NULL, NULL);
+ if (h == NULL)
+ DosFreeMem(mem);
+ return (caddr_t) h;
+}
+
+caddr_t get_shared_heap(const char *Name)
+{
+
+ PVOID BaseAddress; /* Pointer to the base address of
+ the shared memory object */
+ ULONG AttributeFlags; /* Flags describing characteristics
+ of the shared memory object */
+ APIRET rc; /* Return code */
+
+ /* Request read and write access to */
+ /* the shared memory object */
+ AttributeFlags = PAG_WRITE | PAG_READ;
+
+ rc = DosGetNamedSharedMem(&BaseAddress, Name, AttributeFlags);
+
+ if (rc != 0) {
+ printf("DosGetNamedSharedMem error: return code = %ld", rc);
+ return 0;
+ }
+
+ return BaseAddress;
+}
+
+static void setup_shared_mem(pool *p)
+{
+ caddr_t m;
+
+ int rc;
+
+ m = (caddr_t) create_shared_heap("\\SHAREMEM\\SCOREBOARD", SCOREBOARD_SIZE);
+ if (m == 0) {
+ fprintf(stderr, "%s: Could not create OS/2 Shared memory pool.\n",
+ ap_server_argv0);
+ exit(APEXIT_INIT);
+ }
+
+ rc = _uopen((Heap_t) m);
+ if (rc != 0) {
+ fprintf(stderr,
+ "%s: Could not uopen() newly created OS/2 Shared memory pool.\n",
+ ap_server_argv0);
+ }
+ ap_scoreboard_image = (scoreboard *) m;
+ ap_scoreboard_image->global.running_generation = 0;
+}
+
+static void reopen_scoreboard(pool *p)
+{
+ caddr_t m;
+ int rc;
+
+ m = (caddr_t) get_shared_heap("\\SHAREMEM\\SCOREBOARD");
+ if (m == 0) {
+ fprintf(stderr, "%s: Could not find existing OS/2 Shared memory pool.\n",
+ ap_server_argv0);
+ exit(APEXIT_INIT);
+ }
+
+ rc = _uopen((Heap_t) m);
+ ap_scoreboard_image = (scoreboard *) m;
+}
+
+#elif defined(USE_POSIX_SCOREBOARD)
+#include <sys/mman.h>
+/*
+ * POSIX 1003.4 style
+ *
+ * Note 1:
+ * As of version 4.23A, shared memory in QNX must reside under /dev/shmem,
+ * where no subdirectories allowed.
+ *
+ * POSIX shm_open() and shm_unlink() will take care about this issue,
+ * but to avoid confusion, I suggest to redefine scoreboard file name
+ * in httpd.conf to cut "logs/" from it. With default setup actual name
+ * will be "/dev/shmem/logs.apache_status".
+ *
+ * If something went wrong and Apache did not unlinked this object upon
+ * exit, you can remove it manually, using "rm -f" command.
+ *
+ * Note 2:
+ * <sys/mman.h> in QNX defines MAP_ANON, but current implementation
+ * does NOT support BSD style anonymous mapping. So, the order of
+ * conditional compilation is important:
+ * this #ifdef section must be ABOVE the next one (BSD style).
+ *
+ * I tested this stuff and it works fine for me, but if it provides
+ * trouble for you, just comment out USE_MMAP_SCOREBOARD in QNX section
+ * of ap_config.h
+ *
+ * June 5, 1997,
+ * Igor N. Kovalenko -- infoh@mail.wplus.net
+ */
+
+static void cleanup_shared_mem(void *d)
+{
+ shm_unlink(ap_scoreboard_fname);
+}
+
+static void setup_shared_mem(pool *p)
+{
+ char buf[512];
+ caddr_t m;
+ int fd;
+
+ fd = shm_open(ap_scoreboard_fname, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
+ if (fd == -1) {
+ ap_snprintf(buf, sizeof(buf), "%s: could not open(create) scoreboard",
+ ap_server_argv0);
+ perror(buf);
+ exit(APEXIT_INIT);
+ }
+ if (ltrunc(fd, (off_t) SCOREBOARD_SIZE, SEEK_SET) == -1) {
+ ap_snprintf(buf, sizeof(buf), "%s: could not ltrunc scoreboard",
+ ap_server_argv0);
+ perror(buf);
+ shm_unlink(ap_scoreboard_fname);
+ exit(APEXIT_INIT);
+ }
+ if ((m = (caddr_t) mmap((caddr_t) 0,
+ (size_t) SCOREBOARD_SIZE, PROT_READ | PROT_WRITE,
+ MAP_SHARED, fd, (off_t) 0)) == (caddr_t) - 1) {
+ ap_snprintf(buf, sizeof(buf), "%s: cannot mmap scoreboard",
+ ap_server_argv0);
+ perror(buf);
+ shm_unlink(ap_scoreboard_fname);
+ exit(APEXIT_INIT);
+ }
+ close(fd);
+ ap_register_cleanup(p, NULL, cleanup_shared_mem, ap_null_cleanup);
+ ap_scoreboard_image = (scoreboard *) m;
+ ap_scoreboard_image->global.running_generation = 0;
+}
+
+static void reopen_scoreboard(pool *p)
+{
+}
+
+#elif defined(USE_MMAP_SCOREBOARD)
+
+static void setup_shared_mem(pool *p)
+{
+ caddr_t m;
+
+#if defined(MAP_ANON)
+/* BSD style */
+#ifdef CONVEXOS11
+ /*
+ * 9-Aug-97 - Jeff Venters (venters@convex.hp.com)
+ * ConvexOS maps address space as follows:
+ * 0x00000000 - 0x7fffffff : Kernel
+ * 0x80000000 - 0xffffffff : User
+ * Start mmapped area 1GB above start of text.
+ *
+ * Also, the length requires a pointer as the actual length is
+ * returned (rounded up to a page boundary).
+ */
+ {
+ unsigned len = SCOREBOARD_SIZE;
+
+ m = mmap((caddr_t) 0xC0000000, &len,
+ PROT_READ | PROT_WRITE, MAP_ANON | MAP_SHARED, NOFD, 0);
+ }
+#elif defined(MAP_TMPFILE)
+ {
+ char mfile[] = "/tmp/apache_shmem_XXXX";
+ int fd = mkstemp(mfile);
+ if (fd == -1) {
+ perror("open");
+ fprintf(stderr, "%s: Could not open %s\n", ap_server_argv0, mfile);
+ exit(APEXIT_INIT);
+ }
+ m = mmap((caddr_t) 0, SCOREBOARD_SIZE,
+ PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ if (m == (caddr_t) - 1) {
+ perror("mmap");
+ fprintf(stderr, "%s: Could not mmap %s\n", ap_server_argv0, mfile);
+ exit(APEXIT_INIT);
+ }
+ close(fd);
+ unlink(mfile);
+ }
+#else
+ m = mmap((caddr_t) 0, SCOREBOARD_SIZE,
+ PROT_READ | PROT_WRITE, MAP_ANON | MAP_SHARED, -1, 0);
+#endif
+ if (m == (caddr_t) - 1) {
+ perror("mmap");
+ fprintf(stderr, "%s: Could not mmap memory\n", ap_server_argv0);
+ exit(APEXIT_INIT);
+ }
+#else
+/* Sun style */
+ int fd;
+
+ fd = open("/dev/zero", O_RDWR);
+ if (fd == -1) {
+ perror("open");
+ fprintf(stderr, "%s: Could not open /dev/zero\n", ap_server_argv0);
+ exit(APEXIT_INIT);
+ }
+ m = mmap((caddr_t) 0, SCOREBOARD_SIZE,
+ PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ if (m == (caddr_t) - 1) {
+ perror("mmap");
+ fprintf(stderr, "%s: Could not mmap /dev/zero\n", ap_server_argv0);
+ exit(APEXIT_INIT);
+ }
+ close(fd);
+#endif
+ ap_scoreboard_image = (scoreboard *) m;
+ ap_scoreboard_image->global.running_generation = 0;
+}
+
+static void reopen_scoreboard(pool *p)
+{
+}
+
+#elif defined(USE_SHMGET_SCOREBOARD)
+static key_t shmkey = IPC_PRIVATE;
+static int shmid = -1;
+
+static void setup_shared_mem(pool *p)
+{
+ struct shmid_ds shmbuf;
+#ifdef MOVEBREAK
+ char *obrk;
+#endif
+
+ if ((shmid = shmget(shmkey, SCOREBOARD_SIZE, IPC_CREAT | SHM_R | SHM_W)) == -1) {
+#ifdef LINUX
+ if (errno == ENOSYS) {
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_EMERG, server_conf,
+ "Your kernel was built without CONFIG_SYSVIPC\n"
+ "%s: Please consult the Apache FAQ for details",
+ ap_server_argv0);
+ }
+#endif
+ ap_log_error(APLOG_MARK, APLOG_EMERG, server_conf,
+ "could not call shmget");
+ exit(APEXIT_INIT);
+ }
+
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, server_conf,
+ "created shared memory segment #%d", shmid);
+
+#ifdef MOVEBREAK
+ /*
+ * Some SysV systems place the shared segment WAY too close
+ * to the dynamic memory break point (sbrk(0)). This severely
+ * limits the use of malloc/sbrk in the program since sbrk will
+ * refuse to move past that point.
+ *
+ * To get around this, we move the break point "way up there",
+ * attach the segment and then move break back down. Ugly
+ */
+ if ((obrk = sbrk(MOVEBREAK)) == (char *) -1) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, server_conf,
+ "sbrk() could not move break");
+ }
+#endif
+
+#define BADSHMAT ((scoreboard *)(-1))
+ if ((ap_scoreboard_image = (scoreboard *) shmat(shmid, 0, 0)) == BADSHMAT) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, server_conf, "shmat error");
+ /*
+ * We exit below, after we try to remove the segment
+ */
+ }
+ else { /* only worry about permissions if we attached the segment */
+ if (shmctl(shmid, IPC_STAT, &shmbuf) != 0) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, server_conf,
+ "shmctl() could not stat segment #%d", shmid);
+ }
+ else {
+ shmbuf.shm_perm.uid = ap_user_id;
+ shmbuf.shm_perm.gid = ap_group_id;
+ if (shmctl(shmid, IPC_SET, &shmbuf) != 0) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, server_conf,
+ "shmctl() could not set segment #%d", shmid);
+ }
+ }
+ }
+ /*
+ * We must avoid leaving segments in the kernel's
+ * (small) tables.
+ */
+ if (shmctl(shmid, IPC_RMID, NULL) != 0) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf,
+ "shmctl: IPC_RMID: could not remove shared memory segment #%d",
+ shmid);
+ }
+ if (ap_scoreboard_image == BADSHMAT) /* now bailout */
+ exit(APEXIT_INIT);
+
+#ifdef MOVEBREAK
+ if (obrk == (char *) -1)
+ return; /* nothing else to do */
+ if (sbrk(-(MOVEBREAK)) == (char *) -1) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, server_conf,
+ "sbrk() could not move break back");
+ }
+#endif
+ ap_scoreboard_image->global.running_generation = 0;
+}
+
+static void reopen_scoreboard(pool *p)
+{
+}
+
+#elif defined(USE_TPF_SCOREBOARD)
+
+static void cleanup_scoreboard_heap()
+{
+ int rv;
+ rv = rsysc(ap_scoreboard_image, SCOREBOARD_FRAMES, SCOREBOARD_NAME);
+ if(rv == RSYSC_ERROR) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, server_conf,
+ "rsysc() could not release scoreboard system heap");
+ }
+}
+
+static void setup_shared_mem(pool *p)
+{
+ cinfc(CINFC_WRITE, CINFC_CMMCTK2);
+ ap_scoreboard_image = (scoreboard *) gsysc(SCOREBOARD_FRAMES, SCOREBOARD_NAME);
+
+ if (!ap_scoreboard_image) {
+ fprintf(stderr, "httpd: Could not create scoreboard system heap storage.\n");
+ exit(APEXIT_INIT);
+ }
+
+ ap_register_cleanup(p, NULL, cleanup_scoreboard_heap, ap_null_cleanup);
+ ap_scoreboard_image->global.running_generation = 0;
+}
+
+static void reopen_scoreboard(pool *p)
+{
+ cinfc(CINFC_WRITE, CINFC_CMMCTK2);
+}
+
+#else
+#define SCOREBOARD_FILE
+static scoreboard _scoreboard_image;
+static int scoreboard_fd = -1;
+
+/* XXX: things are seriously screwed if we ever have to do a partial
+ * read or write ... we could get a corrupted scoreboard
+ */
+static int force_write(int fd, void *buffer, int bufsz)
+{
+ int rv, orig_sz = bufsz;
+
+ do {
+ rv = write(fd, buffer, bufsz);
+ if (rv > 0) {
+ buffer = (char *) buffer + rv;
+ bufsz -= rv;
+ }
+ } while ((rv > 0 && bufsz > 0) || (rv == -1 && errno == EINTR));
+
+ return rv < 0 ? rv : orig_sz - bufsz;
+}
+
+static int force_read(int fd, void *buffer, int bufsz)
+{
+ int rv, orig_sz = bufsz;
+
+ do {
+ rv = read(fd, buffer, bufsz);
+ if (rv > 0) {
+ buffer = (char *) buffer + rv;
+ bufsz -= rv;
+ }
+ } while ((rv > 0 && bufsz > 0) || (rv == -1 && errno == EINTR));
+
+ return rv < 0 ? rv : orig_sz - bufsz;
+}
+
+static void cleanup_scoreboard_file(void *foo)
+{
+ unlink(ap_scoreboard_fname);
+}
+
+void reopen_scoreboard(pool *p)
+{
+ if (scoreboard_fd != -1)
+ ap_pclosef(p, scoreboard_fd);
+
+#ifdef TPF
+ ap_scoreboard_fname = ap_server_root_relative(p, ap_scoreboard_fname);
+#endif /* TPF */
+ scoreboard_fd = ap_popenf(p, ap_scoreboard_fname, O_CREAT | O_BINARY | O_RDWR, 0666);
+ if (scoreboard_fd == -1) {
+ perror(ap_scoreboard_fname);
+ fprintf(stderr, "Cannot open scoreboard file:\n");
+ clean_child_exit(1);
+ }
+}
+#endif
+
+/* Called by parent process */
+static void reinit_scoreboard(pool *p)
+{
+ int running_gen = 0;
+ if (ap_scoreboard_image)
+ running_gen = ap_scoreboard_image->global.running_generation;
+
+#ifndef SCOREBOARD_FILE
+ if (ap_scoreboard_image == NULL) {
+ setup_shared_mem(p);
+ }
+ memset(ap_scoreboard_image, 0, SCOREBOARD_SIZE);
+ ap_scoreboard_image->global.running_generation = running_gen;
+#else
+ ap_scoreboard_image = &_scoreboard_image;
+ ap_scoreboard_fname = ap_server_root_relative(p, ap_scoreboard_fname);
+
+ scoreboard_fd = ap_popenf(p, ap_scoreboard_fname, O_CREAT | O_BINARY | O_RDWR, 0644);
+ if (scoreboard_fd == -1) {
+ perror(ap_scoreboard_fname);
+ fprintf(stderr, "Cannot open scoreboard file:\n");
+ exit(APEXIT_INIT);
+ }
+ ap_register_cleanup(p, NULL, cleanup_scoreboard_file, ap_null_cleanup);
+
+ memset((char *) ap_scoreboard_image, 0, sizeof(*ap_scoreboard_image));
+ ap_scoreboard_image->global.running_generation = running_gen;
+ force_write(scoreboard_fd, ap_scoreboard_image, sizeof(*ap_scoreboard_image));
+#endif
+}
+
+/* Routines called to deal with the scoreboard image
+ * --- note that we do *not* need write locks, since update_child_status
+ * only updates a *single* record in place, and only one process writes to
+ * a given scoreboard slot at a time (either the child process owning that
+ * slot, or the parent, noting that the child has died).
+ *
+ * As a final note --- setting the score entry to getpid() is always safe,
+ * since when the parent is writing an entry, it's only noting SERVER_DEAD
+ * anyway.
+ */
+
+ap_inline void ap_sync_scoreboard_image(void)
+{
+#ifdef SCOREBOARD_FILE
+ lseek(scoreboard_fd, 0L, 0);
+ force_read(scoreboard_fd, ap_scoreboard_image, sizeof(*ap_scoreboard_image));
+#endif
+}
+
+#endif /* MULTITHREAD */
+
+API_EXPORT(int) ap_exists_scoreboard_image(void)
+{
+ return (ap_scoreboard_image ? 1 : 0);
+}
+
+static ap_inline void put_scoreboard_info(int child_num,
+ short_score *new_score_rec)
+{
+#ifdef SCOREBOARD_FILE
+ lseek(scoreboard_fd, (long) child_num * sizeof(short_score), 0);
+ force_write(scoreboard_fd, new_score_rec, sizeof(short_score));
+#endif
+}
+
+/* a clean exit from the parent with proper cleanup */
+static void clean_parent_exit(int code) __attribute__((noreturn));
+static void clean_parent_exit(int code)
+{
+ /* Clear the pool - including any registered cleanups */
+ ap_destroy_pool(pglobal);
+ exit(code);
+}
+
+int ap_update_child_status(int child_num, int status, request_rec *r)
+{
+ int old_status;
+ short_score *ss;
+
+ if (child_num < 0)
+ return -1;
+
+ ap_check_signals();
+
+ ap_sync_scoreboard_image();
+ ss = &ap_scoreboard_image->servers[child_num];
+ old_status = ss->status;
+ ss->status = status;
+#ifdef OPTIMIZE_TIMEOUTS
+ ++ss->cur_vtime;
+#endif
+
+ if (ap_extended_status) {
+#ifndef OPTIMIZE_TIMEOUTS
+ ss->last_used = time(NULL);
+#endif
+ if (status == SERVER_READY || status == SERVER_DEAD) {
+ /*
+ * Reset individual counters
+ */
+ if (status == SERVER_DEAD) {
+ ss->my_access_count = 0L;
+ ss->my_bytes_served = 0L;
+ }
+ ss->conn_count = (unsigned short) 0;
+ ss->conn_bytes = (unsigned long) 0;
+ }
+ if (r) {
+ conn_rec *c = r->connection;
+ ap_cpystrn(ss->client, ap_get_remote_host(c, r->per_dir_config,
+ REMOTE_NOLOOKUP), sizeof(ss->client));
+ if (r->the_request == NULL) {
+ ap_cpystrn(ss->request, "NULL", sizeof(ss->request));
+ } else if (r->parsed_uri.password == NULL) {
+ ap_cpystrn(ss->request, r->the_request, sizeof(ss->request));
+ } else {
+ /* Don't reveal the password in the server-status view */
+ ap_cpystrn(ss->request, ap_pstrcat(r->pool, r->method, " ",
+ ap_unparse_uri_components(r->pool, &r->parsed_uri, UNP_OMITPASSWORD),
+ r->assbackwards ? NULL : " ", r->protocol, NULL),
+ sizeof(ss->request));
+ }
+ ss->vhostrec = r->server;
+ }
+ }
+ if (status == SERVER_STARTING && r == NULL) {
+ /* clean up the slot's vhostrec pointer (maybe re-used)
+ * and mark the slot as belonging to a new generation.
+ */
+ ss->vhostrec = NULL;
+ ap_scoreboard_image->parent[child_num].generation = ap_my_generation;
+#ifdef SCOREBOARD_FILE
+ lseek(scoreboard_fd, XtOffsetOf(scoreboard, parent[child_num]), 0);
+ force_write(scoreboard_fd, &ap_scoreboard_image->parent[child_num],
+ sizeof(parent_score));
+#endif
+ }
+ put_scoreboard_info(child_num, ss);
+
+ return old_status;
+}
+
+static void update_scoreboard_global(void)
+{
+#ifdef SCOREBOARD_FILE
+ lseek(scoreboard_fd,
+ (char *) &ap_scoreboard_image->global -(char *) ap_scoreboard_image, 0);
+ force_write(scoreboard_fd, &ap_scoreboard_image->global,
+ sizeof ap_scoreboard_image->global);
+#endif
+}
+
+void ap_time_process_request(int child_num, int status)
+{
+ short_score *ss;
+#if defined(NO_GETTIMEOFDAY) && !defined(NO_TIMES)
+ struct tms tms_blk;
+#endif
+
+ if (child_num < 0)
+ return;
+
+ ap_sync_scoreboard_image();
+ ss = &ap_scoreboard_image->servers[child_num];
+
+ if (status == START_PREQUEST) {
+#if defined(NO_GETTIMEOFDAY)
+#ifndef NO_TIMES
+ if ((ss->start_time = times(&tms_blk)) == -1)
+#endif /* NO_TIMES */
+ ss->start_time = (clock_t) 0;
+#else
+ if (gettimeofday(&ss->start_time, (struct timezone *) 0) < 0)
+ ss->start_time.tv_sec =
+ ss->start_time.tv_usec = 0L;
+#endif
+ }
+ else if (status == STOP_PREQUEST) {
+#if defined(NO_GETTIMEOFDAY)
+#ifndef NO_TIMES
+ if ((ss->stop_time = times(&tms_blk)) == -1)
+#endif
+ ss->stop_time = ss->start_time = (clock_t) 0;
+#else
+ if (gettimeofday(&ss->stop_time, (struct timezone *) 0) < 0)
+ ss->stop_time.tv_sec =
+ ss->stop_time.tv_usec =
+ ss->start_time.tv_sec =
+ ss->start_time.tv_usec = 0L;
+#endif
+
+ }
+
+ put_scoreboard_info(child_num, ss);
+}
+
+static void increment_counts(int child_num, request_rec *r)
+{
+ long int bs = 0;
+ short_score *ss;
+
+ ap_sync_scoreboard_image();
+ ss = &ap_scoreboard_image->servers[child_num];
+
+ if (r->sent_bodyct)
+ ap_bgetopt(r->connection->client, BO_BYTECT, &bs);
+
+#ifndef NO_TIMES
+ times(&ss->times);
+#endif
+ ss->access_count++;
+ ss->my_access_count++;
+ ss->conn_count++;
+ ss->bytes_served += (unsigned long) bs;
+ ss->my_bytes_served += (unsigned long) bs;
+ ss->conn_bytes += (unsigned long) bs;
+
+ put_scoreboard_info(child_num, ss);
+}
+
+static int find_child_by_pid(int pid)
+{
+ int i;
+
+ for (i = 0; i < max_daemons_limit; ++i)
+ if (ap_scoreboard_image->parent[i].pid == pid)
+ return i;
+
+ return -1;
+}
+
+static void reclaim_child_processes(int terminate)
+{
+#ifndef MULTITHREAD
+ int i, status;
+ long int waittime = 1024 * 16; /* in usecs */
+ struct timeval tv;
+ int waitret, tries;
+ int not_dead_yet;
+#ifndef NO_OTHER_CHILD
+ other_child_rec *ocr, *nocr;
+#endif
+
+ ap_sync_scoreboard_image();
+
+ for (tries = terminate ? 4 : 1; tries <= 9; ++tries) {
+ /* don't want to hold up progress any more than
+ * necessary, but we need to allow children a few moments to exit.
+ * Set delay with an exponential backoff.
+ */
+ tv.tv_sec = waittime / 1000000;
+ tv.tv_usec = waittime % 1000000;
+ waittime = waittime * 4;
+ ap_select(0, NULL, NULL, NULL, &tv);
+
+ /* now see who is done */
+ not_dead_yet = 0;
+ for (i = 0; i < max_daemons_limit; ++i) {
+ int pid = ap_scoreboard_image->parent[i].pid;
+
+ if (pid == my_pid || pid == 0)
+ continue;
+
+ waitret = waitpid(pid, &status, WNOHANG);
+ if (waitret == pid || waitret == -1) {
+ ap_scoreboard_image->parent[i].pid = 0;
+ continue;
+ }
+ ++not_dead_yet;
+ switch (tries) {
+ case 1: /* 16ms */
+ case 2: /* 82ms */
+ break;
+ case 3: /* 344ms */
+ /* perhaps it missed the SIGHUP, lets try again */
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING,
+ server_conf,
+ "child process %d did not exit, sending another SIGHUP",
+ pid);
+ kill(pid, SIGHUP);
+ waittime = 1024 * 16;
+ break;
+ case 4: /* 16ms */
+ case 5: /* 82ms */
+ case 6: /* 344ms */
+ break;
+ case 7: /* 1.4sec */
+ /* ok, now it's being annoying */
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING,
+ server_conf,
+ "child process %d still did not exit, sending a SIGTERM",
+ pid);
+ kill(pid, SIGTERM);
+ break;
+ case 8: /* 6 sec */
+ /* die child scum */
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, server_conf,
+ "child process %d still did not exit, sending a SIGKILL",
+ pid);
+ kill(pid, SIGKILL);
+ break;
+ case 9: /* 14 sec */
+ /* gave it our best shot, but alas... If this really
+ * is a child we are trying to kill and it really hasn't
+ * exited, we will likely fail to bind to the port
+ * after the restart.
+ */
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, server_conf,
+ "could not make child process %d exit, "
+ "attempting to continue anyway", pid);
+ break;
+ }
+ }
+#ifndef NO_OTHER_CHILD
+ for (ocr = other_children; ocr; ocr = nocr) {
+ nocr = ocr->next;
+ if (ocr->pid == -1)
+ continue;
+
+ waitret = waitpid(ocr->pid, &status, WNOHANG);
+ if (waitret == ocr->pid) {
+ ocr->pid = -1;
+ (*ocr->maintenance) (OC_REASON_DEATH, ocr->data, status);
+ }
+ else if (waitret == 0) {
+ (*ocr->maintenance) (OC_REASON_RESTART, ocr->data, -1);
+ ++not_dead_yet;
+ }
+ else if (waitret == -1) {
+ /* uh what the heck? they didn't call unregister? */
+ ocr->pid = -1;
+ (*ocr->maintenance) (OC_REASON_LOST, ocr->data, -1);
+ }
+ }
+#endif
+ if (!not_dead_yet) {
+ /* nothing left to wait for */
+ break;
+ }
+ }
+#endif /* ndef MULTITHREAD */
+}
+
+
+#if defined(NEED_WAITPID)
+/*
+ Systems without a real waitpid sometimes lose a child's exit while waiting
+ for another. Search through the scoreboard for missing children.
+ */
+int reap_children(ap_wait_t *status)
+{
+ int n, pid;
+
+ for (n = 0; n < max_daemons_limit; ++n) {
+ ap_sync_scoreboard_image();
+ if (ap_scoreboard_image->servers[n].status != SERVER_DEAD &&
+ kill((pid = ap_scoreboard_image->parent[n].pid), 0) == -1) {
+ ap_update_child_status(n, SERVER_DEAD, NULL);
+ /* just mark it as having a successful exit status */
+ bzero((char *) status, sizeof(ap_wait_t));
+ return(pid);
+ }
+ }
+ return 0;
+}
+#endif
+
+/* Finally, this routine is used by the caretaker process to wait for
+ * a while...
+ */
+
+/* number of calls to wait_or_timeout between writable probes */
+#ifndef INTERVAL_OF_WRITABLE_PROBES
+#define INTERVAL_OF_WRITABLE_PROBES 10
+#endif
+static int wait_or_timeout_counter;
+
+static int wait_or_timeout(ap_wait_t *status)
+{
+#ifdef WIN32
+#define MAXWAITOBJ MAXIMUM_WAIT_OBJECTS
+ HANDLE h[MAXWAITOBJ];
+ int e[MAXWAITOBJ];
+ int round, pi, hi, rv, err;
+ for (round = 0; round <= (HARD_SERVER_LIMIT - 1) / MAXWAITOBJ + 1; round++) {
+ hi = 0;
+ for (pi = round * MAXWAITOBJ;
+ (pi < (round + 1) * MAXWAITOBJ) && (pi < HARD_SERVER_LIMIT);
+ pi++) {
+ if (ap_scoreboard_image->servers[pi].status != SERVER_DEAD) {
+ e[hi] = pi;
+ h[hi++] = (HANDLE) ap_scoreboard_image->parent[pi].pid;
+ }
+
+ }
+ if (hi > 0) {
+ rv = WaitForMultipleObjects(hi, h, FALSE, 10000);
+ if (rv == -1)
+ err = GetLastError();
+ if ((WAIT_OBJECT_0 <= (unsigned int) rv) && ((unsigned int) rv < (WAIT_OBJECT_0 + hi)))
+ return (ap_scoreboard_image->parent[e[rv - WAIT_OBJECT_0]].pid);
+ else if ((WAIT_ABANDONED_0 <= (unsigned int) rv) && ((unsigned int) rv < (WAIT_ABANDONED_0 + hi)))
+ return (ap_scoreboard_image->parent[e[rv - WAIT_ABANDONED_0]].pid);
+
+ }
+ }
+ return (-1);
+
+#else /* WIN32 */
+ struct timeval tv;
+ int ret;
+
+ ++wait_or_timeout_counter;
+ if (wait_or_timeout_counter == INTERVAL_OF_WRITABLE_PROBES) {
+ wait_or_timeout_counter = 0;
+#ifndef NO_OTHER_CHILD
+ probe_writable_fds();
+#endif
+ }
+ ret = waitpid(-1, status, WNOHANG);
+ if (ret == -1 && errno == EINTR) {
+ return -1;
+ }
+ if (ret > 0) {
+ return ret;
+ }
+#ifdef NEED_WAITPID
+ if ((ret = reap_children(status)) > 0) {
+ return ret;
+ }
+#endif
+ tv.tv_sec = SCOREBOARD_MAINTENANCE_INTERVAL / 1000000;
+ tv.tv_usec = SCOREBOARD_MAINTENANCE_INTERVAL % 1000000;
+ ap_select(0, NULL, NULL, NULL, &tv);
+ return -1;
+#endif /* WIN32 */
+}
+
+
+#if defined(NSIG)
+#define NumSIG NSIG
+#elif defined(_NSIG)
+#define NumSIG _NSIG
+#elif defined(__NSIG)
+#define NumSIG __NSIG
+#else
+#define NumSIG 32 /* for 1998's unixes, this is still a good assumption */
+#endif
+
+#ifdef SYS_SIGLIST /* platform has sys_siglist[] */
+#define INIT_SIGLIST() /*nothing*/
+#else /* platform has no sys_siglist[], define our own */
+#define SYS_SIGLIST ap_sys_siglist
+#define INIT_SIGLIST() siglist_init();
+
+const char *ap_sys_siglist[NumSIG];
+
+static void siglist_init(void)
+{
+ int sig;
+
+ ap_sys_siglist[0] = "Signal 0";
+#ifdef SIGHUP
+ ap_sys_siglist[SIGHUP] = "Hangup";
+#endif
+#ifdef SIGINT
+ ap_sys_siglist[SIGINT] = "Interrupt";
+#endif
+#ifdef SIGQUIT
+ ap_sys_siglist[SIGQUIT] = "Quit";
+#endif
+#ifdef SIGILL
+ ap_sys_siglist[SIGILL] = "Illegal instruction";
+#endif
+#ifdef SIGTRAP
+ ap_sys_siglist[SIGTRAP] = "Trace/BPT trap";
+#endif
+#ifdef SIGIOT
+ ap_sys_siglist[SIGIOT] = "IOT instruction";
+#endif
+#ifdef SIGABRT
+ ap_sys_siglist[SIGABRT] = "Abort";
+#endif
+#ifdef SIGEMT
+ ap_sys_siglist[SIGEMT] = "Emulator trap";
+#endif
+#ifdef SIGFPE
+ ap_sys_siglist[SIGFPE] = "Arithmetic exception";
+#endif
+#ifdef SIGKILL
+ ap_sys_siglist[SIGKILL] = "Killed";
+#endif
+#ifdef SIGBUS
+ ap_sys_siglist[SIGBUS] = "Bus error";
+#endif
+#ifdef SIGSEGV
+ ap_sys_siglist[SIGSEGV] = "Segmentation fault";
+#endif
+#ifdef SIGSYS
+ ap_sys_siglist[SIGSYS] = "Bad system call";
+#endif
+#ifdef SIGPIPE
+ ap_sys_siglist[SIGPIPE] = "Broken pipe";
+#endif
+#ifdef SIGALRM
+ ap_sys_siglist[SIGALRM] = "Alarm clock";
+#endif
+#ifdef SIGTERM
+ ap_sys_siglist[SIGTERM] = "Terminated";
+#endif
+#ifdef SIGUSR1
+ ap_sys_siglist[SIGUSR1] = "User defined signal 1";
+#endif
+#ifdef SIGUSR2
+ ap_sys_siglist[SIGUSR2] = "User defined signal 2";
+#endif
+#ifdef SIGCLD
+ ap_sys_siglist[SIGCLD] = "Child status change";
+#endif
+#ifdef SIGCHLD
+ ap_sys_siglist[SIGCHLD] = "Child status change";
+#endif
+#ifdef SIGPWR
+ ap_sys_siglist[SIGPWR] = "Power-fail restart";
+#endif
+#ifdef SIGWINCH
+ ap_sys_siglist[SIGWINCH] = "Window changed";
+#endif
+#ifdef SIGURG
+ ap_sys_siglist[SIGURG] = "urgent socket condition";
+#endif
+#ifdef SIGPOLL
+ ap_sys_siglist[SIGPOLL] = "Pollable event occurred";
+#endif
+#ifdef SIGIO
+ ap_sys_siglist[SIGIO] = "socket I/O possible";
+#endif
+#ifdef SIGSTOP
+ ap_sys_siglist[SIGSTOP] = "Stopped (signal)";
+#endif
+#ifdef SIGTSTP
+ ap_sys_siglist[SIGTSTP] = "Stopped";
+#endif
+#ifdef SIGCONT
+ ap_sys_siglist[SIGCONT] = "Continued";
+#endif
+#ifdef SIGTTIN
+ ap_sys_siglist[SIGTTIN] = "Stopped (tty input)";
+#endif
+#ifdef SIGTTOU
+ ap_sys_siglist[SIGTTOU] = "Stopped (tty output)";
+#endif
+#ifdef SIGVTALRM
+ ap_sys_siglist[SIGVTALRM] = "virtual timer expired";
+#endif
+#ifdef SIGPROF
+ ap_sys_siglist[SIGPROF] = "profiling timer expired";
+#endif
+#ifdef SIGXCPU
+ ap_sys_siglist[SIGXCPU] = "exceeded cpu limit";
+#endif
+#ifdef SIGXFSZ
+ ap_sys_siglist[SIGXFSZ] = "exceeded file size limit";
+#endif
+ for (sig=0; sig < sizeof(ap_sys_siglist)/sizeof(ap_sys_siglist[0]); ++sig)
+ if (ap_sys_siglist[sig] == NULL)
+ ap_sys_siglist[sig] = "";
+}
+#endif /* platform has sys_siglist[] */
+
+
+/* handle all varieties of core dumping signals */
+static void sig_coredump(int sig)
+{
+ chdir(ap_coredump_dir);
+ signal(sig, SIG_DFL);
+#ifndef WIN32
+ kill(getpid(), sig);
+#else
+ raise(sig);
+#endif
+ /* At this point we've got sig blocked, because we're still inside
+ * the signal handler. When we leave the signal handler it will
+ * be unblocked, and we'll take the signal... and coredump or whatever
+ * is appropriate for this particular Unix. In addition the parent
+ * will see the real signal we received -- whereas if we called
+ * abort() here, the parent would only see SIGABRT.
+ */
+}
+
+/*****************************************************************
+ * Connection structures and accounting...
+ */
+
+static void just_die(int sig)
+{ /* SIGHUP to child process??? */
+ /* if alarms are blocked we have to wait to die otherwise we might
+ * end up with corruption in alloc.c's internal structures */
+ if (alarms_blocked) {
+ exit_after_unblock = 1;
+ }
+ else {
+ clean_child_exit(0);
+ }
+}
+
+static int volatile usr1_just_die = 1;
+static int volatile deferred_die;
+
+static void usr1_handler(int sig)
+{
+ if (usr1_just_die) {
+ just_die(sig);
+ }
+ deferred_die = 1;
+}
+
+/* volatile just in case */
+static int volatile shutdown_pending;
+static int volatile restart_pending;
+static int volatile is_graceful;
+ap_generation_t volatile ap_my_generation=0;
+
+#ifdef WIN32
+/*
+ * Signalling Apache on NT.
+ *
+ * Under Unix, Apache can be told to shutdown or restart by sending various
+ * signals (HUP, USR, TERM). On NT we don't have easy access to signals, so
+ * we use "events" instead. The parent apache process goes into a loop
+ * where it waits forever for a set of events. Two of those events are
+ * called
+ *
+ * apPID_shutdown
+ * apPID_restart
+ *
+ * (where PID is the PID of the apache parent process). When one of these
+ * is signalled, the Apache parent performs the appropriate action. The events
+ * can become signalled through internal Apache methods (e.g. if the child
+ * finds a fatal error and needs to kill its parent), via the service
+ * control manager (the control thread will signal the shutdown event when
+ * requested to stop the Apache service), from the -k Apache command line,
+ * or from any external program which finds the Apache PID from the
+ * httpd.pid file.
+ *
+ * The signal_parent() function, below, is used to signal one of these events.
+ * It can be called by any child or parent process, since it does not
+ * rely on global variables.
+ *
+ * On entry, type gives the event to signal. 0 means shutdown, 1 means
+ * graceful restart.
+ */
+
+static void signal_parent(int type)
+{
+ HANDLE e;
+ char *signal_name;
+ extern char signal_shutdown_name[];
+ extern char signal_restart_name[];
+
+ /* after updating the shutdown_pending or restart flags, we need
+ * to wake up the parent process so it can see the changes. The
+ * parent will normally be waiting for either a child process
+ * to die, or for a signal on the "spache-signal" event. So set the
+ * "apache-signal" event here.
+ */
+
+ if (one_process) {
+ return;
+ }
+
+ switch(type) {
+ case 0: signal_name = signal_shutdown_name; break;
+ case 1: signal_name = signal_restart_name; break;
+ default: return;
+ }
+
+ APD2("signal_parent signalling event \"%s\"", signal_name);
+
+ e = OpenEvent(EVENT_ALL_ACCESS, FALSE, signal_name);
+ if (!e) {
+ /* Um, problem, can't signal the parent, which means we can't
+ * signal ourselves to die. Ignore for now...
+ */
+ ap_log_error(APLOG_MARK, APLOG_EMERG|APLOG_WIN32ERROR, server_conf,
+ "OpenEvent on %s event", signal_name);
+ return;
+ }
+ if (SetEvent(e) == 0) {
+ /* Same problem as above */
+ ap_log_error(APLOG_MARK, APLOG_EMERG|APLOG_WIN32ERROR, server_conf,
+ "SetEvent on %s event", signal_name);
+ CloseHandle(e);
+ return;
+ }
+ CloseHandle(e);
+}
+#endif
+
+/*
+ * ap_start_shutdown() and ap_start_restart(), below, are a first stab at
+ * functions to initiate shutdown or restart without relying on signals.
+ * Previously this was initiated in sig_term() and restart() signal handlers,
+ * but we want to be able to start a shutdown/restart from other sources --
+ * e.g. on Win32, from the service manager. Now the service manager can
+ * call ap_start_shutdown() or ap_start_restart() as appropiate. Note that
+ * these functions can also be called by the child processes, since global
+ * variables are no longer used to pass on the required action to the parent.
+ */
+
+void ap_start_shutdown(void)
+{
+#ifndef WIN32
+ if (shutdown_pending == 1) {
+ /* Um, is this _probably_ not an error, if the user has
+ * tried to do a shutdown twice quickly, so we won't
+ * worry about reporting it.
+ */
+ return;
+ }
+ shutdown_pending = 1;
+#else
+ signal_parent(0); /* get the parent process to wake up */
+#endif
+}
+
+/* do a graceful restart if graceful == 1 */
+void ap_start_restart(int graceful)
+{
+#ifndef WIN32
+ if (restart_pending == 1) {
+ /* Probably not an error - don't bother reporting it */
+ return;
+ }
+ restart_pending = 1;
+ is_graceful = graceful;
+#else
+ signal_parent(1); /* get the parent process to wake up */
+#endif /* WIN32 */
+}
+
+static void sig_term(int sig)
+{
+ ap_start_shutdown();
+}
+
+static void restart(int sig)
+{
+#ifndef WIN32
+ ap_start_restart(sig == SIGUSR1);
+#else
+ ap_start_restart(1);
+#endif
+}
+
+static void set_signals(void)
+{
+#ifndef NO_USE_SIGACTION
+ struct sigaction sa;
+
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = 0;
+
+ if (!one_process) {
+ sa.sa_handler = sig_coredump;
+#if defined(SA_ONESHOT)
+ sa.sa_flags = SA_ONESHOT;
+#elif defined(SA_RESETHAND)
+ sa.sa_flags = SA_RESETHAND;
+#endif
+ if (sigaction(SIGSEGV, &sa, NULL) < 0)
+ ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "sigaction(SIGSEGV)");
+#ifdef SIGBUS
+ if (sigaction(SIGBUS, &sa, NULL) < 0)
+ ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "sigaction(SIGBUS)");
+#endif
+#ifdef SIGABORT
+ if (sigaction(SIGABORT, &sa, NULL) < 0)
+ ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "sigaction(SIGABORT)");
+#endif
+#ifdef SIGABRT
+ if (sigaction(SIGABRT, &sa, NULL) < 0)
+ ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "sigaction(SIGABRT)");
+#endif
+#ifdef SIGILL
+ if (sigaction(SIGILL, &sa, NULL) < 0)
+ ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "sigaction(SIGILL)");
+#endif
+ sa.sa_flags = 0;
+ }
+ sa.sa_handler = sig_term;
+ if (sigaction(SIGTERM, &sa, NULL) < 0)
+ ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "sigaction(SIGTERM)");
+#ifdef SIGINT
+ if (sigaction(SIGINT, &sa, NULL) < 0)
+ ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "sigaction(SIGINT)");
+#endif
+#ifdef SIGXCPU
+ sa.sa_handler = SIG_DFL;
+ if (sigaction(SIGXCPU, &sa, NULL) < 0)
+ ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "sigaction(SIGXCPU)");
+#endif
+#ifdef SIGXFSZ
+ sa.sa_handler = SIG_DFL;
+ if (sigaction(SIGXFSZ, &sa, NULL) < 0)
+ ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "sigaction(SIGXFSZ)");
+#endif
+#ifdef SIGPIPE
+ sa.sa_handler = SIG_IGN;
+ if (sigaction(SIGPIPE, &sa, NULL) < 0)
+ ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "sigaction(SIGPIPE)");
+#endif
+
+ /* we want to ignore HUPs and USR1 while we're busy processing one */
+ sigaddset(&sa.sa_mask, SIGHUP);
+ sigaddset(&sa.sa_mask, SIGUSR1);
+ sa.sa_handler = restart;
+ if (sigaction(SIGHUP, &sa, NULL) < 0)
+ ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "sigaction(SIGHUP)");
+ if (sigaction(SIGUSR1, &sa, NULL) < 0)
+ ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "sigaction(SIGUSR1)");
+#else
+ if (!one_process) {
+ signal(SIGSEGV, sig_coredump);
+#ifdef SIGBUS
+ signal(SIGBUS, sig_coredump);
+#endif /* SIGBUS */
+#ifdef SIGABORT
+ signal(SIGABORT, sig_coredump);
+#endif /* SIGABORT */
+#ifdef SIGABRT
+ signal(SIGABRT, sig_coredump);
+#endif /* SIGABRT */
+#ifdef SIGILL
+ signal(SIGILL, sig_coredump);
+#endif /* SIGILL */
+#ifdef SIGXCPU
+ signal(SIGXCPU, SIG_DFL);
+#endif /* SIGXCPU */
+#ifdef SIGXFSZ
+ signal(SIGXFSZ, SIG_DFL);
+#endif /* SIGXFSZ */
+ }
+
+ signal(SIGTERM, sig_term);
+#ifdef SIGHUP
+ signal(SIGHUP, restart);
+#endif /* SIGHUP */
+#ifdef SIGUSR1
+ signal(SIGUSR1, restart);
+#endif /* SIGUSR1 */
+#ifdef SIGPIPE
+ signal(SIGPIPE, SIG_IGN);
+#endif /* SIGPIPE */
+
+#endif
+}
+
+
+/*****************************************************************
+ * Here follows a long bunch of generic server bookkeeping stuff...
+ */
+
+static void detach(void)
+{
+#if !defined(WIN32)
+ int x;
+
+ chdir("/");
+#if !defined(MPE) && !defined(OS2) && !defined(TPF)
+/* Don't detach for MPE because child processes can't survive the death of
+ the parent. */
+ if ((x = fork()) > 0)
+ exit(0);
+ else if (x == -1) {
+ perror("fork");
+ fprintf(stderr, "%s: unable to fork new process\n", ap_server_argv0);
+ exit(1);
+ }
+ RAISE_SIGSTOP(DETACH);
+#endif
+#ifndef NO_SETSID
+ if ((pgrp = setsid()) == -1) {
+ perror("setsid");
+ fprintf(stderr, "%s: setsid failed\n", ap_server_argv0);
+ exit(1);
+ }
+#elif defined(NEXT) || defined(NEWSOS)
+ if (setpgrp(0, getpid()) == -1 || (pgrp = getpgrp(0)) == -1) {
+ perror("setpgrp");
+ fprintf(stderr, "%s: setpgrp or getpgrp failed\n", ap_server_argv0);
+ exit(1);
+ }
+#elif defined(OS2) || defined(TPF)
+ /* OS/2 and TPF don't support process group IDs */
+ pgrp = getpid();
+#elif defined(MPE)
+ /* MPE uses negative pid for process group */
+ pgrp = -getpid();
+#else
+ if ((pgrp = setpgrp(getpid(), 0)) == -1) {
+ perror("setpgrp");
+ fprintf(stderr, "%s: setpgrp failed\n", ap_server_argv0);
+ exit(1);
+ }
+#endif
+
+ /* close out the standard file descriptors */
+ if (freopen("/dev/null", "r", stdin) == NULL) {
+ fprintf(stderr, "%s: unable to replace stdin with /dev/null: %s\n",
+ ap_server_argv0, strerror(errno));
+ /* continue anyhow -- note we can't close out descriptor 0 because we
+ * have nothing to replace it with, and if we didn't have a descriptor
+ * 0 the next file would be created with that value ... leading to
+ * havoc.
+ */
+ }
+ if (freopen("/dev/null", "w", stdout) == NULL) {
+ fprintf(stderr, "%s: unable to replace stdout with /dev/null: %s\n",
+ ap_server_argv0, strerror(errno));
+ }
+ /* stderr is a tricky one, we really want it to be the error_log,
+ * but we haven't opened that yet. So leave it alone for now and it'll
+ * be reopened moments later.
+ */
+#endif /* ndef WIN32 */
+}
+
+/* Set group privileges.
+ *
+ * Note that we use the username as set in the config files, rather than
+ * the lookup of to uid --- the same uid may have multiple passwd entries,
+ * with different sets of groups for each.
+ */
+
+static void set_group_privs(void)
+{
+#ifndef WIN32
+ if (!geteuid()) {
+ char *name;
+
+ /* Get username if passed as a uid */
+
+ if (ap_user_name[0] == '#') {
+ struct passwd *ent;
+ uid_t uid = atoi(&ap_user_name[1]);
+
+ if ((ent = getpwuid(uid)) == NULL) {
+ ap_log_error(APLOG_MARK, APLOG_ALERT, server_conf,
+ "getpwuid: couldn't determine user name from uid %u, "
+ "you probably need to modify the User directive",
+ (unsigned)uid);
+ clean_child_exit(APEXIT_CHILDFATAL);
+ }
+
+ name = ent->pw_name;
+ }
+ else
+ name = ap_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_group_id) == -1) {
+ ap_log_error(APLOG_MARK, APLOG_ALERT, server_conf,
+ "setgid: unable to set group id to Group %u",
+ (unsigned)ap_group_id);
+ clean_child_exit(APEXIT_CHILDFATAL);
+ }
+
+ /* Reset `groups' attributes. */
+
+ if (initgroups(name, ap_group_id) == -1) {
+ ap_log_error(APLOG_MARK, APLOG_ALERT, server_conf,
+ "initgroups: unable to set groups for User %s "
+ "and Group %u", name, (unsigned)ap_group_id);
+ clean_child_exit(APEXIT_CHILDFATAL);
+ }
+#ifdef MULTIPLE_GROUPS
+ if (getgroups(NGROUPS_MAX, group_id_list) == -1) {
+ ap_log_error(APLOG_MARK, APLOG_ALERT, server_conf,
+ "getgroups: unable to get group list");
+ clean_child_exit(APEXIT_CHILDFATAL);
+ }
+#endif /* MULTIPLE_GROUPS */
+#endif /* !defined(OS2) && !defined(TPF) */
+ }
+#endif /* ndef WIN32 */
+}
+
+/* check to see if we have the 'suexec' setuid wrapper installed */
+static int init_suexec(void)
+{
+#ifndef WIN32
+ struct stat wrapper;
+
+ if ((stat(SUEXEC_BIN, &wrapper)) != 0)
+ return (ap_suexec_enabled);
+
+ if ((wrapper.st_mode & S_ISUID) && wrapper.st_uid == 0) {
+ ap_suexec_enabled = 1;
+ }
+#endif /* ndef WIN32 */
+ return (ap_suexec_enabled);
+}
+
+/*****************************************************************
+ * Connection structures and accounting...
+ */
+
+
+static conn_rec *new_connection(pool *p, server_rec *server, BUFF *inout,
+ const struct sockaddr_in *remaddr,
+ const struct sockaddr_in *saddr,
+ int child_num)
+{
+ conn_rec *conn = (conn_rec *) ap_pcalloc(p, sizeof(conn_rec));
+
+ /* Got a connection structure, so initialize what fields we can
+ * (the rest are zeroed out by pcalloc).
+ */
+
+ conn->child_num = child_num;
+
+ conn->pool = p;
+ conn->local_addr = *saddr;
+ conn->local_ip = ap_pstrdup(conn->pool,
+ inet_ntoa(conn->local_addr.sin_addr));
+ conn->server = server; /* just a guess for now */
+ ap_update_vhost_given_ip(conn);
+ conn->base_server = conn->server;
+ conn->client = inout;
+
+ conn->remote_addr = *remaddr;
+ conn->remote_ip = ap_pstrdup(conn->pool,
+ inet_ntoa(conn->remote_addr.sin_addr));
+
+ return conn;
+}
+
+#if defined(TCP_NODELAY) && !defined(MPE) && !defined(TPF)
+static void sock_disable_nagle(int s)
+{
+ /* The Nagle algorithm says that we should delay sending partial
+ * packets in hopes of getting more data. We don't want to do
+ * this; we are not telnet. There are bad interactions between
+ * persistent connections and Nagle's algorithm that have very severe
+ * performance penalties. (Failing to disable Nagle is not much of a
+ * problem with simple HTTP.)
+ *
+ * In spite of these problems, failure here is not a shooting offense.
+ */
+ int just_say_no = 1;
+
+ if (setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (char *) &just_say_no,
+ sizeof(int)) < 0) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf,
+ "setsockopt: (TCP_NODELAY)");
+ }
+}
+
+#else
+#define sock_disable_nagle(s) /* NOOP */
+#endif
+
+
+static int make_sock(pool *p, const struct sockaddr_in *server)
+{
+ int s;
+ int one = 1;
+ char addr[512];
+
+ if (server->sin_addr.s_addr != htonl(INADDR_ANY))
+ ap_snprintf(addr, sizeof(addr), "address %s port %d",
+ inet_ntoa(server->sin_addr), ntohs(server->sin_port));
+ else
+ ap_snprintf(addr, sizeof(addr), "port %d", ntohs(server->sin_port));
+
+ /* note that because we're about to slack we don't use psocket */
+ ap_block_alarms();
+ if ((s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
+ ap_log_error(APLOG_MARK, APLOG_CRIT, server_conf,
+ "make_sock: failed to get a socket for %s", addr);
+ ap_unblock_alarms();
+ exit(1);
+ }
+
+ /* Solaris (probably versions 2.4, 2.5, and 2.5.1 with various levels
+ * of tcp patches) has some really weird bugs where if you dup the
+ * socket now it breaks things across SIGHUP restarts. It'll either
+ * be unable to bind, or it won't respond.
+ */
+#if defined (SOLARIS2) && SOLARIS2 < 260
+#define WORKAROUND_SOLARIS_BUG
+#endif
+
+ /* PR#1282 Unixware 1.x appears to have the same problem as solaris */
+#if defined (UW) && UW < 200
+#define WORKAROUND_SOLARIS_BUG
+#endif
+
+ /* PR#1973 NCR SVR4 systems appear to have the same problem */
+#if defined (MPRAS)
+#define WORKAROUND_SOLARIS_BUG
+#endif
+
+#ifndef WORKAROUND_SOLARIS_BUG
+#ifndef BEOS /* this won't work for BeOS sockets!! */
+ s = ap_slack(s, AP_SLACK_HIGH);
+#endif
+
+ ap_note_cleanups_for_socket(p, s); /* arrange to close on exec or restart */
+#ifdef TPF
+ os_note_additional_cleanups(p, s);
+#endif /* TPF */
+#endif
+
+#ifndef MPE
+/* MPE does not support SO_REUSEADDR and SO_KEEPALIVE */
+#ifndef _OSD_POSIX
+ if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *) &one, sizeof(int)) < 0) {
+ ap_log_error(APLOG_MARK, APLOG_CRIT, server_conf,
+ "make_sock: for %s, setsockopt: (SO_REUSEADDR)", addr);
+#ifdef BEOS
+ closesocket(s);
+#else
+ close(s);
+#endif
+ ap_unblock_alarms();
+ return -1;
+ }
+#endif /*_OSD_POSIX*/
+ one = 1;
+#ifdef SO_KEEPALIVE
+ if (setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (char *) &one, sizeof(int)) < 0) {
+ ap_log_error(APLOG_MARK, APLOG_CRIT, server_conf,
+ "make_sock: for %s, setsockopt: (SO_KEEPALIVE)", addr);
+#ifdef BEOS
+ closesocket(s);
+#else
+ close(s);
+#endif
+
+ ap_unblock_alarms();
+ return -1;
+ }
+#endif
+#endif
+
+ sock_disable_nagle(s);
+ sock_enable_linger(s);
+
+ /*
+ * To send data over high bandwidth-delay connections at full
+ * speed we must force the TCP window to open wide enough to keep the
+ * pipe full. The default window size on many systems
+ * is only 4kB. Cross-country WAN connections of 100ms
+ * at 1Mb/s are not impossible for well connected sites.
+ * If we assume 100ms cross-country latency,
+ * a 4kB buffer limits throughput to 40kB/s.
+ *
+ * To avoid this problem I've added the SendBufferSize directive
+ * to allow the web master to configure send buffer size.
+ *
+ * The trade-off of larger buffers is that more kernel memory
+ * is consumed. YMMV, know your customers and your network!
+ *
+ * -John Heidemann <johnh@isi.edu> 25-Oct-96
+ *
+ * If no size is specified, use the kernel default.
+ */
+#ifndef BEOS /* BeOS does not support SO_SNDBUF */
+ if (server_conf->send_buffer_size) {
+ if (setsockopt(s, SOL_SOCKET, SO_SNDBUF,
+ (char *) &server_conf->send_buffer_size, sizeof(int)) < 0) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf,
+ "make_sock: failed to set SendBufferSize for %s, "
+ "using default", addr);
+ /* not a fatal error */
+ }
+ }
+#endif
+
+#ifdef MPE
+/* MPE requires CAP=PM and GETPRIVMODE to bind to ports less than 1024 */
+ if (ntohs(server->sin_port) < 1024)
+ GETPRIVMODE();
+#endif
+
+ if (bind(s, (struct sockaddr *) server, sizeof(struct sockaddr_in)) == -1) {
+ ap_log_error(APLOG_MARK, APLOG_CRIT, server_conf,
+ "make_sock: could not bind to %s", addr);
+#ifdef MPE
+ if (ntohs(server->sin_port) < 1024)
+ GETUSERMODE();
+#endif
+
+#ifdef BEOS
+ closesocket(s);
+#else
+ close(s);
+#endif
+ ap_unblock_alarms();
+ exit(1);
+ }
+#ifdef MPE
+ if (ntohs(server->sin_port) < 1024)
+ GETUSERMODE();
+#endif
+
+ if (listen(s, ap_listenbacklog) == -1) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, server_conf,
+ "make_sock: unable to listen for connections on %s", addr);
+#ifdef BEOS
+ closesocket(s);
+#else
+ close(s);
+#endif
+ ap_unblock_alarms();
+ exit(1);
+ }
+
+#ifdef WORKAROUND_SOLARIS_BUG
+ s = ap_slack(s, AP_SLACK_HIGH);
+
+ ap_note_cleanups_for_socket(p, s); /* arrange to close on exec or restart */
+#endif
+ ap_unblock_alarms();
+
+#ifdef CHECK_FD_SETSIZE
+ /* protect various fd_sets */
+ if (s >= FD_SETSIZE) {
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, NULL,
+ "make_sock: problem listening on %s, filedescriptor (%u) "
+ "larger than FD_SETSIZE (%u) "
+ "found, you probably need to rebuild Apache with a "
+ "larger FD_SETSIZE", addr, s, FD_SETSIZE);
+#ifdef BEOS
+ closesocket(s);
+#else
+ close(s);
+#endif
+ return -1;
+ }
+#endif
+
+ return s;
+}
+
+
+/*
+ * During a restart we keep track of the old listeners here, so that we
+ * can re-use the sockets. We have to do this because we won't be able
+ * to re-open the sockets ("Address already in use").
+ *
+ * Unlike the listeners ring, old_listeners is a NULL terminated list.
+ *
+ * copy_listeners() makes the copy, find_listener() finds an old listener
+ * and close_unused_listener() cleans up whatever wasn't used.
+ */
+static listen_rec *old_listeners;
+
+/* unfortunately copy_listeners may be called before listeners is a ring */
+static void copy_listeners(pool *p)
+{
+ listen_rec *lr;
+
+ ap_assert(old_listeners == NULL);
+ if (ap_listeners == NULL) {
+ return;
+ }
+ lr = ap_listeners;
+ do {
+ listen_rec *nr = malloc(sizeof *nr);
+ if (nr == NULL) {
+ fprintf(stderr, "Ouch! malloc failed in copy_listeners()\n");
+ exit(1);
+ }
+ *nr = *lr;
+ ap_kill_cleanups_for_socket(p, nr->fd);
+ nr->next = old_listeners;
+ ap_assert(!nr->used);
+ old_listeners = nr;
+ lr = lr->next;
+ } while (lr && lr != ap_listeners);
+}
+
+
+static int find_listener(listen_rec *lr)
+{
+ listen_rec *or;
+
+ for (or = old_listeners; or; or = or->next) {
+ if (!memcmp(&or->local_addr, &lr->local_addr, sizeof(or->local_addr))) {
+ or->used = 1;
+ return or->fd;
+ }
+ }
+ return -1;
+}
+
+
+static void close_unused_listeners(void)
+{
+ listen_rec *or, *next;
+
+ for (or = old_listeners; or; or = next) {
+ next = or->next;
+ if (!or->used)
+ closesocket(or->fd);
+ free(or);
+ }
+ old_listeners = NULL;
+}
+
+
+/* open sockets, and turn the listeners list into a singly linked ring */
+static void setup_listeners(pool *p)
+{
+ listen_rec *lr;
+ int fd;
+
+ listenmaxfd = -1;
+ FD_ZERO(&listenfds);
+ lr = ap_listeners;
+ for (;;) {
+ fd = find_listener(lr);
+ if (fd < 0) {
+ fd = make_sock(p, &lr->local_addr);
+ }
+ else {
+ ap_note_cleanups_for_socket(p, fd);
+ }
+ if (fd >= 0) {
+ FD_SET(fd, &listenfds);
+ if (fd > listenmaxfd)
+ listenmaxfd = fd;
+ }
+ lr->fd = fd;
+ if (lr->next == NULL)
+ break;
+ lr = lr->next;
+ }
+ /* turn the list into a ring */
+ lr->next = ap_listeners;
+ head_listener = ap_listeners;
+ close_unused_listeners();
+
+#ifdef NO_SERIALIZED_ACCEPT
+ /* warn them about the starvation problem if they're using multiple
+ * sockets
+ */
+ if (ap_listeners->next != ap_listeners) {
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_CRIT, NULL,
+ "You cannot use multiple Listens safely on your system, "
+ "proceeding anyway. See src/PORTING, search for "
+ "SERIALIZED_ACCEPT.");
+ }
+#endif
+}
+
+
+/*
+ * Find a listener which is ready for accept(). This advances the
+ * head_listener global.
+ */
+static ap_inline listen_rec *find_ready_listener(fd_set * main_fds)
+{
+ listen_rec *lr;
+
+ lr = head_listener;
+ do {
+ if (FD_ISSET(lr->fd, main_fds)) {
+ head_listener = lr->next;
+ return (lr);
+ }
+ lr = lr->next;
+ } while (lr != head_listener);
+ return NULL;
+}
+
+
+#ifdef WIN32
+static int s_iInitCount = 0;
+
+static int AMCSocketInitialize(void)
+{
+ int iVersionRequested;
+ WSADATA wsaData;
+ int err;
+
+ if (s_iInitCount > 0) {
+ s_iInitCount++;
+ return (0);
+ }
+ else if (s_iInitCount < 0)
+ return (s_iInitCount);
+
+ /* s_iInitCount == 0. Do the initailization */
+ iVersionRequested = MAKEWORD(1, 1);
+ err = WSAStartup((WORD) iVersionRequested, &wsaData);
+ if (err) {
+ s_iInitCount = -1;
+ return (s_iInitCount);
+ }
+ if (LOBYTE(wsaData.wVersion) != 1 ||
+ HIBYTE(wsaData.wVersion) != 1) {
+ s_iInitCount = -2;
+ WSACleanup();
+ return (s_iInitCount);
+ }
+
+ s_iInitCount++;
+ return (s_iInitCount);
+
+}
+
+
+static void AMCSocketCleanup(void)
+{
+ if (--s_iInitCount == 0)
+ WSACleanup();
+ return;
+}
+#endif
+
+static void show_compile_settings(void)
+{
+ printf("Server version: %s\n", ap_get_server_version());
+ printf("Server built: %s\n", ap_get_server_built());
+ printf("Server's Module Magic Number: %u:%u\n",
+ MODULE_MAGIC_NUMBER_MAJOR, MODULE_MAGIC_NUMBER_MINOR);
+ printf("Server compiled with....\n");
+#ifdef BIG_SECURITY_HOLE
+ printf(" -D BIG_SECURITY_HOLE\n");
+#endif
+#ifdef SECURITY_HOLE_PASS_AUTHORIZATION
+ printf(" -D SECURITY_HOLE_PASS_AUTHORIZATION\n");
+#endif
+#ifdef HAVE_MMAP
+ printf(" -D HAVE_MMAP\n");
+#endif
+#ifdef HAVE_SHMGET
+ printf(" -D HAVE_SHMGET\n");
+#endif
+#ifdef USE_MMAP_SCOREBOARD
+ printf(" -D USE_MMAP_SCOREBOARD\n");
+#endif
+#ifdef USE_SHMGET_SCOREBOARD
+ printf(" -D USE_SHMGET_SCOREBOARD\n");
+#endif
+#ifdef USE_OS2_SCOREBOARD
+ printf(" -D USE_OS2_SCOREBOARD\n");
+#endif
+#ifdef USE_POSIX_SCOREBOARD
+ printf(" -D USE_POSIX_SCOREBOARD\n");
+#endif
+#ifdef USE_MMAP_FILES
+ printf(" -D USE_MMAP_FILES\n");
+#ifdef MMAP_SEGMENT_SIZE
+ printf(" -D MMAP_SEGMENT_SIZE=%ld\n",(long)MMAP_SEGMENT_SIZE);
+#endif
+#endif /*USE_MMAP_FILES*/
+#ifdef NO_WRITEV
+ printf(" -D NO_WRITEV\n");
+#endif
+#ifdef NO_LINGCLOSE
+ printf(" -D NO_LINGCLOSE\n");
+#endif
+#ifdef USE_FCNTL_SERIALIZED_ACCEPT
+ printf(" -D USE_FCNTL_SERIALIZED_ACCEPT\n");
+#endif
+#ifdef USE_FLOCK_SERIALIZED_ACCEPT
+ printf(" -D USE_FLOCK_SERIALIZED_ACCEPT\n");
+#endif
+#ifdef USE_USLOCK_SERIALIZED_ACCEPT
+ printf(" -D USE_USLOCK_SERIALIZED_ACCEPT\n");
+#endif
+#ifdef USE_SYSVSEM_SERIALIZED_ACCEPT
+ printf(" -D USE_SYSVSEM_SERIALIZED_ACCEPT\n");
+#endif
+#ifdef USE_PTHREAD_SERIALIZED_ACCEPT
+ printf(" -D USE_PTHREAD_SERIALIZED_ACCEPT\n");
+#endif
+#ifdef SINGLE_LISTEN_UNSERIALIZED_ACCEPT
+ printf(" -D SINGLE_LISTEN_UNSERIALIZED_ACCEPT\n");
+#endif
+#ifdef NO_OTHER_CHILD
+ printf(" -D NO_OTHER_CHILD\n");
+#endif
+#ifdef NO_RELIABLE_PIPED_LOGS
+ printf(" -D NO_RELIABLE_PIPED_LOGS\n");
+#endif
+#ifdef BUFFERED_LOGS
+ printf(" -D BUFFERED_LOGS\n");
+#ifdef PIPE_BUF
+ printf(" -D PIPE_BUF=%ld\n",(long)PIPE_BUF);
+#endif
+#endif
+#ifdef MULTITHREAD
+ printf(" -D MULTITHREAD\n");
+#endif
+#ifdef CHARSET_EBCDIC
+ printf(" -D CHARSET_EBCDIC\n");
+#endif
+#ifdef NEED_HASHBANG_EMUL
+ printf(" -D NEED_HASHBANG_EMUL\n");
+#endif
+#ifdef SHARED_CORE
+ printf(" -D SHARED_CORE\n");
+#endif
+
+/* This list displays the compiled-in default paths: */
+#ifdef HTTPD_ROOT
+ printf(" -D HTTPD_ROOT=\"" HTTPD_ROOT "\"\n");
+#endif
+#ifdef SUEXEC_BIN
+ printf(" -D SUEXEC_BIN=\"" SUEXEC_BIN "\"\n");
+#endif
+#if defined(SHARED_CORE) && defined(SHARED_CORE_DIR)
+ printf(" -D SHARED_CORE_DIR=\"" SHARED_CORE_DIR "\"\n");
+#endif
+#ifdef DEFAULT_PIDLOG
+ printf(" -D DEFAULT_PIDLOG=\"" DEFAULT_PIDLOG "\"\n");
+#endif
+#ifdef DEFAULT_SCOREBOARD
+ printf(" -D DEFAULT_SCOREBOARD=\"" DEFAULT_SCOREBOARD "\"\n");
+#endif
+#ifdef DEFAULT_LOCKFILE
+ printf(" -D DEFAULT_LOCKFILE=\"" DEFAULT_LOCKFILE "\"\n");
+#endif
+#ifdef DEFAULT_XFERLOG
+ printf(" -D DEFAULT_XFERLOG=\"" DEFAULT_XFERLOG "\"\n");
+#endif
+#ifdef DEFAULT_ERRORLOG
+ printf(" -D DEFAULT_ERRORLOG=\"" DEFAULT_ERRORLOG "\"\n");
+#endif
+#ifdef TYPES_CONFIG_FILE
+ printf(" -D TYPES_CONFIG_FILE=\"" TYPES_CONFIG_FILE "\"\n");
+#endif
+#ifdef SERVER_CONFIG_FILE
+ printf(" -D SERVER_CONFIG_FILE=\"" SERVER_CONFIG_FILE "\"\n");
+#endif
+#ifdef ACCESS_CONFIG_FILE
+ printf(" -D ACCESS_CONFIG_FILE=\"" ACCESS_CONFIG_FILE "\"\n");
+#endif
+#ifdef RESOURCE_CONFIG_FILE
+ printf(" -D RESOURCE_CONFIG_FILE=\"" RESOURCE_CONFIG_FILE "\"\n");
+#endif
+}
+
+
+/* Some init code that's common between win32 and unix... well actually
+ * some of it is #ifdef'd but was duplicated before anyhow. This stuff
+ * is still a mess.
+ */
+static void common_init(void)
+{
+ INIT_SIGLIST()
+#ifdef AUX3
+ (void) set42sig();
+#endif
+
+#ifdef WIN32
+ /* Initialize the stupid sockets */
+ AMCSocketInitialize();
+#endif /* WIN32 */
+
+ pglobal = ap_init_alloc();
+ pconf = ap_make_sub_pool(pglobal);
+ plog = ap_make_sub_pool(pglobal);
+ ptrans = ap_make_sub_pool(pconf);
+
+ ap_util_init();
+ ap_util_uri_init();
+
+ pcommands = ap_make_sub_pool(NULL);
+ ap_server_pre_read_config = ap_make_array(pcommands, 1, sizeof(char *));
+ ap_server_post_read_config = ap_make_array(pcommands, 1, sizeof(char *));
+ ap_server_config_defines = ap_make_array(pcommands, 1, sizeof(char *));
+}
+
+#ifndef MULTITHREAD
+/*****************************************************************
+ * Child process main loop.
+ * The following vars are static to avoid getting clobbered by longjmp();
+ * they are really private to child_main.
+ */
+
+static int srv;
+static int csd;
+static int dupped_csd;
+static int requests_this_child;
+static fd_set main_fds;
+
+API_EXPORT(void) ap_child_terminate(request_rec *r)
+{
+ r->connection->keepalive = 0;
+ requests_this_child = ap_max_requests_per_child = 1;
+}
+
+static void child_main(int child_num_arg)
+{
+ NET_SIZE_T clen;
+ struct sockaddr sa_server;
+ struct sockaddr sa_client;
+ listen_rec *lr;
+
+ /* All of initialization is a critical section, we don't care if we're
+ * told to HUP or USR1 before we're done initializing. For example,
+ * we could be half way through child_init_modules() when a restart
+ * signal arrives, and we'd have no real way to recover gracefully
+ * and exit properly.
+ *
+ * I suppose a module could take forever to initialize, but that would
+ * be either a broken module, or a broken configuration (i.e. network
+ * problems, file locking problems, whatever). -djg
+ */
+ ap_block_alarms();
+
+ my_pid = getpid();
+ csd = -1;
+ dupped_csd = -1;
+ my_child_num = child_num_arg;
+ requests_this_child = 0;
+
+ /* Get a sub pool for global allocations in this child, so that
+ * we can have cleanups occur when the child exits.
+ */
+ pchild = ap_make_sub_pool(pconf);
+
+ /* needs to be done before we switch UIDs so we have permissions */
+ reopen_scoreboard(pchild);
+ SAFE_ACCEPT(accept_mutex_child_init(pchild));
+
+ set_group_privs();
+#ifdef MPE
+ /* Only try to switch if we're running as MANAGER.SYS */
+ if (geteuid() == 1 && ap_user_id > 1) {
+ GETPRIVMODE();
+ if (setuid(ap_user_id) == -1) {
+ GETUSERMODE();
+ ap_log_error(APLOG_MARK, APLOG_ALERT, server_conf,
+ "setuid: unable to change to uid: %d", ap_user_id);
+ exit(1);
+ }
+ GETUSERMODE();
+ }
+#else
+ /* Only try to switch if we're running as root */
+ if (!geteuid() && (
+#ifdef _OSD_POSIX
+ os_init_job_environment(server_conf, ap_user_name, one_process) != 0 ||
+#endif
+ setuid(ap_user_id) == -1)) {
+ ap_log_error(APLOG_MARK, APLOG_ALERT, server_conf,
+ "setuid: unable to change to uid: %ld", (long) ap_user_id);
+ clean_child_exit(APEXIT_CHILDFATAL);
+ }
+#endif
+
+ ap_child_init_modules(pchild, server_conf);
+
+ /* done with the initialization critical section */
+ ap_unblock_alarms();
+
+ (void) ap_update_child_status(my_child_num, SERVER_READY, (request_rec *) NULL);
+
+ /*
+ * Setup the jump buffers so that we can return here after a timeout
+ */
+ ap_setjmp(jmpbuffer);
+#ifndef OS2
+#ifdef SIGURG
+ signal(SIGURG, timeout);
+#endif
+#endif
+ signal(SIGALRM, alrm_handler);
+#ifdef TPF
+ signal(SIGHUP, just_die);
+ signal(SIGTERM, just_die);
+ signal(SIGUSR1, just_die);
+#endif /* TPF */
+
+#ifdef OS2
+/* Stop Ctrl-C/Ctrl-Break signals going to child processes */
+ {
+ unsigned long ulTimes;
+ DosSetSignalExceptionFocus(0, &ulTimes);
+ }
+#endif
+
+ while (1) {
+ BUFF *conn_io;
+ request_rec *r;
+
+ /* Prepare to receive a SIGUSR1 due to graceful restart so that
+ * we can exit cleanly. Since we're between connections right
+ * now it's the right time to exit, but we might be blocked in a
+ * system call when the graceful restart request is made. */
+ usr1_just_die = 1;
+ signal(SIGUSR1, usr1_handler);
+
+ /*
+ * (Re)initialize this child to a pre-connection state.
+ */
+
+ ap_kill_timeout(0); /* Cancel any outstanding alarms. */
+ current_conn = NULL;
+
+ ap_clear_pool(ptrans);
+
+ ap_sync_scoreboard_image();
+ if (ap_scoreboard_image->global.running_generation != ap_my_generation) {
+ clean_child_exit(0);
+ }
+
+#ifndef WIN32
+ if ((ap_max_requests_per_child > 0
+ && requests_this_child++ >= ap_max_requests_per_child)) {
+ clean_child_exit(0);
+ }
+#else
+ ++requests_this_child;
+#endif
+
+ (void) ap_update_child_status(my_child_num, SERVER_READY, (request_rec *) NULL);
+
+ /*
+ * Wait for an acceptable connection to arrive.
+ */
+
+ /* Lock around "accept", if necessary */
+ SAFE_ACCEPT(accept_mutex_on());
+
+ for (;;) {
+ if (ap_listeners->next != ap_listeners) {
+ /* more than one socket */
+ memcpy(&main_fds, &listenfds, sizeof(fd_set));
+ srv = ap_select(listenmaxfd + 1, &main_fds, NULL, NULL, NULL);
+
+ if (srv < 0 && errno != EINTR) {
+ /* Single Unix documents select as returning errnos
+ * EBADF, EINTR, and EINVAL... and in none of those
+ * cases does it make sense to continue. In fact
+ * on Linux 2.0.x we seem to end up with EFAULT
+ * occasionally, and we'd loop forever due to it.
+ */
+ ap_log_error(APLOG_MARK, APLOG_ERR, server_conf, "select: (listen)");
+ clean_child_exit(1);
+ }
+
+ if (srv <= 0)
+ continue;
+
+ lr = find_ready_listener(&main_fds);
+ if (lr == NULL)
+ continue;
+ sd = lr->fd;
+ }
+ else {
+ /* only one socket, just pretend we did the other stuff */
+ sd = ap_listeners->fd;
+ }
+
+ /* if we accept() something we don't want to die, so we have to
+ * defer the exit
+ */
+ deferred_die = 0;
+ usr1_just_die = 0;
+ for (;;) {
+ clen = sizeof(sa_client);
+ csd = ap_accept(sd, &sa_client, &clen);
+ if (csd >= 0 || errno != EINTR)
+ break;
+ if (deferred_die) {
+ /* we didn't get a socket, and we were told to die */
+ clean_child_exit(0);
+ }
+ }
+
+ if (csd >= 0)
+ break; /* We have a socket ready for reading */
+ else {
+
+ /* Our old behaviour here was to continue after accept()
+ * errors. But this leads us into lots of troubles
+ * because most of the errors are quite fatal. For
+ * example, EMFILE can be caused by slow descriptor
+ * leaks (say in a 3rd party module, or libc). It's
+ * foolish for us to continue after an EMFILE. We also
+ * seem to tickle kernel bugs on some platforms which
+ * lead to never-ending loops here. So it seems best
+ * to just exit in most cases.
+ */
+ switch (errno) {
+#ifdef EPROTO
+ /* EPROTO on certain older kernels really means
+ * ECONNABORTED, so we need to ignore it for them.
+ * See discussion in new-httpd archives nh.9701
+ * search for EPROTO.
+ *
+ * Also see nh.9603, search for EPROTO:
+ * There is potentially a bug in Solaris 2.x x<6,
+ * and other boxes that implement tcp sockets in
+ * userland (i.e. on top of STREAMS). On these
+ * systems, EPROTO can actually result in a fatal
+ * loop. See PR#981 for example. It's hard to
+ * handle both uses of EPROTO.
+ */
+ case EPROTO:
+#endif
+#ifdef ECONNABORTED
+ case ECONNABORTED:
+#endif
+ /* Linux generates the rest of these, other tcp
+ * stacks (i.e. bsd) tend to hide them behind
+ * getsockopt() interfaces. They occur when
+ * the net goes sour or the client disconnects
+ * after the three-way handshake has been done
+ * in the kernel but before userland has picked
+ * up the socket.
+ */
+#ifdef ECONNRESET
+ case ECONNRESET:
+#endif
+#ifdef ETIMEDOUT
+ case ETIMEDOUT:
+#endif
+#ifdef EHOSTUNREACH
+ case EHOSTUNREACH:
+#endif
+#ifdef ENETUNREACH
+ case ENETUNREACH:
+#endif
+ break;
+#ifdef ENETDOWN
+ case ENETDOWN:
+ /*
+ * When the network layer has been shut down, there
+ * is not much use in simply exiting: the parent
+ * would simply re-create us (and we'd fail again).
+ * Use the CHILDFATAL code to tear the server down.
+ * @@@ Martin's idea for possible improvement:
+ * A different approach would be to define
+ * a new APEXIT_NETDOWN exit code, the reception
+ * of which would make the parent shutdown all
+ * children, then idle-loop until it detected that
+ * the network is up again, and restart the children.
+ * Ben Hyde noted that temporary ENETDOWN situations
+ * occur in mobile IP.
+ */
+ ap_log_error(APLOG_MARK, APLOG_EMERG, server_conf,
+ "accept: giving up.");
+ clean_child_exit(APEXIT_CHILDFATAL);
+#endif /*ENETDOWN*/
+
+#ifdef TPF
+ case EINACT:
+ ap_log_error(APLOG_MARK, APLOG_EMERG, server_conf,
+ "offload device inactive");
+ clean_child_exit(APEXIT_CHILDFATAL);
+ break;
+ default:
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, server_conf,
+ "select/accept error (%u)", errno);
+ clean_child_exit(APEXIT_CHILDFATAL);
+#else
+ default:
+ ap_log_error(APLOG_MARK, APLOG_ERR, server_conf,
+ "accept: (client socket)");
+ clean_child_exit(1);
+#endif
+ }
+ }
+
+ /* go around again, safe to die */
+ usr1_just_die = 1;
+ if (deferred_die) {
+ /* ok maybe not, see ya later */
+ clean_child_exit(0);
+ }
+ /* or maybe we missed a signal, you never know on systems
+ * without reliable signals
+ */
+ ap_sync_scoreboard_image();
+ if (ap_scoreboard_image->global.running_generation != ap_my_generation) {
+ clean_child_exit(0);
+ }
+ }
+
+ SAFE_ACCEPT(accept_mutex_off()); /* unlock after "accept" */
+
+#ifdef TPF
+ if (csd == 0) /* 0 is invalid socket for TPF */
+ continue;
+#endif
+
+ /* We've got a socket, let's at least process one request off the
+ * socket before we accept a graceful restart request.
+ */
+ signal(SIGUSR1, SIG_IGN);
+
+ ap_note_cleanups_for_fd(ptrans, csd);
+
+ /* protect various fd_sets */
+#ifdef CHECK_FD_SETSIZE
+ if (csd >= FD_SETSIZE) {
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, NULL,
+ "[csd] filedescriptor (%u) larger than FD_SETSIZE (%u) "
+ "found, you probably need to rebuild Apache with a "
+ "larger FD_SETSIZE", csd, FD_SETSIZE);
+ continue;
+ }
+#endif
+
+ /*
+ * We now have a connection, so set it up with the appropriate
+ * socket options, file descriptors, and read/write buffers.
+ */
+
+ clen = sizeof(sa_server);
+ if (getsockname(csd, &sa_server, &clen) < 0) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, server_conf, "getsockname");
+ continue;
+ }
+
+ sock_disable_nagle(csd);
+
+ (void) ap_update_child_status(my_child_num, SERVER_BUSY_READ,
+ (request_rec *) NULL);
+
+ conn_io = ap_bcreate(ptrans, B_RDWR | B_SOCKET);
+
+#ifdef B_SFIO
+ (void) sfdisc(conn_io->sf_in, SF_POPDISC);
+ sfdisc(conn_io->sf_in, bsfio_new(conn_io->pool, conn_io));
+ sfsetbuf(conn_io->sf_in, NULL, 0);
+
+ (void) sfdisc(conn_io->sf_out, SF_POPDISC);
+ sfdisc(conn_io->sf_out, bsfio_new(conn_io->pool, conn_io));
+ sfsetbuf(conn_io->sf_out, NULL, 0);
+#endif
+
+ dupped_csd = csd;
+#if defined(NEED_DUPPED_CSD)
+ if ((dupped_csd = dup(csd)) < 0) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, server_conf,
+ "dup: couldn't duplicate csd");
+ dupped_csd = csd; /* Oh well... */
+ }
+ ap_note_cleanups_for_fd(ptrans, dupped_csd);
+
+ /* protect various fd_sets */
+#ifdef CHECK_FD_SETSIZE
+ if (dupped_csd >= FD_SETSIZE) {
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, NULL,
+ "[dupped_csd] filedescriptor (%u) larger than FD_SETSIZE (%u) "
+ "found, you probably need to rebuild Apache with a "
+ "larger FD_SETSIZE", dupped_csd, FD_SETSIZE);
+ continue;
+ }
+#endif
+#endif
+ ap_bpushfd(conn_io, csd, dupped_csd);
+
+ current_conn = new_connection(ptrans, server_conf, conn_io,
+ (struct sockaddr_in *) &sa_client,
+ (struct sockaddr_in *) &sa_server,
+ my_child_num);
+
+ /*
+ * Read and process each request found on our connection
+ * until no requests are left or we decide to close.
+ */
+
+ while ((r = ap_read_request(current_conn)) != NULL) {
+
+ /* read_request_line has already done a
+ * signal (SIGUSR1, SIG_IGN);
+ */
+
+ (void) ap_update_child_status(my_child_num, SERVER_BUSY_WRITE, r);
+
+ /* process the request if it was read without error */
+
+ if (r->status == HTTP_OK)
+ ap_process_request(r);
+
+ if(ap_extended_status)
+ increment_counts(my_child_num, r);
+
+ if (!current_conn->keepalive || current_conn->aborted)
+ break;
+
+ ap_destroy_pool(r->pool);
+ (void) ap_update_child_status(my_child_num, SERVER_BUSY_KEEPALIVE,
+ (request_rec *) NULL);
+
+ ap_sync_scoreboard_image();
+ if (ap_scoreboard_image->global.running_generation != ap_my_generation) {
+ ap_bclose(conn_io);
+ clean_child_exit(0);
+ }
+
+ /* In case we get a graceful restart while we're blocked
+ * waiting for the request.
+ *
+ * XXX: This isn't perfect, we might actually read the
+ * request and then just die without saying anything to
+ * the client. This can be fixed by using deferred_die
+ * but you have to teach buff.c about it so that it can handle
+ * the EINTR properly.
+ *
+ * In practice though browsers (have to) expect keepalive
+ * connections to close before receiving a response because
+ * of network latencies and server timeouts.
+ */
+ usr1_just_die = 1;
+ signal(SIGUSR1, usr1_handler);
+ }
+
+ /*
+ * Close the connection, being careful to send out whatever is still
+ * in our buffers. If possible, try to avoid a hard close until the
+ * client has ACKed our FIN and/or has stopped sending us data.
+ */
+
+#ifdef NO_LINGCLOSE
+ ap_bclose(conn_io); /* just close it */
+#else
+ if (r && r->connection
+ && !r->connection->aborted
+ && r->connection->client
+ && (r->connection->client->fd >= 0)) {
+
+ lingering_close(r);
+ }
+ else {
+ ap_bsetflag(conn_io, B_EOUT, 1);
+ ap_bclose(conn_io);
+ }
+#endif
+ }
+}
+
+#ifdef TPF
+static void reset_tpf_listeners(APACHE_TPF_INPUT *input_parms)
+{
+ int count;
+ listen_rec *lr;
+
+ count = 0;
+ listenmaxfd = -1;
+ FD_ZERO(&listenfds);
+ lr = ap_listeners;
+
+ for(;;) {
+ lr->fd = input_parms->listeners[count];
+ if(lr->fd >= 0) {
+ FD_SET(lr->fd, &listenfds);
+ if(lr->fd > listenmaxfd)
+ listenmaxfd = lr->fd;
+ }
+ if(lr->next == NULL)
+ break;
+ lr = lr->next;
+ count++;
+ }
+ lr->next = ap_listeners;
+ head_listener = ap_listeners;
+ close_unused_listeners();
+}
+
+#endif /* TPF */
+
+static int make_child(server_rec *s, int slot, time_t now)
+{
+ int pid;
+
+ if (slot + 1 > max_daemons_limit) {
+ max_daemons_limit = slot + 1;
+ }
+
+ if (one_process) {
+ signal(SIGHUP, just_die);
+ signal(SIGINT, just_die);
+#ifdef SIGQUIT
+ signal(SIGQUIT, SIG_DFL);
+#endif
+ signal(SIGTERM, just_die);
+ child_main(slot);
+ }
+
+ /* avoid starvation */
+ head_listener = head_listener->next;
+
+ Explain1("Starting new child in slot %d", slot);
+ (void) ap_update_child_status(slot, SERVER_STARTING, (request_rec *) NULL);
+
+
+#ifdef _OSD_POSIX
+ /* BS2000 requires a "special" version of fork() before a setuid() call */
+ if ((pid = os_fork(ap_user_name)) == -1) {
+#elif defined(TPF)
+ if ((pid = os_fork(s, slot)) == -1) {
+#else
+ if ((pid = fork()) == -1) {
+#endif
+ ap_log_error(APLOG_MARK, APLOG_ERR, s, "fork: Unable to fork new process");
+
+ /* fork didn't succeed. Fix the scoreboard or else
+ * it will say SERVER_STARTING forever and ever
+ */
+ (void) ap_update_child_status(slot, SERVER_DEAD, (request_rec *) NULL);
+
+ /* In case system resources are maxxed out, we don't want
+ Apache running away with the CPU trying to fork over and
+ over and over again. */
+ sleep(10);
+
+ return -1;
+ }
+
+ if (!pid) {
+#ifdef AIX_BIND_PROCESSOR
+/* by default AIX binds to a single processor
+ * this bit unbinds children which will then bind to another cpu
+ */
+#include <sys/processor.h>
+ int status = bindprocessor(BINDPROCESS, (int)getpid(),
+ PROCESSOR_CLASS_ANY);
+ if (status != OK) {
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, server_conf,
+ "processor unbind failed %d", status);
+ }
+#endif
+ RAISE_SIGSTOP(MAKE_CHILD);
+ MONCONTROL(1);
+ /* Disable the restart signal handlers and enable the just_die stuff.
+ * Note that since restart() just notes that a restart has been
+ * requested there's no race condition here.
+ */
+ signal(SIGHUP, just_die);
+ signal(SIGUSR1, just_die);
+ signal(SIGTERM, just_die);
+ child_main(slot);
+ }
+
+#ifdef OPTIMIZE_TIMEOUTS
+ ap_scoreboard_image->parent[slot].last_rtime = now;
+#endif
+ ap_scoreboard_image->parent[slot].pid = pid;
+#ifdef SCOREBOARD_FILE
+ lseek(scoreboard_fd, XtOffsetOf(scoreboard, parent[slot]), 0);
+ force_write(scoreboard_fd, &ap_scoreboard_image->parent[slot],
+ sizeof(parent_score));
+#endif
+
+ return 0;
+}
+
+
+/* start up a bunch of children */
+static void startup_children(int number_to_start)
+{
+ int i;
+ time_t now = time(0);
+
+ for (i = 0; number_to_start && i < ap_daemons_limit; ++i) {
+ if (ap_scoreboard_image->servers[i].status != SERVER_DEAD) {
+ continue;
+ }
+ if (make_child(server_conf, i, now) < 0) {
+ break;
+ }
+ --number_to_start;
+ }
+}
+
+
+/*
+ * idle_spawn_rate is the number of children that will be spawned on the
+ * next maintenance cycle if there aren't enough idle servers. It is
+ * doubled up to MAX_SPAWN_RATE, and reset only when a cycle goes by
+ * without the need to spawn.
+ */
+static int idle_spawn_rate = 1;
+#ifndef MAX_SPAWN_RATE
+#define MAX_SPAWN_RATE (32)
+#endif
+static int hold_off_on_exponential_spawning;
+
+static void perform_idle_server_maintenance(void)
+{
+ int i;
+ int to_kill;
+ int idle_count;
+ short_score *ss;
+ time_t now = time(0);
+ int free_length;
+ int free_slots[MAX_SPAWN_RATE];
+ int last_non_dead;
+ int total_non_dead;
+
+ /* initialize the free_list */
+ free_length = 0;
+
+ to_kill = -1;
+ idle_count = 0;
+ last_non_dead = -1;
+ total_non_dead = 0;
+
+ ap_sync_scoreboard_image();
+ for (i = 0; i < ap_daemons_limit; ++i) {
+ int status;
+
+ if (i >= max_daemons_limit && free_length == idle_spawn_rate)
+ break;
+ ss = &ap_scoreboard_image->servers[i];
+ status = ss->status;
+ if (status == SERVER_DEAD) {
+ /* try to keep children numbers as low as possible */
+ if (free_length < idle_spawn_rate) {
+ free_slots[free_length] = i;
+ ++free_length;
+ }
+ }
+ else {
+ /* We consider a starting server as idle because we started it
+ * at least a cycle ago, and if it still hasn't finished starting
+ * then we're just going to swamp things worse by forking more.
+ * So we hopefully won't need to fork more if we count it.
+ * This depends on the ordering of SERVER_READY and SERVER_STARTING.
+ */
+ if (status <= SERVER_READY) {
+ ++ idle_count;
+ /* always kill the highest numbered child if we have to...
+ * no really well thought out reason ... other than observing
+ * the server behaviour under linux where lower numbered children
+ * tend to service more hits (and hence are more likely to have
+ * their data in cpu caches).
+ */
+ to_kill = i;
+ }
+
+ ++total_non_dead;
+ last_non_dead = i;
+#ifdef OPTIMIZE_TIMEOUTS
+ if (ss->timeout_len) {
+ /* if it's a live server, with a live timeout then
+ * start checking its timeout */
+ parent_score *ps = &ap_scoreboard_image->parent[i];
+ if (ss->cur_vtime != ps->last_vtime) {
+ /* it has made progress, so update its last_rtime,
+ * last_vtime */
+ ps->last_rtime = now;
+ ps->last_vtime = ss->cur_vtime;
+ }
+ else if (ps->last_rtime + ss->timeout_len < now) {
+ /* no progress, and the timeout length has been exceeded */
+ ss->timeout_len = 0;
+ kill(ps->pid, SIGALRM);
+ }
+ }
+#endif
+ }
+ }
+ max_daemons_limit = last_non_dead + 1;
+ if (idle_count > ap_daemons_max_free) {
+ /* kill off one child... we use SIGUSR1 because that'll cause it to
+ * shut down gracefully, in case it happened to pick up a request
+ * while we were counting
+ */
+ kill(ap_scoreboard_image->parent[to_kill].pid, SIGUSR1);
+ idle_spawn_rate = 1;
+ }
+ else if (idle_count < ap_daemons_min_free) {
+ /* terminate the free list */
+ if (free_length == 0) {
+ /* only report this condition once */
+ static int reported = 0;
+
+ if (!reported) {
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, server_conf,
+ "server reached MaxClients setting, consider"
+ " raising the MaxClients setting");
+ reported = 1;
+ }
+ idle_spawn_rate = 1;
+ }
+ else {
+ if (idle_spawn_rate >= 8) {
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, server_conf,
+ "server seems busy, (you may need "
+ "to increase StartServers, or Min/MaxSpareServers), "
+ "spawning %d children, there are %d idle, and "
+ "%d total children", idle_spawn_rate,
+ idle_count, total_non_dead);
+ }
+ for (i = 0; i < free_length; ++i) {
+#ifdef TPF
+ if(make_child(server_conf, free_slots[i], now) == -1) {
+ if(free_length == 1) {
+ shutdown_pending = 1;
+ ap_log_error(APLOG_MARK, APLOG_EMERG, server_conf,
+ "No active child processes: shutting down");
+ }
+ }
+#else
+ make_child(server_conf, free_slots[i], now);
+#endif /* TPF */
+ }
+ /* the next time around we want to spawn twice as many if this
+ * wasn't good enough, but not if we've just done a graceful
+ */
+ if (hold_off_on_exponential_spawning) {
+ --hold_off_on_exponential_spawning;
+ }
+ else if (idle_spawn_rate < MAX_SPAWN_RATE) {
+ idle_spawn_rate *= 2;
+ }
+ }
+ }
+ else {
+ idle_spawn_rate = 1;
+ }
+}
+
+
+static void process_child_status(int pid, ap_wait_t status)
+{
+ /* Child died... if it died due to a fatal error,
+ * we should simply bail out.
+ */
+ if ((WIFEXITED(status)) &&
+ WEXITSTATUS(status) == APEXIT_CHILDFATAL) {
+ ap_log_error(APLOG_MARK, APLOG_ALERT|APLOG_NOERRNO, server_conf,
+ "Child %d returned a Fatal error... \n"
+ "Apache is exiting!",
+ pid);
+ exit(APEXIT_CHILDFATAL);
+ }
+ if (WIFSIGNALED(status)) {
+ switch (WTERMSIG(status)) {
+ case SIGTERM:
+ case SIGHUP:
+ case SIGUSR1:
+ case SIGKILL:
+ break;
+ default:
+#ifdef SYS_SIGLIST
+#ifdef WCOREDUMP
+ if (WCOREDUMP(status)) {
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE,
+ server_conf,
+ "child pid %d exit signal %s (%d), "
+ "possible coredump in %s",
+ pid, (WTERMSIG(status) >= NumSIG) ? "" :
+ SYS_SIGLIST[WTERMSIG(status)], WTERMSIG(status),
+ ap_coredump_dir);
+ }
+ else {
+#endif
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE,
+ server_conf,
+ "child pid %d exit signal %s (%d)", pid,
+ SYS_SIGLIST[WTERMSIG(status)], WTERMSIG(status));
+#ifdef WCOREDUMP
+ }
+#endif
+#else
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE,
+ server_conf,
+ "child pid %d exit signal %d",
+ pid, WTERMSIG(status));
+#endif
+ }
+ }
+}
+
+
+/*****************************************************************
+ * Executive routines.
+ */
+
+#ifndef STANDALONE_MAIN
+#define STANDALONE_MAIN standalone_main
+
+static void standalone_main(int argc, char **argv)
+{
+ int remaining_children_to_start;
+
+#ifdef OS2
+ printf("%s \n", ap_get_server_version());
+#endif
+
+ ap_standalone = 1;
+
+ is_graceful = 0;
+
+ if (!one_process) {
+ detach();
+ }
+ else {
+ MONCONTROL(1);
+ }
+
+ my_pid = getpid();
+
+ do {
+ copy_listeners(pconf);
+ if (!is_graceful) {
+ ap_restart_time = time(NULL);
+ }
+#ifdef SCOREBOARD_FILE
+ else if (scoreboard_fd != -1) {
+ ap_kill_cleanup(pconf, NULL, cleanup_scoreboard_file);
+ ap_kill_cleanups_for_fd(pconf, scoreboard_fd);
+ }
+#endif
+ ap_clear_pool(pconf);
+ ptrans = ap_make_sub_pool(pconf);
+
+ server_conf = ap_read_config(pconf, ptrans, ap_server_confname);
+ setup_listeners(pconf);
+ ap_clear_pool(plog);
+ ap_open_logs(server_conf, plog);
+ ap_log_pid(pconf, ap_pid_fname);
+ ap_set_version(); /* create our server_version string */
+ ap_init_modules(pconf, server_conf);
+ version_locked++; /* no more changes to server_version */
+ SAFE_ACCEPT(accept_mutex_init(pconf));
+ if (!is_graceful) {
+ reinit_scoreboard(pconf);
+ }
+#ifdef SCOREBOARD_FILE
+ else {
+ ap_scoreboard_fname = ap_server_root_relative(pconf, ap_scoreboard_fname);
+ ap_note_cleanups_for_fd(pconf, scoreboard_fd);
+ }
+#endif
+
+ set_signals();
+
+ if (ap_daemons_max_free < ap_daemons_min_free + 1) /* Don't thrash... */
+ ap_daemons_max_free = ap_daemons_min_free + 1;
+
+ /* If we're doing a graceful_restart then we're going to see a lot
+ * of children exiting immediately when we get into the main loop
+ * below (because we just sent them SIGUSR1). This happens pretty
+ * rapidly... and for each one that exits we'll start a new one until
+ * we reach at least daemons_min_free. But we may be permitted to
+ * start more than that, so we'll just keep track of how many we're
+ * supposed to start up without the 1 second penalty between each fork.
+ */
+ remaining_children_to_start = ap_daemons_to_start;
+ if (remaining_children_to_start > ap_daemons_limit) {
+ remaining_children_to_start = ap_daemons_limit;
+ }
+ if (!is_graceful) {
+ startup_children(remaining_children_to_start);
+ remaining_children_to_start = 0;
+ }
+ else {
+ /* give the system some time to recover before kicking into
+ * exponential mode */
+ hold_off_on_exponential_spawning = 10;
+ }
+
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, server_conf,
+ "%s configured -- resuming normal operations",
+ ap_get_server_version());
+ if (ap_suexec_enabled) {
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, server_conf,
+ "suEXEC mechanism enabled (wrapper: %s)", SUEXEC_BIN);
+ }
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, server_conf,
+ "Server built: %s", ap_get_server_built());
+ restart_pending = shutdown_pending = 0;
+
+ while (!restart_pending && !shutdown_pending) {
+ int child_slot;
+ ap_wait_t status;
+ int pid = wait_or_timeout(&status);
+
+ /* XXX: if it takes longer than 1 second for all our children
+ * to start up and get into IDLE state then we may spawn an
+ * extra child
+ */
+ if (pid >= 0) {
+ process_child_status(pid, status);
+ /* non-fatal death... note that it's gone in the scoreboard. */
+ ap_sync_scoreboard_image();
+ child_slot = find_child_by_pid(pid);
+ Explain2("Reaping child %d slot %d", pid, child_slot);
+ if (child_slot >= 0) {
+ (void) ap_update_child_status(child_slot, SERVER_DEAD,
+ (request_rec *) NULL);
+ if (remaining_children_to_start
+ && child_slot < ap_daemons_limit) {
+ /* we're still doing a 1-for-1 replacement of dead
+ * children with new children
+ */
+ make_child(server_conf, child_slot, time(0));
+ --remaining_children_to_start;
+ }
+#ifndef NO_OTHER_CHILD
+ }
+ else if (reap_other_child(pid, status) == 0) {
+ /* handled */
+#endif
+ }
+ else if (is_graceful) {
+ /* Great, we've probably just lost a slot in the
+ * scoreboard. Somehow we don't know about this
+ * child.
+ */
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, server_conf,
+ "long lost child came home! (pid %d)", pid);
+ }
+ /* Don't perform idle maintenance when a child dies,
+ * only do it when there's a timeout. Remember only a
+ * finite number of children can die, and it's pretty
+ * pathological for a lot to die suddenly.
+ */
+ continue;
+ }
+ else if (remaining_children_to_start) {
+ /* we hit a 1 second timeout in which none of the previous
+ * generation of children needed to be reaped... so assume
+ * they're all done, and pick up the slack if any is left.
+ */
+ startup_children(remaining_children_to_start);
+ remaining_children_to_start = 0;
+ /* In any event we really shouldn't do the code below because
+ * few of the servers we just started are in the IDLE state
+ * yet, so we'd mistakenly create an extra server.
+ */
+ continue;
+ }
+
+ perform_idle_server_maintenance();
+#ifdef TPF
+ shutdown_pending = os_check_server(tpf_server_name);
+ ap_check_signals();
+ sleep(1);
+#endif /*TPF */
+ }
+
+ if (shutdown_pending) {
+ /* Time to gracefully shut down:
+ * Kill child processes, tell them to call child_exit, etc...
+ */
+ if (ap_killpg(pgrp, SIGTERM) < 0) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "killpg SIGTERM");
+ }
+ reclaim_child_processes(1); /* Start with SIGTERM */
+
+ /* cleanup pid file on normal shutdown */
+ {
+ const char *pidfile = NULL;
+ pidfile = ap_server_root_relative (pconf, ap_pid_fname);
+ if ( pidfile != NULL && unlink(pidfile) == 0)
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO,
+ server_conf,
+ "removed PID file %s (pid=%ld)",
+ pidfile, (long)getpid());
+ }
+
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, server_conf,
+ "caught SIGTERM, shutting down");
+ clean_parent_exit(0);
+ }
+
+ /* we've been told to restart */
+ signal(SIGHUP, SIG_IGN);
+ signal(SIGUSR1, SIG_IGN);
+
+ if (one_process) {
+ /* not worth thinking about */
+ clean_parent_exit(0);
+ }
+
+ /* advance to the next generation */
+ /* XXX: we really need to make sure this new generation number isn't in
+ * use by any of the children.
+ */
+ ++ap_my_generation;
+ ap_scoreboard_image->global.running_generation = ap_my_generation;
+ update_scoreboard_global();
+
+ if (is_graceful) {
+#ifndef SCOREBOARD_FILE
+ int i;
+#endif
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, server_conf,
+ "SIGUSR1 received. Doing graceful restart");
+
+ /* kill off the idle ones */
+ if (ap_killpg(pgrp, SIGUSR1) < 0) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "killpg SIGUSR1");
+ }
+#ifndef SCOREBOARD_FILE
+ /* This is mostly for debugging... so that we know what is still
+ * gracefully dealing with existing request. But we can't really
+ * do it if we're in a SCOREBOARD_FILE because it'll cause
+ * corruption too easily.
+ */
+ ap_sync_scoreboard_image();
+ for (i = 0; i < ap_daemons_limit; ++i) {
+ if (ap_scoreboard_image->servers[i].status != SERVER_DEAD) {
+ ap_scoreboard_image->servers[i].status = SERVER_GRACEFUL;
+ }
+ }
+#endif
+ }
+ else {
+ /* Kill 'em off */
+ if (ap_killpg(pgrp, SIGHUP) < 0) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "killpg SIGHUP");
+ }
+ reclaim_child_processes(0); /* Not when just starting up */
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, server_conf,
+ "SIGHUP received. Attempting to restart");
+ }
+ } while (restart_pending);
+
+ /*add_common_vars(NULL);*/
+} /* standalone_main */
+#else
+/* prototype */
+void STANDALONE_MAIN(int argc, char **argv);
+#endif /* STANDALONE_MAIN */
+
+extern char *optarg;
+extern int optind;
+
+int REALMAIN(int argc, char *argv[])
+{
+ int c;
+ int sock_in;
+ int sock_out;
+ char *s;
+
+#ifdef SecureWare
+ if (set_auth_parameters(argc, argv) < 0)
+ perror("set_auth_parameters");
+ if (getluid() < 0)
+ if (setluid(getuid()) < 0)
+ perror("setluid");
+ if (setreuid(0, 0) < 0)
+ perror("setreuid");
+#endif
+
+#ifdef SOCKS
+ SOCKSinit(argv[0]);
+#endif
+
+#ifdef TPF
+ APACHE_TPF_INPUT input_parms;
+ ecbptr()->ebrout = PRIMECRAS;
+ input_parms = * (APACHE_TPF_INPUT *)(&(ecbptr()->ebw000));
+#endif
+
+ MONCONTROL(0);
+
+ common_init();
+
+ if ((s = strrchr(argv[0], PATHSEPARATOR)) != NULL) {
+ ap_server_argv0 = ++s;
+ }
+ else {
+ ap_server_argv0 = argv[0];
+ }
+
+ ap_cpystrn(ap_server_root, HTTPD_ROOT, sizeof(ap_server_root));
+ ap_cpystrn(ap_server_confname, SERVER_CONFIG_FILE, sizeof(ap_server_confname));
+
+ ap_setup_prelinked_modules();
+
+ while ((c = getopt(argc, argv,
+ "D:C:c:xXd:f:vVlLR:StTh"
+#ifdef DEBUG_SIGSTOP
+ "Z:"
+#endif
+ )) != -1) {
+ char **new;
+ switch (c) {
+ case 'c':
+ new = (char **)ap_push_array(ap_server_post_read_config);
+ *new = ap_pstrdup(pcommands, optarg);
+ break;
+ case 'C':
+ new = (char **)ap_push_array(ap_server_pre_read_config);
+ *new = ap_pstrdup(pcommands, optarg);
+ break;
+ case 'D':
+ new = (char **)ap_push_array(ap_server_config_defines);
+ *new = ap_pstrdup(pcommands, optarg);
+ break;
+ case 'd':
+ ap_cpystrn(ap_server_root, optarg, sizeof(ap_server_root));
+ break;
+ case 'f':
+ ap_cpystrn(ap_server_confname, optarg, sizeof(ap_server_confname));
+ break;
+ case 'v':
+ ap_set_version();
+ printf("Server version: %s\n", ap_get_server_version());
+ printf("Server built: %s\n", ap_get_server_built());
+ exit(0);
+ case 'V':
+ ap_set_version();
+ show_compile_settings();
+ exit(0);
+ case 'l':
+ ap_show_modules();
+ exit(0);
+ case 'L':
+ ap_show_directives();
+ exit(0);
+ case 'X':
+ ++one_process; /* Weird debugging mode. */
+ break;
+#ifdef TPF
+ case 'x':
+ os_tpf_child(&input_parms);
+ set_signals();
+ break;
+#endif
+#ifdef DEBUG_SIGSTOP
+ case 'Z':
+ raise_sigstop_flags = atoi(optarg);
+ break;
+#endif
+#ifdef SHARED_CORE
+ case 'R':
+ /* just ignore this option here, because it has only
+ * effect when SHARED_CORE is used and then it was
+ * already handled in the Shared Core Bootstrap
+ * program.
+ */
+ break;
+#endif
+ case 'S':
+ ap_dump_settings = 1;
+ break;
+ case 't':
+ ap_configtestonly = 1;
+ ap_docrootcheck = 1;
+ break;
+ case 'T':
+ ap_configtestonly = 1;
+ ap_docrootcheck = 0;
+ break;
+ case 'h':
+ usage(argv[0]);
+ case '?':
+ usage(argv[0]);
+ }
+ }
+
+ ap_suexec_enabled = init_suexec();
+ server_conf = ap_read_config(pconf, ptrans, ap_server_confname);
+
+ if (ap_configtestonly) {
+ fprintf(stderr, "Syntax OK\n");
+ exit(0);
+ }
+ if (ap_dump_settings) {
+ exit(0);
+ }
+
+ child_timeouts = !ap_standalone || one_process;
+
+#ifndef TPF
+ if (ap_standalone) {
+ ap_open_logs(server_conf, plog);
+ ap_set_version();
+ ap_init_modules(pconf, server_conf);
+ version_locked++;
+ STANDALONE_MAIN(argc, argv);
+ }
+#else
+ if (ap_standalone) {
+ if(!tpf_child) {
+ memcpy(tpf_server_name, input_parms.inetd_server.servname, INETD_SERVNAME_LENGTH);
+ tpf_server_name[INETD_SERVNAME_LENGTH+1] = '\0';
+ ap_open_logs(server_conf, pconf);
+ }
+ ap_set_version();
+ ap_init_modules(pconf, server_conf);
+ version_locked++;
+ if(tpf_child) {
+ copy_listeners(pconf);
+ reset_tpf_listeners(&input_parms);
+ server_conf->error_log = NULL;
+#ifdef SCOREBOARD_FILE
+ scoreboard_fd = input_parms.scoreboard_fd;
+ ap_scoreboard_image = &_scoreboard_image;
+#else /* must be USE_TPF_SCOREBOARD or USE_SHMGET_SCOREBOARD */
+ ap_scoreboard_image = (scoreboard *)input_parms.scoreboard_heap;
+#endif
+ child_main(input_parms.slot);
+ }
+ else
+ STANDALONE_MAIN(argc, argv);
+ }
+#endif
+ else {
+ conn_rec *conn;
+ request_rec *r;
+ struct sockaddr sa_server, sa_client;
+ BUFF *cio;
+ NET_SIZE_T l;
+
+ ap_set_version();
+ /* Yes this is called twice. */
+ ap_init_modules(pconf, server_conf);
+ version_locked++;
+ ap_open_logs(server_conf, plog);
+ ap_init_modules(pconf, server_conf);
+ set_group_privs();
+
+#ifdef MPE
+ /* Only try to switch if we're running as MANAGER.SYS */
+ if (geteuid() == 1 && ap_user_id > 1) {
+ GETPRIVMODE();
+ if (setuid(ap_user_id) == -1) {
+ GETUSERMODE();
+ ap_log_error(APLOG_MARK, APLOG_ALERT, server_conf,
+ "setuid: unable to change to uid: %d", ap_user_id);
+ exit(1);
+ }
+ GETUSERMODE();
+ }
+#else
+ /* Only try to switch if we're running as root */
+ if (!geteuid() && setuid(ap_user_id) == -1) {
+ ap_log_error(APLOG_MARK, APLOG_ALERT, server_conf,
+ "setuid: unable to change to uid: %ld",
+ (long) ap_user_id);
+ exit(1);
+ }
+#endif
+ if (ap_setjmp(jmpbuffer)) {
+ exit(0);
+ }
+
+#ifdef TPF
+/* TPF only passes the incoming socket number from the internet daemon
+ in ebw000 */
+ sock_in = * (int*)(&(ecbptr()->ebw000));
+ sock_out = * (int*)(&(ecbptr()->ebw000));
+/* TPF also needs a signal set for alarm in inetd mode */
+ signal(SIGALRM, alrm_handler);
+#elif defined(MPE)
+/* HP MPE 5.5 inetd only passes the incoming socket as stdin (fd 0), whereas
+ HPUX inetd passes the incoming socket as stdin (fd 0) and stdout (fd 1).
+ Go figure. SR 5003355016 has been submitted to request that the existing
+ functionality be documented, and then to enhance the functionality to be
+ like HPUX. */
+ sock_in = fileno(stdin);
+ sock_out = fileno(stdin);
+#else
+ sock_in = fileno(stdin);
+ sock_out = fileno(stdout);
+#endif
+
+ l = sizeof(sa_client);
+ if ((getpeername(sock_in, &sa_client, &l)) < 0) {
+/* get peername will fail if the input isn't a socket */
+ perror("getpeername");
+ memset(&sa_client, '\0', sizeof(sa_client));
+ }
+
+ l = sizeof(sa_server);
+ if (getsockname(sock_in, &sa_server, &l) < 0) {
+ perror("getsockname");
+ fprintf(stderr, "Error getting local address\n");
+ exit(1);
+ }
+ server_conf->port = ntohs(((struct sockaddr_in *) &sa_server)->sin_port);
+ cio = ap_bcreate(ptrans, B_RDWR | B_SOCKET);
+ cio->fd = sock_out;
+ cio->fd_in = sock_in;
+ conn = new_connection(ptrans, server_conf, cio,
+ (struct sockaddr_in *) &sa_client,
+ (struct sockaddr_in *) &sa_server, -1);
+
+ while ((r = ap_read_request(conn)) != NULL) {
+
+ if (r->status == HTTP_OK)
+ ap_process_request(r);
+
+ if (!conn->keepalive || conn->aborted)
+ break;
+
+ ap_destroy_pool(r->pool);
+ }
+
+ ap_bclose(cio);
+ }
+ exit(0);
+}
+
+#else /* ndef MULTITHREAD */
+
+
+/**********************************************************************
+ * Multithreaded implementation
+ *
+ * This code is fairly specific to Win32.
+ *
+ * The model used to handle requests is a set of threads. One "main"
+ * thread listens for new requests. When something becomes
+ * available, it does a select and places the newly available socket
+ * onto a list of "jobs" (add_job()). Then any one of a fixed number
+ * of "worker" threads takes the top job off the job list with
+ * remove_job() and handles that connection to completion. After
+ * the connection has finished the thread is free to take another
+ * job from the job list.
+ *
+ * In the code, the "main" thread is running within the worker_main()
+ * function. The first thing this function does is create the
+ * worker threads, which operate in the child_sub_main() function. The
+ * main thread then goes into a loop within worker_main() where they
+ * do a select() on the listening sockets. The select times out once
+ * per second so that the thread can check for an "exit" signal
+ * from the parent process (see below). If this signal is set, the
+ * thread can exit, but only after it has accepted all incoming
+ * connections already in the listen queue (since Win32 appears
+ * to through away listened but unaccepted connections when a
+ * process dies).
+ *
+ * Because the main and worker threads exist within a single process
+ * they are vulnerable to crashes or memory leaks (crashes can also
+ * be caused within modules, of course). There also needs to be a
+ * mechanism to perform restarts and shutdowns. This is done by
+ * creating the main & worker threads within a subprocess. A
+ * main process (the "parent process") creates one (or more)
+ * processes to do the work, then the parent sits around waiting
+ * for the working process to die, in which case it starts a new
+ * one. The parent process also handles restarts (by creating
+ * a new working process then signalling the previous working process
+ * exit ) and shutdowns (by signalling the working process to exit).
+ * The parent process operates within the master_main() function. This
+ * process also handles requests from the service manager (NT only).
+ *
+ * Signalling between the parent and working process uses a Win32
+ * event. Each child has a unique name for the event, which is
+ * passed to it with the -Z argument when the child is spawned. The
+ * parent sets (signals) this event to tell the child to die.
+ * At present all children do a graceful die - they finish all
+ * current jobs _and_ empty the listen queue before they exit.
+ * A non-graceful die would need a second event. The -Z argument in
+ * the child is also used to create the shutdown and restart events,
+ * since the prefix (apPID) contains the parent process PID.
+ *
+ * The code below starts with functions at the lowest level -
+ * worker threads, and works up to the top level - the main()
+ * function of the parent process.
+ *
+ * The scoreboard (in process memory) contains details of the worker
+ * threads (within the active working process). There is no shared
+ * "scoreboard" between processes, since only one is ever active
+ * at once (or at most, two, when one has been told to shutdown but
+ * is processes outstanding requests, and a new one has been started).
+ * This is controlled by a "start_mutex" which ensures only one working
+ * process is active at once.
+ **********************************************************************/
+
+/* The code protected by #ifdef UNGRACEFUL_RESTARTS/#endif sections
+ * could implement a sort-of ungraceful restart for Win32. instead of
+ * graceful restarts.
+ *
+ * However it does not work too well because it does not intercept a
+ * connection already in progress (in child_sub_main()). We'd have to
+ * get that to poll on the exit event.
+ */
+
+/*
+ * Definition of jobs, shared by main and worker threads.
+ */
+
+typedef struct joblist_s {
+ struct joblist_s *next;
+ int sock;
+} joblist;
+
+/*
+ * Globals common to main and worker threads. This structure is not
+ * used by the parent process.
+ */
+
+typedef struct globals_s {
+#ifdef UNGRACEFUL_RESTART
+ HANDLE thread_exit_event;
+#else
+ int exit_now;
+#endif
+ semaphore *jobsemaphore;
+ joblist *jobhead;
+ joblist *jobtail;
+ mutex *jobmutex;
+ int jobcount;
+} globals;
+
+globals allowed_globals =
+{0, NULL, NULL, NULL, NULL, 0};
+
+/*
+ * add_job()/remove_job() - add or remove an accepted socket from the
+ * list of sockets connected to clients. allowed_globals.jobmutex protects
+ * against multiple concurrent access to the linked list of jobs.
+ */
+
+void add_job(int sock)
+{
+ joblist *new_job;
+
+ ap_assert(allowed_globals.jobmutex);
+ /* TODO: If too many jobs in queue, sleep, check for problems */
+ ap_acquire_mutex(allowed_globals.jobmutex);
+ new_job = (joblist *) malloc(sizeof(joblist));
+ if (new_job == NULL) {
+ fprintf(stderr, "Ouch! Out of memory in add_job()!\n");
+ }
+ new_job->next = NULL;
+ new_job->sock = sock;
+ if (allowed_globals.jobtail != NULL)
+ allowed_globals.jobtail->next = new_job;
+ allowed_globals.jobtail = new_job;
+ if (!allowed_globals.jobhead)
+ allowed_globals.jobhead = new_job;
+ allowed_globals.jobcount++;
+ release_semaphore(allowed_globals.jobsemaphore);
+ ap_release_mutex(allowed_globals.jobmutex);
+}
+
+int remove_job(void)
+{
+ joblist *job;
+ int sock;
+
+#ifdef UNGRACEFUL_RESTART
+ HANDLE hObjects[2];
+ int rv;
+
+ hObjects[0] = allowed_globals.jobsemaphore;
+ hObjects[1] = allowed_globals.thread_exit_event;
+
+ rv = WaitForMultipleObjects(2, hObjects, FALSE, INFINITE);
+ ap_assert(rv != WAIT_FAILED);
+ if (rv == WAIT_OBJECT_0 + 1) {
+ /* thread_exit_now */
+ APD1("thread got exit now event");
+ return -1;
+ }
+ /* must be semaphore */
+#else
+ acquire_semaphore(allowed_globals.jobsemaphore);
+#endif
+ ap_assert(allowed_globals.jobmutex);
+
+#ifdef UNGRACEFUL_RESTART
+ if (!allowed_globals.jobhead) {
+#else
+ ap_acquire_mutex(allowed_globals.jobmutex);
+ if (allowed_globals.exit_now && !allowed_globals.jobhead) {
+#endif
+ ap_release_mutex(allowed_globals.jobmutex);
+ return (-1);
+ }
+ job = allowed_globals.jobhead;
+ ap_assert(job);
+ allowed_globals.jobhead = job->next;
+ if (allowed_globals.jobhead == NULL)
+ allowed_globals.jobtail = NULL;
+ ap_release_mutex(allowed_globals.jobmutex);
+ sock = job->sock;
+ free(job);
+ return (sock);
+}
+
+/*
+ * child_sub_main() - this is the main loop for the worker threads
+ *
+ * Each thread runs within this function. They wait within remove_job()
+ * for a job to become available, then handle all the requests on that
+ * connection until it is closed, then return to remove_job().
+ *
+ * The worker thread will exit when it removes a job which contains
+ * socket number -1. This provides a graceful thread exit, since
+ * it will never exit during a connection.
+ *
+ * This code in this function is basically equivalent to the child_main()
+ * from the multi-process (Unix) environment, except that we
+ *
+ * - do not call child_init_modules (child init API phase)
+ * - block in remove_job, and when unblocked we have an already
+ * accepted socket, instead of blocking on a mutex or select().
+ */
+
+static void child_sub_main(int child_num)
+{
+ NET_SIZE_T clen;
+ struct sockaddr sa_server;
+ struct sockaddr sa_client;
+ pool *ptrans;
+ int requests_this_child = 0;
+ int csd = -1;
+ int dupped_csd = -1;
+ int srv = 0;
+
+ ptrans = ap_make_sub_pool(pconf);
+
+ (void) ap_update_child_status(child_num, SERVER_READY, (request_rec *) NULL);
+
+ /*
+ * Setup the jump buffers so that we can return here after a timeout.
+ */
+#if defined(USE_LONGJMP)
+ setjmp(jmpbuffer);
+#else
+ sigsetjmp(jmpbuffer, 1);
+#endif
+#ifdef SIGURG
+ signal(SIGURG, timeout);
+#endif
+
+ while (1) {
+ BUFF *conn_io;
+ request_rec *r;
+
+ /*
+ * (Re)initialize this child to a pre-connection state.
+ */
+
+ ap_set_callback_and_alarm(NULL, 0); /* Cancel any outstanding alarms */
+ timeout_req = NULL; /* No request in progress */
+ current_conn = NULL;
+
+ ap_clear_pool(ptrans);
+
+ (void) ap_update_child_status(child_num, SERVER_READY,
+ (request_rec *) NULL);
+
+ /* Get job from the job list. This will block until a job is ready.
+ * If -1 is returned then the main thread wants us to exit.
+ */
+ csd = remove_job();
+ if (csd == -1)
+ break; /* time to exit */
+ requests_this_child++;
+
+ ap_note_cleanups_for_socket(ptrans, csd);
+
+ /*
+ * We now have a connection, so set it up with the appropriate
+ * socket options, file descriptors, and read/write buffers.
+ */
+
+ clen = sizeof(sa_server);
+ if (getsockname(csd, &sa_server, &clen) < 0) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, server_conf, "getsockname");
+ continue;
+ }
+ clen = sizeof(sa_client);
+ if ((getpeername(csd, &sa_client, &clen)) < 0) {
+ /* get peername will fail if the input isn't a socket */
+ perror("getpeername");
+ memset(&sa_client, '\0', sizeof(sa_client));
+ }
+
+ sock_disable_nagle(csd);
+
+ (void) ap_update_child_status(child_num, SERVER_BUSY_READ,
+ (request_rec *) NULL);
+
+ conn_io = ap_bcreate(ptrans, B_RDWR | B_SOCKET);
+ dupped_csd = csd;
+#if defined(NEED_DUPPED_CSD)
+ if ((dupped_csd = dup(csd)) < 0) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, server_conf,
+ "dup: couldn't duplicate csd");
+ dupped_csd = csd; /* Oh well... */
+ }
+ ap_note_cleanups_for_socket(ptrans, dupped_csd);
+#endif
+ ap_bpushfd(conn_io, csd, dupped_csd);
+
+ current_conn = new_connection(ptrans, server_conf, conn_io,
+ (struct sockaddr_in *) &sa_client,
+ (struct sockaddr_in *) &sa_server,
+ child_num);
+
+ /*
+ * Read and process each request found on our connection
+ * until no requests are left or we decide to close.
+ */
+
+ while ((r = ap_read_request(current_conn)) != NULL) {
+ (void) ap_update_child_status(child_num, SERVER_BUSY_WRITE, r);
+
+ if (r->status == HTTP_OK)
+ ap_process_request(r);
+
+ if (ap_extended_status)
+ increment_counts(child_num, r);
+
+ if (!current_conn->keepalive || current_conn->aborted)
+ break;
+
+ ap_destroy_pool(r->pool);
+ (void) ap_update_child_status(child_num, SERVER_BUSY_KEEPALIVE,
+ (request_rec *) NULL);
+
+ ap_sync_scoreboard_image();
+ }
+
+ /*
+ * Close the connection, being careful to send out whatever is still
+ * in our buffers. If possible, try to avoid a hard close until the
+ * client has ACKed our FIN and/or has stopped sending us data.
+ */
+ ap_kill_cleanups_for_socket(ptrans, csd);
+
+#ifdef NO_LINGCLOSE
+ ap_bclose(conn_io); /* just close it */
+#else
+ if (r && r->connection
+ && !r->connection->aborted
+ && r->connection->client
+ && (r->connection->client->fd >= 0)) {
+
+ lingering_close(r);
+ }
+ else {
+ ap_bsetflag(conn_io, B_EOUT, 1);
+ ap_bclose(conn_io);
+ }
+#endif
+ }
+ ap_destroy_pool(ptrans);
+ (void) ap_update_child_status(child_num, SERVER_DEAD, NULL);
+}
+
+
+void child_main(int child_num_arg)
+{
+ /*
+ * Only reason for this function, is to pass in
+ * arguments to child_sub_main() on its stack so
+ * that longjump doesn't try to corrupt its local
+ * variables and I don't need to make those
+ * damn variables static/global
+ */
+ child_sub_main(child_num_arg);
+}
+
+
+
+void cleanup_thread(thread **handles, int *thread_cnt, int thread_to_clean)
+{
+ int i;
+
+ free_thread(handles[thread_to_clean]);
+ for (i = thread_to_clean; i < ((*thread_cnt) - 1); i++)
+ handles[i] = handles[i + 1];
+ (*thread_cnt)--;
+}
+#ifdef WIN32
+/*
+ * The Win32 call WaitForMultipleObjects will only allow you to wait for
+ * a maximum of MAXIMUM_WAIT_OBJECTS (current 64). Since the threading
+ * model in the multithreaded version of apache wants to use this call,
+ * we are restricted to a maximum of 64 threads. This is a simplistic
+ * routine that will increase this size.
+ */
+static DWORD wait_for_many_objects(DWORD nCount, CONST HANDLE *lpHandles,
+ DWORD dwSeconds)
+{
+ time_t tStopTime;
+ DWORD dwRet = WAIT_TIMEOUT;
+ DWORD dwIndex=0;
+ BOOL bFirst = TRUE;
+
+ tStopTime = time(NULL) + dwSeconds;
+
+ do {
+ if (!bFirst)
+ Sleep(1000);
+ else
+ bFirst = FALSE;
+
+ for (dwIndex = 0; dwIndex * MAXIMUM_WAIT_OBJECTS < nCount; dwIndex++) {
+ dwRet = WaitForMultipleObjects(
+ min(MAXIMUM_WAIT_OBJECTS,
+ nCount - (dwIndex * MAXIMUM_WAIT_OBJECTS)),
+ lpHandles + (dwIndex * MAXIMUM_WAIT_OBJECTS),
+ 0, 0);
+
+ if (dwRet != WAIT_TIMEOUT) {
+ break;
+ }
+ }
+ } while((time(NULL) < tStopTime) && (dwRet == WAIT_TIMEOUT));
+
+ return dwRet;
+}
+#endif
+/*****************************************************************
+ * Executive routines.
+ */
+
+extern void main_control_server(void *); /* in hellop.c */
+
+event *exit_event;
+mutex *start_mutex;
+
+#define MAX_SIGNAL_NAME 30 /* Long enough for apPID_shutdown, where PID is an int */
+char signal_name_prefix[MAX_SIGNAL_NAME];
+char signal_restart_name[MAX_SIGNAL_NAME];
+char signal_shutdown_name[MAX_SIGNAL_NAME];
+
+#define MAX_SELECT_ERRORS 100
+
+/*
+ * Initialise the signal names, in the global variables signal_name_prefix,
+ * signal_restart_name and signal_shutdown_name.
+ */
+
+void setup_signal_names(char *prefix)
+{
+ ap_snprintf(signal_name_prefix, sizeof(signal_name_prefix), prefix);
+ ap_snprintf(signal_shutdown_name, sizeof(signal_shutdown_name),
+ "%s_shutdown", signal_name_prefix);
+ ap_snprintf(signal_restart_name, sizeof(signal_restart_name),
+ "%s_restart", signal_name_prefix);
+
+ APD2("signal prefix %s", signal_name_prefix);
+}
+
+static void setup_inherited_listeners(pool *p)
+{
+ HANDLE pipe;
+ listen_rec *lr;
+ int fd;
+ WSAPROTOCOL_INFO WSAProtocolInfo;
+ DWORD BytesRead;
+
+ /* Open the pipe to the parent process to receive the inherited socket
+ * data. The sockets have been set to listening in the parent process.
+ */
+ pipe = GetStdHandle(STD_INPUT_HANDLE);
+
+ /* Setup the listeners */
+ listenmaxfd = -1;
+ FD_ZERO(&listenfds);
+ lr = ap_listeners;
+
+ FD_ZERO(&listenfds);
+
+ for (;;) {
+ fd = find_listener(lr);
+ if (fd < 0) {
+ if (!ReadFile(pipe,
+ &WSAProtocolInfo, sizeof(WSAPROTOCOL_INFO),
+ &BytesRead,
+ (LPOVERLAPPED) NULL)){
+ ap_log_error(APLOG_MARK, APLOG_WIN32ERROR|APLOG_CRIT, server_conf,
+ "setup_inherited_listeners: Unable to read socket data from parent");
+ exit(1);
+ }
+ fd = WSASocket(FROM_PROTOCOL_INFO,
+ FROM_PROTOCOL_INFO,
+ FROM_PROTOCOL_INFO,
+ &WSAProtocolInfo,
+ 0,
+ 0);
+ if (fd == INVALID_SOCKET) {
+ ap_log_error(APLOG_MARK, APLOG_WIN32ERROR|APLOG_CRIT, server_conf,
+ "setup_inherited_listeners: WSASocket failed to get inherit the socket.");
+ exit(1);
+ }
+ APD2("setup_inherited_listeners: WSASocket() returned socket %d", fd);
+ }
+ else {
+ ap_note_cleanups_for_socket(p, fd);
+ }
+ if (fd >= 0) {
+ FD_SET(fd, &listenfds);
+ if (fd > listenmaxfd)
+ listenmaxfd = fd;
+ }
+ lr->fd = fd;
+ if (lr->next == NULL)
+ break;
+ lr = lr->next;
+ }
+ /* turn the list into a ring */
+ lr->next = ap_listeners;
+ head_listener = ap_listeners;
+ close_unused_listeners();
+ CloseHandle(pipe);
+ return;
+}
+
+/*
+ * worker_main() is main loop for the child process. The loop in
+ * this function becomes the controlling thread for the actually working
+ * threads (which run in a loop in child_sub_main()).
+ */
+
+void worker_main(void)
+{
+ int nthreads;
+ fd_set main_fds;
+ int srv;
+ int clen;
+ int csd;
+ struct sockaddr_in sa_client;
+ int total_jobs = 0;
+ thread **child_handles;
+ int rv;
+ time_t end_time;
+ int i;
+ struct timeval tv;
+ int wait_time = 1;
+ int max_jobs_per_exe;
+ int max_jobs_after_exit_request;
+ HANDLE hObjects[2];
+ int count_select_errors = 0;
+ pool *pchild;
+
+ pchild = ap_make_sub_pool(pconf);
+
+ ap_standalone = 1;
+ sd = -1;
+ nthreads = ap_threads_per_child;
+ max_jobs_after_exit_request = ap_excess_requests_per_child;
+ max_jobs_per_exe = ap_max_requests_per_child;
+ if (nthreads <= 0)
+ nthreads = 40;
+ if (max_jobs_per_exe <= 0)
+ max_jobs_per_exe = 0;
+ if (max_jobs_after_exit_request <= 0)
+ max_jobs_after_exit_request = max_jobs_per_exe / 10;
+
+ if (!one_process)
+ detach();
+
+ my_pid = getpid();
+
+ ++ap_my_generation;
+
+ copy_listeners(pconf);
+ ap_restart_time = time(NULL);
+
+ reinit_scoreboard(pconf);
+
+ /*
+ * Wait until we have permission to start accepting connections.
+ * start_mutex is used to ensure that only one child ever
+ * goes into the listen/accept loop at once. Also wait on exit_event,
+ * in case we (this child) is told to die before we get a chance to
+ * serve any requests.
+ */
+ hObjects[0] = (HANDLE)start_mutex;
+ hObjects[1] = (HANDLE)exit_event;
+ rv = WaitForMultipleObjects(2, hObjects, FALSE, INFINITE);
+ if (rv == WAIT_FAILED) {
+ ap_log_error(APLOG_MARK,APLOG_ERR|APLOG_WIN32ERROR, server_conf,
+ "Waiting for start_mutex or exit_event -- process will exit");
+
+ ap_destroy_pool(pchild);
+ cleanup_scoreboard();
+ exit(0);
+ }
+ if (rv == WAIT_OBJECT_0 + 1) {
+ /* exit event signalled - exit now */
+ ap_destroy_pool(pchild);
+ cleanup_scoreboard();
+ exit(0);
+ }
+ /* start_mutex obtained, continue into the select() loop */
+ if (one_process) {
+ setup_listeners(pconf);
+ } else {
+ /* Get listeners from the parent process */
+ setup_inherited_listeners(pconf);
+ }
+
+ if (listenmaxfd == -1) {
+ /* Help, no sockets were made, better log something and exit */
+ ap_log_error(APLOG_MARK, APLOG_CRIT|APLOG_NOERRNO, NULL,
+ "No sockets were created for listening");
+
+ signal_parent(0); /* tell parent to die */
+
+ ap_destroy_pool(pchild);
+ cleanup_scoreboard();
+ exit(0);
+ }
+ set_signals();
+
+ /*
+ * - Initialize allowed_globals
+ * - Create the thread table
+ * - Spawn off threads
+ * - Create listen socket set (done above)
+ * - loop {
+ * wait for request
+ * create new job
+ * } while (!time to exit)
+ * - Close all listeners
+ * - Wait for all threads to complete
+ * - Exit
+ */
+
+ ap_child_init_modules(pconf, server_conf);
+
+ allowed_globals.jobsemaphore = create_semaphore(0);
+ allowed_globals.jobmutex = ap_create_mutex(NULL);
+
+ /* spawn off the threads */
+ child_handles = (thread *) alloca(nthreads * sizeof(int));
+ for (i = 0; i < nthreads; i++) {
+ child_handles[i] = create_thread((void (*)(void *)) child_main, (void *) i);
+ }
+ if (nthreads > max_daemons_limit) {
+ max_daemons_limit = nthreads;
+ }
+
+ while (1) {
+ if (max_jobs_per_exe && (total_jobs > max_jobs_per_exe)) {
+ /* MaxRequestsPerChild hit...
+ */
+ break;
+ }
+ /* Always check for the exit event being signaled.
+ */
+ rv = WaitForSingleObject(exit_event, 0);
+ ap_assert((rv == WAIT_TIMEOUT) || (rv == WAIT_OBJECT_0));
+ if (rv == WAIT_OBJECT_0) {
+ APD1("child: exit event signalled, exiting");
+ break;
+ }
+
+ tv.tv_sec = wait_time;
+ tv.tv_usec = 0;
+
+ memcpy(&main_fds, &listenfds, sizeof(fd_set));
+ srv = ap_select(listenmaxfd + 1, &main_fds, NULL, NULL, &tv);
+#ifdef WIN32
+ if (srv == SOCKET_ERROR) {
+ /* Map the Win32 error into a standard Unix error condition */
+ errno = WSAGetLastError();
+ srv = -1;
+ }
+#endif /* WIN32 */
+
+ if (srv < 0) {
+ /* Error occurred - if EINTR, loop around with problem */
+ if (errno != EINTR) {
+ /* A "real" error occurred, log it and increment the count of
+ * select errors. This count is used to ensure we don't go into
+ * a busy loop of continuous errors.
+ */
+ ap_log_error(APLOG_MARK, APLOG_ERR, server_conf, "select: (listen)");
+ count_select_errors++;
+ if (count_select_errors > MAX_SELECT_ERRORS) {
+ ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, server_conf,
+ "Too many errors in select loop. Child process exiting.");
+ break;
+ }
+ }
+ continue;
+ }
+ count_select_errors = 0; /* reset count of errors */
+ if (srv == 0) {
+ continue;
+ }
+
+ {
+ listen_rec *lr;
+
+ lr = find_ready_listener(&main_fds);
+ if (lr != NULL) {
+ sd = lr->fd;
+ }
+ }
+ do {
+ clen = sizeof(sa_client);
+ csd = accept(sd, (struct sockaddr *) &sa_client, &clen);
+#ifdef WIN32
+ if (csd == INVALID_SOCKET) {
+ csd = -1;
+ errno = WSAGetLastError();
+ }
+#endif /* WIN32 */
+ } while (csd < 0 && errno == EINTR);
+
+ if (csd < 0) {
+#if defined(EPROTO) && defined(ECONNABORTED)
+ if ((errno != EPROTO) && (errno != ECONNABORTED))
+#elif defined(EPROTO)
+ if (errno != EPROTO)
+#elif defined(ECONNABORTED)
+ if (errno != ECONNABORTED)
+#endif
+ ap_log_error(APLOG_MARK, APLOG_ERR, server_conf,
+ "accept: (client socket)");
+ }
+ else {
+ add_job(csd);
+ total_jobs++;
+ }
+ }
+
+ APD2("process PID %d exiting", my_pid);
+
+ /* Get ready to shutdown and exit */
+ allowed_globals.exit_now = 1;
+ ap_release_mutex(start_mutex);
+
+#ifdef UNGRACEFUL_RESTART
+ SetEvent(allowed_globals.thread_exit_event);
+#else
+ for (i = 0; i < nthreads; i++) {
+ add_job(-1);
+ }
+#endif
+
+ APD2("process PID %d waiting for worker threads to exit", my_pid);
+ /* Wait for all your children */
+ end_time = time(NULL) + 180;
+ while (nthreads) {
+ rv = wait_for_many_objects(nthreads, child_handles,
+ end_time - time(NULL));
+ if (rv != WAIT_TIMEOUT) {
+ rv = rv - WAIT_OBJECT_0;
+ ap_assert((rv >= 0) && (rv < nthreads));
+ cleanup_thread(child_handles, &nthreads, rv);
+ continue;
+ }
+ break;
+ }
+
+ APD2("process PID %d killing remaining worker threads", my_pid);
+ for (i = 0; i < nthreads; i++) {
+ kill_thread(child_handles[i]);
+ free_thread(child_handles[i]);
+ }
+#ifdef UNGRACEFUL_RESTART
+ ap_assert(CloseHandle(allowed_globals.thread_exit_event));
+#endif
+ destroy_semaphore(allowed_globals.jobsemaphore);
+ ap_destroy_mutex(allowed_globals.jobmutex);
+
+ ap_child_exit_modules(pconf, server_conf);
+ ap_destroy_pool(pchild);
+
+ cleanup_scoreboard();
+
+ APD2("process PID %d exited", my_pid);
+ clean_parent_exit(0);
+} /* standalone_main */
+
+/*
+ * Spawn a child Apache process. The child process has the command line arguments from
+ * argc and argv[], plus a -Z argument giving the name of an event. The child should
+ * open and poll or wait on this event. When it is signalled, the child should die.
+ * prefix is a prefix string for the event name.
+ *
+ * The child_num argument on entry contains a serial number for this child (used to create
+ * a unique event name). On exit, this number will have been incremented by one, ready
+ * for the next call.
+ *
+ * On exit, the value pointed to be *ev will contain the event created
+ * to signal the new child process.
+ *
+ * The return value is the handle to the child process if successful, else -1. If -1 is
+ * returned the error will already have been logged by ap_log_error().
+ */
+
+/**********************************************************************
+ * master_main - this is the parent (main) process. We create a
+ * child process to do the work, then sit around waiting for either
+ * the child to exit, or a restart or exit signal. If the child dies,
+ * we just respawn a new one. If we have a shutdown or graceful restart,
+ * tell the child to die when it is ready. If it is a non-graceful
+ * restart, force the child to die immediately.
+ **********************************************************************/
+
+#define MAX_PROCESSES 50 /* must be < MAX_WAIT_OBJECTS-1 */
+
+static void cleanup_process(HANDLE *handles, HANDLE *events, int position, int *processes)
+{
+ int i;
+ int handle = 0;
+
+ CloseHandle(handles[position]);
+ CloseHandle(events[position]);
+
+ handle = (int)handles[position];
+
+ for (i = position; i < (*processes)-1; i++) {
+ handles[i] = handles[i + 1];
+ events[i] = events[i + 1];
+ }
+ (*processes)--;
+
+ APD4("cleanup_processes: removed child in slot %d handle %d, max=%d", position, handle, *processes);
+}
+
+static int create_process(pool *p, HANDLE *handles, HANDLE *events,
+ int *processes, int *child_num, char *kill_event_name, int argc, char **argv)
+{
+
+ int rv, i;
+ HANDLE kill_event;
+ char buf[1024];
+ char exit_event_name[40]; /* apPID_C# */
+ char *pCommand;
+
+ STARTUPINFO si; /* Filled in prior to call to CreateProcess */
+ PROCESS_INFORMATION pi; /* filled in on call to CreateProces */
+ LPWSAPROTOCOL_INFO lpWSAProtocolInfo;
+ listen_rec *lr;
+ DWORD BytesWritten;
+ HANDLE hPipeRead = NULL;
+ HANDLE hPipeWrite = NULL;
+ SECURITY_ATTRIBUTES sa = {0};
+
+ sa.nLength = sizeof(sa);
+ sa.bInheritHandle = TRUE;
+ sa.lpSecurityDescriptor = NULL;
+
+ /* Build the command line. Should look something like this:
+ * C:/apache/bin/apache.exe -Z exit_event -f ap_server_confname
+ * First, get the path to the executable...
+ */
+ rv = GetModuleFileName(NULL, buf, sizeof(buf));
+ if (rv == sizeof(buf)) {
+ ap_log_error(APLOG_MARK, APLOG_WIN32ERROR | APLOG_CRIT, server_conf,
+ "Parent: Path to Apache process too long");
+ return -1;
+ } else if (rv == 0) {
+ ap_log_error(APLOG_MARK, APLOG_WIN32ERROR | APLOG_CRIT, server_conf,
+ "Parent: GetModuleFileName() returned NULL for current process.");
+ return -1;
+ }
+
+ /* Create the exit event (apPID_C#). Parent signals this event to tell the
+ * child to exit
+ */
+ ap_snprintf(exit_event_name, sizeof(exit_event_name), "%s_C%d", kill_event_name, ++(*child_num));
+ kill_event = CreateEvent(NULL, TRUE, FALSE, exit_event_name);
+ if (!kill_event) {
+ ap_log_error(APLOG_MARK, APLOG_WIN32ERROR | APLOG_CRIT, server_conf,
+ "Parent: Could not create exit event for child process");
+ return -1;
+ }
+
+ pCommand = ap_psprintf(p, "\"%s\" -Z %s -f \"%s\"", buf, exit_event_name, ap_server_confname);
+
+ for (i = 1; i < argc; i++) {
+ pCommand = ap_pstrcat(p, pCommand, " ", argv[i], NULL);
+ }
+
+ /* Create a pipe to send socket info to the child */
+ if (!CreatePipe(&hPipeRead, &hPipeWrite, &sa, 0)) {
+ ap_log_error(APLOG_MARK, APLOG_WIN32ERROR | APLOG_CRIT, server_conf,
+ "Parent: Unable to create pipe to child process.\n");
+ return -1;
+ }
+
+ /* Give the read in of teh pipe (hPipeRead) to the child as stdin. The
+ * parent will write the socket data to the child on this pipe.
+ */
+ memset(&si, 0, sizeof(si));
+ memset(&pi, 0, sizeof(pi));
+ si.cb = sizeof(si);
+ si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
+ si.wShowWindow = SW_HIDE;
+ si.hStdInput = hPipeRead;
+
+ if (!CreateProcess(NULL, pCommand, NULL, NULL,
+ TRUE, /* Inherit handles */
+ 0, /* Creation flags */
+ NULL, NULL,
+ &si, &pi)) {
+ ap_log_error(APLOG_MARK, APLOG_WIN32ERROR | APLOG_CRIT, server_conf,
+ "Parent: Not able to create the child process.");
+ /*
+ * We must close the handles to the new process and its main thread
+ * to prevent handle and memory leaks.
+ */
+ CloseHandle(pi.hProcess);
+ CloseHandle(pi.hThread);
+
+ return -1;
+ }
+ else {
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_INFO, server_conf,
+ "Parent: Created child process %d", pi.dwProcessId);
+
+ /* Assume the child process lives. Update the process and event tables */
+ handles[*processes] = pi.hProcess;
+ events[*processes] = kill_event;
+ (*processes)++;
+
+ /* We never store the thread's handle, so close it now. */
+ CloseHandle(pi.hThread);
+
+ /* Run the chain of open sockets. For each socket, duplicate it
+ * for the target process then send the WSAPROTOCOL_INFO
+ * (returned by dup socket) to the child */
+ lr = ap_listeners;
+ while (lr != NULL) {
+ lpWSAProtocolInfo = ap_pcalloc(p, sizeof(WSAPROTOCOL_INFO));
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_INFO, server_conf,
+ "Parent: Duplicating socket %d and sending it to child process %d", lr->fd, pi.dwProcessId);
+ if (WSADuplicateSocket(lr->fd,
+ pi.dwProcessId,
+ lpWSAProtocolInfo) == SOCKET_ERROR) {
+ ap_log_error(APLOG_MARK, APLOG_WIN32ERROR | APLOG_CRIT, server_conf,
+ "Parent: WSADuplicateSocket failed for socket %d.", lr->fd );
+ return -1;
+ }
+
+ if (!WriteFile(hPipeWrite, lpWSAProtocolInfo, (DWORD) sizeof(WSAPROTOCOL_INFO),
+ &BytesWritten,
+ (LPOVERLAPPED) NULL)) {
+ ap_log_error(APLOG_MARK, APLOG_WIN32ERROR | APLOG_CRIT, server_conf,
+ "Parent: Unable to write duplicated socket %d to the child.", lr->fd );
+ return -1;
+ }
+
+ lr = lr->next;
+ if (lr == ap_listeners)
+ break;
+ }
+ }
+ CloseHandle(hPipeRead);
+ CloseHandle(hPipeWrite);
+
+ return 0;
+}
+
+/* To share the semaphores with other processes, we need a NULL ACL
+ * Code from MS KB Q106387
+ */
+
+static PSECURITY_ATTRIBUTES GetNullACL()
+{
+ PSECURITY_DESCRIPTOR pSD;
+ PSECURITY_ATTRIBUTES sa;
+
+ sa = (PSECURITY_ATTRIBUTES) LocalAlloc(LPTR, sizeof(SECURITY_ATTRIBUTES));
+ pSD = (PSECURITY_DESCRIPTOR) LocalAlloc(LPTR,
+ SECURITY_DESCRIPTOR_MIN_LENGTH);
+ if (pSD == NULL || sa == NULL) {
+ return NULL;
+ }
+ /*
+ * We can safely use GetLastError() here without presetting it;
+ * {Initialize,Set}SecurityDescriptor() have been verified as clearing it
+ * on successful completion.
+ */
+ if (!InitializeSecurityDescriptor(pSD, SECURITY_DESCRIPTOR_REVISION)
+ || GetLastError()) {
+ LocalFree( pSD );
+ LocalFree( sa );
+ return NULL;
+ }
+ if (!SetSecurityDescriptorDacl(pSD, TRUE, (PACL) NULL, FALSE)
+ || GetLastError()) {
+ LocalFree( pSD );
+ LocalFree( sa );
+ return NULL;
+ }
+ sa->nLength = sizeof(sa);
+ sa->lpSecurityDescriptor = pSD;
+ sa->bInheritHandle = TRUE;
+ return sa;
+}
+
+
+static void CleanNullACL( void *sa ) {
+ if( sa ) {
+ LocalFree( ((PSECURITY_ATTRIBUTES)sa)->lpSecurityDescriptor);
+ LocalFree( sa );
+ }
+}
+
+int master_main(int argc, char **argv)
+{
+ /* returns NULL if invalid (Win95?) */
+ PSECURITY_ATTRIBUTES sa = GetNullACL();
+ int nchild = ap_daemons_to_start;
+ int child_num = 0;
+ int rv, cld;
+ char signal_prefix_string[100];
+ int i;
+ time_t tmstart;
+ HANDLE signal_shutdown_event; /* used to signal shutdown to parent */
+ HANDLE signal_restart_event; /* used to signal a restart to parent */
+ HANDLE process_handles[MAX_PROCESSES];
+ HANDLE process_kill_events[MAX_PROCESSES];
+ int current_live_processes = 0; /* number of child process we know about */
+ int processes_to_create = 0; /* number of child processes to create */
+ pool *pparent = NULL; /* pool for the parent process. Cleaned on each restart */
+
+ nchild = 1; /* only allowed one child process for current generation */
+ processes_to_create = nchild;
+
+ is_graceful = 0;
+
+ ap_snprintf(signal_prefix_string, sizeof(signal_prefix_string),
+ "ap%d", getpid());
+ setup_signal_names(signal_prefix_string);
+
+ /* Create shutdown event, apPID_shutdown, where PID is the parent
+ * Apache process ID. Shutdown is signaled by 'apache -k shutdown'.
+ */
+ signal_shutdown_event = CreateEvent(sa, TRUE, FALSE, signal_shutdown_name);
+ if (!signal_shutdown_event) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG|APLOG_WIN32ERROR, server_conf,
+ "master_main: Cannot create shutdown event %s", signal_shutdown_name);
+ CleanNullACL((void *)sa);
+ exit(1);
+ }
+
+ /* Create restart event, apPID_restart, where PID is the parent
+ * Apache process ID. Restart is signaled by 'apache -k restart'.
+ */
+ signal_restart_event = CreateEvent(sa, TRUE, FALSE, signal_restart_name);
+ if (!signal_restart_event) {
+ CloseHandle(signal_shutdown_event);
+ ap_log_error(APLOG_MARK, APLOG_EMERG|APLOG_WIN32ERROR, server_conf,
+ "master_main: Cannot create restart event %s", signal_restart_name);
+ CleanNullACL((void *)sa);
+ exit(1);
+ }
+ CleanNullACL((void *)sa);
+
+ /* Create the start mutex, apPID, where PID is the parent Apache process ID.
+ * Ths start mutex is used during a restart to prevent more than one
+ * child process from entering the accept loop at once.
+ */
+ start_mutex = ap_create_mutex(signal_prefix_string);
+ restart_pending = shutdown_pending = 0;
+
+ do { /* restart-pending */
+ if (!is_graceful) {
+ ap_restart_time = time(NULL);
+ }
+ copy_listeners(pconf);
+ ap_clear_pool(pconf);
+ pparent = ap_make_sub_pool(pconf);
+
+ server_conf = ap_read_config(pconf, pparent, ap_server_confname);
+ setup_listeners(pconf);
+ ap_clear_pool(plog);
+ ap_open_logs(server_conf, plog);
+ ap_set_version();
+ ap_init_modules(pconf, server_conf);
+ version_locked++;
+ service_set_status(SERVICE_START_PENDING);
+ /* Create child processes */
+ while (processes_to_create--) {
+ if (create_process(pconf, process_handles, process_kill_events,
+ ¤t_live_processes, &child_num, signal_prefix_string, argc, argv) < 0) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, server_conf,
+ "master_main: create child process failed. Exiting.");
+ goto die_now;
+ }
+ }
+ service_set_status(SERVICE_RUNNING);
+ restart_pending = shutdown_pending = 0;
+
+ /* Wait for either the shutdown or restart events to be signaled */
+ process_handles[current_live_processes] = signal_shutdown_event;
+ process_handles[current_live_processes+1] = signal_restart_event;
+ rv = WaitForMultipleObjects(current_live_processes+2, (HANDLE *)process_handles,
+ FALSE, INFINITE);
+ if (rv == WAIT_FAILED) {
+ /* Something serious is wrong */
+ ap_log_error(APLOG_MARK,APLOG_CRIT|APLOG_WIN32ERROR, server_conf,
+ "master_main: : WaitForMultipeObjects on process handles and apache-signal -- doing shutdown");
+ shutdown_pending = 1;
+ break;
+ }
+ if (rv == WAIT_TIMEOUT) {
+ /* Hey, this cannot happen */
+ ap_log_error(APLOG_MARK, APLOG_ERR, server_conf,
+ "master_main: WaitForMultipeObjects with INFINITE wait exited with WAIT_TIMEOUT");
+ shutdown_pending = 1;
+ }
+
+ cld = rv - WAIT_OBJECT_0;
+ APD4("main process: wait finished, cld=%d handle %d (max=%d)", cld, process_handles[cld], current_live_processes);
+ if (cld == current_live_processes) {
+ /* apPID_shutdown event signalled, we should exit now */
+ shutdown_pending = 1;
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, server_conf,
+ "master_main: Shutdown event signaled. Shutting the server down.");
+ if (ResetEvent(signal_shutdown_event) == 0) {
+ ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_WIN32ERROR, server_conf,
+ "ResetEvent(signal_shutdown_event)");
+ }
+ /* Signal each child processes to die */
+ for (i = 0; i < current_live_processes; i++) {
+ APD3("master_main: signalling child %d, handle %d to die", i, process_handles[i]);
+ if (SetEvent(process_kill_events[i]) == 0)
+ ap_log_error(APLOG_MARK,APLOG_ERR|APLOG_WIN32ERROR, server_conf,
+ "master_main: SetEvent for child process in slot #%d failed", i);
+ }
+ break;
+ } else if (cld == current_live_processes+1) {
+ /* apPID_restart event signalled, restart the child process */
+ int children_to_kill = current_live_processes;
+ restart_pending = 1;
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, server_conf,
+ "master_main: Restart event signaled. Doing a graceful restart.");
+ if (ResetEvent(signal_restart_event) == 0) {
+ ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_WIN32ERROR, server_conf,
+ "master_main: ResetEvent(signal_restart_event) failed.");
+ }
+ /* Signal each child process to die */
+ for (i = 0; i < children_to_kill; i++) {
+ APD3("master_main: signalling child #%d handle %d to die", i, process_handles[i]);
+ if (SetEvent(process_kill_events[i]) == 0)
+ ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_WIN32ERROR, server_conf,
+ "master_main: SetEvent for child process in slot #%d failed", i);
+ /* Remove the process (and event) from the process table */
+ cleanup_process(process_handles, process_kill_events, i, ¤t_live_processes);
+ }
+ processes_to_create = nchild;
+ ++ap_my_generation;
+ continue;
+ } else {
+ /* A child process must have exited because of MaxRequestPerChild being hit
+ * or a fatal error condition (seg fault, etc.). Remove the dead process
+ * from the process_handles and process_kill_events table and create a new
+ * child process.
+ */
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, server_conf,
+ "master_main: Child processed exited (due to MaxRequestsPerChild?). Restarting the child process.");
+ ap_assert(cld < current_live_processes);
+ cleanup_process(process_handles, process_kill_events, cld, ¤t_live_processes);
+ APD2("main_process: child in slot %d died", rv);
+ processes_to_create = 1;
+ continue;
+ }
+
+ } while (1);
+
+ /* If we dropped out of the loop we definitly want to die completely. We need to
+ * make sure we wait for all the child process to exit first.
+ */
+
+ APD2("*** main process shutdown, processes=%d ***", current_live_processes);
+
+die_now:
+
+ tmstart = time(NULL);
+ while (current_live_processes && ((tmstart+60) > time(NULL))) {
+ service_set_status(SERVICE_STOP_PENDING);
+ rv = WaitForMultipleObjects(current_live_processes, (HANDLE *)process_handles, FALSE, 2000);
+ if (rv == WAIT_TIMEOUT)
+ continue;
+ ap_assert(rv != WAIT_FAILED);
+ cld = rv - WAIT_OBJECT_0;
+ ap_assert(rv < current_live_processes);
+ APD4("main_process: child in #%d handle %d died, left=%d",
+ rv, process_handles[rv], current_live_processes);
+ cleanup_process(process_handles, process_kill_events, cld, ¤t_live_processes);
+ }
+ for (i = 0; i < current_live_processes; i++) {
+ ap_log_error(APLOG_MARK,APLOG_ERR|APLOG_NOERRNO, server_conf,
+ "forcing termination of child #%d (handle %d)", i, process_handles[i]);
+ TerminateProcess((HANDLE) process_handles[i], 1);
+ }
+
+ CloseHandle(signal_restart_event);
+ CloseHandle(signal_shutdown_event);
+
+ /* cleanup pid file on normal shutdown */
+ {
+ const char *pidfile = NULL;
+ pidfile = ap_server_root_relative (pparent, ap_pid_fname);
+ if ( pidfile != NULL && unlink(pidfile) == 0)
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO,
+ server_conf,
+ "removed PID file %s (pid=%ld)",
+ pidfile, (long)getpid());
+ }
+
+ if (pparent) {
+ ap_destroy_pool(pparent);
+ }
+
+ ap_destroy_mutex(start_mutex);
+
+ service_set_status(SERVICE_STOPPED);
+ return (0);
+}
+
+/*
+ * Send signal to a running Apache. On entry signal should contain
+ * either "shutdown" or "restart"
+ */
+
+int send_signal(pool *p, char *signal)
+{
+ char prefix[20];
+ FILE *fp;
+ int nread;
+ char *fname;
+ int end;
+
+ fname = ap_server_root_relative (p, ap_pid_fname);
+
+ fp = fopen(fname, "r");
+ if (!fp) {
+ printf("Cannot read apache PID file %s\n", fname);
+ return FALSE;
+ }
+ prefix[0] = 'a';
+ prefix[1] = 'p';
+
+ nread = fread(prefix+2, 1, sizeof(prefix)-3, fp);
+ if (nread == 0) {
+ fclose(fp);
+ printf("PID file %s was empty\n", fname);
+ return FALSE;
+ }
+ fclose(fp);
+
+ /* Terminate the prefix string */
+ end = 2 + nread - 1;
+ while (end > 0 && (prefix[end] == '\r' || prefix[end] == '\n'))
+ end--;
+ prefix[end + 1] = '\0';
+
+ setup_signal_names(prefix);
+
+ if (!strcasecmp(signal, "shutdown"))
+ ap_start_shutdown();
+ else if (!strcasecmp(signal, "restart"))
+ ap_start_restart(1);
+ else {
+ printf("Unknown signal name \"%s\". Use either shutdown or restart.\n",
+ signal);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+void post_parse_init()
+{
+ ap_set_version();
+ ap_init_modules(pconf, server_conf);
+ ap_suexec_enabled = init_suexec();
+ version_locked++;
+ ap_open_logs(server_conf, plog);
+ set_group_privs();
+}
+
+int service_init()
+{
+ common_init();
+
+ ap_cpystrn(ap_server_root, HTTPD_ROOT, sizeof(ap_server_root));
+ if (ap_registry_get_service_conf(pconf, ap_server_confname, sizeof(ap_server_confname),
+ ap_server_argv0))
+ return FALSE;
+
+ ap_setup_prelinked_modules();
+ server_conf = ap_read_config(pconf, ptrans, ap_server_confname);
+ ap_log_pid(pconf, ap_pid_fname);
+ post_parse_init();
+ return TRUE;
+}
+
+#ifdef WIN32
+__declspec(dllexport)
+ int apache_main(int argc, char *argv[])
+#else
+int REALMAIN(int argc, char *argv[])
+#endif
+{
+ int c;
+ int child = 0;
+ char *cp;
+ char *s;
+ char *service_name = NULL;
+ int install = 0;
+ int conf_specified = 0;
+ char *signal_to_send = NULL;
+ char cwd[MAX_STRING_LEN];
+
+ /* Service application
+ * Configuration file in registry at:
+ * HKLM\System\CurrentControlSet\Services\[Svc name]\Parameters\ConfPath
+ */
+ if (isProcessService()) {
+ service_main(master_main, argc, argv);
+ clean_parent_exit(0);
+ }
+
+ /* Console application or a child process. */
+
+ if ((s = strrchr(argv[0], PATHSEPARATOR)) != NULL) {
+ ap_server_argv0 = ++s;
+ }
+ else {
+ ap_server_argv0 = argv[0];
+ }
+
+ common_init();
+ ap_setup_prelinked_modules();
+
+ if(!GetCurrentDirectory(sizeof(cwd),cwd)) {
+ ap_log_error(APLOG_MARK,APLOG_WIN32ERROR, NULL,
+ "GetCurrentDirectory() failure");
+ return -1;
+ }
+
+ ap_cpystrn(cwd, ap_os_canonical_filename(pcommands, cwd), sizeof(cwd));
+ ap_cpystrn(ap_server_root, cwd, sizeof(ap_server_root));
+
+ while ((c = getopt(argc, argv, "D:C:c:Xd:f:vVlLZ:iusStThk:n:")) != -1) {
+ char **new;
+ switch (c) {
+ case 'c':
+ new = (char **)ap_push_array(ap_server_post_read_config);
+ *new = ap_pstrdup(pcommands, optarg);
+ break;
+ case 'C':
+ new = (char **)ap_push_array(ap_server_pre_read_config);
+ *new = ap_pstrdup(pcommands, optarg);
+ break;
+ case 'D':
+ new = (char **)ap_push_array(ap_server_config_defines);
+ *new = ap_pstrdup(pcommands, optarg);
+ break;
+#ifdef WIN32
+ case 'Z':
+ exit_event = open_event(optarg);
+ APD2("child: opened process event %s", optarg);
+ cp = strchr(optarg, '_');
+ ap_assert(cp);
+ *cp = 0;
+ setup_signal_names(optarg);
+ start_mutex = ap_open_mutex(signal_name_prefix);
+ ap_assert(start_mutex);
+ child = 1;
+ break;
+ case 'n':
+ service_name = ap_pstrdup(pcommands, optarg);
+ if (isValidService(optarg)) {
+ ap_registry_get_service_conf(pconf, ap_server_confname, sizeof(ap_server_confname),
+ optarg);
+ conf_specified = 1;
+ }
+ break;
+ case 'i':
+ install = 1;
+ break;
+ case 'u':
+ install = -1;
+ break;
+ case 'S':
+ ap_dump_settings = 1;
+ break;
+ case 'k':
+ signal_to_send = optarg;
+ break;
+#endif /* WIN32 */
+ case 'd':
+ optarg = ap_os_canonical_filename(pcommands, optarg);
+ if (!ap_os_is_path_absolute(optarg)) {
+ optarg = ap_pstrcat(pcommands, cwd, optarg, NULL);
+ ap_getparents(optarg);
+ }
+ if (optarg[strlen(optarg)-1] != '/')
+ optarg = ap_pstrcat(pcommands, optarg, "/", NULL);
+ ap_cpystrn(ap_server_root,
+ optarg,
+ sizeof(ap_server_root));
+ break;
+ case 'f':
+ ap_cpystrn(ap_server_confname,
+ ap_os_canonical_filename(pcommands, optarg),
+ sizeof(ap_server_confname));
+ conf_specified = 1;
+ break;
+ case 'v':
+ ap_set_version();
+ printf("Server version: %s\n", ap_get_server_version());
+ printf("Server built: %s\n", ap_get_server_built());
+ exit(0);
+ case 'V':
+ ap_set_version();
+ show_compile_settings();
+ exit(0);
+ case 'l':
+ ap_show_modules();
+ exit(0);
+ case 'L':
+ ap_show_directives();
+ exit(0);
+ case 'X':
+ ++one_process; /* Weird debugging mode. */
+ break;
+ case 't':
+ ap_configtestonly = 1;
+ ap_docrootcheck = 1;
+ break;
+ case 'T':
+ ap_configtestonly = 1;
+ ap_docrootcheck = 0;
+ break;
+ case 'h':
+ usage(ap_server_argv0);
+ case '?':
+ usage(ap_server_argv0);
+ } /* switch */
+ } /* while */
+
+ /* ServerConfFile is found in this order:
+ * (1) -f or -n
+ * (2) [-d]/SERVER_CONFIG_FILE
+ * (3) ./SERVER_CONFIG_FILE
+ * (4) [Registry: HKLM\Software\[product]\ServerRoot]/SERVER_CONFIG_FILE
+ * (5) /HTTPD_ROOT/SERVER_CONFIG_FILE
+ */
+
+ if (!conf_specified) {
+ ap_cpystrn(ap_server_confname, SERVER_CONFIG_FILE, sizeof(ap_server_confname));
+ if (access(ap_server_root_relative(pcommands, ap_server_confname), 0)) {
+ ap_registry_get_server_root(pconf, ap_server_root, sizeof(ap_server_root));
+ if (!*ap_server_root)
+ ap_cpystrn(ap_server_root, HTTPD_ROOT, sizeof(ap_server_root));
+ ap_cpystrn(ap_server_root, ap_os_canonical_filename(pcommands, ap_server_root),
+ sizeof(ap_server_root));
+ }
+ }
+
+ if (!ap_os_is_path_absolute(ap_server_confname)) {
+ char *full_conf_path;
+
+ full_conf_path = ap_pstrcat(pcommands, ap_server_root, "/", ap_server_confname, NULL);
+ full_conf_path = ap_os_canonical_filename(pcommands, full_conf_path);
+ ap_cpystrn(ap_server_confname, full_conf_path, sizeof(ap_server_confname));
+ }
+ ap_getparents(ap_server_confname);
+ ap_no2slash(ap_server_confname);
+
+#ifdef WIN32
+ if (install) {
+ if (!service_name)
+ service_name = ap_pstrdup(pconf, DEFAULTSERVICENAME);
+ if (install > 0)
+ InstallService(service_name, ap_server_root_relative(pcommands, ap_server_confname));
+ else
+ RemoveService(service_name);
+ clean_parent_exit(0);
+ }
+
+ if (service_name && signal_to_send) {
+ send_signal_to_service(service_name, signal_to_send);
+ clean_parent_exit(0);
+ }
+
+ if (service_name && !conf_specified) {
+ printf("Unknown service: %s\n", service_name);
+ clean_parent_exit(0);
+ }
+#endif
+ server_conf = ap_read_config(pconf, ptrans, ap_server_confname);
+
+ if (ap_configtestonly) {
+ fprintf(stderr, "%s: Syntax OK\n", ap_server_root_relative(pcommands, ap_server_confname));
+ clean_parent_exit(0);
+ }
+
+ if (ap_dump_settings) {
+ clean_parent_exit(0);
+ }
+
+ /* Treat -k start confpath as just -f confpath */
+ if (signal_to_send && strcasecmp(signal_to_send, "start")) {
+ send_signal(pconf, signal_to_send);
+ clean_parent_exit(0);
+ }
+
+ if (!child && !ap_dump_settings) {
+ ap_log_pid(pconf, ap_pid_fname);
+ }
+
+ post_parse_init();
+
+#ifdef OS2
+ printf("%s running...\n", ap_get_server_version());
+#endif
+#ifdef WIN32
+ if (!child) {
+ printf("%s running...\n", ap_get_server_version());
+ }
+#endif
+ if (one_process && !exit_event)
+ exit_event = create_event(0, 0, NULL);
+ if (one_process && !start_mutex)
+ start_mutex = ap_create_mutex(NULL);
+ /*
+ * In the future, the main will spawn off a couple
+ * of children and monitor them. As soon as a child
+ * exits, it spawns off a new one
+ */
+ if (child || one_process) {
+ if (!exit_event || !start_mutex)
+ exit(-1);
+ worker_main();
+ ap_destroy_mutex(start_mutex);
+ destroy_event(exit_event);
+ }
+ else
+ master_main(argc, argv);
+
+ clean_parent_exit(0);
+ return 0; /* purely to avoid a warning */
+}
+
+#endif /* ndef MULTITHREAD */
+
+#else /* ndef SHARED_CORE_TIESTATIC */
+
+/*
+** Standalone Tie Program for Shared Core support
+**
+** It's purpose is to tie the static libraries and
+** the shared core library under link-time and
+** passing execution control to the real main function
+** in the shared core library under run-time.
+*/
+
+extern int ap_main(int argc, char *argv[]);
+
+int main(int argc, char *argv[])
+{
+ return ap_main(argc, argv);
+}
+
+#endif /* ndef SHARED_CORE_TIESTATIC */
+#else /* ndef SHARED_CORE_BOOTSTRAP */
+
+#ifdef OS2
+/* Shared core loader for OS/2 */
+
+int ap_main(int argc, char *argv[]); /* Load time linked from libhttpd.dll */
+
+int main(int argc, char *argv[])
+{
+ return ap_main(argc, argv);
+}
+
+#else
+
+/*
+** Standalone Bootstrap Program for Shared Core support
+**
+** It's purpose is to initialise the LD_LIBRARY_PATH
+** environment variable therewith the Unix loader is able
+** to start the Standalone Tie Program (see above)
+** and then replacing itself with this program by
+** immediately passing execution to it.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "ap_config.h"
+#include "httpd.h"
+
+#if defined(HPUX) || defined(HPUX10) || defined(HPUX11)
+#define VARNAME "SHLIB_PATH"
+#else
+#define VARNAME "LD_LIBRARY_PATH"
+#endif
+
+#ifndef SHARED_CORE_DIR
+#define SHARED_CORE_DIR HTTPD_ROOT "/libexec"
+#endif
+
+#ifndef SHARED_CORE_EXECUTABLE_PROGRAM
+#define SHARED_CORE_EXECUTABLE_PROGRAM "lib" TARGET ".ep"
+#endif
+
+extern char *optarg;
+extern int optind;
+
+int main(int argc, char *argv[], char *envp[])
+{
+ char prog[MAX_STRING_LEN];
+ char llp_buf[MAX_STRING_LEN];
+ char **llp_slot;
+ char *llp_existing;
+ char *llp_dir;
+ char **envpnew;
+ int c, i, l;
+
+ /*
+ * parse argument line,
+ * but only handle the -L option
+ */
+ llp_dir = SHARED_CORE_DIR;
+ while ((c = getopt(argc, argv, "D:C:c:Xd:f:vVlLR:SZ:tTh")) != -1) {
+ switch (c) {
+ case 'D':
+ case 'C':
+ case 'c':
+ case 'X':
+ case 'd':
+ case 'f':
+ case 'v':
+ case 'V':
+ case 'l':
+ case 'L':
+ case 'S':
+ case 'Z':
+ case 't':
+ case 'T':
+ case 'h':
+ case '?':
+ break;
+ case 'R':
+ llp_dir = strdup(optarg);
+ break;
+ }
+ }
+
+ /*
+ * create path to SHARED_CORE_EXECUTABLE_PROGRAM
+ */
+ ap_snprintf(prog, sizeof(prog), "%s/%s", llp_dir, SHARED_CORE_EXECUTABLE_PROGRAM);
+
+ /*
+ * adjust process environment therewith the Unix loader
+ * is able to start the SHARED_CORE_EXECUTABLE_PROGRAM.
+ */
+ llp_slot = NULL;
+ llp_existing = NULL;
+ l = strlen(VARNAME);
+ for (i = 0; envp[i] != NULL; i++) {
+ if (strncmp(envp[i], VARNAME "=", l+1) == 0) {
+ llp_slot = &envp[i];
+ llp_existing = strchr(envp[i], '=') + 1;
+ }
+ }
+ if (llp_slot == NULL) {
+ envpnew = (char **)malloc(sizeof(char *)*(i + 2));
+ if (envpnew == NULL) {
+ fprintf(stderr, "Ouch! Out of memory generating envpnew!\n");
+ }
+ memcpy(envpnew, envp, sizeof(char *)*i);
+ envp = envpnew;
+ llp_slot = &envp[i++];
+ envp[i] = NULL;
+ }
+ if (llp_existing != NULL)
+ ap_snprintf(llp_buf, sizeof(llp_buf), "%s=%s:%s", VARNAME, llp_dir, llp_existing);
+ else
+ ap_snprintf(llp_buf, sizeof(llp_buf), "%s=%s", VARNAME, llp_dir);
+ *llp_slot = strdup(llp_buf);
+
+ /*
+ * finally replace our process with
+ * the SHARED_CORE_EXECUTABLE_PROGRAM
+ */
+ if (execve(prog, argv, envp) == -1) {
+ fprintf(stderr,
+ "%s: Unable to exec Shared Core Executable Program `%s'\n",
+ argv[0], prog);
+ return 1;
+ }
+ else
+ return 0;
+}
+
+#endif /* def OS2 */
+#endif /* ndef SHARED_CORE_BOOTSTRAP */
+
+#ifndef SHARED_CORE_BOOTSTRAP
+/*
+ * Force ap_validate_password() into the image so that modules like
+ * mod_auth can use it even if they're dynamically loaded.
+ */
+void suck_in_ap_validate_password(void);
+void suck_in_ap_validate_password(void)
+{
+ ap_validate_password("a", "b");
+}
+#endif
+
+/* force Expat to be linked into the server executable */
+#if defined(USE_EXPAT) && !defined(SHARED_CORE_BOOTSTRAP)
+#include "xmlparse.h"
+const XML_LChar *suck_in_expat(void);
+const XML_LChar *suck_in_expat(void)
+{
+ return XML_ErrorString(XML_ERROR_NONE);
+}
+#endif /* USE_EXPAT */
+
--- /dev/null
+/*
+ * Functions to handle interacting with the Win32 registry
+ */
+
+/*
+ * Apache registry key structure
+ *
+ * Apache's registry information is stored in the HKEY_LOCAL_MACHINE
+ * key, under
+ *
+ * HKLM\SOFTWARE\Apache Group\Apache\version
+ *
+ * These keys are defined in this file. The definition of the "version" part
+ * will need updating each time Apache moves from beta to non-beta or from a
+ * release to a development or beta version.
+ */
+
+/* To allow for multiple services, store the configuration file's full path
+ * under each service entry:
+ *
+ * HKLM\System\CurrentControlSet\Services\[service name]\Parameters\ConfPath
+ *
+ * The default configuration path (for console apache) is still stored:
+ *
+ * HKLM\Software\[Vendor]\[Software]\[Version]\ServerRoot
+ */
+
+#include <windows.h>
+#include <stdio.h>
+
+#include "httpd.h"
+#include "http_log.h"
+
+/* Define where the Apache values are stored in the registry. In general
+ * VERSION will be the same across all beta releases for a particular
+ * major release, but will change when the final release is made.
+ */
+
+#define VENDOR "Apache Group"
+#define SOFTWARE "Apache"
+#define VERSION "1.3.9"
+
+#define REGKEY "SOFTWARE\\" VENDOR "\\" SOFTWARE "\\" VERSION
+
+#define SERVICEKEYPRE "System\\CurrentControlSet\\Services\\"
+#define SERVICEKEYPOST "\\Parameters"
+
+/*
+ * The Windows API registry key functions don't set the last error
+ * value (the windows equivalent of errno). So we need to set it
+ * with SetLastError() before calling the aplog_error() function.
+ * Because this is common, let's have a macro.
+ */
+#define do_error(rv,fmt,arg) do { \
+ SetLastError(rv); \
+ ap_log_error(APLOG_MARK, APLOG_WIN32ERROR|APLOG_ERR, NULL, fmt,arg); \
+ } while (0);
+
+/*
+ * Get the data for registry key value. This is a generic function that
+ * can either get a value into a caller-supplied buffer, or it can
+ * allocate space for the value from the pass-in pool. It will normally
+ * be used by other functions within this file to get specific key values
+ * (e.g. registry_get_server_root()). This function returns a number of
+ * different error statuses, allowing the caller to differentiate
+ * between a key or value not existing and other kinds of errors. Depending
+ * on the type of data being obtained the caller can then either ignore
+ * the key-not-existing error, or treat it as a real error.
+ *
+ * If ppValue is NULL, allocate space for the value and return it in
+ * *pValue. The return value is the number of bytes in the value.
+ * The first argument is the pool to use to allocate space for the value.
+ *
+ * If pValue is not NULL, assume it is a buffer of nSizeValue bytes,
+ * and write the value into the buffer. The return value is the number
+ * of bytes in the value (so if the return value is greater than
+ * the supplied nSizeValue, the caller knows that *pValue is truncated).
+ * The pool argument is ignored.
+ *
+ * The return value is the number of bytes in the successfully retreived
+ * key if everything worked, or:
+ *
+ * -1 the key does not exists
+ * -2 if out of memory during the function
+ * -3 if the buffer specified by *pValue/nSizeValue was not large enough
+ * for the value.
+ * -4 if an error occurred
+ *
+ * If the return value is negative a message will be logged to the error
+ * log (aplog_error) function. If the return value is -2, -3 or -4 the message
+ * will be logged at priority "error", while if the return value is -1 the
+ * message will be logged at priority "warning".
+ */
+
+static int ap_registry_get_key_int(pool *p, char *key, char *name, char *pBuffer, int nSizeBuffer, char **ppValue)
+{
+ long rv;
+ HKEY hKey;
+ char *pValue;
+ int nSize;
+ int retval;
+
+ rv = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
+ key,
+ 0,
+ KEY_READ,
+ &hKey);
+
+ if (rv == ERROR_FILE_NOT_FOUND) {
+ ap_log_error(APLOG_MARK,APLOG_WARNING|APLOG_NOERRNO,NULL,
+ "Registry does not contain key %s",key);
+ return -1;
+ }
+ if (rv != ERROR_SUCCESS) {
+ do_error(rv, "RegOpenKeyEx HKLM\\%s",key);
+ return -4;
+ }
+
+ if (pBuffer == NULL) {
+ /* Find the size required for the data by passing NULL as the buffer
+ * pointer. On return nSize will contain the size required for the
+ * buffer if the return value is ERROR_SUCCESS.
+ */
+ rv = RegQueryValueEx(hKey,
+ name, /* key name */
+ NULL, /* reserved */
+ NULL, /* type */
+ NULL, /* for value */
+ &nSize); /* for size of "value" */
+
+ if (rv != ERROR_SUCCESS) {
+ do_error(rv, "RegQueryValueEx(key %s)", key);
+ return -1;
+ }
+
+ pValue = ap_palloc(p, nSize);
+ *ppValue = pValue;
+ if (!pValue) {
+ /* Eek, out of memory, probably not worth trying to carry on,
+ * but let's give it a go
+ */
+ ap_log_error(APLOG_MARK,APLOG_ERR|APLOG_NOERRNO,NULL,
+ "Error getting registry key: out of memory");
+ return -2;
+ }
+ }
+ else {
+ /* Get the value into the existing buffer of length nSizeBuffer */
+ pValue = pBuffer;
+ nSize = nSizeBuffer;
+ }
+
+ rv = RegQueryValueEx(hKey,
+ name, /* key name */
+ NULL, /* reserved */
+ NULL, /* type */
+ pValue, /* for value */
+ &nSize); /* for size of "value" */
+
+ retval = 0; /* Return value */
+
+ if (rv == ERROR_FILE_NOT_FOUND) {
+ ap_log_error(APLOG_MARK,APLOG_WARNING|APLOG_NOERRNO,NULL,
+ "Registry does not contain value %s\\%s", key, name);
+ retval = -1;
+ }
+ else if (rv == ERROR_MORE_DATA) {
+ /* This should only happen if we got passed a pre-existing buffer
+ * (pBuffer, nSizeBuffer). But I suppose it could also happen if we
+ * allocate a buffer if another process changed the length of the
+ * value since we found out its length above. Umm.
+ */
+ ap_log_error(APLOG_MARK,APLOG_ERR|APLOG_NOERRNO,NULL,
+ "Error getting registry value %s: buffer not big enough", key);
+ retval = -3;
+ }
+ else if (rv != ERROR_SUCCESS) {
+ do_error(rv, "RegQueryValueEx(key %s)", key);
+ retval = -4;
+ }
+
+ rv = RegCloseKey(hKey);
+ if (rv != ERROR_SUCCESS) {
+ do_error(rv, "RegCloseKey HKLM\\%s", key);
+ if (retval == 0) {
+ /* Keep error status from RegQueryValueEx, if any */
+ retval = -4;
+ }
+ }
+
+ return retval < 0 ? retval : nSize;
+}
+
+/*
+ * Get the server root from the registry into 'dir' which is
+ * size bytes long. Returns 0 if the server root was found
+ * or if the serverroot key does not exist (in which case
+ * dir will contain an empty string), or -1 if there was
+ * an error getting the key.
+ */
+
+int ap_registry_get_server_root(pool *p, char *dir, int size)
+{
+ int rv;
+
+ rv = ap_registry_get_key_int(p, REGKEY, "ServerRoot", dir, size, NULL);
+ if (rv < 0) {
+ dir[0] = '\0';
+ }
+
+ return (rv < -1) ? -1 : 0;
+}
+
+char *ap_get_service_key(char *service_name)
+{
+ char *key = malloc(strlen(SERVICEKEYPRE) +
+ strlen(service_name) +
+ strlen(SERVICEKEYPOST) + 1);
+
+ sprintf(key,"%s%s%s", SERVICEKEYPRE, service_name, SERVICEKEYPOST);
+
+ return(key);
+}
+
+int ap_registry_get_service_conf(pool *p, char *dir, int size, char *service_name)
+{
+ int rv;
+ char *key = ap_get_service_key(service_name);
+
+ rv = ap_registry_get_key_int(p, key, "ConfPath", dir, size, NULL);
+ if (rv < 0) {
+ dir[0] = '\0';
+ }
+
+ free(key);
+ return (rv < -1) ? -1 : 0;
+}
+
+/**********************************************************************
+ * The rest of this file deals with storing keys or values in the registry
+ */
+
+char *ap_registry_parse_key(int index, char *key)
+{
+ char *head = key, *skey;
+ int i;
+
+ if(!key)
+ return(NULL);
+
+ for(i = 0; i <= index; i++)
+ {
+ if(key && key[0] == '\\')
+ key++;
+ if (!key)
+ return(NULL);
+ head = key;
+ key = strchr(head, '\\');
+ }
+
+ if(!key)
+ return(strdup(head));
+ *key = '\0';
+ skey = strdup(head);
+ *key = '\\';
+ return(skey);
+}
+
+/*
+ * ap_registry_create_apache_key() creates the Apache registry key
+ * (HLKM\SOFTWARE\Apache Group\Apache\version, as defined at the start
+ * of this file), if it does not already exist. It will be called by
+ * ap_registry_store_key_int() if it cannot open this key. This
+ * function is intended to be called by ap_registry_store_key_int() if
+ * the Apache key does not exist when it comes to store a data item.
+ *
+ * Returns 0 on success or -1 on error. If -1 is returned, the error will
+ * already have been logged.
+ */
+
+static int ap_registry_create_key(char *longkey)
+{
+ int index;
+ HKEY hKey;
+ HKEY hKeyNext;
+ int retval;
+ int rv;
+ char *key;
+
+ hKey = HKEY_LOCAL_MACHINE;
+ index = 0;
+ retval = 0;
+
+ /* Walk the tree, creating at each stage if necessary */
+ while (key=ap_registry_parse_key(index,longkey)) {
+ int result;
+
+ rv = RegCreateKeyEx(hKey,
+ key, /* subkey */
+ 0, /* reserved */
+ NULL, /* class */
+ REG_OPTION_NON_VOLATILE,
+ KEY_WRITE,
+ NULL,
+ &hKeyNext,
+ &result);
+ if (rv != ERROR_SUCCESS) {
+ do_error(rv, "RegCreateKeyEx(%s)", longkey);
+ retval = -4;
+ }
+
+ /* Close the old key */
+ rv = RegCloseKey(hKey);
+ if (rv != ERROR_SUCCESS) {
+ do_error(rv, "RegCloseKey", NULL);
+ if (retval == 0) {
+ /* Keep error status from RegCreateKeyEx, if any */
+ retval = -4;
+ }
+ }
+
+ if (retval) {
+ break;
+ }
+
+ free(key);
+ hKey = hKeyNext;
+ index++;
+ }
+
+ if (!key) {
+ /* Close the final key we opened, if we walked the entire
+ * tree
+ */
+ rv = RegCloseKey(hKey);
+ if (rv != ERROR_SUCCESS) {
+ do_error(rv, "RegCloseKey", NULL);
+ if (retval == 0) {
+ /* Keep error status from RegCreateKeyEx, if any */
+ retval = -4;
+ }
+ }
+ }
+ else
+ free(key);
+
+ return retval;
+}
+
+/*
+ * ap_registry_store_key_int() stores a value name and value under the
+ * Apache registry key. If the Apache key does not exist it is created
+ * first. This function is intended to be called from a wrapper function
+ * in this file to set particular data values, such as
+ * ap_registry_set_server_root() below.
+ *
+ * Returns 0 if the value name and data was stored successfully, or
+ * returns -1 if the Apache key does not exist (since we try to create
+ * this key, this should never happen), or -4 if any other error occurred
+ * (these values are consistent with ap_registry_get_key_int()).
+ * If the return value is negative then the error will already have been
+ * logged via aplog_error().
+ */
+
+static int ap_registry_store_key_int(char *key, char *name, DWORD type, void *value, int value_size)
+{
+ long rv;
+ HKEY hKey;
+ int retval;
+
+ rv = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
+ key,
+ 0,
+ KEY_WRITE,
+ &hKey);
+
+ if (rv == ERROR_FILE_NOT_FOUND) {
+ /* Key could not be opened -- try to create it
+ */
+ if (ap_registry_create_key(key) < 0) {
+ /* Creation failed (error already reported) */
+ return -4;
+ }
+
+ /* Now it has been created we should be able to open it
+ */
+ rv = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
+ key,
+ 0,
+ KEY_WRITE,
+ &hKey);
+
+ if (rv == ERROR_FILE_NOT_FOUND) {
+ ap_log_error(APLOG_MARK,APLOG_WARNING|APLOG_NOERRNO,NULL,
+ "Registry does not contain key %s after creation",key);
+ return -1;
+ }
+ }
+
+ if (rv != ERROR_SUCCESS) {
+ do_error(rv, "RegOpenKeyEx HKLM\\%s", key);
+ return -4;
+ }
+
+ /* Now set the value and data */
+ rv = RegSetValueEx(hKey,
+ name, /* value key name */
+ 0, /* reserved */
+ type, /* type */
+ value, /* value data */
+ (DWORD)value_size); /* for size of "value" */
+
+ retval = 0; /* Return value */
+
+ if (rv != ERROR_SUCCESS) {
+ do_error(rv, "RegQueryValueEx(key %s)", key);
+ retval = -4;
+ }
+ else {
+ ap_log_error(APLOG_MARK,APLOG_INFO|APLOG_NOERRNO,NULL,
+ "Registry stored HKLM\\" REGKEY "\\%s value %s", key,
+ type == REG_SZ ? value : "(not displayable)");
+ }
+
+ /* Make sure we close the key even if there was an error storing
+ * the data
+ */
+ rv = RegCloseKey(hKey);
+ if (rv != ERROR_SUCCESS) {
+ do_error(rv, "RegCloseKey HKLM\\%s", key);
+ if (retval == 0) {
+ /* Keep error status from RegQueryValueEx, if any */
+ retval = -4;
+ }
+ }
+
+ return retval;
+}
+
+/*
+ * Sets the service confpath value within the registry. Returns 0 on success
+ * or -1 on error. If -1 is return the error will already have been
+ * logged via aplog_error().
+ */
+
+int ap_registry_set_service_conf(char *conf, char *service_name)
+{
+ int rv;
+ char *key = ap_get_service_key(service_name);
+
+ rv = ap_registry_store_key_int(key, "ConfPath", REG_SZ, conf, strlen(conf)+1);
+ free(key);
+
+ return rv < 0 ? -1: 0;
+}
+
+/*
+ * Sets the serverroot value within the registry. Returns 0 on success
+ * or -1 on error. If -1 is return the error will already have been
+ * logged via aplog_error().
+ */
+
+int ap_registry_set_server_root(char *dir)
+{
+ int rv;
+
+ rv = ap_registry_store_key_int(REGKEY, "ServerRoot", REG_SZ, dir, strlen(dir)+1);
+
+ return rv < 0 ? -1 : 0;
+}
+
--- /dev/null
+#ifdef WIN32
+
+#include <windows.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <process.h>
+#include <direct.h>
+
+#include "httpd.h"
+#include "http_conf_globals.h"
+#include "http_log.h"
+#include "http_main.h"
+#include "multithread.h"
+#include "service.h"
+#include "registry.h"
+
+static struct
+{
+ int (*main_fn)(int, char **);
+ event *stop_event;
+ int connected;
+ SERVICE_STATUS_HANDLE hServiceStatus;
+ char *name;
+ int exit_status;
+ SERVICE_STATUS ssStatus;
+ FILE *logFile;
+} globdat;
+
+static void WINAPI service_main_fn(DWORD, LPTSTR *);
+static void WINAPI service_ctrl(DWORD ctrlCode);
+static int ReportStatusToSCMgr(int currentState, int exitCode, int waitHint);
+static int ap_start_service(SC_HANDLE);
+static int ap_stop_service(SC_HANDLE);
+
+int service_main(int (*main_fn)(int, char **), int argc, char **argv )
+{
+ SERVICE_TABLE_ENTRY dispatchTable[] =
+ {
+ { "", service_main_fn },
+ { NULL, NULL }
+ };
+
+ globdat.main_fn = main_fn;
+ globdat.stop_event = create_event(0, 0, "apache-signal");
+ globdat.connected = 1;
+
+ if(!StartServiceCtrlDispatcher(dispatchTable))
+ {
+ /* This is a genuine failure of the SCM. */
+ ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_WIN32ERROR, NULL,
+ "Error starting service control dispatcher");
+ return(globdat.exit_status);
+ }
+ else
+ {
+ return(globdat.exit_status);
+ }
+}
+
+void service_cd()
+{
+ /* change to the drive with the executable */
+ char buf[300];
+ GetModuleFileName(NULL, buf, 300);
+ buf[2] = 0;
+ chdir(buf);
+}
+
+void __stdcall service_main_fn(DWORD argc, LPTSTR *argv)
+{
+ ap_server_argv0 = globdat.name = argv[0];
+
+ if(!(globdat.hServiceStatus = RegisterServiceCtrlHandler( globdat.name, service_ctrl)))
+ {
+ ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_WIN32ERROR, NULL,
+ "Failure registering service handler");
+ return;
+ }
+
+ ReportStatusToSCMgr(
+ SERVICE_START_PENDING, // service state
+ NO_ERROR, // exit code
+ 3000); // wait hint
+
+ service_cd();
+ if( service_init() )
+ /* Arguments are ok except for \! */
+ globdat.exit_status = (*globdat.main_fn)( argc, argv );
+
+ ReportStatusToSCMgr(SERVICE_STOPPED, NO_ERROR, 0);
+
+ return;
+}
+
+void service_set_status(int status)
+{
+ ReportStatusToSCMgr(status, NO_ERROR, 3000);
+}
+
+
+
+//
+// FUNCTION: service_ctrl
+//
+// PURPOSE: This function is called by the SCM whenever
+// ControlService() is called on this service.
+//
+// PARAMETERS:
+// dwCtrlCode - type of control requested
+//
+// RETURN VALUE:
+// none
+//
+// COMMENTS:
+//
+VOID WINAPI service_ctrl(DWORD dwCtrlCode)
+{
+ int state;
+
+
+ state = globdat.ssStatus.dwCurrentState;
+ switch(dwCtrlCode)
+ {
+ // Stop the service.
+ //
+ case SERVICE_CONTROL_STOP:
+ state = SERVICE_STOP_PENDING;
+ ap_start_shutdown();
+ break;
+
+ // Update the service status.
+ //
+ case SERVICE_CONTROL_INTERROGATE:
+ break;
+
+ // invalid control code
+ //
+ default:
+ break;
+
+ }
+
+ ReportStatusToSCMgr(state, NO_ERROR, 0);
+}
+
+
+int ReportStatusToSCMgr(int currentState, int exitCode, int waitHint)
+{
+ static int firstTime = 1;
+ static int checkPoint = 1;
+ int rv;
+
+ if(firstTime)
+ {
+ firstTime = 0;
+ globdat.ssStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
+ globdat.ssStatus.dwServiceSpecificExitCode = 0;
+ globdat.ssStatus.dwCheckPoint = 1;
+ }
+
+ if(globdat.connected)
+ {
+ if (currentState == SERVICE_START_PENDING)
+ globdat.ssStatus.dwControlsAccepted = 0;
+ else
+ globdat.ssStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
+
+ globdat.ssStatus.dwCurrentState = currentState;
+ globdat.ssStatus.dwWin32ExitCode = exitCode;
+ if(waitHint)
+ globdat.ssStatus.dwWaitHint = waitHint;
+
+ if ( ( currentState == SERVICE_RUNNING ) ||
+ ( currentState == SERVICE_STOPPED ) )
+ {
+ globdat.ssStatus.dwWaitHint = 0;
+ globdat.ssStatus.dwCheckPoint = 0;
+ }
+ else
+ globdat.ssStatus.dwCheckPoint = ++checkPoint;
+
+ rv = SetServiceStatus(globdat.hServiceStatus, &globdat.ssStatus);
+
+ }
+ return(1);
+}
+
+void InstallService(char *service_name, char *conf)
+{
+ SC_HANDLE schService;
+ SC_HANDLE schSCManager;
+
+ TCHAR szPath[512];
+ TCHAR szQuotedPath[512];
+
+ printf("Installing the %s service to use %s\n", service_name, conf);
+
+ if (GetModuleFileName( NULL, szPath, 512 ) == 0)
+ {
+ ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_WIN32ERROR, NULL,
+ "GetModuleFileName failed");
+ return;
+ }
+
+ ap_snprintf(szQuotedPath, 512, "\"%s\"", szPath);
+
+ schSCManager = OpenSCManager(
+ NULL, // machine (NULL == local)
+ NULL, // database (NULL == default)
+ SC_MANAGER_ALL_ACCESS // access required
+ );
+ if (!schSCManager) {
+ ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_WIN32ERROR, NULL,
+ "OpenSCManager failed");
+ }
+ else {
+ schService = CreateService(
+ schSCManager, // SCManager database
+ service_name, // name of service
+ service_name, // name to display
+ SERVICE_ALL_ACCESS, // desired access
+ SERVICE_WIN32_OWN_PROCESS, // service type
+ SERVICE_AUTO_START, // start type
+ SERVICE_ERROR_NORMAL, // error control type
+ szQuotedPath, // service's binary
+ NULL, // no load ordering group
+ NULL, // no tag identifier
+ NULL, // dependencies
+ NULL, // LocalSystem account
+ NULL); // no password
+
+ if (schService) {
+ CloseServiceHandle(schService);
+
+ /* Now store the server_root in the registry */
+ if(!ap_registry_set_service_conf(conf, service_name))
+ printf("The %s service has been installed successfully.\n", service_name );
+ }
+ else {
+ ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_WIN32ERROR, NULL,
+ "CreateService failed");
+ }
+
+ CloseServiceHandle(schSCManager);
+ }
+}
+
+
+void RemoveService(char *service_name)
+{
+ SC_HANDLE schService;
+ SC_HANDLE schSCManager;
+
+ printf("Removing the %s service\n", service_name);
+
+ schSCManager = OpenSCManager(
+ NULL, // machine (NULL == local)
+ NULL, // database (NULL == default)
+ SC_MANAGER_ALL_ACCESS // access required
+ );
+ if (!schSCManager) {
+ ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_WIN32ERROR, NULL,
+ "OpenSCManager failed");
+ }
+ else {
+ schService = OpenService(schSCManager, service_name, SERVICE_ALL_ACCESS);
+
+ if (schService == NULL) {
+ /* Could not open the service */
+ ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_WIN32ERROR, NULL,
+ "OpenService failed");
+ }
+ else {
+ /* try to stop the service */
+ ap_stop_service(schService);
+
+ // now remove the service
+ if (DeleteService(schService) == 0)
+ ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_WIN32ERROR, NULL,
+ "DeleteService failed");
+ else
+ printf("The %s service has been removed successfully.\n", service_name );
+ CloseServiceHandle(schService);
+ }
+ /* SCM removes registry parameters */
+ CloseServiceHandle(schSCManager);
+ }
+
+}
+
+/* A hack to determine if we're running as a service without waiting for
+ * the SCM to fail; if AllocConsole succeeds, we're a service.
+ */
+
+BOOL isProcessService() {
+ if( !AllocConsole() )
+ return FALSE;
+ FreeConsole();
+ return TRUE;
+}
+
+/* Determine is service_name is a valid service
+ */
+
+BOOL isValidService(char *service_name) {
+ SC_HANDLE schSCM, schSVC;
+ int Err;
+
+ if (!(schSCM = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS))) {
+ ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_WIN32ERROR, NULL,
+ "OpenSCManager failed");
+ return FALSE;
+ }
+
+ if ((schSVC = OpenService(schSCM, service_name, SERVICE_ALL_ACCESS))) {
+ CloseServiceHandle(schSVC);
+ CloseServiceHandle(schSCM);
+ return TRUE;
+ }
+
+ Err = GetLastError();
+ if (Err != ERROR_SERVICE_DOES_NOT_EXIST && Err != ERROR_INVALID_NAME)
+ ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_WIN32ERROR, NULL,
+ "OpenService failed");
+
+ return FALSE;
+}
+
+int send_signal_to_service(char *service_name, char *sig) {
+ SC_HANDLE schService;
+ SC_HANDLE schSCManager;
+ int success = FALSE;
+
+ enum { start, restart, stop, unknown } action;
+ static char *param[] = { "start", "restart", "shutdown" };
+ static char *participle[] = { "starting", "restarting", "stopping" };
+ static char *past[] = { "started", "restarted", "stopped" };
+
+ for (action = start; action < unknown; action++)
+ if (!strcasecmp(sig, param[action]))
+ break;
+
+ if (action == unknown) {
+ printf("signal must be start, restart, or shutdown\n");
+ return FALSE;
+ }
+
+ schSCManager = OpenSCManager(
+ NULL, // machine (NULL == local)
+ NULL, // database (NULL == default)
+ SC_MANAGER_ALL_ACCESS // access required
+ );
+ if (!schSCManager) {
+ ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_WIN32ERROR, NULL,
+ "OpenSCManager failed");
+ }
+ else {
+ schService = OpenService(schSCManager, service_name, SERVICE_ALL_ACCESS);
+
+ if (schService == NULL) {
+ /* Could not open the service */
+ ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_WIN32ERROR, NULL,
+ "OpenService failed");
+ }
+ else {
+ if (!QueryServiceStatus(schService, &globdat.ssStatus))
+ ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_WIN32ERROR, NULL,
+ "QueryService failed");
+ else {
+ if (globdat.ssStatus.dwCurrentState == SERVICE_STOPPED && action == stop)
+ printf("The %s service is not started.\n", service_name);
+ else if (globdat.ssStatus.dwCurrentState == SERVICE_RUNNING && action == start)
+ printf("The %s service has already been started.\n", service_name);
+ else {
+ printf("The %s service is %s.\n", service_name, participle[action]);
+
+ if (action == stop || action == restart)
+ success = ap_stop_service(schService);
+ if (action == start || action == restart)
+ success = ap_start_service(schService);
+
+ if( success )
+ printf("The %s service has %s.\n", service_name, past[action]);
+ else
+ printf("Failed to %s the %s service.\n", sig, service_name );
+ }
+
+ CloseServiceHandle(schService);
+ }
+ }
+ /* SCM removes registry parameters */
+ CloseServiceHandle(schSCManager);
+ }
+ return success;
+}
+
+int ap_stop_service(SC_HANDLE schService)
+{
+ if (ControlService(schService, SERVICE_CONTROL_STOP, &globdat.ssStatus)) {
+ Sleep(1000);
+ while (QueryServiceStatus(schService, &globdat.ssStatus)) {
+ if (globdat.ssStatus.dwCurrentState == SERVICE_STOP_PENDING)
+ Sleep(1000);
+ else
+ break;
+ }
+ }
+ if (QueryServiceStatus(schService, &globdat.ssStatus))
+ if (globdat.ssStatus.dwCurrentState == SERVICE_STOPPED)
+ return TRUE;
+ return FALSE;
+}
+
+int ap_start_service(SC_HANDLE schService) {
+ if (StartService(schService, 0, NULL)) {
+ Sleep(1000);
+ while(QueryServiceStatus(schService, &globdat.ssStatus)) {
+ if(globdat.ssStatus.dwCurrentState == SERVICE_START_PENDING)
+ Sleep(1000);
+ else
+ break;
+ }
+ }
+ if (QueryServiceStatus(schService, &globdat.ssStatus))
+ if (globdat.ssStatus.dwCurrentState == SERVICE_RUNNING)
+ return TRUE;
+ return FALSE;
+}
+
+#endif /* WIN32 */
+
--- /dev/null
+/* ====================================================================
+ * Copyright (c) 1995-1999 The Apache Group. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * 4. The names "Apache Server" and "Apache Group" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission. For written permission, please contact
+ * apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ * nor may "Apache" appear in their names without prior written
+ * permission of the Apache Group.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Group and was originally based
+ * on public domain software written at the National Center for
+ * Supercomputing Applications, University of Illinois, Urbana-Champaign.
+ * For more information on the Apache Group and the Apache HTTP server
+ * project, please see <http://www.apache.org/>.
+ *
+ */
+
+/*
+ * rfc1413() speaks a common subset of the RFC 1413, AUTH, TAP and IDENT
+ * protocols. The code queries an RFC 1413 etc. compatible daemon on a remote
+ * host to look up the owner of a connection. The information should not be
+ * used for authentication purposes. This routine intercepts alarm signals.
+ *
+ * Diagnostics are reported through syslog(3).
+ *
+ * Author: Wietse Venema, Eindhoven University of Technology,
+ * The Netherlands.
+ */
+
+/* Some small additions for Apache --- ditch the "sccsid" var if
+ * compiling with gcc (it *has* changed), include ap_config.h for the
+ * prototypes it defines on at least one system (SunlOSs) which has
+ * them missing from the standard header files, and one minor change
+ * below (extra parens around assign "if (foo = bar) ..." to shut up
+ * gcc -Wall).
+ */
+
+/* Rewritten by David Robinson */
+
+#include "httpd.h" /* for server_rec, conn_rec, ap_longjmp, etc. */
+#include "http_log.h" /* for aplog_error */
+#include "rfc1413.h"
+#include "http_main.h" /* set_callback_and_alarm */
+
+/* Local stuff. */
+/* Semi-well-known port */
+#define RFC1413_PORT 113
+/* maximum allowed length of userid */
+#define RFC1413_USERLEN 512
+/* rough limit on the amount of data we accept. */
+#define RFC1413_MAXDATA 1000
+
+#ifndef RFC1413_TIMEOUT
+#define RFC1413_TIMEOUT 30
+#endif
+#define ANY_PORT 0 /* Any old port will do */
+#define FROM_UNKNOWN "unknown"
+
+int ap_rfc1413_timeout = RFC1413_TIMEOUT; /* Global so it can be changed */
+
+static JMP_BUF timebuf;
+
+/* bind_connect - bind both ends of a socket */
+/* Ambarish fix this. Very broken */
+static int get_rfc1413(int sock, const struct sockaddr_in *our_sin,
+ const struct sockaddr_in *rmt_sin,
+ char user[RFC1413_USERLEN+1], server_rec *srv)
+{
+ struct sockaddr_in rmt_query_sin, our_query_sin;
+ unsigned int rmt_port, our_port;
+ int i;
+ char *cp;
+ char buffer[RFC1413_MAXDATA + 1];
+ int buflen;
+
+ /*
+ * Bind the local and remote ends of the query socket to the same
+ * IP addresses as the connection under investigation. We go
+ * through all this trouble because the local or remote system
+ * might have more than one network address. The RFC1413 etc.
+ * client sends only port numbers; the server takes the IP
+ * addresses from the query socket.
+ */
+
+ our_query_sin = *our_sin;
+ our_query_sin.sin_port = htons(ANY_PORT);
+ rmt_query_sin = *rmt_sin;
+ rmt_query_sin.sin_port = htons(RFC1413_PORT);
+
+ if (bind(sock, (struct sockaddr *) &our_query_sin,
+ sizeof(struct sockaddr_in)) < 0) {
+ ap_log_error(APLOG_MARK, APLOG_CRIT, srv,
+ "bind: rfc1413: Error binding to local port");
+ return -1;
+ }
+
+/*
+ * errors from connect usually imply the remote machine doesn't support
+ * the service
+ */
+ if (connect(sock, (struct sockaddr *) &rmt_query_sin,
+ sizeof(struct sockaddr_in)) < 0)
+ return -1;
+
+/* send the data */
+ buflen = ap_snprintf(buffer, sizeof(buffer), "%u,%u\r\n", ntohs(rmt_sin->sin_port),
+ ntohs(our_sin->sin_port));
+
+ /* send query to server. Handle short write. */
+#ifdef CHARSET_EBCDIC
+ ebcdic2ascii(&buffer, &buffer, buflen);
+#endif
+ i = 0;
+ while(i < strlen(buffer)) {
+ int j;
+ j = write(sock, buffer+i, (strlen(buffer+i)));
+ if (j < 0 && errno != EINTR) {
+ ap_log_error(APLOG_MARK, APLOG_CRIT, srv,
+ "write: rfc1413: error sending request");
+ return -1;
+ }
+ else if (j > 0) {
+ i+=j;
+ }
+ }
+
+ /*
+ * Read response from server. - the response should be newline
+ * terminated according to rfc - make sure it doesn't stomp it's
+ * way out of the buffer.
+ */
+
+ i = 0;
+ memset(buffer, '\0', sizeof(buffer));
+ /*
+ * Note that the strchr function below checks for 10 instead of '\n'
+ * this allows it to work on both ASCII and EBCDIC machines.
+ */
+ while((cp = strchr(buffer, '\012')) == NULL && i < sizeof(buffer) - 1) {
+ int j;
+ j = read(sock, buffer+i, (sizeof(buffer) - 1) - i);
+ if (j < 0 && errno != EINTR) {
+ ap_log_error(APLOG_MARK, APLOG_CRIT, srv,
+ "read: rfc1413: error reading response");
+ return -1;
+ }
+ else if (j > 0) {
+ i+=j;
+ }
+ }
+
+/* RFC1413_USERLEN = 512 */
+#ifdef CHARSET_EBCDIC
+ ascii2ebcdic(&buffer, &buffer, (size_t)i);
+#endif
+ if (sscanf(buffer, "%u , %u : USERID :%*[^:]:%512s", &rmt_port, &our_port,
+ user) != 3 || ntohs(rmt_sin->sin_port) != rmt_port
+ || ntohs(our_sin->sin_port) != our_port)
+ return -1;
+
+ /*
+ * Strip trailing carriage return. It is part of the
+ * protocol, not part of the data.
+ */
+
+ if ((cp = strchr(user, '\r')))
+ *cp = '\0';
+
+ return 0;
+}
+
+/* ident_timeout - handle timeouts */
+static void ident_timeout(int sig)
+{
+ ap_longjmp(timebuf, sig);
+}
+
+/* rfc1413 - return remote user name, given socket structures */
+char *ap_rfc1413(conn_rec *conn, server_rec *srv)
+{
+ static char user[RFC1413_USERLEN + 1]; /* XXX */
+ static char *result;
+ static int sock;
+
+ result = FROM_UNKNOWN;
+
+ sock = ap_psocket(conn->pool, AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (sock < 0) {
+ ap_log_error(APLOG_MARK, APLOG_CRIT, srv,
+ "socket: rfc1413: error creating socket");
+ conn->remote_logname = result;
+ }
+
+ /*
+ * Set up a timer so we won't get stuck while waiting for the server.
+ */
+ if (ap_setjmp(timebuf) == 0) {
+ ap_set_callback_and_alarm(ident_timeout, ap_rfc1413_timeout);
+
+ if (get_rfc1413(sock, &conn->local_addr, &conn->remote_addr, user, srv) >= 0)
+ result = user;
+ }
+ ap_set_callback_and_alarm(NULL, 0);
+ ap_pclosesocket(conn->pool, sock);
+ conn->remote_logname = result;
+
+ return conn->remote_logname;
+}
--- /dev/null
+/* ====================================================================
+ * Copyright (c) 1995-1999 The Apache Group. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * 4. The names "Apache Server" and "Apache Group" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission. For written permission, please contact
+ * apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ * nor may "Apache" appear in their names without prior written
+ * permission of the Apache Group.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Group and was originally based
+ * on public domain software written at the National Center for
+ * Supercomputing Applications, University of Illinois, Urbana-Champaign.
+ * For more information on the Apache Group and the Apache HTTP server
+ * project, please see <http://www.apache.org/>.
+ *
+ */
+
+/*
+ * util.c: string utility things
+ *
+ * 3/21/93 Rob McCool
+ * 1995-96 Many changes by the Apache Group
+ *
+ */
+
+/* Debugging aid:
+ * #define DEBUG to trace all cfg_open*()/cfg_closefile() calls
+ * #define DEBUG_CFG_LINES to trace every line read from the config files
+ */
+
+#include "httpd.h"
+#include "http_conf_globals.h" /* for user_id & group_id */
+#include "http_log.h"
+#if defined(SUNOS4)
+/* stdio.h has been read in ap_config.h already. Add missing prototypes here: */
+extern int fgetc(FILE *);
+extern char *fgets(char *s, int, FILE*);
+extern int fclose(FILE *);
+#endif
+
+/* A bunch of functions in util.c scan strings looking for certain characters.
+ * To make that more efficient we encode a lookup table. The test_char_table
+ * is generated automatically by gen_test_char.c.
+ */
+#include "test_char.h"
+
+/* we assume the folks using this ensure 0 <= c < 256... which means
+ * you need a cast to (unsigned char) first, you can't just plug a
+ * char in here and get it to work, because if char is signed then it
+ * will first be sign extended.
+ */
+#define TEST_CHAR(c, f) (test_char_table[(unsigned)(c)] & (f))
+
+void ap_util_init(void)
+{
+ /* nothing to do... previously there was run-time initialization of
+ * test_char_table here
+ */
+}
+
+
+API_VAR_EXPORT const char ap_month_snames[12][4] =
+{
+ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+};
+API_VAR_EXPORT const char ap_day_snames[7][4] =
+{
+ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
+};
+
+API_EXPORT(char *) ap_get_time()
+{
+ time_t t;
+ char *time_string;
+
+ t = time(NULL);
+ time_string = ctime(&t);
+ time_string[strlen(time_string) - 1] = '\0';
+ return (time_string);
+}
+
+/*
+ * Examine a field value (such as a media-/content-type) string and return
+ * it sans any parameters; e.g., strip off any ';charset=foo' and the like.
+ */
+API_EXPORT(char *) ap_field_noparam(pool *p, const char *intype)
+{
+ const char *semi;
+
+ semi = strchr(intype, ';');
+ if (semi == NULL) {
+ return ap_pstrdup(p, intype);
+ }
+ else {
+ while ((semi > intype) && ap_isspace(semi[-1])) {
+ semi--;
+ }
+ return ap_pstrndup(p, intype, semi - intype);
+ }
+}
+
+API_EXPORT(char *) ap_ht_time(pool *p, time_t t, const char *fmt, int gmt)
+{
+ char ts[MAX_STRING_LEN];
+ char tf[MAX_STRING_LEN];
+ struct tm *tms;
+
+ tms = (gmt ? gmtime(&t) : localtime(&t));
+ if(gmt) {
+ /* Convert %Z to "GMT" and %z to "+0000";
+ * on hosts that do not have a time zone string in struct tm,
+ * strftime must assume its argument is local time.
+ */
+ const char *f;
+ char *strp;
+ for(strp = tf, f = fmt; strp < tf + sizeof(tf) - 6 && (*strp = *f)
+ ; f++, strp++) {
+ if (*f != '%') continue;
+ switch (f[1]) {
+ case '%':
+ *++strp = *++f;
+ break;
+ case 'Z':
+ *strp++ = 'G';
+ *strp++ = 'M';
+ *strp = 'T';
+ f++;
+ break;
+ case 'z': /* common extension */
+ *strp++ = '+';
+ *strp++ = '0';
+ *strp++ = '0';
+ *strp++ = '0';
+ *strp = '0';
+ f++;
+ break;
+ }
+ }
+ *strp = '\0';
+ fmt = tf;
+ }
+
+ /* check return code? */
+ strftime(ts, MAX_STRING_LEN, fmt, tms);
+ ts[MAX_STRING_LEN - 1] = '\0';
+ return ap_pstrdup(p, ts);
+}
+
+API_EXPORT(char *) ap_gm_timestr_822(pool *p, time_t sec)
+{
+ struct tm *tms;
+
+ tms = gmtime(&sec);
+
+ /* RFC date format; as strftime '%a, %d %b %Y %T GMT' */
+ return ap_psprintf(p,
+ "%s, %.2d %s %d %.2d:%.2d:%.2d GMT", ap_day_snames[tms->tm_wday],
+ tms->tm_mday, ap_month_snames[tms->tm_mon], tms->tm_year + 1900,
+ tms->tm_hour, tms->tm_min, tms->tm_sec);
+}
+
+/* What a pain in the ass. */
+#if defined(HAVE_GMTOFF)
+API_EXPORT(struct tm *) ap_get_gmtoff(int *tz)
+{
+ time_t tt = time(NULL);
+ struct tm *t;
+
+ t = localtime(&tt);
+ *tz = (int) (t->tm_gmtoff / 60);
+ return t;
+}
+#else
+API_EXPORT(struct tm *) ap_get_gmtoff(int *tz)
+{
+ time_t tt = time(NULL);
+ struct tm gmt;
+ struct tm *t;
+ int days, hours, minutes;
+
+ /* Assume we are never more than 24 hours away. */
+ gmt = *gmtime(&tt); /* remember gmtime/localtime return ptr to static */
+ t = localtime(&tt); /* buffer... so be careful */
+ days = t->tm_yday - gmt.tm_yday;
+ hours = ((days < -1 ? 24 : 1 < days ? -24 : days * 24)
+ + t->tm_hour - gmt.tm_hour);
+ minutes = hours * 60 + t->tm_min - gmt.tm_min;
+ *tz = minutes;
+ return t;
+}
+#endif
+
+/* Roy owes Rob beer. */
+/* Rob owes Roy dinner. */
+
+/* These legacy comments would make a lot more sense if Roy hadn't
+ * replaced the old later_than() routine with util_date.c.
+ *
+ * Well, okay, they still wouldn't make any sense.
+ */
+
+/* Match = 0, NoMatch = 1, Abort = -1
+ * Based loosely on sections of wildmat.c by Rich Salz
+ * Hmmm... shouldn't this really go component by component?
+ */
+API_EXPORT(int) ap_strcmp_match(const char *str, const char *exp)
+{
+ int x, y;
+
+ for (x = 0, y = 0; exp[y]; ++y, ++x) {
+ if ((!str[x]) && (exp[y] != '*'))
+ return -1;
+ if (exp[y] == '*') {
+ while (exp[++y] == '*');
+ if (!exp[y])
+ return 0;
+ while (str[x]) {
+ int ret;
+ if ((ret = ap_strcmp_match(&str[x++], &exp[y])) != 1)
+ return ret;
+ }
+ return -1;
+ }
+ else if ((exp[y] != '?') && (str[x] != exp[y]))
+ return 1;
+ }
+ return (str[x] != '\0');
+}
+
+API_EXPORT(int) ap_strcasecmp_match(const char *str, const char *exp)
+{
+ int x, y;
+
+ for (x = 0, y = 0; exp[y]; ++y, ++x) {
+ if ((!str[x]) && (exp[y] != '*'))
+ return -1;
+ if (exp[y] == '*') {
+ while (exp[++y] == '*');
+ if (!exp[y])
+ return 0;
+ while (str[x]) {
+ int ret;
+ if ((ret = ap_strcasecmp_match(&str[x++], &exp[y])) != 1)
+ return ret;
+ }
+ return -1;
+ }
+ else if ((exp[y] != '?') && (ap_tolower(str[x]) != ap_tolower(exp[y])))
+ return 1;
+ }
+ return (str[x] != '\0');
+}
+
+API_EXPORT(int) ap_is_matchexp(const char *str)
+{
+ register int x;
+
+ for (x = 0; str[x]; x++)
+ if ((str[x] == '*') || (str[x] == '?'))
+ return 1;
+ return 0;
+}
+
+/*
+ * Apache stub function for the regex libraries regexec() to make sure the
+ * whole regex(3) API is available through the Apache (exported) namespace.
+ * This is especially important for the DSO situations of modules.
+ * DO NOT MAKE A MACRO OUT OF THIS FUNCTION!
+ */
+API_EXPORT(int) ap_regexec(const regex_t *preg, const char *string,
+ size_t nmatch, regmatch_t pmatch[], int eflags)
+{
+ return regexec(preg, string, nmatch, pmatch, eflags);
+}
+
+API_EXPORT(size_t) ap_regerror(int errcode, const regex_t *preg, char *errbuf, size_t errbuf_size)
+{
+ return regerror(errcode, preg, errbuf, errbuf_size);
+}
+
+
+/* This function substitutes for $0-$9, filling in regular expression
+ * submatches. Pass it the same nmatch and pmatch arguments that you
+ * passed ap_regexec(). pmatch should not be greater than the maximum number
+ * of subexpressions - i.e. one more than the re_nsub member of regex_t.
+ *
+ * input should be the string with the $-expressions, source should be the
+ * string that was matched against.
+ *
+ * It returns the substituted string, or NULL on error.
+ *
+ * Parts of this code are based on Henry Spencer's regsub(), from his
+ * AT&T V8 regexp package.
+ */
+
+API_EXPORT(char *) ap_pregsub(pool *p, const char *input, const char *source,
+ size_t nmatch, regmatch_t pmatch[])
+{
+ const char *src = input;
+ char *dest, *dst;
+ char c;
+ size_t no;
+ int len;
+
+ if (!source)
+ return NULL;
+ if (!nmatch)
+ return ap_pstrdup(p, src);
+
+ /* First pass, find the size */
+
+ len = 0;
+
+ while ((c = *src++) != '\0') {
+ if (c == '&')
+ no = 0;
+ else if (c == '$' && ap_isdigit(*src))
+ no = *src++ - '0';
+ else
+ no = 10;
+
+ if (no > 9) { /* Ordinary character. */
+ if (c == '\\' && (*src == '$' || *src == '&'))
+ c = *src++;
+ len++;
+ }
+ else if (no < nmatch && pmatch[no].rm_so < pmatch[no].rm_eo) {
+ len += pmatch[no].rm_eo - pmatch[no].rm_so;
+ }
+
+ }
+
+ dest = dst = ap_pcalloc(p, len + 1);
+
+ /* Now actually fill in the string */
+
+ src = input;
+
+ while ((c = *src++) != '\0') {
+ if (c == '&')
+ no = 0;
+ else if (c == '$' && ap_isdigit(*src))
+ no = *src++ - '0';
+ else
+ no = 10;
+
+ if (no > 9) { /* Ordinary character. */
+ if (c == '\\' && (*src == '$' || *src == '&'))
+ c = *src++;
+ *dst++ = c;
+ }
+ else if (no < nmatch && pmatch[no].rm_so < pmatch[no].rm_eo) {
+ len = pmatch[no].rm_eo - pmatch[no].rm_so;
+ memcpy(dst, source + pmatch[no].rm_so, len);
+ dst += len;
+ }
+
+ }
+ *dst = '\0';
+
+ return dest;
+}
+
+/*
+ * Parse .. so we don't compromise security
+ */
+API_EXPORT(void) ap_getparents(char *name)
+{
+ int l, w;
+
+ /* Four paseses, as per RFC 1808 */
+ /* a) remove ./ path segments */
+
+ for (l = 0, w = 0; name[l] != '\0';) {
+ if (name[l] == '.' && name[l + 1] == '/' && (l == 0 || name[l - 1] == '/'))
+ l += 2;
+ else
+ name[w++] = name[l++];
+ }
+
+ /* b) remove trailing . path, segment */
+ if (w == 1 && name[0] == '.')
+ w--;
+ else if (w > 1 && name[w - 1] == '.' && name[w - 2] == '/')
+ w--;
+ name[w] = '\0';
+
+ /* c) remove all xx/../ segments. (including leading ../ and /../) */
+ l = 0;
+
+ while (name[l] != '\0') {
+ if (name[l] == '.' && name[l + 1] == '.' && name[l + 2] == '/' &&
+ (l == 0 || name[l - 1] == '/')) {
+ register int m = l + 3, n;
+
+ l = l - 2;
+ if (l >= 0) {
+ while (l >= 0 && name[l] != '/')
+ l--;
+ l++;
+ }
+ else
+ l = 0;
+ n = l;
+ while ((name[n] = name[m]))
+ (++n, ++m);
+ }
+ else
+ ++l;
+ }
+
+ /* d) remove trailing xx/.. segment. */
+ if (l == 2 && name[0] == '.' && name[1] == '.')
+ name[0] = '\0';
+ else if (l > 2 && name[l - 1] == '.' && name[l - 2] == '.' && name[l - 3] == '/') {
+ l = l - 4;
+ if (l >= 0) {
+ while (l >= 0 && name[l] != '/')
+ l--;
+ l++;
+ }
+ else
+ l = 0;
+ name[l] = '\0';
+ }
+}
+
+API_EXPORT(void) ap_no2slash(char *name)
+{
+ char *d, *s;
+
+ s = d = name;
+
+#ifdef WIN32
+ /* Check for UNC names. Leave leading two slashes. */
+ if (s[0] == '/' && s[1] == '/')
+ *d++ = *s++;
+#endif
+
+ while (*s) {
+ if ((*d++ = *s) == '/') {
+ do {
+ ++s;
+ } while (*s == '/');
+ }
+ else {
+ ++s;
+ }
+ }
+ *d = '\0';
+}
+
+
+/*
+ * copy at most n leading directories of s into d
+ * d should be at least as large as s plus 1 extra byte
+ * assumes n > 0
+ * the return value is the ever useful pointer to the trailing \0 of d
+ *
+ * examples:
+ * /a/b, 1 ==> /
+ * /a/b, 2 ==> /a/
+ * /a/b, 3 ==> /a/b/
+ * /a/b, 4 ==> /a/b/
+ */
+API_EXPORT(char *) ap_make_dirstr_prefix(char *d, const char *s, int n)
+{
+ for (;;) {
+ *d = *s;
+ if (*d == '\0') {
+ *d = '/';
+ break;
+ }
+ if (*d == '/' && (--n) == 0)
+ break;
+ ++d;
+ ++s;
+ }
+ *++d = 0;
+ return (d);
+}
+
+
+/*
+ * return the parent directory name including trailing / of the file s
+ */
+API_EXPORT(char *) ap_make_dirstr_parent(pool *p, const char *s)
+{
+ char *last_slash = strrchr(s, '/');
+ char *d;
+ int l;
+
+ if (last_slash == NULL) {
+ /* XXX: well this is really broken if this happens */
+ return (ap_pstrdup(p, "/"));
+ }
+ l = (last_slash - s) + 1;
+ d = ap_palloc(p, l + 1);
+ memcpy(d, s, l);
+ d[l] = 0;
+ return (d);
+}
+
+
+/*
+ * This function is deprecated. Use one of the preceeding two functions
+ * which are faster.
+ */
+API_EXPORT(char *) ap_make_dirstr(pool *p, const char *s, int n)
+{
+ register int x, f;
+ char *res;
+
+ for (x = 0, f = 0; s[x]; x++) {
+ if (s[x] == '/')
+ if ((++f) == n) {
+ res = ap_palloc(p, x + 2);
+ memcpy(res, s, x);
+ res[x] = '/';
+ res[x + 1] = '\0';
+ return res;
+ }
+ }
+
+ if (s[strlen(s) - 1] == '/')
+ return ap_pstrdup(p, s);
+ else
+ return ap_pstrcat(p, s, "/", NULL);
+}
+
+API_EXPORT(int) ap_count_dirs(const char *path)
+{
+ register int x, n;
+
+ for (x = 0, n = 0; path[x]; x++)
+ if (path[x] == '/')
+ n++;
+ return n;
+}
+
+
+API_EXPORT(void) ap_chdir_file(const char *file)
+{
+ const char *x;
+ char buf[HUGE_STRING_LEN];
+
+ x = strrchr(file, '/');
+ if (x == NULL) {
+ chdir(file);
+ }
+ else if (x - file < sizeof(buf) - 1) {
+ memcpy(buf, file, x - file);
+ buf[x - file] = '\0';
+ chdir(buf);
+ }
+ /* XXX: well, this is a silly function, no method of reporting an
+ * error... ah well. */
+}
+
+API_EXPORT(char *) ap_getword_nc(pool *atrans, char **line, char stop)
+{
+ return ap_getword(atrans, (const char **) line, stop);
+}
+
+API_EXPORT(char *) ap_getword(pool *atrans, const char **line, char stop)
+{
+ char *pos = strchr(*line, stop);
+ char *res;
+
+ if (!pos) {
+ res = ap_pstrdup(atrans, *line);
+ *line += strlen(*line);
+ return res;
+ }
+
+ res = ap_pstrndup(atrans, *line, pos - *line);
+
+ while (*pos == stop) {
+ ++pos;
+ }
+
+ *line = pos;
+
+ return res;
+}
+
+API_EXPORT(char *) ap_getword_white_nc(pool *atrans, char **line)
+{
+ return ap_getword_white(atrans, (const char **) line);
+}
+
+API_EXPORT(char *) ap_getword_white(pool *atrans, const char **line)
+{
+ int pos = -1, x;
+ char *res;
+
+ for (x = 0; (*line)[x]; x++) {
+ if (ap_isspace((*line)[x])) {
+ pos = x;
+ break;
+ }
+ }
+
+ if (pos == -1) {
+ res = ap_pstrdup(atrans, *line);
+ *line += strlen(*line);
+ return res;
+ }
+
+ res = ap_palloc(atrans, pos + 1);
+ ap_cpystrn(res, *line, pos + 1);
+
+ while (ap_isspace((*line)[pos]))
+ ++pos;
+
+ *line += pos;
+
+ return res;
+}
+
+API_EXPORT(char *) ap_getword_nulls_nc(pool *atrans, char **line, char stop)
+{
+ return ap_getword_nulls(atrans, (const char **) line, stop);
+}
+
+API_EXPORT(char *) ap_getword_nulls(pool *atrans, const char **line, char stop)
+{
+ char *pos = strchr(*line, stop);
+ char *res;
+
+ if (!pos) {
+ res = ap_pstrdup(atrans, *line);
+ *line += strlen(*line);
+ return res;
+ }
+
+ res = ap_pstrndup(atrans, *line, pos - *line);
+
+ ++pos;
+
+ *line = pos;
+
+ return res;
+}
+
+/* Get a word, (new) config-file style --- quoted strings and backslashes
+ * all honored
+ */
+
+static char *substring_conf(pool *p, const char *start, int len, char quote)
+{
+ char *result = ap_palloc(p, len + 2);
+ char *resp = result;
+ int i;
+
+ for (i = 0; i < len; ++i) {
+ if (start[i] == '\\' && (start[i + 1] == '\\'
+ || (quote && start[i + 1] == quote)))
+ *resp++ = start[++i];
+ else
+ *resp++ = start[i];
+ }
+
+ *resp++ = '\0';
+ return result;
+}
+
+API_EXPORT(char *) ap_getword_conf_nc(pool *p, char **line)
+{
+ return ap_getword_conf(p, (const char **) line);
+}
+
+API_EXPORT(char *) ap_getword_conf(pool *p, const char **line)
+{
+ const char *str = *line, *strend;
+ char *res;
+ char quote;
+
+ while (*str && ap_isspace(*str))
+ ++str;
+
+ if (!*str) {
+ *line = str;
+ return "";
+ }
+
+ if ((quote = *str) == '"' || quote == '\'') {
+ strend = str + 1;
+ while (*strend && *strend != quote) {
+ if (*strend == '\\' && strend[1] && strend[1] == quote)
+ strend += 2;
+ else
+ ++strend;
+ }
+ res = substring_conf(p, str + 1, strend - str - 1, quote);
+
+ if (*strend == quote)
+ ++strend;
+ }
+ else {
+ strend = str;
+ while (*strend && !ap_isspace(*strend))
+ ++strend;
+
+ res = substring_conf(p, str, strend - str, 0);
+ }
+
+ while (*strend && ap_isspace(*strend))
+ ++strend;
+ *line = strend;
+ return res;
+}
+
+API_EXPORT(int) ap_cfg_closefile(configfile_t *cfp)
+{
+#ifdef DEBUG
+ ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, NULL,
+ "Done with config file %s", cfp->name);
+#endif
+ return (cfp->close == NULL) ? 0 : cfp->close(cfp->param);
+}
+
+/* Common structure that holds the file and pool for ap_pcfg_openfile */
+typedef struct {
+ struct pool *pool;
+ FILE *file;
+} poolfile_t;
+
+static int cfg_close(void *param)
+{
+ poolfile_t *cfp = (poolfile_t *) param;
+ return (ap_pfclose(cfp->pool, cfp->file));
+}
+
+static int cfg_getch(void *param)
+{
+ poolfile_t *cfp = (poolfile_t *) param;
+ return (fgetc(cfp->file));
+}
+
+static void *cfg_getstr(void *buf, size_t bufsiz, void *param)
+{
+ poolfile_t *cfp = (poolfile_t *) param;
+ return (fgets(buf, bufsiz, cfp->file));
+}
+
+/* Open a configfile_t as FILE, return open configfile_t struct pointer */
+API_EXPORT(configfile_t *) ap_pcfg_openfile(pool *p, const char *name)
+{
+ configfile_t *new_cfg;
+ poolfile_t *new_pfile;
+ FILE *file;
+ struct stat stbuf;
+ int saved_errno;
+
+ if (name == NULL) {
+ ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, NULL,
+ "Internal error: pcfg_openfile() called with NULL filename");
+ return NULL;
+ }
+
+ if (!ap_os_is_filename_valid(name)) {
+ ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, NULL,
+ "Access to config file %s denied: not a valid filename",
+ name);
+ errno = EACCES;
+ return NULL;
+ }
+
+ file = ap_pfopen(p, name, "r");
+#ifdef DEBUG
+ saved_errno = errno;
+ ap_log_error(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, NULL,
+ "Opening config file %s (%s)",
+ name, (file == NULL) ? strerror(errno) : "successful");
+ errno = saved_errno;
+#endif
+ if (file == NULL)
+ return NULL;
+
+ if (fstat(fileno(file), &stbuf) == 0 &&
+ !S_ISREG(stbuf.st_mode) &&
+#if defined(WIN32) || defined(OS2)
+ !(strcasecmp(name, "nul") == 0 ||
+ (strlen(name) >= 4 &&
+ strcasecmp(name + strlen(name) - 4, "/nul") == 0))) {
+#else
+ strcmp(name, "/dev/null") != 0) {
+#endif /* WIN32 || OS2 */
+ saved_errno = errno;
+ ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, NULL,
+ "Access to file %s denied by server: not a regular file",
+ name);
+ ap_pfclose(p, file);
+ errno = saved_errno;
+ return NULL;
+ }
+
+ new_cfg = ap_palloc(p, sizeof(*new_cfg));
+ new_pfile = ap_palloc(p, sizeof(*new_pfile));
+ new_pfile->file = file;
+ new_pfile->pool = p;
+ new_cfg->param = new_pfile;
+ new_cfg->name = ap_pstrdup(p, name);
+ new_cfg->getch = (int (*)(void *)) cfg_getch;
+ new_cfg->getstr = (void *(*)(void *, size_t, void *)) cfg_getstr;
+ new_cfg->close = (int (*)(void *)) cfg_close;
+ new_cfg->line_number = 0;
+ return new_cfg;
+}
+
+
+/* Allocate a configfile_t handle with user defined functions and params */
+API_EXPORT(configfile_t *) ap_pcfg_open_custom(pool *p, const char *descr,
+ void *param,
+ int(*getch)(void *param),
+ void *(*getstr) (void *buf, size_t bufsiz, void *param),
+ int(*close_func)(void *param))
+{
+ configfile_t *new_cfg = ap_palloc(p, sizeof(*new_cfg));
+#ifdef DEBUG
+ ap_log_error(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, NULL, "Opening config handler %s", descr);
+#endif
+ new_cfg->param = param;
+ new_cfg->name = descr;
+ new_cfg->getch = getch;
+ new_cfg->getstr = getstr;
+ new_cfg->close = close_func;
+ new_cfg->line_number = 0;
+ return new_cfg;
+}
+
+
+/* Read one character from a configfile_t */
+API_EXPORT(int) ap_cfg_getc(configfile_t *cfp)
+{
+ register int ch = cfp->getch(cfp->param);
+ if (ch == LF)
+ ++cfp->line_number;
+ return ch;
+}
+
+
+/* Read one line from open configfile_t, strip LF, increase line number */
+/* If custom handler does not define a getstr() function, read char by char */
+API_EXPORT(int) ap_cfg_getline(char *buf, size_t bufsize, configfile_t *cfp)
+{
+ /* If a "get string" function is defined, use it */
+ if (cfp->getstr != NULL) {
+ char *src, *dst;
+ char *cp;
+ char *cbuf = buf;
+ size_t cbufsize = bufsize;
+
+ while (1) {
+ ++cfp->line_number;
+ if (cfp->getstr(cbuf, cbufsize, cfp->param) == NULL)
+ return 1;
+
+ /*
+ * check for line continuation,
+ * i.e. match [^\\]\\[\r]\n only
+ */
+ cp = cbuf;
+ while (cp < cbuf+cbufsize && *cp != '\0')
+ cp++;
+ if (cp > cbuf && cp[-1] == LF) {
+ cp--;
+ if (cp > cbuf && cp[-1] == CR)
+ cp--;
+ if (cp > cbuf && cp[-1] == '\\') {
+ cp--;
+ if (!(cp > cbuf && cp[-1] == '\\')) {
+ /*
+ * line continuation requested -
+ * then remove backslash and continue
+ */
+ cbufsize -= (cp-cbuf);
+ cbuf = cp;
+ continue;
+ }
+ else {
+ /*
+ * no real continuation because escaped -
+ * then just remove escape character
+ */
+ for ( ; cp < cbuf+cbufsize && *cp != '\0'; cp++)
+ cp[0] = cp[1];
+ }
+ }
+ }
+ break;
+ }
+
+ /*
+ * Leading and trailing white space is eliminated completely
+ */
+ src = buf;
+ while (ap_isspace(*src))
+ ++src;
+ /* blast trailing whitespace */
+ dst = &src[strlen(src)];
+ while (--dst >= src && ap_isspace(*dst))
+ *dst = '\0';
+ /* Zap leading whitespace by shifting */
+ if (src != buf)
+ for (dst = buf; (*dst++ = *src++) != '\0'; )
+ ;
+
+#ifdef DEBUG_CFG_LINES
+ ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, NULL, "Read config: %s", buf);
+#endif
+ return 0;
+ } else {
+ /* No "get string" function defined; read character by character */
+ register int c;
+ register size_t i = 0;
+
+ buf[0] = '\0';
+ /* skip leading whitespace */
+ do {
+ c = cfp->getch(cfp->param);
+ } while (c == '\t' || c == ' ');
+
+ if (c == EOF)
+ return 1;
+
+ if(bufsize < 2) {
+ /* too small, assume caller is crazy */
+ return 1;
+ }
+
+ while (1) {
+ if ((c == '\t') || (c == ' ')) {
+ buf[i++] = ' ';
+ while ((c == '\t') || (c == ' '))
+ c = cfp->getch(cfp->param);
+ }
+ if (c == CR) {
+ /* silently ignore CR (_assume_ that a LF follows) */
+ c = cfp->getch(cfp->param);
+ }
+ if (c == LF) {
+ /* increase line number and return on LF */
+ ++cfp->line_number;
+ }
+ if (c == EOF || c == 0x4 || c == LF || i >= (bufsize - 2)) {
+ /*
+ * check for line continuation
+ */
+ if (i > 0 && buf[i-1] == '\\') {
+ i--;
+ if (!(i > 0 && buf[i-1] == '\\')) {
+ /* line is continued */
+ c = cfp->getch(cfp->param);
+ continue;
+ }
+ /* else nothing needs be done because
+ * then the backslash is escaped and
+ * we just strip to a single one
+ */
+ }
+ /* blast trailing whitespace */
+ while (i > 0 && ap_isspace(buf[i - 1]))
+ --i;
+ buf[i] = '\0';
+#ifdef DEBUG_CFG_LINES
+ ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, NULL, "Read config: %s", buf);
+#endif
+ return 0;
+ }
+ buf[i] = c;
+ ++i;
+ c = cfp->getch(cfp->param);
+ }
+ }
+}
+
+/* Size an HTTP header field list item, as separated by a comma.
+ * The return value is a pointer to the beginning of the non-empty list item
+ * within the original string (or NULL if there is none) and the address
+ * of field is shifted to the next non-comma, non-whitespace character.
+ * len is the length of the item excluding any beginning whitespace.
+ */
+API_EXPORT(const char *) ap_size_list_item(const char **field, int *len)
+{
+ const unsigned char *ptr = (const unsigned char *)*field;
+ const unsigned char *token;
+ int in_qpair, in_qstr, in_com;
+
+ /* Find first non-comma, non-whitespace byte */
+
+ while (*ptr == ',' || ap_isspace(*ptr))
+ ++ptr;
+
+ token = ptr;
+
+ /* Find the end of this item, skipping over dead bits */
+
+ for (in_qpair = in_qstr = in_com = 0;
+ *ptr && (in_qpair || in_qstr || in_com || *ptr != ',');
+ ++ptr) {
+
+ if (in_qpair) {
+ in_qpair = 0;
+ }
+ else {
+ switch (*ptr) {
+ case '\\': in_qpair = 1; /* quoted-pair */
+ break;
+ case '"' : if (!in_com) /* quoted string delim */
+ in_qstr = !in_qstr;
+ break;
+ case '(' : if (!in_qstr) /* comment (may nest) */
+ ++in_com;
+ break;
+ case ')' : if (in_com) /* end comment */
+ --in_com;
+ break;
+ default : break;
+ }
+ }
+ }
+
+ if ((*len = (ptr - token)) == 0) {
+ *field = (const char *)ptr;
+ return NULL;
+ }
+
+ /* Advance field pointer to the next non-comma, non-white byte */
+
+ while (*ptr == ',' || ap_isspace(*ptr))
+ ++ptr;
+
+ *field = (const char *)ptr;
+ return (const char *)token;
+}
+
+/* Retrieve an HTTP header field list item, as separated by a comma,
+ * while stripping insignificant whitespace and lowercasing anything not in
+ * a quoted string or comment. The return value is a new string containing
+ * the converted list item (or NULL if none) and the address pointed to by
+ * field is shifted to the next non-comma, non-whitespace.
+ */
+API_EXPORT(char *) ap_get_list_item(pool *p, const char **field)
+{
+ const char *tok_start;
+ const unsigned char *ptr;
+ unsigned char *pos;
+ char *token;
+ int addspace = 0, in_qpair = 0, in_qstr = 0, in_com = 0, tok_len = 0;
+
+ /* Find the beginning and maximum length of the list item so that
+ * we can allocate a buffer for the new string and reset the field.
+ */
+ if ((tok_start = ap_size_list_item(field, &tok_len)) == NULL) {
+ return NULL;
+ }
+ token = ap_palloc(p, tok_len + 1);
+
+ /* Scan the token again, but this time copy only the good bytes.
+ * We skip extra whitespace and any whitespace around a '=', '/',
+ * or ';' and lowercase normal characters not within a comment,
+ * quoted-string or quoted-pair.
+ */
+ for (ptr = (const unsigned char *)tok_start, pos = (unsigned char *)token;
+ *ptr && (in_qpair || in_qstr || in_com || *ptr != ',');
+ ++ptr) {
+
+ if (in_qpair) {
+ in_qpair = 0;
+ *pos++ = *ptr;
+ }
+ else {
+ switch (*ptr) {
+ case '\\': in_qpair = 1;
+ if (addspace == 1)
+ *pos++ = ' ';
+ *pos++ = *ptr;
+ addspace = 0;
+ break;
+ case '"' : if (!in_com)
+ in_qstr = !in_qstr;
+ if (addspace == 1)
+ *pos++ = ' ';
+ *pos++ = *ptr;
+ addspace = 0;
+ break;
+ case '(' : if (!in_qstr)
+ ++in_com;
+ if (addspace == 1)
+ *pos++ = ' ';
+ *pos++ = *ptr;
+ addspace = 0;
+ break;
+ case ')' : if (in_com)
+ --in_com;
+ *pos++ = *ptr;
+ addspace = 0;
+ break;
+ case ' ' :
+ case '\t': if (addspace)
+ break;
+ if (in_com || in_qstr)
+ *pos++ = *ptr;
+ else
+ addspace = 1;
+ break;
+ case '=' :
+ case '/' :
+ case ';' : if (!(in_com || in_qstr))
+ addspace = -1;
+ *pos++ = *ptr;
+ break;
+ default : if (addspace == 1)
+ *pos++ = ' ';
+ *pos++ = (in_com || in_qstr) ? *ptr
+ : ap_tolower(*ptr);
+ addspace = 0;
+ break;
+ }
+ }
+ }
+ *pos = '\0';
+
+ return token;
+}
+
+/* Find an item in canonical form (lowercase, no extra spaces) within
+ * an HTTP field value list. Returns 1 if found, 0 if not found.
+ * This would be much more efficient if we stored header fields as
+ * an array of list items as they are received instead of a plain string.
+ */
+API_EXPORT(int) ap_find_list_item(pool *p, const char *line, const char *tok)
+{
+ const unsigned char *pos;
+ const unsigned char *ptr = (const unsigned char *)line;
+ int good = 0, addspace = 0, in_qpair = 0, in_qstr = 0, in_com = 0;
+
+ if (!line || !tok)
+ return 0;
+
+ do { /* loop for each item in line's list */
+
+ /* Find first non-comma, non-whitespace byte */
+
+ while (*ptr == ',' || ap_isspace(*ptr))
+ ++ptr;
+
+ if (*ptr)
+ good = 1; /* until proven otherwise for this item */
+ else
+ break; /* no items left and nothing good found */
+
+ /* We skip extra whitespace and any whitespace around a '=', '/',
+ * or ';' and lowercase normal characters not within a comment,
+ * quoted-string or quoted-pair.
+ */
+ for (pos = (const unsigned char *)tok;
+ *ptr && (in_qpair || in_qstr || in_com || *ptr != ',');
+ ++ptr) {
+
+ if (in_qpair) {
+ in_qpair = 0;
+ if (good)
+ good = (*pos++ == *ptr);
+ }
+ else {
+ switch (*ptr) {
+ case '\\': in_qpair = 1;
+ if (addspace == 1)
+ good = good && (*pos++ == ' ');
+ good = good && (*pos++ == *ptr);
+ addspace = 0;
+ break;
+ case '"' : if (!in_com)
+ in_qstr = !in_qstr;
+ if (addspace == 1)
+ good = good && (*pos++ == ' ');
+ good = good && (*pos++ == *ptr);
+ addspace = 0;
+ break;
+ case '(' : if (!in_qstr)
+ ++in_com;
+ if (addspace == 1)
+ good = good && (*pos++ == ' ');
+ good = good && (*pos++ == *ptr);
+ addspace = 0;
+ break;
+ case ')' : if (in_com)
+ --in_com;
+ good = good && (*pos++ == *ptr);
+ addspace = 0;
+ break;
+ case ' ' :
+ case '\t': if (addspace || !good)
+ break;
+ if (in_com || in_qstr)
+ good = (*pos++ == *ptr);
+ else
+ addspace = 1;
+ break;
+ case '=' :
+ case '/' :
+ case ';' : if (!(in_com || in_qstr))
+ addspace = -1;
+ good = good && (*pos++ == *ptr);
+ break;
+ default : if (!good)
+ break;
+ if (addspace == 1)
+ good = (*pos++ == ' ');
+ if (in_com || in_qstr)
+ good = good && (*pos++ == *ptr);
+ else
+ good = good && (*pos++ == ap_tolower(*ptr));
+ addspace = 0;
+ break;
+ }
+ }
+ }
+ if (good && *pos)
+ good = 0; /* not good if only a prefix was matched */
+
+ } while (*ptr && !good);
+
+ return good;
+}
+
+
+/* Retrieve a token, spacing over it and returning a pointer to
+ * the first non-white byte afterwards. Note that these tokens
+ * are delimited by semis and commas; and can also be delimited
+ * by whitespace at the caller's option.
+ */
+
+API_EXPORT(char *) ap_get_token(pool *p, const char **accept_line, int accept_white)
+{
+ const char *ptr = *accept_line;
+ const char *tok_start;
+ char *token;
+ int tok_len;
+
+ /* Find first non-white byte */
+
+ while (*ptr && ap_isspace(*ptr))
+ ++ptr;
+
+ tok_start = ptr;
+
+ /* find token end, skipping over quoted strings.
+ * (comments are already gone).
+ */
+
+ while (*ptr && (accept_white || !ap_isspace(*ptr))
+ && *ptr != ';' && *ptr != ',') {
+ if (*ptr++ == '"')
+ while (*ptr)
+ if (*ptr++ == '"')
+ break;
+ }
+
+ tok_len = ptr - tok_start;
+ token = ap_pstrndup(p, tok_start, tok_len);
+
+ /* Advance accept_line pointer to the next non-white byte */
+
+ while (*ptr && ap_isspace(*ptr))
+ ++ptr;
+
+ *accept_line = ptr;
+ return token;
+}
+
+
+/* find http tokens, see the definition of token from RFC2068 */
+API_EXPORT(int) ap_find_token(pool *p, const char *line, const char *tok)
+{
+ const unsigned char *start_token;
+ const unsigned char *s;
+
+ if (!line)
+ return 0;
+
+ s = (const unsigned char *)line;
+ for (;;) {
+ /* find start of token, skip all stop characters, note NUL
+ * isn't a token stop, so we don't need to test for it
+ */
+ while (TEST_CHAR(*s, T_HTTP_TOKEN_STOP)) {
+ ++s;
+ }
+ if (!*s) {
+ return 0;
+ }
+ start_token = s;
+ /* find end of the token */
+ while (*s && !TEST_CHAR(*s, T_HTTP_TOKEN_STOP)) {
+ ++s;
+ }
+ if (!strncasecmp((const char *)start_token, (const char *)tok, s - start_token)) {
+ return 1;
+ }
+ if (!*s) {
+ return 0;
+ }
+ }
+}
+
+
+API_EXPORT(int) ap_find_last_token(pool *p, const char *line, const char *tok)
+{
+ int llen, tlen, lidx;
+
+ if (!line)
+ return 0;
+
+ llen = strlen(line);
+ tlen = strlen(tok);
+ lidx = llen - tlen;
+
+ if ((lidx < 0) ||
+ ((lidx > 0) && !(ap_isspace(line[lidx - 1]) || line[lidx - 1] == ',')))
+ return 0;
+
+ return (strncasecmp(&line[lidx], tok, tlen) == 0);
+}
+
+API_EXPORT(char *) ap_escape_shell_cmd(pool *p, const char *str)
+{
+ char *cmd;
+ unsigned char *d;
+ const unsigned char *s;
+
+ cmd = ap_palloc(p, 2 * strlen(str) + 1); /* Be safe */
+ d = (unsigned char *)cmd;
+ s = (const unsigned char *)str;
+ for (; *s; ++s) {
+
+#if defined(OS2) || defined(WIN32)
+ /* Don't allow '&' in parameters under OS/2. */
+ /* This can be used to send commands to the shell. */
+ if (*s == '&') {
+ *d++ = ' ';
+ continue;
+ }
+#endif
+
+ if (TEST_CHAR(*s, T_ESCAPE_SHELL_CMD)) {
+ *d++ = '\\';
+ }
+ *d++ = *s;
+ }
+ *d = '\0';
+
+ return cmd;
+}
+
+static char x2c(const char *what)
+{
+ register char digit;
+
+#ifndef CHARSET_EBCDIC
+ digit = ((what[0] >= 'A') ? ((what[0] & 0xdf) - 'A') + 10 : (what[0] - '0'));
+ digit *= 16;
+ digit += (what[1] >= 'A' ? ((what[1] & 0xdf) - 'A') + 10 : (what[1] - '0'));
+#else /*CHARSET_EBCDIC*/
+ char xstr[5];
+ xstr[0]='0';
+ xstr[1]='x';
+ xstr[2]=what[0];
+ xstr[3]=what[1];
+ xstr[4]='\0';
+ digit = os_toebcdic[0xFF & strtol(xstr, NULL, 16)];
+#endif /*CHARSET_EBCDIC*/
+ return (digit);
+}
+
+/*
+ * Unescapes a URL.
+ * Returns 0 on success, non-zero on error
+ * Failure is due to
+ * bad % escape returns BAD_REQUEST
+ *
+ * decoding %00 -> \0
+ * decoding %2f -> / (a special character)
+ * returns NOT_FOUND
+ */
+API_EXPORT(int) ap_unescape_url(char *url)
+{
+ register int x, y, badesc, badpath;
+
+ badesc = 0;
+ badpath = 0;
+ for (x = 0, y = 0; url[y]; ++x, ++y) {
+ if (url[y] != '%')
+ url[x] = url[y];
+ else {
+ if (!ap_isxdigit(url[y + 1]) || !ap_isxdigit(url[y + 2])) {
+ badesc = 1;
+ url[x] = '%';
+ }
+ else {
+ url[x] = x2c(&url[y + 1]);
+ y += 2;
+ if (url[x] == '/' || url[x] == '\0')
+ badpath = 1;
+ }
+ }
+ }
+ url[x] = '\0';
+ if (badesc)
+ return BAD_REQUEST;
+ else if (badpath)
+ return NOT_FOUND;
+ else
+ return OK;
+}
+
+API_EXPORT(char *) ap_construct_server(pool *p, const char *hostname,
+ unsigned port, const request_rec *r)
+{
+ if (ap_is_default_port(port, r))
+ return ap_pstrdup(p, hostname);
+ else {
+ return ap_psprintf(p, "%s:%u", hostname, port);
+ }
+}
+
+/* c2x takes an unsigned, and expects the caller has guaranteed that
+ * 0 <= what < 256... which usually means that you have to cast to
+ * unsigned char first, because (unsigned)(char)(x) fist goes through
+ * signed extension to an int before the unsigned cast.
+ *
+ * The reason for this assumption is to assist gcc code generation --
+ * the unsigned char -> unsigned extension is already done earlier in
+ * both uses of this code, so there's no need to waste time doing it
+ * again.
+ */
+static const char c2x_table[] = "0123456789abcdef";
+
+static ap_inline unsigned char *c2x(unsigned what, unsigned char *where)
+{
+ *where++ = '%';
+ *where++ = c2x_table[what >> 4];
+ *where++ = c2x_table[what & 0xf];
+ return where;
+}
+
+/*
+ * escape_path_segment() escapes a path segment, as defined in RFC 1808. This
+ * routine is (should be) OS independent.
+ *
+ * os_escape_path() converts an OS path to a URL, in an OS dependent way. In all
+ * cases if a ':' occurs before the first '/' in the URL, the URL should be
+ * prefixed with "./" (or the ':' escaped). In the case of Unix, this means
+ * leaving '/' alone, but otherwise doing what escape_path_segment() does. For
+ * efficiency reasons, we don't use escape_path_segment(), which is provided for
+ * reference. Again, RFC 1808 is where this stuff is defined.
+ *
+ * If partial is set, os_escape_path() assumes that the path will be appended to
+ * something with a '/' in it (and thus does not prefix "./").
+ */
+
+API_EXPORT(char *) ap_escape_path_segment(pool *p, const char *segment)
+{
+ char *copy = ap_palloc(p, 3 * strlen(segment) + 1);
+ const unsigned char *s = (const unsigned char *)segment;
+ unsigned char *d = (unsigned char *)copy;
+ unsigned c;
+
+ while ((c = *s)) {
+ if (TEST_CHAR(c, T_ESCAPE_PATH_SEGMENT)) {
+ d = c2x(c, d);
+ }
+ else {
+ *d++ = c;
+ }
+ ++s;
+ }
+ *d = '\0';
+ return copy;
+}
+
+API_EXPORT(char *) ap_os_escape_path(pool *p, const char *path, int partial)
+{
+ char *copy = ap_palloc(p, 3 * strlen(path) + 3);
+ const unsigned char *s = (const unsigned char *)path;
+ unsigned char *d = (unsigned char *)copy;
+ unsigned c;
+
+ if (!partial) {
+ char *colon = strchr(path, ':');
+ char *slash = strchr(path, '/');
+
+ if (colon && (!slash || colon < slash)) {
+ *d++ = '.';
+ *d++ = '/';
+ }
+ }
+ while ((c = *s)) {
+ if (TEST_CHAR(c, T_OS_ESCAPE_PATH)) {
+ d = c2x(c, d);
+ }
+ else {
+ *d++ = c;
+ }
+ ++s;
+ }
+ *d = '\0';
+ return copy;
+}
+
+/* ap_escape_uri is now a macro for os_escape_path */
+
+API_EXPORT(char *) ap_escape_html(pool *p, const char *s)
+{
+ int i, j;
+ char *x;
+
+ /* first, count the number of extra characters */
+ for (i = 0, j = 0; s[i] != '\0'; i++)
+ if (s[i] == '<' || s[i] == '>')
+ j += 3;
+ else if (s[i] == '&')
+ j += 4;
+
+ if (j == 0)
+ return ap_pstrndup(p, s, i);
+
+ x = ap_palloc(p, i + j + 1);
+ for (i = 0, j = 0; s[i] != '\0'; i++, j++)
+ if (s[i] == '<') {
+ memcpy(&x[j], "<", 4);
+ j += 3;
+ }
+ else if (s[i] == '>') {
+ memcpy(&x[j], ">", 4);
+ j += 3;
+ }
+ else if (s[i] == '&') {
+ memcpy(&x[j], "&", 5);
+ j += 4;
+ }
+ else
+ x[j] = s[i];
+
+ x[j] = '\0';
+ return x;
+}
+
+API_EXPORT(int) ap_is_directory(const char *path)
+{
+ struct stat finfo;
+
+ if (stat(path, &finfo) == -1)
+ return 0; /* in error condition, just return no */
+
+ return (S_ISDIR(finfo.st_mode));
+}
+
+API_EXPORT(char *) ap_make_full_path(pool *a, const char *src1,
+ const char *src2)
+{
+ register int x;
+
+ x = strlen(src1);
+ if (x == 0)
+ return ap_pstrcat(a, "/", src2, NULL);
+
+ if (src1[x - 1] != '/')
+ return ap_pstrcat(a, src1, "/", src2, NULL);
+ else
+ return ap_pstrcat(a, src1, src2, NULL);
+}
+
+/*
+ * Check for an absoluteURI syntax (see section 3.2 in RFC2068).
+ */
+API_EXPORT(int) ap_is_url(const char *u)
+{
+ register int x;
+
+ for (x = 0; u[x] != ':'; x++) {
+ if ((!u[x]) ||
+ ((!ap_isalpha(u[x])) && (!ap_isdigit(u[x])) &&
+ (u[x] != '+') && (u[x] != '-') && (u[x] != '.'))) {
+ return 0;
+ }
+ }
+
+ return (x ? 1 : 0); /* If the first character is ':', it's broken, too */
+}
+
+API_EXPORT(int) ap_can_exec(const struct stat *finfo)
+{
+#ifdef MULTIPLE_GROUPS
+ int cnt;
+#endif
+#if defined(OS2) || defined(WIN32)
+ /* OS/2 dosen't have Users and Groups */
+ return 1;
+#else
+ if (ap_user_id == finfo->st_uid)
+ if (finfo->st_mode & S_IXUSR)
+ return 1;
+ if (ap_group_id == finfo->st_gid)
+ if (finfo->st_mode & S_IXGRP)
+ return 1;
+#ifdef MULTIPLE_GROUPS
+ for (cnt = 0; cnt < NGROUPS_MAX; cnt++) {
+ if (group_id_list[cnt] == finfo->st_gid)
+ if (finfo->st_mode & S_IXGRP)
+ return 1;
+ }
+#endif
+ return (finfo->st_mode & S_IXOTH);
+#endif
+}
+
+#ifdef NEED_STRDUP
+char *strdup(const char *str)
+{
+ char *sdup;
+
+ if (!(sdup = (char *) malloc(strlen(str) + 1))) {
+ fprintf(stderr, "Ouch! Out of memory in our strdup()!\n");
+ return NULL;
+ }
+ sdup = strcpy(sdup, str);
+
+ return sdup;
+}
+#endif
+
+/* The following two routines were donated for SVR4 by Andreas Vogel */
+#ifdef NEED_STRCASECMP
+int strcasecmp(const char *a, const char *b)
+{
+ const char *p = a;
+ const char *q = b;
+ for (p = a, q = b; *p && *q; p++, q++) {
+ int diff = ap_tolower(*p) - ap_tolower(*q);
+ if (diff)
+ return diff;
+ }
+ if (*p)
+ return 1; /* p was longer than q */
+ if (*q)
+ return -1; /* p was shorter than q */
+ return 0; /* Exact match */
+}
+
+#endif
+
+#ifdef NEED_STRNCASECMP
+int strncasecmp(const char *a, const char *b, int n)
+{
+ const char *p = a;
+ const char *q = b;
+
+ for (p = a, q = b; /*NOTHING */ ; p++, q++) {
+ int diff;
+ if (p == a + n)
+ return 0; /* Match up to n characters */
+ if (!(*p && *q))
+ return *p - *q;
+ diff = ap_tolower(*p) - ap_tolower(*q);
+ if (diff)
+ return diff;
+ }
+ /*NOTREACHED */
+}
+#endif
+
+/* The following routine was donated for UTS21 by dwd@bell-labs.com */
+#ifdef NEED_STRSTR
+char *strstr(char *s1, char *s2)
+{
+ char *p1, *p2;
+ if (*s2 == '\0') {
+ /* an empty s2 */
+ return(s1);
+ }
+ while((s1 = strchr(s1, *s2)) != NULL) {
+ /* found first character of s2, see if the rest matches */
+ p1 = s1;
+ p2 = s2;
+ while (*++p1 == *++p2) {
+ if (*p1 == '\0') {
+ /* both strings ended together */
+ return(s1);
+ }
+ }
+ if (*p2 == '\0') {
+ /* second string ended, a match */
+ break;
+ }
+ /* didn't find a match here, try starting at next character in s1 */
+ s1++;
+ }
+ return(s1);
+}
+#endif
+
+#ifdef NEED_INITGROUPS
+int initgroups(const char *name, gid_t basegid)
+{
+#if defined(QNX) || defined(MPE) || defined(BEOS) || defined(_OSD_POSIX) || defined(TPF) || defined(__TANDEM)
+/* QNX, MPE and BeOS do not appear to support supplementary groups. */
+ return 0;
+#else /* ndef QNX */
+ gid_t groups[NGROUPS_MAX];
+ struct group *g;
+ int index = 0;
+
+ setgrent();
+
+ groups[index++] = basegid;
+
+ while (index < NGROUPS_MAX && ((g = getgrent()) != NULL))
+ if (g->gr_gid != basegid) {
+ char **names;
+
+ for (names = g->gr_mem; *names != NULL; ++names)
+ if (!strcmp(*names, name))
+ groups[index++] = g->gr_gid;
+ }
+
+ endgrent();
+
+ return setgroups(index, groups);
+#endif /* def QNX */
+}
+#endif /* def NEED_INITGROUPS */
+
+#ifdef NEED_WAITPID
+/* From ikluft@amdahl.com
+ * this is not ideal but it works for SVR3 variants
+ * Modified by dwd@bell-labs.com to call wait3 instead of wait because
+ * apache started to use the WNOHANG option.
+ */
+int waitpid(pid_t pid, int *statusp, int options)
+{
+ int tmp_pid;
+ if (kill(pid, 0) == -1) {
+ errno = ECHILD;
+ return -1;
+ }
+ while (((tmp_pid = wait3(statusp, options, 0)) != pid) &&
+ (tmp_pid != -1) && (tmp_pid != 0) && (pid != -1))
+ ;
+ return tmp_pid;
+}
+#endif
+
+API_EXPORT(int) ap_ind(const char *s, char c)
+{
+ register int x;
+
+ for (x = 0; s[x]; x++)
+ if (s[x] == c)
+ return x;
+
+ return -1;
+}
+
+API_EXPORT(int) ap_rind(const char *s, char c)
+{
+ register int x;
+
+ for (x = strlen(s) - 1; x != -1; x--)
+ if (s[x] == c)
+ return x;
+
+ return -1;
+}
+
+API_EXPORT(void) ap_str_tolower(char *str)
+{
+ while (*str) {
+ *str = ap_tolower(*str);
+ ++str;
+ }
+}
+
+API_EXPORT(uid_t) ap_uname2id(const char *name)
+{
+#ifdef WIN32
+ return (1);
+#else
+ struct passwd *ent;
+
+ if (name[0] == '#')
+ return (atoi(&name[1]));
+
+ if (!(ent = getpwnam(name))) {
+ fprintf(stderr, "%s: bad user name %s\n", ap_server_argv0, name);
+ exit(1);
+ }
+ return (ent->pw_uid);
+#endif
+}
+
+API_EXPORT(gid_t) ap_gname2id(const char *name)
+{
+#ifdef WIN32
+ return (1);
+#else
+ struct group *ent;
+
+ if (name[0] == '#')
+ return (atoi(&name[1]));
+
+ if (!(ent = getgrnam(name))) {
+ fprintf(stderr, "%s: bad group name %s\n", ap_server_argv0, name);
+ exit(1);
+ }
+ return (ent->gr_gid);
+#endif
+}
+
+
+/*
+ * Parses a host of the form <address>[:port]
+ * :port is permitted if 'port' is not NULL
+ */
+unsigned long ap_get_virthost_addr(char *w, unsigned short *ports)
+{
+ struct hostent *hep;
+ unsigned long my_addr;
+ char *p;
+
+ p = strchr(w, ':');
+ if (ports != NULL) {
+ *ports = 0;
+ if (p != NULL && strcmp(p + 1, "*") != 0)
+ *ports = atoi(p + 1);
+ }
+
+ if (p != NULL)
+ *p = '\0';
+ if (strcmp(w, "*") == 0) {
+ if (p != NULL)
+ *p = ':';
+ return htonl(INADDR_ANY);
+ }
+
+ my_addr = ap_inet_addr((char *)w);
+ if (my_addr != INADDR_NONE) {
+ if (p != NULL)
+ *p = ':';
+ return my_addr;
+ }
+
+ hep = gethostbyname(w);
+
+ if ((!hep) || (hep->h_addrtype != AF_INET || !hep->h_addr_list[0])) {
+ fprintf(stderr, "Cannot resolve host name %s --- exiting!\n", w);
+ exit(1);
+ }
+
+ if (hep->h_addr_list[1]) {
+ fprintf(stderr, "Host %s has multiple addresses ---\n", w);
+ fprintf(stderr, "you must choose one explicitly for use as\n");
+ fprintf(stderr, "a virtual host. Exiting!!!\n");
+ exit(1);
+ }
+
+ if (p != NULL)
+ *p = ':';
+
+ return ((struct in_addr *) (hep->h_addr))->s_addr;
+}
+
+
+static char *find_fqdn(pool *a, struct hostent *p)
+{
+ int x;
+
+ if (!strchr(p->h_name, '.')) {
+ for (x = 0; p->h_aliases[x]; ++x) {
+ if (strchr(p->h_aliases[x], '.') &&
+ (!strncasecmp(p->h_aliases[x], p->h_name, strlen(p->h_name))))
+ return ap_pstrdup(a, p->h_aliases[x]);
+ }
+ return NULL;
+ }
+ return ap_pstrdup(a, (void *) p->h_name);
+}
+
+char *ap_get_local_host(pool *a)
+{
+#ifndef MAXHOSTNAMELEN
+#define MAXHOSTNAMELEN 256
+#endif
+ char str[MAXHOSTNAMELEN + 1];
+ char *server_hostname;
+ struct hostent *p;
+
+#ifdef BEOS /* BeOS returns zero as an error for gethostname */
+ if (gethostname(str, sizeof(str) - 1) == 0) {
+#else
+ if (gethostname(str, sizeof(str) - 1) != 0) {
+#endif /* BeOS */
+ perror("Unable to gethostname");
+ exit(1);
+ }
+ str[MAXHOSTNAMELEN] = '\0';
+ if ((!(p = gethostbyname(str))) || (!(server_hostname = find_fqdn(a, p)))) {
+ fprintf(stderr, "%s: cannot determine local host name.\n",
+ ap_server_argv0);
+ fprintf(stderr, "Use the ServerName directive to set it manually.\n");
+ exit(1);
+ }
+
+ return server_hostname;
+}
+
+/* simple 'pool' alloc()ing glue to ap_base64.c
+ */
+API_EXPORT(char *) ap_pbase64decode(pool *p, const char *bufcoded)
+{
+ char *decoded;
+ int l;
+
+ decoded = (char *) ap_palloc(p, 1 + ap_base64decode_len(bufcoded));
+ l = ap_base64decode(decoded, bufcoded);
+ decoded[l] = '\0'; /* make binary sequence into string */
+
+ return decoded;
+}
+
+API_EXPORT(char *) ap_pbase64encode(pool *p, char *string)
+{
+ char *encoded;
+ int l = strlen(string);
+
+ encoded = (char *) ap_palloc(p, 1 + ap_base64encode_len(l));
+ l = ap_base64encode(encoded, string, l);
+ encoded[l] = '\0'; /* make binary sequence into string */
+
+ return encoded;
+}
+
+/* deprecated names for the above two functions, here for compatibility
+ */
+API_EXPORT(char *) ap_uudecode(pool *p, const char *bufcoded)
+{
+ return ap_pbase64decode(p, bufcoded);
+}
+
+API_EXPORT(char *) ap_uuencode(pool *p, char *string)
+{
+ return ap_pbase64encode(p, string);
+}
+
+#ifdef OS2
+void os2pathname(char *path)
+{
+ char newpath[MAX_STRING_LEN];
+ int loop;
+ int offset;
+
+ offset = 0;
+ for (loop = 0; loop < (strlen(path) + 1) && loop < sizeof(newpath) - 1; loop++) {
+ if (path[loop] == '/') {
+ newpath[offset] = '\\';
+ /*
+ offset = offset + 1;
+ newpath[offset] = '\\';
+ */
+ }
+ else
+ newpath[offset] = path[loop];
+ offset = offset + 1;
+ };
+ /* Debugging code */
+ /* fprintf(stderr, "%s \n", newpath); */
+
+ strcpy(path, newpath);
+};
+
+/* quotes in the string are doubled up.
+ * Used to escape quotes in args passed to OS/2's cmd.exe
+ */
+char *ap_double_quotes(pool *p, char *str)
+{
+ int num_quotes = 0;
+ int len = 0;
+ char *quote_doubled_str, *dest;
+
+ while (str[len]) {
+ num_quotes += str[len++] == '\"';
+ }
+
+ quote_doubled_str = ap_palloc(p, len + num_quotes + 1);
+ dest = quote_doubled_str;
+
+ while (*str) {
+ if (*str == '\"')
+ *(dest++) = '\"';
+ *(dest++) = *(str++);
+ }
+
+ *dest = 0;
+ return quote_doubled_str;
+}
+#endif
+
+
+#ifdef NEED_STRERROR
+char *
+ strerror(int err)
+{
+
+ char *p;
+ extern char *const sys_errlist[];
+
+ p = sys_errlist[err];
+ return (p);
+}
+#endif
+
+#if defined(NEED_DIFFTIME)
+double difftime(time_t time1, time_t time0)
+{
+ return (time1 - time0);
+}
+#endif
+
+/* we want to downcase the type/subtype for comparison purposes
+ * but nothing else because ;parameter=foo values are case sensitive.
+ * XXX: in truth we want to downcase parameter names... but really,
+ * apache has never handled parameters and such correctly. You
+ * also need to compress spaces and such to be able to compare
+ * properly. -djg
+ */
+API_EXPORT(void) ap_content_type_tolower(char *str)
+{
+ char *semi;
+
+ semi = strchr(str, ';');
+ if (semi) {
+ *semi = '\0';
+ }
+ while (*str) {
+ *str = ap_tolower(*str);
+ ++str;
+ }
+ if (semi) {
+ *semi = ';';
+ }
+}
+
+/*
+ * Given a string, replace any bare " with \" .
+ */
+API_EXPORT(char *) ap_escape_quotes (pool *p, const char *instring)
+{
+ int newlen = 0;
+ const char *inchr = instring;
+ char *outchr, *outstring;
+
+ /*
+ * Look through the input string, jogging the length of the output
+ * string up by an extra byte each time we find an unescaped ".
+ */
+ while (*inchr != '\0') {
+ newlen++;
+ if (*inchr == '"') {
+ newlen++;
+ }
+ /*
+ * If we find a slosh, and it's not the last byte in the string,
+ * it's escaping something - advance past both bytes.
+ */
+ if ((*inchr == '\\') && (inchr[1] != '\0')) {
+ inchr++;
+ newlen++;
+ }
+ inchr++;
+ }
+ outstring = ap_palloc(p, newlen + 1);
+ inchr = instring;
+ outchr = outstring;
+ /*
+ * Now copy the input string to the output string, inserting a slosh
+ * in front of every " that doesn't already have one.
+ */
+ while (*inchr != '\0') {
+ if ((*inchr == '\\') && (inchr[1] != '\0')) {
+ *outchr++ = *inchr++;
+ *outchr++ = *inchr++;
+ }
+ if (*inchr == '"') {
+ *outchr++ = '\\';
+ }
+ if (*inchr != '\0') {
+ *outchr++ = *inchr++;
+ }
+ }
+ *outchr = '\0';
+ return outstring;
+}
--- /dev/null
+/* ====================================================================
+ * Copyright (c) 1996-1999 The Apache Group. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * 4. The names "Apache Server" and "Apache Group" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission. For written permission, please contact
+ * apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ * nor may "Apache" appear in their names without prior written
+ * permission of the Apache Group.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Group and was originally based
+ * on public domain software written at the National Center for
+ * Supercomputing Applications, University of Illinois, Urbana-Champaign.
+ * For more information on the Apache Group and the Apache HTTP server
+ * project, please see <http://www.apache.org/>.
+ *
+ */
+
+/*
+ * util_date.c: date parsing utility routines
+ * These routines are (hopefully) platform-independent.
+ *
+ * 27 Oct 1996 Roy Fielding
+ * Extracted (with many modifications) from mod_proxy.c and
+ * tested with over 50,000 randomly chosen valid date strings
+ * and several hundred variations of invalid date strings.
+ *
+ */
+
+#include "ap_config.h"
+#include "util_date.h"
+#include <ctype.h>
+#include <string.h>
+
+/*
+ * Compare a string to a mask
+ * Mask characters (arbitrary maximum is 256 characters, just in case):
+ * @ - uppercase letter
+ * $ - lowercase letter
+ * & - hex digit
+ * # - digit
+ * ~ - digit or space
+ * * - swallow remaining characters
+ * <x> - exact match for any other character
+ */
+API_EXPORT(int) ap_checkmask(const char *data, const char *mask)
+{
+ int i;
+ char d;
+
+ for (i = 0; i < 256; i++) {
+ d = data[i];
+ switch (mask[i]) {
+ case '\0':
+ return (d == '\0');
+
+ case '*':
+ return 1;
+
+ case '@':
+ if (!ap_isupper(d))
+ return 0;
+ break;
+ case '$':
+ if (!ap_islower(d))
+ return 0;
+ break;
+ case '#':
+ if (!ap_isdigit(d))
+ return 0;
+ break;
+ case '&':
+ if (!ap_isxdigit(d))
+ return 0;
+ break;
+ case '~':
+ if ((d != ' ') && !ap_isdigit(d))
+ return 0;
+ break;
+ default:
+ if (mask[i] != d)
+ return 0;
+ break;
+ }
+ }
+ return 0; /* We only get here if mask is corrupted (exceeds 256) */
+}
+
+/*
+ * tm2sec converts a GMT tm structure into the number of seconds since
+ * 1st January 1970 UT. Note that we ignore tm_wday, tm_yday, and tm_dst.
+ *
+ * The return value is always a valid time_t value -- (time_t)0 is returned
+ * if the input date is outside that capable of being represented by time(),
+ * i.e., before Thu, 01 Jan 1970 00:00:00 for all systems and
+ * beyond 2038 for 32bit systems.
+ *
+ * This routine is intended to be very fast, much faster than mktime().
+ */
+API_EXPORT(time_t) ap_tm2sec(const struct tm * t)
+{
+ int year;
+ time_t days;
+ static const int dayoffset[12] =
+ {306, 337, 0, 31, 61, 92, 122, 153, 184, 214, 245, 275};
+
+ year = t->tm_year;
+
+ if (year < 70 || ((sizeof(time_t) <= 4) && (year >= 138)))
+ return BAD_DATE;
+
+ /* shift new year to 1st March in order to make leap year calc easy */
+
+ if (t->tm_mon < 2)
+ year--;
+
+ /* Find number of days since 1st March 1900 (in the Gregorian calendar). */
+
+ days = year * 365 + year / 4 - year / 100 + (year / 100 + 3) / 4;
+ days += dayoffset[t->tm_mon] + t->tm_mday - 1;
+ days -= 25508; /* 1 jan 1970 is 25508 days since 1 mar 1900 */
+
+ days = ((days * 24 + t->tm_hour) * 60 + t->tm_min) * 60 + t->tm_sec;
+
+ if (days < 0)
+ return BAD_DATE; /* must have overflowed */
+ else
+ return days; /* must be a valid time */
+}
+
+/*
+ * Parses an HTTP date in one of three standard forms:
+ *
+ * Sun, 06 Nov 1994 08:49:37 GMT ; RFC 822, updated by RFC 1123
+ * Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036
+ * Sun Nov 6 08:49:37 1994 ; ANSI C's asctime() format
+ *
+ * and returns the time_t number of seconds since 1 Jan 1970 GMT, or
+ * 0 if this would be out of range or if the date is invalid.
+ *
+ * The restricted HTTP syntax is
+ *
+ * HTTP-date = rfc1123-date | rfc850-date | asctime-date
+ *
+ * rfc1123-date = wkday "," SP date1 SP time SP "GMT"
+ * rfc850-date = weekday "," SP date2 SP time SP "GMT"
+ * asctime-date = wkday SP date3 SP time SP 4DIGIT
+ *
+ * date1 = 2DIGIT SP month SP 4DIGIT
+ * ; day month year (e.g., 02 Jun 1982)
+ * date2 = 2DIGIT "-" month "-" 2DIGIT
+ * ; day-month-year (e.g., 02-Jun-82)
+ * date3 = month SP ( 2DIGIT | ( SP 1DIGIT ))
+ * ; month day (e.g., Jun 2)
+ *
+ * time = 2DIGIT ":" 2DIGIT ":" 2DIGIT
+ * ; 00:00:00 - 23:59:59
+ *
+ * wkday = "Mon" | "Tue" | "Wed"
+ * | "Thu" | "Fri" | "Sat" | "Sun"
+ *
+ * weekday = "Monday" | "Tuesday" | "Wednesday"
+ * | "Thursday" | "Friday" | "Saturday" | "Sunday"
+ *
+ * month = "Jan" | "Feb" | "Mar" | "Apr"
+ * | "May" | "Jun" | "Jul" | "Aug"
+ * | "Sep" | "Oct" | "Nov" | "Dec"
+ *
+ * However, for the sake of robustness (and Netscapeness), we ignore the
+ * weekday and anything after the time field (including the timezone).
+ *
+ * This routine is intended to be very fast; 10x faster than using sscanf.
+ *
+ * Originally from Andrew Daviel <andrew@vancouver-webpages.com>, 29 Jul 96
+ * but many changes since then.
+ *
+ */
+API_EXPORT(time_t) ap_parseHTTPdate(const char *date)
+{
+ struct tm ds;
+ int mint, mon;
+ const char *monstr, *timstr;
+ static const int months[12] =
+ {
+ ('J' << 16) | ('a' << 8) | 'n', ('F' << 16) | ('e' << 8) | 'b',
+ ('M' << 16) | ('a' << 8) | 'r', ('A' << 16) | ('p' << 8) | 'r',
+ ('M' << 16) | ('a' << 8) | 'y', ('J' << 16) | ('u' << 8) | 'n',
+ ('J' << 16) | ('u' << 8) | 'l', ('A' << 16) | ('u' << 8) | 'g',
+ ('S' << 16) | ('e' << 8) | 'p', ('O' << 16) | ('c' << 8) | 't',
+ ('N' << 16) | ('o' << 8) | 'v', ('D' << 16) | ('e' << 8) | 'c'};
+
+ if (!date)
+ return BAD_DATE;
+
+ while (*date && ap_isspace(*date)) /* Find first non-whitespace char */
+ ++date;
+
+ if (*date == '\0')
+ return BAD_DATE;
+
+ if ((date = strchr(date, ' ')) == NULL) /* Find space after weekday */
+ return BAD_DATE;
+
+ ++date; /* Now pointing to first char after space, which should be */
+ /* start of the actual date information for all 3 formats. */
+
+ if (ap_checkmask(date, "## @$$ #### ##:##:## *")) { /* RFC 1123 format */
+ ds.tm_year = ((date[7] - '0') * 10 + (date[8] - '0') - 19) * 100;
+ if (ds.tm_year < 0)
+ return BAD_DATE;
+
+ ds.tm_year += ((date[9] - '0') * 10) + (date[10] - '0');
+
+ ds.tm_mday = ((date[0] - '0') * 10) + (date[1] - '0');
+
+ monstr = date + 3;
+ timstr = date + 12;
+ }
+ else if (ap_checkmask(date, "##-@$$-## ##:##:## *")) { /* RFC 850 format */
+ ds.tm_year = ((date[7] - '0') * 10) + (date[8] - '0');
+ if (ds.tm_year < 70)
+ ds.tm_year += 100;
+
+ ds.tm_mday = ((date[0] - '0') * 10) + (date[1] - '0');
+
+ monstr = date + 3;
+ timstr = date + 10;
+ }
+ else if (ap_checkmask(date, "@$$ ~# ##:##:## ####*")) { /* asctime format */
+ ds.tm_year = ((date[16] - '0') * 10 + (date[17] - '0') - 19) * 100;
+ if (ds.tm_year < 0)
+ return BAD_DATE;
+
+ ds.tm_year += ((date[18] - '0') * 10) + (date[19] - '0');
+
+ if (date[4] == ' ')
+ ds.tm_mday = 0;
+ else
+ ds.tm_mday = (date[4] - '0') * 10;
+
+ ds.tm_mday += (date[5] - '0');
+
+ monstr = date;
+ timstr = date + 7;
+ }
+ else
+ return BAD_DATE;
+
+ if (ds.tm_mday <= 0 || ds.tm_mday > 31)
+ return BAD_DATE;
+
+ ds.tm_hour = ((timstr[0] - '0') * 10) + (timstr[1] - '0');
+ ds.tm_min = ((timstr[3] - '0') * 10) + (timstr[4] - '0');
+ ds.tm_sec = ((timstr[6] - '0') * 10) + (timstr[7] - '0');
+
+ if ((ds.tm_hour > 23) || (ds.tm_min > 59) || (ds.tm_sec > 61))
+ return BAD_DATE;
+
+ mint = (monstr[0] << 16) | (monstr[1] << 8) | monstr[2];
+ for (mon = 0; mon < 12; mon++)
+ if (mint == months[mon])
+ break;
+ if (mon == 12)
+ return BAD_DATE;
+
+ if ((ds.tm_mday == 31) && (mon == 3 || mon == 5 || mon == 8 || mon == 10))
+ return BAD_DATE;
+
+ /* February gets special check for leapyear */
+
+ if ((mon == 1) &&
+ ((ds.tm_mday > 29)
+ || ((ds.tm_mday == 29)
+ && ((ds.tm_year & 3)
+ || (((ds.tm_year % 100) == 0)
+ && (((ds.tm_year % 400) != 100)))))))
+ return BAD_DATE;
+
+ ds.tm_mon = mon;
+
+ return ap_tm2sec(&ds);
+}
--- /dev/null
+/* ====================================================================
+ * Copyright (c) 1995-1999 The Apache Group. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * 4. The names "Apache Server" and "Apache Group" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission. For written permission, please contact
+ * apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ * nor may "Apache" appear in their names without prior written
+ * permission of the Apache Group.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Group and was originally based
+ * on public domain software written at the National Center for
+ * Supercomputing Applications, University of Illinois, Urbana-Champaign.
+ * For more information on the Apache Group and the Apache HTTP server
+ * project, please see <http://www.apache.org/>.
+ *
+ */
+
+/************************************************************************
+ * NCSA HTTPd Server
+ * Software Development Group
+ * National Center for Supercomputing Applications
+ * University of Illinois at Urbana-Champaign
+ * 605 E. Springfield, Champaign, IL 61820
+ * httpd@ncsa.uiuc.edu
+ *
+ * Copyright (C) 1995, Board of Trustees of the University of Illinois
+ *
+ ************************************************************************
+ *
+ * md5.c: NCSA HTTPd code which uses the md5c.c RSA Code
+ *
+ * Original Code Copyright (C) 1994, Jeff Hostetler, Spyglass, Inc.
+ * Portions of Content-MD5 code Copyright (C) 1993, 1994 by Carnegie Mellon
+ * University (see Copyright below).
+ * Portions of Content-MD5 code Copyright (C) 1991 Bell Communications
+ * Research, Inc. (Bellcore) (see Copyright below).
+ * Portions extracted from mpack, John G. Myers - jgm+@cmu.edu
+ * Content-MD5 Code contributed by Martin Hamilton (martin@net.lut.ac.uk)
+ *
+ */
+
+
+
+/* md5.c --Module Interface to MD5. */
+/* Jeff Hostetler, Spyglass, Inc., 1994. */
+
+#include "httpd.h"
+#include "util_md5.h"
+
+API_EXPORT(char *) ap_md5_binary(pool *p, const unsigned char *buf, int length)
+{
+ const char *hex = "0123456789abcdef";
+ AP_MD5_CTX my_md5;
+ unsigned char hash[16];
+ char *r, result[33];
+ int i;
+
+ /*
+ * Take the MD5 hash of the string argument.
+ */
+
+ ap_MD5Init(&my_md5);
+ ap_MD5Update(&my_md5, buf, (unsigned int)length);
+ ap_MD5Final(hash, &my_md5);
+
+ for (i = 0, r = result; i < 16; i++) {
+ *r++ = hex[hash[i] >> 4];
+ *r++ = hex[hash[i] & 0xF];
+ }
+ *r = '\0';
+
+ return ap_pstrdup(p, result);
+}
+
+API_EXPORT(char *) ap_md5(pool *p, const unsigned char *string)
+{
+ return ap_md5_binary(p, string, (int) strlen((char *)string));
+}
+
+/* these portions extracted from mpack, John G. Myers - jgm+@cmu.edu */
+
+/* (C) Copyright 1993,1994 by Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of Carnegie
+ * Mellon University not be used in advertising or publicity
+ * pertaining to distribution of the software without specific,
+ * written prior permission. Carnegie Mellon University makes no
+ * representations about the suitability of this software for any
+ * purpose. It is provided "as is" without express or implied
+ * warranty.
+ *
+ * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
+ * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ */
+
+/*
+ * Copyright (c) 1991 Bell Communications Research, Inc. (Bellcore)
+ *
+ * Permission to use, copy, modify, and distribute this material
+ * for any purpose and without fee is hereby granted, provided
+ * that the above copyright notice and this permission notice
+ * appear in all copies, and that the name of Bellcore not be
+ * used in advertising or publicity pertaining to this
+ * material without the specific, prior written permission
+ * of an authorized representative of Bellcore. BELLCORE
+ * MAKES NO REPRESENTATIONS ABOUT THE ACCURACY OR SUITABILITY
+ * OF THIS MATERIAL FOR ANY PURPOSE. IT IS PROVIDED "AS IS",
+ * WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES.
+ */
+
+static char basis_64[] =
+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+API_EXPORT(char *) ap_md5contextTo64(pool *a, AP_MD5_CTX * context)
+{
+ unsigned char digest[18];
+ char *encodedDigest;
+ int i;
+ char *p;
+
+ encodedDigest = (char *) ap_pcalloc(a, 25 * sizeof(char));
+
+ ap_MD5Final(digest, context);
+ digest[sizeof(digest) - 1] = digest[sizeof(digest) - 2] = 0;
+
+ p = encodedDigest;
+ for (i = 0; i < sizeof(digest); i += 3) {
+ *p++ = basis_64[digest[i] >> 2];
+ *p++ = basis_64[((digest[i] & 0x3) << 4) | ((int) (digest[i + 1] & 0xF0) >> 4)];
+ *p++ = basis_64[((digest[i + 1] & 0xF) << 2) | ((int) (digest[i + 2] & 0xC0) >> 6)];
+ *p++ = basis_64[digest[i + 2] & 0x3F];
+ }
+ *p-- = '\0';
+ *p-- = '=';
+ *p-- = '=';
+ return encodedDigest;
+}
+
+#ifdef CHARSET_EBCDIC
+
+API_EXPORT(char *) ap_md5digest(pool *p, FILE *infile, int convert)
+{
+ AP_MD5_CTX context;
+ unsigned char buf[1000];
+ long length = 0;
+ int nbytes;
+
+ ap_MD5Init(&context);
+ while ((nbytes = fread(buf, 1, sizeof(buf), infile))) {
+ length += nbytes;
+ if (!convert) {
+ ascii2ebcdic(buf, buf, nbytes);
+ }
+ ap_MD5Update(&context, buf, nbytes);
+ }
+ rewind(infile);
+ return ap_md5contextTo64(p, &context);
+}
+
+#else
+
+API_EXPORT(char *) ap_md5digest(pool *p, FILE *infile)
+{
+ AP_MD5_CTX context;
+ unsigned char buf[1000];
+ long length = 0;
+ unsigned int nbytes;
+
+ ap_MD5Init(&context);
+ while ((nbytes = fread(buf, 1, sizeof(buf), infile))) {
+ length += nbytes;
+ ap_MD5Update(&context, buf, nbytes);
+ }
+ rewind(infile);
+ return ap_md5contextTo64(p, &context);
+}
+
+#endif /* CHARSET_EBCDIC */
--- /dev/null
+/* ====================================================================
+ * Copyright (c) 1995-1999 The Apache Group. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * 4. The names "Apache Server" and "Apache Group" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission. For written permission, please contact
+ * apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ * nor may "Apache" appear in their names without prior written
+ * permission of the Apache Group.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Group and was originally based
+ * on public domain software written at the National Center for
+ * Supercomputing Applications, University of Illinois, Urbana-Champaign.
+ * For more information on the Apache Group and the Apache HTTP server
+ * project, please see <http://www.apache.org/>.
+ *
+ */
+
+#define CORE_PRIVATE
+#include "httpd.h"
+#include "http_config.h"
+#include "http_conf_globals.h"
+#include "http_main.h"
+#include "http_log.h"
+#include "http_protocol.h"
+#include "http_core.h" /* For document_root. Sigh... */
+#include "http_request.h" /* for sub_req_lookup_uri() */
+#include "util_script.h"
+#include "util_date.h" /* For parseHTTPdate() */
+
+#ifdef OS2
+#define INCL_DOS
+#include <os2.h>
+#endif
+
+/*
+ * Various utility functions which are common to a whole lot of
+ * script-type extensions mechanisms, and might as well be gathered
+ * in one place (if only to avoid creating inter-module dependancies
+ * where there don't have to be).
+ */
+
+#define MALFORMED_MESSAGE "malformed header from script. Bad header="
+#define MALFORMED_HEADER_LENGTH_TO_SHOW 30
+
+/* If a request includes query info in the URL (stuff after "?"), and
+ * the query info does not contain "=" (indicative of a FORM submission),
+ * then this routine is called to create the argument list to be passed
+ * to the CGI script. When suexec is enabled, the suexec path, user, and
+ * group are the first three arguments to be passed; if not, all three
+ * must be NULL. The query info is split into separate arguments, where
+ * "+" is the separator between keyword arguments.
+ *
+ * XXXX: note that the WIN32 code uses one of the suexec strings
+ * to pass an interpreter name. Remember this if changing the way they
+ * are handled in create_argv.
+ *
+ */
+static char **create_argv(pool *p, char *path, char *user, char *group,
+ char *av0, const char *args)
+{
+ int x, numwords;
+ char **av;
+ char *w;
+ int idx = 0;
+
+ /* count the number of keywords */
+
+ for (x = 0, numwords = 1; args[x]; x++) {
+ if (args[x] == '+') {
+ ++numwords;
+ }
+ }
+
+ if (numwords > APACHE_ARG_MAX - 5) {
+ numwords = APACHE_ARG_MAX - 5; /* Truncate args to prevent overrun */
+ }
+ av = (char **) ap_palloc(p, (numwords + 5) * sizeof(char *));
+
+ if (path) {
+ av[idx++] = path;
+ }
+ if (user) {
+ av[idx++] = user;
+ }
+ if (group) {
+ av[idx++] = group;
+ }
+
+ av[idx++] = av0;
+
+ for (x = 1; x <= numwords; x++) {
+ w = ap_getword_nulls(p, &args, '+');
+ ap_unescape_url(w);
+ av[idx++] = ap_escape_shell_cmd(p, w);
+ }
+ av[idx] = NULL;
+ return av;
+}
+
+
+static char *http2env(pool *a, char *w)
+{
+ char *res = ap_pstrcat(a, "HTTP_", w, NULL);
+ char *cp = res;
+
+ while (*++cp) {
+ if (!ap_isalnum(*cp) && *cp != '_') {
+ *cp = '_';
+ }
+ else {
+ *cp = ap_toupper(*cp);
+ }
+ }
+
+ return res;
+}
+
+API_EXPORT(char **) ap_create_environment(pool *p, table *t)
+{
+ array_header *env_arr = ap_table_elts(t);
+ table_entry *elts = (table_entry *) env_arr->elts;
+ char **env = (char **) ap_palloc(p, (env_arr->nelts + 2) * sizeof(char *));
+ int i, j;
+ char *tz;
+ char *whack;
+
+ j = 0;
+ if (!ap_table_get(t, "TZ")) {
+ tz = getenv("TZ");
+ if (tz != NULL) {
+ env[j++] = ap_pstrcat(p, "TZ=", tz, NULL);
+ }
+ }
+ for (i = 0; i < env_arr->nelts; ++i) {
+ if (!elts[i].key) {
+ continue;
+ }
+ env[j] = ap_pstrcat(p, elts[i].key, "=", elts[i].val, NULL);
+ whack = env[j];
+ if (ap_isdigit(*whack)) {
+ *whack++ = '_';
+ }
+ while (*whack != '=') {
+ if (!ap_isalnum(*whack) && *whack != '_') {
+ *whack = '_';
+ }
+ ++whack;
+ }
+ ++j;
+ }
+
+ env[j] = NULL;
+ return env;
+}
+
+API_EXPORT(void) ap_add_common_vars(request_rec *r)
+{
+ table *e;
+ server_rec *s = r->server;
+ conn_rec *c = r->connection;
+ const char *rem_logname;
+ char *env_path;
+#ifdef WIN32
+ char *env_temp;
+#endif
+ const char *host;
+ array_header *hdrs_arr = ap_table_elts(r->headers_in);
+ table_entry *hdrs = (table_entry *) hdrs_arr->elts;
+ int i;
+
+ /* use a temporary table which we'll overlap onto
+ * r->subprocess_env later
+ */
+ e = ap_make_table(r->pool, 25 + hdrs_arr->nelts);
+
+ /* First, add environment vars from headers... this is as per
+ * CGI specs, though other sorts of scripting interfaces see
+ * the same vars...
+ */
+
+ for (i = 0; i < hdrs_arr->nelts; ++i) {
+ if (!hdrs[i].key) {
+ continue;
+ }
+
+ /* A few headers are special cased --- Authorization to prevent
+ * rogue scripts from capturing passwords; content-type and -length
+ * for no particular reason.
+ */
+
+ if (!strcasecmp(hdrs[i].key, "Content-type")) {
+ ap_table_addn(e, "CONTENT_TYPE", hdrs[i].val);
+ }
+ else if (!strcasecmp(hdrs[i].key, "Content-length")) {
+ ap_table_addn(e, "CONTENT_LENGTH", hdrs[i].val);
+ }
+ /*
+ * You really don't want to disable this check, since it leaves you
+ * wide open to CGIs stealing passwords and people viewing them
+ * in the environment with "ps -e". But, if you must...
+ */
+#ifndef SECURITY_HOLE_PASS_AUTHORIZATION
+ else if (!strcasecmp(hdrs[i].key, "Authorization")
+ || !strcasecmp(hdrs[i].key, "Proxy-Authorization")) {
+ continue;
+ }
+#endif
+ else {
+ ap_table_addn(e, http2env(r->pool, hdrs[i].key), hdrs[i].val);
+ }
+ }
+
+ if (!(env_path = getenv("PATH"))) {
+ env_path = DEFAULT_PATH;
+ }
+
+#ifdef WIN32
+ if (env_temp = getenv("SystemRoot")) {
+ ap_table_addn(e, "SystemRoot", env_temp);
+ }
+ if (env_temp = getenv("COMSPEC")) {
+ ap_table_addn(e, "COMSPEC", env_temp);
+ }
+ if (env_temp = getenv("WINDIR")) {
+ ap_table_addn(e, "WINDIR", env_temp);
+ }
+#endif
+
+ ap_table_addn(e, "PATH", env_path);
+ ap_table_addn(e, "SERVER_SIGNATURE", ap_psignature("", r));
+ ap_table_addn(e, "SERVER_SOFTWARE", ap_get_server_version());
+ ap_table_addn(e, "SERVER_NAME", ap_get_server_name(r));
+ ap_table_addn(e, "SERVER_ADDR", r->connection->local_ip); /* Apache */
+ ap_table_addn(e, "SERVER_PORT",
+ ap_psprintf(r->pool, "%u", ap_get_server_port(r)));
+ host = ap_get_remote_host(c, r->per_dir_config, REMOTE_HOST);
+ if (host) {
+ ap_table_addn(e, "REMOTE_HOST", host);
+ }
+ ap_table_addn(e, "REMOTE_ADDR", c->remote_ip);
+ ap_table_addn(e, "DOCUMENT_ROOT", ap_document_root(r)); /* Apache */
+ ap_table_addn(e, "SERVER_ADMIN", s->server_admin); /* Apache */
+ ap_table_addn(e, "SCRIPT_FILENAME", r->filename); /* Apache */
+
+ ap_table_addn(e, "REMOTE_PORT",
+ ap_psprintf(r->pool, "%d", ntohs(c->remote_addr.sin_port)));
+
+ if (c->user) {
+ ap_table_addn(e, "REMOTE_USER", c->user);
+ }
+ if (c->ap_auth_type) {
+ ap_table_addn(e, "AUTH_TYPE", c->ap_auth_type);
+ }
+ rem_logname = ap_get_remote_logname(r);
+ if (rem_logname) {
+ ap_table_addn(e, "REMOTE_IDENT", ap_pstrdup(r->pool, rem_logname));
+ }
+
+ /* Apache custom error responses. If we have redirected set two new vars */
+
+ if (r->prev) {
+ if (r->prev->args) {
+ ap_table_addn(e, "REDIRECT_QUERY_STRING", r->prev->args);
+ }
+ if (r->prev->uri) {
+ ap_table_addn(e, "REDIRECT_URL", r->prev->uri);
+ }
+ }
+
+ ap_overlap_tables(r->subprocess_env, e, AP_OVERLAP_TABLES_SET);
+}
+
+/* This "cute" little function comes about because the path info on
+ * filenames and URLs aren't always the same. So we take the two,
+ * and find as much of the two that match as possible.
+ */
+
+API_EXPORT(int) ap_find_path_info(const char *uri, const char *path_info)
+{
+ int lu = strlen(uri);
+ int lp = strlen(path_info);
+
+ while (lu-- && lp-- && uri[lu] == path_info[lp]);
+
+ if (lu == -1) {
+ lu = 0;
+ }
+
+ while (uri[lu] != '\0' && uri[lu] != '/') {
+ lu++;
+ }
+ return lu;
+}
+
+/* Obtain the Request-URI from the original request-line, returning
+ * a new string from the request pool containing the URI or "".
+ */
+static char *original_uri(request_rec *r)
+{
+ char *first, *last;
+
+ if (r->the_request == NULL) {
+ return (char *) ap_pcalloc(r->pool, 1);
+ }
+
+ first = r->the_request; /* use the request-line */
+
+ while (*first && !ap_isspace(*first)) {
+ ++first; /* skip over the method */
+ }
+ while (ap_isspace(*first)) {
+ ++first; /* and the space(s) */
+ }
+
+ last = first;
+ while (*last && !ap_isspace(*last)) {
+ ++last; /* end at next whitespace */
+ }
+
+ return ap_pstrndup(r->pool, first, last - first);
+}
+
+API_EXPORT(void) ap_add_cgi_vars(request_rec *r)
+{
+ table *e = r->subprocess_env;
+
+ ap_table_setn(e, "GATEWAY_INTERFACE", "CGI/1.1");
+ ap_table_setn(e, "SERVER_PROTOCOL", r->protocol);
+ ap_table_setn(e, "REQUEST_METHOD", r->method);
+ ap_table_setn(e, "QUERY_STRING", r->args ? r->args : "");
+ ap_table_setn(e, "REQUEST_URI", original_uri(r));
+
+ /* Note that the code below special-cases scripts run from includes,
+ * because it "knows" that the sub_request has been hacked to have the
+ * args and path_info of the original request, and not any that may have
+ * come with the script URI in the include command. Ugh.
+ */
+
+ if (!strcmp(r->protocol, "INCLUDED")) {
+ ap_table_setn(e, "SCRIPT_NAME", r->uri);
+ if (r->path_info && *r->path_info) {
+ ap_table_setn(e, "PATH_INFO", r->path_info);
+ }
+ }
+ else if (!r->path_info || !*r->path_info) {
+ ap_table_setn(e, "SCRIPT_NAME", r->uri);
+ }
+ else {
+ int path_info_start = ap_find_path_info(r->uri, r->path_info);
+
+ ap_table_setn(e, "SCRIPT_NAME",
+ ap_pstrndup(r->pool, r->uri, path_info_start));
+
+ ap_table_setn(e, "PATH_INFO", r->path_info);
+ }
+
+ if (r->path_info && r->path_info[0]) {
+ /*
+ * To get PATH_TRANSLATED, treat PATH_INFO as a URI path.
+ * Need to re-escape it for this, since the entire URI was
+ * un-escaped before we determined where the PATH_INFO began.
+ */
+ request_rec *pa_req;
+
+ pa_req = ap_sub_req_lookup_uri(ap_escape_uri(r->pool, r->path_info), r);
+
+ if (pa_req->filename) {
+#ifdef WIN32
+ char buffer[HUGE_STRING_LEN];
+#endif
+ char *pt = ap_pstrcat(r->pool, pa_req->filename, pa_req->path_info,
+ NULL);
+#ifdef WIN32
+ /* We need to make this a real Windows path name */
+ GetFullPathName(pt, HUGE_STRING_LEN, buffer, NULL);
+ ap_table_setn(e, "PATH_TRANSLATED", ap_pstrdup(r->pool, buffer));
+#else
+ ap_table_setn(e, "PATH_TRANSLATED", pt);
+#endif
+ }
+ ap_destroy_sub_req(pa_req);
+ }
+}
+
+
+static int set_cookie_doo_doo(void *v, const char *key, const char *val)
+{
+ ap_table_addn(v, key, val);
+ return 1;
+}
+
+API_EXPORT(int) ap_scan_script_header_err_core(request_rec *r, char *buffer,
+ int (*getsfunc) (char *, int, void *),
+ void *getsfunc_data)
+{
+ char x[MAX_STRING_LEN];
+ char *w, *l;
+ int p;
+ int cgi_status = HTTP_OK;
+ table *merge;
+ table *cookie_table;
+
+ if (buffer) {
+ *buffer = '\0';
+ }
+ w = buffer ? buffer : x;
+
+ ap_hard_timeout("read script header", r);
+
+ /* temporary place to hold headers to merge in later */
+ merge = ap_make_table(r->pool, 10);
+
+ /* The HTTP specification says that it is legal to merge duplicate
+ * headers into one. Some browsers that support Cookies don't like
+ * merged headers and prefer that each Set-Cookie header is sent
+ * separately. Lets humour those browsers by not merging.
+ * Oh what a pain it is.
+ */
+ cookie_table = ap_make_table(r->pool, 2);
+ ap_table_do(set_cookie_doo_doo, cookie_table, r->err_headers_out, "Set-Cookie", NULL);
+
+ while (1) {
+
+ if ((*getsfunc) (w, MAX_STRING_LEN - 1, getsfunc_data) == 0) {
+ ap_kill_timeout(r);
+ ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
+ "Premature end of script headers: %s", r->filename);
+ return HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ /* Delete terminal (CR?)LF */
+
+ p = strlen(w);
+ if (p > 0 && w[p - 1] == '\n') {
+ if (p > 1 && w[p - 2] == '\015') {
+ w[p - 2] = '\0';
+ }
+ else {
+ w[p - 1] = '\0';
+ }
+ }
+
+ /*
+ * If we've finished reading the headers, check to make sure any
+ * HTTP/1.1 conditions are met. If so, we're done; normal processing
+ * will handle the script's output. If not, just return the error.
+ * The appropriate thing to do would be to send the script process a
+ * SIGPIPE to let it know we're ignoring it, close the channel to the
+ * script process, and *then* return the failed-to-meet-condition
+ * error. Otherwise we'd be waiting for the script to finish
+ * blithering before telling the client the output was no good.
+ * However, we don't have the information to do that, so we have to
+ * leave it to an upper layer.
+ */
+ if (w[0] == '\0') {
+ int cond_status = OK;
+
+ ap_kill_timeout(r);
+ if ((cgi_status == HTTP_OK) && (r->method_number == M_GET)) {
+ cond_status = ap_meets_conditions(r);
+ }
+ ap_overlap_tables(r->err_headers_out, merge,
+ AP_OVERLAP_TABLES_MERGE);
+ if (!ap_is_empty_table(cookie_table)) {
+ /* the cookies have already been copied to the cookie_table */
+ ap_table_unset(r->err_headers_out, "Set-Cookie");
+ r->err_headers_out = ap_overlay_tables(r->pool,
+ r->err_headers_out, cookie_table);
+ }
+ return cond_status;
+ }
+
+ /* if we see a bogus header don't ignore it. Shout and scream */
+
+#ifdef CHARSET_EBCDIC
+ /* Chances are that we received an ASCII header text instead of
+ * the expected EBCDIC header lines. Try to auto-detect:
+ */
+ if (!(l = strchr(w, ':'))) {
+ int maybeASCII = 0, maybeEBCDIC = 0;
+ char *cp;
+
+ for (cp = w; *cp != '\0'; ++cp) {
+ if (isprint(*cp) && !isprint(os_toebcdic[*cp]))
+ ++maybeEBCDIC;
+ if (!isprint(*cp) && isprint(os_toebcdic[*cp]))
+ ++maybeASCII;
+ }
+ if (maybeASCII > maybeEBCDIC) {
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r->server,
+ "CGI Interface Error: Script headers apparently ASCII: (CGI = %s)", r->filename);
+ ascii2ebcdic(w, w, cp - w);
+ }
+ }
+#endif
+ if (!(l = strchr(w, ':'))) {
+ char malformed[(sizeof MALFORMED_MESSAGE) + 1
+ + MALFORMED_HEADER_LENGTH_TO_SHOW];
+
+ strcpy(malformed, MALFORMED_MESSAGE);
+ strncat(malformed, w, MALFORMED_HEADER_LENGTH_TO_SHOW);
+
+ if (!buffer) {
+ /* Soak up all the script output - may save an outright kill */
+ while ((*getsfunc) (w, MAX_STRING_LEN - 1, getsfunc_data)) {
+ continue;
+ }
+ }
+
+ ap_kill_timeout(r);
+ ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
+ "%s: %s", malformed, r->filename);
+ return HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ *l++ = '\0';
+ while (*l && ap_isspace(*l)) {
+ ++l;
+ }
+
+ if (!strcasecmp(w, "Content-type")) {
+ char *tmp;
+
+ /* Nuke trailing whitespace */
+
+ char *endp = l + strlen(l) - 1;
+ while (endp > l && ap_isspace(*endp)) {
+ *endp-- = '\0';
+ }
+
+ tmp = ap_pstrdup(r->pool, l);
+ ap_content_type_tolower(tmp);
+ r->content_type = tmp;
+ }
+ /*
+ * If the script returned a specific status, that's what
+ * we'll use - otherwise we assume 200 OK.
+ */
+ else if (!strcasecmp(w, "Status")) {
+ r->status = cgi_status = atoi(l);
+ r->status_line = ap_pstrdup(r->pool, l);
+ }
+ else if (!strcasecmp(w, "Location")) {
+ ap_table_set(r->headers_out, w, l);
+ }
+ else if (!strcasecmp(w, "Content-Length")) {
+ ap_table_set(r->headers_out, w, l);
+ }
+ else if (!strcasecmp(w, "Transfer-Encoding")) {
+ ap_table_set(r->headers_out, w, l);
+ }
+ /*
+ * If the script gave us a Last-Modified header, we can't just
+ * pass it on blindly because of restrictions on future values.
+ */
+ else if (!strcasecmp(w, "Last-Modified")) {
+ time_t mtime = ap_parseHTTPdate(l);
+
+ ap_update_mtime(r, mtime);
+ ap_set_last_modified(r);
+ }
+ else if (!strcasecmp(w, "Set-Cookie")) {
+ ap_table_add(cookie_table, w, l);
+ }
+ else {
+ ap_table_add(merge, w, l);
+ }
+ }
+}
+
+static int getsfunc_FILE(char *buf, int len, void *f)
+{
+ return fgets(buf, len, (FILE *) f) != NULL;
+}
+
+API_EXPORT(int) ap_scan_script_header_err(request_rec *r, FILE *f,
+ char *buffer)
+{
+ return ap_scan_script_header_err_core(r, buffer, getsfunc_FILE, f);
+}
+
+static int getsfunc_BUFF(char *w, int len, void *fb)
+{
+ return ap_bgets(w, len, (BUFF *) fb) > 0;
+}
+
+API_EXPORT(int) ap_scan_script_header_err_buff(request_rec *r, BUFF *fb,
+ char *buffer)
+{
+ return ap_scan_script_header_err_core(r, buffer, getsfunc_BUFF, fb);
+}
+
+
+API_EXPORT(void) ap_send_size(size_t size, request_rec *r)
+{
+ /* XXX: this -1 thing is a gross hack */
+ if (size == (size_t)-1) {
+ ap_rputs(" -", r);
+ }
+ else if (!size) {
+ ap_rputs(" 0k", r);
+ }
+ else if (size < 1024) {
+ ap_rputs(" 1k", r);
+ }
+ else if (size < 1048576) {
+ ap_rprintf(r, "%4dk", (size + 512) / 1024);
+ }
+ else if (size < 103809024) {
+ ap_rprintf(r, "%4.1fM", size / 1048576.0);
+ }
+ else {
+ ap_rprintf(r, "%4dM", (size + 524288) / 1048576);
+ }
+}
+
+#if defined(OS2) || defined(WIN32)
+static char **create_argv_cmd(pool *p, char *av0, const char *args, char *path)
+{
+ register int x, n;
+ char **av;
+ char *w;
+
+ for (x = 0, n = 2; args[x]; x++) {
+ if (args[x] == '+') {
+ ++n;
+ }
+ }
+
+ /* Add extra strings to array. */
+ n = n + 2;
+
+ av = (char **) ap_palloc(p, (n + 1) * sizeof(char *));
+ av[0] = av0;
+
+ /* Now insert the extra strings we made room for above. */
+ av[1] = strdup("/C");
+ av[2] = strdup(path);
+
+ for (x = (1 + 2); x < n; x++) {
+ w = ap_getword(p, &args, '+');
+ ap_unescape_url(w);
+ av[x] = ap_escape_shell_cmd(p, w);
+ }
+ av[n] = NULL;
+ return av;
+}
+#endif
+
+
+API_EXPORT(int) ap_call_exec(request_rec *r, child_info *pinfo, char *argv0,
+ char **env, int shellcmd)
+{
+ int pid = 0;
+#if defined(RLIMIT_CPU) || defined(RLIMIT_NPROC) || \
+ defined(RLIMIT_DATA) || defined(RLIMIT_VMEM) || defined (RLIMIT_AS)
+
+ core_dir_config *conf;
+ conf = (core_dir_config *) ap_get_module_config(r->per_dir_config,
+ &core_module);
+
+#endif
+
+#if !defined(WIN32) && !defined(OS2)
+ /* the fd on r->server->error_log is closed, but we need somewhere to
+ * put the error messages from the log_* functions. So, we use stderr,
+ * since that is better than allowing errors to go unnoticed. Don't do
+ * this on Win32, though, since we haven't fork()'d.
+ */
+ r->server->error_log = stderr;
+#endif
+
+#ifdef RLIMIT_CPU
+ if (conf->limit_cpu != NULL) {
+ if ((setrlimit(RLIMIT_CPU, conf->limit_cpu)) != 0) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, r->server,
+ "setrlimit: failed to set CPU usage limit");
+ }
+ }
+#endif
+#ifdef RLIMIT_NPROC
+ if (conf->limit_nproc != NULL) {
+ if ((setrlimit(RLIMIT_NPROC, conf->limit_nproc)) != 0) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, r->server,
+ "setrlimit: failed to set process limit");
+ }
+ }
+#endif
+#if defined(RLIMIT_AS)
+ if (conf->limit_mem != NULL) {
+ if ((setrlimit(RLIMIT_AS, conf->limit_mem)) != 0) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, r->server,
+ "setrlimit(RLIMIT_AS): failed to set memory "
+ "usage limit");
+ }
+ }
+#elif defined(RLIMIT_DATA)
+ if (conf->limit_mem != NULL) {
+ if ((setrlimit(RLIMIT_DATA, conf->limit_mem)) != 0) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, r->server,
+ "setrlimit(RLIMIT_DATA): failed to set memory "
+ "usage limit");
+ }
+ }
+#elif defined(RLIMIT_VMEM)
+ if (conf->limit_mem != NULL) {
+ if ((setrlimit(RLIMIT_VMEM, conf->limit_mem)) != 0) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, r->server,
+ "setrlimit(RLIMIT_VMEM): failed to set memory "
+ "usage limit");
+ }
+ }
+#endif
+
+#ifdef OS2
+ {
+ /* Additions by Alec Kloss, to allow exec'ing of scripts under OS/2 */
+ int is_script = 0;
+ char interpreter[2048]; /* hope it's enough for the interpreter path */
+ char error_object[260];
+ FILE *program;
+ char *cmdline = r->filename, *cmdline_pos;
+ int cmdlen;
+ char *args = "", *args_end;
+ ULONG rc;
+ RESULTCODES rescodes;
+ int env_len, e;
+ char *env_block, *env_block_pos;
+
+ if (r->args && r->args[0] && !strchr(r->args, '='))
+ args = r->args;
+
+ program = fopen(r->filename, "rt");
+
+ if (!program) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, r, "fopen(%s) failed",
+ r->filename);
+ return (pid);
+ }
+
+ fgets(interpreter, sizeof(interpreter), program);
+ fclose(program);
+
+ if (!strncmp(interpreter, "#!", 2)) {
+ is_script = 1;
+ interpreter[strlen(interpreter) - 1] = '\0';
+ if (interpreter[2] != '/' && interpreter[2] != '\\' && interpreter[3] != ':') {
+ char buffer[300];
+ if (DosSearchPath(SEARCH_ENVIRONMENT, "PATH", interpreter+2, buffer, sizeof(buffer)) == 0) {
+ strcpy(interpreter+2, buffer);
+ } else {
+ strcat(interpreter, ".exe");
+ if (DosSearchPath(SEARCH_ENVIRONMENT, "PATH", interpreter+2, buffer, sizeof(buffer)) == 0) {
+ strcpy(interpreter+2, buffer);
+ }
+ }
+ }
+ }
+
+ if (is_script) {
+ cmdline = ap_pstrcat(r->pool, interpreter+2, " ", r->filename, NULL);
+ }
+ else if (strstr(strupr(r->filename), ".CMD") > 0) {
+ /* Special case to allow use of REXX commands as scripts. */
+ os2pathname(r->filename);
+ cmdline = ap_pstrcat(r->pool, SHELL_PATH, " /C ", r->filename, NULL);
+ }
+ else {
+ cmdline = r->filename;
+ }
+
+ args = ap_pstrdup(r->pool, args);
+ ap_unescape_url(args);
+ args = ap_double_quotes(r->pool, args);
+ args_end = args + strlen(args);
+
+ if (args_end - args > 4000) { /* cmd.exe won't handle lines longer than 4k */
+ args_end = args + 4000;
+ *args_end = 0;
+ }
+
+ /* +4 = 1 space between progname and args, 2 for double null at end, 2 for possible quote on first arg */
+ cmdlen = strlen(cmdline) + strlen(args) + 4;
+ cmdline_pos = cmdline;
+
+ while (*cmdline_pos) {
+ cmdlen += 2 * (*cmdline_pos == '+'); /* Allow space for each arg to be quoted */
+ cmdline_pos++;
+ }
+
+ cmdline = ap_pstrndup(r->pool, cmdline, cmdlen);
+ cmdline_pos = cmdline + strlen(cmdline);
+
+ while (args < args_end) {
+ char *arg;
+
+ arg = ap_getword_nc(r->pool, &args, '+');
+
+ if (strpbrk(arg, "&|<> "))
+ arg = ap_pstrcat(r->pool, "\"", arg, "\"", NULL);
+
+ *(cmdline_pos++) = ' ';
+ strcpy(cmdline_pos, arg);
+ cmdline_pos += strlen(cmdline_pos);
+ }
+
+ *(++cmdline_pos) = 0; /* Add required second terminator */
+ args = strchr(cmdline, ' ');
+
+ if (args) {
+ *args = 0;
+ args++;
+ }
+
+ /* Create environment block from list of envariables */
+ for (env_len=1, e=0; env[e]; e++)
+ env_len += strlen(env[e]) + 1;
+
+ env_block = ap_palloc(r->pool, env_len);
+ env_block_pos = env_block;
+
+ for (e=0; env[e]; e++) {
+ strcpy(env_block_pos, env[e]);
+ env_block_pos += strlen(env_block_pos) + 1;
+ }
+
+ *env_block_pos = 0; /* environment block is terminated by a double null */
+
+ rc = DosExecPgm(error_object, sizeof(error_object), EXEC_ASYNC, cmdline, env_block, &rescodes, cmdline);
+
+ if (rc) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, r, "DosExecPgm(%s %s) failed, %s - %s",
+ cmdline, args ? args : "", ap_os_error_message(rc), error_object );
+ return -1;
+ }
+
+ return rescodes.codeTerminate;
+ }
+#elif defined(WIN32)
+ {
+ /* Adapted from Alec Kloss' work for OS/2 */
+ char *interpreter = NULL;
+ char *arguments = NULL;
+ char *ext = NULL;
+ char *exename = NULL;
+ char *s = NULL;
+ char *quoted_filename;
+ char *pCommand;
+ char *pEnvBlock, *pNext;
+
+ int i;
+ int iEnvBlockLen;
+
+ file_type_e fileType;
+
+ STARTUPINFO si;
+ PROCESS_INFORMATION pi;
+
+ memset(&si, 0, sizeof(si));
+ memset(&pi, 0, sizeof(pi));
+
+ pid = -1;
+
+ if (!shellcmd) {
+
+ fileType = ap_get_win32_interpreter(r, &interpreter);
+
+ if (fileType == eFileTypeUNKNOWN) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, r,
+ "%s is not executable; ensure interpreted scripts have "
+ "\"#!\" first line",
+ r->filename);
+ return (pid);
+ }
+
+ /*
+ * Look at the arguments...
+ */
+ arguments = "";
+ if ((r->args) && (r->args[0]) && !strchr(r->args, '=')) {
+ /* If we are in this leg, there are some other arguments
+ * that we must include in the execution of the CGI.
+ * Because CreateProcess is the way it is, we have to
+ * create a command line like format for the execution
+ * of the CGI. This means we need to create on long
+ * string with the executable and arguments.
+ *
+ * The arguments string comes in the request structure,
+ * and each argument is separated by a '+'. We'll replace
+ * these pluses with spaces.
+ */
+
+ int iStringSize = 0;
+ int x;
+
+ /*
+ * Duplicate the request structure string so we don't change it.
+ */
+ arguments = ap_pstrdup(r->pool, r->args);
+
+ /*
+ * Change the '+' to ' '
+ */
+ for (x=0; arguments[x]; x++) {
+ if ('+' == arguments[x]) {
+ arguments[x] = ' ';
+ }
+ }
+
+ /*
+ * We need to unescape any characters that are
+ * in the arguments list.
+ */
+ ap_unescape_url(arguments);
+ arguments = ap_escape_shell_cmd(r->pool, arguments);
+ }
+
+ /*
+ * We have the interpreter (if there is one) and we have
+ * the arguments (if there are any).
+ * Build the command string to pass to CreateProcess.
+ */
+ quoted_filename = ap_pstrcat(r->pool, "\"", r->filename, "\"", NULL);
+ if (interpreter && *interpreter) {
+ pCommand = ap_pstrcat(r->pool, interpreter, " ",
+ quoted_filename, " ", arguments, NULL);
+ }
+ else {
+ pCommand = ap_pstrcat(r->pool, quoted_filename, " ", arguments, NULL);
+ }
+
+ } else {
+
+ char *shell_cmd = "CMD.EXE /C ";
+ OSVERSIONINFO osver;
+ osver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+
+ /*
+ * Use CMD.EXE for NT, COMMAND.COM for WIN95
+ */
+ if (GetVersionEx(&osver)) {
+ if (osver.dwPlatformId != VER_PLATFORM_WIN32_NT) {
+ shell_cmd = "COMMAND.COM /C ";
+ }
+ }
+ pCommand = ap_pstrcat(r->pool, shell_cmd, argv0, NULL);
+ }
+
+ /*
+ * Make child process use hPipeOutputWrite as standard out,
+ * and make sure it does not show on screen.
+ */
+ si.cb = sizeof(si);
+ si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
+ si.wShowWindow = SW_HIDE;
+ si.hStdInput = pinfo->hPipeInputRead;
+ si.hStdOutput = pinfo->hPipeOutputWrite;
+ si.hStdError = pinfo->hPipeErrorWrite;
+
+ /*
+ * Win32's CreateProcess call requires that the environment
+ * be passed in an environment block, a null terminated block of
+ * null terminated strings.
+ */
+ i = 0;
+ iEnvBlockLen = 1;
+ while (env[i]) {
+ iEnvBlockLen += strlen(env[i]) + 1;
+ i++;
+ }
+
+ pEnvBlock = (char *)ap_pcalloc(r->pool,iEnvBlockLen);
+
+ i = 0;
+ pNext = pEnvBlock;
+ while (env[i]) {
+ strcpy(pNext, env[i]);
+ pNext = pNext + strlen(pNext) + 1;
+ i++;
+ }
+
+ if (CreateProcess(NULL, pCommand, NULL, NULL, TRUE, DETACHED_PROCESS, pEnvBlock,
+ ap_make_dirstr_parent(r->pool, r->filename),
+ &si, &pi)) {
+ if (fileType == eFileTypeEXE16) {
+ /* Hack to get 16-bit CGI's working. It works for all the
+ * standard modules shipped with Apache. pi.dwProcessId is 0
+ * for 16-bit CGIs and all the Unix specific code that calls
+ * ap_call_exec interprets this as a failure case. And we can't
+ * use -1 either because it is mapped to 0 by the caller.
+ */
+ pid = -2;
+ }
+ else {
+ pid = pi.dwProcessId;
+ /*
+ * We must close the handles to the new process and its main thread
+ * to prevent handle and memory leaks.
+ */
+ CloseHandle(pi.hProcess);
+ CloseHandle(pi.hThread);
+ }
+ }
+ return (pid);
+ }
+
+#else
+ if (ap_suexec_enabled
+ && ((r->server->server_uid != ap_user_id)
+ || (r->server->server_gid != ap_group_id)
+ || (!strncmp("/~", r->uri, 2)))) {
+
+ char *execuser, *grpname;
+ struct passwd *pw;
+ struct group *gr;
+
+ if (!strncmp("/~", r->uri, 2)) {
+ gid_t user_gid;
+ char *username = ap_pstrdup(r->pool, r->uri + 2);
+ char *pos = strchr(username, '/');
+
+ if (pos) {
+ *pos = '\0';
+ }
+
+ if ((pw = getpwnam(username)) == NULL) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
+ "getpwnam: invalid username %s", username);
+ return (pid);
+ }
+ execuser = ap_pstrcat(r->pool, "~", pw->pw_name, NULL);
+ user_gid = pw->pw_gid;
+
+ if ((gr = getgrgid(user_gid)) == NULL) {
+ if ((grpname = ap_palloc(r->pool, 16)) == NULL) {
+ return (pid);
+ }
+ else {
+ ap_snprintf(grpname, 16, "%ld", (long) user_gid);
+ }
+ }
+ else {
+ grpname = gr->gr_name;
+ }
+ }
+ else {
+ if ((pw = getpwuid(r->server->server_uid)) == NULL) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
+ "getpwuid: invalid userid %ld",
+ (long) r->server->server_uid);
+ return (pid);
+ }
+ execuser = ap_pstrdup(r->pool, pw->pw_name);
+
+ if ((gr = getgrgid(r->server->server_gid)) == NULL) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
+ "getgrgid: invalid groupid %ld",
+ (long) r->server->server_gid);
+ return (pid);
+ }
+ grpname = gr->gr_name;
+ }
+
+ if (shellcmd) {
+ execle(SUEXEC_BIN, SUEXEC_BIN, execuser, grpname, argv0,
+ NULL, env);
+ }
+
+ else if ((!r->args) || (!r->args[0]) || strchr(r->args, '=')) {
+ execle(SUEXEC_BIN, SUEXEC_BIN, execuser, grpname, argv0,
+ NULL, env);
+ }
+
+ else {
+ execve(SUEXEC_BIN,
+ create_argv(r->pool, SUEXEC_BIN, execuser, grpname,
+ argv0, r->args),
+ env);
+ }
+ }
+ else {
+ if (shellcmd) {
+ execle(SHELL_PATH, SHELL_PATH, "-c", argv0, NULL, env);
+ }
+
+ else if ((!r->args) || (!r->args[0]) || strchr(r->args, '=')) {
+ execle(r->filename, argv0, NULL, env);
+ }
+
+ else {
+ execve(r->filename,
+ create_argv(r->pool, NULL, NULL, NULL, argv0, r->args),
+ env);
+ }
+ }
+ return (pid);
+#endif
+}
--- /dev/null
+/* ====================================================================
+ * Copyright (c) 1998-1999 The Apache Group. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * 4. The names "Apache Server" and "Apache Group" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ * nor may "Apache" appear in their names without prior written
+ * permission of the Apache Group.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Group and was originally based
+ * on public domain software written at the National Center for
+ * Supercomputing Applications, University of Illinois, Urbana-Champaign.
+ * For more information on the Apache Group and the Apache HTTP server
+ * project, please see <http://www.apache.org/>.
+ *
+ */
+
+/*
+ * util_uri.c: URI related utility things
+ *
+ */
+
+#include "httpd.h"
+#include "http_log.h"
+#include "http_conf_globals.h" /* for user_id & group_id */
+#include "util_uri.h"
+
+/* Some WWW schemes and their default ports; this is basically /etc/services */
+/* This will become global when the protocol abstraction comes */
+/* As the schemes are searched by a linear search, */
+/* they are sorted by their expected frequency */
+static schemes_t schemes[] =
+{
+ {"http", DEFAULT_HTTP_PORT},
+ {"ftp", DEFAULT_FTP_PORT},
+ {"https", DEFAULT_HTTPS_PORT},
+ {"gopher", DEFAULT_GOPHER_PORT},
+ {"wais", DEFAULT_WAIS_PORT},
+ {"nntp", DEFAULT_NNTP_PORT},
+ {"snews", DEFAULT_SNEWS_PORT},
+ {"prospero", DEFAULT_PROSPERO_PORT},
+ { NULL, 0xFFFF } /* unknown port */
+};
+
+
+API_EXPORT(unsigned short) ap_default_port_for_scheme(const char *scheme_str)
+{
+ schemes_t *scheme;
+
+ for (scheme = schemes; scheme->name != NULL; ++scheme)
+ if (strcasecmp(scheme_str, scheme->name) == 0)
+ return scheme->default_port;
+
+ return 0;
+}
+
+API_EXPORT(unsigned short) ap_default_port_for_request(const request_rec *r)
+{
+ return (r->parsed_uri.scheme)
+ ? ap_default_port_for_scheme(r->parsed_uri.scheme)
+ : 0;
+}
+
+/* Create a copy of a "struct hostent" record; it was presumably returned
+ * from a call to gethostbyname() and lives in static storage.
+ * By creating a copy we can tuck it away for later use.
+ */
+API_EXPORT(struct hostent *) ap_pduphostent(pool *p, const struct hostent *hp)
+{
+ struct hostent *newent;
+ char **ptrs;
+ char **aliases;
+ struct in_addr *addrs;
+ int i = 0, j = 0;
+
+ if (hp == NULL)
+ return NULL;
+
+ /* Count number of alias entries */
+ if (hp->h_aliases != NULL)
+ for (; hp->h_aliases[j] != NULL; ++j)
+ continue;
+
+ /* Count number of in_addr entries */
+ if (hp->h_addr_list != NULL)
+ for (; hp->h_addr_list[i] != NULL; ++i)
+ continue;
+
+ /* Allocate hostent structure, alias ptrs, addr ptrs, addrs */
+ newent = (struct hostent *) ap_palloc(p, sizeof(*hp));
+ aliases = (char **) ap_palloc(p, (j+1) * sizeof(char*));
+ ptrs = (char **) ap_palloc(p, (i+1) * sizeof(char*));
+ addrs = (struct in_addr *) ap_palloc(p, (i+1) * sizeof(struct in_addr));
+
+ *newent = *hp;
+ newent->h_name = ap_pstrdup(p, hp->h_name);
+ newent->h_aliases = aliases;
+ newent->h_addr_list = (char**) ptrs;
+
+ /* Copy Alias Names: */
+ for (j = 0; hp->h_aliases[j] != NULL; ++j) {
+ aliases[j] = ap_pstrdup(p, hp->h_aliases[j]);
+ }
+ aliases[j] = NULL;
+
+ /* Copy address entries */
+ for (i = 0; hp->h_addr_list[i] != NULL; ++i) {
+ ptrs[i] = (char*) &addrs[i];
+ addrs[i] = *(struct in_addr *) hp->h_addr_list[i];
+ }
+ ptrs[i] = NULL;
+
+ return newent;
+}
+
+
+/* pgethostbyname(): resolve hostname, if successful return an ALLOCATED
+ * COPY OF the hostent structure, intended to be stored and used later.
+ * (gethostbyname() uses static storage that would be overwritten on each call)
+ */
+API_EXPORT(struct hostent *) ap_pgethostbyname(pool *p, const char *hostname)
+{
+ struct hostent *hp = gethostbyname(hostname);
+ return (hp == NULL) ? NULL : ap_pduphostent(p, hp);
+}
+
+
+/* Unparse a uri_components structure to an URI string.
+ * Optionally suppress the password for security reasons.
+ */
+API_EXPORT(char *) ap_unparse_uri_components(pool *p, const uri_components *uptr, unsigned flags)
+{
+ char *ret = "";
+
+ /* If suppressing the site part, omit both user name & scheme://hostname */
+ if (!(flags & UNP_OMITSITEPART)) {
+
+ /* Construct a "user:password@" string, honoring the passed UNP_ flags: */
+ if (uptr->user||uptr->password)
+ ret = ap_pstrcat (p,
+ (uptr->user && !(flags & UNP_OMITUSER)) ? uptr->user : "",
+ (uptr->password && !(flags & UNP_OMITPASSWORD)) ? ":" : "",
+ (uptr->password && !(flags & UNP_OMITPASSWORD))
+ ? ((flags & UNP_REVEALPASSWORD) ? uptr->password : "XXXXXXXX")
+ : "",
+ "@", NULL);
+
+ /* Construct scheme://site string */
+ if (uptr->hostname) {
+ int is_default_port;
+
+ is_default_port =
+ (uptr->port_str == NULL ||
+ uptr->port == 0 ||
+ uptr->port == ap_default_port_for_scheme(uptr->scheme));
+
+ ret = ap_pstrcat (p,
+ uptr->scheme, "://", ret,
+ uptr->hostname ? uptr->hostname : "",
+ is_default_port ? "" : ":",
+ is_default_port ? "" : uptr->port_str,
+ NULL);
+ }
+ }
+
+ /* Should we suppress all path info? */
+ if (!(flags & UNP_OMITPATHINFO)) {
+ /* Append path, query and fragment strings: */
+ ret = ap_pstrcat (p,
+ ret,
+ uptr->path ? uptr->path : "",
+ (uptr->query && !(flags & UNP_OMITQUERY)) ? "?" : "",
+ (uptr->query && !(flags & UNP_OMITQUERY)) ? uptr->query : "",
+ (uptr->fragment && !(flags & UNP_OMITQUERY)) ? "#" : NULL,
+ (uptr->fragment && !(flags & UNP_OMITQUERY)) ? uptr->fragment : NULL,
+ NULL);
+ }
+ return ret;
+}
+
+/* The regex version of parse_uri_components has the advantage that it is
+ * relatively easy to understand and extend. But it has the disadvantage
+ * that the regexes are complex enough that regex libraries really
+ * don't do a great job with them performancewise.
+ *
+ * The default is a hand coded scanner that is two orders of magnitude
+ * faster.
+ */
+#ifdef UTIL_URI_REGEX
+
+static regex_t re_uri;
+static regex_t re_hostpart;
+
+void ap_util_uri_init(void)
+{
+ int ret;
+ const char *re_str;
+
+ /* This is a modified version of the regex that appeared in
+ * draft-fielding-uri-syntax-01. It doesnt allow the uri to contain a
+ * scheme but no hostinfo or vice versa.
+ *
+ * draft-fielding-uri-syntax-01.txt, section 4.4 tells us:
+ *
+ * Although the BNF defines what is allowed in each component, it is
+ * ambiguous in terms of differentiating between a site component and
+ * a path component that begins with two slash characters.
+ *
+ * RFC2068 disambiguates this for the Request-URI, which may only ever be
+ * the "abs_path" portion of the URI. So a request "GET //foo/bar
+ * HTTP/1.1" is really referring to the path //foo/bar, not the host foo,
+ * path /bar. Nowhere in RFC2068 is it possible to have a scheme but no
+ * hostinfo or a hostinfo but no scheme. (Unless you're proxying a
+ * protocol other than HTTP, but this parsing engine probably won't work
+ * for other protocols.)
+ *
+ * 12 3 4 5 6 7 8 */
+ re_str = "^(([^:/?#]+)://([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?$";
+ /* ^scheme--^ ^site---^ ^path--^ ^query^ ^frag */
+ if ((ret = regcomp(&re_uri, re_str, REG_EXTENDED)) != 0) {
+ char line[1024];
+
+ /* Make a readable error message */
+ ret = regerror(ret, &re_uri, line, sizeof line);
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, NULL,
+ "Internal error: regcomp(\"%s\") returned non-zero (%s) - "
+ "possibly due to broken regex lib! "
+ "Did you define WANTHSREGEX=yes?",
+ re_str, line);
+
+ exit(1);
+ }
+
+ /* This is a sub-RE which will break down the hostinfo part,
+ * i.e., user, password, hostname and port.
+ * $ 12 3 4 5 6 7 */
+ re_str = "^(([^:]*)(:(.*))?@)?([^@:]*)(:([0-9]*))?$";
+ /* ^^user^ :pw ^host^ ^:[port]^ */
+ if ((ret = regcomp(&re_hostpart, re_str, REG_EXTENDED)) != 0) {
+ char line[1024];
+
+ /* Make a readable error message */
+ ret = regerror(ret, &re_hostpart, line, sizeof line);
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, NULL,
+ "Internal error: regcomp(\"%s\") returned non-zero (%s) - "
+ "possibly due to broken regex lib! "
+ "Did you define WANTHSREGEX=yes?",
+ re_str, line);
+
+ exit(1);
+ }
+}
+
+
+/* parse_uri_components():
+ * Parse a given URI, fill in all supplied fields of a uri_components
+ * structure. This eliminates the necessity of extracting host, port,
+ * path, query info repeatedly in the modules.
+ * Side effects:
+ * - fills in fields of uri_components *uptr
+ * - none on any of the r->* fields
+ */
+API_EXPORT(int) ap_parse_uri_components(pool *p, const char *uri, uri_components *uptr)
+{
+ int ret;
+ regmatch_t match[10]; /* This must have at least as much elements
+ * as there are braces in the re_strings */
+
+ ap_assert (uptr != NULL);
+
+ /* Initialize the structure. parse_uri() and parse_uri_components()
+ * can be called more than once per request.
+ */
+ memset (uptr, '\0', sizeof(*uptr));
+ uptr->is_initialized = 1;
+
+ ret = ap_regexec(&re_uri, uri, re_uri.re_nsub + 1, match, 0);
+
+ if (ret != 0) {
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, NULL,
+ "ap_regexec() could not parse uri (\"%s\")",
+ uri);
+
+ return HTTP_BAD_REQUEST;
+ }
+
+ if (match[2].rm_so != match[2].rm_eo)
+ uptr->scheme = ap_pstrndup (p, uri+match[2].rm_so, match[2].rm_eo - match[2].rm_so);
+
+ /* empty hostinfo is valid, that's why we test $1 but use $3 */
+ if (match[1].rm_so != match[1].rm_eo)
+ uptr->hostinfo = ap_pstrndup (p, uri+match[3].rm_so, match[3].rm_eo - match[3].rm_so);
+
+ if (match[4].rm_so != match[4].rm_eo)
+ uptr->path = ap_pstrndup (p, uri+match[4].rm_so, match[4].rm_eo - match[4].rm_so);
+
+ /* empty query string is valid, that's why we test $5 but use $6 */
+ if (match[5].rm_so != match[5].rm_eo)
+ uptr->query = ap_pstrndup (p, uri+match[6].rm_so, match[6].rm_eo - match[6].rm_so);
+
+ /* empty fragment is valid, test $7 use $8 */
+ if (match[7].rm_so != match[7].rm_eo)
+ uptr->fragment = ap_pstrndup (p, uri+match[8].rm_so, match[8].rm_eo - match[8].rm_so);
+
+ if (uptr->hostinfo) {
+ /* Parse the hostinfo part to extract user, password, host, and port */
+ ret = ap_regexec(&re_hostpart, uptr->hostinfo, re_hostpart.re_nsub + 1, match, 0);
+ if (ret != 0) {
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, NULL,
+ "ap_regexec() could not parse (\"%s\") as host part",
+ uptr->hostinfo);
+
+ return HTTP_BAD_REQUEST;
+ }
+
+ /* $ 12 3 4 5 6 7 */
+ /* "^(([^:]*)(:(.*))?@)?([^@:]*)(:([0-9]*))?$" */
+ /* ^^user^ :pw ^host^ ^:[port]^ */
+
+ /* empty user is valid, that's why we test $1 but use $2 */
+ if (match[1].rm_so != match[1].rm_eo)
+ uptr->user = ap_pstrndup (p, uptr->hostinfo+match[2].rm_so, match[2].rm_eo - match[2].rm_so);
+
+ /* empty password is valid, test $3 but use $4 */
+ if (match[3].rm_so != match[3].rm_eo)
+ uptr->password = ap_pstrndup (p, uptr->hostinfo+match[4].rm_so, match[4].rm_eo - match[4].rm_so);
+
+ /* empty hostname is valid, and implied by the existence of hostinfo */
+ uptr->hostname = ap_pstrndup (p, uptr->hostinfo+match[5].rm_so, match[5].rm_eo - match[5].rm_so);
+
+ if (match[6].rm_so != match[6].rm_eo) {
+ /* Note that the port string can be empty.
+ * If it is, we use the default port associated with the scheme
+ */
+ uptr->port_str = ap_pstrndup (p, uptr->hostinfo+match[7].rm_so, match[7].rm_eo - match[7].rm_so);
+ if (uptr->port_str[0] != '\0') {
+ char *endstr;
+ int port;
+
+ port = strtol(uptr->port_str, &endstr, 10);
+ uptr->port = port;
+ if (*endstr != '\0') {
+ /* Invalid characters after ':' found */
+ return HTTP_BAD_REQUEST;
+ }
+ }
+ else {
+ uptr->port = uptr->scheme ? ap_default_port_for_scheme(uptr->scheme) : DEFAULT_HTTP_PORT;
+ }
+ }
+ }
+
+ if (ret == 0)
+ ret = HTTP_OK;
+ return ret;
+}
+#else
+
+/* Here is the hand-optimized parse_uri_components(). There are some wild
+ * tricks we could pull in assembly language that we don't pull here... like we
+ * can do word-at-time scans for delimiter characters using the same technique
+ * that fast memchr()s use. But that would be way non-portable. -djg
+ */
+
+/* We have a table that we can index by character and it tells us if the
+ * character is one of the interesting delimiters. Note that we even get
+ * compares for NUL for free -- it's just another delimiter.
+ */
+
+#define T_COLON 0x01 /* ':' */
+#define T_SLASH 0x02 /* '/' */
+#define T_QUESTION 0x04 /* '?' */
+#define T_HASH 0x08 /* '#' */
+#define T_NUL 0x80 /* '\0' */
+
+/* the uri_delims.h file is autogenerated by gen_uri_delims.c */
+#include "uri_delims.h"
+
+/* it works like this:
+ if (uri_delims[ch] & NOTEND_foobar) {
+ then we're not at a delimiter for foobar
+ }
+*/
+
+/* Note that we optimize the scheme scanning here, we cheat and let the
+ * compiler know that it doesn't have to do the & masking.
+ */
+#define NOTEND_SCHEME (0xff)
+#define NOTEND_HOSTINFO (T_SLASH | T_QUESTION | T_HASH | T_NUL)
+#define NOTEND_PATH (T_QUESTION | T_HASH | T_NUL)
+
+void ap_util_uri_init(void)
+{
+ /* nothing to do */
+}
+
+/* parse_uri_components():
+ * Parse a given URI, fill in all supplied fields of a uri_components
+ * structure. This eliminates the necessity of extracting host, port,
+ * path, query info repeatedly in the modules.
+ * Side effects:
+ * - fills in fields of uri_components *uptr
+ * - none on any of the r->* fields
+ */
+API_EXPORT(int) ap_parse_uri_components(pool *p, const char *uri, uri_components *uptr)
+{
+ const char *s;
+ const char *s1;
+ const char *hostinfo;
+ char *endstr;
+ int port;
+
+ /* Initialize the structure. parse_uri() and parse_uri_components()
+ * can be called more than once per request.
+ */
+ memset (uptr, '\0', sizeof(*uptr));
+ uptr->is_initialized = 1;
+
+ /* We assume the processor has a branch predictor like most --
+ * it assumes forward branches are untaken and backwards are taken. That's
+ * the reason for the gotos. -djg
+ */
+ if (uri[0] == '/') {
+deal_with_path:
+ /* we expect uri to point to first character of path ... remember
+ * that the path could be empty -- http://foobar?query for example
+ */
+ s = uri;
+ while ((uri_delims[*(unsigned char *)s] & NOTEND_PATH) == 0) {
+ ++s;
+ }
+ if (s != uri) {
+ uptr->path = ap_pstrndup(p, uri, s - uri);
+ }
+ if (*s == 0) {
+ return HTTP_OK;
+ }
+ if (*s == '?') {
+ ++s;
+ s1 = strchr(s, '#');
+ if (s1) {
+ uptr->fragment = ap_pstrdup(p, s1 + 1);
+ uptr->query = ap_pstrndup(p, s, s1 - s);
+ }
+ else {
+ uptr->query = ap_pstrdup(p, s);
+ }
+ return HTTP_OK;
+ }
+ /* otherwise it's a fragment */
+ uptr->fragment = ap_pstrdup(p, s + 1);
+ return HTTP_OK;
+ }
+
+ /* find the scheme: */
+ s = uri;
+ while ((uri_delims[*(unsigned char *)s] & NOTEND_SCHEME) == 0) {
+ ++s;
+ }
+ /* scheme must be non-empty and followed by :// */
+ if (s == uri || s[0] != ':' || s[1] != '/' || s[2] != '/') {
+ goto deal_with_path; /* backwards predicted taken! */
+ }
+
+ uptr->scheme = ap_pstrndup(p, uri, s - uri);
+ s += 3;
+ hostinfo = s;
+ while ((uri_delims[*(unsigned char *)s] & NOTEND_HOSTINFO) == 0) {
+ ++s;
+ }
+ uri = s; /* whatever follows hostinfo is start of uri */
+ uptr->hostinfo = ap_pstrndup(p, hostinfo, uri - hostinfo);
+
+ /* If there's a username:password@host:port, the @ we want is the last @...
+ * too bad there's no memrchr()... For the C purists, note that hostinfo
+ * is definately not the first character of the original uri so therefore
+ * &hostinfo[-1] < &hostinfo[0] ... and this loop is valid C.
+ */
+ do {
+ --s;
+ } while (s >= hostinfo && *s != '@');
+ if (s < hostinfo) {
+ /* again we want the common case to be fall through */
+deal_with_host:
+ /* We expect hostinfo to point to the first character of
+ * the hostname. If there's a port it is the first colon.
+ */
+ s = memchr(hostinfo, ':', uri - hostinfo);
+ if (s == NULL) {
+ /* we expect the common case to have no port */
+ uptr->hostname = ap_pstrndup(p, hostinfo, uri - hostinfo);
+ goto deal_with_path;
+ }
+ uptr->hostname = ap_pstrndup(p, hostinfo, s - hostinfo);
+ ++s;
+ uptr->port_str = ap_pstrndup(p, s, uri - s);
+ if (uri != s) {
+ port = strtol(uptr->port_str, &endstr, 10);
+ uptr->port = port;
+ if (*endstr == '\0') {
+ goto deal_with_path;
+ }
+ /* Invalid characters after ':' found */
+ return HTTP_BAD_REQUEST;
+ }
+ uptr->port = ap_default_port_for_scheme(uptr->scheme);
+ goto deal_with_path;
+ }
+
+ /* first colon delimits username:password */
+ s1 = memchr(hostinfo, ':', s - hostinfo);
+ if (s1) {
+ uptr->user = ap_pstrndup(p, hostinfo, s1 - hostinfo);
+ ++s1;
+ uptr->password = ap_pstrndup(p, s1, s - s1);
+ }
+ else {
+ uptr->user = ap_pstrndup(p, hostinfo, s - hostinfo);
+ }
+ hostinfo = s + 1;
+ goto deal_with_host;
+}
+
+/* Special case for CONNECT parsing: it comes with the hostinfo part only */
+/* See the INTERNET-DRAFT document "Tunneling SSL Through a WWW Proxy"
+ * currently at http://www.mcom.com/newsref/std/tunneling_ssl.html
+ * for the format of the "CONNECT host:port HTTP/1.0" request
+ */
+API_EXPORT(int) ap_parse_hostinfo_components(pool *p, const char *hostinfo, uri_components *uptr)
+{
+ const char *s;
+ char *endstr;
+
+ /* Initialize the structure. parse_uri() and parse_uri_components()
+ * can be called more than once per request.
+ */
+ memset (uptr, '\0', sizeof(*uptr));
+ uptr->is_initialized = 1;
+ uptr->hostinfo = ap_pstrdup(p, hostinfo);
+
+ /* We expect hostinfo to point to the first character of
+ * the hostname. There must be a port, separated by a colon
+ */
+ s = strchr(hostinfo, ':');
+ if (s == NULL) {
+ return HTTP_BAD_REQUEST;
+ }
+ uptr->hostname = ap_pstrndup(p, hostinfo, s - hostinfo);
+ ++s;
+ uptr->port_str = ap_pstrdup(p, s);
+ if (*s != '\0') {
+ uptr->port = strtol(uptr->port_str, &endstr, 10);
+ if (*endstr == '\0') {
+ return HTTP_OK;
+ }
+ /* Invalid characters after ':' found */
+ }
+ return HTTP_BAD_REQUEST;
+}
+#endif
--- /dev/null
+/* ====================================================================
+ * Copyright (c) 1995-1999 The Apache Group. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * 4. The names "Apache Server" and "Apache Group" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission. For written permission, please contact
+ * apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ * nor may "Apache" appear in their names without prior written
+ * permission of the Apache Group.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Group and was originally based
+ * on public domain software written at the National Center for
+ * Supercomputing Applications, University of Illinois, Urbana-Champaign.
+ * For more information on the Apache Group and the Apache HTTP server
+ * project, please see <http://www.apache.org/>.
+ *
+ */
+
+/*
+ * http_vhost.c: functions pertaining to virtual host addresses
+ * (configuration and run-time)
+ */
+
+#define CORE_PRIVATE
+#include "httpd.h"
+#include "http_config.h"
+#include "http_conf_globals.h"
+#include "http_log.h"
+#include "http_vhost.h"
+#include "http_protocol.h"
+
+/*
+ * After all the definitions there's an explanation of how it's all put
+ * together.
+ */
+
+/* meta-list of name-vhosts. Each server_rec can be in possibly multiple
+ * lists of name-vhosts.
+ */
+typedef struct name_chain name_chain;
+struct name_chain {
+ name_chain *next;
+ server_addr_rec *sar; /* the record causing it to be in
+ * this chain (needed for port comparisons) */
+ server_rec *server; /* the server to use on a match */
+};
+
+/* meta-list of ip addresses. Each server_rec can be in possibly multiple
+ * hash chains since it can have multiple ips.
+ */
+typedef struct ipaddr_chain ipaddr_chain;
+struct ipaddr_chain {
+ ipaddr_chain *next;
+ server_addr_rec *sar; /* the record causing it to be in
+ * this chain (need for both ip addr and port
+ * comparisons) */
+ server_rec *server; /* the server to use if this matches */
+ name_chain *names; /* if non-NULL then a list of name-vhosts
+ * sharing this address */
+};
+
+/* This defines the size of the hash table used for hashing ip addresses
+ * of virtual hosts. It must be a power of two.
+ */
+#ifndef IPHASH_TABLE_SIZE
+#define IPHASH_TABLE_SIZE 256
+#endif
+
+/* A (n) bucket hash table, each entry has a pointer to a server rec and
+ * a pointer to the other entries in that bucket. Each individual address,
+ * even for virtualhosts with multiple addresses, has an entry in this hash
+ * table. There are extra buckets for _default_, and name-vhost entries.
+ *
+ * Note that after config time this is constant, so it is thread-safe.
+ */
+static ipaddr_chain *iphash_table[IPHASH_TABLE_SIZE];
+
+/* dump out statistics about the hash function */
+/* #define IPHASH_STATISTICS */
+
+/* list of the _default_ servers */
+static ipaddr_chain *default_list;
+
+/* list of the NameVirtualHost addresses */
+static server_addr_rec *name_vhost_list;
+static server_addr_rec **name_vhost_list_tail;
+
+/*
+ * How it's used:
+ *
+ * The ip address determines which chain in iphash_table is interesting, then
+ * a comparison is done down that chain to find the first ipaddr_chain whose
+ * sar matches the address:port pair.
+ *
+ * If that ipaddr_chain has names == NULL then you're done, it's an ip-vhost.
+ *
+ * Otherwise it's a name-vhost list, and the default is the server in the
+ * ipaddr_chain record. We tuck away the ipaddr_chain record in the
+ * conn_rec field vhost_lookup_data. Later on after the headers we get a
+ * second chance, and we use the name_chain to figure out what name-vhost
+ * matches the headers.
+ *
+ * If there was no ip address match in the iphash_table then do a lookup
+ * in the default_list.
+ *
+ * How it's put together ... well you should be able to figure that out
+ * from how it's used. Or something like that.
+ */
+
+
+/* called at the beginning of the config */
+void ap_init_vhost_config(pool *p)
+{
+ memset(iphash_table, 0, sizeof(iphash_table));
+ default_list = NULL;
+ name_vhost_list = NULL;
+ name_vhost_list_tail = &name_vhost_list;
+}
+
+
+/*
+ * Parses a host of the form <address>[:port]
+ * paddr is used to create a list in the order of input
+ * **paddr is the ->next pointer of the last entry (or s->addrs)
+ * *paddr is the variable used to keep track of **paddr between calls
+ * port is the default port to assume
+ */
+static const char *get_addresses(pool *p, char *w, server_addr_rec ***paddr,
+ unsigned port)
+{
+ struct hostent *hep;
+ unsigned long my_addr;
+ server_addr_rec *sar;
+ char *t;
+ int i, is_an_ip_addr;
+
+ if (*w == 0)
+ return NULL;
+
+ t = strchr(w, ':');
+ if (t) {
+ if (strcmp(t + 1, "*") == 0) {
+ port = 0;
+ }
+ else if ((i = atoi(t + 1))) {
+ port = i;
+ }
+ else {
+ return ":port must be numeric";
+ }
+ *t = 0;
+ }
+
+ is_an_ip_addr = 0;
+ if (strcmp(w, "*") == 0) {
+ my_addr = htonl(INADDR_ANY);
+ is_an_ip_addr = 1;
+ }
+ else if (strcasecmp(w, "_default_") == 0
+ || strcmp(w, "255.255.255.255") == 0) {
+ my_addr = DEFAULT_VHOST_ADDR;
+ is_an_ip_addr = 1;
+ }
+ else if ((my_addr = ap_inet_addr(w)) != INADDR_NONE) {
+ is_an_ip_addr = 1;
+ }
+ if (is_an_ip_addr) {
+ sar = ap_pcalloc(p, sizeof(server_addr_rec));
+ **paddr = sar;
+ *paddr = &sar->next;
+ sar->host_addr.s_addr = my_addr;
+ sar->host_port = port;
+ sar->virthost = ap_pstrdup(p, w);
+ if (t != NULL)
+ *t = ':';
+ return NULL;
+ }
+
+ hep = gethostbyname(w);
+
+ if ((!hep) || (hep->h_addrtype != AF_INET || !hep->h_addr_list[0])) {
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, NULL,
+ "Cannot resolve host name %s --- ignoring!", w);
+ if (t != NULL)
+ *t = ':';
+ return NULL;
+ }
+
+ for (i = 0; hep->h_addr_list[i]; ++i) {
+ sar = ap_pcalloc(p, sizeof(server_addr_rec));
+ **paddr = sar;
+ *paddr = &sar->next;
+ sar->host_addr = *(struct in_addr *) hep->h_addr_list[i];
+ sar->host_port = port;
+ sar->virthost = ap_pstrdup(p, w);
+ }
+
+ if (t != NULL)
+ *t = ':';
+ return NULL;
+}
+
+
+/* parse the <VirtualHost> addresses */
+const char *ap_parse_vhost_addrs(pool *p, const char *hostname, server_rec *s)
+{
+ server_addr_rec **addrs;
+ const char *err;
+
+ /* start the list of addreses */
+ addrs = &s->addrs;
+ while (hostname[0]) {
+ err = get_addresses(p, ap_getword_conf(p, &hostname), &addrs, s->port);
+ if (err) {
+ *addrs = NULL;
+ return err;
+ }
+ }
+ /* terminate the list */
+ *addrs = NULL;
+ if (s->addrs) {
+ if (s->addrs->host_port) {
+ /* override the default port which is inherited from main_server */
+ s->port = s->addrs->host_port;
+ }
+ }
+ return NULL;
+}
+
+
+const char *ap_set_name_virtual_host (cmd_parms *cmd, void *dummy, char *arg)
+{
+ /* use whatever port the main server has at this point */
+ return get_addresses(cmd->pool, arg, &name_vhost_list_tail,
+ cmd->server->port);
+}
+
+
+/* hash table statistics, keep this in here for the beta period so
+ * we can find out if the hash function is ok
+ */
+#ifdef IPHASH_STATISTICS
+static int iphash_compare(const void *a, const void *b)
+{
+ return (*(const int *) b - *(const int *) a);
+}
+
+
+static void dump_iphash_statistics(server_rec *main_s)
+{
+ unsigned count[IPHASH_TABLE_SIZE];
+ int i;
+ ipaddr_chain *src;
+ unsigned total;
+ char buf[HUGE_STRING_LEN];
+ char *p;
+
+ total = 0;
+ for (i = 0; i < IPHASH_TABLE_SIZE; ++i) {
+ count[i] = 0;
+ for (src = iphash_table[i]; src; src = src->next) {
+ ++count[i];
+ if (i < IPHASH_TABLE_SIZE) {
+ /* don't count the slop buckets in the total */
+ ++total;
+ }
+ }
+ }
+ qsort(count, IPHASH_TABLE_SIZE, sizeof(count[0]), iphash_compare);
+ p = buf + ap_snprintf(buf, sizeof(buf),
+ "iphash: total hashed = %u, avg chain = %u, "
+ "chain lengths (count x len):",
+ total, total / IPHASH_TABLE_SIZE);
+ total = 1;
+ for (i = 1; i < IPHASH_TABLE_SIZE; ++i) {
+ if (count[i - 1] != count[i]) {
+ p += ap_snprintf(p, sizeof(buf) - (p - buf), " %ux%u",
+ total, count[i - 1]);
+ total = 1;
+ }
+ else {
+ ++total;
+ }
+ }
+ p += ap_snprintf(p, sizeof(buf) - (p - buf), " %ux%u",
+ total, count[IPHASH_TABLE_SIZE - 1]);
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, main_s, buf);
+}
+#endif
+
+
+/* This hashing function is designed to get good distribution in the cases
+ * where the server is handling entire "networks" of servers. i.e. a
+ * whack of /24s. This is probably the most common configuration for
+ * ISPs with large virtual servers.
+ *
+ * NOTE: This function is symmetric (i.e. collapses all 4 octets
+ * into one), so machine byte order (big/little endianness) does not matter.
+ *
+ * Hash function provided by David Hankins.
+ */
+static ap_inline unsigned hash_inaddr(unsigned key)
+{
+ key ^= (key >> 16);
+ return ((key >> 8) ^ key) % IPHASH_TABLE_SIZE;
+}
+
+
+
+static ipaddr_chain *new_ipaddr_chain(pool *p,
+ server_rec *s, server_addr_rec *sar)
+{
+ ipaddr_chain *new;
+
+ new = ap_palloc(p, sizeof(*new));
+ new->names = NULL;
+ new->server = s;
+ new->sar = sar;
+ new->next = NULL;
+ return new;
+}
+
+
+static name_chain *new_name_chain(pool *p, server_rec *s, server_addr_rec *sar)
+{
+ name_chain *new;
+
+ new = ap_palloc(p, sizeof(*new));
+ new->server = s;
+ new->sar = sar;
+ new->next = NULL;
+ return new;
+}
+
+
+static ap_inline ipaddr_chain *find_ipaddr(struct in_addr *server_ip,
+ unsigned port)
+{
+ unsigned bucket;
+ ipaddr_chain *trav;
+ unsigned addr;
+
+ /* scan the hash table for an exact match first */
+ addr = server_ip->s_addr;
+ bucket = hash_inaddr(addr);
+ for (trav = iphash_table[bucket]; trav; trav = trav->next) {
+ server_addr_rec *sar = trav->sar;
+ if ((sar->host_addr.s_addr == addr)
+ && (sar->host_port == 0 || sar->host_port == port
+ || port == 0)) {
+ return trav;
+ }
+ }
+ return NULL;
+}
+
+
+static ipaddr_chain *find_default_server(unsigned port)
+{
+ server_addr_rec *sar;
+ ipaddr_chain *trav;
+
+ for (trav = default_list; trav; trav = trav->next) {
+ sar = trav->sar;
+ if (sar->host_port == 0 || sar->host_port == port) {
+ /* match! */
+ return trav;
+ }
+ }
+ return NULL;
+}
+
+
+static void dump_vhost_config(FILE *f)
+{
+ int i;
+ ipaddr_chain *ic;
+ name_chain *nc;
+ char buf[MAX_STRING_LEN];
+
+ fprintf(f, "VirtualHost configuration:\n");
+ for (i = 0; i < IPHASH_TABLE_SIZE; ++i) {
+ for (ic = iphash_table[i]; ic; ic = ic->next) {
+ if (ic->sar->host_port == 0) {
+ ap_snprintf(buf, sizeof(buf), "%pA:*", &ic->sar->host_addr);
+ }
+ else {
+ ap_snprintf(buf, sizeof(buf), "%pA:%u", &ic->sar->host_addr,
+ ic->sar->host_port);
+ }
+ if (ic->names == NULL) {
+ fprintf(f, "%-22s %s (%s:%u)\n", buf,
+ ic->server->server_hostname, ic->server->defn_name,
+ ic->server->defn_line_number);
+ continue;
+ }
+ fprintf(f, "%-22s is a NameVirtualHost\n"
+ "%22s default server %s (%s:%u)\n",
+ buf, "", ic->server->server_hostname,
+ ic->server->defn_name, ic->server->defn_line_number);
+ for (nc = ic->names; nc; nc = nc->next) {
+ if (nc->sar->host_port) {
+ fprintf(f, "%22s port %u ", "", nc->sar->host_port);
+ }
+ else {
+ fprintf(f, "%22s port * ", "");
+ }
+ fprintf(f, "namevhost %s (%s:%u)\n",
+ nc->server->server_hostname,
+ nc->server->defn_name,
+ nc->server->defn_line_number);
+ }
+ }
+ }
+ if (default_list) {
+ fprintf(f, "_default_ servers:\n");
+ for (ic = default_list; ic; ic = ic->next) {
+ if (ic->sar->host_port == 0) {
+ fprintf(f, "port * ");
+ }
+ else {
+ fprintf(f, "port %u ", ic->sar->host_port);
+ }
+ fprintf(f, "server %s (%s:%u)\n",
+ ic->server->server_hostname, ic->server->defn_name,
+ ic->server->defn_line_number);
+ }
+ }
+}
+
+/* compile the tables and such we need to do the run-time vhost lookups */
+void ap_fini_vhost_config(pool *p, server_rec *main_s)
+{
+ server_addr_rec *sar;
+ int has_default_vhost_addr;
+ server_rec *s;
+ int i;
+ ipaddr_chain **iphash_table_tail[IPHASH_TABLE_SIZE];
+
+ /* terminate the name_vhost list */
+ *name_vhost_list_tail = NULL;
+
+ /* Main host first */
+ s = main_s;
+
+ if (!s->server_hostname) {
+ s->server_hostname = ap_get_local_host(p);
+ }
+
+ /* initialize the tails */
+ for (i = 0; i < IPHASH_TABLE_SIZE; ++i) {
+ iphash_table_tail[i] = &iphash_table[i];
+ }
+
+ /* The first things to go into the hash table are the NameVirtualHosts
+ * Since name_vhost_list is in the same order that the directives
+ * occured in the config file, we'll copy it in that order.
+ */
+ for (sar = name_vhost_list; sar; sar = sar->next) {
+ unsigned bucket = hash_inaddr(sar->host_addr.s_addr);
+ ipaddr_chain *new = new_ipaddr_chain(p, NULL, sar);
+
+ *iphash_table_tail[bucket] = new;
+ iphash_table_tail[bucket] = &new->next;
+
+ /* Notice that what we've done is insert an ipaddr_chain with
+ * both server and names NULL. Remember that.
+ */
+ }
+
+ /* The next things to go into the hash table are the virtual hosts
+ * themselves. They're listed off of main_s->next in the reverse
+ * order they occured in the config file, so we insert them at
+ * the iphash_table_tail but don't advance the tail.
+ */
+
+ for (s = main_s->next; s; s = s->next) {
+ has_default_vhost_addr = 0;
+ for (sar = s->addrs; sar; sar = sar->next) {
+ ipaddr_chain *ic;
+
+ if (sar->host_addr.s_addr == DEFAULT_VHOST_ADDR
+ || sar->host_addr.s_addr == INADDR_ANY) {
+ /* add it to default bucket for each appropriate sar
+ * since we need to do a port test
+ */
+ ipaddr_chain *other;
+
+ other = find_default_server(sar->host_port);
+ if (other && other->sar->host_port != 0) {
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, main_s,
+ "_default_ VirtualHost overlap on port %u,"
+ " the first has precedence", sar->host_port);
+ }
+ has_default_vhost_addr = 1;
+ ic = new_ipaddr_chain(p, s, sar);
+ ic->next = default_list;
+ default_list = ic;
+ }
+ else {
+ /* see if it matches something we've already got */
+ ic = find_ipaddr(&sar->host_addr, sar->host_port);
+
+ /* the first time we encounter a NameVirtualHost address
+ * ic->server will be NULL, on subsequent encounters
+ * ic->names will be non-NULL.
+ */
+ if (ic && (ic->names || ic->server == NULL)) {
+ name_chain *nc = new_name_chain(p, s, sar);
+ nc->next = ic->names;
+ ic->names = nc;
+ ic->server = s;
+ if (sar->host_port != ic->sar->host_port) {
+ /* one of the two is a * port, the other isn't */
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, main_s,
+ "VirtualHost %s:%u -- mixing * "
+ "ports and non-* ports with "
+ "a NameVirtualHost address is not supported,"
+ " proceeding with undefined results",
+ sar->virthost, sar->host_port);
+ }
+ }
+ else if (ic) {
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, main_s,
+ "VirtualHost %s:%u overlaps with "
+ "VirtualHost %s:%u, the first has precedence, "
+ "perhaps you need a NameVirtualHost directive",
+ sar->virthost, sar->host_port,
+ ic->sar->virthost, ic->sar->host_port);
+ ic->sar = sar;
+ ic->server = s;
+ }
+ else {
+ unsigned bucket = hash_inaddr(sar->host_addr.s_addr);
+
+ ic = new_ipaddr_chain(p, s, sar);
+ ic->next = *iphash_table_tail[bucket];
+ *iphash_table_tail[bucket] = ic;
+ }
+ }
+ }
+
+ /* Ok now we want to set up a server_hostname if the user was
+ * silly enough to forget one.
+ * XXX: This is silly we should just crash and burn.
+ */
+ if (!s->server_hostname) {
+ if (has_default_vhost_addr) {
+ s->server_hostname = main_s->server_hostname;
+ }
+ else if (!s->addrs) {
+ /* what else can we do? at this point this vhost has
+ no configured name, probably because they used
+ DNS in the VirtualHost statement. It's disabled
+ anyhow by the host matching code. -djg */
+ s->server_hostname =
+ ap_pstrdup(p, "bogus_host_without_forward_dns");
+ }
+ else {
+ struct hostent *h;
+
+ if ((h = gethostbyaddr((char *) &(s->addrs->host_addr),
+ sizeof(struct in_addr), AF_INET))) {
+ s->server_hostname = ap_pstrdup(p, (char *) h->h_name);
+ }
+ else {
+ /* again, what can we do? They didn't specify a
+ ServerName, and their DNS isn't working. -djg */
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, main_s,
+ "Failed to resolve server name "
+ "for %s (check DNS) -- or specify an explicit "
+ "ServerName",
+ inet_ntoa(s->addrs->host_addr));
+ s->server_hostname =
+ ap_pstrdup(p, "bogus_host_without_reverse_dns");
+ }
+ }
+ }
+ }
+
+ /* now go through and delete any NameVirtualHosts that didn't have any
+ * hosts associated with them. Lamers.
+ */
+ for (i = 0; i < IPHASH_TABLE_SIZE; ++i) {
+ ipaddr_chain **pic = &iphash_table[i];
+
+ while (*pic) {
+ ipaddr_chain *ic = *pic;
+
+ if (ic->server == NULL) {
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, main_s,
+ "NameVirtualHost %s:%u has no VirtualHosts",
+ ic->sar->virthost, ic->sar->host_port);
+ *pic = ic->next;
+ }
+ else if (ic->names == NULL) {
+ /* if server != NULL and names == NULL then we're done
+ * looking at NameVirtualHosts
+ */
+ break;
+ }
+ else {
+ pic = &ic->next;
+ }
+ }
+ }
+
+#ifdef IPHASH_STATISTICS
+ dump_iphash_statistics(main_s);
+#endif
+ if (ap_dump_settings) {
+ dump_vhost_config(stderr);
+ }
+}
+
+
+/*****************************************************************************
+ * run-time vhost matching functions
+ */
+
+/* Remove :port and optionally a single trailing . from the hostname, this
+ * canonicalizes it somewhat.
+ */
+static void fix_hostname(request_rec *r)
+{
+ const char *hostname = r->hostname;
+ char *host = ap_getword(r->pool, &hostname, ':'); /* get rid of port */
+ size_t l;
+
+ /* trim a trailing . */
+ l = strlen(host);
+ if (l > 0 && host[l-1] == '.') {
+ host[l-1] = '\0';
+ }
+
+ r->hostname = host;
+}
+
+
+/* return 1 if host matches ServerName or ServerAliases */
+static int matches_aliases(server_rec *s, const char *host)
+{
+ int i;
+ array_header *names;
+
+ /* match ServerName */
+ if (!strcasecmp(host, s->server_hostname)) {
+ return 1;
+ }
+
+ /* search all the aliases from ServerAlias directive */
+ names = s->names;
+ if (names) {
+ char **name = (char **) names->elts;
+ for (i = 0; i < names->nelts; ++i) {
+ if(!name[i]) continue;
+ if (!strcasecmp(host, name[i]))
+ return 1;
+ }
+ }
+ names = s->wild_names;
+ if (names) {
+ char **name = (char **) names->elts;
+ for (i = 0; i < names->nelts; ++i) {
+ if(!name[i]) continue;
+ if (!ap_strcasecmp_match(host, name[i]))
+ return 1;
+ }
+ }
+ return 0;
+}
+
+
+/* Suppose a request came in on the same socket as this r, and included
+ * a header "Host: host:port", would it map to r->server? It's more
+ * than just that though. When we do the normal matches for each request
+ * we don't even bother considering Host: etc on non-namevirtualhosts,
+ * we just call it a match. But here we require the host:port to match
+ * the ServerName and/or ServerAliases.
+ */
+API_EXPORT(int) ap_matches_request_vhost(request_rec *r, const char *host,
+ unsigned port)
+{
+ server_rec *s;
+ server_addr_rec *sar;
+
+ s = r->server;
+
+ /* search all the <VirtualHost> values */
+ /* XXX: If this is a NameVirtualHost then we may not be doing the Right Thing
+ * consider:
+ *
+ * NameVirtualHost 10.1.1.1
+ * <VirtualHost 10.1.1.1>
+ * ServerName v1
+ * </VirtualHost>
+ * <VirtualHost 10.1.1.1>
+ * ServerName v2
+ * </VirtualHost>
+ *
+ * Suppose r->server is v2, and we're asked to match "10.1.1.1". We'll say
+ * "yup it's v2", when really it isn't... if a request came in for 10.1.1.1
+ * it would really go to v1.
+ */
+ for (sar = s->addrs; sar; sar = sar->next) {
+ if ((sar->host_port == 0 || port == sar->host_port)
+ && !strcasecmp(host, sar->virthost)) {
+ return 1;
+ }
+ }
+
+ /* the Port has to match now, because the rest don't have ports associated
+ * with them. */
+ if (port != s->port) {
+ return 0;
+ }
+
+ return matches_aliases(s, host);
+}
+
+
+static void check_hostalias(request_rec *r)
+{
+ /*
+ * Even if the request has a Host: header containing a port we ignore
+ * that port. We always use the physical port of the socket. There
+ * are a few reasons for this:
+ *
+ * - the default of 80 or 443 for SSL is easier to handle this way
+ * - there is less of a possibility of a security problem
+ * - it simplifies the data structure
+ * - the client may have no idea that a proxy somewhere along the way
+ * translated the request to another ip:port
+ * - except for the addresses from the VirtualHost line, none of the other
+ * names we'll match have ports associated with them
+ */
+ const char *host = r->hostname;
+ unsigned port = ntohs(r->connection->local_addr.sin_port);
+ server_rec *s;
+ server_rec *last_s;
+ name_chain *src;
+
+ last_s = NULL;
+
+ /* Recall that the name_chain is a list of server_addr_recs, some of
+ * whose ports may not match. Also each server may appear more than
+ * once in the chain -- specifically, it will appear once for each
+ * address from its VirtualHost line which matched. We only want to
+ * do the full ServerName/ServerAlias comparisons once for each
+ * server, fortunately we know that all the VirtualHost addresses for
+ * a single server are adjacent to each other.
+ */
+
+ for (src = r->connection->vhost_lookup_data; src; src = src->next) {
+ server_addr_rec *sar;
+
+ /* We only consider addresses on the name_chain which have a matching
+ * port
+ */
+ sar = src->sar;
+ if (sar->host_port != 0 && port != sar->host_port) {
+ continue;
+ }
+
+ s = src->server;
+
+ /* does it match the virthost from the sar? */
+ if (!strcasecmp(host, sar->virthost)) {
+ goto found;
+ }
+
+ if (s == last_s) {
+ /* we've already done ServerName and ServerAlias checks for this
+ * vhost
+ */
+ continue;
+ }
+ last_s = s;
+
+ if (matches_aliases(s, host)) {
+ goto found;
+ }
+ }
+ return;
+
+found:
+ /* s is the first matching server, we're done */
+ r->server = r->connection->server = s;
+}
+
+
+static void check_serverpath(request_rec *r)
+{
+ server_rec *s;
+ server_rec *last_s;
+ name_chain *src;
+ unsigned port = ntohs(r->connection->local_addr.sin_port);
+
+ /*
+ * This is in conjunction with the ServerPath code in http_core, so we
+ * get the right host attached to a non- Host-sending request.
+ *
+ * See the comment in check_hostalias about how each vhost can be
+ * listed multiple times.
+ */
+
+ last_s = NULL;
+ for (src = r->connection->vhost_lookup_data; src; src = src->next) {
+ /* We only consider addresses on the name_chain which have a matching
+ * port
+ */
+ if (src->sar->host_port != 0 && port != src->sar->host_port) {
+ continue;
+ }
+
+ s = src->server;
+ if (s == last_s) {
+ continue;
+ }
+ last_s = s;
+
+ if (s->path && !strncmp(r->uri, s->path, s->pathlen) &&
+ (s->path[s->pathlen - 1] == '/' ||
+ r->uri[s->pathlen] == '/' ||
+ r->uri[s->pathlen] == '\0')) {
+ r->server = r->connection->server = s;
+ return;
+ }
+ }
+}
+
+
+void ap_update_vhost_from_headers(request_rec *r)
+{
+ /* must set this for HTTP/1.1 support */
+ if (r->hostname || (r->hostname = ap_table_get(r->headers_in, "Host"))) {
+ fix_hostname(r);
+ }
+ /* check if we tucked away a name_chain */
+ if (r->connection->vhost_lookup_data) {
+ if (r->hostname)
+ check_hostalias(r);
+ else
+ check_serverpath(r);
+ }
+}
+
+
+/* Called for a new connection which has a known local_addr. Note that the
+ * new connection is assumed to have conn->server == main server.
+ */
+void ap_update_vhost_given_ip(conn_rec *conn)
+{
+ ipaddr_chain *trav;
+ unsigned port = ntohs(conn->local_addr.sin_port);
+
+ /* scan the hash table for an exact match first */
+ trav = find_ipaddr(&conn->local_addr.sin_addr, port);
+ if (trav) {
+ /* save the name_chain for later in case this is a name-vhost */
+ conn->vhost_lookup_data = trav->names;
+ conn->server = trav->server;
+ return;
+ }
+
+ /* There's certainly no name-vhosts with this address, they would have
+ * been matched above.
+ */
+ conn->vhost_lookup_data = NULL;
+
+ /* maybe there's a default server matching this port */
+ trav = find_default_server(port);
+ if (trav) {
+ conn->server = trav->server;
+ }
+
+ /* otherwise we're stuck with just the main server */
+}
--- /dev/null
+Makefile
+rotatelogs
+htpasswd
+htdigest
+unescape
+inc2shtml
+httpd_monitor
+suexec
+logresolve
+ab
+apxs
--- /dev/null
+-i4 -npsl -di0 -br -nce -d0 -cli0 -npcs -nfc1
+-TBUFF
+-TFILE
+-TTRANS
+-TUINT4
+-T_trans
+-Tallow_options_t
+-Tapache_sfio
+-Tarray_header
+-Tbool_int
+-Tbuf_area
+-Tbuff_struct
+-Tbuffy
+-Tcmd_how
+-Tcmd_parms
+-Tcommand_rec
+-Tcommand_struct
+-Tconn_rec
+-Tcore_dir_config
+-Tcore_server_config
+-Tdir_maker_func
+-Tevent
+-Tglobals_s
+-Thandler_func
+-Thandler_rec
+-Tjoblist_s
+-Tlisten_rec
+-Tmerger_func
+-Tmode_t
+-Tmodule
+-Tmodule_struct
+-Tmutex
+-Tn_long
+-Tother_child_rec
+-Toverrides_t
+-Tparent_score
+-Tpid_t
+-Tpiped_log
+-Tpool
+-Trequest_rec
+-Trequire_line
+-Trlim_t
+-Tscoreboard
+-Tsemaphore
+-Tserver_addr_rec
+-Tserver_rec
+-Tserver_rec_chain
+-Tshort_score
+-Ttable
+-Ttable_entry
+-Tthread
+-Tu_wide_int
+-Tvtime_t
+-Twide_int
--- /dev/null
+Support files:
+
+ab
+ ABuse your server with this benchmarker. Rudimentary
+ command line testing tool.
+
+apachectl
+ Apache run-time Control script. To facilitate the
+ administrator and/or your rc.d scripts to control the
+ functioning of the Apache httpd daemon.
+
+apxs
+ APache eXtenSion tool. Eases building and installing
+ DSO style modules.
+
+dbmmanage
+ Create and update user authentication files in the faster
+ DBM format used by mod_auth_db.
+
+htdigest
+ Create and update user authentication files used in
+ DIGEST authentification. See mod_auth_digest.
+
+htpasswd
+ Create and update user authentication files used in
+ BASIC authentification. I.e. the htpasswd files.
+ See mod_auth.
+
+httpd.8
+ General apache man page.
+
+log_server_status
+ This script is designed to be run at a frequent interval by something
+ like cron. It connects to the server and downloads the status
+ information. It reformats the information to a single line and logs
+ it to a file.
+
+logresolve
+ resolve hostnames for IP-adresses in Apache logfiles
+
+phf_abuse_log.cgi
+ This script can be used to detect people trying to abuse an ancient
+ and long plugged security hole which existed in a CGI script distributed
+ with Apache 1.0.3 and earlier versions.
+
+rotatelogs
+ rotate Apache logs without having to kill the server.
+
+split-logfile
+ This script will take a combined virtual hosts access
+ log file and break its contents into separate files.
+
+suexec
+ Switch User For Exec. Used internally by apache,
+ see the document `Apache suEXEC Support'
+ under http://www.apache.org/docs/suexec.html .
+
+SHA1
+ This directory includes some utilities to allow Apache 1.3.6 to
+ recognize passwords in SHA1 format, as used by Netscape web
+ servers. It is not installed by default.
+
--- /dev/null
+This directory includes some utilities to allow Apache 1.3.6 to
+recognize passwords in SHA1 format, as used by Netscape web servers.
+
+From Netscape's admin interface, export the password database to an
+ldif file and then use convert.pl in this distribution to generate
+apache style password files.
+
+Note: SHA1 support is useful for migration purposes, but is less
+ secure than Apache's password format, since Apache's (MD5)
+ password format uses a random eight character salt to generate
+ one of many possible hashes for the same password. Netscape
+ uses plain SHA1 without a salt, so the same password
+ will always generate the same hash, making it easier
+ to break since the search space is smaller.
+
+This code was contributed by Clinton Wong <clintdw@netcom.com>.
+
+README.sha1
+ this file
+
+convert-sha1.pl
+ takes an ldif dump from Netscape's web server on
+ standard in, outputs apache htpasswd format on standard out.
+
+ Usage: convert.pl < ldif > passwords
+
+htpasswd-sha1.pl
+ perl script to generate entries in apache htpasswd format.
+
+ Usage: htpasswd-sha1.pl some_user some_password
+
+ldif-sha1.example
+ sample ldif dump with one sha1 password and one crypt password.
+
--- /dev/null
+#!/usr/bin/perl -w
+use strict;
+
+# This is public domain code. Do whatever you want with it.
+# It was originally included in Clinton Wong's Apache 1.3.6 SHA1/ldif
+# patch distribution as sample code for converting accounts from
+# ldif format (as used by Netscape web servers) to Apache password format.
+
+my $uid='';
+my $passwd='';
+
+while (my $line = <>) {
+ chomp $line;
+ if ( $line =~ /uid:\s*(.+)/) { $uid = $1 }
+ if ( $line =~ /userpassword:\s*(\{\w+\}.+)/) {
+ $passwd = $1;
+ $passwd =~ s/^\{crypt\}//i; # Apache stores crypt without a magic string
+ }
+
+ if (length($line)==0) {
+
+ if (length $uid and length $passwd) {
+ print $uid, ':', $passwd, "\n";
+ } # output if we have something to print
+
+ $uid = '';
+ $passwd = '';
+
+ } # if newline
+} # while something to read
+
+# handle last entry if there isn't a newline before EOF
+ if (length $uid and length $passwd) {
+ print $uid, ':', $passwd, "\n";
+}
+
--- /dev/null
+#!/usr/bin/perl -w
+use strict;
+#
+# Utility which takes a username and password
+# on the command line and generates a username
+# sha1-encrytped password on the stdout.
+#
+# Typical useage:
+# ./htpasswd-sha1.pl dirkx MySecret >> sha1-passwd
+#
+# This is public domain code. Do whatever you want with it.
+# It was originally included in Clinton Wong's Apache 1.3.6 SHA1/ldif
+# patch distribution as sample code for generating entries for
+# Apache password files using SHA1.
+
+use MIME::Base64; # http://www.cpan.org/modules/by-module/MIME/
+use Digest::SHA1; # http://www.cpan.org/modules/by-module/MD5/
+
+if ($#ARGV!=1) { die "Usage $0: user password\n" }
+
+print $ARGV[0], ':{SHA}', encode_base64( Digest::SHA1::sha1($ARGV[1]) );
+
--- /dev/null
+dn: cn=someuser
+cn: someuser
+sn: someuser
+objectclass: top
+objectclass: person
+objectclass: organizationalPerson
+objectclass: inetOrgPerson
+uid: someuser
+userpassword: {SHA}GvF+c3IdvgxAARuC7Uuxp9vjzik=
+
+dn: cn=anotheruser
+cn: anotheruser
+sn: anotheruser
+objectclass: top
+objectclass: person
+objectclass: organizationalPerson
+objectclass: inetOrgPerson
+uid: anotheruser
+userpassword: {crypt}eFnp.4sz5XnH6
--- /dev/null
+/* ====================================================================
+ * Copyright (c) 1998-1999 The Apache Group. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * 4. The names "Apache Server" and "Apache Group" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission. For written permission, please contact
+ * apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ * nor may "Apache" appear in their names without prior written
+ * permission of the Apache Group.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Group and was originally based
+ * on public domain software written at the National Center for
+ * Supercomputing Applications, University of Illinois, Urbana-Champaign.
+ * For more information on the Apache Group and the Apache HTTP server
+ * project, please see <http://www.apache.org/>.
+ *
+ */
+
+/*
+ ** This program is based on ZeusBench V1.0 written by Adam Twiss
+ ** which is Copyright (c) 1996 by Zeus Technology Ltd. http://www.zeustech.net/
+ **
+ ** This software is provided "as is" and any express or implied waranties,
+ ** including but not limited to, the implied warranties of merchantability and
+ ** fitness for a particular purpose are disclaimed. In no event shall
+ ** Zeus Technology Ltd. be liable for any direct, indirect, incidental, special,
+ ** exemplary, or consequential damaged (including, but not limited to,
+ ** procurement of substitute good or services; loss of use, data, or profits;
+ ** or business interruption) however caused and on theory of liability. Whether
+ ** in contract, strict liability or tort (including negligence or otherwise)
+ ** arising in any way out of the use of this software, even if advised of the
+ ** possibility of such damage.
+ **
+ */
+
+/*
+ ** HISTORY:
+ ** - Originally written by Adam Twiss <adam@zeus.co.uk>, March 1996
+ ** with input from Mike Belshe <mbelshe@netscape.com> and
+ ** Michael Campanella <campanella@stevms.enet.dec.com>
+ ** - Enhanced by Dean Gaudet <dgaudet@apache.org>, November 1997
+ ** - Cleaned up by Ralf S. Engelschall <rse@apache.org>, March 1998
+ ** - POST and verbosity by Kurt Sussman <kls@merlot.com>, August 1998
+ ** - HTML table output added by David N. Welton <davidw@prosa.it>, January 1999
+ ** - Added Cookie, Arbitrary header and auth support. <dirkx@webweaving.org>, April 199
+ **
+ */
+
+/*
+ * BUGS:
+ *
+ * - uses strcpy/etc.
+ * - has various other poor buffer attacks related to the lazy parsing of
+ * response headers from the server
+ * - doesn't implement much of HTTP/1.x, only accepts certain forms of
+ * responses
+ * - (performance problem) heavy use of strstr shows up top in profile
+ * only an issue for loopback usage
+ */
+
+#define VERSION "1.3a"
+
+/* -------------------------------------------------------------------- */
+
+/* affects include files on Solaris */
+#define BSD_COMP
+
+/* allow compilation outside an Apache build tree */
+#ifdef NO_APACHE_INCLUDES
+#include <sys/time.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <string.h>
+
+#define ap_select select
+#else /* (!)NO_APACHE_INCLUDES */
+#include "ap_config.h"
+#include "ap.h"
+#ifdef CHARSET_EBCDIC
+#include "ebcdic.h"
+#endif
+#include <fcntl.h>
+#include <sys/time.h>
+
+#ifndef NO_WRITEV
+#include <sys/types.h>
+#include <sys/uio.h>
+#endif
+
+#endif /* NO_APACHE_INCLUDES */
+/* ------------------- DEFINITIONS -------------------------- */
+
+/* maximum number of requests on a time limited test */
+#define MAX_REQUESTS 50000
+
+/* good old state hostname */
+#define STATE_UNCONNECTED 0
+#define STATE_CONNECTING 1
+#define STATE_READ 2
+
+#define CBUFFSIZE 512
+
+struct connection {
+ int fd;
+ int state;
+ int read; /* amount of bytes read */
+ int bread; /* amount of body read */
+ int length; /* Content-Length value used for keep-alive */
+ char cbuff[CBUFFSIZE]; /* a buffer to store server response header */
+ int cbx; /* offset in cbuffer */
+ int keepalive; /* non-zero if a keep-alive request */
+ int gotheader; /* non-zero if we have the entire header in
+ * cbuff */
+ struct timeval start, connect, done;
+};
+
+struct data {
+ int read; /* number of bytes read */
+ int ctime; /* time in ms to connect */
+ int time; /* time in ms for connection */
+};
+
+#define ap_min(a,b) ((a)<(b))?(a):(b)
+#define ap_max(a,b) ((a)>(b))?(a):(b)
+
+/* --------------------- GLOBALS ---------------------------- */
+
+int verbosity = 0; /* no verbosity by default */
+int posting = 0; /* GET by default */
+int requests = 1; /* Number of requests to make */
+int concurrency = 1; /* Number of multiple requests to make */
+int tlimit = 0; /* time limit in cs */
+int keepalive = 0; /* try and do keepalive connections */
+char servername[1024]; /* name that server reports */
+char hostname[1024]; /* host name */
+char path[1024]; /* path name */
+char postfile[1024]; /* name of file containing post data */
+char *postdata; /* *buffer containing data from postfile */
+int postlen = 0; /* length of data to be POSTed */
+char content_type[1024]; /* content type to put in POST header */
+char cookie[1024], /* optional cookie line */
+ auth[1024], /* optional (basic/uuencoded)
+ * authentification */
+ hdrs[4096]; /* optional arbitrary headers */
+int port = 80; /* port number */
+
+int use_html = 0; /* use html in the report */
+char *tablestring;
+char *trstring;
+char *tdstring;
+
+int doclen = 0; /* the length the document should be */
+int totalread = 0; /* total number of bytes read */
+int totalbread = 0; /* totoal amount of entity body read */
+int totalposted = 0; /* total number of bytes posted, inc. headers */
+int done = 0; /* number of requests we have done */
+int doneka = 0; /* number of keep alive connections done */
+int good = 0, bad = 0; /* number of good and bad requests */
+
+/* store error cases */
+int err_length = 0, err_conn = 0, err_except = 0;
+int err_response = 0;
+
+struct timeval start, endtime;
+
+/* global request (and its length) */
+char request[512];
+int reqlen;
+
+/* one global throw-away buffer to read stuff into */
+char buffer[8192];
+
+struct connection *con; /* connection array */
+struct data *stats; /* date for each request */
+
+fd_set readbits, writebits; /* bits for select */
+struct sockaddr_in server; /* server addr structure */
+
+/* --------------------------------------------------------- */
+
+/* simple little function to perror and exit */
+
+static void err(char *s)
+{
+ if (errno) {
+ perror(s);
+ }
+ else {
+ printf("%s", s);
+ }
+ exit(errno);
+}
+
+/* --------------------------------------------------------- */
+
+/* write out request to a connection - assumes we can write
+ (small) request out in one go into our new socket buffer */
+
+static void write_request(struct connection * c)
+{
+#ifndef NO_WRITEV
+ struct iovec out[2]; int outcnt = 1;
+#endif
+ gettimeofday(&c->connect, 0);
+#ifndef NO_WRITEV
+ out[0].iov_base = request;
+ out[0].iov_len = reqlen;
+
+ if (posting) {
+ out[1].iov_base = postdata;
+ out[1].iov_len = postlen;
+ outcnt = 2;
+ totalposted += (reqlen + postlen);
+ }
+ writev(c->fd,out, outcnt);
+#else
+ write(c->fd,request,reqlen);
+ if (posting) {
+ write(c->fd,postdata,postlen);
+ totalposted += (reqlen + postlen);
+ }
+#endif
+
+ c->state = STATE_READ;
+ FD_SET(c->fd, &readbits);
+ FD_CLR(c->fd, &writebits);
+}
+
+/* --------------------------------------------------------- */
+
+/* make an fd non blocking */
+
+static void nonblock(int fd)
+{
+ int i = 1;
+ ioctl(fd, FIONBIO, &i);
+}
+
+/* --------------------------------------------------------- */
+
+/* returns the time in ms between two timevals */
+
+static int timedif(struct timeval a, struct timeval b)
+{
+ register int us, s;
+
+ us = a.tv_usec - b.tv_usec;
+ us /= 1000;
+ s = a.tv_sec - b.tv_sec;
+ s *= 1000;
+ return s + us;
+}
+
+/* --------------------------------------------------------- */
+
+/* calculate and output results */
+
+static void output_results(void)
+{
+ int timetaken;
+
+ gettimeofday(&endtime, 0);
+ timetaken = timedif(endtime, start);
+
+ printf("\r \r");
+ printf("Server Software: %s\n", servername);
+ printf("Server Hostname: %s\n", hostname);
+ printf("Server Port: %d\n", port);
+ printf("\n");
+ printf("Document Path: %s\n", path);
+ printf("Document Length: %d bytes\n", doclen);
+ printf("\n");
+ printf("Concurrency Level: %d\n", concurrency);
+ printf("Time taken for tests: %d.%03d seconds\n",
+ timetaken / 1000, timetaken % 1000);
+ printf("Complete requests: %d\n", done);
+ printf("Failed requests: %d\n", bad);
+ if (bad)
+ printf(" (Connect: %d, Length: %d, Exceptions: %d)\n",
+ err_conn, err_length, err_except);
+ if (err_response)
+ printf("Non-2xx responses: %d\n", err_response);
+ if (keepalive)
+ printf("Keep-Alive requests: %d\n", doneka);
+ printf("Total transferred: %d bytes\n", totalread);
+ if (posting)
+ printf("Total POSTed: %d\n", totalposted);
+ printf("HTML transferred: %d bytes\n", totalbread);
+
+ /* avoid divide by zero */
+ if (timetaken) {
+ printf("Requests per second: %.2f\n", 1000 * (float) (done) / timetaken);
+ printf("Transfer rate: %.2f kb/s received\n",
+ (float) (totalread) / timetaken);
+ if (posting) {
+ printf(" %.2f kb/s sent\n",
+ (float) (totalposted) / timetaken);
+ printf(" %.2f kb/s total\n",
+ (float) (totalread + totalposted) / timetaken);
+ }
+ }
+
+ {
+ /* work out connection times */
+ int i;
+ int totalcon = 0, total = 0;
+ int mincon = 9999999, mintot = 999999;
+ int maxcon = 0, maxtot = 0;
+
+ for (i = 0; i < requests; i++) {
+ struct data s = stats[i];
+ mincon = ap_min(mincon, s.ctime);
+ mintot = ap_min(mintot, s.time);
+ maxcon = ap_max(maxcon, s.ctime);
+ maxtot = ap_max(maxtot, s.time);
+ totalcon += s.ctime;
+ total += s.time;
+ }
+ if (requests > 0) { /* avoid division by zero (if 0 requests) */
+ printf("\nConnnection Times (ms)\n");
+ printf(" min avg max\n");
+ printf("Connect: %5d %5d %5d\n", mincon, totalcon / requests, maxcon);
+ printf("Processing: %5d %5d %5d\n",
+ mintot - mincon, (total / requests) - (totalcon / requests),
+ maxtot - maxcon);
+ printf("Total: %5d %5d %5d\n", mintot, total / requests, maxtot);
+ }
+ }
+}
+
+/* --------------------------------------------------------- */
+
+/* calculate and output results in HTML */
+
+static void output_html_results(void)
+{
+ int timetaken;
+
+ gettimeofday(&endtime, 0);
+ timetaken = timedif(endtime, start);
+
+ printf("\n\n<table %s>\n", tablestring);
+ printf("<tr %s><th colspan=2 %s>Server Software:</th>"
+ "<td colspan=2 %s>%s</td></tr>\n",
+ trstring, tdstring, tdstring, servername);
+ printf("<tr %s><th colspan=2 %s>Server Hostname:</th>"
+ "<td colspan=2 %s>%s</td></tr>\n",
+ trstring, tdstring, tdstring, hostname);
+ printf("<tr %s><th colspan=2 %s>Server Port:</th>"
+ "<td colspan=2 %s>%d</td></tr>\n",
+ trstring, tdstring, tdstring, port);
+ printf("<tr %s><th colspan=2 %s>Document Path:</th>"
+ "<td colspan=2 %s>%s</td></tr>\n",
+ trstring, tdstring, tdstring, path);
+ printf("<tr %s><th colspan=2 %s>Document Length:</th>"
+ "<td colspan=2 %s>%d bytes</td></tr>\n",
+ trstring, tdstring, tdstring, doclen);
+ printf("<tr %s><th colspan=2 %s>Concurrency Level:</th>"
+ "<td colspan=2 %s>%d</td></tr>\n",
+ trstring, tdstring, tdstring, concurrency);
+ printf("<tr %s><th colspan=2 %s>Time taken for tests:</th>"
+ "<td colspan=2 %s>%d.%03d seconds</td></tr>\n",
+ trstring, tdstring, tdstring, timetaken / 1000, timetaken % 1000);
+ printf("<tr %s><th colspan=2 %s>Complete requests:</th>"
+ "<td colspan=2 %s>%d</td></tr>\n",
+ trstring, tdstring, tdstring, done);
+ printf("<tr %s><th colspan=2 %s>Failed requests:</th>"
+ "<td colspan=2 %s>%d</td></tr>\n",
+ trstring, tdstring, tdstring, bad);
+ if (bad)
+ printf("<tr %s><td colspan=4 %s > (Connect: %d, Length: %d, Exceptions: %d)</td></tr>\n",
+ trstring, tdstring, err_conn, err_length, err_except);
+ if (err_response)
+ printf("<tr %s><th colspan=2 %s>Non-2xx responses:</th>"
+ "<td colspan=2 %s>%d</td></tr>\n",
+ trstring, tdstring, tdstring, err_response);
+ if (keepalive)
+ printf("<tr %s><th colspan=2 %s>Keep-Alive requests:</th>"
+ "<td colspan=2 %s>%d</td></tr>\n",
+ trstring, tdstring, tdstring, doneka);
+ printf("<tr %s><th colspan=2 %s>Total transferred:</th>"
+ "<td colspan=2 %s>%d bytes</td></tr>\n",
+ trstring, tdstring, tdstring, totalread);
+ if (posting)
+ printf("<tr %s><th colspan=2 %s>Total POSTed:</th>"
+ "<td colspan=2 %s>%d</td></tr>\n",
+ trstring, tdstring, tdstring, totalposted);
+ printf("<tr %s><th colspan=2 %s>HTML transferred:</th>"
+ "<td colspan=2 %s>%d bytes</td></tr>\n",
+ trstring, tdstring, tdstring, totalbread);
+
+ /* avoid divide by zero */
+ if (timetaken) {
+ printf("<tr %s><th colspan=2 %s>Requests per second:</th>"
+ "<td colspan=2 %s>%.2f</td></tr>\n",
+ trstring, tdstring, tdstring, 1000 * (float) (done) / timetaken);
+ printf("<tr %s><th colspan=2 %s>Transfer rate:</th>"
+ "<td colspan=2 %s>%.2f kb/s received</td></tr>\n",
+ trstring, tdstring, tdstring, (float) (totalread) / timetaken);
+ if (posting) {
+ printf("<tr %s><td colspan=2 %s> </td>"
+ "<td colspan=2 %s>%.2f kb/s sent</td></tr>\n",
+ trstring, tdstring, tdstring,
+ (float) (totalposted) / timetaken);
+ printf("<tr %s><td colspan=2 %s> </td>"
+ "<td colspan=2 %s>%.2f kb/s total</td></tr>\n",
+ trstring, tdstring, tdstring,
+ (float) (totalread + totalposted) / timetaken);
+ }
+ }
+
+ {
+ /* work out connection times */
+ int i;
+ int totalcon = 0, total = 0;
+ int mincon = 9999999, mintot = 999999;
+ int maxcon = 0, maxtot = 0;
+
+ for (i = 0; i < requests; i++) {
+ struct data s = stats[i];
+ mincon = ap_min(mincon, s.ctime);
+ mintot = ap_min(mintot, s.time);
+ maxcon = ap_max(maxcon, s.ctime);
+ maxtot = ap_max(maxtot, s.time);
+ totalcon += s.ctime;
+ total += s.time;
+ }
+
+ if (requests > 0) { /* avoid division by zero (if 0 requests) */
+ printf("<tr %s><th %s colspan=4>Connnection Times (ms)</th></tr>\n",
+ trstring, tdstring);
+ printf("<tr %s><th %s> </th> <th %s>min</th> <th %s>avg</th> <th %s>max</th></tr>\n",
+ trstring, tdstring, tdstring, tdstring, tdstring);
+ printf("<tr %s><th %s>Connect:</th>"
+ "<td %s>%5d</td>"
+ "<td %s>%5d</td>"
+ "<td %s>%5d</td></tr>\n",
+ trstring, tdstring, tdstring, mincon, tdstring, totalcon / requests, tdstring, maxcon);
+ printf("<tr %s><th %s>Processing:</th>"
+ "<td %s>%5d</td>"
+ "<td %s>%5d</td>"
+ "<td %s>%5d</td></tr>\n",
+ trstring, tdstring, tdstring, mintot - mincon, tdstring,
+ (total / requests) - (totalcon / requests), tdstring, maxtot - maxcon);
+ printf("<tr %s><th %s>Total:</th>"
+ "<td %s>%5d</td>"
+ "<td %s>%5d</td>"
+ "<td %s>%5d</td></tr>\n",
+ trstring, tdstring, tdstring, mintot, tdstring, total / requests, tdstring, maxtot);
+ }
+ printf("</table>\n");
+ }
+}
+
+/* --------------------------------------------------------- */
+
+/* start asnchronous non-blocking connection */
+
+static void start_connect(struct connection * c)
+{
+ c->read = 0;
+ c->bread = 0;
+ c->keepalive = 0;
+ c->cbx = 0;
+ c->gotheader = 0;
+
+ c->fd = socket(AF_INET, SOCK_STREAM, 0);
+ if (c->fd < 0)
+ err("socket");
+
+ nonblock(c->fd);
+ gettimeofday(&c->start, 0);
+
+ if (connect(c->fd, (struct sockaddr *) & server, sizeof(server)) < 0) {
+ if (errno == EINPROGRESS) {
+ c->state = STATE_CONNECTING;
+ FD_SET(c->fd, &writebits);
+ return;
+ }
+ else {
+ close(c->fd);
+ err_conn++;
+ if (bad++ > 10) {
+ err("\nTest aborted after 10 failures\n\n");
+ }
+ start_connect(c);
+ }
+ }
+
+ /* connected first time */
+ write_request(c);
+}
+
+/* --------------------------------------------------------- */
+
+/* close down connection and save stats */
+
+static void close_connection(struct connection * c)
+{
+ if (c->read == 0 && c->keepalive) {
+ /* server has legitimately shut down an idle keep alive request */
+ good--; /* connection never happend */
+ }
+ else {
+ if (good == 1) {
+ /* first time here */
+ doclen = c->bread;
+ }
+ else if (c->bread != doclen) {
+ bad++;
+ err_length++;
+ }
+
+ /* save out time */
+ if (done < requests) {
+ struct data s;
+ gettimeofday(&c->done, 0);
+ s.read = c->read;
+ s.ctime = timedif(c->connect, c->start);
+ s.time = timedif(c->done, c->start);
+ stats[done++] = s;
+ }
+ }
+
+ close(c->fd);
+ FD_CLR(c->fd, &readbits);
+ FD_CLR(c->fd, &writebits);
+
+ /* connect again */
+ start_connect(c);
+ return;
+}
+
+/* --------------------------------------------------------- */
+
+/* read data from connection */
+
+static void read_connection(struct connection * c)
+{
+ int r;
+ char *part;
+ char respcode[4]; /* 3 digits and null */
+
+ r = read(c->fd, buffer, sizeof(buffer));
+ if (r == 0 || (r < 0 && errno != EAGAIN)) {
+ good++;
+ close_connection(c);
+ return;
+ }
+
+ if (r < 0 && errno == EAGAIN)
+ return;
+
+ c->read += r;
+ totalread += r;
+
+ if (!c->gotheader) {
+ char *s;
+ int l = 4;
+ int space = CBUFFSIZE - c->cbx - 1; /* -1 to allow for 0
+ * terminator */
+ int tocopy = (space < r) ? space : r;
+#ifndef CHARSET_EBCDIC
+ memcpy(c->cbuff + c->cbx, buffer, tocopy);
+#else /* CHARSET_EBCDIC */
+ ascii2ebcdic(c->cbuff + c->cbx, buffer, tocopy);
+#endif /* CHARSET_EBCDIC */
+ c->cbx += tocopy;
+ space -= tocopy;
+ c->cbuff[c->cbx] = 0; /* terminate for benefit of strstr */
+ if (verbosity >= 4) {
+ printf("LOG: header received:\n%s\n", c->cbuff);
+ }
+ s = strstr(c->cbuff, "\r\n\r\n");
+ /*
+ * this next line is so that we talk to NCSA 1.5 which blatantly
+ * breaks the http specification
+ */
+ if (!s) {
+ s = strstr(c->cbuff, "\n\n");
+ l = 2;
+ }
+
+ if (!s) {
+ /* read rest next time */
+ if (space)
+ return;
+ else {
+ /* header is in invalid or too big - close connection */
+ close(c->fd);
+ if (bad++ > 10) {
+ err("\nTest aborted after 10 failures\n\n");
+ }
+ FD_CLR(c->fd, &writebits);
+ start_connect(c);
+ }
+ }
+ else {
+ /* have full header */
+ if (!good) {
+ /* this is first time, extract some interesting info */
+ char *p, *q;
+ p = strstr(c->cbuff, "Server:");
+ q = servername;
+ if (p) {
+ p += 8;
+ while (*p > 32)
+ *q++ = *p++;
+ }
+ *q = 0;
+ }
+
+ /*
+ * XXX: this parsing isn't even remotely HTTP compliant... but in
+ * the interest of speed it doesn't totally have to be, it just
+ * needs to be extended to handle whatever servers folks want to
+ * test against. -djg
+ */
+
+ /* check response code */
+ part = strstr(c->cbuff, "HTTP"); /* really HTTP/1.x_ */
+ strncpy(respcode, (part + strlen("HTTP/1.x_")), 3);
+ respcode[3] = '\0';
+ if (respcode[0] != '2') {
+ err_response++;
+ if (verbosity >= 2)
+ printf("WARNING: Response code not 2xx (%s)\n", respcode);
+ }
+ else if (verbosity >= 3) {
+ printf("LOG: Response code = %s\n", respcode);
+ }
+
+ c->gotheader = 1;
+ *s = 0; /* terminate at end of header */
+ if (keepalive &&
+ (strstr(c->cbuff, "Keep-Alive")
+ || strstr(c->cbuff, "keep-alive"))) { /* for benefit of MSIIS */
+ char *cl;
+ cl = strstr(c->cbuff, "Content-Length:");
+ /* handle NCSA, which sends Content-length: */
+ if (!cl)
+ cl = strstr(c->cbuff, "Content-length:");
+ if (cl) {
+ c->keepalive = 1;
+ c->length = atoi(cl + 16);
+ }
+ }
+ c->bread += c->cbx - (s + l - c->cbuff) + r - tocopy;
+ totalbread += c->bread;
+ }
+ }
+ else {
+ /* outside header, everything we have read is entity body */
+ c->bread += r;
+ totalbread += r;
+ }
+
+ if (c->keepalive && (c->bread >= c->length)) {
+ /* finished a keep-alive connection */
+ good++;
+ doneka++;
+ /* save out time */
+ if (good == 1) {
+ /* first time here */
+ doclen = c->bread;
+ }
+ else if (c->bread != doclen) {
+ bad++;
+ err_length++;
+ }
+ if (done < requests) {
+ struct data s;
+ gettimeofday(&c->done, 0);
+ s.read = c->read;
+ s.ctime = timedif(c->connect, c->start);
+ s.time = timedif(c->done, c->start);
+ stats[done++] = s;
+ }
+ c->keepalive = 0;
+ c->length = 0;
+ c->gotheader = 0;
+ c->cbx = 0;
+ c->read = c->bread = 0;
+ write_request(c);
+ c->start = c->connect; /* zero connect time with keep-alive */
+ }
+}
+
+/* --------------------------------------------------------- */
+
+/* run the tests */
+
+static void test(void)
+{
+ struct timeval timeout, now;
+ fd_set sel_read, sel_except, sel_write;
+ int i;
+
+ if (!use_html) {
+ printf("Benchmarking %s (be patient)...", hostname);
+ fflush(stdout);
+ }
+
+ {
+ /* get server information */
+ struct hostent *he;
+ he = gethostbyname(hostname);
+ if (!he)
+ err("bad hostname");
+ server.sin_family = he->h_addrtype;
+ server.sin_port = htons(port);
+ server.sin_addr.s_addr = ((unsigned long *) (he->h_addr_list[0]))[0];
+ }
+
+ con = malloc(concurrency * sizeof(struct connection));
+ memset(con, 0, concurrency * sizeof(struct connection));
+
+ stats = malloc(requests * sizeof(struct data));
+
+ FD_ZERO(&readbits);
+ FD_ZERO(&writebits);
+
+ /* setup request */
+ if (!posting) {
+ sprintf(request, "GET %s HTTP/1.0\r\n"
+ "User-Agent: ApacheBench/%s\r\n"
+ "%s" "%s" "%s"
+ "Host: %s\r\n"
+ "Accept: */*\r\n"
+ "\r\n" "%s",
+ path,
+ VERSION,
+ keepalive ? "Connection: Keep-Alive\r\n" : "",
+ cookie, auth, hostname, hdrs);
+ }
+ else {
+ sprintf(request, "POST %s HTTP/1.0\r\n"
+ "User-Agent: ApacheBench/%s\r\n"
+ "%s" "%s" "%s"
+ "Host: %s\r\n"
+ "Accept: */*\r\n"
+ "Content-length: %d\r\n"
+ "Content-type: %s\r\n"
+ "%s"
+ "\r\n",
+ path,
+ VERSION,
+ keepalive ? "Connection: Keep-Alive\r\n" : "",
+ cookie, auth,
+ hostname, postlen,
+ (content_type[0]) ? content_type : "text/plain", hdrs);
+ }
+
+ if (verbosity >= 2)
+ printf("INFO: POST header == \n---\n%s\n---\n", request);
+
+ reqlen = strlen(request);
+
+#ifdef CHARSET_EBCDIC
+ ebcdic2ascii(request, request, reqlen);
+#endif /* CHARSET_EBCDIC */
+
+ /* ok - lets start */
+ gettimeofday(&start, 0);
+
+ /* initialise lots of requests */
+ for (i = 0; i < concurrency; i++)
+ start_connect(&con[i]);
+
+ while (done < requests) {
+ int n;
+ /* setup bit arrays */
+ memcpy(&sel_except, &readbits, sizeof(readbits));
+ memcpy(&sel_read, &readbits, sizeof(readbits));
+ memcpy(&sel_write, &writebits, sizeof(readbits));
+
+ /* check for time limit expiry */
+ gettimeofday(&now, 0);
+ if (tlimit && timedif(now, start) > (tlimit * 1000)) {
+ requests = done; /* so stats are correct */
+ }
+
+ /* Timeout of 30 seconds. */
+ timeout.tv_sec = 30;
+ timeout.tv_usec = 0;
+ n = ap_select(FD_SETSIZE, &sel_read, &sel_write, &sel_except, &timeout);
+ if (!n) {
+ err("\nServer timed out\n\n");
+ }
+ if (n < 1)
+ err("select");
+
+ for (i = 0; i < concurrency; i++) {
+ int s = con[i].fd;
+ if (FD_ISSET(s, &sel_except)) {
+ bad++;
+ err_except++;
+ start_connect(&con[i]);
+ continue;
+ }
+ if (FD_ISSET(s, &sel_read))
+ read_connection(&con[i]);
+ if (FD_ISSET(s, &sel_write))
+ write_request(&con[i]);
+ }
+ }
+ if (use_html)
+ output_html_results();
+ else
+ output_results();
+}
+
+/* ------------------------------------------------------- */
+
+/* display copyright information */
+static void copyright(void)
+{
+ if (!use_html) {
+ printf("This is ApacheBench, Version %s\n", VERSION);
+ printf("Copyright (c) 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/\n");
+ printf("Copyright (c) 1998-1999 The Apache Group, http://www.apache.org/\n");
+ printf("\n");
+ }
+ else {
+ printf("<p>\n");
+ printf(" This is ApacheBench, Version %s<br>\n", VERSION);
+ printf(" Copyright (c) 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/<br>\n");
+ printf(" Copyright (c) 1998-1999 The Apache Group, http://www.apache.org/<br>\n");
+ printf("</p>\n<p>\n");
+ }
+}
+
+/* display usage information */
+static void usage(char *progname)
+{
+ fprintf(stderr, "Usage: %s [options] [http://]hostname[:port]/path\n", progname);
+ fprintf(stderr, "Options are:\n");
+ fprintf(stderr, " -n requests Number of requests to perform\n");
+ fprintf(stderr, " -c concurrency Number of multiple requests to make\n");
+ fprintf(stderr, " -t timelimit Seconds to max. wait for responses\n");
+ fprintf(stderr, " -p postfile File containg data to POST\n");
+ fprintf(stderr, " -T content-type Content-type header for POSTing\n");
+ fprintf(stderr, " -v verbosity How much troubleshooting info to print\n");
+ fprintf(stderr, " -w Print out results in HTML tables\n");
+ fprintf(stderr, " -x attributes String to insert as table attributes\n");
+ fprintf(stderr, " -y attributes String to insert as tr attributes\n");
+ fprintf(stderr, " -z attributes String to insert as td or th attributes\n");
+ fprintf(stderr, " -C attribute Add cookie, eg. 'Apache=1234. (repeatable)\n");
+ fprintf(stderr, " -H attribute Add Arbitrary header line, eg. 'Accept-Encoding: zop'\n");
+ fprintf(stderr, " Inserted after all normal header lines. (repeatable)\n");
+ fprintf(stderr, " -A attribute Add Basic WWW Authentication, the attributes\n");
+ fprintf(stderr, " are a colon separated username and password.\n");
+ fprintf(stderr, " -p attribute Add Basic Proxy Authentication, the attributes\n");
+ fprintf(stderr, " are a colon separated username and password.\n");
+ fprintf(stderr, " -V Print version number and exit\n");
+ fprintf(stderr, " -k Use HTTP KeepAlive feature\n");
+ fprintf(stderr, " -h Display usage information (this message)\n");
+ exit(EINVAL);
+}
+
+/* ------------------------------------------------------- */
+
+/* split URL into parts */
+
+static int parse_url(char *url)
+{
+ char *cp;
+ char *h;
+ char *p = NULL;
+
+ if (strlen(url) > 7 && strncmp(url, "http://", 7) == 0)
+ url += 7;
+ h = url;
+ if ((cp = strchr(url, ':')) != NULL) {
+ *cp++ = '\0';
+ p = cp;
+ url = cp;
+ }
+ if ((cp = strchr(url, '/')) == NULL)
+ return 1;
+ strcpy(path, cp);
+ *cp = '\0';
+ strcpy(hostname, h);
+ if (p != NULL)
+ port = atoi(p);
+ return 0;
+}
+
+/* ------------------------------------------------------- */
+
+/* read data to POST from file, save contents and length */
+
+static int open_postfile(char *pfile)
+{
+ int postfd, status;
+ struct stat postfilestat;
+
+ if ((postfd = open(pfile, O_RDONLY)) == -1) {
+ printf("Invalid postfile name (%s)\n", pfile);
+ return errno;
+ }
+ if ((status = fstat(postfd, &postfilestat)) == -1) {
+ perror("Can\'t stat postfile\n");
+ return status;
+ }
+ postdata = malloc(postfilestat.st_size);
+ if (!postdata) {
+ printf("Can\'t alloc postfile buffer\n");
+ return ENOMEM;
+ }
+ if (read(postfd, postdata, postfilestat.st_size) != postfilestat.st_size) {
+ printf("error reading postfilen");
+ return EIO;
+ }
+ postlen = postfilestat.st_size;
+ return 0;
+}
+
+/* ------------------------------------------------------- */
+
+extern char *optarg;
+extern int optind, opterr, optopt;
+
+/* sort out command-line args and call test */
+int main(int argc, char **argv)
+{
+ int c, r,l;
+ char tmp[1024];
+
+ /* table defaults */
+ tablestring = "";
+ trstring = "";
+ tdstring = "bgcolor=white";
+ cookie[0] = '\0';
+ auth[0] = '\0';
+ hdrs[0] = '\0';
+ optind = 1;
+ while ((c = getopt(argc, argv, "n:c:t:T:p:v:kVhwx:y:z:C:H:P:A:")) > 0) {
+ switch (c) {
+ case 'n':
+ requests = atoi(optarg);
+ if (!requests) {
+ err("Invalid number of requests\n");
+ }
+ break;
+ case 'k':
+ keepalive = 1;
+ break;
+ case 'c':
+ concurrency = atoi(optarg);
+ break;
+ case 'p':
+ if (0 == (r = open_postfile(optarg))) {
+ posting = 1;
+ }
+ else if (postdata) {
+ exit(r);
+ }
+ break;
+ case 'v':
+ verbosity = atoi(optarg);
+ break;
+ case 't':
+ tlimit = atoi(optarg);
+ requests = MAX_REQUESTS; /* need to size data array on
+ * something */
+ break;
+ case 'T':
+ strcpy(content_type, optarg);
+ break;
+ case 'C':
+ strncat(cookie, "Cookie: ", sizeof(cookie));
+ strncat(cookie, optarg, sizeof(cookie));
+ strncat(cookie, "\r\n", sizeof(cookie));
+ break;
+ case 'A':
+ /* assume username passwd already to be in colon separated form. Ready
+ * to be uu-encoded.
+ */
+ while(isspace(*optarg))
+ optarg++;
+ l=ap_base64encode(tmp,optarg,strlen(optarg));
+ tmp[l]='\0';
+
+ strncat(auth, "Authorization: basic ", sizeof(auth));
+ strncat(auth, tmp, sizeof(auth));
+ strncat(auth, "\r\n", sizeof(auth));
+ break;
+ case 'P':
+ /*
+ * assume username passwd already to be in colon separated form.
+ */
+ while(isspace(*optarg))
+ optarg++;
+ l=ap_base64encode(tmp,optarg,strlen(optarg));
+ tmp[l]='\0';
+
+ strncat(auth, "Proxy-Authorization: basic ", sizeof(auth));
+ strncat(auth, tmp, sizeof(auth));
+ strncat(auth, "\r\n", sizeof(auth));
+ break;
+ case 'H':
+ strncat(hdrs, optarg, sizeof(hdrs));
+ strncat(hdrs, "\r\n", sizeof(hdrs));
+ break;
+ case 'V':
+ copyright();
+ exit(0);
+ break;
+ case 'w':
+ use_html = 1;
+ break;
+ /*
+ * if any of the following three are used, turn on html output
+ * automatically
+ */
+ case 'x':
+ use_html = 1;
+ tablestring = optarg;
+ break;
+ case 'y':
+ use_html = 1;
+ trstring = optarg;
+ break;
+ case 'z':
+ use_html = 1;
+ tdstring = optarg;
+ break;
+ case 'h':
+ usage(argv[0]);
+ break;
+ default:
+ fprintf(stderr, "%s: invalid option `%c'\n", argv[0], c);
+ usage(argv[0]);
+ break;
+ }
+ }
+ if (optind != argc - 1) {
+ fprintf(stderr, "%s: wrong number of arguments\n", argv[0]);
+ usage(argv[0]);
+ }
+
+ if (parse_url(argv[optind++])) {
+ fprintf(stderr, "%s: invalid URL\n", argv[0]);
+ usage(argv[0]);
+ }
+
+ copyright();
+ test();
+
+ exit(0);
+}
--- /dev/null
+#!/usr/local/bin/perl
+## ====================================================================
+## Copyright (c) 1998-1999 The Apache Group. All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted provided that the following conditions
+## are met:
+##
+## 1. Redistributions of source code must retain the above copyright
+## notice, this list of conditions and the following disclaimer.
+##
+## 2. Redistributions in binary form must reproduce the above copyright
+## notice, this list of conditions and the following disclaimer in
+## the documentation and/or other materials provided with the
+## distribution.
+##
+## 3. All advertising materials mentioning features or use of this
+## software must display the following acknowledgment:
+## "This product includes software developed by the Apache Group
+## for use in the Apache HTTP server project (http://www.apache.org/)."
+##
+## 4. The names "Apache Server" and "Apache Group" must not be used to
+## endorse or promote products derived from this software without
+## prior written permission. For written permission, please contact
+## apache@apache.org.
+##
+## 5. Products derived from this software may not be called "Apache"
+## nor may "Apache" appear in their names without prior written
+## permission of the Apache Group.
+##
+## 6. Redistributions of any form whatsoever must retain the following
+## acknowledgment:
+## "This product includes software developed by the Apache Group
+## for use in the Apache HTTP server project (http://www.apache.org/)."
+##
+## THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+## EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
+## ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+## NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+## LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+## HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+## STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+## ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+## OF THE POSSIBILITY OF SUCH DAMAGE.
+## ====================================================================
+##
+## This software consists of voluntary contributions made by many
+## individuals on behalf of the Apache Group and was originally based
+## on public domain software written at the National Center for
+## Supercomputing Applications, University of Illinois, Urbana-Champaign.
+## For more information on the Apache Group and the Apache HTTP server
+## project, please see <http://www.apache.org/>.
+##
+
+##
+## apxs -- APache eXtenSion tool
+## Written by Ralf S. Engelschall <rse@apache.org>
+##
+
+require 5.003;
+use strict;
+package apxs;
+
+##
+## Configuration
+##
+
+my $CFG_TARGET = '@TARGET@'; # substituted via Makefile.tmpl
+my $CFG_CC = '@CC@'; # substituted via Makefile.tmpl
+my $CFG_CFLAGS = '@CFLAGS@'; # substituted via Makefile.tmpl
+my $CFG_CFLAGS_SHLIB = '@CFLAGS_SHLIB@'; # substituted via Makefile.tmpl
+my $CFG_LD_SHLIB = '@LD_SHLIB@'; # substituted via Makefile.tmpl
+my $CFG_LDFLAGS_SHLIB = '@LDFLAGS_MOD_SHLIB@'; # substituted via Makefile.tmpl
+my $CFG_LIBS_SHLIB = '@LIBS_SHLIB@'; # substituted via Makefile.tmpl
+my $CFG_PREFIX = '@prefix@'; # substituted via APACI install
+my $CFG_SBINDIR = '@sbindir@'; # substituted via APACI install
+my $CFG_INCLUDEDIR = '@includedir@'; # substituted via APACI install
+my $CFG_LIBEXECDIR = '@libexecdir@'; # substituted via APACI install
+my $CFG_SYSCONFDIR = '@sysconfdir@'; # substituted via APACI install
+
+##
+## Cleanup the above stuff
+##
+$CFG_CFLAGS =~ s|^\s+||;
+$CFG_CFLAGS =~ s|\s+$||;
+$CFG_CFLAGS =~ s|\s+`.+apaci`||;
+
+##
+## Initial shared object support check
+##
+if (not -x "$CFG_SBINDIR/$CFG_TARGET") {
+ print STDERR "apxs:Error: $CFG_SBINDIR/$CFG_TARGET not found or not executable\n";
+ exit(1);
+}
+if (not grep(/mod_so/, `$CFG_SBINDIR/$CFG_TARGET -l`)) {
+ print STDERR "apxs:Error: Sorry, no shared object support for Apache\n";
+ print STDERR "apxs:Error: available under your platform. Make sure\n";
+ print STDERR "apxs:Error: the Apache module mod_so is compiled into\n";
+ print STDERR "apxs:Error: your server binary `$CFG_SBINDIR/$CFG_TARGET'.\n";
+ exit(1);
+}
+
+##
+## parse argument line
+##
+
+# defaults for parameters
+my $opt_n = '';
+my $opt_g = '';
+my $opt_c = 0;
+my $opt_o = '';
+my @opt_D = ();
+my @opt_I = ();
+my @opt_L = ();
+my @opt_l = ();
+my @opt_W = ();
+my @opt_S = ();
+my $opt_e = 0;
+my $opt_i = 0;
+my $opt_a = 0;
+my $opt_A = 0;
+my $opt_q = 0;
+
+# this subroutine is derived from Perl's getopts.pl with the enhancement of
+# the "+" metacharater at the format string to allow a list to be build by
+# subsequent occurance of the same option.
+sub Getopts {
+ my ($argumentative, @ARGV) = @_;
+ my (@args, $first, $rest, $pos);
+ my ($errs) = 0;
+ local ($_);
+ local ($[) = 0;
+
+ @args = split( / */, $argumentative);
+ while(@ARGV && ($_ = $ARGV[0]) =~ /^-(.)(.*)/) {
+ ($first, $rest) = ($1,$2);
+ if ($_ =~ m|^--$|) {
+ shift(@ARGV);
+ last;
+ }
+ $pos = index($argumentative,$first);
+ if($pos >= $[) {
+ if($args[$pos+1] eq ':') {
+ shift(@ARGV);
+ if($rest eq '') {
+ unless (@ARGV) {
+ print STDERR "apxs:Error: Incomplete option: $first (needs an argument)\n";
+ ++$errs;
+ }
+ $rest = shift(@ARGV);
+ }
+ eval "\$opt_$first = \$rest;";
+ }
+ elsif ($args[$pos+1] eq '+') {
+ shift(@ARGV);
+ if($rest eq '') {
+ unless (@ARGV) {
+ print STDERR "apxs:Error: Incomplete option: $first (needs an argument)\n";
+ ++$errs;
+ }
+ $rest = shift(@ARGV);
+ }
+ eval "push(\@opt_$first, \$rest);";
+ }
+ else {
+ eval "\$opt_$first = 1";
+ if($rest eq '') {
+ shift(@ARGV);
+ }
+ else {
+ $ARGV[0] = "-$rest";
+ }
+ }
+ }
+ else {
+ print STDERR "apxs:Error: Unknown option: $first\n";
+ ++$errs;
+ if($rest ne '') {
+ $ARGV[0] = "-$rest";
+ }
+ else {
+ shift(@ARGV);
+ }
+ }
+ }
+ return ($errs == 0, @ARGV);
+}
+
+sub usage {
+ print STDERR "Usage: apxs -g [-S <var>=<val>] -n <modname>\n";
+ print STDERR " apxs -q [-S <var>=<val>] <query> ...\n";
+ print STDERR " apxs -c [-S <var>=<val>] [-o <dsofile>] [-D <name>[=<value>]]\n";
+ print STDERR " [-I <incdir>] [-L <libdir>] [-l <libname>] [-Wc,<flags>]\n";
+ print STDERR " [-Wl,<flags>] <files> ...\n";
+ print STDERR " apxs -i [-S <var>=<val>] [-a] [-A] [-n <modname>] <dsofile> ...\n";
+ print STDERR " apxs -e [-S <var>=<val>] [-a] [-A] [-n <modname>] <dsofile> ...\n";
+ exit(1);
+}
+
+# option handling
+my $rc;
+($rc, @ARGV) = &Getopts("qn:gco:I+D+L+l+W+S+eiaA", @ARGV);
+&usage if ($rc == 0);
+&usage if ($#ARGV == -1 and not $opt_g);
+&usage if (not $opt_q and not ($opt_g and $opt_n) and not $opt_i and not $opt_c and not $opt_e);
+
+# argument handling
+my @args = @ARGV;
+my $name = 'unknown';
+$name = $opt_n if ($opt_n ne '');
+
+if (@opt_S) {
+ my ($opt_S);
+ foreach $opt_S (@opt_S) {
+ if ($opt_S =~ m/^([^=]+)=(.*)$/) {
+ my ($var) = $1;
+ my ($val) = $2;
+ my $oldval = eval "\$CFG_$var";
+
+ unless ($var and $oldval) {
+ print STDERR "apxs:Error: no config variable $var\n";
+ &usage;
+ }
+
+ eval "\$CFG_${var}=\"${val}\"";
+ } else {
+ print STDERR "apxs:Error: malformatted -S option\n";
+ &usage;
+ }
+ }
+}
+
+##
+## Operation
+##
+
+# helper function for executing a list of
+# system command with return code checks
+sub execute_cmds {
+ my (@cmds) = @_;
+ my ($cmd, $rc);
+
+ foreach $cmd (@cmds) {
+ print STDERR "$cmd\n";
+ $rc = system("$cmd");
+ if ($rc != 0) {
+ printf(STDERR "apxs:Break: Command failed with rc=%d\n", $rc << 8);
+ exit(1);
+ }
+ }
+}
+
+if ($opt_g) {
+ ##
+ ## SAMPLE MODULE SOURCE GENERATION
+ ##
+
+ if (-d $name) {
+ print STDERR "apxs:Error: Directory `$name' already exists. Remove first\n";
+ exit(1);
+ }
+
+ my $data = join('', <DATA>);
+ $data =~ s|%NAME%|$name|sg;
+ $data =~ s|%TARGET%|$CFG_TARGET|sg;
+
+ my ($mkf, $src) = ($data =~ m|^(.+)-=#=-\n(.+)|s);
+
+ print STDERR "Creating [DIR] $name\n";
+ system("mkdir $name");
+ print STDERR "Creating [FILE] $name/Makefile\n";
+ open(FP, ">${name}/Makefile") || die;
+ print FP $mkf;
+ close(FP);
+ print STDERR "Creating [FILE] $name/mod_$name.c\n";
+ open(FP, ">${name}/mod_${name}.c") || die;
+ print FP $src;
+ close(FP);
+
+ exit(0);
+}
+
+
+if ($opt_q) {
+ ##
+ ## QUERY INFORMATION
+ ##
+
+ my $result = '';
+ my $arg;
+ foreach $arg (@args) {
+ my $ok = 0;
+ my $name;
+ foreach $name (qw(
+ TARGET CC CFLAGS CFLAGS_SHLIB LD_SHLIB LDFLAGS_SHLIB LIBS_SHLIB
+ PREFIX SBINDIR INCLUDEDIR LIBEXECDIR SYSCONFDIR
+ )) {
+ if ($arg eq $name or $arg eq lc($name)) {
+ my $val = eval "\$CFG_$name";
+ $result .= "${val}::";
+ $ok = 1;
+ }
+ }
+ if (not $ok) {
+ printf(STDERR "apxs:Error: Invalid query string `%s'\n", $arg);
+ exit(1);
+ }
+ }
+ $result =~ s|::$||;
+ $result =~ s|::| |;
+ print $result;
+}
+
+if ($opt_c) {
+ ##
+ ## SHARED OBJECT COMPILATION
+ ##
+
+ # split files into sources and objects
+ my @srcs = ();
+ my @objs = ();
+ my $f;
+ foreach $f (@args) {
+ if ($f =~ m|\.c$|) {
+ push(@srcs, $f);
+ }
+ else {
+ push(@objs, $f);
+ }
+ }
+
+ # determine output file
+ my $dso_file;
+ if ($opt_o eq '') {
+ if ($#srcs > -1) {
+ $dso_file = $srcs[0];
+ $dso_file =~ s|\.[^.]+$|.so|;
+ }
+ elsif ($#objs > -1) {
+ $dso_file = $objs[0];
+ $dso_file =~ s|\.[^.]+$|.so|;
+ }
+ else {
+ $dso_file = "mod_unknown.so";
+ }
+ }
+ else {
+ $dso_file = $opt_o;
+ }
+
+ # create compilation commands
+ my @cmds = ();
+ my $opt = '';
+ my ($opt_Wc, $opt_I, $opt_D);
+ foreach $opt_Wc (@opt_W) {
+ $opt .= "$1 " if ($opt_Wc =~ m|^\s*c,(.*)$|);
+ }
+ foreach $opt_I (@opt_I) {
+ $opt .= "-I$opt_I ";
+ }
+ foreach $opt_D (@opt_D) {
+ $opt .= "-D$opt_D ";
+ }
+ my $cflags = "$CFG_CFLAGS $CFG_CFLAGS_SHLIB";
+ my $s;
+ foreach $s (@srcs) {
+ my $o = $s;
+ $o =~ s|\.c$|.o|;
+ push(@cmds, "$CFG_CC $cflags -I$CFG_INCLUDEDIR $opt -c $s");
+ unshift(@objs, $o);
+ }
+
+ # create link command
+ my $cmd = "$CFG_LD_SHLIB $CFG_LDFLAGS_SHLIB -o $dso_file";
+ my $o;
+ foreach $o (@objs) {
+ $cmd .= " $o";
+ }
+ $opt = '';
+ my ($opt_Wl, $opt_L, $opt_l);
+ foreach $opt_Wl (@opt_W) {
+ if($CFG_LD_SHLIB ne "gcc") {
+ $opt .= " $1" if ($opt_Wl =~ m|^\s*l,(.*)$|);
+ } else {
+ $opt .= " -W$opt_Wl";
+ }
+ }
+ foreach $opt_L (@opt_L) {
+ $opt .= " -L$opt_L";
+ }
+ foreach $opt_l (@opt_l) {
+ $opt .= " -l$opt_l";
+ }
+ $cmd .= $opt;
+ $cmd .= " $CFG_LIBS_SHLIB";
+ push(@cmds, $cmd);
+
+ # execute the commands
+ &execute_cmds(@cmds);
+
+ # allow one-step compilation and installation
+ if ($opt_i or $opt_e) {
+ @args = ( $dso_file );
+ }
+}
+
+if ($opt_i or $opt_e) {
+ ##
+ ## SHARED OBJECT INSTALLATION
+ ##
+
+ # determine installation commands
+ # and corresponding LoadModule/AddModule directives
+ my @lmd = ();
+ my @amd = ();
+ my @cmds = ();
+ my $f;
+ foreach $f (@args) {
+ if ($f !~ m|\.so$|) {
+ print STDERR "apxs:Error: file $f is not a shared object\n";
+ exit(1);
+ }
+ my $t = $f;
+ $t =~ s|^.+/([^/]+)$|$1|;
+ if ($opt_i) {
+ push(@cmds, "cp $f $CFG_LIBEXECDIR/$t");
+ push(@cmds, "chmod 755 $CFG_LIBEXECDIR/$t");
+ }
+
+ # determine module symbolname and filename
+ my $filename = '';
+ if ($name eq 'unknown') {
+ $name = '';
+ my $base = $f;
+ $base =~ s|\.[^.]+$||;
+ if (-f "$base.c") {
+ open(FP, "<$base.c");
+ my $content = join('', <FP>);
+ close(FP);
+ if ($content =~ m|.*module\s+(?:MODULE_VAR_EXPORT\s+)?([a-zA-Z0-9_]+)_module\s*=\s*.*|s) {
+ $name = "$1";
+ $filename = "$base.c";
+ $filename =~ s|^[^/]+/||;
+ }
+ }
+ if ($name eq '') {
+ if ($base =~ m|.*mod_([a-zA-Z0-9_]+)\..+|) {
+ $name = "$1";
+ $filename = $base;
+ $filename =~ s|^[^/]+/||;
+ }
+ }
+ if ($name eq '') {
+ print "apxs:Error: Sorry, cannot determine bootstrap symbol name\n";
+ print "apxs:Error: Please specify one with option `-n'\n";
+ exit(1);
+ }
+ }
+ if ($filename eq '') {
+ $filename = "mod_${name}.c";
+ }
+ my $dir = $CFG_LIBEXECDIR;
+ $dir =~ s|^$CFG_PREFIX/?||;
+ $dir =~ s|(.)$|$1/|;
+ push(@lmd, sprintf("LoadModule %-18s %s", "${name}_module", "$dir$t"));
+ push(@amd, sprintf("AddModule %s", $filename));
+ }
+
+ # execute the commands
+ &execute_cmds(@cmds);
+
+ # activate module via LoadModule/AddModule directive
+ if ($opt_a or $opt_A) {
+ if (not -f "$CFG_SYSCONFDIR/$CFG_TARGET.conf") {
+ print "apxs:Error: Config file $CFG_SYSCONFDIR/$CFG_TARGET.conf not found\n";
+ exit(1);
+ }
+
+ open(FP, "<$CFG_SYSCONFDIR/$CFG_TARGET.conf") || die;
+ my $content = join('', <FP>);
+ close(FP);
+
+ if ($content !~ m|\n#?\s*LoadModule\s+|) {
+ print STDERR "apxs:Error: Activation failed for custom $CFG_SYSCONFDIR/$CFG_TARGET.conf file.\n";
+ print STDERR "apxs:Error: At least one `LoadModule' directive already has to exist.\n";
+ exit(1);
+ }
+
+ my $lmd;
+ my $c = '';
+ $c = '#' if ($opt_A);
+ foreach $lmd (@lmd) {
+ my $what = $opt_A ? "preparing" : "activating";
+ if ($content !~ m|\n#?\s*$lmd|) {
+ $content =~ s|^(.*\n#?\s*LoadModule\s+[^\n]+\n)|$1$c$lmd\n|sg;
+ } else {
+ $content =~ s|^(.*\n)#?\s*$lmd[^\n]*\n|$1$c$lmd\n|sg;
+ }
+ $lmd =~ m|LoadModule\s+(.+?)_module.*|;
+ print STDERR "[$what module `$1' in $CFG_SYSCONFDIR/$CFG_TARGET.conf]\n";
+ }
+ my $amd;
+ foreach $amd (@amd) {
+ if ($content !~ m|\n#?\s*$amd|) {
+ $content =~ s|^(.*\n#?\s*AddModule\s+[^\n]+\n)|$1$c$amd\n|sg;
+ } else {
+ $content =~ s|^(.*\n)#?\s*$amd[^\n]*\n|$1$c$amd\n|sg;
+ }
+ }
+ if (@lmd or @amd) {
+ open(FP, ">$CFG_SYSCONFDIR/$CFG_TARGET.conf.new") || die;
+ print FP $content;
+ close(FP);
+ system("cp $CFG_SYSCONFDIR/$CFG_TARGET.conf $CFG_SYSCONFDIR/$CFG_TARGET.conf.bak && " .
+ "cp $CFG_SYSCONFDIR/$CFG_TARGET.conf.new $CFG_SYSCONFDIR/$CFG_TARGET.conf && " .
+ "rm $CFG_SYSCONFDIR/$CFG_TARGET.conf.new");
+ }
+ }
+}
+
+##EOF##
+__DATA__
+##
+## Makefile -- Build procedure for sample %NAME% Apache module
+## Autogenerated via ``apxs -n %NAME% -g''.
+##
+
+# the used tools
+APXS=apxs
+APACHECTL=apachectl
+
+# additional defines, includes and libraries
+#DEF=-Dmy_define=my_value
+#INC=-Imy/include/dir
+#LIB=-Lmy/lib/dir -lmylib
+
+# the default target
+all: mod_%NAME%.so
+
+# compile the shared object file
+mod_%NAME%.so: mod_%NAME%.c
+ $(APXS) -c $(DEF) $(INC) $(LIB) mod_%NAME%.c
+
+# install the shared object file into Apache
+install: all
+ $(APXS) -i -a -n '%NAME%' mod_%NAME%.so
+
+# cleanup
+clean:
+ -rm -f mod_%NAME%.o mod_%NAME%.so
+
+# simple test
+test: reload
+ lynx -mime_header http://localhost/%NAME%
+
+# install and activate shared object by reloading Apache to
+# force a reload of the shared object file
+reload: install restart
+
+# the general Apache start/restart/stop
+# procedures
+start:
+ $(APACHECTL) start
+restart:
+ $(APACHECTL) restart
+stop:
+ $(APACHECTL) stop
+
+-=#=-
+/*
+** mod_%NAME%.c -- Apache sample %NAME% module
+** [Autogenerated via ``apxs -n %NAME% -g'']
+**
+** To play with this sample module first compile it into a
+** DSO file and install it into Apache's libexec directory
+** by running:
+**
+** $ apxs -c -i mod_%NAME%.c
+**
+** Then activate it in Apache's %TARGET%.conf file for instance
+** for the URL /%NAME% in as follows:
+**
+** # %TARGET%.conf
+** LoadModule %NAME%_module libexec/mod_%NAME%.so
+** <Location /%NAME%>
+** SetHandler %NAME%
+** </Location>
+**
+** Then after restarting Apache via
+**
+** $ apachectl restart
+**
+** you immediately can request the URL /%NAME and watch for the
+** output of this module. This can be achieved for instance via:
+**
+** $ lynx -mime_header http://localhost/%NAME%
+**
+** The output should be similar to the following one:
+**
+** HTTP/1.1 200 OK
+** Date: Tue, 31 Mar 1998 14:42:22 GMT
+** Server: Apache/1.3.4 (Unix)
+** Connection: close
+** Content-Type: text/html
+**
+** The sample page from mod_%NAME%.c
+*/
+
+#include "httpd.h"
+#include "http_config.h"
+#include "http_protocol.h"
+#include "ap_config.h"
+
+/* The sample content handler */
+static int %NAME%_handler(request_rec *r)
+{
+ r->content_type = "text/html";
+ ap_send_http_header(r);
+ if (!r->header_only)
+ ap_rputs("The sample page from mod_%NAME%.c\n", r);
+ return OK;
+}
+
+/* Dispatch list of content handlers */
+static const handler_rec %NAME%_handlers[] = {
+ { "%NAME%", %NAME%_handler },
+ { NULL, NULL }
+};
+
+/* Dispatch list for API hooks */
+module MODULE_VAR_EXPORT %NAME%_module = {
+ STANDARD_MODULE_STUFF,
+ NULL, /* module initializer */
+ NULL, /* create per-dir config structures */
+ NULL, /* merge per-dir config structures */
+ NULL, /* create per-server config structures */
+ NULL, /* merge per-server config structures */
+ NULL, /* table of config file commands */
+ %NAME%_handlers, /* [#8] MIME-typed-dispatched handlers */
+ NULL, /* [#1] URI to filename translation */
+ NULL, /* [#4] validate user id from request */
+ NULL, /* [#5] check if the user is ok _here_ */
+ NULL, /* [#3] check access by host address */
+ NULL, /* [#6] determine MIME type */
+ NULL, /* [#7] pre-run fixups */
+ NULL, /* [#9] log a transaction */
+ NULL, /* [#2] header parser */
+ NULL, /* child_init */
+ NULL, /* child_exit */
+ NULL /* [#0] post read-request */
+};
+
--- /dev/null
+#!/usr/local/bin/perl
+
+# ====================================================================
+# Copyright (c) 1995-1999 The Apache Group. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+#
+# 3. All advertising materials mentioning features or use of this
+# software must display the following acknowledgment:
+# "This product includes software developed by the Apache Group
+# for use in the Apache HTTP server project (http://www.apache.org/)."
+#
+# 4. The names "Apache Server" and "Apache Group" must not be used to
+# endorse or promote products derived from this software without
+# prior written permission. For written permission, please contact
+# apache@apache.org.
+#
+# 5. Products derived from this software may not be called "Apache"
+# nor may "Apache" appear in their names without prior written
+# permission of the Apache Group.
+#
+# 6. Redistributions of any form whatsoever must retain the following
+# acknowledgment:
+# "This product includes software developed by the Apache Group
+# for use in the Apache HTTP server project (http://www.apache.org/)."
+#
+# THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+# EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
+# ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+# ====================================================================
+#
+# This software consists of voluntary contributions made by many
+# individuals on behalf of the Apache Group and was originally based
+# on public domain software written at the National Center for
+# Supercomputing Applications, University of Illinois, Urbana-Champaign.
+# For more information on the Apache Group and the Apache HTTP server
+# project, please see <http://www.apache.org/>.
+
+#for more functionality see the HTTPD::UserAdmin module:
+# http://www.perl.com/CPAN/modules/by-module/HTTPD/HTTPD-Tools-x.xx.tar.gz
+#
+# usage: dbmmanage <DBMfile> <command> <key> <value>
+
+package dbmmanage;
+# -ldb -lndbm -lgdbm
+BEGIN { @AnyDBM_File::ISA = qw(DB_File NDBM_File GDBM_File) }
+use strict;
+use Fcntl;
+use AnyDBM_File ();
+
+my($file,$command,$key,$crypted_pwd) = @ARGV;
+
+usage() unless $file and $command and defined &{$dbmc::{$command}};
+
+# if your osname is in $newstyle_salt, then use new style salt (starts with '_' and contains
+# four bytes of iteration count and four bytes of salt). Otherwise, just use
+# the traditional two-byte salt.
+# see the man page on your system to decide if you have a newer crypt() lib.
+# I believe that 4.4BSD derived systems do (at least BSD/OS 2.0 does).
+# The new style crypt() allows up to 20 characters of the password to be
+# significant rather than only 8.
+my $newstyle_salt = join '|', qw{bsdos}; #others?
+
+# remove extension if any
+my $chop = join '|', qw{db.? pag dir};
+$file =~ s/\.($chop)$//;
+
+my $is_update = $command eq "update";
+my $Is_Win32 = $^O eq "MSWin32";
+my %DB = ();
+my @range = ();
+my($mode, $flags) = $command =~
+ /^(?:view|check)$/ ? (0644, O_RDONLY) : (0644, O_RDWR|O_CREAT);
+
+tie %DB, "AnyDBM_File", $file, $flags, $mode || die "Can't tie $file: $!";
+dbmc->$command();
+untie %DB;
+
+sub usage {
+ my $cmds = join "|", sort keys %dbmc::;
+ die "usage: $0 filename [$cmds] [username]\n";
+}
+
+my $x;
+sub genseed {
+ my $psf;
+ for (qw(-xlwwa -le)) {
+ `ps $_ 2>/dev/null`;
+ $psf = $_, last unless $?;
+ }
+ srand (time ^ $$ ^ unpack("%L*", `ps $psf | gzip -f`));
+ @range = (qw(. /), '0'..'9','a'..'z','A'..'Z');
+ $x = int scalar @range;
+}
+
+sub randchar {
+ join '', map $range[rand $x], 1..shift||1;
+}
+
+sub salt {
+ my $newstyle = $^O =~ /(?:$newstyle_salt)/;
+ genseed() unless @range;
+ return $newstyle ?
+ join '', "_", randchar, "a..", randchar(4) :
+ randchar(2);
+}
+
+sub getpass {
+ my $prompt = shift || "Enter password:";
+
+ unless($Is_Win32) {
+ open STDIN, "/dev/tty" or warn "couldn't open /dev/tty $!\n";
+ system "stty -echo;";
+ }
+
+ my($c,$pwd);
+ print STDERR $prompt;
+ while (($c = getc(STDIN)) ne '' and $c ne "\n" and $c ne "\r") {
+ $pwd .= $c;
+ }
+
+ system "stty echo" unless $Is_Win32;
+ print STDERR "\n";
+ die "Can't use empty password!\n" unless length $pwd;
+ return $pwd;
+}
+
+sub dbmc::update {
+ die "Sorry, user `$key' doesn't exist!\n" unless $DB{$key};
+ dbmc->adduser;
+}
+
+sub dbmc::add {
+ die "Can't use empty password!\n" unless $crypted_pwd;
+ unless($is_update) {
+ die "Sorry, user `$key' already exists!\n" if $DB{$key};
+ }
+ $DB{$key} = $crypted_pwd;
+ my $action = $is_update ? "updated" : "added";
+ print "User $key $action with password encrypted to $DB{$key}\n";
+}
+
+sub dbmc::adduser {
+ my $value = getpass "New password:";
+ die "They don't match, sorry.\n" unless getpass("Re-type new password:") eq $value;
+ $crypted_pwd = crypt $value, caller->salt;
+ dbmc->add;
+}
+
+sub dbmc::delete {
+ die "Sorry, user `$key' doesn't exist!\n" unless $DB{$key};
+ delete $DB{$key}, print "`$key' deleted\n";
+}
+
+sub dbmc::view {
+ print $key ? "$key:$DB{$key}\n" : map { "$_:$DB{$_}\n" if $DB{$_} } keys %DB;
+}
+
+sub dbmc::check {
+ die "Sorry, user `$key' doesn't exist!\n" unless $DB{$key};
+ print crypt(getpass(), $DB{$key}) eq $DB{$key} ? "password ok\n" : "password mismatch\n";
+}
+
+sub dbmc::import {
+ while(defined($_ = <STDIN>) and chomp) {
+ ($key,$crypted_pwd) = split /:/, $_, 2;
+ dbmc->add;
+ }
+}
+
--- /dev/null
+/* ====================================================================
+ * Copyright (c) 1995-1999 The Apache Group. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * 4. The names "Apache Server" and "Apache Group" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission. For written permission, please contact
+ * apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ * nor may "Apache" appear in their names without prior written
+ * permission of the Apache Group.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Group and was originally based
+ * on public domain software written at the National Center for
+ * Supercomputing Applications, University of Illinois, Urbana-Champaign.
+ * For more information on the Apache Group and the Apache HTTP server
+ * project, please see <http://www.apache.org/>.
+ *
+ */
+/******************************************************************************
+ ******************************************************************************
+ * NOTE! This program is not safe as a setuid executable! Do not make it
+ * setuid!
+ ******************************************************************************
+ *****************************************************************************/
+/*
+ * htdigest.c: simple program for manipulating digest passwd file for Apache
+ *
+ * by Alexei Kosut, based on htpasswd.c, by Rob McCool
+ */
+
+#include "ap_config.h"
+#include <sys/types.h>
+#include "ap.h"
+#include "ap_md5.h"
+#if defined(MPE) || defined(QNX) || defined(WIN32) || defined(__TANDEM)
+#include <signal.h>
+#else
+#include <sys/signal.h>
+#endif
+
+#ifdef WIN32
+#include <conio.h>
+#define unlink _unlink
+#endif
+
+#ifdef CHARSET_EBCDIC
+#define LF '\n'
+#define CR '\r'
+#else
+#define LF 10
+#define CR 13
+#endif /* CHARSET_EBCDIC */
+
+#define MAX_STRING_LEN 256
+
+char *tn;
+
+static void getword(char *word, char *line, char stop)
+{
+ int x = 0, y;
+
+ for (x = 0; ((line[x]) && (line[x] != stop)); x++)
+ word[x] = line[x];
+
+ word[x] = '\0';
+ if (line[x])
+ ++x;
+ y = 0;
+
+ while ((line[y++] = line[x++]));
+}
+
+static int getline(char *s, int n, FILE *f)
+{
+ register int i = 0;
+
+ while (1) {
+ s[i] = (char) fgetc(f);
+
+ if (s[i] == CR)
+ s[i] = fgetc(f);
+
+ if ((s[i] == 0x4) || (s[i] == LF) || (i == (n - 1))) {
+ s[i] = '\0';
+ return (feof(f) ? 1 : 0);
+ }
+ ++i;
+ }
+}
+
+static void putline(FILE *f, char *l)
+{
+ int x;
+
+ for (x = 0; l[x]; x++)
+ fputc(l[x], f);
+ fputc('\n', f);
+}
+
+
+static void add_password(char *user, char *realm, FILE *f)
+{
+ char *pw;
+ AP_MD5_CTX context;
+ unsigned char digest[16];
+ char string[MAX_STRING_LEN];
+ char pwin[MAX_STRING_LEN];
+ char pwv[MAX_STRING_LEN];
+ unsigned int i;
+
+ if (ap_getpass("New password: ", pwin, sizeof(pwin)) != 0) {
+ fprintf(stderr, "password too long");
+ exit(5);
+ }
+ ap_getpass("Re-type new password: ", pwv, sizeof(pwv));
+ if (strcmp(pwin, pwv) != 0) {
+ fprintf(stderr, "They don't match, sorry.\n");
+ if (tn) {
+ unlink(tn);
+ }
+ exit(1);
+ }
+ pw = pwin;
+ fprintf(f, "%s:%s:", user, realm);
+
+ /* Do MD5 stuff */
+ sprintf(string, "%s:%s:%s", user, realm, pw);
+
+ ap_MD5Init(&context);
+ ap_MD5Update(&context, (unsigned char *) string, strlen(string));
+ ap_MD5Final(digest, &context);
+
+ for (i = 0; i < 16; i++)
+ fprintf(f, "%02x", digest[i]);
+
+ fprintf(f, "\n");
+}
+
+static void usage(void)
+{
+ fprintf(stderr, "Usage: htdigest [-c] passwordfile realm username\n");
+ fprintf(stderr, "The -c flag creates a new file.\n");
+ exit(1);
+}
+
+static void interrupted(void)
+{
+ fprintf(stderr, "Interrupted.\n");
+ if (tn)
+ unlink(tn);
+ exit(1);
+}
+
+int main(int argc, char *argv[])
+{
+ FILE *tfp, *f;
+ char user[MAX_STRING_LEN];
+ char realm[MAX_STRING_LEN];
+ char line[MAX_STRING_LEN];
+ char l[MAX_STRING_LEN];
+ char w[MAX_STRING_LEN];
+ char x[MAX_STRING_LEN];
+ char command[MAX_STRING_LEN];
+ int found;
+
+ tn = NULL;
+ signal(SIGINT, (void (*)(int)) interrupted);
+ if (argc == 5) {
+ if (strcmp(argv[1], "-c"))
+ usage();
+ if (!(tfp = fopen(argv[2], "w"))) {
+ fprintf(stderr, "Could not open passwd file %s for writing.\n",
+ argv[2]);
+ perror("fopen");
+ exit(1);
+ }
+ printf("Adding password for %s in realm %s.\n", argv[4], argv[3]);
+ add_password(argv[4], argv[3], tfp);
+ fclose(tfp);
+ exit(0);
+ }
+ else if (argc != 4)
+ usage();
+
+ tn = tmpnam(NULL);
+ if (!(tfp = fopen(tn, "w"))) {
+ fprintf(stderr, "Could not open temp file.\n");
+ exit(1);
+ }
+
+ if (!(f = fopen(argv[1], "r"))) {
+ fprintf(stderr,
+ "Could not open passwd file %s for reading.\n", argv[1]);
+ fprintf(stderr, "Use -c option to create new one.\n");
+ exit(1);
+ }
+ strcpy(user, argv[3]);
+ strcpy(realm, argv[2]);
+
+ found = 0;
+ while (!(getline(line, MAX_STRING_LEN, f))) {
+ if (found || (line[0] == '#') || (!line[0])) {
+ putline(tfp, line);
+ continue;
+ }
+ strcpy(l, line);
+ getword(w, l, ':');
+ getword(x, l, ':');
+ if (strcmp(user, w) || strcmp(realm, x)) {
+ putline(tfp, line);
+ continue;
+ }
+ else {
+ printf("Changing password for user %s in realm %s\n", user, realm);
+ add_password(user, realm, tfp);
+ found = 1;
+ }
+ }
+ if (!found) {
+ printf("Adding user %s in realm %s\n", user, realm);
+ add_password(user, realm, tfp);
+ }
+ fclose(f);
+ fclose(tfp);
+#if defined(OS2) || defined(WIN32)
+ sprintf(command, "copy \"%s\" \"%s\"", tn, argv[1]);
+#else
+ sprintf(command, "cp %s %s", tn, argv[1]);
+#endif
+ system(command);
+ unlink(tn);
+ return 0;
+}
--- /dev/null
+/* ====================================================================
+ * Copyright (c) 1995-1999 The Apache Group. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * 4. The names "Apache Server" and "Apache Group" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission. For written permission, please contact
+ * apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ * nor may "Apache" appear in their names without prior written
+ * permission of the Apache Group.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Group and was originally based
+ * on public domain software written at the National Center for
+ * Supercomputing Applications, University of Illinois, Urbana-Champaign.
+ * For more information on the Apache Group and the Apache HTTP server
+ * project, please see <http://www.apache.org/>.
+ *
+ */
+
+/******************************************************************************
+ ******************************************************************************
+ * NOTE! This program is not safe as a setuid executable! Do not make it
+ * setuid!
+ ******************************************************************************
+ *****************************************************************************/
+/*
+ * htpasswd.c: simple program for manipulating password file for
+ * the Apache HTTP server
+ *
+ * Originally by Rob McCool
+ *
+ * Exit values:
+ * 0: Success
+ * 1: Failure; file access/permission problem
+ * 2: Failure; command line syntax problem (usage message issued)
+ * 3: Failure; password verification failure
+ * 4: Failure; operation interrupted (such as with CTRL/C)
+ * 5: Failure; buffer would overflow (username, filename, or computed
+ * record too long)
+ * 6: Failure; username contains illegal or reserved characters
+ */
+
+#include "ap_config.h"
+#include <sys/types.h>
+#include <signal.h>
+#include <errno.h>
+#include "ap.h"
+#include "ap_md5.h"
+#include "ap_sha1.h"
+
+#ifdef WIN32
+#include <conio.h>
+#include "../os/win32/getopt.h"
+#define unlink _unlink
+#endif
+
+#ifndef CHARSET_EBCDIC
+#define LF 10
+#define CR 13
+#else /*CHARSET_EBCDIC*/
+#define LF '\n'
+#define CR '\r'
+#endif /*CHARSET_EBCDIC*/
+
+#define MAX_STRING_LEN 256
+#define ALG_PLAIN 0
+#define ALG_CRYPT 1
+#define ALG_APMD5 2
+#define ALG_APSHA 3
+
+#define ERR_FILEPERM 1
+#define ERR_SYNTAX 2
+#define ERR_PWMISMATCH 3
+#define ERR_INTERRUPTED 4
+#define ERR_OVERFLOW 5
+#define ERR_BADUSER 6
+
+/*
+ * This needs to be declared statically so the signal handler can
+ * access it.
+ */
+static char *tempfilename;
+
+/*
+ * Get a line of input from the user, not including any terminating
+ * newline.
+ */
+static int getline(char *s, int n, FILE *f)
+{
+ register int i = 0;
+
+ while (1) {
+ s[i] = (char) fgetc(f);
+
+ if (s[i] == CR) {
+ s[i] = fgetc(f);
+ }
+
+ if ((s[i] == 0x4) || (s[i] == LF) || (i == (n - 1))) {
+ s[i] = '\0';
+ return (feof(f) ? 1 : 0);
+ }
+ ++i;
+ }
+}
+
+static void putline(FILE *f, char *l)
+{
+ int x;
+
+ for (x = 0; l[x]; x++) {
+ fputc(l[x], f);
+ }
+ fputc('\n', f);
+}
+
+/*
+ * Make a password record from the given information. A zero return
+ * indicates success; failure means that the output buffer contains an
+ * error message instead.
+ */
+static int mkrecord(char *user, char *record, size_t rlen, char *passwd,
+ int alg)
+{
+ char *pw;
+ char cpw[120];
+ char pwin[MAX_STRING_LEN];
+ char pwv[MAX_STRING_LEN];
+ char salt[9];
+
+ if (passwd != NULL) {
+ pw = passwd;
+ }
+ else {
+ if (ap_getpass("New password: ", pwin, sizeof(pwin)) != 0) {
+ ap_snprintf(record, (rlen - 1), "password too long (>%d)",
+ sizeof(pwin) - 1);
+ return ERR_OVERFLOW;
+ }
+ ap_getpass("Re-type new password: ", pwv, sizeof(pwv));
+ if (strcmp(pwin, pwv) != 0) {
+ ap_cpystrn(record, "password verification error", (rlen - 1));
+ return ERR_PWMISMATCH;
+ }
+ pw = pwin;
+ memset(pwv, '\0', sizeof(pwin));
+ }
+ switch (alg) {
+
+ case ALG_APSHA:
+ /* XXX cpw >= 28 + strlen(sha1) chars - fixed len SHA */
+ ap_sha1_base64(pw,strlen(pw),cpw);
+ break;
+
+ case ALG_APMD5:
+ (void) srand((int) time((time_t *) NULL));
+ ap_to64(&salt[0], rand(), 8);
+ salt[8] = '\0';
+
+ ap_MD5Encode((const unsigned char *)pw, (const unsigned char *)salt,
+ cpw, sizeof(cpw));
+ break;
+
+ case ALG_PLAIN:
+ /* XXX this len limitation is not in sync with any HTTPd len. */
+ ap_cpystrn(cpw,pw,sizeof(cpw));
+ break;
+
+ case ALG_CRYPT:
+ default:
+ (void) srand((int) time((time_t *) NULL));
+ ap_to64(&salt[0], rand(), 8);
+ salt[8] = '\0';
+
+ ap_cpystrn(cpw, (char *)crypt(pw, salt), sizeof(cpw) - 1);
+ break;
+ }
+ memset(pw, '\0', strlen(pw));
+
+ /*
+ * Check to see if the buffer is large enough to hold the username,
+ * hash, and delimiters.
+ */
+ if ((strlen(user) + 1 + strlen(cpw)) > (rlen - 1)) {
+ ap_cpystrn(record, "resultant record too long", (rlen - 1));
+ return ERR_OVERFLOW;
+ }
+ strcpy(record, user);
+ strcat(record, ":");
+ strcat(record, cpw);
+ return 0;
+}
+
+static int usage(void)
+{
+ fprintf(stderr, "Usage:\n");
+ fprintf(stderr, "\thtpasswd [-cmdps] passwordfile username\n");
+ fprintf(stderr, "\thtpasswd -b[cmdps] passwordfile username password\n\n");
+ fprintf(stderr, " -c Create a new file.\n");
+ fprintf(stderr, " -m Force MD5 encryption of the password"
+#if defined(WIN32) || defined(TPF)
+ " (default)"
+#endif
+ ".\n");
+ fprintf(stderr, " -d Force CRYPT encryption of the password"
+#if (!(defined(WIN32) || defined(TPF)))
+ " (default)"
+#endif
+ ".\n");
+ fprintf(stderr, " -p Do not encrypt the password (plaintext).\n");
+ fprintf(stderr, " -s Force SHA encryption of the password.\n");
+ fprintf(stderr, " -b Use the password from the command line rather "
+ "than prompting for it.\n");
+ fprintf(stderr,
+ "On Windows and TPF systems the '-m' flag is used by default.\n");
+ fprintf(stderr,
+ "On all other systems, the '-p' flag will probably not work.\n");
+ return ERR_SYNTAX;
+}
+
+static void interrupted(void)
+{
+ fprintf(stderr, "Interrupted.\n");
+ if (tempfilename != NULL) {
+ unlink(tempfilename);
+ }
+ exit(ERR_INTERRUPTED);
+}
+
+/*
+ * Check to see if the specified file can be opened for the given
+ * access.
+ */
+static int accessible(char *fname, char *mode)
+{
+ FILE *s;
+
+ s = fopen(fname, mode);
+ if (s == NULL) {
+ return 0;
+ }
+ fclose(s);
+ return 1;
+}
+
+/*
+ * Return true if a file is readable.
+ */
+static int readable(char *fname)
+{
+ return accessible(fname, "r");
+}
+
+/*
+ * Return true if the specified file can be opened for write access.
+ */
+static int writable(char *fname)
+{
+ return accessible(fname, "a");
+}
+
+/*
+ * Return true if the named file exists, regardless of permissions.
+ */
+static int exists(char *fname)
+{
+#ifdef WIN32
+ struct _stat sbuf;
+#else
+ struct stat sbuf;
+#endif
+ int check;
+
+#ifdef WIN32
+ check = _stat(fname, &sbuf);
+#else
+ check = stat(fname, &sbuf);
+#endif
+ return ((check == -1) && (errno == ENOENT)) ? 0 : 1;
+}
+
+/*
+ * Copy from the current position of one file to the current position
+ * of another.
+ */
+static void copy_file(FILE *target, FILE *source)
+{
+ static char line[MAX_STRING_LEN];
+
+ while (fgets(line, sizeof(line), source) != NULL) {
+ fputs(line, target);
+ }
+}
+
+/*
+ * Let's do it. We end up doing a lot of file opening and closing,
+ * but what do we care? This application isn't run constantly.
+ */
+int main(int argc, char *argv[])
+{
+ FILE *ftemp = NULL;
+ FILE *fpw = NULL;
+ char user[MAX_STRING_LEN];
+ char password[MAX_STRING_LEN];
+ char record[MAX_STRING_LEN];
+ char line[MAX_STRING_LEN];
+ char pwfilename[MAX_STRING_LEN];
+ char *arg;
+ int found = 0;
+ int alg = ALG_CRYPT;
+ int newfile = 0;
+ int noninteractive = 0;
+ int i;
+ int args_left = 2;
+
+ tempfilename = NULL;
+ signal(SIGINT, (void (*)(int)) interrupted);
+
+ /*
+ * Preliminary check to make sure they provided at least
+ * three arguments, we'll do better argument checking as
+ * we parse the command line.
+ */
+ if (argc < 3) {
+ return usage();
+ }
+
+ /*
+ * Go through the argument list and pick out any options. They
+ * have to precede any other arguments.
+ */
+ for (i = 1; i < argc; i++) {
+ arg = argv[i];
+ if (*arg != '-') {
+ break;
+ }
+ while (*++arg != '\0') {
+ if (*arg == 'c') {
+ newfile++;
+ }
+ else if (*arg == 'm') {
+ alg = ALG_APMD5;
+ }
+ else if (*arg == 's') {
+ alg = ALG_APSHA;
+ }
+ else if (*arg == 'p') {
+ alg = ALG_PLAIN;
+ }
+ else if (*arg == 'd') {
+ alg = ALG_CRYPT;
+ }
+ else if (*arg == 'b') {
+ noninteractive++;
+ args_left++;
+ }
+ else {
+ return usage();
+ }
+ }
+ }
+
+ /*
+ * Make sure we still have exactly the right number of arguments left
+ * (the filename, the username, and possibly the password if -b was
+ * specified).
+ */
+ if ((argc - i) != args_left) {
+ return usage();
+ }
+ if (strlen(argv[i]) > (sizeof(pwfilename) - 1)) {
+ fprintf(stderr, "%s: filename too long\n", argv[0]);
+ return ERR_OVERFLOW;
+ }
+ strcpy(pwfilename, argv[i]);
+ if (strlen(argv[i + 1]) > (sizeof(user) - 1)) {
+ fprintf(stderr, "%s: username too long (>%d)\n", argv[0],
+ sizeof(user) - 1);
+ return ERR_OVERFLOW;
+ }
+ strcpy(user, argv[i + 1]);
+ if ((arg = strchr(user, ':')) != NULL) {
+ fprintf(stderr, "%s: username contains illegal character '%c'\n",
+ argv[0], *arg);
+ return ERR_BADUSER;
+ }
+ if (noninteractive) {
+ if (strlen(argv[i + 2]) > (sizeof(password) - 1)) {
+ fprintf(stderr, "%s: password too long (>%d)\n", argv[0],
+ sizeof(password) - 1);
+ return ERR_OVERFLOW;
+ }
+ strcpy(password, argv[i + 2]);
+ }
+
+#ifdef WIN32
+ if (alg == ALG_CRYPT) {
+ alg = ALG_APMD5;
+ fprintf(stderr, "Automatically using MD5 format on Windows.\n");
+ }
+#endif
+
+#if (!(defined(WIN32) || defined(TPF)))
+ if (alg == ALG_PLAIN) {
+ fprintf(stderr,"Warning: storing passwords as plain text might "
+ "just not work on this platform.\n");
+ }
+#endif
+ /*
+ * Verify that the file exists if -c was omitted. We give a special
+ * message if it doesn't.
+ */
+ if ((! newfile) && (! exists(pwfilename))) {
+ fprintf(stderr, "%s: cannot modify file %s; use '-c' to create it\n",
+ argv[0], pwfilename);
+ perror("fopen");
+ exit(ERR_FILEPERM);
+ }
+ /*
+ * Verify that we can read the existing file in the case of an update
+ * to it (rather than creation of a new one).
+ */
+ if ((! newfile) && (! readable(pwfilename))) {
+ fprintf(stderr, "%s: cannot open file %s for read access\n",
+ argv[0], pwfilename);
+ perror("fopen");
+ exit(ERR_FILEPERM);
+ }
+ /*
+ * Now check to see if we can preserve an existing file in case
+ * of password verification errors on a -c operation.
+ */
+ if (newfile && exists(pwfilename) && (! readable(pwfilename))) {
+ fprintf(stderr, "%s: cannot open file %s for read access\n"
+ "%s: existing auth data would be lost on password mismatch",
+ argv[0], pwfilename, argv[0]);
+ perror("fopen");
+ exit(ERR_FILEPERM);
+ }
+ /*
+ * Now verify that the file is writable!
+ */
+ if (! writable(pwfilename)) {
+ fprintf(stderr, "%s: cannot open file %s for write access\n",
+ argv[0], pwfilename);
+ perror("fopen");
+ exit(ERR_FILEPERM);
+ }
+
+ /*
+ * All the file access checks have been made. Time to go to work;
+ * try to create the record for the username in question. If that
+ * fails, there's no need to waste any time on file manipulations.
+ * Any error message text is returned in the record buffer, since
+ * the mkrecord() routine doesn't have access to argv[].
+ */
+ i = mkrecord(user, record, sizeof(record) - 1,
+ noninteractive ? password : NULL,
+ alg);
+ if (i != 0) {
+ fprintf(stderr, "%s: %s\n", argv[0], record);
+ exit(i);
+ }
+
+ /*
+ * We can access the files the right way, and we have a record
+ * to add or update. Let's do it..
+ */
+ tempfilename = tmpnam(NULL);
+ ftemp = fopen(tempfilename, "w+");
+ if (ftemp == NULL) {
+ fprintf(stderr, "%s: unable to create temporary file\n", argv[0]);
+ perror("fopen");
+ exit(ERR_FILEPERM);
+ }
+ /*
+ * If we're not creating a new file, copy records from the existing
+ * one to the temporary file until we find the specified user.
+ */
+ if (! newfile) {
+ char scratch[MAX_STRING_LEN];
+
+ fpw = fopen(pwfilename, "r");
+ while (! (getline(line, sizeof(line), fpw))) {
+ char *colon;
+
+ if ((line[0] == '#') || (line[0] == '\0')) {
+ putline(ftemp, line);
+ continue;
+ }
+ strcpy(scratch, line);
+ /*
+ * See if this is our user.
+ */
+ colon = strchr(scratch, ':');
+ if (colon != NULL) {
+ *colon = '\0';
+ }
+ if (strcmp(user, scratch) != 0) {
+ putline(ftemp, line);
+ continue;
+ }
+ found++;
+ break;
+ }
+ }
+ if (found) {
+ fprintf(stderr, "Updating ");
+ }
+ else {
+ fprintf(stderr, "Adding ");
+ }
+ fprintf(stderr, "password for user %s\n", user);
+ /*
+ * Now add the user record we created.
+ */
+ putline(ftemp, record);
+ /*
+ * If we're updating an existing file, there may be additional
+ * records beyond the one we're updating, so copy them.
+ */
+ if (! newfile) {
+ copy_file(ftemp, fpw);
+ fclose(fpw);
+ }
+ /*
+ * The temporary file now contains the information that should be
+ * in the actual password file. Close the open files, re-open them
+ * in the appropriate mode, and copy them file to the real one.
+ */
+ fclose(ftemp);
+ fpw = fopen(pwfilename, "w+");
+ ftemp = fopen(tempfilename, "r");
+ copy_file(fpw, ftemp);
+ fclose(fpw);
+ fclose(ftemp);
+ unlink(tempfilename);
+ return 0;
+}
--- /dev/null
+#!
+ap_MD5Encode
+ap_MD5Final
+ap_MD5Init
+ap_MD5Update
+ap_SHA1Final
+ap_SHA1Init
+ap_SHA1Update_binary
+ap_SHA1Update
+ap_add_cgi_vars
+ap_add_common_vars
+ap_add_module
+ap_add_named_module
+ap_add_per_dir_conf
+ap_add_per_url_conf
+ap_add_version_component
+ap_allow_options
+ap_allow_overrides
+ap_append_arrays
+ap_array_cat
+ap_array_pstrcat
+ap_auth_name
+ap_auth_type
+ap_base64encode
+ap_base64encode_binary
+ap_base64encode_len
+ap_base64decode
+ap_base64decode_binary
+ap_base64decode_len
+ap_basic_http_header
+ap_bclose
+ap_bcreate
+ap_bfilbuf
+ap_bfileno
+ap_bflsbuf
+ap_bflush
+ap_bgetopt
+ap_bgets
+ap_bhalfduplex
+ap_bind_address
+ap_block_alarms
+ap_blookc
+ap_bnonblock
+ap_bonerror
+ap_bprintf
+ap_bpushfd
+ap_bputs
+ap_bread
+ap_bsetflag
+ap_bsetopt
+ap_bskiplf
+ap_bspawn_child
+ap_bvputs
+ap_bwrite
+ap_bytes_in_free_blocks
+ap_bytes_in_pool
+ap_call_exec
+ap_can_exec
+ap_cfg_closefile
+ap_cfg_getc
+ap_cfg_getline
+ap_chdir_file
+ap_check_access
+ap_check_auth
+ap_check_cmd_context
+ap_check_user_id
+ap_checkmask
+ap_child_exit_modules
+ap_child_init_modules
+ap_child_terminate
+ap_cleanup_for_exec
+ap_clear_module_list
+ap_clear_pool
+ap_clear_table
+ap_close_piped_log
+ap_construct_server
+ap_construct_url
+ap_content_type_tolower
+ap_copy_array
+ap_copy_array_hdr
+ap_copy_table
+ap_core_reorder_directories
+ap_coredump_dir
+ap_count_dirs
+ap_cpystrn
+ap_create_environment
+ap_create_per_dir_config
+ap_create_request_config
+ap_daemons_limit
+ap_daemons_max_free
+ap_daemons_min_free
+ap_daemons_to_start
+ap_day_snames
+ap_default_port_for_request
+ap_default_port_for_scheme
+ap_default_type
+ap_destroy_pool
+ap_destroy_sub_req
+ap_die
+ap_discard_request_body
+ap_document_root
+ap_dummy_mutex
+ap_each_byterange
+ap_error_log2stderr
+ap_escape_html
+ap_escape_path_segment
+ap_escape_quotes
+ap_escape_shell_cmd
+ap_excess_requests_per_child
+ap_exists_config_define
+ap_exists_scoreboard_image
+ap_extended_status
+ap_field_noparam
+ap_finalize_request_protocol
+ap_finalize_sub_req_protocol
+ap_find_command
+ap_find_command_in_modules
+ap_find_last_token
+ap_find_linked_module
+ap_find_list_item
+ap_find_module_name
+ap_find_path_info
+ap_find_token
+ap_find_types
+ap_fini_vhost_config
+ap_fnmatch
+ap_force_library_loading
+ap_get_basic_auth_pw
+ap_get_client_block
+ap_get_gmtoff
+ap_get_list_item
+ap_get_local_host
+ap_get_remote_host
+ap_get_remote_logname
+ap_get_server_built
+ap_get_server_name
+ap_get_server_port
+ap_get_server_version
+ap_get_time
+ap_get_token
+ap_get_virthost_addr
+ap_getparents
+ap_getword
+ap_getword_conf
+ap_getword_conf_nc
+ap_getword_nc
+ap_getword_nulls
+ap_getword_nulls_nc
+ap_getword_white
+ap_getword_white_nc
+ap_gm_timestr_822
+ap_gname2id
+ap_group_id
+ap_handle_command
+ap_hard_timeout
+ap_header_parse
+ap_ht_time
+ap_ind
+ap_index_of_response
+ap_init_alloc
+ap_init_modules
+ap_init_vhost_config
+ap_init_virtual_host
+ap_internal_redirect
+ap_internal_redirect_handler
+ap_invoke_handler
+ap_is_directory
+ap_is_fnmatch
+ap_is_initial_req
+ap_is_matchexp
+ap_is_url
+ap_keepalive_timeout
+ap_kill_cleanup
+ap_kill_cleanups_for_fd
+ap_kill_cleanups_for_socket
+ap_kill_timeout
+ap_limit_section
+ap_listenbacklog
+ap_listeners
+ap_lock_fname
+ap_log_assert
+ap_log_error
+ap_log_error_old
+ap_log_pid
+ap_log_printf
+ap_log_reason
+ap_log_rerror
+ap_log_transaction
+ap_log_unixerr
+ap_make_array
+ap_make_dirstr
+ap_make_dirstr_parent
+ap_make_dirstr_prefix
+ap_make_etag
+ap_make_full_path
+ap_make_sub_pool
+ap_make_table
+ap_matches_request_vhost
+ap_max_requests_per_child
+ap_md5
+ap_md5contextTo64
+ap_md5digest
+ap_meets_conditions
+ap_merge_per_dir_configs
+ap_method_number_of
+ap_month_snames
+ap_my_generation
+ap_no2slash
+ap_note_auth_failure
+ap_note_basic_auth_failure
+ap_note_cleanups_for_fd
+ap_note_cleanups_for_file
+ap_note_cleanups_for_socket
+ap_note_digest_auth_failure
+ap_note_subprocess
+ap_null_cleanup
+ap_open_logs
+ap_open_piped_log
+ap_os_escape_path
+ap_os_is_path_absolute
+ap_overlay_tables
+ap_overlap_tables
+ap_palloc
+ap_parseHTTPdate
+ap_parse_hostinfo_components
+ap_parse_htaccess
+ap_parse_uri
+ap_parse_uri_components
+ap_parse_vhost_addrs
+ap_pbase64decode
+ap_pbase64encode
+ap_pcalloc
+ap_pcfg_open_custom
+ap_pcfg_openfile
+ap_pclosedir
+ap_pclosef
+ap_pclosesocket
+ap_pduphostent
+ap_pfclose
+ap_pfdopen
+ap_pfopen
+ap_pgethostbyname
+ap_pid_fname
+ap_popendir
+ap_popenf
+ap_pregcomp
+ap_pregfree
+ap_pregsub
+ap_prelinked_modules
+ap_preloaded_modules
+ap_process_request
+ap_process_resource_config
+ap_psignature
+ap_psocket
+ap_psprintf
+ap_pstrcat
+ap_pstrdup
+ap_pstrndup
+ap_push_array
+ap_pvsprintf
+ap_rationalize_mtime
+ap_read_config
+ap_read_request
+ap_regerror
+ap_regexec
+ap_register_cleanup
+ap_register_other_child
+ap_remove_module
+ap_requires
+ap_reset_timeout
+ap_response_code_string
+ap_restart_time
+ap_rfc1413
+ap_rfc1413_timeout
+ap_rflush
+ap_rind
+ap_rprintf
+ap_rputc
+ap_rputs
+ap_run_cleanup
+ap_run_fixups
+ap_run_post_read_request
+ap_run_sub_req
+ap_rvputs
+ap_rwrite
+ap_satisfies
+ap_scan_script_header_err
+ap_scan_script_header_err_buff
+ap_scoreboard_fname
+ap_scoreboard_image
+ap_send_error_response
+ap_send_fb
+ap_send_fb_length
+ap_send_fd
+ap_send_fd_length
+ap_send_header_field
+ap_send_http_header
+ap_send_http_options
+ap_send_http_trace
+ap_send_mmap
+ap_send_size
+ap_server_argv0
+ap_server_config_defines
+ap_server_confname
+ap_server_post_read_config
+ap_server_pre_read_config
+ap_server_root
+ap_server_root_relative
+ap_set_byterange
+ap_set_callback_and_alarm
+ap_set_content_length
+ap_set_etag
+ap_set_file_slot
+ap_set_flag_slot
+ap_set_keepalive
+ap_set_last_modified
+ap_set_name_virtual_host
+ap_set_string_slot
+ap_set_string_slot_lower
+ap_set_sub_req_protocol
+ap_setup_client_block
+ap_setup_prelinked_modules
+ap_sha1_base64
+ap_should_client_block
+ap_show_directives
+ap_show_modules
+ap_signal
+ap_single_module_configure
+ap_size_list_item
+ap_slack
+ap_snprintf
+ap_soft_timeout
+ap_some_auth_required
+ap_spawn_child
+ap_srm_command_loop
+ap_standalone
+ap_start_restart
+ap_start_shutdown
+ap_str_tolower
+ap_strcasecmp_match
+ap_strcmp_match
+ap_sub_req_lookup_file
+ap_sub_req_lookup_uri
+ap_sub_req_method_uri
+ap_suexec_enabled
+ap_sync_scoreboard_image
+ap_table_add
+ap_table_addn
+ap_table_do
+ap_table_get
+ap_table_merge
+ap_table_mergen
+ap_table_set
+ap_table_setn
+ap_table_unset
+ap_threads_per_child
+ap_tm2sec
+ap_translate_name
+ap_uname2id
+ap_unblock_alarms
+ap_unescape_url
+ap_unparse_uri_components
+ap_unregister_other_child
+ap_update_child_status
+ap_update_mtime
+ap_update_vhost_from_headers
+ap_update_vhost_given_ip
+ap_user_id
+ap_user_name
+ap_util_init
+ap_util_uri_init
+ap_uudecode
+ap_validate_password
+ap_vbprintf
+ap_vformatter
+ap_vsnprintf
+core_module
+top_module
+XML_DefaultCurrent
+XML_ErrorString
+XML_ExternalEntityParserCreate
+XML_GetBase
+XML_GetBuffer
+XML_GetCurrentByteCount
+XML_GetCurrentByteIndex
+XML_GetCurrentColumnNumber
+XML_GetCurrentLineNumber
+XML_GetErrorCode
+XML_GetSpecifiedAttributeCount
+XML_Parse
+XML_ParseBuffer
+XML_ParserCreate
+XML_ParserCreateNS
+XML_ParserFree
+XML_SetBase
+XML_SetCdataSectionHandler
+XML_SetCharacterDataHandler
+XML_SetCommentHandler
+XML_SetDefaultHandler
+XML_SetDefaultHandlerExpand
+XML_SetElementHandler
+XML_SetEncoding
+XML_SetExternalEntityRefHandler
+XML_SetExternalEntityRefHandlerArg
+XML_SetNamespaceDeclHandler
+XML_SetNotStandaloneHandler
+XML_SetNotationDeclHandler
+XML_SetProcessingInstructionHandler
+XML_SetUnknownEncodingHandler
+XML_SetUnparsedEntityDeclHandler
+XML_SetUserData
+XML_UseParserAsHandlerArg
--- /dev/null
+#!/usr/local/bin/perl
+
+# ====================================================================
+# Copyright (c) 1995-1999 The Apache Group. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+#
+# 3. All advertising materials mentioning features or use of this
+# software must display the following acknowledgment:
+# "This product includes software developed by the Apache Group
+# for use in the Apache HTTP server project (http://www.apache.org/)."
+#
+# 4. The names "Apache Server" and "Apache Group" must not be used to
+# endorse or promote products derived from this software without
+# prior written permission. For written permission, please contact
+# apache@apache.org.
+#
+# 5. Products derived from this software may not be called "Apache"
+# nor may "Apache" appear in their names without prior written
+# permission of the Apache Group.
+#
+# 6. Redistributions of any form whatsoever must retain the following
+# acknowledgment:
+# "This product includes software developed by the Apache Group
+# for use in the Apache HTTP server project (http://www.apache.org/)."
+#
+# THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+# EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
+# ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+# ====================================================================
+#
+# This software consists of voluntary contributions made by many
+# individuals on behalf of the Apache Group and was originally based
+# on public domain software written at the National Center for
+# Supercomputing Applications, University of Illinois, Urbana-Champaign.
+# For more information on the Apache Group and the Apache HTTP server
+# project, please see <http://www.apache.org/>.
+
+
+# Log Server Status
+# Mark J Cox, UK Web Ltd 1996, mark@ukweb.com
+#
+# This script is designed to be run at a frequent interval by something
+# like cron. It connects to the server and downloads the status
+# information. It reformats the information to a single line and logs
+# it to a file. Make sure the directory $wherelog is writable by the
+# user who runs this script.
+#
+require 'sys/socket.ph';
+
+$wherelog = "/var/log/graph/"; # Logs will be like "/var/log/graph/19960312"
+$server = "localhost"; # Name of server, could be "www.foo.com"
+$port = "80"; # Port on server
+$request = "/status/?auto"; # Request to send
+
+sub tcp_connect
+{
+ local($host,$port) =@_;
+ $sockaddr='S n a4 x8';
+ chop($hostname=`hostname`);
+ $port=(getservbyname($port, 'tcp'))[2] unless $port =~ /^\d+$/;
+ $me=pack($sockaddr,&AF_INET,0,(gethostbyname($hostname))[4]);
+ $them=pack($sockaddr,&AF_INET,$port,(gethostbyname($host))[4]);
+ socket(S,&PF_INET,&SOCK_STREAM,(getprotobyname('tcp'))[2]) ||
+ die "socket: $!";
+ bind(S,$me) || return "bind: $!";
+ connect(S,$them) || return "connect: $!";
+ select(S);
+ $| = 1;
+ select(stdout);
+ return "";
+}
+
+### Main
+
+{
+ $year=`date +%y`;
+ chomp($year);
+ $year += ($year < 70) ? 2000 : 1900;
+ $date = $year . `date +%m%d:%H%M%S`;
+ chomp($date);
+ ($day,$time)=split(/:/,$date);
+ $res=&tcp_connect($server,$port);
+ open(OUT,">>$wherelog$day");
+ if ($res) {
+ print OUT "$time:-1:-1:-1:-1:$res\n";
+ exit 1;
+ }
+ print S "GET $request\n";
+ while (<S>) {
+ $requests=$1 if ( m|^BusyServers:\ (\S+)|);
+ $idle=$1 if ( m|^IdleServers:\ (\S+)|);
+ $number=$1 if ( m|sses:\ (\S+)|);
+ $cpu=$1 if (m|^CPULoad:\ (\S+)|);
+ }
+ print OUT "$time:$requests:$idle:$number:$cpu\n";
+}
+
+
--- /dev/null
+/*
+ * logresolve 1.1
+ *
+ * Tom Rathborne - tomr@uunet.ca - http://www.uunet.ca/~tomr/
+ * UUNET Canada, April 16, 1995
+ *
+ * Rewritten by David Robinson. (drtr@ast.cam.ac.uk)
+ *
+ * Usage: logresolve [-s filename] [-c] < access_log > new_log
+ *
+ * Arguments:
+ * -s filename name of a file to record statistics
+ * -c check the DNS for a matching A record for the host.
+ *
+ * Notes:
+ *
+ * To generate meaningful statistics from an HTTPD log file, it's good
+ * to have the domain name of each machine that accessed your site, but
+ * doing this on the fly can slow HTTPD down.
+ *
+ * Compiling NCSA HTTPD with the -DMINIMAL_DNS flag turns IP#->hostname
+ * resolution off. Before running your stats program, just run your log
+ * file through this program (logresolve) and all of your IP numbers will
+ * be resolved into hostnames (where possible).
+ *
+ * logresolve takes an HTTPD access log (in the COMMON log file format,
+ * or any other format that has the IP number/domain name as the first
+ * field for that matter), and outputs the same file with all of the
+ * domain names looked up. Where no domain name can be found, the IP
+ * number is left in.
+ *
+ * To minimize impact on your nameserver, logresolve has its very own
+ * internal hash-table cache. This means that each IP number will only
+ * be looked up the first time it is found in the log file.
+ *
+ * The -c option causes logresolve to apply the same check as httpd
+ * compiled with -DMAXIMUM_DNS; after finding the hostname from the IP
+ * address, it looks up the IP addresses for the hostname and checks
+ * that one of these matches the original address.
+ */
+
+#include "ap_config.h"
+#include <sys/types.h>
+
+#include <ctype.h>
+
+#ifndef MPE
+#include <arpa/inet.h>
+#endif
+
+static void cgethost(struct in_addr ipnum, char *string, int check);
+static int getline(char *s, int n);
+static void stats(FILE *output);
+
+
+/* maximum line length */
+#define MAXLINE 1024
+
+/* maximum length of a domain name */
+#ifndef MAXDNAME
+#define MAXDNAME 256
+#endif
+
+/* number of buckets in cache hash table */
+#define BUCKETS 256
+
+#if defined(NEED_STRDUP)
+char *strdup (const char *str)
+{
+ char *dup;
+
+ if (!(dup = (char *) malloc(strlen(str) + 1)))
+ return NULL;
+ dup = strcpy(dup, str);
+
+ return dup;
+}
+#endif
+
+/*
+ * struct nsrec - record of nameservice for cache linked list
+ *
+ * ipnum - IP number hostname - hostname noname - nonzero if IP number has no
+ * hostname, i.e. hostname=IP number
+ */
+
+struct nsrec {
+ struct in_addr ipnum;
+ char *hostname;
+ int noname;
+ struct nsrec *next;
+} *nscache[BUCKETS];
+
+/*
+ * statistics - obvious
+ */
+
+#ifndef h_errno
+extern int h_errno; /* some machines don't have this in their headers */
+#endif
+
+/* largeste value for h_errno */
+#define MAX_ERR (NO_ADDRESS)
+#define UNKNOWN_ERR (MAX_ERR+1)
+#define NO_REVERSE (MAX_ERR+2)
+
+static int cachehits = 0;
+static int cachesize = 0;
+static int entries = 0;
+static int resolves = 0;
+static int withname = 0;
+static int errors[MAX_ERR + 3];
+
+/*
+ * cgethost - gets hostname by IP address, caching, and adding unresolvable
+ * IP numbers with their IP number as hostname, setting noname flag
+ */
+
+static void cgethost (struct in_addr ipnum, char *string, int check)
+{
+ struct nsrec **current, *new;
+ struct hostent *hostdata;
+ char *name;
+
+ current = &nscache[((ipnum.s_addr + (ipnum.s_addr >> 8) +
+ (ipnum.s_addr >> 16) + (ipnum.s_addr >> 24)) % BUCKETS)];
+
+ while (*current != NULL && ipnum.s_addr != (*current)->ipnum.s_addr)
+ current = &(*current)->next;
+
+ if (*current == NULL) {
+ cachesize++;
+ new = (struct nsrec *) malloc(sizeof(struct nsrec));
+ if (new == NULL) {
+ perror("malloc");
+ fprintf(stderr, "Insufficient memory\n");
+ exit(1);
+ }
+ *current = new;
+ new->next = NULL;
+
+ new->ipnum = ipnum;
+
+ hostdata = gethostbyaddr((const char *) &ipnum, sizeof(struct in_addr),
+ AF_INET);
+ if (hostdata == NULL) {
+ if (h_errno > MAX_ERR)
+ errors[UNKNOWN_ERR]++;
+ else
+ errors[h_errno]++;
+ new->noname = h_errno;
+ name = strdup(inet_ntoa(ipnum));
+ }
+ else {
+ new->noname = 0;
+ name = strdup(hostdata->h_name);
+ if (check) {
+ if (name == NULL) {
+ perror("strdup");
+ fprintf(stderr, "Insufficient memory\n");
+ exit(1);
+ }
+ hostdata = gethostbyname(name);
+ if (hostdata != NULL) {
+ char **hptr;
+
+ for (hptr = hostdata->h_addr_list; *hptr != NULL; hptr++)
+ if (((struct in_addr *) (*hptr))->s_addr == ipnum.s_addr)
+ break;
+ if (*hptr == NULL)
+ hostdata = NULL;
+ }
+ if (hostdata == NULL) {
+ fprintf(stderr, "Bad host: %s != %s\n", name,
+ inet_ntoa(ipnum));
+ new->noname = NO_REVERSE;
+ free(name);
+ name = strdup(inet_ntoa(ipnum));
+ errors[NO_REVERSE]++;
+ }
+ }
+ }
+ new->hostname = name;
+ if (new->hostname == NULL) {
+ perror("strdup");
+ fprintf(stderr, "Insufficient memory\n");
+ exit(1);
+ }
+ }
+ else
+ cachehits++;
+
+ /* size of string == MAXDNAME +1 */
+ strncpy(string, (*current)->hostname, MAXDNAME);
+ string[MAXDNAME] = '\0';
+}
+
+/*
+ * prints various statistics to output
+ */
+
+static void stats (FILE *output)
+{
+ int i;
+ char *ipstring;
+ struct nsrec *current;
+ char *errstring[MAX_ERR + 3];
+
+ for (i = 0; i < MAX_ERR + 3; i++)
+ errstring[i] = "Unknown error";
+ errstring[HOST_NOT_FOUND] = "Host not found";
+ errstring[TRY_AGAIN] = "Try again";
+ errstring[NO_RECOVERY] = "Non recoverable error";
+ errstring[NO_DATA] = "No data record";
+ errstring[NO_ADDRESS] = "No address";
+ errstring[NO_REVERSE] = "No reverse entry";
+
+ fprintf(output, "logresolve Statistics:\n");
+
+ fprintf(output, "Entries: %d\n", entries);
+ fprintf(output, " With name : %d\n", withname);
+ fprintf(output, " Resolves : %d\n", resolves);
+ if (errors[HOST_NOT_FOUND])
+ fprintf(output, " - Not found : %d\n", errors[HOST_NOT_FOUND]);
+ if (errors[TRY_AGAIN])
+ fprintf(output, " - Try again : %d\n", errors[TRY_AGAIN]);
+ if (errors[NO_DATA])
+ fprintf(output, " - No data : %d\n", errors[NO_DATA]);
+ if (errors[NO_ADDRESS])
+ fprintf(output, " - No address: %d\n", errors[NO_ADDRESS]);
+ if (errors[NO_REVERSE])
+ fprintf(output, " - No reverse: %d\n", errors[NO_REVERSE]);
+ fprintf(output, "Cache hits : %d\n", cachehits);
+ fprintf(output, "Cache size : %d\n", cachesize);
+ fprintf(output, "Cache buckets : IP number * hostname\n");
+
+ for (i = 0; i < BUCKETS; i++)
+ for (current = nscache[i]; current != NULL; current = current->next) {
+ ipstring = inet_ntoa(current->ipnum);
+ if (current->noname == 0)
+ fprintf(output, " %3d %15s - %s\n", i, ipstring,
+ current->hostname);
+ else {
+ if (current->noname > MAX_ERR + 2)
+ fprintf(output, " %3d %15s : Unknown error\n", i,
+ ipstring);
+ else
+ fprintf(output, " %3d %15s : %s\n", i, ipstring,
+ errstring[current->noname]);
+ }
+ }
+}
+
+
+/*
+ * gets a line from stdin
+ */
+
+static int getline (char *s, int n)
+{
+ char *cp;
+
+ if (!fgets(s, n, stdin))
+ return (0);
+ cp = strchr(s, '\n');
+ if (cp)
+ *cp = '\0';
+ return (1);
+}
+
+int main (int argc, char *argv[])
+{
+ struct in_addr ipnum;
+ char *bar, hoststring[MAXDNAME + 1], line[MAXLINE], *statfile;
+ int i, check;
+
+ check = 0;
+ statfile = NULL;
+ for (i = 1; i < argc; i++) {
+ if (strcmp(argv[i], "-c") == 0)
+ check = 1;
+ else if (strcmp(argv[i], "-s") == 0) {
+ if (i == argc - 1) {
+ fprintf(stderr, "logresolve: missing filename to -s\n");
+ exit(1);
+ }
+ i++;
+ statfile = argv[i];
+ }
+ else {
+ fprintf(stderr, "Usage: logresolve [-s statfile] [-c] < input > output\n");
+ exit(0);
+ }
+ }
+
+
+ for (i = 0; i < BUCKETS; i++)
+ nscache[i] = NULL;
+ for (i = 0; i < MAX_ERR + 2; i++)
+ errors[i] = 0;
+
+ while (getline(line, MAXLINE)) {
+ if (line[0] == '\0')
+ continue;
+ entries++;
+ if (!isdigit(line[0])) { /* short cut */
+ puts(line);
+ withname++;
+ continue;
+ }
+ bar = strchr(line, ' ');
+ if (bar != NULL)
+ *bar = '\0';
+ ipnum.s_addr = inet_addr(line);
+ if (ipnum.s_addr == 0xffffffffu) {
+ if (bar != NULL)
+ *bar = ' ';
+ puts(line);
+ withname++;
+ continue;
+ }
+
+ resolves++;
+
+ cgethost(ipnum, hoststring, check);
+ if (bar != NULL)
+ printf("%s %s\n", hoststring, bar + 1);
+ else
+ puts(hoststring);
+ }
+
+ if (statfile != NULL) {
+ FILE *fp;
+ fp = fopen(statfile, "w");
+ if (fp == NULL) {
+ fprintf(stderr, "logresolve: could not open statistics file '%s'\n"
+ ,statfile);
+ exit(1);
+ }
+ stats(fp);
+ fclose(fp);
+ }
+
+ return (0);
+}
--- /dev/null
+#!/usr/local/bin/perl
+
+# ====================================================================
+# Copyright (c) 1995-1999 The Apache Group. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+#
+# 3. All advertising materials mentioning features or use of this
+# software must display the following acknowledgment:
+# "This product includes software developed by the Apache Group
+# for use in the Apache HTTP server project (http://www.apache.org/)."
+#
+# 4. The names "Apache Server" and "Apache Group" must not be used to
+# endorse or promote products derived from this software without
+# prior written permission. For written permission, please contact
+# apache@apache.org.
+#
+# 5. Products derived from this software may not be called "Apache"
+# nor may "Apache" appear in their names without prior written
+# permission of the Apache Group.
+#
+# 6. Redistributions of any form whatsoever must retain the following
+# acknowledgment:
+# "This product includes software developed by the Apache Group
+# for use in the Apache HTTP server project (http://www.apache.org/)."
+#
+# THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+# EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
+# ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+# ====================================================================
+#
+# This software consists of voluntary contributions made by many
+# individuals on behalf of the Apache Group and was originally based
+# on public domain software written at the National Center for
+# Supercomputing Applications, University of Illinois, Urbana-Champaign.
+# For more information on the Apache Group and the Apache HTTP server
+# project, please see <http://www.apache.org/>.
+
+# logresolve.pl
+#
+# v 1.2 by robh @ imdb.com
+#
+# usage: logresolve.pl <infile >outfile
+#
+# input = Apache/NCSA/.. logfile with IP numbers at start of lines
+# output = same logfile with IP addresses resolved to hostnames where
+# name lookups succeeded.
+#
+# this differs from the C based 'logresolve' in that this script
+# spawns a number ($CHILDREN) of subprocesses to resolve addresses
+# concurrently and sets a short timeout ($TIMEOUT) for each lookup in
+# order to keep things moving quickly.
+#
+# the parent process handles caching of IP->hostnames using a Perl hash
+# it also avoids sending the same IP to multiple child processes to be
+# resolved multiple times concurrently.
+#
+# Depending on the settings of $CHILDREN and $TIMEOUT you should see
+# significant reductions in the overall time taken to resolve your
+# logfiles. With $CHILDREN=40 and $TIMEOUT=5 I've seen 200,000 - 300,000
+# logfile lines processed per hour compared to ~45,000 per hour
+# with 'logresolve'.
+#
+# I haven't yet seen any noticable reduction in the percentage of IPs
+# that fail to get resolved. Your mileage will no doubt vary. 5s is long
+# enough to wait IMO.
+#
+# Known to work with FreeBSD 2.2
+# Known to have problems with Solaris
+#
+# 980417 - use 'sockaddr_un' for bind/connect to make the script work
+# with linux. Fix from Luuk de Boer <luuk_de_boer@pi.net>
+
+require 5.004;
+
+$|=1;
+
+use FileHandle;
+use Socket;
+
+use strict;
+no strict 'refs';
+
+use vars qw($PROTOCOL);
+$PROTOCOL = 0;
+
+my $CHILDREN = 40;
+my $TIMEOUT = 5;
+
+my $filename;
+my %hash = ();
+my $parent = $$;
+
+my @children = ();
+for (my $child = 1; $child <=$CHILDREN; $child++) {
+ my $f = fork();
+ if (!$f) {
+ $filename = "./.socket.$parent.$child";
+ if (-e $filename) { unlink($filename) || warn "$filename .. $!\n";}
+ &child($child);
+ exit(0);
+ }
+ push(@children, $f);
+}
+
+&parent;
+&cleanup;
+
+## remove all temporary files before shutting down
+sub cleanup {
+ # die kiddies, die
+ kill(15, @children);
+ for (my $child = 1; $child <=$CHILDREN; $child++) {
+ if (-e "./.socket.$parent.$child") {
+ unlink("./.socket.$parent.$child")
+ || warn ".socket.$parent.$child $!";
+ }
+ }
+}
+
+sub parent {
+ # Trap some possible signals to trigger temp file cleanup
+ $SIG{'KILL'} = $SIG{'INT'} = $SIG{'PIPE'} = \&cleanup;
+
+ my %CHILDSOCK;
+ my $filename;
+
+ ## fork child processes. Each child will create a socket connection
+ ## to this parent and use an unique temp filename to do so.
+ for (my $child = 1; $child <=$CHILDREN; $child++) {
+ $CHILDSOCK{$child}= FileHandle->new;
+
+ if (!socket($CHILDSOCK{$child}, AF_UNIX, SOCK_STREAM, $PROTOCOL)) {
+ warn "parent socket to child failed $!";
+ }
+ $filename = "./.socket.$parent.$child";
+ my $response;
+ do {
+ $response = connect($CHILDSOCK{$child}, sockaddr_un($filename));
+ if ($response != 1) {
+ sleep(1);
+ }
+ } while ($response != 1);
+ $CHILDSOCK{$child}->autoflush;
+ }
+ ## All child processes should now be ready or at worst warming up
+
+ my (@buffer, $child, $ip, $rest, $hostname, $response);
+ ## read the logfile lines from STDIN
+ while(<STDIN>) {
+ @buffer = (); # empty the logfile line buffer array.
+ $child = 1; # children are numbered 1..N, start with #1
+
+ # while we have a child to talk to and data to give it..
+ do {
+ push(@buffer, $_); # buffer the line
+ ($ip, $rest) = split(/ /, $_, 2); # separate IP form rest
+
+ unless ($hash{$ip}) { # resolve if unseen IP
+ $CHILDSOCK{$child}->print("$ip\n"); # pass IP to next child
+ $hash{$ip} = $ip; # don't look it up again.
+ $child++;
+ }
+ } while (($child < ($CHILDREN-1)) and ($_ = <STDIN>));
+
+ ## now poll each child for a response
+ while (--$child > 0) {
+ $response = $CHILDSOCK{$child}->getline;
+ chomp($response);
+ # child sends us back both the IP and HOSTNAME, no need for us
+ # to remember what child received any given IP, and no worries
+ # what order we talk to the children
+ ($ip, $hostname) = split(/\|/, $response, 2);
+ $hash{$ip} = $hostname;
+ }
+
+ # resolve all the logfiles lines held in the log buffer array..
+ for (my $line = 0; $line <=$#buffer; $line++) {
+ # get next buffered line
+ ($ip, $rest) = split(/ /, $buffer[$line], 2);
+ # separate IP from rest and replace with cached hostname
+ printf STDOUT ("%s %s", $hash{$ip}, $rest);
+ }
+ }
+}
+
+########################################
+
+sub child {
+ # arg = numeric ID - how the parent refers to me
+ my $me = shift;
+
+ # add trap for alarm signals.
+ $SIG{'ALRM'} = sub { die "alarmed"; };
+
+ # create a socket to communicate with parent
+ socket(INBOUND, AF_UNIX, SOCK_STREAM, $PROTOCOL)
+ || die "Error with Socket: !$\n";
+ $filename = "./.socket.$parent.$me";
+ bind(INBOUND, sockaddr_un($filename))
+ || die "Error Binding $filename: $!\n";
+ listen(INBOUND, 5) || die "Error Listening: $!\n";
+
+ my ($ip, $send_back);
+ my $talk = FileHandle->new;
+
+ # accept a connection from the parent process. We only ever have
+ # have one connection where we exchange 1 line of info with the
+ # parent.. 1 line in (IP address), 1 line out (IP + hostname).
+ accept($talk, INBOUND) || die "Error Accepting: $!\n";
+ # disable I/O buffering just in case
+ $talk->autoflush;
+ # while the parent keeps sending data, we keep responding..
+ while(($ip = $talk->getline)) {
+ chomp($ip);
+ # resolve the IP if time permits and send back what we found..
+ $send_back = sprintf("%s|%s", $ip, &nslookup($ip));
+ $talk->print($send_back."\n");
+ }
+}
+
+# perform a time restricted hostname lookup.
+sub nslookup {
+ # get the IP as an arg
+ my $ip = shift;
+ my $hostname = undef;
+
+ # do the hostname lookup inside an eval. The eval will use the
+ # already configured SIGnal handler and drop out of the {} block
+ # regardless of whether the alarm occured or not.
+ eval {
+ alarm($TIMEOUT);
+ $hostname = gethostbyaddr(gethostbyname($ip), AF_INET);
+ alarm(0);
+ };
+ if ($@ =~ /alarm/) {
+ # useful for debugging perhaps..
+ # print "alarming, isn't it? ($ip)";
+ }
+
+ # return the hostname or the IP address itself if there is no hostname
+ $hostname ne "" ? $hostname : $ip;
+}
+
+
--- /dev/null
+#!/usr/local/bin/perl
+
+# This script can be used to detect people trying to abuse the security hole which
+# existed in A CGI script direstributed with Apache 1.0.3 and earlier versions.
+# You can redirect them to here using the "<Location /cgi-bin/phf*>" suggestion in
+# httpd.conf.
+#
+# The format logged to is "[date] remote_addr remote_host [date] referrer user_agent".
+
+$LOG = "/var/log/phf_log";
+
+require "ctime.pl";
+$when = &ctime(time);
+$when =~ s/\n//go;
+$ENV{HTTP_USER_AGENT} .= " via $ENV{HTTP_VIA}" if($ENV{HTTP_VIA});
+
+open(LOG, ">>$LOG") || die "boo hoo, phf_log $!";
+print LOG "[$when] $ENV{REMOTE_ADDR} $ENV{REMOTE_HOST} $ENV{$HTTP_REFERER} $ENV{HTTP_USER_AGENT}\n";
+close(LOG);
+
+print "Content-type: text/html\r\n\r\n<BLINK>Smile, you're on Candid Camera.</BLINK>\n";
--- /dev/null
+/*
+ * Simple program to rotate Apache logs without having to kill the server.
+ *
+ * Contributed by Ben Laurie <ben@algroup.co.uk>
+ *
+ * 12 Mar 1996
+ */
+
+
+#define BUFSIZE 65536
+#define MAX_PATH 1024
+
+#include "ap_config.h"
+#include <time.h>
+#include <errno.h>
+#include <fcntl.h>
+
+int main (int argc, char **argv)
+{
+ char buf[BUFSIZE], buf2[MAX_PATH];
+ time_t tLogEnd = 0;
+ time_t tRotation;
+ int nLogFD = -1;
+ int nRead;
+ char *szLogRoot;
+
+ if (argc != 3) {
+ fprintf(stderr,
+ "%s <logfile> <rotation time in seconds>\n\n",
+ argv[0]);
+#ifdef OS2
+ fprintf(stderr,
+ "Add this:\n\nTransferLog \"|%s.exe /some/where 86400\"\n\n",
+ argv[0]);
+#else
+ fprintf(stderr,
+ "Add this:\n\nTransferLog \"|%s /some/where 86400\"\n\n",
+ argv[0]);
+#endif
+ fprintf(stderr,
+ "to httpd.conf. The generated name will be /some/where.nnnn "
+ "where nnnn is the\nsystem time at which the log nominally "
+ "starts (N.B. this time will always be a\nmultiple of the "
+ "rotation time, so you can synchronize cron scripts with it).\n"
+ "At the end of each rotation time a new log is started.\n");
+ exit(1);
+ }
+
+ szLogRoot = argv[1];
+ tRotation = atoi(argv[2]);
+ if (tRotation <= 0) {
+ fprintf(stderr, "Rotation time must be > 0\n");
+ exit(6);
+ }
+
+ for (;;) {
+ nRead = read(0, buf, sizeof buf);
+ if (nRead == 0)
+ exit(3);
+ if (nRead < 0)
+ if (errno != EINTR)
+ exit(4);
+ if (nLogFD >= 0 && (time(NULL) >= tLogEnd || nRead < 0)) {
+ close(nLogFD);
+ nLogFD = -1;
+ }
+ if (nLogFD < 0) {
+ time_t tLogStart = (time(NULL) / tRotation) * tRotation;
+ sprintf(buf2, "%s.%010d", szLogRoot, (int) tLogStart);
+ tLogEnd = tLogStart + tRotation;
+ nLogFD = open(buf2, O_WRONLY | O_CREAT | O_APPEND, 0666);
+ if (nLogFD < 0) {
+ perror(buf2);
+ exit(2);
+ }
+ }
+ if (write(nLogFD, buf, nRead) != nRead) {
+ perror(buf2);
+ exit(5);
+ }
+ }
+}
--- /dev/null
+#!/usr/local/bin/perl
+#
+# ====================================================================
+# Copyright (c) 1995-1999 The Apache Group. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+#
+# 3. All advertising materials mentioning features or use of this
+# software must display the following acknowledgment:
+# "This product includes software developed by the Apache Group
+# for use in the Apache HTTP server project (http://www.apache.org/)."
+#
+# 4. The names "Apache Server" and "Apache Group" must not be used to
+# endorse or promote products derived from this software without
+# prior written permission. For permission please contact
+# Apache@Apache.Org.
+#
+# 5. Products derived from this software may not be called "Apache"
+# nor may "Apache" appear in their names without prior written
+# permission of the Apache Group.
+#
+# 6. Redistributions of any form whatsoever must retain the following
+# acknowledgment:
+# "This product includes software developed by the Apache Group
+# for use in the Apache HTTP server project (http://www.apache.org/)."
+#
+# THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+# EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
+# ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+# ====================================================================
+#
+# This software consists of voluntary contributions made by many
+# individuals on behalf of the Apache Group and was originally based
+# on public domain software written at the National Center for
+# Supercomputing Applications, University of Illinois, Urbana-Champaign.
+# For more information on the Apache Group and the Apache HTTP server
+# project, please see <http://www.apache.org/>.
+#
+
+#
+# This script will take a combined Web server access
+# log file and break its contents into separate files.
+# It assumes that the first field of each line is the
+# virtual host identity (put there by "%v"), and that
+# the logfiles should be named that+".log" in the current
+# directory.
+#
+# The combined log file is read from stdin. Records read
+# will be appended to any existing log files.
+#
+%is_open = ();
+
+while ($log_line = <STDIN>) {
+ #
+ # Get the first token from the log record; it's the
+ # identity of the virtual host to which the record
+ # applies.
+ #
+ ($vhost) = split (/\s/, $log_line);
+ #
+ # Normalize the virtual host name to all lowercase.
+ # If it's blank, the request was handled by the default
+ # server, so supply a default name. This shouldn't
+ # happen, but caution rocks.
+ #
+ $vhost = lc ($vhost) or "access";
+ #
+ # If the log file for this virtual host isn't opened
+ # yet, do it now.
+ #
+ if (! $is_open{$vhost}) {
+ open $vhost, ">>${vhost}.log"
+ or die ("Can't open ${vhost}.log");
+ $is_open{$vhost} = 1;
+ }
+ #
+ # Strip off the first token (which may be null in the
+ # case of the default server), and write the edited
+ # record to the current log file.
+ #
+ $log_line =~ s/^\S*\s+//;
+ printf $vhost "%s", $log_line;
+}
+exit 0;
--- /dev/null
+/* ====================================================================
+ * Copyright (c) 1995-1999 The Apache Group. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * 4. The names "Apache Server" and "Apache Group" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission. For written permission, please contact
+ * apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ * nor may "Apache" appear in their names without prior written
+ * permission of the Apache Group.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Group and was originally based
+ * on public domain software written at the National Center for
+ * Supercomputing Applications, University of Illinois, Urbana-Champaign.
+ * For more information on the Apache Group and the Apache HTTP server
+ * project, please see <http://www.apache.org/>.
+ *
+ */
+
+/*
+ * suexec.c -- "Wrapper" support program for suEXEC behaviour for Apache
+ *
+ ***********************************************************************
+ *
+ * NOTE! : DO NOT edit this code!!! Unless you know what you are doing,
+ * editing this code might open up your system in unexpected
+ * ways to would-be crackers. Every precaution has been taken
+ * to make this code as safe as possible; alter it at your own
+ * risk.
+ *
+ ***********************************************************************
+ *
+ *
+ */
+
+#include "ap_config.h"
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <stdarg.h>
+
+#include "suexec.h"
+
+/*
+ ***********************************************************************
+ * There is no initgroups() in QNX, so I believe this is safe :-)
+ * Use cc -osuexec -3 -O -mf -DQNX suexec.c to compile.
+ *
+ * May 17, 1997.
+ * Igor N. Kovalenko -- infoh@mail.wplus.net
+ ***********************************************************************
+ */
+
+#if defined(NEED_INITGROUPS)
+int initgroups(const char *name, gid_t basegid)
+{
+/* QNX and MPE do not appear to support supplementary groups. */
+ return 0;
+}
+#endif
+
+#if defined(PATH_MAX)
+#define AP_MAXPATH PATH_MAX
+#elif defined(MAXPATHLEN)
+#define AP_MAXPATH MAXPATHLEN
+#else
+#define AP_MAXPATH 8192
+#endif
+
+#define AP_ENVBUF 256
+
+extern char **environ;
+static FILE *log = NULL;
+
+char *safe_env_lst[] =
+{
+ "AUTH_TYPE",
+ "CONTENT_LENGTH",
+ "CONTENT_TYPE",
+ "DATE_GMT",
+ "DATE_LOCAL",
+ "DOCUMENT_NAME",
+ "DOCUMENT_PATH_INFO",
+ "DOCUMENT_ROOT",
+ "DOCUMENT_URI",
+ "FILEPATH_INFO",
+ "GATEWAY_INTERFACE",
+ "LAST_MODIFIED",
+ "PATH_INFO",
+ "PATH_TRANSLATED",
+ "QUERY_STRING",
+ "QUERY_STRING_UNESCAPED",
+ "REMOTE_ADDR",
+ "REMOTE_HOST",
+ "REMOTE_IDENT",
+ "REMOTE_PORT",
+ "REMOTE_USER",
+ "REDIRECT_QUERY_STRING",
+ "REDIRECT_STATUS",
+ "REDIRECT_URL",
+ "REQUEST_METHOD",
+ "REQUEST_URI",
+ "SCRIPT_FILENAME",
+ "SCRIPT_NAME",
+ "SCRIPT_URI",
+ "SCRIPT_URL",
+ "SERVER_ADMIN",
+ "SERVER_NAME",
+ "SERVER_ADDR",
+ "SERVER_PORT",
+ "SERVER_PROTOCOL",
+ "SERVER_SOFTWARE",
+ "UNIQUE_ID",
+ "USER_NAME",
+ "TZ",
+ NULL
+};
+
+
+static void err_output(const char *fmt, va_list ap)
+{
+#ifdef LOG_EXEC
+ time_t timevar;
+ struct tm *lt;
+
+ if (!log) {
+ if ((log = fopen(LOG_EXEC, "a")) == NULL) {
+ fprintf(stderr, "failed to open log file\n");
+ perror("fopen");
+ exit(1);
+ }
+ }
+
+ time(&timevar);
+ lt = localtime(&timevar);
+
+ fprintf(log, "[%d-%.2d-%.2d %.2d:%.2d:%.2d]: ",
+ lt->tm_year + 1900, lt->tm_mon + 1, lt->tm_mday,
+ lt->tm_hour, lt->tm_min, lt->tm_sec);
+
+ vfprintf(log, fmt, ap);
+
+ fflush(log);
+#endif /* LOG_EXEC */
+ return;
+}
+
+static void log_err(const char *fmt,...)
+{
+#ifdef LOG_EXEC
+ va_list ap;
+
+ va_start(ap, fmt);
+ err_output(fmt, ap);
+ va_end(ap);
+#endif /* LOG_EXEC */
+ return;
+}
+
+static void clean_env(void)
+{
+ char pathbuf[512];
+ char **cleanenv;
+ char **ep;
+ int cidx = 0;
+ int idx;
+
+
+ if ((cleanenv = (char **) calloc(AP_ENVBUF, sizeof(char *))) == NULL) {
+ log_err("failed to malloc memory for environment\n");
+ exit(120);
+ }
+
+ sprintf(pathbuf, "PATH=%s", SAFE_PATH);
+ cleanenv[cidx] = strdup(pathbuf);
+ cidx++;
+
+ for (ep = environ; *ep && cidx < AP_ENVBUF-1; ep++) {
+ if (!strncmp(*ep, "HTTP_", 5)) {
+ cleanenv[cidx] = *ep;
+ cidx++;
+ }
+ else {
+ for (idx = 0; safe_env_lst[idx]; idx++) {
+ if (!strncmp(*ep, safe_env_lst[idx],
+ strlen(safe_env_lst[idx]))) {
+ cleanenv[cidx] = *ep;
+ cidx++;
+ break;
+ }
+ }
+ }
+ }
+
+ cleanenv[cidx] = NULL;
+
+ environ = cleanenv;
+}
+
+int main(int argc, char *argv[])
+{
+ int userdir = 0; /* ~userdir flag */
+ uid_t uid; /* user information */
+ gid_t gid; /* target group placeholder */
+ char *target_uname; /* target user name */
+ char *target_gname; /* target group name */
+ char *target_homedir; /* target home directory */
+ char *actual_uname; /* actual user name */
+ char *actual_gname; /* actual group name */
+ char *prog; /* name of this program */
+ char *cmd; /* command to be executed */
+ char cwd[AP_MAXPATH]; /* current working directory */
+ char dwd[AP_MAXPATH]; /* docroot working directory */
+ struct passwd *pw; /* password entry holder */
+ struct group *gr; /* group entry holder */
+ struct stat dir_info; /* directory info holder */
+ struct stat prg_info; /* program info holder */
+
+ /*
+ * If there are a proper number of arguments, set
+ * all of them to variables. Otherwise, error out.
+ */
+ prog = argv[0];
+ if (argc < 4) {
+ log_err("too few arguments\n");
+ exit(101);
+ }
+ target_uname = argv[1];
+ target_gname = argv[2];
+ cmd = argv[3];
+
+ /*
+ * Check existence/validity of the UID of the user
+ * running this program. Error out if invalid.
+ */
+ uid = getuid();
+ if ((pw = getpwuid(uid)) == NULL) {
+ log_err("invalid uid: (%ld)\n", uid);
+ exit(102);
+ }
+
+ /*
+ * Check to see if the user running this program
+ * is the user allowed to do so as defined in
+ * suexec.h. If not the allowed user, error out.
+ */
+#ifdef _OSD_POSIX
+ /* User name comparisons are case insensitive on BS2000/OSD */
+ if (strcasecmp(HTTPD_USER, pw->pw_name)) {
+ log_err("user mismatch (%s instead of %s)\n", pw->pw_name, HTTPD_USER);
+ exit(103);
+ }
+#else /*_OSD_POSIX*/
+ if (strcmp(HTTPD_USER, pw->pw_name)) {
+ log_err("user mismatch (%s instead of %s)\n", pw->pw_name, HTTPD_USER);
+ exit(103);
+ }
+#endif /*_OSD_POSIX*/
+
+ /*
+ * Check for a leading '/' (absolute path) in the command to be executed,
+ * or attempts to back up out of the current directory,
+ * to protect against attacks. If any are
+ * found, error out. Naughty naughty crackers.
+ */
+ if ((cmd[0] == '/') || (!strncmp(cmd, "../", 3))
+ || (strstr(cmd, "/../") != NULL)) {
+ log_err("invalid command (%s)\n", cmd);
+ exit(104);
+ }
+
+ /*
+ * Check to see if this is a ~userdir request. If
+ * so, set the flag, and remove the '~' from the
+ * target username.
+ */
+ if (!strncmp("~", target_uname, 1)) {
+ target_uname++;
+ userdir = 1;
+ }
+
+ /*
+ * Error out if the target username is invalid.
+ */
+ if ((pw = getpwnam(target_uname)) == NULL) {
+ log_err("invalid target user name: (%s)\n", target_uname);
+ exit(105);
+ }
+
+ /*
+ * Error out if the target group name is invalid.
+ */
+ if (strspn(target_gname, "1234567890") != strlen(target_gname)) {
+ if ((gr = getgrnam(target_gname)) == NULL) {
+ log_err("invalid target group name: (%s)\n", target_gname);
+ exit(106);
+ }
+ gid = gr->gr_gid;
+ actual_gname = strdup(gr->gr_name);
+ }
+ else {
+ gid = atoi(target_gname);
+ actual_gname = strdup(target_gname);
+ }
+
+#ifdef _OSD_POSIX
+ /*
+ * Initialize BS2000 user environment
+ */
+ {
+ pid_t pid;
+ int status;
+
+ switch (pid = ufork(target_uname))
+ {
+ case -1: /* Error */
+ log_err("failed to setup bs2000 environment for user %s: %s\n",
+ target_uname, strerror(errno));
+ exit(150);
+ case 0: /* Child */
+ break;
+ default: /* Father */
+ while (pid != waitpid(pid, &status, 0))
+ ;
+ /* @@@ FIXME: should we deal with STOP signals as well? */
+ if (WIFSIGNALED(status))
+ kill (getpid(), WTERMSIG(status));
+ exit(WEXITSTATUS(status));
+ }
+ }
+#endif /*_OSD_POSIX*/
+
+ /*
+ * Save these for later since initgroups will hose the struct
+ */
+ uid = pw->pw_uid;
+ actual_uname = strdup(pw->pw_name);
+ target_homedir = strdup(pw->pw_dir);
+
+ /*
+ * Log the transaction here to be sure we have an open log
+ * before we setuid().
+ */
+ log_err("uid: (%s/%s) gid: (%s/%s) cmd: %s\n",
+ target_uname, actual_uname,
+ target_gname, actual_gname,
+ cmd);
+
+ /*
+ * Error out if attempt is made to execute as root or as
+ * a UID less than UID_MIN. Tsk tsk.
+ */
+ if ((uid == 0) || (uid < UID_MIN)) {
+ log_err("cannot run as forbidden uid (%d/%s)\n", uid, cmd);
+ exit(107);
+ }
+
+ /*
+ * Error out if attempt is made to execute as root group
+ * or as a GID less than GID_MIN. Tsk tsk.
+ */
+ if ((gid == 0) || (gid < GID_MIN)) {
+ log_err("cannot run as forbidden gid (%d/%s)\n", gid, cmd);
+ exit(108);
+ }
+
+ /*
+ * Change UID/GID here so that the following tests work over NFS.
+ *
+ * Initialize the group access list for the target user,
+ * and setgid() to the target group. If unsuccessful, error out.
+ */
+ if (((setgid(gid)) != 0) || (initgroups(actual_uname, gid) != 0)) {
+ log_err("failed to setgid (%ld: %s)\n", gid, cmd);
+ exit(109);
+ }
+
+ /*
+ * setuid() to the target user. Error out on fail.
+ */
+ if ((setuid(uid)) != 0) {
+ log_err("failed to setuid (%ld: %s)\n", uid, cmd);
+ exit(110);
+ }
+
+ /*
+ * Get the current working directory, as well as the proper
+ * document root (dependant upon whether or not it is a
+ * ~userdir request). Error out if we cannot get either one,
+ * or if the current working directory is not in the docroot.
+ * Use chdir()s and getcwd()s to avoid problems with symlinked
+ * directories. Yuck.
+ */
+ if (getcwd(cwd, AP_MAXPATH) == NULL) {
+ log_err("cannot get current working directory\n");
+ exit(111);
+ }
+
+ if (userdir) {
+ if (((chdir(target_homedir)) != 0) ||
+ ((chdir(USERDIR_SUFFIX)) != 0) ||
+ ((getcwd(dwd, AP_MAXPATH)) == NULL) ||
+ ((chdir(cwd)) != 0)) {
+ log_err("cannot get docroot information (%s)\n", target_homedir);
+ exit(112);
+ }
+ }
+ else {
+ if (((chdir(DOC_ROOT)) != 0) ||
+ ((getcwd(dwd, AP_MAXPATH)) == NULL) ||
+ ((chdir(cwd)) != 0)) {
+ log_err("cannot get docroot information (%s)\n", DOC_ROOT);
+ exit(113);
+ }
+ }
+
+ if ((strncmp(cwd, dwd, strlen(dwd))) != 0) {
+ log_err("command not in docroot (%s/%s)\n", cwd, cmd);
+ exit(114);
+ }
+
+ /*
+ * Stat the cwd and verify it is a directory, or error out.
+ */
+ if (((lstat(cwd, &dir_info)) != 0) || !(S_ISDIR(dir_info.st_mode))) {
+ log_err("cannot stat directory: (%s)\n", cwd);
+ exit(115);
+ }
+
+ /*
+ * Error out if cwd is writable by others.
+ */
+ if ((dir_info.st_mode & S_IWOTH) || (dir_info.st_mode & S_IWGRP)) {
+ log_err("directory is writable by others: (%s)\n", cwd);
+ exit(116);
+ }
+
+ /*
+ * Error out if we cannot stat the program.
+ */
+ if (((lstat(cmd, &prg_info)) != 0) || (S_ISLNK(prg_info.st_mode))) {
+ log_err("cannot stat program: (%s)\n", cmd);
+ exit(117);
+ }
+
+ /*
+ * Error out if the program is writable by others.
+ */
+ if ((prg_info.st_mode & S_IWOTH) || (prg_info.st_mode & S_IWGRP)) {
+ log_err("file is writable by others: (%s/%s)\n", cwd, cmd);
+ exit(118);
+ }
+
+ /*
+ * Error out if the file is setuid or setgid.
+ */
+ if ((prg_info.st_mode & S_ISUID) || (prg_info.st_mode & S_ISGID)) {
+ log_err("file is either setuid or setgid: (%s/%s)\n", cwd, cmd);
+ exit(119);
+ }
+
+ /*
+ * Error out if the target name/group is different from
+ * the name/group of the cwd or the program.
+ */
+ if ((uid != dir_info.st_uid) ||
+ (gid != dir_info.st_gid) ||
+ (uid != prg_info.st_uid) ||
+ (gid != prg_info.st_gid)) {
+ log_err("target uid/gid (%ld/%ld) mismatch "
+ "with directory (%ld/%ld) or program (%ld/%ld)\n",
+ uid, gid,
+ dir_info.st_uid, dir_info.st_gid,
+ prg_info.st_uid, prg_info.st_gid);
+ exit(120);
+ }
+ /*
+ * Error out if the program is not executable for the user.
+ * Otherwise, she won't find any error in the logs except for
+ * "[error] Premature end of script headers: ..."
+ */
+ if (!(prg_info.st_mode & S_IXUSR)) {
+ log_err("file has no execute permission: (%s/%s)\n", cwd, cmd);
+ exit(121);
+ }
+
+ clean_env();
+
+ /*
+ * Be sure to close the log file so the CGI can't
+ * mess with it. If the exec fails, it will be reopened
+ * automatically when log_err is called. Note that the log
+ * might not actually be open if LOG_EXEC isn't defined.
+ * However, the "log" cell isn't ifdef'd so let's be defensive
+ * and assume someone might have done something with it
+ * outside an ifdef'd LOG_EXEC block.
+ */
+ if (log != NULL) {
+ fclose(log);
+ log = NULL;
+ }
+
+ /*
+ * Execute the command, replacing our image with its own.
+ */
+#ifdef NEED_HASHBANG_EMUL
+ /* We need the #! emulation when we want to execute scripts */
+ {
+ extern char **environ;
+
+ ap_execve(cmd, &argv[3], environ);
+ }
+#else /*NEED_HASHBANG_EMUL*/
+ execv(cmd, &argv[3]);
+#endif /*NEED_HASHBANG_EMUL*/
+
+ /*
+ * (I can't help myself...sorry.)
+ *
+ * Uh oh. Still here. Where's the kaboom? There was supposed to be an
+ * EARTH-shattering kaboom!
+ *
+ * Oh well, log the failure and error out.
+ */
+ log_err("(%d)%s: exec failed (%s)\n", errno, strerror(errno), cmd);
+ exit(255);
+}
--- /dev/null
+/* ====================================================================
+ * Copyright (c) 1995-1999 The Apache Group. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * 4. The names "Apache Server" and "Apache Group" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission. For written permission, please contact
+ * apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ * nor may "Apache" appear in their names without prior written
+ * permission of the Apache Group.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Group and was originally based
+ * on public domain software written at the National Center for
+ * Supercomputing Applications, University of Illinois, Urbana-Champaign.
+ * For more information on the Apache Group and the Apache HTTP server
+ * project, please see <http://www.apache.org/>.
+ *
+ */
+
+/*
+ * suexec.h -- user-definable variables for the suexec wrapper code.
+ * (See README.configure on how to customize these variables.)
+ */
+
+
+#ifndef _SUEXEC_H
+#define _SUEXEC_H
+
+/*
+ * HTTPD_USER -- Define as the username under which Apache normally
+ * runs. This is the only user allowed to execute
+ * this program.
+ */
+#ifndef HTTPD_USER
+#define HTTPD_USER "www"
+#endif
+
+/*
+ * UID_MIN -- Define this as the lowest UID allowed to be a target user
+ * for suEXEC. For most systems, 500 or 100 is common.
+ */
+#ifndef UID_MIN
+#define UID_MIN 100
+#endif
+
+/*
+ * GID_MIN -- Define this as the lowest GID allowed to be a target group
+ * for suEXEC. For most systems, 100 is common.
+ */
+#ifndef GID_MIN
+#define GID_MIN 100
+#endif
+
+/*
+ * USERDIR_SUFFIX -- Define to be the subdirectory under users'
+ * home directories where suEXEC access should
+ * be allowed. All executables under this directory
+ * will be executable by suEXEC as the user so
+ * they should be "safe" programs. If you are
+ * using a "simple" UserDir directive (ie. one
+ * without a "*" in it) this should be set to
+ * the same value. suEXEC will not work properly
+ * in cases where the UserDir directive points to
+ * a location that is not the same as the user's
+ * home directory as referenced in the passwd file.
+ *
+ * If you have VirtualHosts with a different
+ * UserDir for each, you will need to define them to
+ * all reside in one parent directory; then name that
+ * parent directory here. IF THIS IS NOT DEFINED
+ * PROPERLY, ~USERDIR CGI REQUESTS WILL NOT WORK!
+ * See the suEXEC documentation for more detailed
+ * information.
+ */
+#ifndef USERDIR_SUFFIX
+#define USERDIR_SUFFIX "public_html"
+#endif
+
+/*
+ * LOG_EXEC -- Define this as a filename if you want all suEXEC
+ * transactions and errors logged for auditing and
+ * debugging purposes.
+ */
+#ifndef LOG_EXEC
+#define LOG_EXEC "/usr/local/apache/logs/cgi.log" /* Need me? */
+#endif
+
+/*
+ * DOC_ROOT -- Define as the DocumentRoot set for Apache. This
+ * will be the only hierarchy (aside from UserDirs)
+ * that can be used for suEXEC behavior.
+ */
+#ifndef DOC_ROOT
+#define DOC_ROOT "/usr/local/apache/htdocs"
+#endif
+
+/*
+ * SAFE_PATH -- Define a safe PATH environment to pass to CGI executables.
+ *
+ */
+#ifndef SAFE_PATH
+#define SAFE_PATH "/usr/local/bin:/usr/bin:/bin"
+#endif
+
+#endif /* _SUEXEC_H */
--- /dev/null
+a.out
+time-FCNTL
+time-FLOCK
+time-SYSVSEM
+time-SYSVSEM2
+time-PTHREAD
+time-USLOCK
+zb
+test-writev
+test_date
+test_select
--- /dev/null
+-i4 -npsl -di0 -br -nce -d0 -cli0 -npcs -nfc1
+-TBUFF
+-TFILE
+-TTRANS
+-TUINT4
+-T_trans
+-Tallow_options_t
+-Tapache_sfio
+-Tarray_header
+-Tbool_int
+-Tbuf_area
+-Tbuff_struct
+-Tbuffy
+-Tcmd_how
+-Tcmd_parms
+-Tcommand_rec
+-Tcommand_struct
+-Tconn_rec
+-Tcore_dir_config
+-Tcore_server_config
+-Tdir_maker_func
+-Tevent
+-Tglobals_s
+-Thandler_func
+-Thandler_rec
+-Tjoblist_s
+-Tlisten_rec
+-Tmerger_func
+-Tmode_t
+-Tmodule
+-Tmodule_struct
+-Tmutex
+-Tn_long
+-Tother_child_rec
+-Toverrides_t
+-Tparent_score
+-Tpid_t
+-Tpiped_log
+-Tpool
+-Trequest_rec
+-Trequire_line
+-Trlim_t
+-Tscoreboard
+-Tsemaphore
+-Tserver_addr_rec
+-Tserver_rec
+-Tserver_rec_chain
+-Tshort_score
+-Ttable
+-Ttable_entry
+-Tthread
+-Tu_wide_int
+-Tvtime_t
+-Twide_int
--- /dev/null
+This directory contains useful test code for testing various bits
+of Apache functionality. This stuff is for the developers only,
+so we might remove it on public releases.
--- /dev/null
+#!/usr/bin/perl -w
+
+# This is meant to be used on the raw output of an HTTP/1.1 connection
+# to check that the chunks are all correctly laid out. It's easiest
+# to use a tool like netcat to generate the output. This script
+# *insists* that \r exist in the output.
+#
+# You can find netcat at avian.org:/src/hacks/nc110.tgz.
+
+use strict;
+
+my $is_chunked = 0;
+
+# must toss headers
+while(<>) {
+ if (/^Transfer-Encoding:\s+chunked/i) {
+ $is_chunked = 1;
+ }
+ last if ($_ eq "\r\n");
+}
+
+$is_chunked || die "wasn't chunked\n";
+
+for(;;) {
+ $_ = <> || die "unexpected end of file!\n";
+
+ m#^([0-9a-f]+) *\r$#i || die "bogus chunklen: $_";
+
+ my $chunklen = hex($1);
+
+ exit 0 if ($chunklen == 0);
+
+ chop; chop;
+ print "$_ ";
+
+ my $data = '';
+ read(ARGV, $data, $chunklen) == $chunklen || die "short read!\n";
+
+ $_ = <> || die "unexpected end of file!\n";
+
+ $_ eq "\r\n" || die "missing chunk trailer!\n";
+}
--- /dev/null
+#include <ctype.h>
+#include <dirent.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+
+/*
+ * Compare a string to a mask
+ * Mask characters:
+ * @ - uppercase letter
+ * # - lowercase letter
+ * & - hex digit
+ * # - digit
+ * * - swallow remaining characters
+ * <x> - exact match for any other character
+ */
+static int checkmask(const char *data, const char *mask)
+{
+ int i, ch, d;
+
+ for (i = 0; mask[i] != '\0' && mask[i] != '*'; i++) {
+ ch = mask[i];
+ d = data[i];
+ if (ch == '@') {
+ if (!isupper(d))
+ return 0;
+ }
+ else if (ch == '$') {
+ if (!islower(d))
+ return 0;
+ }
+ else if (ch == '#') {
+ if (!isdigit(d))
+ return 0;
+ }
+ else if (ch == '&') {
+ if (!isxdigit(d))
+ return 0;
+ }
+ else if (ch != d)
+ return 0;
+ }
+
+ if (mask[i] == '*')
+ return 1;
+ else
+ return (data[i] == '\0');
+}
+
+/*
+ * Converts 8 hex digits to a time integer
+ */
+static int hex2sec(const char *x)
+{
+ int i, ch;
+ unsigned int j;
+
+ for (i = 0, j = 0; i < 8; i++) {
+ ch = x[i];
+ j <<= 4;
+ if (isdigit(ch))
+ j |= ch - '0';
+ else if (isupper(ch))
+ j |= ch - ('A' - 10);
+ else
+ j |= ch - ('a' - 10);
+ }
+ if (j == 0xffffffff)
+ return -1; /* so that it works with 8-byte ints */
+ else
+ return j;
+}
+
+int main(int argc, char **argv)
+{
+ int i, ver;
+ DIR *d;
+ struct dirent *e;
+ const char *s;
+ FILE *fp;
+ char path[FILENAME_MAX + 1];
+ char line[1035];
+ time_t date, lmod, expire;
+ unsigned int len;
+ struct tm ts;
+ char sdate[30], slmod[30], sexpire[30];
+ const char time_format[] = "%e %b %Y %R";
+
+ if (argc != 2) {
+ printf("Usage: cls directory\n");
+ exit(0);
+ }
+
+ d = opendir(argv[1]);
+ if (d == NULL) {
+ perror("opendir");
+ exit(1);
+ }
+
+ for (;;) {
+ e = readdir(d);
+ if (e == NULL)
+ break;
+ s = e->d_name;
+ if (s[0] == '.' || s[0] == '#')
+ continue;
+ sprintf(path, "%s/%s", argv[1], s);
+ fp = fopen(path, "r");
+ if (fp == NULL) {
+ perror("fopen");
+ continue;
+ }
+ if (fgets(line, 1034, fp) == NULL) {
+ perror("fgets");
+ fclose(fp);
+ continue;
+ }
+ if (!checkmask(line, "&&&&&&&& &&&&&&&& &&&&&&&& &&&&&&&& &&&&&&&&\n")) {
+ fprintf(stderr, "Bad cache file\n");
+ fclose(fp);
+ continue;
+ }
+ date = hex2sec(line);
+ lmod = hex2sec(line + 9);
+ expire = hex2sec(line + 18);
+ ver = hex2sec(line + 27);
+ len = hex2sec(line + 35);
+ if (fgets(line, 1034, fp) == NULL) {
+ perror("fgets");
+ fclose(fp);
+ continue;
+ }
+ fclose(fp);
+ i = strlen(line);
+ if (strncmp(line, "X-URL: ", 7) != 0 || line[i - 1] != '\n') {
+ fprintf(stderr, "Bad cache file\n");
+ continue;
+ }
+ line[i - 1] = '\0';
+ if (date != -1) {
+ ts = *gmtime(&date);
+ strftime(sdate, 30, time_format, &ts);
+ }
+ else
+ strcpy(sdate, "-");
+
+ if (lmod != -1) {
+ ts = *gmtime(&lmod);
+ strftime(slmod, 30, time_format, &ts);
+ }
+ else
+ strcpy(slmod, "-");
+
+ if (expire != -1) {
+ ts = *gmtime(&expire);
+ strftime(sexpire, 30, time_format, &ts);
+ }
+ else
+ strcpy(sexpire, "-");
+
+ printf("%s: %d; %s %s %s\n", line + 7, ver, sdate, slmod, sexpire);
+ }
+
+ closedir(d);
+ return 0;
+}
--- /dev/null
+
+From marcs@znep.com Fri Apr 17 15:16:16 1998
+Date: Sat, 22 Nov 1997 20:44:10 -0700 (MST)
+From: Marc Slemko <marcs@znep.com>
+To: TLOSAP <new-httpd@apache.org>
+Subject: Re: Getting ethernet packets content under FreeBSD? (fwd)
+Reply-To: new-httpd@apache.org
+
+Anyone too lazy to hack tcpdump (eg. my tcpdump has a -X option to display
+the data in ASCII) can use something like the below to grab HTTP headers
+when debugging broken clients.
+
+Nothing complicated, but handy.
+
+---------- Forwarded message ----------
+Date: Sat, 22 Nov 1997 14:35:23 PST
+From: Bill Fenner <fenner@parc.xerox.com>
+To: Nate Williams <nate@mt.sri.com>
+Cc: bmah@ca.sandia.gov, hackers@FreeBSD.ORG
+Subject: Re: Getting ethernet packets content under FreeBSD?
+
+I usually just use this perl script, which I call "tcpdumpscii".
+Then run "tcpdumpscii -s 1500 -x [other tcpdump args]".
+
+ Bill
+
+#!/import/misc/bin/perl
+#
+#
+open(TCPDUMP,"tcpdump -l @ARGV|");
+while (<TCPDUMP>) {
+ if (/^\s+(\S\S)+/) {
+ $sav = $_;
+ $asc = "";
+ while (s/\s*(\S\S)\s*//) {
+ $i = hex($1);
+ if ($i < 32 || $i > 126) {
+ $asc .= ".";
+ } else {
+ $asc .= pack(C,hex($1));
+ }
+ }
+ $foo = "." x length($asc);
+ $_ = $sav;
+ s/\t/ /g;
+ s/^$foo/$asc/;
+ }
+ print;
+}
+
--- /dev/null
+/*
+ test-writev: use this to figure out if your writev() does intelligent
+ things on the network. Some writev()s when given multiple buffers
+ will break them up into multiple packets, which is a waste.
+
+ Linux prior to 2.0.31 has this problem.
+
+ Solaris 2.5, 2.5.1 doesn't appear to, 2.6 hasn't been tested.
+
+ IRIX 5.3 doesn't have this problem.
+
+ To use this you want to snoop the wire with tcpdump, and then run
+ "test-writev a.b.c.d port#" ... against some TCP service on another
+ box. For example you can run it against port 80 on another server.
+ You want to look to see how many data packets are sent, you're hoping
+ only one of size 300 is sent.
+*/
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+#include <sys/uio.h>
+#include <errno.h>
+
+#ifndef INADDR_NONE
+#define INADDR_NONE (-1ul)
+#endif
+
+void main( int argc, char **argv )
+{
+ struct sockaddr_in server_addr;
+ int s;
+ struct iovec vector[3];
+ char buf[100];
+ int i;
+ const int just_say_no = 1;
+
+ if( argc != 3 ) {
+usage:
+ fprintf( stderr, "usage: test-writev a.b.c.d port#\n" );
+ exit( 1 );
+ }
+ server_addr.sin_family = AF_INET;
+ server_addr.sin_addr.s_addr = inet_addr( argv[1] );
+ if( server_addr.sin_addr.s_addr == INADDR_NONE ) {
+ fprintf( stderr, "bogus address\n" );
+ goto usage;
+ }
+ server_addr.sin_port = htons( atoi( argv[2] ) );
+
+ s = socket( AF_INET, SOCK_STREAM, 0 );
+ if( s < 0 ) {
+ perror("socket");
+ exit(1);
+ }
+ if( connect( s, (struct sockaddr *)&server_addr, sizeof( server_addr ) )
+ != 0 ) {
+ perror("connect");
+ exit(1);
+ }
+
+ if( setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (char*)&just_say_no,
+ sizeof(just_say_no)) != 0 ) {
+ perror( "TCP_NODELAY" );
+ exit(1);
+ }
+ /* now build up a two part writev and write it out */
+ for( i = 0; i < sizeof( buf ); ++i ) {
+ buf[i] = 'x';
+ }
+ vector[0].iov_base = buf;
+ vector[0].iov_len = sizeof(buf);
+ vector[1].iov_base = buf;
+ vector[1].iov_len = sizeof(buf);
+ vector[2].iov_base = buf;
+ vector[2].iov_len = sizeof(buf);
+
+ i = writev( s, &vector[0], 3 );
+ fprintf( stdout, "i=%d, errno=%d\n", i, errno );
+ exit(0);
+}
--- /dev/null
+/* This program tests the parseHTTPdate routine in ../main/util_date.c.
+ *
+ * It is only semiautomated in that I would run it, modify the code to
+ * use a different algorithm or seed, recompile and run again, etc.
+ * Obviously it should use an argument for that, but I never got around
+ * to changing the implementation.
+ *
+ * gcc -g -O2 -I../main -o test_date ../main/util_date.o test_date.c
+ * test_date | egrep '^No '
+ *
+ * Roy Fielding, 1996
+ */
+#define API_EXPORT(x) x
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "util_date.h"
+
+static const long year2secs[] = {
+ 0L, /* 1970 */
+ 31536000L, /* 1971 */
+ 63072000L, /* 1972 */
+ 94694400L, /* 1973 */
+ 126230400L, /* 1974 */
+ 157766400L, /* 1975 */
+ 189302400L, /* 1976 */
+ 220924800L, /* 1977 */
+ 252460800L, /* 1978 */
+ 283996800L, /* 1979 */
+ 315532800L, /* 1980 */
+ 347155200L, /* 1981 */
+ 378691200L, /* 1982 */
+ 410227200L, /* 1983 */
+ 441763200L, /* 1984 */
+ 473385600L, /* 1985 */
+ 504921600L, /* 1986 */
+ 536457600L, /* 1987 */
+ 567993600L, /* 1988 */
+ 599616000L, /* 1989 */
+ 631152000L, /* 1990 */
+ 662688000L, /* 1991 */
+ 694224000L, /* 1992 */
+ 725846400L, /* 1993 */
+ 757382400L, /* 1994 */
+ 788918400L, /* 1995 */
+ 820454400L, /* 1996 */
+ 852076800L, /* 1997 */
+ 883612800L, /* 1998 */
+ 915148800L, /* 1999 */
+ 946684800L, /* 2000 */
+ 978307200L, /* 2001 */
+ 1009843200L, /* 2002 */
+ 1041379200L, /* 2003 */
+ 1072915200L, /* 2004 */
+ 1104537600L, /* 2005 */
+ 1136073600L, /* 2006 */
+ 1167609600L, /* 2007 */
+ 1199145600L, /* 2008 */
+ 1230768000L, /* 2009 */
+ 1262304000L, /* 2010 */
+ 1293840000L, /* 2011 */
+ 1325376000L, /* 2012 */
+ 1356998400L, /* 2013 */
+ 1388534400L, /* 2014 */
+ 1420070400L, /* 2015 */
+ 1451606400L, /* 2016 */
+ 1483228800L, /* 2017 */
+ 1514764800L, /* 2018 */
+ 1546300800L, /* 2019 */
+ 1577836800L, /* 2020 */
+ 1609459200L, /* 2021 */
+ 1640995200L, /* 2022 */
+ 1672531200L, /* 2023 */
+ 1704067200L, /* 2024 */
+ 1735689600L, /* 2025 */
+ 1767225600L, /* 2026 */
+ 1798761600L, /* 2027 */
+ 1830297600L, /* 2028 */
+ 1861920000L, /* 2029 */
+ 1893456000L, /* 2030 */
+ 1924992000L, /* 2031 */
+ 1956528000L, /* 2032 */
+ 1988150400L, /* 2033 */
+ 2019686400L, /* 2034 */
+ 2051222400L, /* 2035 */
+ 2082758400L, /* 2036 */
+ 2114380800L, /* 2037 */
+ 2145916800L /* 2038 */
+};
+
+const char month_snames[12][4] = {
+ "Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"
+};
+
+void gm_timestr_822(char *ts, time_t sec)
+{
+ static const char *const days[7]=
+ {"Sun","Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
+ struct tm *tms;
+
+ tms = gmtime(&sec);
+
+ sprintf(ts, "%s, %.2d %s %d %.2d:%.2d:%.2d GMT", days[tms->tm_wday],
+ tms->tm_mday, month_snames[tms->tm_mon], tms->tm_year + 1900,
+ tms->tm_hour, tms->tm_min, tms->tm_sec);
+}
+
+void gm_timestr_850(char *ts, time_t sec)
+{
+ static const char *const days[7]=
+ {"Sunday","Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
+ struct tm *tms;
+ int year;
+
+ tms = gmtime(&sec);
+
+ year = tms->tm_year;
+ if (year >= 100) year -= 100;
+
+ sprintf(ts, "%s, %.2d-%s-%.2d %.2d:%.2d:%.2d GMT", days[tms->tm_wday],
+ tms->tm_mday, month_snames[tms->tm_mon], year,
+ tms->tm_hour, tms->tm_min, tms->tm_sec);
+}
+
+void gm_timestr_ccc(char *ts, time_t sec)
+{
+ static const char *const days[7]=
+ {"Sun","Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
+ struct tm *tms;
+
+ tms = gmtime(&sec);
+
+ sprintf(ts, "%s %s %2d %.2d:%.2d:%.2d %d", days[tms->tm_wday],
+ month_snames[tms->tm_mon], tms->tm_mday,
+ tms->tm_hour, tms->tm_min, tms->tm_sec, tms->tm_year + 1900);
+}
+
+int main (void)
+{
+ int year, i;
+ time_t guess;
+ time_t offset = 0;
+ /* time_t offset = 0; */
+ /* time_t offset = ((31 + 28) * 24 * 3600) - 1; */
+ time_t secstodate, newsecs;
+ char datestr[50];
+
+ for (year = 1970; year < 2038; ++year) {
+ secstodate = (time_t)year2secs[year - 1970] + offset;
+ gm_timestr_822(datestr, secstodate);
+ newsecs = parseHTTPdate(datestr);
+ if (secstodate == newsecs)
+ printf("Yes %4d %11ld %s\n", year, (long)secstodate, datestr);
+ else if (newsecs == BAD_DATE)
+ printf("No %4d %11ld %11ld %s\n", year, (long)secstodate,
+ (long)newsecs, datestr);
+ else
+ printf("No* %4d %11ld %11ld %s\n", year, (long)secstodate,
+ (long)newsecs, datestr);
+ }
+
+ srand48(978245L);
+
+ for (i = 0; i < 10000; ++i) {
+ guess = (time_t)mrand48();
+ if (guess < 0) guess *= -1;
+ secstodate = guess + offset;
+ gm_timestr_822(datestr, secstodate);
+ newsecs = parseHTTPdate(datestr);
+ if (secstodate == newsecs)
+ printf("Yes %11ld %s\n", (long)secstodate, datestr);
+ else if (newsecs == BAD_DATE)
+ printf("No %11ld %11ld %s\n", (long)secstodate,
+ (long)newsecs, datestr);
+ else
+ printf("No* %11ld %11ld %s\n", (long)secstodate,
+ (long)newsecs, datestr);
+ }
+ exit(0);
+}
--- /dev/null
+/* This program tests the ap_find_list_item routine in ../main/util.c.
+ *
+ * The defines in this sample compile line are specific to Roy's system.
+ * They should match whatever was used to compile Apache first.
+ *
+ gcc -g -O2 -I../os/unix -I../include -o test_find \
+ -DSOLARIS2=250 -Wall -DALLOC_DEBUG -DPOOL_DEBUG \
+ ../main/alloc.o ../main/buff.o ../main/util.o \
+ ../ap/libap.a -lsocket -lnsl test_find.c
+ *
+ * Roy Fielding, 1999
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include "httpd.h"
+#include "alloc.h"
+
+/*
+ * Dummy a bunch of stuff just to get a compile
+ */
+uid_t ap_user_id;
+gid_t ap_group_id;
+void *ap_dummy_mutex = &ap_dummy_mutex;
+char *ap_server_argv0;
+
+API_EXPORT(void) ap_block_alarms(void)
+{
+ ;
+}
+
+API_EXPORT(void) ap_unblock_alarms(void)
+{
+ ;
+}
+
+API_EXPORT(void) ap_log_error(const char *file, int line, int level,
+ const request_rec *r, const char *fmt, ...)
+{
+ ;
+}
+
+int main (void)
+{
+ ap_pool *p;
+ char line[512];
+ char tok[512];
+
+ p = ap_init_alloc();
+
+ printf("Enter field value to find items within:\n");
+ if (!gets(line))
+ exit(0);
+
+ printf("Enter search item:\n");
+ while (gets(tok)) {
+ printf(" [%s] == %s\n", tok, ap_find_list_item(p, line, tok)
+ ? "Yes" : "No");
+ printf("Enter search item:\n");
+ }
+
+ exit(0);
+}
--- /dev/null
+/**************************************************************
+ * test_limits.c
+ *
+ * A simple program for sending abusive requests to a server, based
+ * on the sioux.c exploit code that this nimrod posted (see below).
+ * Roy added options for testing long header fieldsize (-t h), long
+ * request-lines (-t r), and a long request body (-t b).
+ *
+ * FreeBSD 2.2.x, FreeBSD 3.0, IRIX 5.3, IRIX 6.2:
+ * gcc -o test_limits test_limits.c
+ *
+ * Solaris 2.5.1:
+ * gcc -o test_limits test_limits.c -lsocket -lnsl
+ *
+ *
+ * Message-ID: <861zqspvtw.fsf@niobe.ewox.org>
+ * Date: Fri, 7 Aug 1998 19:04:27 +0200
+ * Sender: Bugtraq List <BUGTRAQ@netspace.org>
+ * From: Dag-Erling Coidan =?ISO-8859-1?Q?Sm=F8rgrav?= <finrod@EWOX.ORG>
+ * Subject: YA Apache DoS attack
+ *
+ * Copyright (c) 1998 Dag-Erling Codan Smrgrav
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer
+ * in this position and unchanged.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software withough specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+/*
+ * Kudos to Mark Huizer who originally suggested this on freebsd-current
+ */
+
+#include <sys/types.h>
+#include <sys/uio.h>
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define TEST_LONG_REQUEST_LINE 1
+#define TEST_LONG_REQUEST_FIELDS 2
+#define TEST_LONG_REQUEST_FIELDSIZE 3
+#define TEST_LONG_REQUEST_BODY 4
+
+void
+usage(void)
+{
+ fprintf(stderr,
+ "usage: test_limits [-t (r|n|h|b)] [-a address] [-p port] [-n num]\n");
+ exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+ struct sockaddr_in sin;
+ struct hostent *he;
+ FILE *f;
+ int o, sd;
+
+ /* default parameters */
+ char *addr = "localhost";
+ int port = 80;
+ int num = 1000;
+ int testtype = TEST_LONG_REQUEST_FIELDS;
+
+ /* get options */
+ while ((o = getopt(argc, argv, "t:a:p:n:")) != EOF)
+ switch (o) {
+ case 't':
+ if (*optarg == 'r')
+ testtype = TEST_LONG_REQUEST_LINE;
+ else if (*optarg == 'n')
+ testtype = TEST_LONG_REQUEST_FIELDS;
+ else if (*optarg == 'h')
+ testtype = TEST_LONG_REQUEST_FIELDSIZE;
+ else if (*optarg == 'b')
+ testtype = TEST_LONG_REQUEST_BODY;
+ break;
+ case 'a':
+ addr = optarg;
+ break;
+ case 'p':
+ port = atoi(optarg);
+ break;
+ case 'n':
+ num = atoi(optarg);
+ break;
+ default:
+ usage();
+ }
+
+ if (argc != optind)
+ usage();
+
+ /* connect */
+ if ((he = gethostbyname(addr)) == NULL) {
+ perror("gethostbyname");
+ exit(1);
+ }
+ bzero(&sin, sizeof(sin));
+ bcopy(he->h_addr, (char *)&sin.sin_addr, he->h_length);
+ sin.sin_family = he->h_addrtype;
+ sin.sin_port = htons(port);
+
+ if ((sd = socket(sin.sin_family, SOCK_STREAM, IPPROTO_TCP)) == -1) {
+ perror("socket");
+ exit(1);
+ }
+
+ if (connect(sd, (struct sockaddr *)&sin, sizeof(sin)) == -1) {
+ perror("connect");
+ exit(1);
+ }
+
+ if ((f = fdopen(sd, "r+")) == NULL) {
+ perror("fdopen");
+ exit(1);
+ }
+
+ /* attack! */
+ fprintf(stderr, "Testing like a plague of locusts on %s\n", addr);
+
+ if (testtype == TEST_LONG_REQUEST_LINE) {
+ fprintf(f, "GET ");
+ while (num-- && !ferror(f)) {
+ fprintf(f, "/123456789");
+ fflush(f);
+ }
+ fprintf(f, " HTTP/1.0\r\n\r\n");
+ }
+ else {
+ fprintf(f, "GET /fred/foo HTTP/1.0\r\n");
+
+ if (testtype == TEST_LONG_REQUEST_FIELDSIZE) {
+ while (num-- && !ferror(f)) {
+ fprintf(f, "User-Agent: sioux");
+ fflush(f);
+ }
+ fprintf(f, "\r\n");
+ }
+ else if (testtype == TEST_LONG_REQUEST_FIELDS) {
+ while (num-- && !ferror(f))
+ fprintf(f, "User-Agent: sioux\r\n");
+ fprintf(f, "\r\n");
+ }
+ else if (testtype == TEST_LONG_REQUEST_BODY) {
+ fprintf(f, "User-Agent: sioux\r\n");
+ fprintf(f, "Content-Length: 33554433\r\n");
+ fprintf(f, "\r\n");
+ while (num-- && !ferror(f))
+ fprintf(f, "User-Agent: sioux\r\n");
+ }
+ else {
+ fprintf(f, "\r\n");
+ }
+ }
+ fflush(f);
+
+ {
+ ssize_t len;
+ char buff[512];
+
+ while ((len = read(sd, buff, 512)) > 0)
+ len = write(1, buff, len);
+ }
+ if (ferror(f)) {
+ perror("fprintf");
+ exit(1);
+ }
+
+ fclose(f);
+ exit(0);
+}
--- /dev/null
+/* This program tests the ap_get_list_item routine in ../main/util.c.
+ *
+ * The defines in this sample compile line are specific to Roy's system.
+ * They should match whatever was used to compile Apache first.
+ *
+ gcc -g -O2 -I../os/unix -I../include -o test_parser \
+ -DSOLARIS2=250 -Wall -DALLOC_DEBUG -DPOOL_DEBUG \
+ ../main/alloc.o ../main/buff.o ../main/util.o \
+ ../ap/libap.a -lsocket -lnsl test_parser.c
+ *
+ * Roy Fielding, 1999
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include "httpd.h"
+#include "alloc.h"
+
+/*
+ * Dummy a bunch of stuff just to get a compile
+ */
+uid_t ap_user_id;
+gid_t ap_group_id;
+void *ap_dummy_mutex = &ap_dummy_mutex;
+char *ap_server_argv0;
+
+API_EXPORT(void) ap_block_alarms(void)
+{
+ ;
+}
+
+API_EXPORT(void) ap_unblock_alarms(void)
+{
+ ;
+}
+
+API_EXPORT(void) ap_log_error(const char *file, int line, int level,
+ const request_rec *r, const char *fmt, ...)
+{
+ ;
+}
+
+int main (void)
+{
+ ap_pool *p;
+ const char *field;
+ char *newstr;
+ char instr[512];
+
+ p = ap_init_alloc();
+
+ while (gets(instr)) {
+ printf(" [%s] ==\n", instr);
+ field = instr;
+ while ((newstr = ap_get_list_item(p, &field)) != NULL)
+ printf(" <%s> ..\n", newstr);
+ }
+
+ exit(0);
+}
--- /dev/null
+/* This is just a quick test program to see how long a wait is
+ * produced by a select loop with an exponential backoff.
+ *
+ * gcc -g -O2 -o test_select test_select.c
+ * test_select
+ *
+ * Roy Fielding, 1996
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/time.h>
+
+int main (void)
+{
+ int srv;
+ long waittime = 4096;
+ struct timeval tv;
+
+ printf("Start\n");
+ while ((waittime > 0) && (waittime < 3000000)) {
+ printf("%d\n", waittime);
+ tv.tv_sec = waittime/1000000;
+ tv.tv_usec = waittime%1000000;
+ waittime <<= 1;
+ srv = select(0, NULL, NULL, NULL, &tv);
+ }
+ printf("End\n");
+ exit(0);
+}
--- /dev/null
+/*
+time-sem.c has the basics of the semaphores we use in http_main.c. It's
+intended for timing differences between various methods on an
+architecture. In practice we've found many things affect which semaphore
+to be used:
+
+ - NFS filesystems absolutely suck for fcntl() and flock()
+
+ - uslock absolutely sucks on single-processor IRIX boxes, but
+ absolutely rocks on multi-processor boxes. The converse
+ is true for fcntl. sysvsem seems a moderate balance.
+
+ - Under Solaris you can't have too many processes use SEM_UNDO, there
+ might be a tuneable somewhere that increases the limit from 29.
+ We're not sure what the tunable is, so there's a define
+ NO_SEM_UNDO which can be used to simulate us trapping/blocking
+ signals to be able to properly release the semaphore on a clean
+ child death. You'll also need to define NEED_UNION_SEMUN
+ under solaris.
+
+You'll need to define USE_SHMGET_SCOREBOARD if anonymous shared mmap()
+doesn't work on your system (i.e. linux).
+
+argv[1] is the #children, argv[2] is the #iterations per child
+
+You should run each over many different #children inputs, and choose
+#iter such that the program runs for at least a second or so... or even
+longer depending on your patience.
+
+compile with:
+
+gcc -o time-FCNTL -Wall -O time-sem.c -DUSE_FCNTL_SERIALIZED_ACCEPT
+gcc -o time-FLOCK -Wall -O time-sem.c -DUSE_FLOCK_SERIALIZED_ACCEPT
+gcc -o time-SYSVSEM -Wall -O time-sem.c -DUSE_SYSVSEM_SERIALIZED_ACCEPT
+gcc -o time-SYSVSEM2 -Wall -O time-sem.c -DUSE_SYSVSEM_SERIALIZED_ACCEPT -DNO_SEM_UNDO
+gcc -o time-PTHREAD -Wall -O time-sem.c -DUSE_PTHREAD_SERIALIZED_ACCEPT -lpthread
+gcc -o time-USLOCK -Wall -O time-sem.c -DUSE_USLOCK_SERIALIZED_ACCEPT
+
+not all versions work on all systems.
+*/
+
+#include <errno.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/wait.h>
+#include <sys/mman.h>
+#include <signal.h>
+
+#if defined(USE_FCNTL_SERIALIZED_ACCEPT)
+
+static struct flock lock_it;
+static struct flock unlock_it;
+
+static int fcntl_fd=-1;
+
+#define accept_mutex_child_init()
+#define accept_mutex_cleanup()
+
+/*
+ * Initialize mutex lock.
+ * Must be safe to call this on a restart.
+ */
+void
+accept_mutex_init(void)
+{
+
+ lock_it.l_whence = SEEK_SET; /* from current point */
+ lock_it.l_start = 0; /* -"- */
+ lock_it.l_len = 0; /* until end of file */
+ lock_it.l_type = F_WRLCK; /* set exclusive/write lock */
+ lock_it.l_pid = 0; /* pid not actually interesting */
+ unlock_it.l_whence = SEEK_SET; /* from current point */
+ unlock_it.l_start = 0; /* -"- */
+ unlock_it.l_len = 0; /* until end of file */
+ unlock_it.l_type = F_UNLCK; /* set exclusive/write lock */
+ unlock_it.l_pid = 0; /* pid not actually interesting */
+
+ printf("opening test-lock-thing in current directory\n");
+ fcntl_fd = open("test-lock-thing", O_CREAT | O_WRONLY | O_EXCL, 0644);
+ if (fcntl_fd == -1)
+ {
+ perror ("open");
+ fprintf (stderr, "Cannot open lock file: %s\n", "test-lock-thing");
+ exit (1);
+ }
+ unlink("test-lock-thing");
+}
+
+void accept_mutex_on(void)
+{
+ int ret;
+
+ while ((ret = fcntl(fcntl_fd, F_SETLKW, &lock_it)) < 0 && errno == EINTR)
+ continue;
+
+ if (ret < 0) {
+ perror ("fcntl lock_it");
+ exit(1);
+ }
+}
+
+void accept_mutex_off(void)
+{
+ if (fcntl (fcntl_fd, F_SETLKW, &unlock_it) < 0)
+ {
+ perror ("fcntl unlock_it");
+ exit(1);
+ }
+}
+
+#elif defined(USE_FLOCK_SERIALIZED_ACCEPT)
+
+#include <sys/file.h>
+
+static int flock_fd=-1;
+
+#define FNAME "test-lock-thing"
+
+/*
+ * Initialize mutex lock.
+ * Must be safe to call this on a restart.
+ */
+void accept_mutex_init(void)
+{
+
+ printf("opening " FNAME " in current directory\n");
+ flock_fd = open(FNAME, O_CREAT | O_WRONLY | O_EXCL, 0644);
+ if (flock_fd == -1)
+ {
+ perror ("open");
+ fprintf (stderr, "Cannot open lock file: %s\n", "test-lock-thing");
+ exit (1);
+ }
+}
+
+void accept_mutex_child_init(void)
+{
+ flock_fd = open(FNAME, O_WRONLY, 0600);
+ if (flock_fd == -1) {
+ perror("open");
+ exit(1);
+ }
+}
+
+void accept_mutex_cleanup(void)
+{
+ unlink(FNAME);
+}
+
+void accept_mutex_on(void)
+{
+ int ret;
+
+ while ((ret = flock(flock_fd, LOCK_EX)) < 0 && errno == EINTR)
+ continue;
+
+ if (ret < 0) {
+ perror ("flock(LOCK_EX)");
+ exit(1);
+ }
+}
+
+void accept_mutex_off(void)
+{
+ if (flock (flock_fd, LOCK_UN) < 0)
+ {
+ perror ("flock(LOCK_UN)");
+ exit(1);
+ }
+}
+
+#elif defined (USE_SYSVSEM_SERIALIZED_ACCEPT)
+
+#include <sys/types.h>
+#include <sys/ipc.h>
+#include <sys/sem.h>
+
+static int sem_id = -1;
+#ifdef NO_SEM_UNDO
+static sigset_t accept_block_mask;
+static sigset_t accept_previous_mask;
+#endif
+
+#define accept_mutex_child_init()
+#define accept_mutex_cleanup()
+
+void accept_mutex_init(void)
+{
+#ifdef NEED_UNION_SEMUN
+ /* believe it or not, you need to define this under solaris */
+ union semun {
+ int val;
+ struct semid_ds *buf;
+ ushort *array;
+ };
+#endif
+
+ union semun ick;
+
+ sem_id = semget(999, 1, IPC_CREAT | 0666);
+ if (sem_id < 0) {
+ perror ("semget");
+ exit (1);
+ }
+ ick.val = 1;
+ if (semctl(sem_id, 0, SETVAL, ick) < 0) {
+ perror ("semctl");
+ exit(1);
+ }
+#ifdef NO_SEM_UNDO
+ sigfillset(&accept_block_mask);
+ sigdelset(&accept_block_mask, SIGHUP);
+ sigdelset(&accept_block_mask, SIGTERM);
+ sigdelset(&accept_block_mask, SIGUSR1);
+#endif
+}
+
+void accept_mutex_on()
+{
+ struct sembuf op;
+
+#ifdef NO_SEM_UNDO
+ if (sigprocmask(SIG_BLOCK, &accept_block_mask, &accept_previous_mask)) {
+ perror("sigprocmask(SIG_BLOCK)");
+ exit (1);
+ }
+ op.sem_flg = 0;
+#else
+ op.sem_flg = SEM_UNDO;
+#endif
+ op.sem_num = 0;
+ op.sem_op = -1;
+ if (semop(sem_id, &op, 1) < 0) {
+ perror ("accept_mutex_on");
+ exit (1);
+ }
+}
+
+void accept_mutex_off()
+{
+ struct sembuf op;
+
+ op.sem_num = 0;
+ op.sem_op = 1;
+#ifdef NO_SEM_UNDO
+ op.sem_flg = 0;
+#else
+ op.sem_flg = SEM_UNDO;
+#endif
+ if (semop(sem_id, &op, 1) < 0) {
+ perror ("accept_mutex_off");
+ exit (1);
+ }
+#ifdef NO_SEM_UNDO
+ if (sigprocmask(SIG_SETMASK, &accept_previous_mask, NULL)) {
+ perror("sigprocmask(SIG_SETMASK)");
+ exit (1);
+ }
+#endif
+}
+
+#elif defined (USE_PTHREAD_SERIALIZED_ACCEPT)
+
+/* note: pthread mutexes aren't released on child death, hence the
+ * signal goop ... in a real implementation we'd do special things
+ * during hup, term, usr1.
+ */
+
+#include <pthread.h>
+
+static pthread_mutex_t *mutex;
+static sigset_t accept_block_mask;
+static sigset_t accept_previous_mask;
+
+#define accept_mutex_child_init()
+#define accept_mutex_cleanup()
+
+void accept_mutex_init(void)
+{
+ pthread_mutexattr_t mattr;
+ int fd;
+
+ fd = open ("/dev/zero", O_RDWR);
+ if (fd == -1) {
+ perror ("open(/dev/zero)");
+ exit (1);
+ }
+ mutex = (pthread_mutex_t *)mmap ((caddr_t)0, sizeof (*mutex),
+ PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
+ if (mutex == (void *)(caddr_t)-1) {
+ perror ("mmap");
+ exit (1);
+ }
+ close (fd);
+ if (pthread_mutexattr_init(&mattr)) {
+ perror ("pthread_mutexattr_init");
+ exit (1);
+ }
+ if (pthread_mutexattr_setpshared(&mattr, PTHREAD_PROCESS_SHARED)) {
+ perror ("pthread_mutexattr_setpshared");
+ exit (1);
+ }
+ if (pthread_mutex_init(mutex, &mattr)) {
+ perror ("pthread_mutex_init");
+ exit (1);
+ }
+ sigfillset(&accept_block_mask);
+ sigdelset(&accept_block_mask, SIGHUP);
+ sigdelset(&accept_block_mask, SIGTERM);
+ sigdelset(&accept_block_mask, SIGUSR1);
+}
+
+void accept_mutex_on()
+{
+ if (sigprocmask(SIG_BLOCK, &accept_block_mask, &accept_previous_mask)) {
+ perror("sigprocmask(SIG_BLOCK)");
+ exit (1);
+ }
+ if (pthread_mutex_lock (mutex)) {
+ perror ("pthread_mutex_lock");
+ exit (1);
+ }
+}
+
+void accept_mutex_off()
+{
+ if (pthread_mutex_unlock (mutex)) {
+ perror ("pthread_mutex_unlock");
+ exit (1);
+ }
+ if (sigprocmask(SIG_SETMASK, &accept_previous_mask, NULL)) {
+ perror("sigprocmask(SIG_SETMASK)");
+ exit (1);
+ }
+}
+
+#elif defined (USE_USLOCK_SERIALIZED_ACCEPT)
+
+#include <ulocks.h>
+
+static usptr_t *us = NULL;
+static ulock_t uslock = NULL;
+
+#define accept_mutex_child_init()
+#define accept_mutex_cleanup()
+
+void accept_mutex_init(void)
+{
+ ptrdiff_t old;
+ /* default is 8 */
+#define CONF_INITUSERS_MAX 15
+ if ((old = usconfig(CONF_INITUSERS, CONF_INITUSERS_MAX)) == -1) {
+ perror("usconfig");
+ exit(-1);
+ }
+ if ((old = usconfig(CONF_LOCKTYPE, US_NODEBUG)) == -1) {
+ perror("usconfig");
+ exit(-1);
+ }
+ if ((old = usconfig(CONF_ARENATYPE, US_SHAREDONLY)) == -1) {
+ perror("usconfig");
+ exit(-1);
+ }
+ if ((us = usinit("/dev/zero")) == NULL) {
+ perror("usinit");
+ exit(-1);
+ }
+ if ((uslock = usnewlock(us)) == NULL) {
+ perror("usnewlock");
+ exit(-1);
+ }
+}
+void accept_mutex_on()
+{
+ switch(ussetlock(uslock)) {
+ case 1:
+ /* got lock */
+ break;
+ case 0:
+ fprintf(stderr, "didn't get lock\n");
+ exit(-1);
+ case -1:
+ perror("ussetlock");
+ exit(-1);
+ }
+}
+void accept_mutex_off()
+{
+ if (usunsetlock(uslock) == -1) {
+ perror("usunsetlock");
+ exit(-1);
+ }
+}
+#endif
+
+
+#ifndef USE_SHMGET_SCOREBOARD
+static void *get_shared_mem(size_t size)
+{
+ void *result;
+
+ /* allocate shared memory for the shared_counter */
+ result = (unsigned long *)mmap ((caddr_t)0, size,
+ PROT_READ|PROT_WRITE, MAP_ANON|MAP_SHARED, -1, 0);
+ if (result == (void *)(caddr_t)-1) {
+ perror ("mmap");
+ exit (1);
+ }
+ return result;
+}
+#else
+#include <sys/types.h>
+#include <sys/ipc.h>
+#include <sys/shm.h>
+
+static void *get_shared_mem(size_t size)
+{
+ key_t shmkey = IPC_PRIVATE;
+ int shmid = -1;
+ void *result;
+#ifdef MOVEBREAK
+ char *obrk;
+#endif
+
+ if ((shmid = shmget(shmkey, size, IPC_CREAT | SHM_R | SHM_W)) == -1) {
+ perror("shmget");
+ exit(1);
+ }
+
+#ifdef MOVEBREAK
+ /*
+ * Some SysV systems place the shared segment WAY too close
+ * to the dynamic memory break point (sbrk(0)). This severely
+ * limits the use of malloc/sbrk in the program since sbrk will
+ * refuse to move past that point.
+ *
+ * To get around this, we move the break point "way up there",
+ * attach the segment and then move break back down. Ugly
+ */
+ if ((obrk = sbrk(MOVEBREAK)) == (char *) -1) {
+ perror("sbrk");
+ }
+#endif
+
+#define BADSHMAT ((void *)(-1))
+ if ((result = shmat(shmid, 0, 0)) == BADSHMAT) {
+ perror("shmat");
+ }
+ /*
+ * We must avoid leaving segments in the kernel's
+ * (small) tables.
+ */
+ if (shmctl(shmid, IPC_RMID, NULL) != 0) {
+ perror("shmctl(IPC_RMID)");
+ }
+ if (result == BADSHMAT) /* now bailout */
+ exit(1);
+
+#ifdef MOVEBREAK
+ if (obrk == (char *) -1)
+ return; /* nothing else to do */
+ if (sbrk(-(MOVEBREAK)) == (char *) -1) {
+ perror("sbrk 2");
+ }
+#endif
+ return result;
+}
+#endif
+
+#ifdef _POSIX_PRIORITY_SCHEDULING
+/* don't ask */
+#define _P __P
+#include <sched.h>
+#define YIELD sched_yield()
+#else
+#define YIELD do { struct timeval zero; zero.tv_sec = zero.tv_usec = 0; select(0,0,0,0,&zero); } while(0)
+#endif
+
+void main (int argc, char **argv)
+{
+ int num_iter;
+ int num_child;
+ int i;
+ struct timeval first;
+ struct timeval last;
+ long ms;
+ int pid;
+ unsigned long *shared_counter;
+
+ if (argc != 3) {
+ fprintf (stderr, "Usage: time-sem num-child num-iter\n");
+ exit (1);
+ }
+
+ num_child = atoi (argv[1]);
+ num_iter = atoi (argv[2]);
+
+ /* allocate shared memory for the shared_counter */
+ shared_counter = get_shared_mem(sizeof(*shared_counter));
+
+ /* initialize counter to 0 */
+ *shared_counter = 0;
+
+ accept_mutex_init ();
+
+ /* parent grabs mutex until done spawning children */
+ accept_mutex_on ();
+
+ for (i = 0; i < num_child; ++i) {
+ pid = fork();
+ if (pid == 0) {
+ /* child, do our thing */
+ accept_mutex_child_init();
+ for (i = 0; i < num_iter; ++i) {
+ unsigned long tmp;
+
+ accept_mutex_on ();
+ tmp = *shared_counter;
+ YIELD;
+ *shared_counter = tmp + 1;
+ accept_mutex_off ();
+ }
+ exit (0);
+ } else if (pid == -1) {
+ perror ("fork");
+ exit (1);
+ }
+ }
+
+ /* a quick test to see that nothing is screwed up */
+ if (*shared_counter != 0) {
+ puts ("WTF! shared_counter != 0 before the children have been started!");
+ exit (1);
+ }
+
+ gettimeofday (&first, NULL);
+ /* launch children into action */
+ accept_mutex_off ();
+ for (i = 0; i < num_child; ++i) {
+ if (wait(NULL) == -1) {
+ perror ("wait");
+ }
+ }
+ gettimeofday (&last, NULL);
+
+ if (*shared_counter != num_child * num_iter) {
+ printf ("WTF! shared_counter != num_child * num_iter!\n"
+ "shared_counter = %lu\nnum_child = %d\nnum_iter=%d\n",
+ *shared_counter,
+ num_child, num_iter);
+ }
+
+ last.tv_sec -= first.tv_sec;
+ ms = last.tv_usec - first.tv_usec;
+ if (ms < 0) {
+ --last.tv_sec;
+ ms += 1000000;
+ }
+ last.tv_usec = ms;
+ printf ("%8lu.%06lu\n", last.tv_sec, last.tv_usec);
+
+ accept_mutex_cleanup();
+
+ exit(0);
+}
+
--- /dev/null
+
+/* ZeusBench V1.01
+ ===============
+
+This program is Copyright (C) Zeus Technology Limited 1996.
+
+This program may be used and copied freely providing this copyright notice
+is not removed.
+
+This software is provided "as is" and any express or implied waranties,
+including but not limited to, the implied warranties of merchantability and
+fitness for a particular purpose are disclaimed. In no event shall
+Zeus Technology Ltd. be liable for any direct, indirect, incidental, special,
+exemplary, or consequential damaged (including, but not limited to,
+procurement of substitute good or services; loss of use, data, or profits;
+or business interruption) however caused and on theory of liability. Whether
+in contract, strict liability or tort (including negligence or otherwise)
+arising in any way out of the use of this software, even if advised of the
+possibility of such damage.
+
+ Written by Adam Twiss (adam@zeus.co.uk). March 1996
+
+Thanks to the following people for their input:
+ Mike Belshe (mbelshe@netscape.com)
+ Michael Campanella (campanella@stevms.enet.dec.com)
+
+*/
+
+/* -------------------- Notes on compiling ------------------------------
+
+This should compile unmodified using gcc on HP-UX, FreeBSD, Linux,
+IRIX, Solaris, AIX and Digital Unix (OSF). On Solaris 2.x you will
+need to compile with "-lnsl -lsocket" options. If you have any
+difficulties compiling then let me know.
+
+On SunOS 4.x.x you may need to compile with -DSUNOS4 to add the following
+two lines of code which appear not to exist in my SunOS headers */
+
+#ifdef SUNOS4
+extern char *optarg;
+extern int optind, opterr, optopt;
+#endif
+
+/* -------------------------------------------------------------------- */
+
+/* affects include files on Solaris */
+#define BSD_COMP
+
+#include <sys/time.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <string.h>
+
+/* ------------------- DEFINITIONS -------------------------- */
+
+/* maximum number of requests on a time limited test */
+#define MAX_REQUESTS 50000
+
+/* good old state machine */
+#define STATE_UNCONNECTED 0
+#define STATE_CONNECTING 1
+#define STATE_READ 2
+
+#define CBUFFSIZE 512
+
+struct connection
+{
+ int fd;
+ int state;
+ int read; /* amount of bytes read */
+ int bread; /* amount of body read */
+ int length; /* Content-Length value used for keep-alive */
+ char cbuff[CBUFFSIZE]; /* a buffer to store server response header */
+ int cbx; /* offset in cbuffer */
+ int keepalive; /* non-zero if a keep-alive request */
+ int gotheader; /* non-zero if we have the entire header in cbuff */
+ struct timeval start, connect, done;
+};
+
+struct data
+{
+ int read; /* number of bytes read */
+ int ctime; /* time in ms to connect */
+ int time; /* time in ms for connection */
+};
+
+#define min(a,b) ((a)<(b))?(a):(b)
+#define max(a,b) ((a)>(b))?(a):(b)
+
+/* --------------------- GLOBALS ---------------------------- */
+
+int requests = 1; /* Number of requests to make */
+int concurrency = 1; /* Number of multiple requests to make */
+int tlimit = 0; /* time limit in cs */
+int keepalive = 0; /* try and do keepalive connections */
+char *machine; /* Machine name */
+char *file; /* file name to use */
+char server_name[80]; /* name that server reports */
+int port = 80; /* port to use */
+
+int doclen = 0; /* the length the document should be */
+int totalread = 0; /* total number of bytes read */
+int totalbread = 0; /* totoal amount of entity body read */
+int done=0; /* number of requests we have done */
+int doneka=0; /* number of keep alive connections done */
+int good=0, bad=0; /* number of good and bad requests */
+
+/* store error cases */
+int err_length = 0, err_conn = 0, err_except = 0;
+
+struct timeval start, endtime;
+
+/* global request (and its length) */
+char request[512];
+int reqlen;
+
+/* one global throw-away buffer to read stuff into */
+char buffer[4096];
+
+struct connection *con; /* connection array */
+struct data *stats; /* date for each request */
+
+fd_set readbits, writebits; /* bits for select */
+struct sockaddr_in server; /* server addr structure */
+
+/* --------------------------------------------------------- */
+
+/* simple little function to perror and exit */
+
+static void err(char *s)
+{
+ perror(s);
+ exit(errno);
+}
+
+/* --------------------------------------------------------- */
+
+/* write out request to a connection - assumes we can write
+ (small) request out in one go into our new socket buffer */
+
+void write_request(struct connection *c)
+{
+ gettimeofday(&c->connect,0);
+ write(c->fd,request, reqlen);
+ c->state = STATE_READ;
+ FD_SET(c->fd, &readbits);
+ FD_CLR(c->fd, &writebits);
+}
+
+/* --------------------------------------------------------- */
+
+/* make an fd non blocking */
+
+void nonblock(int fd)
+{
+ int i=1;
+ ioctl(fd, FIONBIO, &i);
+}
+
+/* --------------------------------------------------------- */
+
+/* returns the time in ms between two timevals */
+
+int timedif(struct timeval a, struct timeval b)
+{
+ register int us,s;
+
+ us = a.tv_usec - b.tv_usec;
+ us /= 1000;
+ s = a.tv_sec - b.tv_sec;
+ s *= 1000;
+ return s+us;
+}
+
+/* --------------------------------------------------------- */
+
+/* calculate and output results and exit */
+
+void output_results()
+{
+ int timetaken;
+
+ gettimeofday(&endtime,0);
+ timetaken = timedif(endtime, start);
+
+ printf("\n---\n");
+ printf("Server: %s\n", server_name);
+ printf("Document Length: %d\n", doclen);
+ printf("Concurency Level: %d\n", concurrency);
+ printf("Time taken for tests: %d.%03d seconds\n",
+ timetaken/1000, timetaken%1000);
+ printf("Complete requests: %d\n", done);
+ printf("Failed requests: %d\n", bad);
+ if(bad) printf(" (Connect: %d, Length: %d, Exceptions: %d)\n",
+ err_conn, err_length, err_except);
+ if(keepalive) printf("Keep-Alive requests: %d\n", doneka);
+ printf("Bytes transfered: %d\n", totalread);
+ printf("HTML transfered: %d\n", totalbread);
+
+ /* avoid divide by zero */
+ if(timetaken) {
+ printf("Requests per seconds: %.2f\n", 1000*(float)(done)/timetaken);
+ printf("Transfer rate: %.2f kb/s\n",
+ (float)(totalread)/timetaken);
+ }
+
+ {
+ /* work out connection times */
+ int i;
+ int totalcon=0, total=0;
+ int mincon=9999999, mintot=999999;
+ int maxcon=0, maxtot=0;
+
+ for(i=0; i<requests; i++) {
+ struct data s = stats[i];
+ mincon = min(mincon, s.ctime);
+ mintot = min(mintot, s.time);
+ maxcon = max(maxcon, s.ctime);
+ maxtot = max(maxtot, s.time);
+ totalcon += s.ctime;
+ total += s.time;
+ }
+ printf("\nConnnection Times (ms)\n");
+ printf(" min avg max\n");
+ printf("Connect: %5d %5d %5d\n",mincon, totalcon/requests, maxcon );
+ printf("Total: %5d %5d %5d\n", mintot, total/requests, maxtot);
+ printf("---\n\n");
+ }
+
+ exit(0);
+}
+
+/* --------------------------------------------------------- */
+
+/* start asnchronous non-blocking connection */
+
+void start_connect(struct connection *c)
+{
+ c->read = 0;
+ c->bread = 0;
+ c->keepalive = 0;
+ c->cbx = 0;
+ c->gotheader = 0;
+
+ c->fd = socket(AF_INET, SOCK_STREAM, 0);
+ if(c->fd<0) err("socket");
+
+ nonblock(c->fd);
+ gettimeofday(&c->start,0);
+
+ if(connect(c->fd, (struct sockaddr *) &server, sizeof(server))<0) {
+ if(errno==EINPROGRESS) {
+ c->state = STATE_CONNECTING;
+ FD_SET(c->fd, &writebits);
+ return;
+ }
+ else {
+ close(c->fd);
+ err_conn++;
+ if(bad++>10) {
+ printf("\nTest aborted after 10 failures\n\n");
+ exit(1);
+ }
+ start_connect(c);
+ }
+ }
+
+ /* connected first time */
+ write_request(c);
+}
+
+/* --------------------------------------------------------- */
+
+/* close down connection and save stats */
+
+void close_connection(struct connection *c)
+{
+ if(c->read == 0 && c->keepalive) {
+ /* server has legitiamately shut down an idle keep alive request */
+ good--; /* connection never happend */
+ }
+ else {
+ if(good==1) {
+ /* first time here */
+ doclen = c->bread;
+ } else if (c->bread!=doclen) {
+ bad++;
+ err_length++;
+ }
+
+ /* save out time */
+ if(done < requests) {
+ struct data s;
+ gettimeofday(&c->done,0);
+ s.read = c->read;
+ s.ctime = timedif(c->connect, c->start);
+ s.time = timedif(c->done, c->start);
+ stats[done++] = s;
+ }
+ }
+
+ close(c->fd);
+ FD_CLR(c->fd, &readbits);
+ FD_CLR(c->fd, &writebits);
+
+ /* connect again */
+ start_connect(c);
+ return;
+}
+
+/* --------------------------------------------------------- */
+
+/* read data from connection */
+
+void read_connection(struct connection *c)
+{
+ int r;
+
+ r=read(c->fd,buffer,sizeof(buffer));
+ if(r==0 || (r<0 && errno!=EAGAIN)) {
+ good++;
+ close_connection(c);
+ return;
+ }
+
+ if(r<0 && errno==EAGAIN) return;
+
+ c->read += r;
+ totalread += r;
+
+ if(!c->gotheader) {
+ char *s;
+ int l=4;
+ int space = CBUFFSIZE - c->cbx - 1; /* -1 to allow for 0 terminator */
+ int tocopy = (space<r)?space:r;
+ memcpy(c->cbuff+c->cbx, buffer, space);
+ c->cbx += tocopy; space -= tocopy;
+ c->cbuff[c->cbx] = 0; /* terminate for benefit of strstr */
+ s = strstr(c->cbuff, "\r\n\r\n");
+ /* this next line is so that we talk to NCSA 1.5 which blatantly breaks
+ the http specifaction */
+ if(!s) { s = strstr(c->cbuff,"\n\n"); l=2; }
+
+ if(!s) {
+ /* read rest next time */
+ if(space)
+ return;
+ else {
+ /* header is in invalid or too big - close connection */
+ close(c->fd);
+ if(bad++>10) {
+ printf("\nTest aborted after 10 failures\n\n");
+ exit(1);
+ }
+ FD_CLR(c->fd, &writebits);
+ start_connect(c);
+ }
+ }
+ else {
+ /* have full header */
+ if(!good) {
+ /* this is first time, extract some interesting info */
+ char *p, *q;
+ p = strstr(c->cbuff, "Server:");
+ q = server_name;
+ if(p) { p+=8; while(*p>32) *q++ = *p++; }
+ *q = 0;
+ }
+
+ c->gotheader = 1;
+ *s = 0; /* terminate at end of header */
+ if(keepalive &&
+ (strstr(c->cbuff, "Keep-Alive")
+ || strstr(c->cbuff, "keep-alive"))) /* for benefit of MSIIS */
+ {
+ char *cl;
+ cl = strstr(c->cbuff, "Content-Length:");
+ /* for cacky servers like NCSA which break the spec and send a
+ lower case 'l' */
+ if(!cl) cl = strstr(c->cbuff, "Content-length:");
+ if(cl) {
+ c->keepalive=1;
+ c->length = atoi(cl+16);
+ }
+ }
+ c->bread += c->cbx - (s+l-c->cbuff) + r-tocopy;
+ totalbread += c->bread;
+ }
+ }
+ else {
+ /* outside header, everything we have read is entity body */
+ c->bread += r;
+ totalbread += r;
+ }
+
+ if(c->keepalive && (c->bread >= c->length)) {
+ /* finished a keep-alive connection */
+ good++; doneka++;
+ /* save out time */
+ if(good==1) {
+ /* first time here */
+ doclen = c->bread;
+ } else if(c->bread!=doclen) { bad++; err_length++; }
+ if(done < requests) {
+ struct data s;
+ gettimeofday(&c->done,0);
+ s.read = c->read;
+ s.ctime = timedif(c->connect, c->start);
+ s.time = timedif(c->done, c->start);
+ stats[done++] = s;
+ }
+ c->keepalive = 0; c->length = 0; c->gotheader=0; c->cbx = 0;
+ c->read = c->bread = 0;
+ write_request(c);
+ c->start = c->connect; /* zero connect time with keep-alive */
+ }
+}
+
+/* --------------------------------------------------------- */
+
+/* run the tests */
+
+int test()
+{
+ struct timeval timeout, now;
+ fd_set sel_read, sel_except, sel_write;
+ int i;
+
+ {
+ /* get server information */
+ struct hostent *he;
+ he = gethostbyname(machine);
+ if (!he) err("gethostbyname");
+ server.sin_family = he->h_addrtype;
+ server.sin_port = htons(port);
+ server.sin_addr.s_addr = ((unsigned long *)(he->h_addr_list[0]))[0];
+ }
+
+ con = malloc(concurrency*sizeof(struct connection));
+ memset(con,0,concurrency*sizeof(struct connection));
+
+ stats = malloc(requests * sizeof(struct data));
+
+ FD_ZERO(&readbits);
+ FD_ZERO(&writebits);
+
+ /* setup request */
+ sprintf(request,"GET %s HTTP/1.0\r\nUser-Agent: ZeusBench/1.0\r\n"
+ "%sHost: %s\r\nAccept: */*\r\n\r\n", file,
+ keepalive?"Connection: Keep-Alive\r\n":"", machine );
+
+ reqlen = strlen(request);
+
+ /* ok - lets start */
+ gettimeofday(&start,0);
+
+ /* initialise lots of requests */
+ for(i=0; i<concurrency; i++) start_connect(&con[i]);
+
+ while(done<requests) {
+ int n;
+ /* setup bit arrays */
+ memcpy(&sel_except, &readbits, sizeof(readbits));
+ memcpy(&sel_read, &readbits, sizeof(readbits));
+ memcpy(&sel_write, &writebits, sizeof(readbits));
+
+ /* check for time limit expiry */
+ gettimeofday(&now,0);
+ if(tlimit && timedif(now,start) > (tlimit*1000)) {
+ requests=done; /* so stats are correct */
+ output_results();
+ }
+
+ /* Timeout of 30 seconds. */
+ timeout.tv_sec=30; timeout.tv_usec=0;
+ n=select(256, &sel_read, &sel_write, &sel_except, &timeout);
+ if(!n) {
+ printf("\nServer timed out\n\n");
+ exit(1);
+ }
+ if(n<1) err("select");
+
+ for(i=0; i<concurrency; i++) {
+ int s = con[i].fd;
+ if(FD_ISSET(s, &sel_except)) {
+ bad++;
+ err_except++;
+ start_connect(&con[i]);
+ continue;
+ }
+ if(FD_ISSET(s, &sel_read)) read_connection(&con[i]);
+ if(FD_ISSET(s, &sel_write)) write_request(&con[i]);
+ }
+ if(done>=requests) output_results();
+ }
+ return 0;
+}
+
+/* ------------------------------------------------------- */
+
+/* display usage information */
+
+void usage(char *progname) {
+ printf("\nZeusBench v1.0\n\n");
+ printf("Usage: %s <machine> <file> [-k] [-n requests | -t timelimit (sec)]"
+ "\n\t\t[-c concurrency] [-p port] \n",progname);
+ printf("Filename should start with a '/' e.g. /index.html\n\n");
+ exit(EINVAL);
+}
+
+/* ------------------------------------------------------- */
+
+/* sort out command-line args and call test */
+
+int main(int argc, char **argv) {
+ int c;
+ if (argc < 3) usage(argv[0]);
+
+ machine = argv[1];
+ file = argv[2];
+ optind = 3;
+ while ((c = getopt(argc,argv,"p:n:c:d:t:d:k"))>0) {
+ switch(c) {
+ case 'd':
+ break;
+ case 'n':
+ requests = atoi(optarg);
+ if(!requests) {
+ printf("Invalid number of requests\n");
+ exit(1);
+ }
+ break;
+ case 'k':
+ keepalive=1;
+ break;
+ case 'c':
+ concurrency = atoi(optarg);
+ break;
+ case 'p':
+ port = atoi(optarg);
+ break;
+ case 't':
+ tlimit = atoi(optarg);
+ requests = MAX_REQUESTS; /* need to size data array on something */
+ break;
+ default:
+ usage(argv[0]);
+ break;
+ }
+ }
+ test();
+ return 0;
+}
+
+
+
+
+
+