@@ -245,7 +278,7 @@
# don't use Group #-1 on these systems!
#
User nobody
-Group #-1
+Group nobody
#
# ServerAdmin: Your address, where problems with the server should be
diff -Naur apache_2.0a4/htdocs/manual/mpm.html-orig apache_2.0a4/htdocs/manual/mpm.html
--- apache_2.0a4/htdocs/manual/mpm.html-orig Mon Jun 5 10:48:36 2000
+++ apache_2.0a4/htdocs/manual/mpm.html Tue Jul 25 13:57:53 2000
@@ -45,6 +45,11 @@
| This is Manoj's plaything. It has a number of hybrid features that Manoj has been looking at to improve performance. |
Manoj |
+
+| stm |
+State-threaded MPM.
+ | Mike Abbott, Accelerating Apache Project |
+
Windows
diff -Naur apache_2.0a4/htdocs/manual/stm.html-orig apache_2.0a4/htdocs/manual/stm.html
--- apache_2.0a4/htdocs/manual/stm.html-orig
+++ apache_2.0a4/htdocs/manual/stm.html Thu Jul 27 15:45:01 2000
@@ -0,0 +1,1074 @@
+
+
+ STM MPM
+
+
+
+
+STM: State-Threaded Multi-Processing Module for Apache/2.0
+
+
+Mike Abbott - mja@sgi.com
+Accelerating Apache Project
+
+
+Contents
+
+
+
+
+State
+threads are simple, fast, and scalable threads ideally suited for
+use in Apache/2.0. They combine the simplicity of the multithreaded
+programming paradigm, in which one thread supports one client
+connection, with the performance and scalability of an event-driven
+state machine architecture such as Zeus. In other words, state threads
+offer a threading API for structuring an Internet application like
+Apache/2.0 as a state machine.
+
+
Version 2.0 of the Apache HTTP Server introduces multi-processing modules (MPMs) which, on Unix-like
+platforms, mix processes and threads. Processes provide fault
+containment and recovery. An error, crash, or resource leak affects
+only one process not the whole server, and processes are easy to replace
+when they fault. Threads provide scalability and ease of programming.
+A server can maintain a large number of threads, with one thread per
+client connection.
+
+
The MPMs for Unix-like platforms use POSIX pthreads. Pthreads are
+well-known, widely available, and more or less standardized, and they
+perform and scale adequately for a large class of programming problems.
+However, they are not the right choice for Apache/2.0.
+
+
Programming pthreads is complex. Not only Apache developers but also
+independent module writers and patch contributors must design and code
+around race conditions, deadlocks, and data corruption, and must use
+only thread-safe (reentrant) library routines. Debugging pthreaded
+applications can be tricky. Furthermore, pthreads implementations
+differ sufficiently to complicate programming and hinder portability,
+and pthreads are either available for a given platform or not. Very few
+people are going to write a complete pthreads library for their system
+if there isn't one already present.
+
+
Pthreads can be slow. They are preemptive and concurrent and always
+share data, mandating cycle-sapping mutex locking. Libraries sometimes
+use locks to protect hidden resources (such as malloc/free)
+which can be difficult or inefficient to work around.
+
+
Pthreads scale poorly in the presence of blocking I/O, which
+proliferates kernel execution vehicles and slows kernel-level
+scheduling. (Non-blocking I/O with pthreads also suffers because
+select still blocks the kernel thread.)
+
+
State threads are simple to use because they are non-preemptive and
+non-concurrent. They need no mutual exclusion locking and can use all
+the static variables and non-reentrant library functions they want.
+Programmers need not worry about race conditions or deadlocks. State
+threads are available for many Unix-like platforms and since the
+complete library is open source it can support additional platforms
+easily.
+
+
State threads are fast because they schedule entirely in user mode
+and since mutex locking is unnecessary they waste no time contending for
+locks.
+
+
State threads are scalable because they take advantage of hardware
+concurrency (such as multiple CPUs) without interfering with each other
+at all by running in separate processes -- which also provides fault
+containment.
+
+
State threads are the best threads for Apache/2.0 because they
+provide exactly the abstractions around which Apache/2.0 is designed --
+one lightweight scheduling entity per client connection, fault
+containment and recovery, high performance and scalability -- and add
+simplicity of programming, ease of debugging, and the promise of
+extensive portability, without suffering from the complexity of more
+traditional thread packages.
+
+
+
+The state-threaded multi-processing module (STM MPM) for Apache/2.0
+uses a multi-process, multi-threaded (MPMT) architecture similar to the
+standard MPMT MPMs except that it uses state threads instead of
+pthreads. The number of processes is constant and the number of threads
+per process varies with the load against the server. In state thread
+lingo, each process is a virtual processor (VP) managing its own
+independent set of state threads. Each state thread listens to, and
+processes connections from, exactly one listening socket.
+
+
The STM's one-thread-per-socket design is different from the standard
+MPMs, in which all threads listen to all sockets simultaneously. This
+subtly changes the meaning of some configuration
+directives (such as MinSpareThreads).
+
+
In addition to MPMs Apache/2.0 introduces I/O layers. The STM
+includes its own state-threaded I/O layer called STIOL, which simply
+directs all I/O operations on sockets to the state thread library's I/O
+routines, a crucial and required part of using state threads.
+
+
Informal performance measurements reveal that the STM is faster than
+any other standard MPM on all the systems it currently supports.
+
+
It is a credit to the Apache developers that integrating state
+threads into Apache/2.0 was easy and (mostly) clean.
+
+
+
+Virtual processors (processes) may be bound to particular CPUs to
+improve cache utilization. Cache utilization is further improved when
+VP binding combines with listen binding and the
+interrupts from the network interfaces bound to a VP are bound to the
+same CPU as the VP.
+
+The STM's VPBind configuration
+directive binds a VP to a specific CPU.
+
+
+
+Virtual processors may listen to all or a subset of all the listen
+sockets to reduce contention on each socket. Cache utilization also is
+improved when listen binding combines with VP
+binding and the interrupts from the network interfaces bound to a VP
+are bound to the same CPU as the VP.
+
+When only one VP listens to each socket, accept serialization is no
+longer necessary, further improving performance. Note that this does
+not imply that a VP must listen to only one socket.
+
+
The STM's VPListen configuration
+directive binds listeners (also called accepting or listening
+sockets) to a VP.
+
+
+
+The STM comes in the form of one or more patches against Apache/2.0
+from SGI's open source Accelerating Apache
+Project. It requires the state threads library, which comes in the
+form of a complete distribution from SGI's open source State Threads
+Project. First download all the software (Apache, patches,
+library), apply the patches, and build and install the state threads
+library.
+
+
It's a good idea to try out the STM with STM_DEBUG defined at first, to catch internal
+inconsistencies, especially on new platforms. Please report errors
+and contribute
+fixes.
+
+
+
+The STM and STIOL support these compile-time configuration options.
+To specify these options, define them with CPPFLAGS to
+configure, like this:
+
+
+ % env CPPFLAGS="-DSTM_LISTENER_LIMIT=2 -DSTM_DEBUG -DIRIX=65" \
+ configure --with-mpm=stm ...
+
+
+
+
+
+ IRIX
+ |
+
+
+ The version of SGI's Irix operating system, encoded as a
+ two-digit number, to take advantage of certain
+ high-performance features. For example, for Irix 6.2 use
+ IRIX=62, and for Irix 6.5.x use
+ IRIX=65. Use only on Irix systems.
+
+ Note: The presence of this option does not mean that the
+ features it enables are available only on Irix, just that
+ the STM leverages them currently only on Irix. Other
+ operating systems could have their own similar options if
+ someone contributes
+ the code.
+
+ Default: IRIX is not defined.
+
+ |
+
+
+
+ STIOL_DEBUG
+ |
+
+
+ Enables internal consistency checks and debugging features within the STIOL. Only
+ the presence or absence of this token is meaningful; the
+ value is ignored.
+
+ Default: STIOL_DEBUG is not defined so
+ debugging is disabled.
+
+ |
+
+
+
+ STM_DEBUG
+ |
+
+
+ Enables internal consistency checks and debugging features within the STM. Only
+ the presence or absence of this token is meaningful; the
+ value is ignored.
+
+ Default: STM_DEBUG is not defined so
+ debugging is disabled.
+
+ |
+
+
+
+ STM_LISTENER_LIMIT
+ |
+
+
+ Maximum number of listeners (Listen or VPListen directives, also known
+ as accepting or listening sockets).
+
+ Default: 8
+
+ |
+
+
+
+ STM_SCORE_KEY_SIZE
+ |
+
+
+ Maximum length in bytes of the key string in the scoreboards' connection status tables,
+ including the string-terminating null. Keys longer than
+ this will be truncated. It's probably a good idea to make
+ STM_SCORE_KEY_SIZE + STM_SCORE_VALUE_SIZE equal
+ to one or more times the size of your system's largest cache
+ line.
+
+ Default: 16
+
+ |
+
+
+
+ STM_SCORE_LIMIT
+ |
+
+
+ Maximum number of key/value pairs in the scoreboards' connection status tables.
+ When the table fills additional insertions are ignored.
+
+ Default: 16
+
+ |
+
+
+
+ STM_SCORE_VALUE_SIZE
+ |
+
+
+ Maximum length in bytes of the value string in the scoreboards' connection status tables,
+ including the string-terminating null. Values longer than
+ this will be truncated. It's probably a good idea to make
+ STM_SCORE_KEY_SIZE + STM_SCORE_VALUE_SIZE equal
+ to one or more times the size of your system's largest cache
+ line.
+
+ Default: 48
+
+ |
+
+
+
+ STM_ST_LIMIT
+ |
+
+
+ Maximum number of state threads per virtual processor (upper
+ bound on MaxThreads). The server-status page reports the maximum
+ number of threads actually used at any time, which can be
+ used to tune this value optimally for your system and
+ workload. However, lingering closes can artificially
+ inflate the reported maximum.
+
+ Default: 512
+
+ |
+
+
+
+ STM_VP_LIMIT
+ |
+
+
+ Maximum number of virtual processors.
+
+ Default: 8
+
+ |
+
+
+
+
+
+The STM supports these run-time configuration directives, specified
+in the file conf/httpd.conf. To invoke these directives
+edit the STM section of that file, like this:
+
+
+ <IfModule stm.c>
+ NumVPs 4
+ StartThreads 8
+ MinSpareThreads 8
+ MaxSpareThreads 32
+ MaxThreads 64
+ StackSize 65536
+ VPConnections 0
+ VPBind 0 0
+ VPListen 0 100.100.100.1:80
+ VPBind 1 0
+ VPListen 1 100.100.101.1:80
+ VPBind 2 1
+ VPListen 2 100.100.102.1:80
+ VPBind 3 1
+ VPListen 3 100.100.103.1:80
+ ConnectionStatus on
+ ScoreboardDir scoreboards
+ # CoreDir cores
+ </IfModule>
+
+
+The above example is for a two-CPU system with four network
+interfaces. On this system the interrupts from interface 100.100.100.1
+are bound to CPU 0, 100.100.102.1 to CPU 1, and so on.
+
+
+
+
+
+ ConnectionStatus
+ |
+
+
+ Flag enabling or disabling per-connection status in scoreboards (they can be slow to
+ maintain).
+
+ Default: on
+
+ |
+
+
+
+ CoreDir
+ |
+
+
+ Path of a directory where Apache will dump core if
+ necessary. The directory should exist before Apache
+ startup.
+
+ Default: server root directory
+
+ |
+
+
+
+ MaxSpareThreads
+ |
+
+
+ Maximum number of spare state threads per socket --
+ not per VP. Spare threads are threads awaiting a connection
+ (as opposed to processing requests on established
+ connections). Must be between 1 and
+ STM_ST_LIMIT inclusive. Should be between
+ 1 and MaxThreads divided by the
+ number of listen sockets inclusive. Making this too small
+ can cause long latency for clients; too big and you waste
+ CPU cycles on low-volume servers.
+
+ Default: 10
+
+ |
+
+
+
+ MaxThreads
+ |
+
+
+ Maximum number of state threads -- spare or busy -- per VP
+ across all sockets. Must be between 1 and
+ STM_ST_LIMIT inclusive. Making this too small
+ can cause long latency for clients, especially if your site
+ is largely dynamic (like CGI); too big and you waste memory.
+
+ Default: 64
+
+ |
+
+
+
+ MinSpareThreads
+ |
+
+
+ Minimum number of spare state threads per socket --
+ not per VP. Spare threads are threads awaiting a connection
+ (as opposed to processing requests on established
+ connections). Must be between 1 and
+ STM_ST_LIMIT inclusive. Should be between
+ 1 and MaxThreads divided by the
+ number of listen sockets inclusive. Making this too small
+ can cause long latency for clients; too big and you waste
+ CPU cycles on low-volume servers.
+
+ Default: 5
+
+ |
+
+
+
+ NumVPs
+ |
+
+
+ Number of virtual processors. Must be between
+ 1 and STM_VP_LIMIT inclusive.
+ Only one or a few are needed per CPU. The server starts a
+ replacement when a VP dies so the number running is
+ constant. Making this too small can cause long latency for
+ clients; too big and you waste CPU cycles and memory.
+
+ Default: 4
+
+ |
+
+
+
+ PidFile
+ |
+
+
+ Path of the file where Apache writes the process ID (PID) of
+ the master process. The file may or may not exist before
+ startup.
+
+ Default: server-root-directory/logs/httpd.pid
+
+ |
+
+
+
+ ScoreboardDir
+ |
+
+
+ Path of the directory where the STM creates scoreboard files for each VP. The
+ directory must already exist before Apache startup. The
+ scoreboard files themselves may or may not pre-exist and
+ have names that are just the ordinal of the VP.
+
+ Default: server-root-directory/scoreboards
+
+ |
+
+
+
+ StackSize
+ |
+
+
+ Size in bytes of each thread's stack. Must be between the
+ system's page size and INT_MAX inclusive.
+ Making this too small can cause stack overflow errors; too
+ big and you waste memory.
+
+ Default: 65536 (64 KB)
+
+ |
+
+
+
+ StartThreads
+ |
+
+
+ Number of state threads each VP should start initially
+ per socket -- not per VP. Must be between
+ 1 and STM_ST_LIMIT inclusive.
+ Should be between 1 and MaxThreads
+ divided by the number of listen sockets inclusive.
+ Generally this value should be the same as
+ MinSpareThreads but it can be higher to
+ facilitate Apache restarts on high-volume servers.
+
+ Default: 5
+
+ |
+
+
+
+ VPBind
+ |
+
+
+ Binding VPs to CPUs.
+
+ Syntax: VPBind VP-ID
+ CPU-ID
+
+ where VP-ID is the number of the
+ virtual processor, from 0 to NumVPs -
+ 1 inclusive, and CPU-ID is the
+ number of the CPU to which to bind that VP, from
+ 0 to the number of CPUs in your system - 1,
+ inclusive. You may have multiple VPBind
+ directives to bind some or all of the VPs. If more than one
+ VPBind directive specifies the same
+ VP-ID the last one overrides all the
+ previous ones.
+
+ Default: VPBind is not used so no binding is
+ in effect.
+
+ |
+
+
+
+ VPConnections
+ |
+
+
+ Number of connections (not requests) each VP should serve
+ before exiting gracefully and allowing the master process to
+ replace it to reclaim resources such as memory or file
+ descriptors leaked by faulty modules. Must be between
+ 0 and INT_MAX inclusive. The
+ value 0 means the number of connections is
+ unlimited and VPs should never voluntarily exit.
+
+ Default: 0
+ |
+
+
+
+ VPListen
+ |
+
+
+ Binding listeners to VPs.
+
+ Syntax: VPListen VP-ID
+ IP-address:port ...
+
+ where VP-ID is the number of the
+ virtual processor, from 0 to NumVPs -
+ 1 inclusive, and
+ IP-address:port specifies the
+ network address (in dot notation) and port number to which
+ that VP should listen, separated by a colon (:). You may
+ specify more than one address and port. You may have
+ multiple VPListen directives. If more than one
+ VPListen directive specifies the same
+ VP-ID they all take effect.
+
+ VPListen and Listen directives
+ cannot be mixed. If any VP has listeners bound to it, then
+ all such bindings must be explicit and every VP must have at
+ least one listener bound to it.
+
+
Default: VPListen is not used so no binding
+ is in effect.
+
+ |
+
+
+
+
+
+Compiling the STM with STM_DEBUG defined enables
+internal consistency checks and use of its debugging features.
+Compiling with STIOL_DEBUG defined enables use of the
+STIOL's debugging features. To activate these features, define the
+variable in the environment in which Apache will run, like this:
+
+
+ % env STM_INTERACTIVE=1 STM_TRACE=1 bin/httpd ...
+
+
+By default, all of the following are not defined and therefore their
+debugging features are deactivated.
+
+
+
+
+
+ ONE_PROCESS
+ |
+
+
+ Limits the STM to use only one process, to simplify use of a
+ debugger. This feature overrides the NumVPs
+ configuration directive. Normally the STM uses one process
+ per VP plus one master process.
+
+ Use this feature only for debugging as it degrades
+ performance and disables fault recovery.
+
+ |
+
+
+
+ STIOL_TRACE
+ |
+
+
+ Causes the STIOL to print lots of debugging
+ information, including one line at the beginning of each
+ procedure call within the STIOL.
+
+ Use with STM_INTERACTIVE to see all the
+ output, otherwise output will stop when the server detaches
+ from the controlling terminal.
+
+ Use this feature only for debugging as it severely
+ degrades performance.
+
+ |
+
+
+
+ STM_INTERACTIVE
+ |
+
+
+ Forces the STM to run interactively, that is, attached to
+ the controlling terminal and session for normal standard I/O
+ and keyboard signalling, not in the background detached from
+ I/O with the terminal as during normal operation.
+
+ |
+
+
+
+ STM_TRACE
+ |
+
+
+ Causes the STM to print lots of debugging
+ information, including one line at the beginning of each
+ procedure call within the STM.
+
+ Use with STM_INTERACTIVE to see all the
+ output, otherwise output will stop when the server detaches
+ from the controlling terminal.
+
+ Use this feature only for debugging as it severely
+ degrades performance.
+
+ |
+
+
+
+
+
+Scoreboards contain status information and counters. Each virtual
+processor maintains its own scoreboard. Scoreboards are kept in
+memory-mapped disk files (as opposed to anonymous shared memory) so that
+support tools such as stmstat (included with STM) can read
+scoreboard data without causing Apache to serve a page. Most of the
+scoreboard data is also available on the server-status page from mod_status.
+
+Unfortunately, at this time (alpha 4) the programming interface to
+mod_status is limited to key-value pairs about client connections
+identified by a single number, and the page it generates has a format
+fixed by this interface. For the STM to report information not directly
+related to connections it resorts to using negative connection numbers
+so the resulting page is ugly. The author hopes to extend this
+interface someday. Or you could do it and contribute
+your changes.
+
+
This is what the STM's status information looks like,
+annotated. The output from the stmstat tool is
+similar but less polished. The configuration file for this server
+includes the example from the section on configuration directives.
+
+
+
+ Apache Server Status for myserver
+
+ Server Version: Apache/2.0a4
+ Server Built: Jul 27 2000 11:33:03
+
+ Current Time: Thursday, 27-Jul-2000 12:35:12 PDT
+
+ 87 connections currently being processed
+ Connection -1
+ Total connections served
+ 130573
+ Most threads used
+ 39
+ Total connections served for 100.100.100.1:80
+ 31798
+ Total connections served for 100.100.101.1:80
+ 32986
+ Total connections served for 100.100.102.1:80
+ 33115
+ Total connections served for 100.100.103.1:80
+ 32674
+
+Connection -1 is a summary of all STM activity across all virtual
+processors and state threads. This section shows the total number of
+client connections processed since server start and also the number per
+listener (accepting socket), and the maximum number of threads ever used
+on any single VP (useful for fine-tuning
+STM_ST_LIMIT).
+
+ Connection -1000
+ Process ID
+ 7008
+ Start time
+ Thu Jul 27 12:31:47 2000
+ Incarnations
+ 1
+ Thread starts
+ 39
+ Thread exits
+ 0
+ Most threads used at once
+ 39
+ Connections served, prior incarnations
+ 0
+ Connections served, this incarnation
+ 31798
+ Connections served for 100.100.100.1:80
+ 31798
+ Connections served by thread 0
+ 843
+ Connections served by thread 1
+ 805
+ Connections served by thread 2
+ 1085
+
+... threads 3-62 elided for brevity ...
+
+ Connections served by thread 63
+ 0
+
+Connection -1000 is a summary of activity for virtual processor
+#0. More generally, info for VP #n comes under the heading for
+connection -1000 - n. The process ID and start time are
+self-explanatory. Incarnations reports the number of different VP's
+inhabiting this slot, that is, the number of times a VP has been started
+to replace a dead one, including the initial VP. (The STM restarts VPs
+when the die, which can be voluntarily by exceeding its
+VPConnections limit or unexpectedly by crashing.) Next
+comes thread activity and connection activity. Connections served is
+split by incarnations so you can see how many connections remain before
+the VP reaches its VPConnections limit. Connections served
+also is broken down by listener (this VP happens to listen to only one
+socket) and by thread, to give a really detailed understanding of server
+activity.
+
+ Connection 0
+ Status
+ Writing
+ Method
+ GET
+ Protocol
+ HTTP/1.0
+ Connection 4
+ Status
+ Writing
+ Method
+ GET
+ Protocol
+ HTTP/1.0
+ Connection 8
+ Status
+ Writing
+ Method
+ GET
+ Protocol
+ HTTP/1.0
+
+... a bunch of normal connection status elided for brevity ...
+
+
+ Connection 35
+ Status
+ Writing
+ Method
+ GET
+ Protocol
+ HTTP/1.0
+
+These are the connections that VP #0 is currently processing.
+These are present only when ConnectionStatus is
+on.
+
+ Connection -1001
+ Process ID
+ 7011
+ Start time
+ Thu Jul 27 12:31:47 2000
+ Incarnations
+ 1
+ Thread starts
+ 39
+ Thread exits
+ 0
+ Most threads used at once
+ 39
+ Connections served, prior incarnations
+ 0
+ Connections served, this incarnation
+ 32986
+ Connections served for 100.100.101.1:80
+ 32986
+ Connections served by thread 0
+ 957
+ Connections served by thread 1
+ 765
+ Connections served by thread 2
+ 747
+
+... threads 3-62 elided for brevity ...
+
+ Connections served by thread 63
+ 0
+
+Connection -1000 is a summary of activity for VP #1. This VP
+happens to listen to a different address than VP #0.
+
+ Connection 64
+ Status
+ Writing
+ Method
+ GET
+ Protocol
+ HTTP/1.0
+ Connection 66
+ Status
+ Writing
+ Method
+ GET
+ Protocol
+ HTTP/1.0
+
+... a bunch of normal connection status elided for brevity ...
+
+
+ Connection 102
+ Status
+ Writing
+ Method
+ GET
+ Protocol
+ HTTP/1.0
+
+These are the connections that VP #1 is currently
+processing.
+
+ Connection -1002
+ Process ID
+ 7009
+ Start time
+ Thu Jul 27 12:31:47 2000
+ Incarnations
+ 1
+ Thread starts
+ 39
+ Thread exits
+ 0
+ Most threads used at once
+ 39
+ Connections served, prior incarnations
+ 0
+ Connections served, this incarnation
+ 33115
+ Connections served for 100.100.102.1:80
+ 33115
+ Connections served by thread 0
+ 674
+ Connections served by thread 1
+ 821
+ Connections served by thread 2
+ 923
+
+... threads 3-62 elided for brevity ...
+
+ Connections served by thread 63
+ 0
+ Connection 128
+ Status
+ Writing
+ Method
+ GET
+ Protocol
+ HTTP/1.0
+ Connection 130
+ Status
+ Writing
+ Method
+ GET
+ Protocol
+ HTTP/1.0
+
+... a bunch of normal connection status elided for brevity ...
+
+
+ Connection 164
+ Status
+ Writing
+ Method
+ GET
+ Protocol
+ HTTP/1.0
+
+This is VP #2.
+
+ Connection -1003
+ Process ID
+ 7012
+ Start time
+ Thu Jul 27 12:31:47 2000
+ Incarnations
+ 1
+ Thread starts
+ 41
+ Thread exits
+ 2
+ Most threads used at once
+ 39
+ Connections served, prior incarnations
+ 0
+ Connections served, this incarnation
+ 32674
+ Connections served for 100.100.103.1:80
+ 32674
+ Connections served by thread 0
+ 617
+ Connections served by thread 1
+ 656
+ Connections served by thread 2
+ 1111
+
+... threads 3-62 elided for brevity ...
+
+ Connections served by thread 63
+ 0
+ Connection 193
+ Status
+ Writing
+ Method
+ GET
+ Protocol
+ HTTP/1.0
+ Connection 194
+ Status
+ Writing
+ Method
+ GET
+ Protocol
+ HTTP/1.0
+
+... a bunch of normal connection status elided for brevity ...
+
+
+ Connection 227
+ Status
+ Writing
+ Method
+ GET
+ Protocol
+ HTTP/1.0
+
+This is VP #3.
+
+ |
+
+
+
+Additional information is available from these sources.
+
+
+
+Copyright © 2000 Silicon Graphics, Inc.
+All rights reserved.
+
+
+
diff -Naur apache_2.0a4/src/Makefile.in-orig apache_2.0a4/src/Makefile.in
--- apache_2.0a4/src/Makefile.in-orig Tue May 16 03:57:27 2000
+++ apache_2.0a4/src/Makefile.in Wed Jul 19 16:36:24 2000
@@ -70,6 +70,7 @@
@cp -p $(builddir)/support/rotatelogs $(bindir)
@cp -p $(builddir)/support/logresolve $(bindir)
@cp -p $(builddir)/support/ab $(bindir)
+ @cp -p $(builddir)/support/stmstat $(bindir)
install-other:
@test -d $(logdir) || $(mkinstalldirs) $(logdir)
@@ -79,9 +80,9 @@
@test -d $(includedir) || $(mkinstalldirs) $(includedir)
@test -d $(includedir)/xml || $(mkinstalldirs) $(includedir)/xml
@test -d $(includedir)/apr || $(mkinstalldirs) $(includedir)/apr
- @cp -p include/*.h $(srcdir)/include/*.h $(includedir)
+ @cp -p $(srcdir)/include/*.h $(includedir)
@cp -p $(srcdir)/os/$(OS_DIR)/os.h $(includedir)
@cp -p $(srcdir)/os/$(OS_DIR)/os-inline.c $(includedir)
@cp -p $(srcdir)/lib/expat-lite/*.h $(includedir)/xml
- @cp -p lib/apr/include/*.h $(srcdir)/lib/apr/include/*.h $(includedir)/apr
+ @cp -p $(srcdir)/lib/apr/include/*.h $(includedir)/apr
@chmod 644 $(includedir)/*.h $(includedir)/xml/*.h $(includedir)/apr/*.h
diff -Naur apache_2.0a4/src/README.MPM-orig apache_2.0a4/src/README.MPM
--- apache_2.0a4/src/README.MPM-orig Mon Mar 13 10:44:41 2000
+++ apache_2.0a4/src/README.MPM Mon Jul 24 21:01:22 2000
@@ -8,7 +8,7 @@
To build (from this directory):
./buildconf
-./configure --with-mpm={dexter,mpmt_pthread,prefork,...}
+./configure --with-mpm={dexter,stm,mpmt_pthread,prefork,...}
make
Read ./configure --help for more command line options
diff -Naur apache_2.0a4/src/ap/ap_hooks.c-orig apache_2.0a4/src/ap/ap_hooks.c
--- apache_2.0a4/src/ap/ap_hooks.c-orig Sat May 27 15:40:19 2000
+++ apache_2.0a4/src/ap/ap_hooks.c Tue Jul 18 16:34:16 2000
@@ -115,6 +115,51 @@
return pHead;
}
+#ifdef MJA_DEBUG_HOOKS
+void
+dumpsort(TSort *s, int n)
+{
+ int i, j;
+
+ printf("0x%08x has %d elements <\n", s, n);
+ for (i = 0; i < n; i++) {
+ printf(" [%d] = 0x%08x -> (TSort) {\n", i, &s[i]);
+ printf(" pData = 0x%08x -> (TSortData) {\n", s[i].pData);
+ if (s[i].pData) {
+ printf(" dummy (pFunc) = 0x%08x\n", ((TSortData *) s[i].pData)->dummy);
+ printf(" szName = 0x%08x = \"%1$s\"\n", ((TSortData *) s[i].pData)->szName);
+ printf(" aszPredecessors = 0x%08x -> (char *) {\n", ((TSortData *) s[i].pData)->aszPredecessors);
+ if (((TSortData *) s[i].pData)->aszPredecessors) {
+ for (j = 0; ((TSortData *) s[i].pData)->aszPredecessors[j]; j++)
+ printf(" [%d] = 0x%08x = \"%2$s\"\n", j, ((TSortData *) s[i].pData)->aszPredecessors[j]);
+ printf(" NULL\n");
+ }
+ printf(" }\n");
+ printf(" aszSuccessors = 0x%08x -> (char *) {\n", ((TSortData *) s[i].pData)->aszSuccessors);
+ if (((TSortData *) s[i].pData)->aszSuccessors) {
+ for (j = 0; ((TSortData *) s[i].pData)->aszSuccessors[j]; j++)
+ printf(" [%d] = 0x%08x = \"%2$s\"\n", j, ((TSortData *) s[i].pData)->aszSuccessors[j]);
+ printf(" NULL\n");
+ }
+ printf(" }\n");
+ printf(" nOrder = %d\n", ((TSortData *) s[i].pData)->nOrder);
+ }
+ printf(" }\n");
+ printf(" nPredecessors = %d\n", s[i].nPredecessors);
+ printf(" ppPredecessors = 0x%08x -> (TSort) {\n", s[i].ppPredecessors);
+ if (s[i].ppPredecessors) {
+ for (j = 0; s[i].ppPredecessors[j]; j++)
+ printf(" [%d] = 0x%08x\n", j, s[i].ppPredecessors[j]);
+ }
+ printf(" NULL\n");
+ printf(" }\n");
+ printf(" pNext = 0x%08x\n", s[i].pNext);
+ printf(" }\n");
+ }
+ printf(">\n");
+}
+#endif
+
static ap_array_header_t *sort_hook(ap_array_header_t *pHooks,const char *szName)
{
ap_pool_t *p;
@@ -124,7 +169,19 @@
ap_create_pool(&p, ap_global_hook_pool);
pSort=prepare(p,(TSortData *)pHooks->elts,pHooks->nelts);
+#ifdef MJA_DEBUG_HOOKS
+ if (ap_debug_module_hooks) {
+ printf("pre-sort:\n");
+ dumpsort(pSort, pHooks->nelts);
+ }
+#endif
pSort=tsort(pSort,pHooks->nelts);
+#ifdef MJA_DEBUG_HOOKS
+ if (ap_debug_module_hooks) {
+ printf("post-sort:\n");
+ dumpsort(pSort, pHooks->nelts);
+ }
+#endif
pNew=ap_make_array(ap_global_hook_pool,pHooks->nelts,sizeof(TSortData));
if(ap_debug_module_hooks)
printf("Sorting %s:",szName);
diff -Naur apache_2.0a4/src/lib/apr/dso/unix/Makefile.in-orig apache_2.0a4/src/lib/apr/dso/unix/Makefile.in
--- apache_2.0a4/src/lib/apr/dso/unix/Makefile.in-orig Sun Apr 30 10:58:25 2000
+++ apache_2.0a4/src/lib/apr/dso/unix/Makefile.in Wed Jul 19 15:07:03 2000
@@ -10,7 +10,7 @@
LIBS=@LIBS@
LDFLAGS=@LDFLAGS@ $(LIBS)
INCDIR=../../include
-INCLUDES=-I$(INCDIR) -I$(INCDIR1) -I.
+INCLUDES=-I$(INCDIR) -I.
LIB=libdso.a
diff -Naur apache_2.0a4/src/lib/apr/locks/unix/crossproc.c-orig apache_2.0a4/src/lib/apr/locks/unix/crossproc.c
--- apache_2.0a4/src/lib/apr/locks/unix/crossproc.c-orig Fri Jun 2 20:30:10 2000
+++ apache_2.0a4/src/lib/apr/locks/unix/crossproc.c Wed Jul 19 14:56:49 2000
@@ -130,7 +130,7 @@
return stat;
}
-ap_status_t ap_unix_child_init_lock(ap_lock_t **lock, ap_pool_t *cont, char *fname)
+ap_status_t ap_unix_child_init_lock(ap_lock_t **lock, ap_pool_t *cont, const char *fname)
{
return APR_SUCCESS;
}
@@ -230,7 +230,7 @@
return stat;
}
-ap_status_t ap_unix_child_init_lock(ap_lock_t **lock, ap_pool_t *cont, char *fname)
+ap_status_t ap_unix_child_init_lock(ap_lock_t **lock, ap_pool_t *cont, const char *fname)
{
return APR_SUCCESS;
}
diff -Naur apache_2.0a4/src/main/Makefile.in-orig apache_2.0a4/src/main/Makefile.in
--- apache_2.0a4/src/main/Makefile.in-orig Thu May 25 13:51:23 2000
+++ apache_2.0a4/src/main/Makefile.in Wed Jul 19 15:16:40 2000
@@ -26,5 +26,5 @@
test_char.h: gen_test_char
./gen_test_char > test_char.h
-$(srcdir)/util_uri.c: uri_delims.h
-$(srcdir)/util.c: test_char.h
+$(srcdir)/util_uri.o: uri_delims.h
+$(srcdir)/util.o: test_char.h
diff -Naur apache_2.0a4/src/main/mpm_common.c-orig apache_2.0a4/src/main/mpm_common.c
--- apache_2.0a4/src/main/mpm_common.c-orig Thu Jun 1 11:42:04 2000
+++ apache_2.0a4/src/main/mpm_common.c Wed Jul 19 15:35:16 2000
@@ -74,6 +74,8 @@
#include "mpm.h"
#include "mpm_common.h"
+#ifndef UNCOMMON_MPM
+
#if defined(DEXTER_MPM) || defined(MPMT_BEOS_MPM)
#define CHILD_TABLE 1
#define CHILD_INFO_TABLE ap_child_table
@@ -212,4 +214,4 @@
return;
}
-
+#endif /* UNCOMMON_MPM */
diff -Naur apache_2.0a4/src/modules/mpm/MPM.NAMING-orig apache_2.0a4/src/modules/mpm/MPM.NAMING
--- apache_2.0a4/src/modules/mpm/MPM.NAMING-orig Thu Aug 12 11:47:11 1999
+++ apache_2.0a4/src/modules/mpm/MPM.NAMING Wed Jul 26 23:18:34 2000
@@ -29,6 +29,7 @@
mpmt_pthread .. Multi Process Model with Threading via Pthreads
Variable number of processes, constant number of
threads/child (= Apache/pthread)
+ stm ........... State-threaded. See htdocs/manual/stm.html.
spmt_os2 ...... Single Process Model with Threading on OS/2
winnt ......... Single Process Model with Threading on Windows NT
diff -Naur apache_2.0a4/src/modules/mpm/config.m4-orig apache_2.0a4/src/modules/mpm/config.m4
--- apache_2.0a4/src/modules/mpm/config.m4-orig Sat Apr 29 19:31:07 2000
+++ apache_2.0a4/src/modules/mpm/config.m4 Mon Jul 24 21:01:22 2000
@@ -1,7 +1,7 @@
AC_MSG_CHECKING(which MPM to use)
AC_ARG_WITH(mpm,
[ --with-mpm=MPM Choose the process model for Apache to use.
- MPM={dexter,mpmt_beos,mpmt_pthread,prefork,spmt_os2}],[
+ MPM={dexter,stm,mpmt_beos,mpmt_pthread,prefork,spmt_os2}],[
APACHE_MPM=$withval
],[
APACHE_MPM=mpmt_pthread
diff -Naur apache_2.0a4/src/modules/mpm/stm/Makefile.in-orig apache_2.0a4/src/modules/mpm/stm/Makefile.in
--- apache_2.0a4/src/modules/mpm/stm/Makefile.in-orig
+++ apache_2.0a4/src/modules/mpm/stm/Makefile.in Tue Jul 18 16:49:54 2000
@@ -0,0 +1,7 @@
+# Makefile.in for the STM MPM.
+# Copyright (c) 2000 Silicon Graphics, Inc. All rights reserved.
+
+LTLIBRARY_NAME = libstm.la
+LTLIBRARY_SOURCES = stm.c stiol.c
+
+include $(top_srcdir)/build/ltlib.mk
diff -Naur apache_2.0a4/src/modules/mpm/stm/Makefile.libdir-orig apache_2.0a4/src/modules/mpm/stm/Makefile.libdir
--- apache_2.0a4/src/modules/mpm/stm/Makefile.libdir-orig
+++ apache_2.0a4/src/modules/mpm/stm/Makefile.libdir Wed Mar 29 10:39:36 2000
@@ -0,0 +1,4 @@
+This is a place-holder which indicates to Configure that it shouldn't
+provide the default targets when building the Makefile in this directory.
+Instead it'll just prepend all the important variable definitions, and
+copy the Makefile.tmpl onto the end.
diff -Naur apache_2.0a4/src/modules/mpm/stm/config.m4-orig apache_2.0a4/src/modules/mpm/stm/config.m4
--- apache_2.0a4/src/modules/mpm/stm/config.m4-orig
+++ apache_2.0a4/src/modules/mpm/stm/config.m4 Tue Apr 11 14:47:12 2000
@@ -0,0 +1,23 @@
+dnl config snippet for the STM MPM.
+dnl Copyright (c) 2000 Silicon Graphics, Inc. All rights reserved.
+
+dnl Check for state threads library
+AC_DEFUN(APACHE_MPM_STATETHREAD, [
+ AC_CHECK_HEADER(st.h, [ ],[
+ AC_MSG_ERROR(This MPM requires state threads. Try --with-mpm=dexter.)
+ ])
+ AC_CHECK_FUNC(st_thread_create, [ ],[
+ AC_MSG_ERROR(Can't compile state-threaded code.)
+ ])
+])
+
+if test "$MPM_NAME" = "stm" ; then
+ ac_cv_enable_threads="no"
+ AC_CACHE_SAVE
+
+ LIBS="$LIBS -lst"
+ APACHE_FAST_OUTPUT(modules/mpm/$MPM_NAME/Makefile)
+ APACHE_MPM_STATETHREAD
+ APACHE_MPM_CHECK_SHMEM
+fi
+
diff -Naur apache_2.0a4/src/modules/mpm/stm/mpm.h-orig apache_2.0a4/src/modules/mpm/stm/mpm.h
--- apache_2.0a4/src/modules/mpm/stm/mpm.h-orig
+++ apache_2.0a4/src/modules/mpm/stm/mpm.h Wed Jul 19 15:34:36 2000
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2000 Silicon Graphics, Inc. All rights reserved.
+ */
+
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2000 The Apache Software Foundation. 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. The end-user documentation included with the redistribution,
+ * if any, must include the following acknowledgment:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache" and "Apache Software Foundation" 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 name, without prior written
+ * permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``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 SOFTWARE FOUNDATION 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 Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * .
+ *
+ * Portions of this software are based upon public domain software
+ * originally written at the National Center for Supercomputing Applications,
+ * University of Illinois, Urbana-Champaign.
+ */
+
+#ifndef MODULES_MPM_STM_MPM_H
+#define MODULES_MPM_STM_MPM_H
+
+/* Disable all the stuff in mpm_common.c that STM doesn't need. */
+#define UNCOMMON_MPM
+
+#endif
diff -Naur apache_2.0a4/src/modules/mpm/stm/stiol.c-orig apache_2.0a4/src/modules/mpm/stm/stiol.c
--- apache_2.0a4/src/modules/mpm/stm/stiol.c-orig
+++ apache_2.0a4/src/modules/mpm/stm/stiol.c Tue Jul 18 17:33:45 2000
@@ -0,0 +1,285 @@
+/*
+ * Copyright (c) 2000 Silicon Graphics, Inc. All rights reserved.
+ */
+
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2000 The Apache Software Foundation. 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. The end-user documentation included with the redistribution,
+ * if any, must include the following acknowledgment:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache" and "Apache Software Foundation" 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 name, without prior written
+ * permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``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 SOFTWARE FOUNDATION 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 Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * .
+ *
+ * Portions of this software are based upon public domain software
+ * originally written at the National Center for Supercomputing Applications,
+ * University of Illinois, Urbana-Champaign.
+ */
+
+/*
+ * ST IOL - State-Threaded I/O Layer for the STM MPM.
+ *
+ * Mike Abbott - mja@sgi.com
+ * Accelerating Apache Project - http://oss.sgi.com/projects/apache/
+ *
+ * Direct all I/O operations on sockets to the state thread library's I/O
+ * functions. Must not use APR's functions because only ST's I/O functions
+ * support state thread scheduling.
+ */
+
+#include
+#include "ap_config.h"
+#include "httpd.h"
+#include "ap_iol.h"
+#include "stiol.h"
+
+#ifdef STIOL_DEBUG
+# define STIOL_ASSERT(x) { \
+ if (!(x)) { \
+ fprintf(stderr, "STIOL assertion botched: \"%s\", %s line %d, pid %d\n", \
+ #x, __FILE__, __LINE__, getpid()); \
+ abort(); \
+ } \
+}
+# define STIOL_TRACE(x) if (stiol_trace > 0) printf x
+# define HIDDEN /* nothing */
+HIDDEN int stiol_trace = -1;
+#else
+# define STIOL_ASSERT(x) /* nothing */
+# define STIOL_TRACE(x) /* nothing */
+# define HIDDEN static
+#endif
+
+/*
+ * ST IOL file descriptor and methods.
+ */
+typedef struct stiol_fd {
+ ap_iol iol; /* required iol boilerplate */
+ st_netfd_t fd; /* st library's file descriptor */
+ st_utime_t timeout; /* I/O timeout in microseconds */
+ struct stiol_fd *next; /* in free list */
+} stiol_fd;
+HIDDEN ap_status_t stiol_close(ap_iol *);
+HIDDEN ap_status_t stiol_write(ap_iol *, const char *, ap_size_t,
+ ap_ssize_t *);
+HIDDEN ap_status_t stiol_writev(ap_iol *, const struct iovec *, ap_size_t,
+ ap_ssize_t *);
+HIDDEN ap_status_t stiol_read(ap_iol *, char *, ap_size_t, ap_ssize_t *);
+HIDDEN ap_status_t stiol_setopt(ap_iol *, ap_iol_option, const void *);
+HIDDEN ap_status_t stiol_getopt(ap_iol *, ap_iol_option, void *);
+HIDDEN ap_status_t stiol_sendfile(ap_iol *, ap_file_t *, ap_hdtr_t *,
+ ap_off_t *, ap_size_t *, ap_int32_t);
+HIDDEN ap_status_t stiol_shutdown(ap_iol *, int);
+
+/* List of free fds */
+HIDDEN stiol_fd *stiol_fd_freelist;
+#ifdef STIOL_DEBUG
+HIDDEN int stiol_fd_freelist_length;
+#endif
+
+/* Exported methods */
+HIDDEN const ap_iol_methods stiol_methods = {
+ stiol_close, /* close */
+ stiol_write, /* write */
+ stiol_writev, /* writev */
+ stiol_read, /* read */
+ stiol_setopt, /* setopt */
+ stiol_getopt, /* getopt */
+ stiol_sendfile, /* sendfile */
+ stiol_shutdown /* shutdown */
+};
+
+HIDDEN ap_status_t
+stiol_close(ap_iol *iol)
+{
+ stiol_fd *sfp;
+
+ STIOL_TRACE(("%d: stiol_close(iol=%p)\n", getpid(), iol));
+
+ sfp = (stiol_fd *) iol;
+ sfp->next = stiol_fd_freelist;
+ stiol_fd_freelist = sfp;
+#ifdef STIOL_DEBUG
+ stiol_fd_freelist_length++;
+#endif
+ return (st_netfd_close(sfp->fd) == 0) ? APR_SUCCESS : errno;
+}
+
+HIDDEN ap_status_t
+stiol_write(ap_iol *iol, const char *buf, ap_size_t nbytes,
+ ap_ssize_t *writtenp)
+{
+ stiol_fd *sfp;
+
+ STIOL_TRACE(("%d: stiol_write(iol=%p, buf=%p, nbytes=%lu)\n", getpid(),
+ iol, buf, (unsigned long) nbytes));
+
+ sfp = (stiol_fd *) iol;
+ *writtenp = st_write(sfp->fd, buf, nbytes, sfp->timeout);
+ return (*writtenp >= 0) ? APR_SUCCESS : errno;
+}
+
+HIDDEN ap_status_t
+stiol_writev(ap_iol *iol, const struct iovec *vec, ap_size_t nvec,
+ ap_ssize_t *writtenp)
+{
+ stiol_fd *sfp;
+
+ STIOL_TRACE(("%d: stiol_writev(iol=%p, vec=%p, nvec=%lu)\n", getpid(),
+ iol, vec, (unsigned long) nvec));
+
+ sfp = (stiol_fd *) iol;
+ *writtenp = st_writev(sfp->fd, vec, nvec, sfp->timeout);
+ return (*writtenp >= 0) ? APR_SUCCESS : errno;
+}
+
+HIDDEN ap_status_t
+stiol_read(ap_iol *iol, char *buf, ap_size_t nbytes, ap_ssize_t *readp)
+{
+ stiol_fd *sfp;
+
+ STIOL_TRACE(("%d: stiol_read(iol=%p, buf=%p, nbytes=%lu)\n", getpid(),
+ iol, buf, (unsigned long) nbytes));
+
+ sfp = (stiol_fd *) iol;
+ *readp = st_read(sfp->fd, buf, nbytes, sfp->timeout);
+ return (*readp >= 0) ? APR_SUCCESS : errno;
+}
+
+HIDDEN ap_status_t
+stiol_setopt(ap_iol *iol, ap_iol_option opt, const void *value)
+{
+ stiol_fd *sfp;
+
+ STIOL_TRACE(("%d: stiol_setopt(iol=%p, opt=%d, value=%p)\n", getpid(),
+ iol, opt, value));
+
+ sfp = (stiol_fd *) iol;
+ switch (opt) {
+ case AP_IOL_TIMEOUT:
+ STIOL_TRACE(("\tAP_IOL_TIMEOUT = %d\n", *(const int *) value));
+ sfp->timeout = (*(const int *) value) * 1000000;
+ return APR_SUCCESS;
+ }
+ return APR_EINVAL;
+}
+
+HIDDEN ap_status_t
+stiol_getopt(ap_iol *iol, ap_iol_option opt, void *value)
+{
+ stiol_fd *sfp;
+
+ STIOL_TRACE(("%d: stiol_getopt(iol=%p, opt=%d)\n", getpid(),
+ iol, opt));
+
+ sfp = (stiol_fd *) iol;
+ switch (opt) {
+ case AP_IOL_TIMEOUT:
+ *(int *) value = (sfp->timeout != -1) ? sfp->timeout / 1000000 : -1;
+ return APR_SUCCESS;
+ }
+ return APR_EINVAL;
+}
+
+HIDDEN ap_status_t
+stiol_sendfile(ap_iol *iol, ap_file_t *file, ap_hdtr_t *hdtr, ap_off_t *off,
+ ap_size_t *len, ap_int32_t flags)
+{
+ STIOL_TRACE(("%d: stiol_sendfile(iol=%p, file=%p, hdtr=%p, off=%p, len=%p, flags=%#x)\n",
+ getpid(), iol, file, hdtr, off, len, flags));
+
+ return APR_ENOTIMPL;
+}
+
+HIDDEN ap_status_t
+stiol_shutdown(ap_iol *iol, int how)
+{
+ stiol_fd *sfp;
+
+ STIOL_TRACE(("%d: stiol_shutdown(iol=%p, how=%d)\n", getpid(), iol, how));
+
+ sfp = (stiol_fd *) iol;
+ return (shutdown(st_netfd_fileno(sfp->fd), how) == 0) ?
+ APR_SUCCESS : errno;
+}
+
+/*
+ * Wrap the given st fd in the stiol layer. Returns an stiol fd.
+ * The caller should not close the given naked fd, but let it happen
+ * naturally via stiol_close().
+ */
+ap_iol *
+stiol_attach(st_netfd_t *fd)
+{
+ stiol_fd *sfp;
+
+#ifdef STIOL_DEBUG
+ if (stiol_trace < 0)
+ stiol_trace = getenv("STIOL_TRACE") != NULL;
+#endif
+ STIOL_TRACE(("%d: stiol_attach(fd=%p)\n", getpid(), fd));
+
+ /* recycle old fds for speed */
+ if (stiol_fd_freelist) {
+ sfp = stiol_fd_freelist;
+ stiol_fd_freelist = stiol_fd_freelist->next;
+#ifdef STIOL_DEBUG
+ stiol_fd_freelist_length--;
+#endif
+ STIOL_TRACE(("\trecycled iol = %p\n", sfp));
+ } else {
+ sfp = (stiol_fd *) calloc(1, sizeof *sfp);
+ sfp->iol.methods = &stiol_methods;
+ STIOL_TRACE(("\tnew iol = %p\n", sfp));
+ }
+ sfp->fd = fd;
+ sfp->timeout = -1;
+ sfp->next = NULL;
+
+ STIOL_ASSERT((void *) sfp == (void *) &sfp->iol);
+ return &sfp->iol;
+}
diff -Naur apache_2.0a4/src/modules/mpm/stm/stiol.h-orig apache_2.0a4/src/modules/mpm/stm/stiol.h
--- apache_2.0a4/src/modules/mpm/stm/stiol.h-orig
+++ apache_2.0a4/src/modules/mpm/stm/stiol.h Tue Apr 11 15:07:33 2000
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2000 Silicon Graphics, Inc. All rights reserved.
+ */
+
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2000 The Apache Software Foundation. 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. The end-user documentation included with the redistribution,
+ * if any, must include the following acknowledgment:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache" and "Apache Software Foundation" 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 name, without prior written
+ * permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``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 SOFTWARE FOUNDATION 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 Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * .
+ *
+ * Portions of this software are based upon public domain software
+ * originally written at the National Center for Supercomputing Applications,
+ * University of Illinois, Urbana-Champaign.
+ */
+
+#ifndef MODULES_MPM_STM_STIOL_H
+#define MODULES_MPM_STM_STIOL_H
+
+ap_iol *stiol_attach(st_netfd_t *sock);
+
+#endif
diff -Naur apache_2.0a4/src/modules/mpm/stm/stm.c-orig apache_2.0a4/src/modules/mpm/stm/stm.c
--- apache_2.0a4/src/modules/mpm/stm/stm.c-orig
+++ apache_2.0a4/src/modules/mpm/stm/stm.c Thu Jul 27 12:30:40 2000
@@ -0,0 +1,3197 @@
+/*
+ * Copyright (c) 2000 Silicon Graphics, Inc. All rights reserved.
+ */
+
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2000 The Apache Software Foundation. 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. The end-user documentation included with the redistribution,
+ * if any, must include the following acknowledgment:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache" and "Apache Software Foundation" 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 name, without prior written
+ * permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``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 SOFTWARE FOUNDATION 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 Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * .
+ *
+ * Portions of this software are based upon public domain software
+ * originally written at the National Center for Supercomputing Applications,
+ * University of Illinois, Urbana-Champaign.
+ */
+
+/*
+ * STM - State-Threaded Multi-Processing Module for Apache/2.0
+ *
+ * Mike Abbott - mja@sgi.com
+ * Accelerating Apache Project - http://oss.sgi.com/projects/apache/
+ * State Threads Project - http://oss.sgi.com/projects/state-threads/
+ *
+ * State threads are non-preemptive non-concurrent threads that run on
+ * virtual processors which do not share data. Performance exceeds that
+ * of concurrent threads such as pthreads because there is no mutual
+ * exclusion locking and no proliferation of kernel execution vehicles
+ * from use of blocking I/O system calls. State threads are extremely
+ * lightweight, scheduling entirely in user mode. Hardware concurrency
+ * is available by forking multiple virtual processors.
+ *
+ * The state-threaded multi-processing module (STM MPM) for Apache/2.0
+ * creates a constant number of virtual processors (VPs) and replaces
+ * them when they die. Each virtual processor manages its own
+ * independent set of state threads (STs), the number of which varies
+ * with load against the server. Each state thread listens to, and
+ * processes connections from, exactly one listening socket.
+ *
+ * When the main Apache server calls STM's ap_mpm_run() the initial
+ * process becomes the watchdog, waiting for children (VPs) to die or
+ * for a signal requesting shutdown or restart. (See stm_main().) This
+ * is the master process. The master process mostly sits blocked in
+ * wait(). It does not wake up periodically to perform maintenance such
+ * as the scoreboard updates other MPMs do every second. Passive idling
+ * via wait() is friendly to both low-volume and high-volume servers.
+ * Low-volume servers benefit from passive idling because they can
+ * completely page out Apache when it is idle for long periods and won't
+ * have some CPU cache lines always containing Apache data. High-volume
+ * servers benefit because there are no extraneous scheduling events and
+ * the CPU caches remain hot with data useful to serving pages quickly.
+ *
+ * The master process forks off children which become virtual
+ * processors. Each VP starts a number of state threads and then
+ * becomes the primordial state thread which blocks waiting for input
+ * from a pipe. (See stm_vp_main().) Only the VP itself writes to this
+ * pipe, and only to begin graceful shutdown of the VP and all its
+ * threads. This happens when a VP receives SIGHUP from the master
+ * process or a VP has processed all of the connections it is allowed
+ * and terminates itself (to reclaim resources leaked by faulty
+ * modules). This signal is the only way the master process and the VPs
+ * communicate (except for SIGTERM, upon receipt of which the VPs exit
+ * immediately because they set its disposition to SIG_DFL). A pipe is
+ * used because using write() on a pipe is guaranteed to be safe inside
+ * a signal handler and no ST function -- such as st_cond_signal() -- is
+ * safe to use inside a signal handler.
+ *
+ * Each state thread loops processing connections from a single
+ * listening socket. (See stm_thread_main().) (Threads could span
+ * multiple sockets but doing so would cloud the code a bit. There
+ * would be no extra system calls since everything boils down to
+ * select() in the state thread library anyway.) Only one ST runs on a
+ * VP at a time, and VPs do not share memory (except for the
+ * scoreboards, discussed below), so no mutual exclusion locking is
+ * necessary on any data, and the entire server is free to use all the
+ * static variables and non-reentrant library functions it wants,
+ * greatly simplifying programming and debugging and increasing
+ * performance. The current thread on each VP maintains equilibrium on
+ * that VP, starting a new thread or terminating itself if the number of
+ * spare threads exceeds the lower or upper limit, and also updates
+ * counters and the scoreboard for that VP.
+ *
+ * Each VP has its own scoreboard. The scoreboards are memory-mapped
+ * disk files (as opposed to anonymous shared memory) so that tools can
+ * read scoreboard data without causing Apache to serve a page. Of
+ * course, most of the scoreboard data is also available on the
+ * server-status page from mod_status. VPs have read/write access to
+ * all VPs' scoreboards but in practice read/write their own and only
+ * read others'.
+ *
+ * All I/O operations on sockets must use the state thread library's I/O
+ * functions because only those functions prevent blocking of the entire
+ * VP process and perform state thread scheduling. No APR I/O function
+ * must ever be used. The STM relies on the state thread I/O layer (ST
+ * IOL) to direct I/O operations to ST I/O functions.
+ *
+ * Virtual processors may be bound to particular CPUs to improve cache
+ * utilization, and may listen to all or a subset of the listen sockets.
+ * Cache utilization is further improved when network interface
+ * interrupts are bound to the same CPUs as the VPs listening to those
+ * interfaces. Unfortunately intercepting Listen directives to support
+ * this optimization introcudes some really ugly code.
+ *
+ * See htdocs/manual/stm.html for further documentation.
+ */
+
+#include
+
+#define CORE_PRIVATE
+#include "ap_config.h"
+#include "apr_portable.h"
+#include "httpd.h"
+#include "http_main.h"
+#include "http_log.h"
+#include "http_config.h"
+#include "http_connection.h"
+#include "ap_listen.h"
+#include "ap_mpm.h"
+#include "mpm_status.h"
+#include "unixd.h"
+#include "stiol.h"
+
+#ifdef HAVE_NETINET_TCP_H
+#include
+#endif
+#include
+
+#if defined(IRIX) && _MIPS_SIM != _ABIO32
+/* disable warnings about unused formal parameters */
+#pragma set woff 1174
+#endif
+
+/*
+ * Maximum number of listeners (Listen/VPListen directives, aka
+ * accept()ing sockets). The default value is arbitrary but should be
+ * kept small. Feel free to override the default with any value though.
+ */
+#ifndef STM_LISTENER_LIMIT
+#define STM_LISTENER_LIMIT 8
+#endif
+
+/*
+ * Maximum number of virtual processors. The default value is arbitrary
+ * but should be kept small. Feel free to override the default with any
+ * value though.
+ */
+#ifndef STM_VP_LIMIT
+#define STM_VP_LIMIT 8
+#endif
+
+/*
+ * Maximum number of state threads per virtual processor (upper bound on
+ * MaxThreads). The default value is arbitrary but should be kept
+ * moderate: not so small as to hinder performance, not so big as to
+ * waste memory. Feel free to override the default with any value
+ * though. The server-status page (see mod_status) reports the maximum
+ * number of threads actually used at any time, which can be used to
+ * tune this value optimally for your system and workload.
+ */
+#ifndef STM_ST_LIMIT
+#define STM_ST_LIMIT 512
+#endif
+
+/*
+ * Maximum length in bytes of the key string in the scoreboards'
+ * connection status tables, including the string-terminating null.
+ * Keys longer than this will be truncated. The default value is
+ * arbitrary but should be kept small. Feel free to override the
+ * default with any value though. It's probably a good idea to keep
+ * STM_SCORE_KEY_SIZE + STM_SCORE_VALUE_SIZE one or more times the size
+ * of your system's largest cache line.
+ */
+#ifndef STM_SCORE_KEY_SIZE
+#define STM_SCORE_KEY_SIZE 16
+#endif
+
+/*
+ * Maximum length in bytes of the value string in the scoreboards'
+ * connection status tables, including the string-terminating null.
+ * Values longer than this will be truncated. The default value is
+ * arbitrary but should be kept moderate. Feel free to override the
+ * default with any value though. It's probably a good idea to keep
+ * STM_SCORE_KEY_SIZE + STM_SCORE_VALUE_SIZE one or more times the size
+ * of your system's largest cache line.
+ */
+#ifndef STM_SCORE_VALUE_SIZE
+#define STM_SCORE_VALUE_SIZE 48
+#endif
+
+/*
+ * Maximum number of key/value pairs in the scoreboards' connection
+ * status tables. When the table fills additional insertions are
+ * ignored. The default value is arbitrary but should be kept small.
+ * Feel free to override the default with any value though.
+ */
+#ifndef STM_SCORE_LIMIT
+#define STM_SCORE_LIMIT 16
+#endif
+
+/* Debugging aids. */
+#ifdef STM_DEBUG
+# define STM_ASSERT(x) { \
+ if (!(x)) { \
+ fprintf(stderr, "STM assertion botched: \"%s\", %s line %d, pid %d, thread %p\n", \
+ #x, __FILE__, __LINE__, getpid(), stm_thread_self()); \
+ abort(); \
+ } \
+}
+# define STM_TRACE(x) if (stm.debug.trace) printf x
+# define HIDDEN /* nothing */
+#else
+# define STM_ASSERT(x) /* nothing */
+# define STM_TRACE(x) /* nothing */
+# define HIDDEN static
+#endif
+
+/* Configured VPListen directives. */
+typedef struct stm_config_vp_listen {
+ int num_listeners; /* number of listeners */
+ struct stm_listener *listeners[STM_LISTENER_LIMIT]; /* ptrs to stm.listeners */
+} stm_config_vp_listen;
+
+/*
+ * Configuration, defaults, and methods. All STM configuration settings
+ * are kept in this structure.
+ */
+typedef struct stm_config {
+ /*
+ * Number of virtual processors. Must be between 1 and STM_VP_LIMIT
+ * inclusive. Only one or a few are needed per CPU. The server
+ * starts a replacement when a VP dies so the number running is
+ * constant. Making this too small can cause long latency for
+ * clients; too big and you waste CPU cycles and memory.
+ */
+ int num_vps;
+#define STM_CMD_NUM_VPS "NumVPs"
+
+ /*
+ * Number of state threads each VP should start initially PER SOCKET
+ * -- not per VP. Must be between 1 and STM_ST_LIMIT inclusive.
+ * Should be between 1 and max_threads/num_listeners inclusive.
+ * Generally this value should be the same as min_spare_threads but
+ * it can be higher to facilitate Apache restarts on high-volume
+ * servers.
+ */
+ int start_threads;
+#define STM_CMD_START_THREADS "StartThreads"
+
+ /*
+ * Minimum number of spare state threads PER SOCKET -- not per VP.
+ * Spare threads are threads awaiting a connection (as opposed to
+ * processing requests on established connections). Must be between
+ * 1 and STM_ST_LIMIT inclusive. Should be between 1 and
+ * max_threads/num_listeners inclusive. Making this too small can
+ * cause long latency for clients; too big and you waste CPU cycles
+ * on low-volume servers.
+ */
+ int min_spare_threads;
+#define STM_CMD_MIN_SPARE_THREADS "MinSpareThreads"
+
+ /*
+ * Maximum number of spare state threads PER SOCKET -- not per VP.
+ * See min_spare_threads.
+ */
+ int max_spare_threads;
+#define STM_CMD_MAX_SPARE_THREADS "MaxSpareThreads"
+
+ /*
+ * Maximum number of state threads -- spare or busy -- per VP across
+ * all sockets. Must be between 1 and STM_ST_LIMIT inclusive.
+ * Making this too small can cause long latency for clients,
+ * especially if your site is largely dynamic (e.g. CGI); too big
+ * and you waste memory.
+ */
+ int max_threads;
+#define STM_CMD_MAX_THREADS "MaxThreads"
+
+ /*
+ * Size in bytes of each thread's stack. Must be between the
+ * system's page size and INT_MAX inclusive. Making this too small
+ * can cause stack overflow errors; too big and you waste memory.
+ */
+ int stack_size;
+#define STM_CMD_STACK_SIZE "StackSize"
+
+ /*
+ * Number of connections (not requests) each VP should serve before
+ * exiting gracefully and allowing the master process to replace it
+ * to reclaim resources such as memory or file descriptors leaked by
+ * faulty modules. Must be between 0 and INT_MAX inclusive. The
+ * value 0 means the number of connections is unlimited and VPs
+ * should never voluntarily exit.
+ */
+ int vp_connections;
+#define STM_CMD_VP_CONNECTIONS "VPConnections"
+
+ /*
+ * Binding VPs to CPUs. If vp_bind is non-null, vp_bind[n] is the
+ * CPU number to which VP n should be bound.
+ */
+ int *vp_bind;
+#define STM_CMD_VP_BIND "VPBind"
+
+ /*
+ * Binding listeners to VPs. If vp_listen is non-null, vp_listen[n]
+ * is a vector of IP addresses to which VP n should listen.
+ */
+ stm_config_vp_listen *vp_listen;
+#define STM_CMD_VP_LISTEN "VPListen"
+
+ /*
+ * Path of the file where Apache writes the PID of the master
+ * process. The file may or may not exist before startup. Except
+ * for the defaults structure this is always an absolute path name.
+ */
+ const char *pid_file;
+#define STM_CMD_PID_FILE "PidFile"
+
+ /*
+ * Flag enabling or disabling connection status in scoreboards (they
+ * can be slow to maintain). Zero means disabled, nonzero means
+ * enabled.
+ */
+ int connection_status;
+#define STM_CMD_CONNECTION_STATUS "ConnectionStatus"
+
+ /*
+ * Path of the directory where the STM creates scoreboard files for
+ * each VP. The directory must already exist before Apache startup.
+ * The scoreboard files themselves may or may not pre-exist, have
+ * names that are just the ordinal of the VP, and have a data format
+ * described in the scoreboard section below. Except for the
+ * defaults structure this is always an absolute path name.
+ */
+ const char *scoreboard_dir;
+#define STM_CMD_SCOREBOARD_DIR "ScoreboardDir"
+
+ /*
+ * Path of a directory where Apache will dump core if necessary.
+ * The directory should exist before Apache startup. Except for the
+ * defaults structure this is always an absolute path name. A NULL
+ * value means Apache should dump core in whatever directory it
+ * happens to be in at the time, generally the server root directory.
+ */
+ const char *core_dir;
+#define STM_CMD_CORE_DIR "CoreDir"
+} stm_config;
+HIDDEN const stm_config stm_config_defaults = {
+ 4, /* num_vps */
+ 5, /* start_threads */
+ 5, /* min_spare_threads */
+ 10, /* max_spare_threads */
+ 64, /* max_threads */
+ 64 * 1024, /* stack_size */
+ 0, /* vp_connections */
+ NULL, /* vp_bind */
+ NULL, /* vp_listen */
+ "logs/httpd.pid", /* pid_file */
+ 1, /* connection_status */
+ "scoreboards", /* scoreboard_dir */
+ NULL /* core_dir */
+};
+HIDDEN const char *stm_config_set_num(cmd_parms *, const char *, int *, int,
+ int, const char *);
+HIDDEN const char *stm_config_set_num_vps(cmd_parms *, void *,
+ const char *);
+HIDDEN const char *stm_config_set_start_threads(cmd_parms *, void *,
+ const char *);
+HIDDEN const char *stm_config_set_min_spare_threads(cmd_parms *, void *,
+ const char *);
+HIDDEN const char *stm_config_set_max_spare_threads(cmd_parms *, void *,
+ const char *);
+HIDDEN const char *stm_config_set_max_threads(cmd_parms *, void *,
+ const char *);
+HIDDEN const char *stm_config_set_stack_size(cmd_parms *, void *,
+ const char *);
+HIDDEN const char *stm_config_set_vp_connections(cmd_parms *, void *,
+ const char *);
+HIDDEN const char *stm_config_set_vp_bind(cmd_parms *, void *,
+ const char *, const char *);
+HIDDEN const char *stm_config_set_vp_listen(cmd_parms *, void *,
+ const char *, const char *);
+HIDDEN const char *stm_config_set_ap_listen(cmd_parms *, void *,
+ const char *);
+HIDDEN const char *stm_config_set_pid_file(cmd_parms *, void *,
+ const char *);
+HIDDEN const char *stm_config_set_connection_status(cmd_parms *, void *,
+ int);
+HIDDEN const char *stm_config_set_scoreboard_dir(cmd_parms *, void *,
+ const char *);
+HIDDEN const char *stm_config_set_core_dir(cmd_parms *, void *,
+ const char *);
+HIDDEN const command_rec stm_cmds[] = {
+ {
+ STM_CMD_NUM_VPS,
+ stm_config_set_num_vps,
+ NULL, RSRC_CONF, TAKE1,
+ "Number of virtual processors"
+ }, {
+ STM_CMD_START_THREADS,
+ stm_config_set_start_threads,
+ NULL, RSRC_CONF, TAKE1,
+ "Initial number of state threads per listen socket"
+ }, {
+ STM_CMD_MIN_SPARE_THREADS,
+ stm_config_set_min_spare_threads,
+ NULL, RSRC_CONF, TAKE1,
+ "Minumum number of spare state threads per listen socket"
+ }, {
+ STM_CMD_MAX_SPARE_THREADS,
+ stm_config_set_max_spare_threads,
+ NULL, RSRC_CONF, TAKE1,
+ "Maxumum number of spare state threads per listen socket"
+ }, {
+ STM_CMD_MAX_THREADS,
+ stm_config_set_max_threads,
+ NULL, RSRC_CONF, TAKE1,
+ "Maximum number of state threads per virtual processor"
+ }, {
+ STM_CMD_STACK_SIZE,
+ stm_config_set_stack_size,
+ NULL, RSRC_CONF, TAKE1,
+ "Size in bytes of each thread's stack"
+ }, {
+ STM_CMD_VP_CONNECTIONS,
+ stm_config_set_vp_connections,
+ NULL, RSRC_CONF, TAKE1,
+ "Number of connections each virtual processor should serve before exiting"
+ }, {
+ STM_CMD_VP_BIND,
+ stm_config_set_vp_bind,
+ NULL, RSRC_CONF, TAKE2,
+ "Bind a virtual processor to a specific CPU"
+ }, {
+ STM_CMD_VP_LISTEN,
+ stm_config_set_vp_listen,
+ NULL, RSRC_CONF, ITERATE2,
+ "List of ip-address:port-numbers to which a virtual processor should listen"
+ }, {
+ STM_CMD_PID_FILE,
+ stm_config_set_pid_file,
+ NULL, RSRC_CONF, TAKE1,
+ "Name of file in which the server records its PID upon startup"
+ }, {
+ STM_CMD_CONNECTION_STATUS,
+ stm_config_set_connection_status,
+ NULL, RSRC_CONF, FLAG,
+ "Enables/disables connection status information in the scoreboards"
+ }, {
+ STM_CMD_SCOREBOARD_DIR,
+ stm_config_set_scoreboard_dir,
+ NULL, RSRC_CONF, TAKE1,
+ "Name of existing directory in which the server maintains scoreboard file(s)"
+ }, {
+ STM_CMD_CORE_DIR,
+ stm_config_set_core_dir,
+ NULL, RSRC_CONF, TAKE1,
+ "Name of existing directory in which the server should dump core if necessary"
+ },
+ UNIX_DAEMON_COMMANDS
+
+ /*
+ * Redirect Listen directives to our own handler. This is a gross
+ * kludge but so is the existence of, definition of, and MPMs'
+ * dependence on the LISTEN_COMMANDS macro.
+ */
+#define ap_set_listener stm_config_set_ap_listen
+ LISTEN_COMMANDS
+#undef ap_set_listener
+
+ { NULL }
+};
+HIDDEN void stm_pre_config(ap_pool_t *, ap_pool_t *, ap_pool_t *);
+HIDDEN void stm_hooks(void);
+
+/* VPs and STs health indicators. */
+typedef enum stm_health {
+ STM_HEALTH_DEAD, /* entity is not running */
+ STM_HEALTH_DYING, /* entity has been asked to terminate */
+ STM_HEALTH_ALIVE /* entity is running */
+} stm_health;
+
+/* Master process state. */
+typedef enum stm_state {
+ STM_STATE_SHUTDOWN, /* shutdown requested or in progress */
+ STM_STATE_RUN, /* running */
+ STM_STATE_GRACEFUL, /* graceful restart requested or in progress */
+ STM_STATE_RESTART /* restart requested or in progress */
+} stm_state;
+
+/* Evaluation of seriousness of child process's death. */
+typedef enum stm_grief {
+ STM_GRIEF_NORMAL, /* normal and/or expected */
+ STM_GRIEF_FATAL, /* catastrophic */
+ STM_GRIEF_SURPRISE /* unexpected but not tragic */
+} stm_grief;
+
+#ifdef STM_DEBUG
+/* Debugging aids. */
+typedef struct stm_debug {
+ int interactive; /* when nonzero don't detach */
+ int one_process; /* when nonzero restrict to a single process */
+ int trace; /* when nonzero trace function calls */
+} stm_debug;
+#endif
+
+/* Datum about a connection. Key/value type mandated by mod_status. */
+typedef struct stm_score {
+ char key[STM_SCORE_KEY_SIZE];
+ char value[STM_SCORE_VALUE_SIZE];
+} stm_score;
+
+/*
+ * Per-VP scoreboard and methods. Fields are explicitly sized because
+ * this is also the on-disk file format. When changing this structure
+ * also change the version string so tools can correctly parse the data
+ * and also update the stmstat tool. The version string should be
+ * exactly STM_SCOREBOARD_VERSION_LENGTH bytes long excluding the null.
+ * Do not change STM_SCOREBOARD_VERSION_LENGTH.
+ */
+#define STM_SCOREBOARD_VERSION_LENGTH 16
+#define STM_SCOREBOARD_VERSION "STM-MPM-sb-0001\n"
+typedef struct stm_scoreboard {
+ char version[STM_SCOREBOARD_VERSION_LENGTH]; /* see above */
+ ap_int32_t st_limit; /* size of arrays below */
+ ap_int32_t listener_limit; /* size of arrays below */
+ ap_int32_t score_limit; /* size of arrays below */
+ ap_int32_t key_size; /* size of arrays below */
+ ap_int32_t value_size; /* size of arrays below */
+ ap_int32_t pid; /* this VP's PID */
+ ap_time_t start_time; /* server's start time */
+ ap_time_t vp_start_time; /* this VP's start time */
+ ap_int32_t incarnations; /* # of different VPs in this slot */
+ ap_int32_t thread_starts; /* # of thread start events */
+ ap_int32_t thread_exits; /* # of thread exit events */
+ ap_int32_t most_threads; /* most threads ever used */
+ ap_int32_t historic_vp_connections;/* # of conns, prior incarnations */
+ ap_int32_t vp_connections; /* # of conns, this incarnation */
+ ap_int32_t thread_connections[STM_ST_LIMIT]; /* per thread */
+ ap_int32_t listener_connections[STM_LISTENER_LIMIT]; /* per listener */
+ char listeners[STM_LISTENER_LIMIT][24]; /* IP address:port */
+ stm_score scores[STM_ST_LIMIT][STM_SCORE_LIMIT]; /* per-conn data */
+} stm_scoreboard;
+HIDDEN stm_scoreboard *stm_scoreboard_new(int);
+HIDDEN void stm_scoreboard_init(stm_scoreboard *);
+
+/*
+ * Listener and methods. These listeners are wrappers around the more
+ * traditional ap_listen_rec so we can accept on an st_netfd_t rather
+ * than an ap_socket_t.
+ */
+typedef ap_uint32_t stm_port; /* simplify profusion of IP port types */
+typedef ap_uint32_t stm_addr; /* simplify profusion of IP address types */
+typedef struct stm_listener {
+ int id; /* == (int) (this - stm.listeners) */
+ const char *addr_string; /* inet_ntoa(addr.sin_addr) */
+ struct sockaddr_in addr; /* IP addr and port in network order */
+ int unique; /* is addr unique (not INADDR_ANY)? */
+ int share_count; /* number of VPs using this listener */
+ ap_listen_rec *alr; /* standard listener */
+ st_netfd_t sd; /* ST version of alr->sd */
+ int spare_threads; /* # of spare threads right now */
+} stm_listener;
+HIDDEN void stm_listener_init(stm_listener *);
+HIDDEN stm_listener *stm_listener_add(const char *, stm_addr, stm_port, int *);
+HIDDEN int stm_listener_usurp(stm_listener *, ap_listen_rec *);
+HIDDEN void stm_listener_revert(stm_listener *);
+
+/*
+ * Thread and methods.
+ */
+typedef struct stm_thread {
+ int id; /* == (int) (this - vp->threads) */
+ stm_health health; /* how is this thread doing? */
+ st_thread_t st; /* ST for this thread */
+ struct stm_vp *vp; /* VP on which this thread runs */
+ stm_listener *listener; /* listener on which to accept() */
+ int connected; /* connected to a client right now? */
+} stm_thread;
+HIDDEN void stm_thread_init(stm_thread *, struct stm_vp *);
+HIDDEN int stm_thread_start(stm_thread *, stm_listener *);
+HIDDEN stm_thread *stm_thread_self(void);
+HIDDEN void *stm_thread_main(void *);
+HIDDEN void stm_thread_process_connection(const stm_listener *, ap_pool_t *,
+ st_netfd_t, const struct sockaddr_in *, long);
+HIDDEN void stm_thread_stop(stm_thread *);
+HIDDEN int stm_thread_stopped(stm_thread *);
+
+/*
+ * Virtual processor and methods.
+ */
+typedef struct stm_vp {
+ int id; /* == (int) (this - stm.vps) */
+ pid_t pid; /* PID of VP process */
+ stm_health health; /* how is this VP doing? */
+ ap_pool_t *pool; /* pool for lifetime of VP */
+ int stop_pipe[2]; /* for asynchronous-safe termination */
+ int num_listeners; /* number of listeners for this VP */
+ stm_listener *listeners[STM_LISTENER_LIMIT]; /* ptrs to stm.listeners */
+ int connections; /* conns remaining before suicide */
+ int max_threads_hit; /* reported hitting thread ceiling? */
+ int num_threads; /* number of non-dead threads */
+ stm_thread threads[STM_ST_LIMIT]; /* threads */
+ stm_thread *dead_threads[STM_ST_LIMIT];/* to recycle them quickly */
+ stm_scoreboard *scoreboard; /* scoreboard in shared memory */
+} stm_vp;
+HIDDEN int stm_vp_init(stm_vp *);
+HIDDEN int stm_vp_listen(stm_vp *);
+HIDDEN int stm_vp_start(stm_vp *);
+HIDDEN void stm_vp_bind(stm_vp *);
+HIDDEN void stm_vp_signals(void);
+HIDDEN void stm_vp_main(stm_vp *);
+HIDDEN void stm_vp_stop(stm_vp *);
+HIDDEN void stm_vp_stop_self(stm_vp *);
+HIDDEN void stm_vp_onsig_stop(int);
+HIDDEN void stm_vp_onexit(void);
+HIDDEN ap_status_t stm_vp_cleanup(void *);
+
+/*
+ * Global data and methods. All STM data is in this one place for easy
+ * debugging.
+ */
+HIDDEN struct stm {
+ ap_time_t start_time; /* server start time */
+ ap_pool_t *pool; /* main pool */
+ server_rec *server; /* for logging */
+ stm_state state; /* run state */
+ int initialized; /* stm_init() ever called? */
+ int num_restarts; /* number of server restarts */
+ pid_t pid; /* pid of master process */
+ stm_config config; /* config parameters */
+ int num_listeners; /* number of listeners */
+ stm_listener listeners[STM_LISTENER_LIMIT];
+ int num_vps; /* number of VPs */
+ stm_vp vps[STM_VP_LIMIT]; /* table of VP info */
+ int self_key; /* for stm_thread_self() */
+#ifdef STM_DEBUG
+ stm_debug debug; /* debugging parameters */
+#endif
+} stm;
+HIDDEN int stm_init(ap_pool_t *, server_rec *);
+HIDDEN void stm_fini(void);
+HIDDEN void stm_signals(void);
+HIDDEN void stm_main(void);
+HIDDEN void stm_reap(pid_t, int);
+HIDDEN stm_grief stm_mourn(pid_t, int);
+HIDDEN void stm_doomsday(void);
+
+/* Signal helper routines */
+HIDDEN void stm_signal_catch(int, void (*)(int));
+HIDDEN void stm_signal_default(int);
+HIDDEN void stm_signal_ignore(int);
+HIDDEN void stm_signal_core(int);
+
+/* Master process's signal handlers */
+HIDDEN void stm_onsig_core(int);
+HIDDEN void stm_onsig_restart(int);
+HIDDEN void stm_onsig_shutdown(int);
+
+/* Miscellany */
+HIDDEN long stm_id_make(int, int);
+HIDDEN int stm_id_break(long, int *, int *);
+HIDDEN void stm_disable_nagle(st_netfd_t);
+
+/* Exported API routines */
+void ap_start_shutdown(void);
+void ap_start_restart(int);
+int ap_graceful_stop_signalled(void);
+void ap_reset_connection_status(long);
+void ap_update_connection_status(long, const char *, const char *);
+ap_array_header_t *ap_get_status_table(ap_pool_t *);
+int ap_mpm_run(ap_pool_t *, ap_pool_t *, server_rec *);
+
+/* Module definition */
+module MODULE_VAR_EXPORT mpm_stm_module = {
+ MPM20_MODULE_STUFF,
+ NULL, /* run before apache parses argv */
+ NULL, /* create per-directory config structure */
+ NULL, /* merge per-directory config structures */
+ NULL, /* create per-server config structure */
+ NULL, /* merge per-server config structures */
+ stm_cmds, /* command table */
+ NULL, /* handlers */
+ stm_hooks /* register_hooks */
+};
+
+
+/***********************************************************************
+ * Configuration
+ */
+
+/*
+ * Standard MPM pre-config hook: initializes/resets all configuration
+ * parameters to their default values.
+ */
+HIDDEN void
+stm_pre_config(ap_pool_t *conf_pool, ap_pool_t *log_pool, ap_pool_t *tmp_pool)
+{
+ int i;
+
+#ifdef STM_DEBUG
+ if (stm.pid == 0) {
+ /* set these just once */
+ stm.debug.interactive = getenv("STM_INTERACTIVE") != NULL;
+ stm.debug.one_process = getenv("ONE_PROCESS") != NULL;
+ stm.debug.trace = getenv("STM_TRACE") != NULL;
+ }
+#endif
+
+ STM_TRACE(("%d: stm_pre_config() #%d\n", getpid(), stm.num_restarts));
+
+ /* it would be bad to reset stm.config accidentally while running */
+ STM_ASSERT(stm.state != STM_STATE_RUN);
+
+ /* called twice on normal startup; detach only on second one */
+ if (stm.num_restarts++ == 1) {
+#ifdef STM_DEBUG
+ setlinebuf(stdout);
+ if (!stm.debug.interactive && !stm.debug.one_process)
+#endif
+ unixd_detach();
+ stm.pid = getpid();
+ }
+
+ unixd_pre_config();
+ ap_listen_pre_config();
+
+ stm.num_listeners = 0;
+ for (i = 0; i < STM_LISTENER_LIMIT; i++)
+ stm_listener_init(&stm.listeners[i]);
+
+ if (stm.config.vp_bind) {
+ free(stm.config.vp_bind);
+ stm.config.vp_bind = NULL;
+ }
+ if (stm.config.vp_listen) {
+ free(stm.config.vp_listen);
+ stm.config.vp_listen = NULL;
+ }
+
+ stm.config = stm_config_defaults;
+ stm.config.pid_file = ap_server_root_relative(conf_pool,
+ stm_config_defaults.pid_file);
+ stm.config.scoreboard_dir = ap_server_root_relative(conf_pool,
+ stm_config_defaults.scoreboard_dir);
+ stm.config.core_dir = ap_pstrdup(conf_pool, ap_server_root);
+}
+
+/*
+ * Standard module hook registration.
+ */
+HIDDEN void
+stm_hooks(void)
+{
+ STM_TRACE(("%d: stm_hooks()\n", getpid()));
+
+ INIT_SIGLIST()
+
+ ap_hook_pre_config(stm_pre_config, NULL, NULL, AP_HOOK_MIDDLE);
+}
+
+/*
+ * Utility function to set an integer configuration parameter if the new
+ * value is in bounds:
+ * max_value >= 0 min_value <= atoi(value_string) <= max_value
+ * max_value < 0 min_value <= atoi(value_string)
+ * Returns an error string, or NULL on success.
+ */
+HIDDEN const char *
+stm_config_set_num(cmd_parms *cmd, const char *value_string, int *paramp,
+ int min_value, int max_value, const char *max_name)
+{
+ const char *err;
+ int value;
+
+ STM_TRACE(("%d: stm_config_set_num(cmd->\"%s\", value_string=\"%s\", min_value=%d, max_value=%d, max_name=\"%s\")\n",
+ getpid(), cmd->cmd->name, value_string, min_value, max_value, max_name));
+
+ err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+ if (err == NULL) {
+ value = atoi(value_string);
+ if (value >= min_value) {
+ if (max_value < 0 || value <= max_value)
+ *paramp = value;
+ else {
+ ap_log_error(APLOG_MARK,
+ APLOG_WARNING | APLOG_STARTUP | APLOG_NOERRNO, 0, NULL,
+ "value %d for %s is above maximum %d, using maximum"
+ "; re-compile with larger %s if desired",
+ value, cmd->cmd->name, max_value, max_name);
+ *paramp = max_value;
+ }
+ } else {
+ ap_log_error(APLOG_MARK,
+ APLOG_WARNING | APLOG_STARTUP | APLOG_NOERRNO, 0, NULL,
+ "value %d for %s is below minimum %d, using minimum",
+ value, cmd->cmd->name, min_value);
+ *paramp = min_value;
+ }
+ }
+
+ return err;
+}
+
+/*
+ * Set the num_vps configuration parameter if the new value is in
+ * bounds. Returns error string, or NULL on success.
+ */
+HIDDEN const char *
+stm_config_set_num_vps(cmd_parms *cmd, void *dummy, const char *num)
+{
+ return stm_config_set_num(cmd, num, &stm.config.num_vps,
+ 1, STM_VP_LIMIT, "STM_VP_LIMIT");
+}
+
+/*
+ * Set the start_threads configuration parameter if the new value is in
+ * bounds. Returns error string, or NULL on success.
+ */
+HIDDEN const char *
+stm_config_set_start_threads(cmd_parms *cmd, void *dummy, const char *num)
+{
+ return stm_config_set_num(cmd, num, &stm.config.start_threads,
+ 1, STM_ST_LIMIT, "STM_ST_LIMIT");
+}
+
+/*
+ * Set the min_spare_threads configuration parameter if the new value is
+ * in bounds. Returns error string, or NULL on success.
+ */
+HIDDEN const char *
+stm_config_set_min_spare_threads(cmd_parms *cmd, void *dummy, const char *num)
+{
+ return stm_config_set_num(cmd, num, &stm.config.min_spare_threads,
+ 1, STM_ST_LIMIT, "STM_ST_LIMIT");
+}
+
+/*
+ * Set the max_spare_threads configuration parameter if the new value is
+ * in bounds. Returns error string, or NULL on success.
+ */
+HIDDEN const char *
+stm_config_set_max_spare_threads(cmd_parms *cmd, void *dummy, const char *num)
+{
+ return stm_config_set_num(cmd, num, &stm.config.max_spare_threads,
+ 1, STM_ST_LIMIT, "STM_ST_LIMIT");
+}
+
+/*
+ * Set the max_threads configuration parameter if the new value is in
+ * bounds. Returns error string, or NULL on success.
+ */
+HIDDEN const char *
+stm_config_set_max_threads(cmd_parms *cmd, void *dummy, const char *num)
+{
+ return stm_config_set_num(cmd, num, &stm.config.max_threads,
+ 1, STM_ST_LIMIT, "STM_ST_LIMIT");
+}
+
+/*
+ * Set the stack_size configuration parameter if the new value is in
+ * bounds. Returns error string, or NULL on success.
+ */
+HIDDEN const char *
+stm_config_set_stack_size(cmd_parms *cmd, void *dummy, const char *num)
+{
+ return stm_config_set_num(cmd, num, &stm.config.stack_size,
+ getpagesize(), -1, NULL);
+}
+
+/*
+ * Set the vp_connections configuration parameter if the new value is in
+ * bounds. Returns error string, or NULL on success.
+ */
+HIDDEN const char *
+stm_config_set_vp_connections(cmd_parms *cmd, void *dummy, const char *num)
+{
+ return stm_config_set_num(cmd, num, &stm.config.vp_connections,
+ 0, -1, NULL);
+}
+
+/*
+ * Add to the vp_bind configuration parameter if the new values are in
+ * bounds. Returns error string, or NULL on success.
+ */
+HIDDEN const char *
+stm_config_set_vp_bind(cmd_parms *cmd, void *dummy, const char *vp_string,
+ const char *cpu_string)
+{
+ const char *err;
+
+ STM_TRACE(("%d: stm_config_set_vp_bind(vp_string=\"%s\", cpu_string=\"%s\")\n",
+ getpid(), vp_string, cpu_string));
+
+ err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+ if (err == NULL) {
+ int v;
+ char *stop;
+
+ v = (int) strtol(vp_string, &stop, 0);
+ if (stop != vp_string && *stop == 0) {
+ /*
+ * Compare against STM_VP_LIMIT here because num_vps may not
+ * have been set yet (could appear later in config file).
+ * stm_init() warns about bindings for unused VPs.
+ */
+ if (v >= 0 && v < STM_VP_LIMIT) {
+ int c;
+
+ c = (int) strtol(cpu_string, &stop, 0);
+ if (stop != cpu_string && *stop == 0 && c >= 0) {
+ if (stm.config.vp_bind == NULL) {
+ int i;
+
+ stm.config.vp_bind = (int *) malloc(STM_VP_LIMIT *
+ sizeof *stm.config.vp_bind);
+ for (i = 0; i < STM_VP_LIMIT; i++)
+ stm.config.vp_bind[i] = -1;
+ }
+ stm.config.vp_bind[v] = c;
+ } else
+ err = "CPU identifier must be a non-negative number";
+ } else
+ err = ap_psprintf(cmd->pool,
+ "value %d for %s is out of range, must be between 0 and %d inclusive"
+ "; re-compile with larger STM_VP_LIMIT if desired",
+ v, cmd->cmd->name, STM_VP_LIMIT - 1);
+ } else
+ err = "virtual processor identifier must be a number";
+ }
+
+ return err;
+}
+
+/*
+ * Add to the vp_listen configuration parameter if the new values are in
+ * bounds. Returns error string, or NULL on success.
+ */
+HIDDEN const char *
+stm_config_set_vp_listen(cmd_parms *cmd, void *dummy, const char *vp_string,
+ const char *listen_string)
+{
+ const char *err;
+
+ STM_TRACE(("%d: stm_config_set_vp_listen(vp_string=\"%s\", listen_string=\"%s\")\n",
+ getpid(), vp_string, listen_string));
+
+ err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+ if (err == NULL && ap_listeners && stm.config.vp_listen == NULL)
+ err = "cannot mix Listen and " STM_CMD_VP_LISTEN " directives.";
+ if (err == NULL) {
+ int v;
+ char *stop;
+
+ v = (int) strtol(vp_string, &stop, 0);
+ if (stop != vp_string && *stop == 0) {
+ /*
+ * Compare against STM_VP_LIMIT here because num_vps may
+ * not have been set yet (could appear later in config
+ * file). stm_init() warns about bindings for unused
+ * VPs.
+ */
+ if (v >= 0 && v < STM_VP_LIMIT) {
+ char *listen_copy;
+ char *port_string;
+
+ listen_copy = ap_pstrdup(cmd->pool, listen_string);
+ port_string = strchr(listen_copy, ':');
+ if (port_string) {
+ stm_port port;
+
+ *port_string++ = 0;
+
+ /* should be strtoul() but can't use it, oh well */
+ port = (stm_port) strtol(port_string, &stop, 0);
+ if (stop != port_string && *stop == 0) {
+ int addr_ok;
+ struct in_addr addr;
+
+ port = htons(port);
+
+ addr_ok = inet_aton(listen_copy, &addr);
+ if (!addr_ok && !strcmp(listen_copy, "*")) {
+ addr.s_addr = htonl(INADDR_ANY);
+ addr_ok = 1;
+ }
+
+ if (addr_ok) {
+ stm_config_vp_listen *vlp;
+ int new;
+ stm_listener *lp;
+
+ if (stm.config.vp_listen == NULL)
+ stm.config.vp_listen = (stm_config_vp_listen *)
+ calloc(STM_VP_LIMIT,
+ sizeof *stm.config.vp_listen);
+
+ vlp = &stm.config.vp_listen[v];
+ new = 0;
+ lp = (vlp->num_listeners < STM_LISTENER_LIMIT) ?
+ stm_listener_add(listen_copy, addr.s_addr,
+ port, &new) : NULL;
+ if (lp) {
+ vlp->listeners[vlp->num_listeners++] = lp;
+ if (new) {
+ /*
+ * Call the regular Listen directive
+ * handler to add a standard
+ * listener to ap_listeners. This
+ * is a gross kludge but so is the
+ * way Apache manages listeners.
+ * ap_set_listener()'s third argument
+ * should be const too, but it ain't.
+ */
+ err = ap_set_listener(cmd, dummy,
+ (addr.s_addr == htonl(INADDR_ANY)) ?
+ port_string : (char *) listen_string);
+
+ /*
+ * An error from ap_set_listener()
+ * means our call to it is wrong and
+ * should be fixed.
+ */
+ STM_ASSERT(err == NULL);
+ }
+ } else
+ err = "too many listeners; re-compile with larger STM_LISTENER_LIMIT if desired";
+ } else
+ err = "invalid IP address";
+ } else
+ err = "invalid port number";
+ } else
+ err = "listen address must be of the form: ip-address:port-number or *:port-number";
+ } else
+ err = ap_psprintf(cmd->pool,
+ "value %d for %s is out of range, must be between 0 and %d inclusive"
+ "; re-compile with larger STM_VP_LIMIT if desired",
+ v, cmd->cmd->name, STM_VP_LIMIT - 1);
+ } else
+ err = "virtual processor identifier must be a number";
+ }
+
+ return err;
+}
+
+/*
+ * Intercept standard Listen directives, saving information that is not
+ * readily available later. Returns error string, or NULL on success.
+ */
+HIDDEN const char *
+stm_config_set_ap_listen(cmd_parms *cmd, void *dummy,
+ const char *listen_string)
+{
+ const char *err;
+
+ STM_TRACE(("%d: stm_config_set_ap_listen(listen_string=\"%s\")\n",
+ getpid(), listen_string));
+
+ err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+ if (err == NULL && stm.config.vp_listen)
+ err = "cannot mix Listen and " STM_CMD_VP_LISTEN " directives";
+ if (err == NULL) {
+ char *ips, *ports;
+ stm_port port;
+ struct in_addr addr;
+
+ /*
+ * Parse the Listen directive's argument so we can fill in the
+ * address and port number in the stm_listener. Then call the
+ * regular Listen directive handler since we can't call
+ * alloc_listener() directly.
+ */
+
+ ips = ap_pstrdup(cmd->pool, listen_string);
+
+ /*
+ *****************************************
+ * begin copied code from ap_set_listener()
+ */
+ ports = strchr(ips, ':');
+ if (ports != NULL) {
+ if (ports == ips) {
+ return "Missing IP address";
+ }
+ else if (ports[1] == '\0') {
+ return "Address must end in :";
+ }
+ *(ports++) = '\0';
+ }
+ else {
+ ports = ips;
+ }
+
+ port = atoi(ports);
+ if (!port) {
+ return "Port must be numeric";
+ }
+
+ if (ports == ips) { /* no address */
+ ips = APR_ANYADDR;
+ }
+ else {
+ ips[(ports - ips) - 1] = '\0';
+ }
+ /*
+ * end copied code from ap_set_listener()
+ *****************************************
+ */
+
+ if (inet_aton(ips, &addr)) {
+ int new;
+
+ if (stm_listener_add(ips, addr.s_addr, htons(port), &new)) {
+ if (new) {
+ /*
+ * ap_set_listener()'s third arg should be const but
+ * it ain't. Oh well.
+ */
+ err = ap_set_listener(cmd, dummy, (char *) listen_string);
+ } /* else ignore it */
+ } else
+ err = "too many listeners; re-compile with larger STM_LISTENER_LIMIT if desired";
+ } else
+ err = "invalid IP address";
+ }
+
+ return err;
+}
+
+/*
+ * Set the pid_file configuration parameter. Returns error string, or
+ * NULL on success.
+ */
+HIDDEN const char *
+stm_config_set_pid_file(cmd_parms *cmd, void *dummy, const char *path)
+{
+ const char *err;
+
+ STM_TRACE(("%d: stm_config_set_pid_file(path=\"%s\")\n", getpid(), path));
+
+ err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+ if (err == NULL)
+ stm.config.pid_file = ap_server_root_relative(stm.pool, path);
+
+ return err;
+}
+
+/*
+ * Set the connection_status configuration parameter. Returns error
+ * string, or NULL on success.
+ */
+HIDDEN const char *
+stm_config_set_connection_status(cmd_parms *cmd, void *dummy, int flag)
+{
+ const char *err;
+
+ STM_TRACE(("%d: stm_config_set_connection_status(flag=%d)\n", getpid(),
+ flag));
+
+ err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+ if (err == NULL)
+ stm.config.connection_status = flag;
+
+ return err;
+}
+
+/*
+ * Set the scoreboard_dir configuration parameter if the directory
+ * exists. Returns error string, or NULL on success.
+ */
+HIDDEN const char *
+stm_config_set_scoreboard_dir(cmd_parms *cmd, void *dummy, const char *path)
+{
+ const char *err;
+
+ STM_TRACE(("%d: stm_config_set_scoreboard_dir(path=\"%s\")\n", getpid(),
+ path));
+
+ err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+ if (err == NULL) {
+ const char *rpath;
+ struct stat stbuf;
+
+ rpath = ap_server_root_relative(stm.pool, path);
+ if (stat(rpath, &stbuf) == 0 && S_ISDIR(stbuf.st_mode))
+ stm.config.scoreboard_dir = rpath;
+ else
+ err = ap_psprintf(cmd->pool,
+ "%s \"%s\" does not exist or is not a directory",
+ cmd->cmd->name, rpath);
+ }
+
+ return err;
+}
+
+/*
+ * Set the core_dir configuration parameter if the directory exists.
+ * Returns error string, or NULL on success.
+ */
+HIDDEN const char *
+stm_config_set_core_dir(cmd_parms *cmd, void *dummy, const char *path)
+{
+ const char *err;
+
+ STM_TRACE(("%d: stm_config_set_core_dir(path=\"%s\")\n", getpid(), path));
+
+ err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+ if (err == NULL) {
+ const char *rpath;
+ struct stat stbuf;
+
+ rpath = ap_server_root_relative(stm.pool, path);
+ if (stat(rpath, &stbuf) == 0 && S_ISDIR(stbuf.st_mode))
+ stm.config.core_dir = rpath;
+ else
+ err = ap_psprintf(cmd->pool,
+ "%s \"%s\" does not exist or is not a directory",
+ cmd->cmd->name, rpath);
+ }
+
+ return err;
+}
+
+
+/***********************************************************************
+ * Scoreboard
+ */
+
+/*
+ * Create and return a pointer to a new scoreboard in shared memory for
+ * VP slot n, or NULL on failure.
+ */
+HIDDEN stm_scoreboard *
+stm_scoreboard_new(int n)
+{
+ stm_scoreboard *sbp;
+ char *path;
+ int fd;
+
+ STM_TRACE(("%d: stm_scoreboard_new(n=%d)\n", getpid(), n));
+
+ sbp = NULL;
+
+ path = ap_psprintf(stm.pool, "%s/%d", stm.config.scoreboard_dir, n);
+ fd = open(path, O_RDWR | O_CREAT | O_TRUNC, 0600);
+ if (fd >= 0) {
+ stm_scoreboard zsc;
+
+ /* zero-fill the new file to exactly the right length */
+ memset(&zsc, 0, sizeof zsc);
+ if (write(fd, &zsc, sizeof zsc) == sizeof zsc) {
+ void *vp;
+
+ vp = mmap(0, sizeof *sbp, PROT_READ | PROT_WRITE, MAP_SHARED, fd,
+ 0);
+ if (vp != (void *) -1)
+ sbp = (stm_scoreboard *) vp;
+ else
+ ap_log_error(APLOG_MARK, APLOG_ERR, errno, stm.server,
+ "stm_scoreboard_new: cannot map %ld bytes read-write in \"%s\": mmap",
+ (long) sizeof *sbp, path);
+ } else
+ ap_log_error(APLOG_MARK, APLOG_ERR, errno, stm.server,
+ "stm_scoreboard_new: cannot zero-fill %ld bytes in \"%s\": write",
+ (long) sizeof zsc, path);
+
+ close(fd);
+ } else
+ ap_log_error(APLOG_MARK, APLOG_ERR, errno, stm.server,
+ "stm_scoreboard_new: cannot create \"%s\" read-write: open", path);
+
+ return sbp;
+}
+
+/*
+ * Initialize/reset the given scoreboard.
+ */
+HIDDEN void
+stm_scoreboard_init(stm_scoreboard *sb)
+{
+ int i, j;
+
+ STM_TRACE(("%d: stm_scoreboard_init(sb=%p)\n", getpid(), sb));
+
+ /*
+ * All the values start out as zero but this function is used for
+ * both init and reset. Resetting a field to zero here makes it
+ * keep track of only one incarnation of a VP. Not resetting it
+ * makes it keep track since server start.
+ */
+
+ memcpy(sb->version, STM_SCOREBOARD_VERSION, sizeof sb->version);
+ sb->st_limit = STM_ST_LIMIT;
+ sb->listener_limit = STM_LISTENER_LIMIT;
+ sb->score_limit = STM_SCORE_LIMIT;
+ sb->key_size = STM_SCORE_KEY_SIZE;
+ sb->value_size = STM_SCORE_VALUE_SIZE;
+ sb->pid = -1;
+ sb->start_time = stm.start_time;
+ sb->vp_start_time = 0;
+ /* leave sb->incarnations alone */
+ sb->thread_starts = 0;
+ sb->thread_exits = 0;
+ /* leave sb->most_threads alone */
+ sb->historic_vp_connections += sb->vp_connections;
+ sb->vp_connections = 0;
+ for (i = 0; i < STM_ST_LIMIT; i++) {
+ sb->thread_connections[i] = 0;
+ for (j = 0; j < STM_SCORE_LIMIT; j++)
+ sb->scores[i][j].key[0] = 0;
+ }
+ for (i = 0; i < STM_LISTENER_LIMIT; i++) {
+ sb->listener_connections[i] = 0;
+ sb->listeners[i][0] = 0;
+ }
+}
+
+
+/***********************************************************************
+ * Listeners
+ */
+
+/*
+ * Initialize/reset the given listener.
+ */
+HIDDEN void
+stm_listener_init(stm_listener *listener)
+{
+ STM_TRACE(("%d: stm_listener_init(listener=%p,#%d)\n", getpid(),
+ listener, (int) (listener - stm.listeners)));
+
+ listener->id = (int) (listener - stm.listeners);
+ STM_ASSERT(listener->id >= 0 && listener->id < STM_LISTENER_LIMIT);
+ listener->addr_string = NULL;
+ memset(&listener->addr, 0, sizeof listener->addr);
+ listener->unique = 0;
+ listener->share_count = 0;
+ listener->alr = NULL;
+ STM_ASSERT(listener->sd == NULL); /* init or stm_listener_revert() */
+ listener->sd = NULL;
+ listener->spare_threads = 0;
+}
+
+/*
+ * Add the given IP address and port number (both in network byte order)
+ * to the list of listeners if not already there. Sets *new to 0 if
+ * already there or to 1 if not. Returns the new listener, or NULL if
+ * the listener table already contains the maximum number
+ * (STM_LISTENER_LIMIT) of listeners.
+ */
+HIDDEN stm_listener *
+stm_listener_add(const char *addr_string, stm_addr addr, stm_port port,
+ int *new)
+{
+ stm_listener *lp, *ep;
+
+ STM_TRACE(("%d: stm_listener_add(addr_string=\"%s\", addr=%#x, port=%u)\n",
+ getpid(), addr_string, ntohl(addr), ntohs(port)));
+
+ *new = 0;
+ for (lp = stm.listeners, ep = lp + stm.num_listeners; lp < ep; lp++)
+ if (lp->addr.sin_addr.s_addr == addr && lp->addr.sin_port == port)
+ return lp;
+ if (stm.num_listeners < STM_LISTENER_LIMIT) {
+ lp->addr_string = ap_pstrdup(stm.pool, addr_string);
+ lp->addr.sin_family = AF_INET;
+ lp->addr.sin_port = port;
+ lp->addr.sin_addr.s_addr = addr;
+ lp->unique = addr != htonl(INADDR_ANY);
+ stm.num_listeners++;
+ *new = 1;
+ return lp;
+ }
+
+ return NULL;
+}
+
+/*
+ * Adapt the given ap-listener so that the given stm-listener can be
+ * used instead. Returns nonzero on success, zero on failure.
+ */
+HIDDEN int
+stm_listener_usurp(stm_listener *listener, ap_listen_rec *alr)
+{
+ int sd;
+
+ STM_TRACE(("%d: stm_listener_usurp(listener=%p,#%d, alr=%p) share_count=%d\n",
+ getpid(), listener, listener->id, alr, listener->share_count));
+
+ /* the listener must have a valid address at this point */
+ STM_ASSERT(listener->addr_string);
+ STM_ASSERT(listener->share_count > 0);
+
+ listener->alr = alr;
+ if (ap_get_os_sock(&sd, alr->sd) == APR_SUCCESS) {
+ listener->sd = st_netfd_open_socket(sd);
+ if (listener->sd != NULL) {
+ /* serialize accepts only on shared listeners */
+ if (listener->share_count == 1 ||
+ st_netfd_serialize_accept(listener->sd) == 0)
+ return 1;
+ else
+ ap_log_error(APLOG_MARK, APLOG_ERR, errno, stm.server,
+ "stm_listener_usurp: st_netfd_serialize_accept");
+ } else
+ ap_log_error(APLOG_MARK, APLOG_ERR, errno, stm.server,
+ "stm_listener_usurp: st_netfd_open_socket");
+ } else
+ ap_log_error(APLOG_MARK, APLOG_ERR, errno, stm.server,
+ "stm_listener_usurp: ap_get_os_sock");
+
+ return 0;
+}
+
+/*
+ * Revert the given listener from STM control.
+ */
+HIDDEN void
+stm_listener_revert(stm_listener *listener)
+{
+ STM_TRACE(("%d: stm_listener_revert(listener=%p,#%d)\n", getpid(),
+ listener, listener->id));
+
+ st_netfd_free(listener->sd);
+ listener->sd = NULL;
+}
+
+
+/***********************************************************************
+ * Threads
+ */
+
+/*
+ * Initialize/reset the given thread which is bound to the given VP.
+ */
+HIDDEN void
+stm_thread_init(stm_thread *thread, stm_vp *vp)
+{
+ STM_TRACE(("%d: stm_thread_init(thread=%p,#%d, vp=%p,#%d)\n", getpid(),
+ thread, (int) (thread - vp->threads), vp, vp->id));
+
+ thread->id = (int) (thread - vp->threads);
+ STM_ASSERT(thread->id >= 0 && thread->id < STM_ST_LIMIT);
+ thread->health = STM_HEALTH_DEAD;
+ thread->st = 0;
+ thread->vp = vp;
+ thread->listener = NULL;
+ thread->connected = 0;
+}
+
+/*
+ * Attach the given listener to the given thread and start the given
+ * thread running. Returns nonzero on success, zero on failure.
+ */
+HIDDEN int
+stm_thread_start(stm_thread *thread, stm_listener *listener)
+{
+ STM_TRACE(("%d: stm_thread_start(thread=%p,#%d, listener=%p,#%d)\n",
+ getpid(), thread, thread->id, listener, listener->id));
+
+ /* it would be bad to clobber a running thread */
+ STM_ASSERT(thread->health == STM_HEALTH_DEAD);
+
+ thread->listener = listener;
+ thread->st = st_thread_create(stm_thread_main, thread, 0,
+ stm.config.stack_size);
+ if (thread->st) {
+ /*
+ * Note it is impossible for the new thread to run until this
+ * thread blocks. No race conditions on startup.
+ */
+ thread->health = STM_HEALTH_ALIVE;
+ return 1;
+ }
+
+ ap_log_error(APLOG_MARK, APLOG_ERR, errno, stm.server,
+ "stm_thread_start: st_thread_create");
+ return 0;
+}
+
+/*
+ * Return the thread currently running, or NULL if called by a
+ * non-thread entity (e.g., the master process or the primordial VP
+ * thread).
+ */
+HIDDEN stm_thread *
+stm_thread_self(void)
+{
+ STM_TRACE(("%d: stm_thread_self()\n", getpid()));
+
+ /*
+ * We could walk through all of STs on all of the VPs in stm looking
+ * for a match for st_thread_self() but a faster way is to use
+ * thread-specific data. The catch with TSD is that the key must be
+ * the same on all VPs, since we don't have a VP reference either.
+ * Therefore the key must be allocated before any VP.
+ */
+ return (stm_thread *) st_thread_getspecific(stm.self_key);
+}
+
+/*
+ * Thread main function. Always returns NULL. arg is a pointer to the
+ * thread.
+ */
+HIDDEN void *
+stm_thread_main(void *arg)
+{
+ stm_thread *thread;
+ stm_vp *vp;
+ stm_listener *lp;
+ long id;
+ stm_scoreboard *sbp;
+ ap_int32_t *sbtcp, *sblcp;
+ ap_pool_t *conn_pool;
+
+ STM_TRACE(("%d: => stm_thread_main(arg=%p)\n", getpid(), arg));
+
+ thread = (stm_thread *) arg;
+ st_thread_setspecific(stm.self_key, thread);
+ vp = thread->vp;
+ lp = thread->listener;
+ id = stm_id_make(vp->id, thread->id);
+ sbp = vp->scoreboard;
+ sbtcp = &sbp->thread_connections[thread->id];
+ sblcp = &sbp->listener_connections[lp->id];
+ sbp->thread_starts++;
+
+ /*
+ * Check health once before entering st_accept() to catch early stop
+ * requests (see stm_thread_stop()). Even if this ST has been
+ * st_interrupt()ed st_accept() will return EINTR only if there is
+ * no connection pending. (ST I/O functions check for interrupts
+ * only when about to block the thread.)
+ */
+ if (thread->health == STM_HEALTH_ALIVE &&
+ ap_create_pool(&conn_pool, stm.pool) == APR_SUCCESS) {
+ do {
+ struct sockaddr_in addr;
+ int addrlen;
+ st_netfd_t nsd;
+
+ /* get a connection */
+ addrlen = sizeof addr;
+ nsd = st_accept(lp->sd, (struct sockaddr *) &addr, &addrlen, -1);
+ if (nsd == NULL) {
+ if (errno == EINTR)
+ break; /* stm_thread_stop() called; terminate now */
+ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, stm.server,
+ "stm_thread_main: st_accept");
+ if (st_sleep(1) == -1 && errno == EINTR)
+ break;
+ continue;
+ }
+
+ /*
+ * I have a connection, so keep stm_thread_stop() from
+ * st_thread_interrupt()ing me now.
+ */
+ thread->connected = 1;
+
+ /*
+ * This thread is no longer "spare." Start a replacement
+ * spare thread if necessary and allowed.
+ */
+ if (--lp->spare_threads < stm.config.min_spare_threads) {
+ if (vp->num_threads < stm.config.max_threads) {
+ if (stm_thread_start(vp->dead_threads[vp->num_threads],
+ lp)) {
+ vp->num_threads++;
+ if (sbp->most_threads < vp->num_threads)
+ sbp->most_threads = vp->num_threads;
+ lp->spare_threads++;
+ }
+ } else if (!vp->max_threads_hit) {
+ vp->max_threads_hit = 1; /* report once per VP */
+ ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_NOERRNO,
+ 0, stm.server, "ran out of threads on VP %d; "
+ "increase " STM_CMD_MAX_THREADS " and/or " STM_CMD_NUM_VPS,
+ vp->pid);
+ }
+ }
+
+ /* process the connection */
+ stm_thread_process_connection(lp, conn_pool, nsd, &addr, id);
+
+ /*
+ * Connection has been closed. Clean up and return thread
+ * to spare state.
+ */
+ ap_clear_pool(conn_pool);
+ thread->connected = 0;
+ lp->spare_threads++;
+
+ /* update counters */
+ sbp->vp_connections++; /* per VP */
+ (*sbtcp)++; /* per thread */
+ (*sblcp)++; /* per listener */
+
+ if (vp->connections > 0 && --vp->connections == 0) {
+ /*
+ * This VP has processed as many connections as it's
+ * allowed. Terminate the whole VP, including this
+ * thread, immediately and gently.
+ */
+ stm_vp_stop_self(vp);
+ break;
+ }
+
+ /* check for stop requests, and for too many spare threads */
+ } while (thread->health == STM_HEALTH_ALIVE &&
+ lp->spare_threads <= stm.config.max_spare_threads);
+
+ ap_destroy_pool(conn_pool);
+ }
+
+ /* this thread is dying */
+ lp->spare_threads--;
+ vp->num_threads--;
+ vp->dead_threads[vp->num_threads] = thread;
+ sbp->thread_exits++;
+
+ /* this thread is dead */
+ stm_thread_init(thread, vp);
+
+ STM_TRACE(("%d: <= stm_thread_main(arg=%p)\n", getpid(), arg));
+
+ return NULL;
+}
+
+/*
+ * Process a connection with a client. Calls back into the main server
+ * to process requests, etc. On return, sd has been closed.
+ */
+HIDDEN void
+stm_thread_process_connection(const stm_listener *listener,
+ ap_pool_t *conn_pool, st_netfd_t sd, const struct sockaddr_in *raddr,
+ long id)
+{
+ struct sockaddr_in laddr;
+ const struct sockaddr_in *laddrp;
+
+ STM_TRACE(("%d: stm_thread_process_connection(listener=%p,#%d, conn_pool=%p, sd=%p(%d), raddr=%s:%hu, id=%ld)\n",
+ getpid(), listener, listener->id, conn_pool, sd, st_netfd_fileno(sd),
+ inet_ntoa(raddr->sin_addr), ntohs(raddr->sin_port), id));
+
+ stm_disable_nagle(sd);
+
+ if (listener->unique)
+ laddrp = &listener->addr;
+ else {
+ int addrlen;
+
+ addrlen = sizeof laddr;
+ if (getsockname(st_netfd_fileno(sd), &laddr, &addrlen) == 0)
+ laddrp = &laddr;
+ else {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, stm.server,
+ "stm_thread_process_connection: getsockname");
+ laddrp = NULL;
+ }
+ }
+
+ if (laddrp) {
+ ap_iol *iolp;
+
+ /* make all I/O on this socket use ST's I/O functions */
+ iolp = stiol_attach(sd);
+ if (iolp) {
+ BUFF *bp;
+ conn_rec *conn;
+
+ bp = ap_bcreate(conn_pool, B_RDWR);
+ ap_bpush_iol(bp, iolp);
+ conn = ap_new_connection(conn_pool, stm.server, bp, raddr,
+ laddrp, id);
+ ap_process_connection(conn);
+ ap_lingering_close(conn);
+ } else {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, stm.server,
+ "stm_thread_process_connection: stiol_attach");
+ st_netfd_close(sd);
+ }
+ } else
+ st_netfd_close(sd);
+}
+
+/*
+ * Ask the given thread to terminate. Must be called only from within
+ * the VP of the given thread.
+ */
+HIDDEN void
+stm_thread_stop(stm_thread *thread)
+{
+ STM_TRACE(("%d: stm_thread_stop(thread=%p,#%d)\n", getpid(), thread,
+ thread->id));
+ STM_ASSERT(getpid() == thread->vp->pid);
+
+ if (thread->health == STM_HEALTH_ALIVE) {
+ thread->health = STM_HEALTH_DYING;
+
+ /*
+ * break the thread out of st_accept() if necessary, and no
+ * other I/O function
+ */
+ if (!thread->connected)
+ st_thread_interrupt(thread->st);
+ }
+}
+
+/*
+ * Return nonzero if the given thread has been terminated or asked to
+ * terminate.
+ */
+HIDDEN int
+stm_thread_stopped(stm_thread *thread)
+{
+ STM_TRACE(("%d: stm_thread_stopped(thread=%p,#%d)\n", getpid(), thread,
+ thread->id));
+
+ return thread->health != STM_HEALTH_ALIVE;
+}
+
+
+/***********************************************************************
+ * VPs
+ */
+
+/*
+ * Initialize/reset the given VP. Returns nonzero on success, zero on
+ * failure.
+ */
+HIDDEN int
+stm_vp_init(stm_vp *vp)
+{
+ int i;
+
+ STM_TRACE(("%d: stm_vp_init(vp=%p,#%d)\n", getpid(), vp,
+ (int) (vp - stm.vps)));
+
+ vp->id = (int) (vp - stm.vps);
+ STM_ASSERT(vp->id >= 0 && vp->id < STM_VP_LIMIT);
+ vp->pid = -1;
+ vp->health = STM_HEALTH_DEAD;
+ vp->pool = NULL;
+ vp->stop_pipe[0] = -1;
+ vp->stop_pipe[1] = -1;
+ /* leave vp->num_listeners and vp->listeners alone */
+ vp->connections = stm.config.vp_connections;
+ vp->max_threads_hit = 0;
+ vp->num_threads = 0;
+ for (i = 0; i < STM_ST_LIMIT; i++) {
+ stm_thread_init(&vp->threads[i], vp);
+ vp->dead_threads[i] = &vp->threads[i];
+ }
+
+ if (vp->scoreboard == NULL) {
+ /*
+ * create scoreboard even if !stm.config.connection_status
+ * because we may want them after a restart and we keep other
+ * stats in there
+ */
+ vp->scoreboard = stm_scoreboard_new(vp->id);
+ if (vp->scoreboard == NULL)
+ return 0;
+ }
+ stm_scoreboard_init(vp->scoreboard);
+
+ return 1;
+}
+
+/*
+ * Initialize the listeners for the given VP from the stm.listeners and
+ * stm.config.vp_listen tables. Returns nonzero on success, zero on
+ * failure.
+ */
+HIDDEN int
+stm_vp_listen(stm_vp *vp)
+{
+ int i;
+
+ STM_TRACE(("%d: stm_vp_listen(vp=%p,#%d)\n", getpid(), vp, vp->id));
+
+ if (stm.config.vp_listen
+#ifdef STM_DEBUG
+ && !stm.debug.one_process
+#endif
+ ) {
+ const stm_config_vp_listen *vlp;
+
+ /*
+ * VPListen directives were used. Each VP listens only to its
+ * designated listeners.
+ */
+
+ vlp = &stm.config.vp_listen[vp->id];
+ for (i = 0; i < vlp->num_listeners; i++) {
+ vp->listeners[i] = vlp->listeners[i];
+ vp->listeners[i]->share_count++;
+ }
+ vp->num_listeners = i;
+ while (i < STM_LISTENER_LIMIT)
+ vp->listeners[i++] = NULL;
+
+ if (vp->num_listeners < 1) {
+ ap_log_error(APLOG_MARK,
+ APLOG_CRIT | APLOG_STARTUP | APLOG_NOERRNO, 0, stm.server,
+ "no listeners for virtual processor %d", vp->id);
+ return 0;
+ }
+ } else {
+ /*
+ * Listen directives were used, or no Listen or VPListen
+ * directives were used at all. Each VP listens to all
+ * listeners.
+ */
+
+ vp->num_listeners = stm.num_listeners;
+ for (i = 0; i < vp->num_listeners; i++) {
+ vp->listeners[i] = &stm.listeners[i];
+ vp->listeners[i]->share_count++;
+ }
+ while (i < STM_LISTENER_LIMIT)
+ vp->listeners[i++] = NULL;
+ }
+
+ /* this VP must have at least one listener at this point */
+ STM_ASSERT(vp->num_listeners > 0);
+
+ return 1;
+}
+
+/*
+ * Start the given VP running. Returns nonzero on success, zero on
+ * failure.
+ */
+HIDDEN int
+stm_vp_start(stm_vp *vp)
+{
+ pid_t pid;
+
+ STM_TRACE(("%d: stm_vp_start(vp=%p,#%d)\n", getpid(), vp, vp->id));
+
+ /* it would be bad to clobber a running VP */
+ STM_ASSERT(vp->health == STM_HEALTH_DEAD);
+
+#ifdef STM_DEBUG
+ if (stm.debug.one_process) {
+ vp->pid = getpid();
+ stm_vp_bind(vp);
+ stm_vp_main(vp);
+ /*NOTREACHED*/
+ return 0;
+ }
+#endif
+
+ pid = fork();
+ switch (pid) {
+ case -1:
+ ap_log_error(APLOG_MARK, APLOG_ERR, errno, stm.server,
+ "stm_vp_start: fork");
+ sleep(10); /* avoid thrashing the system */
+ return 0;
+ case 0:
+ vp->pid = getpid();
+ stm_vp_bind(vp); /* in case child runs first */
+ stm_vp_main(vp);
+ /*NOTREACHED*/
+ exit(APEXIT_CHILDFATAL);
+ break;
+ default:
+ vp->pid = pid;
+ stm_vp_bind(vp); /* in case parent runs first */
+ vp->health = STM_HEALTH_ALIVE;
+ break;
+ }
+
+ return 1;
+}
+
+/*
+ * Bind the given VP to the CPU to which it should be bound, if any.
+ */
+#if defined(IRIX) && IRIX >= 50
+# include
+#endif
+HIDDEN void
+stm_vp_bind(stm_vp *vp)
+{
+ STM_TRACE(("%d: stm_vp_bind(vp=%p,#%d)\n", getpid(), vp, vp->id));
+
+ /* we need the VP's pid to bind it */
+ STM_ASSERT(vp->pid > 0);
+
+ if (stm.config.vp_bind) {
+ int cpu;
+
+ cpu = stm.config.vp_bind[vp->id];
+ if (cpu >= 0) {
+#if defined(IRIX) && IRIX >= 50
+ /* only Irix 5.0 and beyond have sysmp(MP_MUSTRUN_PID) */
+ if (sysmp(MP_MUSTRUN_PID, cpu, vp->pid) == -1)
+ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, stm.server,
+ "cannot bind process %d to cpu %d: sysmp(MP_MUSTRUN_PID)",
+ vp->pid, cpu);
+#else
+ ap_log_error(APLOG_MARK, APLOG_NOTICE | APLOG_NOERRNO, 0,
+ stm.server, "don't know how to bind process %d to cpu %d",
+ vp->pid, cpu);
+#endif
+ }
+ }
+}
+
+/*
+ * Establish signal dispositions for VP processes.
+ */
+HIDDEN void
+stm_vp_signals(void)
+{
+ STM_TRACE(("%d: stm_vp_signals()\n", getpid()));
+
+ /* adjust signal dispositions this VP inherited from the master process */
+
+ stm_signal_default(SIGTERM);
+#ifdef SIGINT
+ stm_signal_default(SIGINT);
+#endif
+ stm_signal_catch(SIGHUP, stm_vp_onsig_stop);
+ stm_signal_ignore(SIGWINCH);
+}
+
+/*
+ * VP main function. Does not return.
+ */
+HIDDEN void
+stm_vp_main(stm_vp *vp)
+{
+ st_netfd_t stop_fd;
+ int i;
+
+ STM_TRACE(("%d: => stm_vp_main(vp=%p,#%d)\n", getpid(), vp, vp->id));
+
+ /*
+ * Set stm.num_vps to our VP index so asynchronous global functions
+ * like stm_vp_onsig_stop() and stm_vp_onexit() have a handle on the
+ * current VP.
+ */
+ stm.num_vps = vp->id;
+
+ vp->health = STM_HEALTH_ALIVE;
+ vp->scoreboard->pid = vp->pid;
+ vp->scoreboard->vp_start_time = ap_now();
+ vp->scoreboard->incarnations++;
+
+ if (unixd_setup_child() != 0)
+ exit(APEXIT_CHILDINIT);
+
+ /*
+ * Create the stop pipe, a safe means for this VP to signal
+ * asynchronous events.
+ */
+ if (pipe(vp->stop_pipe) < 0) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, errno, stm.server,
+ "stm_vp_main: pipe");
+ exit(APEXIT_CHILDINIT);
+ }
+ stop_fd = st_netfd_open(vp->stop_pipe[0]);
+ if (stop_fd == NULL) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, errno, stm.server,
+ "stm_vp_main: st_netfd_open");
+ exit(APEXIT_CHILDINIT);
+ }
+
+ /*
+ * Create a pool that endures for the life of this VP, run child
+ * init hooks on it, and use it to close both ends of the stop_pipe
+ * when execing another program.
+ */
+ ap_create_pool(&vp->pool, stm.pool);
+ ap_child_init_hook(vp->pool, stm.server);
+ ap_register_cleanup(vp->pool, vp, ap_null_cleanup, stm_vp_cleanup);
+ atexit(stm_vp_onexit);
+
+ /* Start the requested number of spare threads per listener. */
+ for (i = 0; i < vp->num_listeners && stm.state == STM_STATE_RUN; i++) {
+ stm_listener *lp;
+ int start_error;
+
+ lp = vp->listeners[i];
+ start_error = 0;
+
+ ap_snprintf(vp->scoreboard->listeners[lp->id],
+ sizeof vp->scoreboard->listeners[lp->id], "%s:%u",
+ lp->addr_string, ntohs(lp->addr.sin_port));
+
+ while (lp->spare_threads < stm.config.start_threads &&
+ vp->num_threads < stm.config.max_threads) {
+ if (stm_thread_start(&vp->threads[vp->num_threads], lp)) {
+ vp->num_threads++;
+ lp->spare_threads++;
+ } else {
+ start_error = 1;
+ break;
+ }
+ }
+ if (vp->scoreboard->most_threads < vp->num_threads)
+ vp->scoreboard->most_threads = vp->num_threads;
+ if (lp->spare_threads < stm.config.start_threads) {
+ /*
+ * We didn't make all the threads we wanted to. Either we
+ * exceeded the total number of threads per VP or a thread
+ * failed to start. Warn, once, about the former condition.
+ */
+ if (!start_error && !vp->max_threads_hit) {
+ STM_ASSERT(vp->num_threads >= stm.config.max_threads);
+ vp->max_threads_hit = 1; /* report once per VP */
+ ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_NOERRNO, 0,
+ stm.server, "ran out of threads on VP %d; "
+ "increase " STM_CMD_MAX_THREADS,
+ vp->pid);
+ }
+
+ /* Also warn about too-few threads per listener. */
+ ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_NOERRNO, 0,
+ stm.server,
+ "started only %d out of %d thread(s) listening to %s on virtual processor %d",
+ lp->spare_threads, stm.config.start_threads,
+ vp->scoreboard->listeners[lp->id], vp->pid);
+ }
+ }
+
+ stm_vp_signals();
+ if (stm.state != STM_STATE_RUN) {
+ /* caught signal on parent's handler: do the right thing */
+ exit(APEXIT_CHILDINIT);
+ }
+
+ if (vp->num_threads > 0) {
+ char c;
+
+ /*
+ * Block here for data on the pipe. This st_read() returns only
+ * when the pipe has data on it (see stm_vp_stop_self()) and
+ * that means it's time to shut down all the threads.
+ */
+ st_read(stop_fd, &c, sizeof c, -1);
+ for (i = 0; i < STM_ST_LIMIT; i++)
+ stm_thread_stop(&vp->threads[i]);
+ }
+
+ /* this VP is dying */
+ st_netfd_close(stop_fd);
+ close(vp->stop_pipe[1]);
+ STM_TRACE(("%d: <= stm_vp_main(vp=%p,#%d)\n", getpid(), vp, vp->id));
+ st_thread_exit(NULL);
+ /*NOTREACHED*/
+}
+
+/*
+ * Ask the given VP to terminate. Must be called only by the master
+ * process.
+ */
+HIDDEN void
+stm_vp_stop(stm_vp *vp)
+{
+ STM_TRACE(("%d: stm_vp_stop(vp=%p,#%d)\n", getpid(), vp, vp->id));
+ STM_ASSERT(stm.pid == getpid());
+
+ if (vp->health == STM_HEALTH_ALIVE) {
+ vp->health = STM_HEALTH_DYING;
+ STM_ASSERT(vp->pid > 0);
+ kill(vp->pid, SIGHUP);
+ }
+}
+
+/*
+ * Ask the given VP to terminate. Must be called only from within the
+ * given VP.
+ */
+HIDDEN void
+stm_vp_stop_self(stm_vp *vp)
+{
+ char c;
+
+ STM_TRACE(("%d: stm_vp_stop_self(vp=%p,#%d)\n", getpid(), vp, vp->id));
+ STM_ASSERT(vp->pid == getpid());
+
+ if (vp->health == STM_HEALTH_ALIVE) {
+ vp->health = STM_HEALTH_DYING;
+ write(vp->stop_pipe[1], &c, sizeof c);
+ }
+}
+
+/*
+ * Signal handler for a VP process: Terminate this VP.
+ */
+HIDDEN void
+stm_vp_onsig_stop(int sig)
+{
+ STM_TRACE(("%d: stm_vp_onsig_stop(sig=%d)\n", getpid(), sig));
+
+ stm_vp_stop_self(&stm.vps[stm.num_vps]);
+}
+
+/*
+ * Atexit handler for a VP process: Destroy the VP's pool.
+ */
+HIDDEN void
+stm_vp_onexit(void)
+{
+ STM_TRACE(("%d: stm_vp_onexit()\n", getpid()));
+
+ ap_destroy_pool(stm.vps[stm.num_vps].pool);
+}
+
+/*
+ * Clean up the VP to prepare to exec another program.
+ */
+HIDDEN ap_status_t
+stm_vp_cleanup(void *arg)
+{
+ stm_vp *vp;
+
+ vp = (stm_vp *) arg;
+ STM_TRACE(("%d: stm_vp_cleanup(vp=%p,#%d)\n", getpid(), vp, vp->id));
+
+ close(vp->stop_pipe[0]);
+ close(vp->stop_pipe[1]);
+
+ return APR_SUCCESS;
+}
+
+
+/***********************************************************************
+ * Main STM
+ */
+
+/*
+ * Initialize/reset the STM MPM. Returns nonzero on success, zero on
+ * failure.
+ */
+HIDDEN int
+stm_init(ap_pool_t *pool, server_rec *server)
+{
+ int first;
+ int i;
+ ap_listen_rec *alp;
+
+ STM_TRACE(("%d: stm_init(pool=%p, server=%p) #%d\n", getpid(), pool,
+ server, stm.initialized));
+
+ /* verify all configuration parameters' bounds checks */
+ STM_ASSERT(stm.config.num_vps > 0 && stm.config.num_vps <= STM_VP_LIMIT);
+ STM_ASSERT(stm.config.start_threads > 0 &&
+ stm.config.start_threads <= STM_ST_LIMIT);
+ STM_ASSERT(stm.config.min_spare_threads > 0 &&
+ stm.config.min_spare_threads <= STM_ST_LIMIT);
+ STM_ASSERT(stm.config.max_spare_threads > 0 &&
+ stm.config.max_spare_threads <= STM_ST_LIMIT);
+ STM_ASSERT(stm.config.max_threads > 0 &&
+ stm.config.max_threads <= STM_ST_LIMIT);
+
+ /* first is nonzero to initialize, zero to reset */
+ first = stm.initialized++ == 0;
+
+ if (first) {
+ int maxfds;
+
+ stm.start_time = ap_now();
+
+ /* initialize the state threads library */
+ if (st_init() != 0) {
+ ap_log_error(APLOG_MARK, APLOG_CRIT, errno, server,
+ "cannot initialize state threads library");
+ return 0;
+ }
+
+ /*
+ * Are there enough file descriptors for max_threads connections
+ * per VP?
+ */
+ maxfds = st_getfdlimit() - 10; /* or so */
+ if (stm.config.max_threads > maxfds) {
+ ap_log_error(APLOG_MARK, APLOG_CRIT | APLOG_NOERRNO, 0, server,
+ STM_CMD_MAX_THREADS " %d exceeds "
+ "number of file descriptors per process %d; "
+ "decrease " STM_CMD_MAX_THREADS " and compensate "
+ "by increasing " STM_CMD_NUM_VPS,
+ stm.config.max_threads, maxfds);
+ return 0;
+ }
+
+ st_timecache_set(1);
+ }
+
+ /*
+ * Initialize/reset each member of stm not already initialized/reset
+ * by stm_pre_config() and the config routines.
+ */
+ stm.pool = pool;
+ stm.server = server;
+ stm.state = STM_STATE_RUN;
+ /*
+ * leave alone:
+ * stm.initialized (incremented above)
+ * stm.num_restarts (stm_pre_config())
+ * stm.pid (stm_pre_config())
+ * stm.config (stm_pre_config())
+ * stm.num_listeners (stm_pre_config())
+ * stm.listeners (stm_pre_config())
+ */
+ stm.num_vps = 0;
+ for (i = 0; i < STM_VP_LIMIT; i++)
+ if (!stm_vp_init(&stm.vps[i]))
+ return 0;
+ if (first && st_key_create(&stm.self_key, NULL) == -1) {
+ ap_log_error(APLOG_MARK, APLOG_CRIT, errno, server,
+ "stm_init: st_key_create");
+ return 0;
+ }
+ /* leave stm.debug alone (stm_pre_config()) */
+
+ /* establish AP listeners and convert them to STM listeners */
+ if (ap_listeners == NULL) {
+ int new;
+
+ /*
+ * There were no Listen or VPListen directives so
+ * ap_listen_open() will add a default AP listener (yecch). Add
+ * a matching STM listener.
+ */
+ STM_ASSERT(stm.num_listeners == 0);
+ stm_listener_add(APR_ANYADDR, htonl(INADDR_ANY), htons(server->port),
+ &new);
+ STM_ASSERT(new);
+ }
+ STM_ASSERT(stm.num_listeners > 0);
+ for (i = 0; i < stm.config.num_vps; i++)
+ if (!stm_vp_listen(&stm.vps[i]))
+ return 0;
+ if (ap_listen_open(server->process, server->port) == 0) {
+ for (alp = ap_listeners; alp; alp = alp->next) {
+ char *addr_string;
+ struct in_addr addr;
+ stm_port port;
+ stm_listener *lp, *ep;
+
+ /* find the STM listener for the AP listener */
+
+ if (ap_get_local_ipaddr(&addr_string, alp->sd) != APR_SUCCESS ||
+ !inet_aton(addr_string, &addr))
+ addr.s_addr = htonl(INADDR_ANY);
+ if (ap_get_local_port(&port, alp->sd) != APR_SUCCESS)
+ port = server->port;
+ port = htons(port);
+
+ for (lp = stm.listeners, ep = lp + stm.num_listeners; lp < ep;
+ lp++) {
+ if (lp->addr.sin_addr.s_addr == addr.s_addr &&
+ lp->addr.sin_port == port) {
+ if (stm_listener_usurp(lp, alp))
+ break;
+ else {
+ ap_log_error(APLOG_MARK,
+ APLOG_CRIT | APLOG_STARTUP | APLOG_NOERRNO,
+ 0, server,
+ "cannot initialize listen socket for %s:%u",
+ addr_string, ntohs(port));
+ return 0;
+ }
+ }
+ }
+
+ /*
+ * Every AP listener must have an STM listener (and vice
+ * versa, which we check below).
+ */
+ STM_ASSERT(lp < ep);
+ }
+ } else {
+ ap_log_error(APLOG_MARK, APLOG_CRIT | APLOG_STARTUP | APLOG_NOERRNO,
+ 0, server, "no listening sockets available");
+ return 0;
+ }
+
+#ifdef STM_DEBUG
+ for (i = 0; i < stm.num_listeners; i++)
+ STM_ASSERT(stm.listeners[i].share_count > 0 &&
+ stm.listeners[i].alr && stm.listeners[i].sd);
+#endif
+
+ if (stm.config.min_spare_threads > stm.config.max_spare_threads)
+ stm.config.min_spare_threads = stm.config.max_spare_threads;
+
+ /*
+ * Warn if the number of threads per VP needed to satisfy all the
+ * listeners exceeds the maximum. (Other MPM's measure
+ * start/min-spare/max-spare threads per child process but STM
+ * measures them per listener so these warnings may catch
+ * configuration mistakes caused by the different semantics.)
+ */
+ for (i = 0; i < stm.config.num_vps; i++) {
+ int nl = stm.vps[i].num_listeners;
+
+ if (nl * stm.config.start_threads > stm.config.max_threads)
+ ap_log_error(APLOG_MARK,
+ APLOG_WARNING | APLOG_STARTUP | APLOG_NOERRNO, 0, server,
+ "too few threads: %d " STM_CMD_START_THREADS
+ " for each of %d listener(s) on virtual processor %d exceeds limit of %d "
+ STM_CMD_MAX_THREADS "; decrease " STM_CMD_START_THREADS
+ " or increase " STM_CMD_MAX_THREADS,
+ stm.config.start_threads, nl, i, stm.config.max_threads);
+ if (nl * stm.config.min_spare_threads > stm.config.max_threads)
+ ap_log_error(APLOG_MARK,
+ APLOG_WARNING | APLOG_STARTUP | APLOG_NOERRNO, 0, server,
+ "too few threads: %d " STM_CMD_MIN_SPARE_THREADS
+ " for each of %d listener(s) on virtual processor %d exceeds limit of %d "
+ STM_CMD_MAX_THREADS "; decrease " STM_CMD_MIN_SPARE_THREADS
+ " or increase " STM_CMD_MAX_THREADS,
+ stm.config.min_spare_threads, nl, i, stm.config.max_threads);
+ if (nl * stm.config.max_spare_threads > stm.config.max_threads)
+ ap_log_error(APLOG_MARK,
+ APLOG_WARNING | APLOG_STARTUP | APLOG_NOERRNO, 0, server,
+ "too few threads: %d " STM_CMD_MAX_SPARE_THREADS
+ " for each of %d listener(s) on virtual processor %d exceeds limit of %d "
+ STM_CMD_MAX_THREADS "; decrease " STM_CMD_MAX_SPARE_THREADS
+ " or increase " STM_CMD_MAX_THREADS,
+ stm.config.max_spare_threads, nl, i, stm.config.max_threads);
+ }
+
+ /* Warn about CPU- or listen-binding for unused VPs */
+ for (i = stm.config.num_vps; i < STM_VP_LIMIT; i++) {
+ if (stm.config.vp_bind && stm.config.vp_bind[i] != -1)
+ ap_log_error(APLOG_MARK,
+ APLOG_WARNING | APLOG_STARTUP | APLOG_NOERRNO, 0, server,
+ STM_CMD_VP_BIND " directive(s) for unused virtual processor %d ignored: "
+ STM_CMD_NUM_VPS " is %d",
+ i, stm.config.num_vps);
+ if (stm.config.vp_listen && stm.config.vp_listen[i].num_listeners > 0)
+ ap_log_error(APLOG_MARK,
+ APLOG_WARNING | APLOG_STARTUP | APLOG_NOERRNO, 0, server,
+ STM_CMD_VP_LISTEN " directive(s) for unused virtual processor %d ignored: "
+ STM_CMD_NUM_VPS " is %d",
+ i, stm.config.num_vps);
+ }
+
+ return 1;
+}
+
+/*
+ * Finalize the STM in preparation for server restart or shutdown.
+ */
+HIDDEN void
+stm_fini(void)
+{
+ int i;
+
+ STM_TRACE(("%d: stm_fini()\n", getpid()));
+
+ for (i = 0; i < stm.num_listeners; i++)
+ stm_listener_revert(&stm.listeners[i]);
+}
+
+/*
+ * Establish signal dispositions for the master process (and embryonic
+ * VP processes).
+ */
+HIDDEN void
+stm_signals(void)
+{
+ STM_TRACE(("%d: stm_signals()\n", getpid()));
+
+#ifdef SIGSEGV
+ stm_signal_core(SIGSEGV);
+#endif
+#ifdef SIGBUS
+ stm_signal_core(SIGBUS);
+#endif
+#ifdef SIGABORT
+ stm_signal_core(SIGABORT);
+#endif
+#ifdef SIGABRT
+ stm_signal_core(SIGABRT);
+#endif
+#ifdef SIGILL
+ stm_signal_core(SIGILL);
+#endif
+
+ stm_signal_catch(SIGTERM, stm_onsig_shutdown);
+#ifdef SIGINT
+ stm_signal_catch(SIGINT, stm_onsig_shutdown);
+#endif
+#ifdef SIGXCPU
+ stm_signal_default(SIGXCPU);
+#endif
+#ifdef SIGXFSZ
+ stm_signal_default(SIGXFSZ);
+#endif
+#ifdef SIGPIPE
+ stm_signal_ignore(SIGPIPE);
+#endif
+
+ stm_signal_catch(SIGHUP, stm_onsig_restart);
+ stm_signal_catch(SIGWINCH, stm_onsig_restart);
+}
+
+/*
+ * Master process main function. Returns when server restart or
+ * shutdown has been requested.
+ */
+HIDDEN void
+stm_main(void)
+{
+ STM_TRACE(("%d: => stm_main()\n", getpid()));
+
+ stm_signals();
+
+ /* start VPs */
+ while (stm.state == STM_STATE_RUN &&
+ stm.num_vps < stm.config.num_vps &&
+ stm_vp_start(&stm.vps[stm.num_vps]))
+ stm.num_vps++;
+ if (stm.state == STM_STATE_RUN && stm.num_vps < stm.config.num_vps)
+ ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_STARTUP | APLOG_NOERRNO,
+ 0, stm.server, "started only %d out of %d virtual processor(s)",
+ stm.num_vps, stm.config.num_vps);
+
+ /* wait for shutdown or restart, restarting VPs as needed */
+ if (stm.num_vps > 0) {
+ while (stm.state == STM_STATE_RUN) {
+ pid_t pid;
+ int status;
+
+ pid = wait(&status);
+ if (pid > 0)
+ stm_reap(pid, status);
+ }
+ } else
+ stm.state = STM_STATE_SHUTDOWN;
+
+ STM_TRACE(("%d: <= stm_main(), state %d\n", getpid(), stm.state));
+}
+
+/*
+ * Reap a child process. If the process was a VP, start a replacement VP.
+ */
+HIDDEN void
+stm_reap(pid_t pid, int status)
+{
+ stm_grief grief;
+
+ STM_TRACE(("%d: stm_reap(pid=%d, status=%#x)\n", getpid(), pid, status));
+
+ grief = stm_mourn(pid, status);
+ if (grief != STM_GRIEF_FATAL) {
+ int i;
+
+ for (i = 0; i < STM_VP_LIMIT; i++)
+ if (stm.vps[i].pid == pid)
+ break;
+ if (i < STM_VP_LIMIT) {
+ /* this was a VP; finish killing it */
+ stm.num_vps--;
+ stm_vp_init(&stm.vps[i]);
+
+ /* replace it */
+ if (stm.num_vps < stm.config.num_vps &&
+ stm_vp_start(&stm.vps[i])) {
+ stm.num_vps++;
+ if (grief != STM_GRIEF_NORMAL)
+ sleep(5); /* avoid thrashing */
+ }
+ }
+ } else
+ stm.state = STM_STATE_SHUTDOWN;
+}
+
+/*
+ * Evaluate the seriousness of a child process's death and log a warning if
+ * appropriate. Returns:
+ * STM_GRIEF_NORMAL if the death was expected (e.g., voluntary VP death
+ * after processing its allowed number of connections) or irrelevent
+ * (e.g., not a VP)
+ * STM_GRIEF_FATAL if the death was catastrophic (e.g., VP botched an
+ * assertion) and the server should terminate too
+ * STM_GRIEF_SURPRISE if the death was neither expected nor fatal (e.g.,
+ * VP failed to initialize)
+ */
+HIDDEN stm_grief
+stm_mourn(pid_t pid, int status)
+{
+ stm_grief grief;
+
+ STM_TRACE(("%d: stm_mourn(pid=%d, status=%#x)\n", getpid(), pid, status));
+
+ grief = STM_GRIEF_NORMAL;
+ if (WIFEXITED(status)) {
+ int ev = WEXITSTATUS(status);
+
+ switch (ev) {
+ case 0:
+ break;
+ case APEXIT_CHILDFATAL:
+ ap_log_error(APLOG_MARK, APLOG_CRIT | APLOG_NOERRNO, 0, stm.server,
+ "child %d returned a fatal error; shutting down", pid);
+ grief = STM_GRIEF_FATAL;
+ break;
+ case APEXIT_CHILDINIT:
+ ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, stm.server,
+ "child %d failed to initialize", pid);
+ grief = STM_GRIEF_SURPRISE;
+ break;
+ default:
+ ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, stm.server,
+ "child %d exited with unexpected status %d", pid, ev);
+ grief = STM_GRIEF_SURPRISE;
+ break;
+ }
+ } else if (WIFSIGNALED(status)) {
+ int sig = WTERMSIG(status);
+
+ switch (sig) {
+#ifdef STM_DEBUG
+ case SIGABRT:
+ ap_log_error(APLOG_MARK, APLOG_CRIT | APLOG_NOERRNO, 0, stm.server,
+ "child %d aborted, possible assertion botch; shutting down",
+ pid);
+ grief = STM_GRIEF_FATAL;
+ break;
+#endif
+ case SIGTERM:
+ case SIGHUP:
+ case SIGUSR1:
+ case SIGKILL:
+ break;
+ default:
+ ap_log_error(APLOG_MARK, APLOG_NOTICE | APLOG_NOERRNO, 0,
+ stm.server,
+ "child pid %d exit signal %d"
+#ifdef SYS_SIGLIST
+ " (%s)"
+#endif
+ , pid, sig
+#ifdef SYS_SIGLIST
+ , (sig < NumSIG) ? SYS_SIGLIST[sig] : "?"
+#endif
+ );
+ if (WCOREDUMP(status))
+ ap_log_error(APLOG_MARK, APLOG_NOTICE | APLOG_NOERRNO, 0,
+ stm.server, "-- possible core dump in %s",
+ stm.config.core_dir);
+#ifdef STM_DEBUG
+ grief = STM_GRIEF_FATAL;
+#else
+ grief = STM_GRIEF_SURPRISE;
+#endif
+ break;
+ }
+ }
+
+ return grief;
+}
+
+/*
+ * Wait a limited amount of time for all VPs to terminate. Kill them if
+ * they take too long to terminate themselves. The caller must have
+ * already commenced their termination.
+ */
+HIDDEN void
+stm_doomsday(void)
+{
+ int tries;
+ st_utime_t waittime;
+
+ STM_TRACE(("%d: stm_doomsday()\n", getpid()));
+
+ /* wait increasing intervals but not forever */
+ for (tries = 0, waittime = 10000;
+ stm.num_vps > 0 && tries <= 5;
+ tries++, waittime *= 4) {
+ stm_vp *vp;
+
+ st_usleep(waittime);
+
+ /* see who's dead */
+ for (vp = stm.vps; vp < &stm.vps[STM_VP_LIMIT]; vp++) {
+ if (vp->health != STM_HEALTH_DEAD) {
+ pid_t pid;
+ int status;
+
+ STM_ASSERT(vp->pid > 0);
+ pid = waitpid(vp->pid, &status, WNOHANG);
+ if (pid == vp->pid || pid == -1) {
+ if (pid > 0)
+ stm_mourn(pid, status);
+ vp->health = STM_HEALTH_DEAD;
+ stm.num_vps--;
+ /*
+ * don't need to stm_vp_init(vp) because mpm is
+ * exiting and will be reinitialized later if
+ * necessary
+ */
+ } else {
+ switch (tries) {
+ case 0: /* 10 ms since doomsday */
+ case 1: /* 50 ms since doomsday */
+ break;
+ case 2: /* 210 ms since doomsday */
+ case 3: /* 850 ms since doomsday */
+ ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_NOERRNO,
+ 0, stm.server,
+ "waiting for process %d to die, trying SIGTERM",
+ vp->pid);
+ kill(vp->pid, SIGTERM);
+ break;
+ case 4: /* 3.4 sec since doomsday */
+ ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_NOERRNO,
+ 0, stm.server,
+ "waiting for process %d to die, trying SIGKILL",
+ vp->pid);
+ kill(vp->pid, SIGKILL);
+ break;
+ default: /* >= 14 sec since doomsday */
+ ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_NOERRNO,
+ 0, stm.server,
+ "could not make process %d die, giving up",
+ vp->pid);
+ break;
+ }
+ }
+ }
+ }
+ }
+}
+
+
+/***********************************************************************
+ * Signal helper routines
+ */
+
+/*
+ * Direct the given signal to the given handler in a platform-neutral
+ * way.
+ */
+HIDDEN void
+stm_signal_catch(int sig, void (*handler)(int))
+{
+#ifdef NO_USE_SIGACTION
+ STM_TRACE(("%d: stm_signal_catch(sig=%d, handler=%p)\n", getpid(), sig,
+ handler));
+
+ signal(sig, handler);
+#else
+ struct sigaction sa;
+
+ STM_TRACE(("%d: stm_signal_catch(sig=%d, handler=%p)\n", getpid(), sig,
+ handler));
+
+ sa.sa_flags = 0;
+ sa.sa_handler = handler;
+ sigemptyset(&sa.sa_mask);
+ if (sigaction(sig, &sa, NULL) != 0)
+ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, stm.server,
+ "stm_signal_catch: sigaction(%d)", sig);
+#endif
+}
+
+/*
+ * Set the given signal to its default disposition.
+ */
+HIDDEN void
+stm_signal_default(int sig)
+{
+ stm_signal_catch(sig, SIG_DFL);
+}
+
+/*
+ * Set the given signal to be ignored.
+ */
+HIDDEN void
+stm_signal_ignore(int sig)
+{
+ stm_signal_catch(sig, SIG_IGN);
+}
+
+/*
+ * Set the given signal to dump core in the designated core dump
+ * directory.
+ */
+HIDDEN void
+stm_signal_core(int sig)
+{
+#ifdef NO_USE_SIGACTION
+ STM_TRACE(("%d: stm_signal_core(sig=%d)\n", getpid(), sig));
+
+ signal(sig, stm_onsig_core);
+#else
+ struct sigaction sa;
+
+ STM_TRACE(("%d: stm_signal_core(sig=%d)\n", getpid(), sig));
+
+# if defined(SA_ONESHOT)
+ sa.sa_flags = SA_ONESHOT;
+# elif defined(SA_RESETHAND)
+ sa.sa_flags = SA_RESETHAND;
+# else
+ sa.sa_flags = 0;
+# endif
+ sigemptyset(&sa.sa_mask);
+ sa.sa_handler = stm_onsig_core;
+ if (sigaction(sig, &sa, NULL) != 0)
+ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, stm.server,
+ "stm_signal_core: sigaction(%d)", sig);
+#endif
+}
+
+
+/***********************************************************************
+ * Signal handlers
+ */
+
+/*
+ * Dump core in the designated core dump directory.
+ */
+HIDDEN void
+stm_onsig_core(int sig)
+{
+ STM_TRACE(("%d: stm_onsig_core(sig=%d)\n", getpid(), sig));
+
+ if (stm.config.core_dir)
+ chdir(stm.config.core_dir);
+ stm_signal_default(sig);
+ kill(getpid(), sig);
+}
+
+/*
+ * Begin server restart. Only SIGWINCH uses graceful restart.
+ */
+HIDDEN void
+stm_onsig_restart(int sig)
+{
+ STM_TRACE(("%d: stm_onsig_restart(sig=%d)\n", getpid(), sig));
+
+ stm.state = (sig == SIGWINCH) ? STM_STATE_GRACEFUL : STM_STATE_RESTART;
+}
+
+/*
+ * Begin server shutdown.
+ */
+HIDDEN void
+stm_onsig_shutdown(int sig)
+{
+ STM_TRACE(("%d: stm_onsig_shutdown(sig=%d)\n", getpid(), sig));
+
+ stm.state = STM_STATE_SHUTDOWN;
+}
+
+
+/***********************************************************************
+ * Miscellany
+ */
+
+/*
+ * Convert the given VP and thread indices to a "connection identifier"
+ * for mod_status.
+ */
+HIDDEN long
+stm_id_make(int v, int t)
+{
+ STM_TRACE(("%d: stm_id_make(v=%d, t=%d)\n", getpid(), v, t));
+
+ STM_ASSERT(v >= 0 && v < STM_VP_LIMIT);
+ STM_ASSERT(t >= 0 && t < STM_ST_LIMIT);
+
+ return (long) v * STM_ST_LIMIT + t;
+}
+
+/*
+ * Convert the given connection identifier into VP and thread indices,
+ * if within bounds. Returns nonzero on success, zero on failure.
+ */
+HIDDEN int
+stm_id_break(long id, int *vp, int *tp)
+{
+ int v, t;
+
+ STM_TRACE(("%d: stm_id_break(id=%ld)\n", getpid(), id));
+
+ v = id / STM_ST_LIMIT;
+ t = id % STM_ST_LIMIT;
+ if (v >= 0 && v < STM_VP_LIMIT && t >= 0 && t < STM_ST_LIMIT) {
+ *vp = v;
+ *tp = t;
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * Disable the Nagle algorithm on an accepted TCP socket.
+ */
+HIDDEN void
+stm_disable_nagle(st_netfd_t fd)
+{
+ STM_TRACE(("%d: stm_disable_nagle(fd=%p(%d))\n", getpid(), fd,
+ st_netfd_fileno(fd)));
+
+#if !defined(IRIX) || IRIX < 65
+ {
+ int on = 1;
+
+ if (setsockopt(st_netfd_fileno(fd), IPPROTO_TCP, TCP_NODELAY,
+ (char *) &on, sizeof on) < 0)
+ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, stm.server,
+ "stm_disable_nagle: setsockopt(%d, TCP_NODELAY)",
+ st_netfd_fileno(fd));
+ }
+#endif
+}
+
+
+/***********************************************************************
+ * Exported API routines
+ */
+
+/*
+ * Begin server shutdown.
+ */
+void
+ap_start_shutdown(void)
+{
+ STM_TRACE(("%d: ap_start_shutdown() STM\n", getpid()));
+
+ kill(stm.pid, SIGTERM);
+}
+
+/*
+ * Begin server restart.
+ */
+void
+ap_start_restart(int graceful)
+{
+ STM_TRACE(("%d: ap_start_restart(graceful=%d) STM\n", getpid(), graceful));
+
+ kill(stm.pid, graceful ? SIGWINCH : SIGHUP);
+}
+
+/*
+ * Returns nonzero if the loop processing requests on a connection
+ * should prematurely terminate. This is the actual usage which belies
+ * the name of this predicate function.
+ */
+int
+ap_graceful_stop_signalled(void)
+{
+ STM_TRACE(("%d: ap_graceful_stop_signalled() STM\n", getpid()));
+
+ /*
+ * This is called from a ST on a VP via ap_process_http_connection()
+ * so we must ask our thread if it has been requested to stop.
+ * Doesn't matter if the whole server is going down, just that this
+ * thread is, so the server should abort the connection ASAP.
+ */
+ return stm_thread_stopped(stm_thread_self());
+}
+
+/*
+ * Clear all state about the connection with the given ID.
+ */
+void
+ap_reset_connection_status(long id)
+{
+ int v, t;
+
+ STM_TRACE(("%d: ap_reset_connection_status(id=%ld) STM\n", getpid(), id));
+
+ if (!stm.config.connection_status)
+ return;
+
+ if (stm_id_break(id, &v, &t)) {
+ stm_score *sp, *ep;
+
+ /* We should be running on VP v but it's not strictly necessary */
+
+ for (sp = stm.vps[v].scoreboard->scores[t],
+ ep = sp + STM_SCORE_LIMIT; sp < ep; sp++)
+ sp->key[0] = 0;
+ } else
+ ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_NOERRNO, 0,
+ stm.server, "ap_reset_connection_status (STM): bogus id %ld", id);
+}
+
+/*
+ * Associate the given key/value state with the connection with the
+ * given ID. Keys and values may be silently truncated if they exceed
+ * STM_SCORE_KEY_SIZE or STM_SCORE_VALUE_SIZE respectively. If state
+ * with the given key already exists for this connection the new value
+ * replaces the old.
+ */
+void
+ap_update_connection_status(long id, const char *key, const char *value)
+{
+ int v, t;
+
+ STM_TRACE(("%d: ap_update_connection_status(id=%ld, key=\"%s\", value=\"%s\") STM\n",
+ getpid(), id, key, value));
+
+ if (!stm.config.connection_status)
+ return;
+
+ if (stm_id_break(id, &v, &t)) {
+ stm_score *sp, *ep;
+
+ /* We should be running on VP v but it's not strictly necessary */
+
+ for (sp = stm.vps[v].scoreboard->scores[t],
+ ep = sp + STM_SCORE_LIMIT; sp < ep; sp++) {
+ if (sp->key[0] == 0) {
+ ap_cpystrn(sp->key, key, sizeof sp->key);
+ ap_cpystrn(sp->value, value, sizeof sp->value);
+ break;
+ }
+ if (!strcmp(sp->key, key)) {
+ ap_cpystrn(sp->value, value, sizeof sp->value);
+ break;
+ }
+ }
+ } else
+ ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_NOERRNO, 0, stm.server,
+ "ap_update_connection_status (STM): bogus id %ld", id);
+}
+
+/*
+ * Return an array of status information.
+ */
+ap_array_header_t *
+ap_get_status_table(ap_pool_t *pool)
+{
+ ap_array_header_t *ap;
+ int asize, tsize, i;
+ ap_status_table_row_t *srp;
+ int total_connections, most_threads,
+ listener_connections[STM_LISTENER_LIMIT];
+
+ STM_TRACE(("%d: ap_get_status_table(pool=%p)\n", getpid(), pool));
+
+ /*
+ * Create an array large enough to hold:
+ * - one entry per VP containing VP-specific status
+ * - one entry per thread per VP containing connection-specific status
+ * - one entry containing general status
+ */
+ asize = 1;
+ if (stm.config.connection_status)
+ asize += stm.config.max_threads;
+ asize *= stm.config.num_vps;
+ asize++;
+ ap = ap_make_array(pool, asize, sizeof (ap_status_table_row_t));
+
+ /* put the general status table first */
+ srp = ap_push_array(ap);
+
+ total_connections = 0;
+ most_threads = 0;
+ memset(listener_connections, 0, sizeof listener_connections);
+ for (i = 0; i < stm.config.num_vps; i++) {
+ const stm_vp *vp;
+ const stm_scoreboard *sbp;
+ ap_status_table_row_t *vrp;
+ char timebuf[32];
+ int j;
+
+ vp = &stm.vps[i];
+ sbp = vp->scoreboard;
+
+ /*
+ * Create tables large enough to hold:
+ * - eight data about VP-specific status
+ * - one datum per listener about listener-specific status
+ * - one datum per thread about connection-specific status
+ */
+ tsize = 8 + vp->num_listeners + stm.config.max_threads;
+
+ vrp = ap_push_array(ap);
+ vrp->conn_id = -1000 - i;
+ vrp->data = ap_make_table(pool, tsize);
+ ap_ctime(timebuf, sbp->vp_start_time);
+ ap_table_addn(vrp->data, "Process ID",
+ ap_psprintf(pool, "%d", sbp->pid));
+ ap_table_addn(vrp->data, "Start time",
+ ap_pstrdup(pool, timebuf));
+ ap_table_addn(vrp->data, "Incarnations",
+ ap_psprintf(pool, "%d", sbp->incarnations));
+ ap_table_addn(vrp->data, "Thread starts",
+ ap_psprintf(pool, "%d", sbp->thread_starts));
+ ap_table_addn(vrp->data, "Thread exits",
+ ap_psprintf(pool, "%d", sbp->thread_exits));
+ ap_table_addn(vrp->data, "Most threads used at once",
+ ap_psprintf(pool, "%d", sbp->most_threads));
+ ap_table_addn(vrp->data, "Connections served, prior incarnations",
+ ap_psprintf(pool, "%d", sbp->historic_vp_connections));
+ ap_table_addn(vrp->data, "Connections served, this incarnation",
+ ap_psprintf(pool, "%d", sbp->vp_connections));
+
+ total_connections += sbp->historic_vp_connections +
+ sbp->vp_connections;
+ if (most_threads < sbp->most_threads)
+ most_threads = sbp->most_threads;
+
+ for (j = 0; j < vp->num_listeners; j++) {
+ const stm_listener *lp;
+ int lc;
+
+ lp = vp->listeners[j];
+ STM_ASSERT(lp->id >= 0 && lp->id < STM_LISTENER_LIMIT);
+ lc = sbp->listener_connections[lp->id];
+
+ ap_table_addn(vrp->data,
+ ap_psprintf(pool, "Connections served for %s",
+ sbp->listeners[lp->id]),
+ ap_psprintf(pool, "%d", lc));
+ listener_connections[lp->id] += lc;
+ }
+
+ for (j = 0; j < stm.config.max_threads; j++) {
+ const stm_score *sp;
+
+ ap_table_addn(vrp->data,
+ ap_psprintf(pool, "Connections served by thread %d", j),
+ ap_psprintf(pool, "%d", sbp->thread_connections[j]));
+
+ sp = sbp->scores[j];
+ if (stm.config.connection_status && sp->key[0]) {
+ const stm_score *lep;
+ ap_status_table_row_t *rp;
+
+ for (lep = sp + STM_SCORE_LIMIT; --lep >= sp; )
+ if (lep->key[0])
+ break;
+
+ rp = ap_push_array(ap);
+ rp->conn_id = stm_id_make(i, j);
+ rp->data = ap_make_table(pool, lep - sp + 1);
+
+ while (sp <= lep) {
+ ap_table_add(rp->data, sp->key, sp->value);
+ sp++;
+ }
+ }
+ }
+
+ /* see below */
+ STM_ASSERT(ap_table_elts(vrp->data)->nelts <= tsize);
+ }
+
+ /*
+ * Create a table large enough to hold:
+ * - two data about general server status
+ * - one datum per listener about listener-specific status
+ */
+ tsize = 2 + stm.num_listeners;
+ srp->conn_id = -1;
+ srp->data = ap_make_table(pool, tsize);
+ ap_table_addn(srp->data, "Total connections served",
+ ap_psprintf(pool, "%d", total_connections));
+ ap_table_addn(srp->data, "Most threads used",
+ ap_psprintf(pool, "%d", most_threads));
+ for (i = 0; i < stm.num_listeners; i++)
+ ap_table_addn(srp->data,
+ ap_psprintf(pool, "Total connections served for %s:%u",
+ stm.listeners[i].addr_string,
+ ntohs(stm.listeners[i].addr.sin_port)),
+ ap_psprintf(pool, "%d", listener_connections[i]));
+ STM_ASSERT(ap_table_elts(srp->data)->nelts <= tsize); /* see below */
+
+ /*
+ * Not really worthy of an abort if botched, because arrays and
+ * tables grow automatically, but good to check that we're
+ * preallocating enough space, for speed.
+ */
+ STM_ASSERT(ap->nelts <= asize);
+
+ return ap;
+}
+
+/*
+ * Run the already-configured STM MPM. Returns nonzero if the server
+ * should exit, zero if it should reconfigure and restart.
+ */
+int
+ap_mpm_run(ap_pool_t *conf_pool, ap_pool_t *log_pool, server_rec *server)
+{
+ int i;
+
+ STM_TRACE(("%d: => ap_mpm_run(conf_pool=%p, log_pool=%p, server=%p) STM\n",
+ getpid(), conf_pool, log_pool, server));
+
+ if (!stm_init(conf_pool, server)) {
+ ap_log_error(APLOG_MARK, APLOG_CRIT | APLOG_NOERRNO, errno, server,
+ "STM MPM failed to initialize, shutting down");
+ return -1;
+ }
+
+ ap_log_pid(conf_pool, stm.config.pid_file);
+
+ ap_log_error(APLOG_MARK, APLOG_NOTICE | APLOG_NOERRNO, 0, server,
+ "%s configured -- resuming normal operations",
+ ap_get_server_version());
+ ap_log_error(APLOG_MARK, APLOG_INFO | APLOG_NOERRNO, 0, server,
+ "State Threaded MPM: %d virtual processors with up to %d threads each",
+ stm.config.num_vps, stm.config.max_threads);
+ ap_log_error(APLOG_MARK, APLOG_INFO | APLOG_NOERRNO, 0, server,
+ "Server built: %s", ap_get_server_built());
+
+ stm_main();
+
+ STM_ASSERT(stm.state != STM_STATE_RUN);
+ switch (stm.state) {
+ case STM_STATE_SHUTDOWN:
+ ap_log_error(APLOG_MARK, APLOG_NOTICE | APLOG_NOERRNO, 0, server,
+ "shutting down by request from signal");
+ for (i = 0; i < STM_VP_LIMIT; i++)
+ stm_vp_stop(&stm.vps[i]);
+ stm_doomsday();
+ if (unlink(stm.config.pid_file) == 0)
+ ap_log_error(APLOG_MARK, APLOG_INFO | APLOG_NOERRNO, 0, stm.server,
+ "removed PID file %s (pid=%d)", stm.config.pid_file, getpid());
+ break;
+ case STM_STATE_RESTART:
+ ap_log_error(APLOG_MARK, APLOG_NOTICE | APLOG_NOERRNO, 0, server,
+ "restarting by request from signal");
+ stm_signal_ignore(SIGTERM);
+ if (unixd_killpg(getpgrp(), SIGTERM) == -1)
+ ap_log_error(APLOG_MARK, APLOG_WARNING, errno, server,
+ "ap_mpm_run(STM): killpg");
+ stm_doomsday();
+ break;
+ case STM_STATE_GRACEFUL:
+ ap_log_error(APLOG_MARK, APLOG_NOTICE | APLOG_NOERRNO, 0, server,
+ "gracefully restarting by request from signal");
+ for (i = 0; i < STM_VP_LIMIT; i++)
+ stm_vp_stop(&stm.vps[i]);
+ stm_doomsday();
+ break;
+ }
+
+ stm_fini();
+
+ STM_TRACE(("%d: <= ap_mpm_run(conf_pool=%p, log_pool=%p, server=%p) STM, shutdown=%d\n",
+ getpid(), conf_pool, log_pool, server, stm.state == STM_STATE_SHUTDOWN));
+ return stm.state == STM_STATE_SHUTDOWN;
+}
diff -Naur apache_2.0a4/src/support/Makefile.in-orig apache_2.0a4/src/support/Makefile.in
--- apache_2.0a4/src/support/Makefile.in-orig Mon May 15 17:44:47 2000
+++ apache_2.0a4/src/support/Makefile.in Wed Jul 19 15:23:14 2000
@@ -1,5 +1,5 @@
-PROGRAMS = htpasswd htdigest rotatelogs logresolve ab
+PROGRAMS = htpasswd htdigest rotatelogs logresolve ab stmstat
targets = $(PROGRAMS)
PROGRAM_LDADD = $(EXTRA_LDFLAGS) $(PROGRAM_DEPENDENCIES) $(EXTRA_LIBS)
@@ -28,3 +28,6 @@
ab: $(ab_OBJECTS)
$(LINK) $(ab_OBJECTS) $(PROGRAM_LDADD)
+stmstat_OBJECTS = stmstat.lo
+stmstat: $(stmstat_OBJECTS)
+ $(LINK) $(stmstat_OBJECTS) $(PROGRAM_LDADD)
diff -Naur apache_2.0a4/src/support/stmstat.c-orig apache_2.0a4/src/support/stmstat.c
--- apache_2.0a4/src/support/stmstat.c-orig
+++ apache_2.0a4/src/support/stmstat.c Thu Jul 27 11:34:32 2000
@@ -0,0 +1,415 @@
+/*
+ * Copyright (c) 2000 Silicon Graphics, Inc. All rights reserved.
+ */
+
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2000 The Apache Software Foundation. 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. The end-user documentation included with the redistribution,
+ * if any, must include the following acknowledgment:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache" and "Apache Software Foundation" 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 name, without prior written
+ * permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``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 SOFTWARE FOUNDATION 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 Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * .
+ *
+ * Portions of this software are based upon public domain software
+ * originally written at the National Center for Supercomputing Applications,
+ * University of Illinois, Urbana-Champaign.
+ */
+
+/*
+ * stmstat - Status reporting tool for the STM
+ *
+ * Mike Abbott - mja@sgi.com
+ * Accelerating Apache Project - http://oss.sgi.com/projects/apache/
+ *
+ * The state-threaded multi-processing module (STM MPM) for Apache/2.0
+ * maintains status information in disk files. Although all the
+ * information is available via the server's server-status page,
+ * gathering it this way causes the server to serve a page which might
+ * be undesirable in high load situations. The stmstat tool reports the
+ * same information without interfering with the web server.
+ *
+ * This tool is designed to be extended and not modified. Unless you're
+ * fixing a bug in an existing version, do not modify the routines that
+ * are already here. When the on-disk scoreboard format changes, the
+ * version number the STM stores in the file must also change. Add new
+ * parsing/printing routines for that version to this tool, taking care
+ * not to depend on or modify any part of any other version. Preserving
+ * backward compatibility is crucial. This tool must always be able to
+ * report any new or old version of the status information.
+ *
+ * This tool uses read instead of mmap to get the data, for no good
+ * reason. Read is more portable, but the STM already requires mmap.
+ * Whatever.
+ *
+ * See htdocs/manual/stm.html for further documentation.
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+int verbose = 0;
+
+/*
+ * Gcc needs these to be happy in maintainer-mode. Doesn't make sense,
+ * since they're defined before use. Oh well.
+ */
+void readn(int, void *, size_t, const char *, const char *);
+void padto(int, size_t, const char *);
+void eof(int, const char *);
+int show(const char *, int, int);
+
+/*
+ * Read nbytes of data, described by desc, into buf from fd (which is
+ * from fn). Exits on any error.
+ */
+void
+readn(int fd, void *buf, size_t nbytes, const char *desc, const char *fn)
+{
+ ssize_t n;
+
+ n = read(fd, buf, nbytes);
+ if (n == -1) {
+ fprintf(stderr, "cannot read %s from %s: %s\n", desc, fn,
+ strerror(errno));
+ exit(1);
+ } else if (n != nbytes) {
+ fprintf(stderr, "short read (%d/%d at %ld) of %s from %s\n",
+ n, nbytes, (long) lseek(fd, -n, SEEK_CUR), desc, fn);
+ exit(1);
+ }
+}
+
+/*
+ * Read and discard zero to pad-1 bytes of padding from the given file
+ * (which is from the given file name). Exits if the padding is
+ * missing.
+ */
+void
+padto(int fd, size_t pad, const char *fn)
+{
+ off_t o;
+
+ o = lseek(fd, 0, SEEK_CUR) & (pad - 1);
+ if (o) {
+ char junk[15];
+
+ if (o > sizeof junk)
+ abort();
+ readn(fd, junk, o, "padding", fn);
+ }
+}
+
+/*
+ * Verify that there is no more data in the given file (which is from
+ * the given file name). Exits on failure.
+ */
+void
+eof(int fd, const char *fn)
+{
+ char c;
+
+ if (read(fd, &c, 1) == 1) {
+ fprintf(stderr, "data beyond expected eof (at %ld) in %s\n",
+ (long) lseek(fd, -1, SEEK_CUR), fn);
+ exit(1);
+ }
+}
+
+/* The scoreboard version size must never change. */
+#define STM_SCOREBOARD_VERSION_LENGTH 16
+
+typedef int ap_int32_t;
+typedef long long ap_time_t;
+
+
+/* insert new scoreboard version data and procedures here */
+
+
+/*****************************************
+ * Scoreboard version 0001
+ */
+#define STM_SCOREBOARD_VERSION_0001 "STM-MPM-sb-0001\n"
+typedef struct {
+ ap_int32_t st_limit; /* size of arrays below */
+ ap_int32_t listener_limit; /* size of arrays below */
+ ap_int32_t score_limit; /* size of arrays below */
+ ap_int32_t key_size; /* size of arrays below */
+ ap_int32_t value_size; /* size of arrays below */
+ ap_int32_t pid; /* this VP's PID */
+ ap_time_t start_time; /* server's start time */
+ ap_time_t vp_start_time; /* this VP's start time */
+ ap_int32_t incarnations; /* # of different VPs in this slot */
+ ap_int32_t thread_starts; /* # of thread start events */
+ ap_int32_t thread_exits; /* # of thread exit events */
+ ap_int32_t most_threads; /* most threads ever used */
+ ap_int32_t historic_vp_connections;/* # of conns, prior incarnations */
+ ap_int32_t vp_connections; /* # of conns, this incarnation */
+} scoreboard_0001_fixed; /* fixed-size portion of the scoreboard */
+typedef struct {
+ char *key;
+ char *value;
+} score_0001; /* scores are key-value pairs */
+typedef struct {
+ ap_int32_t *thread_connections; /* per thread */
+ ap_int32_t *listener_connections; /* per listener */
+ char **listeners; /* IP address:port */
+ score_0001 **scores; /* per-conn data */
+} scoreboard_0001_vary; /* varying-size portion of the scoreboard */
+
+/* for gcc in maintainer-mode */
+const char *ttoa_0001(ap_time_t);
+void show_0001(int, const char *);
+
+/*
+ * Convert the given time (in microseconds since the epoch) to a string.
+ * Returns a static buffer.
+ */
+const char *
+ttoa_0001(ap_time_t t)
+{
+ static char buf[64];
+ time_t s;
+ int u;
+ struct tm *tp;
+
+ s = (time_t) (t / 1000000);
+ u = t % 1000000;
+ tp = localtime(&s);
+ sprintf(&buf[strftime(buf, sizeof buf, "%Y-%b-%d %T", tp)], ".%06d", u);
+
+ return buf;
+}
+
+/*
+ * Read the scoreboard data from the given file descriptor (which is from
+ * the given file name) and parse and print all the information.
+ */
+void
+show_0001(int fd, const char *fn)
+{
+ scoreboard_0001_fixed fixed;
+ scoreboard_0001_vary vary;
+ size_t size;
+ int i, j;
+
+ /*
+ * The output could be prettier. I mostly wanted to write this as a
+ * proof of concept.
+ */
+
+ printf("%s\n", fn);
+
+ /* first read and sanity check the fixed-size portion */
+ readn(fd, &fixed, sizeof fixed, "fixed-size portion", fn);
+ if (verbose) {
+ printf("st_limit %-5d\n", fixed.st_limit);
+ printf("listener_limit %-5d\n", fixed.listener_limit);
+ printf("score_limit %-5d\n", fixed.score_limit);
+ printf("key_size %-5d\n", fixed.key_size);
+ printf("value_size %-5d\n", fixed.value_size);
+ }
+ if (fixed.st_limit < 1 || fixed.st_limit > 1000000 ||
+ fixed.listener_limit < 1 || fixed.listener_limit > 1000000 ||
+ fixed.score_limit < 1 || fixed.score_limit > 1000000 ||
+ fixed.key_size < 1 || fixed.key_size > 1000000 ||
+ fixed.value_size < 1 || fixed.value_size > 1000000) {
+ fprintf(stderr, "invalid sizes in %s\n", fn);
+ exit(1);
+ }
+
+ /* dead VP? */
+ if (fixed.pid == -1) {
+ printf("blank\n");
+ return;
+ }
+
+ /* then read the varying-size portion in chunks */
+
+ size = fixed.st_limit * sizeof *vary.thread_connections;
+ vary.thread_connections = (ap_int32_t *) malloc(size);
+ readn(fd, vary.thread_connections, size, "thread_connections", fn);
+
+ size = fixed.listener_limit * sizeof *vary.listener_connections;
+ vary.listener_connections = (ap_int32_t *) malloc(size);
+ readn(fd, vary.listener_connections, size, "listener_connections", fn);
+
+ size = fixed.listener_limit * sizeof *vary.listeners;
+ vary.listeners = (char **) malloc(size);
+ for (i = 0; i < fixed.listener_limit; i++) {
+ size = 24;
+ vary.listeners[i] = (char *) malloc(size);
+ readn(fd, vary.listeners[i], size, "listeners", fn);
+ }
+
+ size = fixed.st_limit * sizeof *vary.scores;
+ vary.scores = (score_0001 **) malloc(size);
+ for (i = 0; i < fixed.st_limit; i++) {
+ size = fixed.score_limit * sizeof *vary.scores[i];
+ vary.scores[i] = (score_0001 *) malloc(size);
+
+ for (j = 0; j < fixed.score_limit; j++) {
+ size = fixed.key_size * sizeof *vary.scores[i][j].key;
+ vary.scores[i][j].key = (char *) malloc(size);
+ readn(fd, vary.scores[i][j].key, size, "scores", fn);
+
+ size = fixed.value_size * sizeof *vary.scores[i][j].value;
+ vary.scores[i][j].value = (char *) malloc(size);
+ readn(fd, vary.scores[i][j].value, size, "scores", fn);
+ }
+ }
+
+#if defined(_MIPS_SIM) && _MIPS_SIM != _ABIO32
+ /*
+ * SGI's MIPSpro compilers pad the struct out to an 8-byte boundary
+ * in the N32 and 64-bit ABIs.
+ */
+ padto(fd, 8, fn);
+#endif
+ eof(fd, fn);
+
+ /* now report */
+
+ printf("Server started at %s\n", ttoa_0001(fixed.start_time));
+ printf("VP started at %s\n", ttoa_0001(fixed.vp_start_time));
+ printf("VP pid %d\n", fixed.pid);
+ printf("incarnations %d\n", fixed.incarnations);
+ printf("thread_starts %d\n", fixed.thread_starts);
+ printf("thread_exits %d\n", fixed.thread_exits);
+ printf("most_threads %d\n", fixed.most_threads);
+ printf("historic_vp_connections %d\n", fixed.historic_vp_connections);
+ printf("vp_connections %d\n", fixed.vp_connections);
+ for (i = 0; i < fixed.st_limit; i++)
+ printf("thread_connections[%d] %d\n", i, vary.thread_connections[i]);
+ for (i = 0; i < fixed.listener_limit; i++) {
+ printf("listeners[%d] = %.*s\n", i, 24, vary.listeners[i]);
+ printf("listener_connections[%d] %d\n", i, vary.listener_connections[i]);
+ }
+ for (i = 0; i < fixed.st_limit; i++)
+ for (j = 0; j < fixed.score_limit; j++)
+ if (vary.scores[i][j].key[0])
+ printf("score[%d][%d] %.*s = %.*s\n", i, j,
+ fixed.key_size, vary.scores[i][j].key,
+ fixed.value_size, vary.scores[i][j].value);
+}
+
+
+/*
+ * Read and report status for VP #n from its scoreboard file in the
+ * given directory. If must is non-zero, report open errors on the
+ * file, otherwise we're just scanning so keep quiet. Returns nonzero
+ * on success, zero on failure.
+ */
+int
+show(const char *dir, int n, int must)
+{
+ char fn[PATH_MAX], version[STM_SCOREBOARD_VERSION_LENGTH];
+ int fd;
+
+ sprintf(fn, "%s/%d", dir, n);
+ fd = open(fn, O_RDONLY);
+ if (fd > 0) {
+ readn(fd, version, sizeof version, "version", fn);
+
+ /* insert new version checks here */
+
+ if (!memcmp(version, STM_SCOREBOARD_VERSION_0001, sizeof version)) {
+ show_0001(fd, fn);
+ return 1;
+ } else
+ fprintf(stderr, "unknown scoreboard version \"%.*s\" in %s\n",
+ (int) sizeof version, version, fn);
+
+ close(fd);
+ } else if (must)
+ perror(fn);
+
+ return 0;
+}
+
+int
+main(int argc, char **argv)
+{
+ int n, c;
+
+ n = -1;
+ while ((c = getopt(argc, argv, "n:v")) != -1) {
+ switch (c) {
+ case 'n':
+ n = atoi(optarg);
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ default:
+usage: fprintf(stderr, "Usage: %s"
+ " [-n number]"
+ " [-v]"
+ " scoreboards-directory\n", argv[0]);
+ exit(1);
+ }
+ }
+
+ if (argc - optind != 1)
+ goto usage;
+
+ if (n >= 0)
+ show(argv[optind], n, 1);
+ else {
+ c = 0;
+ while (show(argv[optind], c, 0))
+ c++;
+ }
+
+ return 0;
+}