diff options
author | (no author) <(no author)@unknown> | 2001-10-16 21:29:08 +0000 |
---|---|---|
committer | (no author) <(no author)@unknown> | 2001-10-16 21:29:08 +0000 |
commit | 517bf7127f1807ffeafea29bf3bafc33f1471acd (patch) | |
tree | 489237d958e3d03ae82584a2f8f704848628f918 | |
parent | 5271b3826e7ca321b092d858e061ff01a00983a2 (diff) | |
download | httpd-517bf7127f1807ffeafea29bf3bafc33f1471acd.tar.gz |
This commit was manufactured by cvs2svn to create branch 'avendor'.
git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/avendor@91505 13f79535-47bb-0310-9956-ffa450edef68
213 files changed, 16149 insertions, 0 deletions
diff --git a/build/.cvsignore b/build/.cvsignore new file mode 100644 index 0000000000..29d51bb51c --- /dev/null +++ b/build/.cvsignore @@ -0,0 +1 @@ +rules.mk diff --git a/build/make_exports.awk b/build/make_exports.awk new file mode 100644 index 0000000000..27fd631eb9 --- /dev/null +++ b/build/make_exports.awk @@ -0,0 +1,120 @@ + +BEGIN { + printf("/*\n") + printf(" * THIS FILE WAS AUTOGENERATED BY make_exports.awk\n") + printf(" *\n") + printf(" * This is an ugly hack that needs to be here, so\n") + printf(" * that libtool will link all of the APR functions\n") + printf(" * into server regardless of whether the base server\n") + printf(" * uses them.\n") + printf(" */\n") + printf("\n") + printf("#define CORE_PRIVATE\n") + printf("\n") + + for (i = 1; i < ARGC; i++) { + file = ARGV[i] + sub("([^/]*[/])*", "", file) + printf("#include \"%s\"\n", file) + } + + printf("\n") + printf("const void *ap_ugly_hack = NULL;\n") + printf("\n") + + TYPE_NORMAL = 0 + TYPE_HEADER = 1 + + stackptr = 0 +} + +function push(line) { + stack[stackptr] = line + stackptr++ +} + +function do_output() { + printf("/*\n") + printf(" * %s\n", FILENAME) + printf(" */\n") + + for (i = 0; i < stackptr; i++) { + printf("%s\n", stack[i]) + } + + stackptr = 0 + + printf("\n"); +} + +function enter_scope(type) { + scope++ + scope_type[scope] = type + scope_stack[scope] = stackptr + delete scope_used[scope] +} + +function leave_scope() { + used = scope_used[scope] + + if (!used) + stackptr = scope_stack[scope] + + scope-- + if (used) { + scope_used[scope] = 1 + + if (!scope) + do_output() + } +} + +function add_symbol(symbol) { + idx = index(symbol, "#") + + if (!idx) { + push("const void *ap_hack_" symbol " = (const void *)" symbol ";") + scope_used[scope] = 1 + } +} + +/^[ \t]*AP[RU]?_DECLARE[^(]*[(][^)]*[)]([^ ]* )*[^(]+[(]/ { + sub("[ \t]*AP[RU]?_DECLARE[^(]*[(][^)]*[)]", ""); + sub("[(].*", ""); + sub("^[ \t]+", ""); + sub("([^ ]* ^([ \t]*[(]))*", ""); + + add_symbol($0) + next +} + +/^#[ \t]*if(ndef| !defined[(])([^_]*_)*H/ { + enter_scope(TYPE_HEADER) + next +} + +/^#[ \t]*if([n]?def)? / { + enter_scope(TYPE_NORMAL) + push($0) + next +} + +/^#[ \t]*endif/ { + if (scope_type[scope] == TYPE_NORMAL) + push($0) + + leave_scope() + next +} + +/^#[ \t]*else/ { + push($0) + next +} + +/^#[ \t]*elif/ { + push($0) + next +} + + diff --git a/build/make_var_export.awk b/build/make_var_export.awk new file mode 100644 index 0000000000..62263f10e6 --- /dev/null +++ b/build/make_var_export.awk @@ -0,0 +1,59 @@ +# Based on apr's make_export.awk, which is +# based on Ryan Bloom's make_export.pl + +/^#[ \t]*if(def)? (AP[RU]?_|!?defined).*/ { + if (old_filename != FILENAME) { + if (old_filename != "") printf("%s", line) + macro_no = 0 + found = 0 + count = 0 + old_filename = FILENAME + line = "" + } + macro_stack[macro_no++] = macro + macro = substr($0, length($1)+2) + count++ + line = line "#ifdef " macro "\n" + next +} + +/^#[ \t]*endif/ { + if (count > 0) { + count-- + line = line "#endif " macro "\n" + macro = macro_stack[--macro_no] + } + if (count == 0) { + if (found != 0) { + printf("%s", line) + } + line = "" + } + next +} + +function add_symbol (sym_name) { + if (count) { + found++ + } + for (i = 0; i < count; i++) { + line = line "\t" + } + line = line sym_name "\n" + + if (count == 0) { + printf("%s", line) + line = "" + } +} + +/^[ \t]*AP[RU]?_DECLARE_DATA .*;$/ { + varname = $NF; + gsub( /[*;]/, "", varname); + gsub( /\[.*\]/, "", varname); + add_symbol(varname); +} + +END { + printf("%s", line) +} diff --git a/build/rules.mk.in b/build/rules.mk.in new file mode 100644 index 0000000000..659cbcf877 --- /dev/null +++ b/build/rules.mk.in @@ -0,0 +1,264 @@ +# ==================================================================== +# The Apache Software License, Version 1.1 +# +# Copyright (c) 2000-2001 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 +# <http://www.apache.org/>. +# +# The build environment was originally provided by Sascha Schumann. +# + +include $(top_builddir)/config_vars.mk + +# Combine all of the flags together in the proper order so that +# the user-defined flags can always override the configure ones, if needed. +# Note that includes are listed after the flags because -I options have +# left-to-right precedence and CPPFLAGS may include user-defined overrides. +# +ALL_CFLAGS = $(EXTRA_CFLAGS) $(NOTEST_CFLAGS) $(CFLAGS) +ALL_CPPFLAGS = $(DEFS) $(EXTRA_CPPFLAGS) $(NOTEST_CPPFLAGS) $(CPPFLAGS) +ALL_CXXFLAGS = $(EXTRA_CXXFLAGS) $(NOTEST_CXXFLAGS) $(CXXFLAGS) +ALL_LDFLAGS = $(EXTRA_LDFLAGS) $(NOTEST_LDFLAGS) $(LDFLAGS) +ALL_LIBS = $(EXTRA_LIBS) $(NOTEST_LIBS) $(LIBS) +ALL_INCLUDES = $(INCLUDES) $(EXTRA_INCLUDES) + +# Compile commands + +COMPILE = $(CC) $(ALL_CFLAGS) $(ALL_CPPFLAGS) $(ALL_INCLUDES) +CXX_COMPILE = $(CXX) $(ALL_CXXFLAGS) $(ALL_CPPFLAGS) $(ALL_INCLUDES) + +SH_COMPILE = $(LIBTOOL) --mode=compile $(COMPILE) @SHLTCFLAGS@ -c $< && touch $@ +SH_CXX_COMPILE = $(LIBTOOL) --mode=compile $(CXX_COMPILE) @SHLTCFLAGS@ -c $< && touch $@ + +LT_COMPILE = $(LIBTOOL) --mode=compile $(COMPILE) @LTCFLAGS@ -c $< && touch $@ +LT_CXX_COMPILE = $(LIBTOOL) --mode=compile $(CXX_COMPILE) @LTCFLAGS@ -c $< && touch $@ + +# Link-related commands + +LINK = $(LIBTOOL) --mode=link $(COMPILE) $(LT_LDFLAGS) $(ALL_LDFLAGS) -o $@ +SH_LINK = $(SH_LIBTOOL) --mode=link $(COMPILE) $(LT_LDFLAGS) $(ALL_LDFLAGS) $(SH_LDFLAGS) $(CORE_IMPLIB) -o $@ +MOD_LINK = $(LIBTOOL) --mode=link $(COMPILE) -module $(LT_LDFLAGS) $(ALL_LDFLAGS) -o $@ + +# Cross compile commands + +# Helper programs + +MKINSTALLDIRS = $(abs_srcdir)/build/mkdir.sh +INSTALL = $(abs_srcdir)/build/install.sh -c +INSTALL_DATA = $(INSTALL) -m 644 +INSTALL_PROGRAM = $(INSTALL) -m 755 $(INSTALL_PROG_FLAGS) + +# +# Standard build rules +# +all: all-recursive +depend: depend-recursive +clean: clean-recursive +distclean: distclean-recursive +extraclean: extraclean-recursive +install: install-recursive +shared-build: shared-build-recursive + +all-recursive install-recursive depend-recursive: + @otarget=`echo $@|sed s/-recursive//`; \ + list='$(SUBDIRS)'; \ + for i in $$list; do \ + if test -d "$$i"; then \ + target="$$otarget"; \ + echo "Making $$target in $$i"; \ + if test "$$i" = "."; then \ + made_local=yes; \ + target="local-$$target"; \ + fi; \ + (cd $$i && $(MAKE) $$target) || exit 1; \ + fi; \ + done; \ + if test "$$otarget" = "all" && test -z '$(TARGETS)'; then \ + made_local=yes; \ + fi; \ + if test "$$made_local" != "yes"; then \ + $(MAKE) "local-$$otarget" || exit 1; \ + fi + +clean-recursive distclean-recursive extraclean-recursive: + @otarget=`echo $@|sed s/-recursive//`; \ + list='$(SUBDIRS) $(CLEAN_SUBDIRS)'; \ + for i in $$list; do \ + if test -d "$$i"; then \ + target="$$otarget"; \ + echo "Making $$target in $$i"; \ + if test "$$i" = "."; then \ + made_local=yes; \ + target="local-$$target"; \ + fi; \ + (cd $$i && $(MAKE) $$target); \ + fi; \ + done; \ + if test "$$otarget" = "all" && test -z '$(TARGETS)'; then \ + made_local=yes; \ + fi; \ + if test "$$made_local" != "yes"; then \ + $(MAKE) "local-$$otarget"; \ + fi + +shared-build-recursive: + @if test `pwd` = "$(top_builddir)"; then \ + $(PRE_SHARED_CMDS) ; \ + fi; \ + list='$(SUBDIRS)'; for i in $$list; do \ + target="shared-build"; \ + if test "$$i" = "."; then \ + made_local=yes; \ + target="local-shared-build"; \ + fi; \ + if test "$$i" != "srclib"; then \ + (cd $$i && $(MAKE) $$target) || exit 1; \ + fi; \ + done; \ + if test -f 'modules.mk'; then \ + if test -n '$(SHARED_TARGETS)'; then \ + echo "Building shared: $(SHARED_TARGETS)"; \ + if test "$$made_local" != "yes"; then \ + $(MAKE) "local-shared-build" || exit 1; \ + fi; \ + fi; \ + fi; \ + if test `pwd` = "$(top_builddir)"; then \ + $(POST_SHARED_CMDS) ; \ + fi + +local-all: $(TARGETS) + +local-shared-build: $(SHARED_TARGETS) + +local-depend: x-local-depend + if test "`echo $(srcdir)/*.c`" != "$(srcdir)'/*.c'"; then \ + $(CC) -MM $(ALL_CPPFLAGS) $(ALL_INCLUDES) $(srcdir)/*.c | sed 's/\.o:/.lo:/' > $(builddir)/.deps || true; \ + fi + +local-clean: x-local-clean + rm -f *.o *.lo *.slo *.obj *.a *.la $(CLEAN_TARGETS) $(TARGETS) + rm -rf .libs + +local-distclean: local-clean x-local-distclean + rm -f .deps Makefile $(DISTCLEAN_TARGETS) + +local-extraclean: local-distclean x-local-extraclean + @if test -n "$(EXTRACLEAN_TARGETS)"; then \ + echo "rm -f $(EXTRACLEAN_TARGETS)"; \ + rm -f $(EXTRACLEAN_TARGETS) ; \ + fi + +local-install: $(TARGETS) $(SHARED_TARGETS) $(INSTALL_TARGETS) + @if test -n '$(PROGRAMS)'; then \ + test -d $(bindir) || $(MKINSTALLDIRS) $(bindir); \ + list='$(PROGRAMS)'; for i in $$list; do \ + $(INSTALL_PROGRAM) $$i $(bindir); \ + done; \ + fi + +# to be filled in by the actual Makefile if extra commands are needed +x-local-depend x-local-clean x-local-distclean x-local-extraclean: + +# +# Implicit rules for creating outputs from input files +# +CXX_SUFFIX = cpp +SHLIB_SUFFIX = so + +.SUFFIXES: +.SUFFIXES: .S .c .$(CXX_SUFFIX) .lo .o .s .y .l .slo .def .la + +.c.o: + $(COMPILE) -c $< + +.s.o: + $(COMPILE) -c $< + +.c.lo: + $(LT_COMPILE) + +.s.lo: + $(LT_COMPILE) + +.c.slo: + $(SH_COMPILE) + +.$(CXX_SUFFIX).lo: + $(LT_CXX_COMPILE) + +.$(CXX_SUFFIX).slo: + $(SH_CXX_COMPILE) + +.y.c: + $(YACC) $(YFLAGS) $< && mv y.tab.c $*.c + if test -f y.tab.h; then \ + if cmp -s y.tab.h $*.h; then rm -f y.tab.h; else mv y.tab.h $*.h; fi; \ + else :; fi + +.l.c: + $(LEX) $(LFLAGS) $< && mv $(LEX_OUTPUT_ROOT).c $@ + +# Makes an import library from a def file +.def.la: + $(LIBTOOL) --mode=compile $(MK_IMPLIB) -o $@ $< + +# +# Dependencies +# +include $(builddir)/.deps + +.PHONY: all all-recursive install-recursive local-all $(PHONY_TARGETS) \ + shared-build shared-build-recursive local-shared-build \ + depend depend-recursive local-depend x-local-depend \ + clean clean-recursive local-clean x-local-clean \ + distclean distclean-recursive local-distclean x-local-distclean \ + extraclean extraclean-recursive local-extraclean x-local-extraclean \ + install local-install $(INSTALL_TARGETS) + diff --git a/build/win32/.cvsignore b/build/win32/.cvsignore new file mode 100644 index 0000000000..3a75560491 --- /dev/null +++ b/build/win32/.cvsignore @@ -0,0 +1,2 @@ +Apache.aps +Apache.rc diff --git a/build/win32/apache.ico b/build/win32/apache.ico Binary files differnew file mode 100644 index 0000000000..5e8adcbfc5 --- /dev/null +++ b/build/win32/apache.ico diff --git a/build/win32/win32ver.awk b/build/win32/win32ver.awk new file mode 100644 index 0000000000..8862aa87f3 --- /dev/null +++ b/build/win32/win32ver.awk @@ -0,0 +1,108 @@ +BEGIN { + + # ff bits: 1(debug), 2(prerelease), 4(patched), 8(vendor) and 32(special) + # debug is summed based on the /Define _DEBUG + # prerelease is based on the -dev extension, + # patched is based on a non-standard "-ver" extension, + # special and vendor are toggled by their args. + # + ff = 0; + + file=ARGV[1]; + desc=ARGV[2]; + rel_h=ARGV[3]; + + i = 4; + while (length(ARGV[i])) { + if (match(ARGV[i], /icon=/)) { + icon = substr(ARGV[i], 6); + } + if (match(ARGV[i], /vendor=/)) { + vendor = substr(ARGV[i], 8); + ff = ff + 8; + } + if (match(ARGV[i], /special=/)) { + special = substr(ARGV[i], 9); + ff = ff + 32; + } + i = i + 1 + } + + i = i - 1; + while (i) { + delete ARGV[i]; + i = i - 1; + } + + while ((getline < rel_h) > 0) { + if (match ($0, /^#define AP_SERVER_BASEREVISION "[^"]+"/)) { + ver = substr($0, RSTART + 32, RLENGTH - 33); + } + } + + verc = ver; + gsub(/\./, ",", verc); + if (build) { + sub(/-.*/, "", verc) + verc = verc "," build; + } else if (sub(/-dev/, ",0", verc)) { + ff = ff + 2; + } else if (!sub(/-alpha/, ",10", verc) \ + && !sub(/-beta/, ",100", verc) \ + && !sub(/-gold/, ",200", verc)) { + sub(/-.*/, "", verc); + verc = verc "," 0; + } + + if (length(vendor)) { + ff = ff + 8; + } + + if (length(icon)) { + print "1 ICON DISCARDABLE \"" icon "\""; + } + print "1 VERSIONINFO"; + print " FILEVERSION " verc ""; + print " PRODUCTVERSION " verc ""; + print " FILEFLAGSMASK 0x3fL"; + print "#if defined(_DEBUG)" + print " FILEFLAGS 0x" sprintf("%02x", ff + 1) "L"; + print "#else" + print " FILEFLAGS 0x" sprintf("%02x", ff) "L"; + print "#endif" + print " FILEOS 0x40004L"; + print " FILETYPE 0x1L"; + print " FILESUBTYPE 0x0L"; + print "BEGIN"; + print " BLOCK \"StringFileInfo\""; + print " BEGIN"; + print " BLOCK \"00000000\""; + print " BEGIN"; + print " VALUE \"Comments\", \"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 <http://www.apache.org/>\\0\""; + print " VALUE \"CompanyName\", \"Apache Software Foundation.\\0\""; + print " VALUE \"FileDescription\", \"" desc "\\0\""; + print " VALUE \"FileVersion\", \"" ver "\\0\""; + print " VALUE \"InternalName\", \"" file "\\0\""; + print " VALUE \"LegalCopyright\", \"Copyright (c) 2001, " \ + "The Apache Software Foundation. Current License is available from " \ + "<http://www.apache.org/LICENSE.txt>\\0\""; + print " VALUE \"OriginalFilename\", \"" file ".exe\\0\""; + if (vendor) { + print " VALUE \"PrivateBuild\", \"" vendor "\\0\""; + } + if (special) { + print " VALUE \"SpecialBuild\", \"" vendor "\\0\""; + } + print " VALUE \"ProductName\", \"Apache httpd Server\\0\""; + print " VALUE \"ProductVersion\", \"" ver "\\0\""; + print " END"; + print " END"; + print " BLOCK \"VarFileInfo\""; + print " BEGIN"; + print " VALUE \"Translation\", 0, 1200"; + print " END"; + print "END"; +}
\ No newline at end of file diff --git a/docs/docroot/apache_pb.png b/docs/docroot/apache_pb.png Binary files differnew file mode 100644 index 0000000000..eb99a8cd39 --- /dev/null +++ b/docs/docroot/apache_pb.png diff --git a/docs/docroot/apache_pb2.png b/docs/docroot/apache_pb2.png Binary files differnew file mode 100644 index 0000000000..28baa70fb8 --- /dev/null +++ b/docs/docroot/apache_pb2.png diff --git a/docs/docroot/index.html.cz.iso8859-2 b/docs/docroot/index.html.cz.iso8859-2 new file mode 100644 index 0000000000..e0cd2ba610 --- /dev/null +++ b/docs/docroot/index.html.cz.iso8859-2 @@ -0,0 +1,51 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<HTML> + <HEAD> + <META HTTP-Equiv="Content-Type" CONTENT="text/html; charset=iso-8859-2"> + <TITLE>Testovací stránka instalace web serveru Apache</TITLE> + </HEAD> +<!-- Background white, links blue (unvisited), navy (visited), red (active) --> + <BODY + BGCOLOR="#FFFFFF" + TEXT="#000000" + LINK="#0000FF" + VLINK="#000080" + ALINK="#FF0000" + > + <H1 ALIGN="CENTER"> + Funguje to! Na tomto serveru bì¾í Apache! + </H1> + <P> + Pokud vidíte tuto zprávu, správce tohoto poèítaèe právì úspì¹nì + nainstaloval Apache web server. + Teï je¹tì musí doplnit obsah a nahradit tuto standardní uvítací + stránku, nebo pøesmìrovat server na skuteènou domovskou stránku. + </P> + <HR> + <BLOCKQUOTE> + Pokud vidíte tuto zprávu místo oèekávané domovské stránky, + <STRONG>kontaktujte, prosím, správce pøíslu¹ného web + serveru.</STRONG> (Zkuste poslat e-mail na adresu + <SAMP><Webmaster@<EM>domain</EM>></SAMP>.) + Pøesto¾e tento server pou¾ívá program Apache, témìø jistì nemá nic + spoleèného s Apache Group, proto prosím neposílejte e-maily o tomto + serveru nebo jeho obsahu autorùm Apache. Pokud to udìláte, budeme + va¹e zprávy <STRONG><BIG>ignorovat</BIG></STRONG>. + </BLOCKQUOTE> + <HR> + <P> + Zde najdete + <A + HREF="manual/" + >dokumentaci</A> + Apache (anglicky) obsa¾enou v této distribuci. + </P> + <P> + Následující logo mù¾e být pou¾ito bez omezení na web serveru s + programem Apache. Dìkujeme za pou¾ití Apache! + </P> + <DIV ALIGN="CENTER"> + <IMG SRC="apache_pb.gif" ALT=""> + </DIV> + </BODY> +</HTML> diff --git a/docs/docroot/index.html.hr.iso8859-2 b/docs/docroot/index.html.hr.iso8859-2 new file mode 100644 index 0000000000..7af963d66f --- /dev/null +++ b/docs/docroot/index.html.hr.iso8859-2 @@ -0,0 +1,35 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-2"> +<title>Testna Stranica Apache Instalacije</title> +</head> +<!-- Background white, links blue (unvisited), navy (visited), red +(active) --> +<body bgcolor="#FFFFFF" text="#000000" link="#0000FF" +vlink="#000080" alink="#FF0000"> +<h2 align="center">Èestitamo! Apache web poslu¾itelj funkcionira!</h2> +<p>Ukoliko vidite ovu stranicu, to znaèi da je instalacija <a +href="http://www.apache.org/foundation/preFAQ.html">Apache web poslu¾itelja</a> uspje¹no izvr¹ena na ovom raèunalu. +Sada mo¾ete dodati sadr¾aj u ovaj direktorij te promijeniti ovu stranicu.</p> + +<hr width="50%" size="8" /> +<h2 align="center">Oèekivali ste neku drugu stranicu?</h2> + +<p>Ovu stranica vidite stoga ¹to je administrator ovog poslu¾itelja promijenio +konfiguraciju. Apache Softver Fondacija napisala je softver koji koristi +administrator ovog web poslu¾itelja i nije odgovorna za odr¾avanje ovog poslu¾itelja. Molimo vas da za sva pitanja <strong>kontaktirate odgovornu +osobu za odr¾avanje ovog poslu¾itelja.</strong></p> + +<hr width="50%" size="8" /> +<p>Ovdje se nalazi <a href="manual/">Dokumentacija</a> o Apache web poslu¾itelju +(engleski).</p> + +<p>Donju slièicu mo¾ete slobodno koristiti na stranicama ovog veb poslu¾itelja. +Hvala vam ¹to koristite Apache!</p> + +<div align="center"><img src="apache_pb.gif" alt="Apache Web Poslu¾itelj" /></div> +</body> +</html> + diff --git a/docs/docroot/index.html.nn b/docs/docroot/index.html.nn new file mode 100644 index 0000000000..67db9570b0 --- /dev/null +++ b/docs/docroot/index.html.nn @@ -0,0 +1,48 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<HTML> + <HEAD> + <TITLE>Testside Apache-installasjon</TITLE> + </HEAD> +<!-- Background white, links blue (unvisited), navy (visited), red (active) --> + <BODY + BGCOLOR="#FFFFFF" + TEXT="#000000" + LINK="#0000FF" + VLINK="#000080" + ALINK="#FF0000" + > + <H1 ALIGN="CENTER"> + Det gjekk bra! Apache er no installert på denne maskina! + </H1> + <P> + Dersom du kan sjå denne sida, har den eller dei som driv denne nettstaden + installert <A href="http://www.apache.org/foundation/preFAQ.html">Apache vevtenar</a>. + No må denne testsida erstattast med verkeleg innhald. + </P> + <HR> + <BLOCKQUOTE> + Dersom du hadde venta å sjå ei anna side enn denne, bør du + <strong>ta kontakt med den som er ansvarleg for denne nettstaden</strong> + (Prøv å sende e-post til <SAMP><webmaster@<EM>domene</EM>></SAMP>.) + Sjølv om denne nettstaden vert kjørt på Apache, har den ingen annan + tilknytning til Apache-gruppa, som har utvikla programvaren. + Ver snill og ikkje send e-post om denne nettstaden eller + innhaldet du finn her til utviklarane i Apache-gruppa. + I så tilfelle vil førespurnaden <strong><BIG>ignorerast</BIG></strong>. + </BLOCKQUOTE> + <HR> + <P> + <A + HREF="manual/index.html" + >Dokumentasjon</A> + for Apache er inkludert i denne pakka. + </P> + <P> + Logoen under kan brukast på kva som helst av maskiner som køyrer Apache. + Takk for at du nyttar Apache! + </P> + <DIV ALIGN="CENTER"> + <IMG SRC="apache_pb.gif" ALT=""> + </DIV> + </BODY> +</HTML> diff --git a/docs/docroot/index.html.po.iso8859-2 b/docs/docroot/index.html.po.iso8859-2 new file mode 100644 index 0000000000..8b030bd651 --- /dev/null +++ b/docs/docroot/index.html.po.iso8859-2 @@ -0,0 +1,40 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<HTML> + <HEAD> + <TITLE>Strona testowa instalacji serwera Apache</TITLE> + <META http-equiv="Content-Type" content="text/html; charset=iso-8859-2"> + <!-- translated by Bart Grzybicki, bgrzybicki@morliny.pl - http://www.bgrzybicki.morliny.pl on 19th of December 2000 --> + </HEAD> +<!-- Background white, links blue (unvisited), navy (visited), red (active) --> + <BODY + BGCOLOR="#FFFFFF" + TEXT="#000000" + LINK="#0000FF" + VLINK="#000080" + ALINK="#FF0000" + > + + +<P> +Je¿eli strona ta jest widoczna, oznacza to poprawn± instalacjê serwera Apache. Mo¿na ju¿ zamieniæ zawarto¶æ tej witryny. + +<P><HR WIDTH="50%" SIZE="8"> + +<H2 ALIGN="CENTER">Czy zamiast spodziewanej witryny WWW widoczna jest ta?</H2> + +<P> +Strona ta jest widoczna, poniewa¿ administrator serwera WWW zmieni³ jego konfiguracjê. +Proszê <STRONG>skontaktowaæ siê z osob± odpowiedzialn± za zarz±dzanie tym serwerem.</STRONG> Apache Software Foundation, producent oprogramowania serwerowego Apache, nie administruje t± witryn± i nie jest w stanie pomóc w sprawach zwi±zanych z jej konfiguracj±. + +<P><HR WIDTH="50%" SIZE="8"> + + +<P> +Do niniejszej dystrybucji serwera Apache do³±czono anglojêzyczn± <A HREF="manual/index.html">dokumentacjê</A>. + +<P> +Poni¿sze logo, "Powered by Apache", mo¿na stosowaæ bez ograniczeñ. Dziêkujemy za wybranie Apache'a! + +<DIV ALIGN="CENTER"><IMG SRC="apache_pb.gif" ALT=""></DIV> +</BODY> +</HTML> diff --git a/docs/docroot/index.html.var b/docs/docroot/index.html.var new file mode 100644 index 0000000000..12693fff15 --- /dev/null +++ b/docs/docroot/index.html.var @@ -0,0 +1,34 @@ +index.html.ca +index.html.cz +index.html.de +index.html.dk +index.html.ee +index.html.el +index.html.en +index.html.es +index.html.et +index.html.fr +index.html.he.iso8859-8 +index.html.it +index.html.ja.iso2022-jp +index.html.ja.jis +index.html.kr.iso-kr +index.html.kr.iso2022-kr +index.html.ltz +index.html.lu +index.html.nl +index.html.no +index.html.po.iso-pl +index.html.pt +index.html.pt-br +index.html.ru.cp-1251 +index.html.ru.cp866 +index.html.ru.iso-ru +index.html.ru.koi8-r +index.html.ru.ucs2 +index.html.ru.ucs4 +index.html.ru.utf8 +index.html.se +index.html.tw +index.html.tw.Big5 +index.html.var diff --git a/docs/error/HTTP_BAD_GATEWAY.html.var b/docs/error/HTTP_BAD_GATEWAY.html.var new file mode 100644 index 0000000000..c6f81de0a1 --- /dev/null +++ b/docs/error/HTTP_BAD_GATEWAY.html.var @@ -0,0 +1,66 @@ +Content-language: de +Content-type: text/html +Body:----------de-- +<!--#set var="TITLE" value="Fehlerhaftes Gateway!" --> +<!--#include virtual="include/top.html" --> + + Der Proxy-Server erhielt eine fehlerhafte Antwort + eines übergeordneten Servers oder Proxies. + + <!--#if expr="$REDIRECT_ERROR_NOTES" --> + <!--#include virtual="include/spacer.html" --> + <!--#echo var="REDIRECT_ERROR_NOTES" --> + <!--#endif --> + +<!--#include virtual="include/bottom.html" --> +----------de-- + +Content-language: en +Content-type: text/html +Body:----------en-- +<!--#set var="TITLE" value="Bad Gateway!" --> +<!--#include virtual="include/top.html" --> + + The proxy server received an invalid + response from an upstream server. + + <!--#if expr="$REDIRECT_ERROR_NOTES" --> + <!--#include virtual="include/spacer.html" --> + <!--#echo var="REDIRECT_ERROR_NOTES" --> + <!--#endif --> + +<!--#include virtual="include/bottom.html" --> +----------en-- + +Content-language: es +Content-type: text/html +Body:----------es-- +<!--#set var="TITLE" value="Bad Gateway!" --> +<!--#include virtual="include/top.html" --> + + El servidor fuente recibio información + inválida por parte del servidor destino. + + <!--#if expr="$REDIRECT_ERROR_NOTES" --> + <!--#include virtual="include/spacer.html" --> + <!--#echo var="REDIRECT_ERROR_NOTES" --> + <!--#endif --> + +<!--#include virtual="include/bottom.html" --> +----------es-- + +Content-language: fr +Content-type: text/html +Body:----------fr-- +<!--#set var="TITLE" value="Gateway incorrect!" --> +<!--#include virtual="include/top.html" --> + + Le serveur proxy a reçu une réponse incorrecte de la part d'un serveur supérieur. + + <!--#if expr="$REDIRECT_ERROR_NOTES" --> + <!--#include virtual="include/spacer.html" --> + <!--#echo var="REDIRECT_ERROR_NOTES" --> + <!--#endif --> + +<!--#include virtual="include/bottom.html" --> +----------fr-- diff --git a/docs/error/HTTP_BAD_REQUEST.html.var b/docs/error/HTTP_BAD_REQUEST.html.var new file mode 100644 index 0000000000..938db8174d --- /dev/null +++ b/docs/error/HTTP_BAD_REQUEST.html.var @@ -0,0 +1,46 @@ +Content-language: de +Content-type: text/html +Body:----------de-- +<!--#set var="TITLE" value="Fehlerhafte Anfrage!" --> +<!--#include virtual="include/top.html" --> + + Ihr Browser (oder Proxy) hat eine ungültige Anfrage gesendet, + die vom Server nicht beantwortet werden kann. + +<!--#include virtual="include/bottom.html" --> +----------de-- + +Content-language: en +Content-type: text/html +Body:----------en-- +<!--#set var="TITLE" value="Bad request!" --> +<!--#include virtual="include/top.html" --> + + Your browser (or proxy) sent a request that + this server could not understand. + +<!--#include virtual="include/bottom.html" --> +----------en-- + +Content-language: es +Content-type: text/html +Body:----------es-- +<!--#set var="TITLE" value="Bad request!" --> +<!--#include virtual="include/top.html" --> + + El buscador ha solicitado una operación + que no puede ser procesada por el servidor. + +<!--#include virtual="include/bottom.html" --> +----------es-- + +Content-language: fr +Content-type: text/html +Body:----------fr-- +<!--#set var="TITLE" value="demande incorrecte!" --> +<!--#include virtual="include/top.html" --> + + Votre navigateur (ou votre proxy) a envoyé une demande que ce serveur n'a pas comprise. + +<!--#include virtual="include/bottom.html" --> +----------fr-- diff --git a/docs/error/HTTP_FORBIDDEN.html.var b/docs/error/HTTP_FORBIDDEN.html.var new file mode 100644 index 0000000000..9a30ceb9f1 --- /dev/null +++ b/docs/error/HTTP_FORBIDDEN.html.var @@ -0,0 +1,84 @@ +Content-language: de +Content-type: text/html +Body:----------de-- +<!--#set var="TITLE" value="Zugriff verweigert!" --> +<!--#include virtual="include/top.html" --> + + <!--#if expr="$REDIRECT_URL=//$/" --> + + Der Zugriff auf das angeforderte Verzeichnis ist nicht möglich. + Entweder ist kein Index-Dokument vorhanden oder das Verzeichnis + ist zugriffsgeschützt. + + <!--#else --> + + Der Zugriff auf das angeforderte Objekt ist nicht möglich. + Entweder kann es vom Server nicht gelesen werden oder es + ist zugriffsgeschützt. + + <!--#endif --> + +<!--#include virtual="include/bottom.html" --> +----------de-- + +Content-language: en +Content-type: text/html +Body:----------en-- +<!--#set var="TITLE" value="Access forbidden!" --> +<!--#include virtual="include/top.html" --> +<!--#if expr="$REDIRECT_URL=//$/" --> + + You don't have permission to access the requested directory. + There is either no index document or the directory is read-protected. + + <!--#else --> + + You don't have permission to access the requested object. + It is either read-protected or not readable by the server. + + <!--#endif --> + +<!--#include virtual="include/bottom.html" --> +----------en-- + +Content-language: es +Content-type: text/html +Body:----------es-- +<!--#set var="TITLE" value="Access forbidden!" --> +<!--#include virtual="include/top.html" --> + + <!--#if expr="$REDIRECT_URL=//$/" --> + + Usted no tiene permiso para accesar a la dirección + solicitada. Existe la posibilidad de que el directorio + este protegido contra lectura o que no exista la + documentación requerida. + + <!--#else --> + + Usted no tiene permiso de accesar al objeto solicitado. + Existe la posibilidad de que este protegido contra + lectura o que no haya podido ser leido por el servidor. + + <!--#endif --> + +<!--#include virtual="include/bottom.html" --> +----------es-- + +Content-language: fr +Content-type: text/html +Body:----------fr-- +<!--#set var="TITLE" value="Accès interdit!" --> +<!--#include virtual="include/top.html" --> +<!--#if expr="$REDIRECT_URL=//$/" --> +Vous n'avez pas le droit d'accéder au répertoire +demandé. Soit il n'y a pas de document index soit le répertoire +est protégé. +<!--#else --> +Vous n'avez pas le droit d'accéder à l'objet +demandé. Soit celui-ci est protégé, soit il ne peut +être lu par le serveur. +<!--#endif --> + +<!--#include virtual="include/bottom.html" --> +----------fr-- diff --git a/docs/error/HTTP_GONE.html.var b/docs/error/HTTP_GONE.html.var new file mode 100644 index 0000000000..22aef7c674 --- /dev/null +++ b/docs/error/HTTP_GONE.html.var @@ -0,0 +1,100 @@ +Content-language: de +Content-type: text/html +Body:----------de-- +<!--#set var="TITLE" value="Objekt nicht mehr verfügbar!" --> +<!--#include virtual="include/top.html" --> + + Der angeforderte URL existiert auf dem Server nicht mehr + und wurde dauerhaft entfernt. + Eine Weiterleitungsadresse ist nicht verfügbar. + + <!--#if expr="$HTTP_REFERER" --> + + Bitte informieren Sie den Autor der + <a href="<!--#echo encoding="url" var="HTTP_REFERER"-->">verweisenden Seite</a> + das der Link nicht mehr aktuell ist. + + <!--#else --> + + Falls Sie einem Link von einer anderen Seite gefolgt sind, + informieren Sie bitte den Autor der Seite hierüber. + + <!--#endif --> + +<!--#include virtual="include/bottom.html" --> +----------de-- + +Content-language: en +Content-type: text/html +Body:----------en-- +<!--#set var="TITLE" value="Resource is no longer available!" --> +<!--#include virtual="include/top.html" --> + + The requested URL is no longer available on this server and there is no + forwarding address. + + <!--#if expr="$HTTP_REFERER" --> + + Please inform the author of the + <a href="<!--#echo encoding="url" var="HTTP_REFERER"-->">referring + page</a> that the link is outdated. + + <!--#else --> + + If you followed a link from a foreign page, please contact the + author of this page. + + <!--#endif --> + +<!--#include virtual="include/bottom.html" --> +----------en-- + +Content-language: es +Content-type: text/html +Body:----------es-- +<!--#set var="TITLE" value="Resource is no longer available!" --> +<!--#include virtual="include/top.html" --> + + Los recursos solicitados ya no estan disponibles en + este servidor y no existe una dirección alterna. + + <!--#if expr="$HTTP_REFERER" --> + + Le solicitamos que comunique al autor de la + <a href="<!--#echo encoding="url" var="HTTP_REFERER"-->">página + referida</a> que el URL esta fuera de tiempo. + + <!--#else --> + + Si usted siguio el URL desde una página externa, + porfavor contacte al autor de esa página. + + + <!--#endif --> + +<!--#include virtual="include/bottom.html" --> +----------es-- + +Content-language: fr +Content-type: text/html +Body:----------fr-- +<!--#set var="TITLE" value="Cette ressource n'existe plus!" --> +<!--#include virtual="include/top.html" --> + + L'URL demandée n'est plus accessible sur ce serveur et il + n'y a pas d'adresse de suite. + + <!--#if expr="$HTTP_REFERER" --> + + Nous vous prions d'informer l'auteur de + <a href="<!--#echo encoding="url" var="HTTP_REFERER"-->">la + page en question</a> que la référence n'est plus actuelle. + + <!--#else --> + + Si vous avez suivi une référence issue d'une page autre, + veuillez contacter l'auteur de cette page. + + <!--#endif --> +<!--#include virtual="include/bottom.html" --> +----------fr-- diff --git a/docs/error/HTTP_INTERNAL_SERVER_ERROR.html.var b/docs/error/HTTP_INTERNAL_SERVER_ERROR.html.var new file mode 100644 index 0000000000..742e235eb7 --- /dev/null +++ b/docs/error/HTTP_INTERNAL_SERVER_ERROR.html.var @@ -0,0 +1,86 @@ +Content-language: de +Content-type: text/html +Body:----------de-- +<!--#set var="TITLE" value="Serverfehler!" --> +<!--#include virtual="include/top.html" --> + + <!--#if expr="$REDIRECT_ERROR_NOTES" --> + + Dir Proxy-Anfrage konnte nicht ausgeführt werden. + + <!--#include virtual="include/spacer.html" --> + + Fehlermeldung: + <BR><!--#echo var="REDIRECT_ERROR_NOTES" --> + + + <!--#else --> + + Die Anfrage kann nicht beantwortet werden, da im Server + ein interner Fehler aufgetreten ist. + Der Server ist entweder überlastet oder ein Fehler in + einem CGI-Script ist aufgetreten. + + <!--#endif --> + +<!--#include virtual="include/bottom.html" --> +----------de-- + +Content-language: en +Content-type: text/html +Body:----------en-- +<!--#set var="TITLE" value="Server error!" --> +<!--#include virtual="include/top.html" --> + + <!--#if expr="$REDIRECT_ERROR_NOTES" --> + <!--#echo var="REDIRECT_ERROR_NOTES" --> + <!--#else --> + + The server encountered an internal error and was + unable to complete your request. The server is either + overloaded or there was an error in a CGI script. + + <!--#endif --> + +<!--#include virtual="include/bottom.html" --> +----------en-- + +Content-language: es +Content-type: text/html +Body:----------es-- +<!--#set var="TITLE" value="Server error!" --> +<!--#include virtual="include/top.html" --> + + <!--#if expr="$REDIRECT_ERROR_NOTES" --> + <!--#echo var="REDIRECT_ERROR_NOTES" --> + <!--#else --> + + El servidor encontro un error interno y fue + imposible completar su solicitud. + Existe tambien la posibilidad de que el servidor + este sobrecargado o de algún error en un + programa de CGI. + + <!--#endif --> + +<!--#include virtual="include/bottom.html" --> +----------es-- + +Content-language: fr +Content-type: text/html +Body:----------fr-- +<!--#set var="TITLE" value="Erreur du serveur!" --> +<!--#include virtual="include/top.html" --> + + <!--#if expr="$REDIRECT_ERROR_NOTES" --> + <!--#echo var="REDIRECT_ERROR_NOTES" --> + <!--#else --> + + Le serveur a èté victime d'une erreur interne et n'a pas été + capable de faire aboutir votre requête. Soit le server est surchargé + soit il s'agit d'une erreur dans le script CGI. + + <!--#endif --> + +<!--#include virtual="include/bottom.html" --> +----------fr-- diff --git a/docs/error/HTTP_LENGTH_REQUIRED.html.var b/docs/error/HTTP_LENGTH_REQUIRED.html.var new file mode 100644 index 0000000000..24d6909c3c --- /dev/null +++ b/docs/error/HTTP_LENGTH_REQUIRED.html.var @@ -0,0 +1,37 @@ +Content-language: de +Content-type: text/html +Body:----------de-- +<!--#set var="TITLE" value="Content-Length-Angabe fehlerhaft!" --> +<!--#include virtual="include/top.html" --> + + Die Anfrage kann nicht beantwortet werden. + Bei Verwendung der <!--#echo encoding="none" var="REDIRECT_METHOD"-->-Methode + muß ein korrekter <code>Content-Length</code>-Header + angegeben werden. + +<!--#include virtual="include/bottom.html" --> +----------de-- + +Content-language: en +Content-type: text/html +Body:----------en-- +<!--#set var="TITLE" value="Bad Content-Length!" --> +<!--#include virtual="include/top.html" --> + + A request with the <!--#echo encoding="none" var="REDIRECT_METHOD"--> + method requires a valid <code>Content-Length</code> header. + +<!--#include virtual="include/bottom.html" --> +----------en-- + +Content-language: fr +Content-type: text/html +Body:----------fr-- +<!--#set var="TITLE" value="Longueur du contenu illégal!" --> +<!--#include virtual="include/top.html" --> + + Une requête utilisant la méthode <!--#echo encoding="none" var="REDIRECT_METHOD"--> + nécessite un header valable <code>Content-Length</code> (indiquant la longueur). + +<!--#include virtual="include/bottom.html" --> +----------fr-- diff --git a/docs/error/HTTP_METHOD_NOT_ALLOWED.html.var b/docs/error/HTTP_METHOD_NOT_ALLOWED.html.var new file mode 100644 index 0000000000..38d2bd8a05 --- /dev/null +++ b/docs/error/HTTP_METHOD_NOT_ALLOWED.html.var @@ -0,0 +1,48 @@ +Content-language: de +Content-type: text/html +Body:----------de-- +<!--#set var="TITLE" value="Methode nicht erlaubt!" --> +<!--#include virtual="include/top.html" --> + + Die <!--#echo encoding="none" var="REDIRECT_METHOD"-->-Methode + ist für den angeforderten URL nicht erlaubt. + +<!--#include virtual="include/bottom.html" --> +----------de-- + +Content-language: en +Content-type: text/html +Body:----------en-- +<!--#set var="TITLE" value="Method not allowed!" --> +<!--#include virtual="include/top.html" --> + + The <!--#echo encoding="none" var="REDIRECT_METHOD"--> + method is not allowed for the requested URL. + +<!--#include virtual="include/bottom.html" --> +----------en-- + +Content-language: es +Content-type: text/html +Body:----------es-- +<!--#set var="TITLE" value="Method not allowed!" --> +<!--#include virtual="include/top.html" --> + + El <!--#echo encoding="none" var="REDIRECT_METHOD"--> + método utilizado por su solicitud no esta + permitido por el URL. + +<!--#include virtual="include/bottom.html" --> +----------es-- + +Content-language: fr +Content-type: text/html +Body:----------fr-- +<!--#set var="TITLE" value="Méthode interdite!" --> +<!--#include virtual="include/top.html" --> + + La méthode <!--#echo encoding="none" var="REDIRECT_METHOD"--> + n'est pas utilisable pour l'URL requise. + +<!--#include virtual="include/bottom.html" --> +----------fr-- diff --git a/docs/error/HTTP_NOT_ACCEPTABLE.html.var b/docs/error/HTTP_NOT_ACCEPTABLE.html.var new file mode 100644 index 0000000000..fe4236fac7 --- /dev/null +++ b/docs/error/HTTP_NOT_ACCEPTABLE.html.var @@ -0,0 +1,49 @@ +Content-language: de +Content-type: text/html +Body:----------de-- +<!--#set var="TITLE" value="Kein passendes Objekt gefunden!" --> +<!--#include virtual="include/top.html" --> + + Unter dem angeforderten URL konnte kein geeignetes Objekt + auf dem Server gefunden werden, das dem vom Browser + geforderten Format entsprechen würde. + +<!--#include virtual="include/bottom.html" --> +----------de-- + +Content-language: en +Content-type: text/html +Body:----------en-- +<!--#set var="TITLE" value="No acceptable object found!" --> +<!--#include virtual="include/top.html" --> + + An appropriate representation of the requested resource + could not be found on this server. + +<!--#include virtual="include/bottom.html" --> +----------en-- + +Content-language: es +Content-type: text/html +Body:----------es-- +<!--#set var="TITLE" value="No acceptable object found!" --> +<!--#include virtual="include/top.html" --> + + Una representación apropiada de los recursos + solicitados no ha podido ser localizada en + este servidor. + +<!--#include virtual="include/bottom.html" --> +----------es-- + +Content-language: fr +Content-type: text/html +Body:----------fr-- +<!--#set var="TITLE" value="L'objet trouvé n'est pas acceptable!" --> +<!--#include virtual="include/top.html" --> + + Une représentation appropriée de la ressource requise + n'a pu être trouvée sur ce serveur. + +<!--#include virtual="include/bottom.html" --> +----------fr-- diff --git a/docs/error/HTTP_NOT_FOUND.html.var b/docs/error/HTTP_NOT_FOUND.html.var new file mode 100644 index 0000000000..8a7e8ee5fb --- /dev/null +++ b/docs/error/HTTP_NOT_FOUND.html.var @@ -0,0 +1,113 @@ +Content-language: de +Content-type: text/html +Body:----------de-- +<!--#set var="TITLE" value="Objekt nicht gefunden!" --> +<!--#include virtual="include/top.html" --> + + Der angeforderte URL konnte auf dem Server nicht gefunden werden. + + <!--#if expr="$HTTP_REFERER" --> + + Der Link auf der + <a href="<!--#echo encoding="url" var="HTTP_REFERER"-->">verweisenden +Seite< +/a> + scheint falsch oder nicht mehr aktuell zu sein. + Bitte informieren Sie den Autor + <a href="<!--#echo encoding="url" var="HTTP_REFERER"-->">dieser Seite</a> + über den Fehler. + + <!--#else --> + + Sofern Sie den URL manuell eingegeben haben, + überprüfen Sie bitte die Schreibweise und versuchen es erneut. + + <!--#endif --> + +<!--#include virtual="include/bottom.html" --> +----------de-- + +Content-language: en +Content-type: text/html +Body:----------en-- +<!--#set var="TITLE" value="Object not found!" --> +<!--#include virtual="include/top.html" --> + + The requested URL was not found on this server. + + <!--#if expr="$HTTP_REFERER" --> + + The link on the + <a href="<!--#echo encoding="url" var="HTTP_REFERER"-->">referring +page</a> + seems to be wrong or outdated. Please inform the author of + <a href="<!--#echo encoding="url" var="HTTP_REFERER"-->">that page</a> + about the error. + + <!--#else --> + + If you entered the URL manually please check your + spelling and try again. + + <!--#endif --> + +<!--#include virtual="include/bottom.html" --> +----------en-- + +Content-language: es +Content-type: text/html +Body:----------es-- +<!--#set var="TITLE" value="Object not found!" --> +<!--#include virtual="include/top.html" --> + + El URL requerido no ha sido localizado en + este servidor. + + <!--#if expr="$HTTP_REFERER" --> + + El URL en la + <a href="<!--#echo encoding="url" var="HTTP_REFERER"-->">página +referida</a> + parece tener algun error o estar fuera de tiempo. Porfavor + comunique al autor de + <a href="<!--#echo encoding="url" var="HTTP_REFERER"-->">la +página</a> + acerca del error. + + <!--#else --> + + Si usted provisiono el URL de manera manual le solicitamos + que porfavor revise los datos e intente de nuevo. + + <!--#endif --> + +<!--#include virtual="include/bottom.html" --> +----------es-- + +Content-language: fr +Content-type: text/html +Body:----------fr-- +<!--#set var="TITLE" value="Objet non trouvé!" --> +<!--#include virtual="include/top.html" --> + + L'URL requise n'a pu etre trouvée sur ce serveur. + + <!--#if expr="$HTTP_REFERER" --> + + La référence sur + <a href="<!--#echo encoding="url" var="HTTP_REFERER"-->">la page +citée</a> + semble être erronée ou perimée. Nous vous prions +d'informer l'auteur de + <a href="<!--#echo encoding="url" var="HTTP_REFERER"-->">cette page</a> + de cette erreur. + + <!--#else --> + + Si vous avez tapé l'URL à la main, veuillez vérifier + l'orthographe et réessayer. + + <!--#endif --> + +<!--#include virtual="include/bottom.html" --> +----------fr-- diff --git a/docs/error/HTTP_NOT_IMPLEMENTED.html.var b/docs/error/HTTP_NOT_IMPLEMENTED.html.var new file mode 100644 index 0000000000..ed3468eaf3 --- /dev/null +++ b/docs/error/HTTP_NOT_IMPLEMENTED.html.var @@ -0,0 +1,45 @@ +Content-language: de +Content-type: text/html +Body:----------de-- +<!--#set var="TITLE" value="Anfrage nicht ausführbar!" --> +<!--#include virtual="include/top.html" --> + + Die vom Browser angeforderte Aktion wird vom Server + nicht unterstützt. + +<!--#include virtual="include/bottom.html" --> +----------de-- + +Content-language: en +Content-type: text/html +Body:----------en-- +<!--#set var="TITLE" value="Cannot proceed request!" --> +<!--#include virtual="include/top.html" --> + + The server does not support the action requested by the browser. + +<!--#include virtual="include/bottom.html" --> +----------en-- + +Content-language: es +Content-type: text/html +Body:----------es-- +<!--#set var="TITLE" value="Cannot proceed request!" --> +<!--#include virtual="include/top.html" --> + + El buscador esta solicitando una acción + que no puede ser procesada. + +<!--#include virtual="include/bottom.html" --> +----------es-- + +Content-language: fr +Content-type: text/html +Body:----------fr-- +<!--#set var="TITLE" value="La requête ne peut être effectuée!" --> +<!--#include virtual="include/top.html" --> + +Le serveur n'est pas en mesure d'effectuer l'action requise par le navigateur. + +<!--#include virtual="include/bottom.html" --> +----------fr-- diff --git a/docs/error/HTTP_PRECONDITION_FAILED.html.var b/docs/error/HTTP_PRECONDITION_FAILED.html.var new file mode 100644 index 0000000000..164bfcef6d --- /dev/null +++ b/docs/error/HTTP_PRECONDITION_FAILED.html.var @@ -0,0 +1,44 @@ +Content-language: de +Content-type: text/html +Body:----------de-- +<!--#set var="TITLE" value="Vorbedingung negativ!" --> +<!--#include virtual="include/top.html" --> + + Die Vorbedingung unter dem angeforderten URL wurde + negativ ausgewertet. + +<!--#include virtual="include/bottom.html" --> +----------de-- + +Content-language: en +Content-type: text/html +Body:----------en-- +<!--#set var="TITLE" value="Precondition failed!" --> +<!--#include virtual="include/top.html" --> + + The precondition on the request for the URL failed positive evaluation. + +<!--#include virtual="include/bottom.html" --> +----------en-- + +Content-language: es +Content-type: text/html +Body:----------es-- +<!--#set var="TITLE" value="Precondition failed!" --> +<!--#include virtual="include/top.html" --> + + La precondicion para que exista una + conección al URL solicitado es falsa. + +<!--#include virtual="include/bottom.html" --> +----------es-- + +Content-language: fr +Content-type: text/html +Body:----------fr-- +<!--#set var="TITLE" value="Précondition négative!" --> +<!--#include virtual="include/top.html" --> + +La précondition pour l'URL requise a été évaluée négativement. + +<!--#include virtual="include/bottom.html" -->----------fr-- diff --git a/docs/error/HTTP_REQUEST_ENTITY_TOO_LARGE.html.var b/docs/error/HTTP_REQUEST_ENTITY_TOO_LARGE.html.var new file mode 100644 index 0000000000..af7ef260e0 --- /dev/null +++ b/docs/error/HTTP_REQUEST_ENTITY_TOO_LARGE.html.var @@ -0,0 +1,51 @@ +Content-language: de +Content-type: text/html +Body:----------de-- +<!--#set var="TITLE" value="Übergebene Daten zu groß!" --> +<!--#include virtual="include/top.html" --> + + Die bei der Anfrage übermittelnden Daten sind für + die <!--#echo encoding="none" var="REDIRECT_METHOD"-->-Methode nicht erlaubt + oder die Datenmenge hat das Maximum überschritten. + +<!--#include virtual="include/bottom.html" --> +----------de-- + +Content-language: en +Content-type: text/html +Body:----------en-- +<!--#set var="TITLE" value="Request entity too large!" --> +<!--#include virtual="include/top.html" --> + + The <!--#echo encoding="none" var="REDIRECT_METHOD"--> method does not allow the data + transmitted, or the data volume exceeds the capacity limit. + +<!--#include virtual="include/bottom.html" --> +----------en-- + +Content-language: es +Content-type: text/html +Body:----------es-- +<!--#set var="TITLE" value="Request entity too large!" --> +<!--#include virtual="include/top.html" --> + + Los recursos establecidos no permiten peticiones con + el <!--#echo encoding="none" var="REDIRECT_METHOD"--> método + suministrado por su solicitud, o, la cantidad de datos + provistos exceden los límites de capacidad. + +<!--#include virtual="include/bottom.html" --> +----------es-- + +Content-language: fr +Content-type: text/html +Body:----------fr-- +<!--#set var="TITLE" value="Volume de la demande trop grand!" --> +<!--#include virtual="include/top.html" --> + +La méthode <!--#echo encoding="none" var="REDIRECT_METHOD"--> +n'autorise pas le transfert de ces données ou bien le volume +des données excède la limite de capacité. + +<!--#include virtual="include/bottom.html" --> +----------fr-- diff --git a/docs/error/HTTP_REQUEST_TIME_OUT.html.var b/docs/error/HTTP_REQUEST_TIME_OUT.html.var new file mode 100644 index 0000000000..6295279a03 --- /dev/null +++ b/docs/error/HTTP_REQUEST_TIME_OUT.html.var @@ -0,0 +1,48 @@ +Content-language: de +Content-type: text/html +Body:----------de-- +<!--#set var="TITLE" value="Zeitlimit überschritten!" --> +<!--#include virtual="include/top.html" --> + + Der Server konnte nicht mehr länger auf die Beendigung + der Browseranfrage warten, die Netzwerkverbindung wurde + vom Server geschlossen. + +<!--#include virtual="include/bottom.html" --> +----------de-- + +Content-language: en +Content-type: text/html +Body:----------en-- +<!--#set var="TITLE" value="Request time-out!" --> +<!--#include virtual="include/top.html" --> + + The server closed the network connection because the browser + didn't finish the request within the specified time. + +<!--#include virtual="include/bottom.html" --> +----------en-- + +Content-language: es +Content-type: text/html +Body:----------es-- +<!--#set var="TITLE" value="Request time-out!" --> +<!--#include virtual="include/top.html" --> + + El servidor ha cerrado la conección de red + debido a que el buscador no termino la solicitud + dentro del tiempo especificado. + +<!--#include virtual="include/bottom.html" --> +----------es-- + +Content-language: fr +Content-type: text/html +Body:----------fr-- +<!--#set var="TITLE" value="Précondition négative!" --> +<!--#include virtual="include/top.html" --> + +La précondition pour l'URL requise a été évaluée négativement. + +<!--#include virtual="include/bottom.html" --> +----------fr-- diff --git a/docs/error/HTTP_REQUEST_URI_TOO_LARGE.html.var b/docs/error/HTTP_REQUEST_URI_TOO_LARGE.html.var new file mode 100644 index 0000000000..f34de5558c --- /dev/null +++ b/docs/error/HTTP_REQUEST_URI_TOO_LARGE.html.var @@ -0,0 +1,48 @@ +Content-language: de +Content-type: text/html +Body:----------de-- +<!--#set var="TITLE" value="Übergebener URI zu groß!" --> +<!--#include virtual="include/top.html" --> + + Der bei der Anfrage übermittelnde URI überschreitet + die maximale Länge. + Die Anfrage kann nicht ausgeführt werden. + +<!--#include virtual="include/bottom.html" --> +----------de-- + +Content-language: en +Content-type: text/html +Body:----------en-- +<!--#set var="TITLE" value="Submitted URI too large!" --> +<!--#include virtual="include/top.html" --> + + The length of the requested URL exceeds the capacity limit for this server. + The request cannot be processed. + +<!--#include virtual="include/bottom.html" --> +----------en-- + +Content-language: es +Content-type: text/html +Body:----------es-- +<!--#set var="TITLE" value="Submitted URI too large!" --> +<!--#include virtual="include/top.html" --> + + Su solicitud no puede procesarse debido a que la + longitud del URL excede la capacidad limite del + servidor. + +<!--#include virtual="include/bottom.html" --> +----------es-- + +Content-language: fr +Content-type: text/html +Body:----------fr-- +<!--#set var="TITLE" value="L'URI demandee est trop longue!" --> +<!--#include virtual="include/top.html" --> + +La longueur de l'URL demandée excède la limite de capacitè pour ce serveur. Nous ne pouvons donner suite à votre requête. + +<!--#include virtual="include/bottom.html" --> +----------fr-- diff --git a/docs/error/HTTP_SERVICE_UNAVAILABLE.html.var b/docs/error/HTTP_SERVICE_UNAVAILABLE.html.var new file mode 100644 index 0000000000..d29f16fac8 --- /dev/null +++ b/docs/error/HTTP_SERVICE_UNAVAILABLE.html.var @@ -0,0 +1,56 @@ +Content-language: de +Content-type: text/html +Body:----------de-- +<!--#set var="TITLE" value="Zugriff nicht möglich!" --> +<!--#include virtual="include/top.html" --> + + Der Server ist derzeit nicht in der Lage die Anfrage + zu bearbeiten. Entweder ist der Server derzeit überlastet + oder wegen Wartungsarbeiten nicht verfügbar. + Bitte versuchen Sie es später wieder. + +<!--#include virtual="include/bottom.html" --> +----------de-- + +Content-language: en +Content-type: text/html +Body:----------en-- +<!--#set var="TITLE" value="Service unavailable!" --> +<!--#include virtual="include/top.html" --> + + The server is temporarily unable to service your + request due to maintenance downtime or capacity + problems. Please try again later. + +<!--#include virtual="include/bottom.html" --> +----------en-- + +Content-language: es +Content-type: text/html +Body:----------es-- +<!--#set var="TITLE" value="Service unavailable!" --> +<!--#include virtual="include/top.html" --> + + El servidor no puede atender su solicitud por + el momento debido a problemas de mantenimiento + o de capacidad. + + Le solicitamos que porfavor repita la operación + mas tarde. + + +<!--#include virtual="include/bottom.html" --> +----------es-- + +Content-language: fr +Content-type: text/html +Body:----------fr-- +<!--#set var="TITLE" value="Service inaccessible!" --> +<!--#include virtual="include/top.html" --> + + En raison de travaux de maintenance ou de problèmes de capacité + le serveur n'est pas en mesure de répondre à votre requête pour l'instant. + Veuillez réessayer plus tard. + +<!--#include virtual="include/bottom.html" --> +----------fr-- diff --git a/docs/error/HTTP_UNAUTHORIZED.html.var b/docs/error/HTTP_UNAUTHORIZED.html.var new file mode 100644 index 0000000000..4152b66146 --- /dev/null +++ b/docs/error/HTTP_UNAUTHORIZED.html.var @@ -0,0 +1,84 @@ +Content-language: de +Content-type: text/html +Body:----------de-- +<!--#set var="TITLE" value="Authentikation fehlgeschlagen!" --> +<!--#include virtual="include/top.html" --> + + Der Server konnte nicht verifizieren, ob Sie authorisiert sind, + auf den URL "<!--#echo encoding="url" var="REDIRECT_URL"-->" zuzugreifen. + Entweder wurden falsche Referenzen (z.B. ein falsches Passwort) + angeben oder ihr Browser versteht nicht, wie die geforderten + Referenzen zu übermitteln sind. + +<!--#include virtual="include/spacer.html" --> + + Sofern Sie für den Zugriff berechtigt sind, überprüfen Sie bitte + die eingegebene User-ID und das Passwort und versuchen es erneut. + +<!--#include virtual="include/bottom.html" --> +----------de-- + +Content-language: en +Content-type: text/html +Body:----------en-- +<!--#set var="TITLE" value="Authentication required!" --> +<!--#include virtual="include/top.html" --> + + This server could not verify that you are authorized to access + the URL "<!--#echo encoding="url" var="REDIRECT_URL" -->". + You either supplied the wrong credentials (e.g., bad password), or your + browser doesn't understand how to supply the credentials required. + + <!--#include virtual="include/spacer.html" --> + + In case you are allowed to request the document, please + check your user-id and password and try again. + +<!--#include virtual="include/bottom.html" --> +----------en-- + +Content-language: es +Content-type: text/html +Body:----------es-- +<!--#set var="TITLE" value="Authentication required!" --> +<!--#include virtual="include/top.html" --> + + El servidor no puede certificar que usted este autorizado + para accesar el URL "<!--#echo encoding="url" var="REDIRECT_URL" -->". + Usted pudo suministrar información incorrecta accidentalmente + (ejem. una contraseña inválida) o, el buscador no sabe + como suministrar la información requerida. + + <!--#include virtual="include/spacer.html" --> + + En caso de que a usted le este permitido el uso del + documento requerido, le solicitamos de la manera mas atenta + que porfavor vuelva a intentar la operación suministrando + nuevamente su numero de identificación y su contraseña. + + Muchas Gracias. +<!--#include virtual="include/bottom.html" --> +----------es-- + +Content-language: fr +Content-type: text/html +Body:----------fr-- +<!--#set var="TITLE" value="Autorisation requise!" --> +<!--#include virtual="include/top.html" --> + + Ce server n'a pas été en mesure de vérifier que + vous êtes autorisé à accéder à cette + URL "<!--#echo encoding="url" var="REDIRECT_URL" -->". + + Vous avez ou bien fourni des coordonnées erronées + (p.ex. mot de passe inexact) ou bien votre navigateur ne parvient + pas à fournir les données exactes. + + <!--#include virtual="include/spacer.html" --> + + Si vous êtez autorisé à requérir le document, + veuillez vérifier votre nom d'utilisateur et votre mot de passe + et réessayer. + +<!--#include virtual="include/bottom.html" --> +----------fr-- diff --git a/docs/error/HTTP_UNSUPPORTED_MEDIA_TYPE.html.var b/docs/error/HTTP_UNSUPPORTED_MEDIA_TYPE.html.var new file mode 100644 index 0000000000..78c48bd2c4 --- /dev/null +++ b/docs/error/HTTP_UNSUPPORTED_MEDIA_TYPE.html.var @@ -0,0 +1,45 @@ +Content-language: de +Content-type: text/html +Body:----------de-- +<!--#set var="TITLE" value="Nicht unterstütztes Format!" --> +<!--#include virtual="include/top.html" --> + + Das bei der Anfrage übermittelte Format (media type) + wird vom Server nicht unterstützt. + +<!--#include virtual="include/bottom.html" --> +----------de-- + +Content-language: en +Content-type: text/html +Body:----------en-- +<!--#set var="TITLE" value="Unsupported media type!" --> +<!--#include virtual="include/top.html" --> + + The server does not support the media type transmitted in the request. + +<!--#include virtual="include/bottom.html" --> +----------en-- + +Content-language: es +Content-type: text/html +Body:----------es-- +<!--#set var="TITLE" value="Unsupported media type!" --> +<!--#include virtual="include/top.html" --> + + Los datos de su solicitud no se encuentran en + un formato aceptado por este recurso. + +<!--#include virtual="include/bottom.html" --> +----------es-- + +Content-language: fr +Content-type: text/html +Body:----------fr-- +<!--#set var="TITLE" value="type de média invalide!" --> +<!--#include virtual="include/top.html" --> + +Le serveur ne supporte pas le type de média utilisé dans votre requête. + +<!--#include virtual="include/bottom.html" --> +----------fr-- diff --git a/docs/error/HTTP_VARIANT_ALSO_VARIES.html.var b/docs/error/HTTP_VARIANT_ALSO_VARIES.html.var new file mode 100644 index 0000000000..b558284f47 --- /dev/null +++ b/docs/error/HTTP_VARIANT_ALSO_VARIES.html.var @@ -0,0 +1,51 @@ +Content-language: de +Content-type: text/html +Body:----------de-- +<!--#set var="TITLE" value="Variante ebenfalls veränderlich!" --> +<!--#include virtual="include/top.html" --> + + Ein Zugriff auf das angeforderte Objekt bzw. einer + Variante dieses Objektes ist nicht möglich, da es ebenfalls + ein variables Objekt darstellt. + +<!--#include virtual="include/bottom.html" --> +----------de-- + +Content-language: en +Content-type: text/html +Body:----------en-- +<!--#set var="TITLE" value="Variant also varies!" --> +<!--#include virtual="include/top.html" --> + + A variant for the requested entity + is itself a negotiable resource. + Access not possible. + +<!--#include virtual="include/bottom.html" --> +----------en-- + +Content-language: es +Content-type: text/html +Body:----------es-- +<!--#set var="TITLE" value="Variant also varies!" --> +<!--#include virtual="include/top.html" --> + + No es posible tener acceso debido a que + una variante de la solicitud es por si + misma un recurso negociable. + +<!--#include virtual="include/bottom.html" --> +----------es-- + +Content-language: fr +Content-type: text/html +Body:----------fr-- +<!--#set var="TITLE" value="La variante varie elle-même!" --> +<!--#include virtual="include/top.html" --> + + Une variante pour l'entité requise + est elle-même une ressource négociable. + L'accès est impossible. + +<!--#include virtual="include/bottom.html" --> +----------fr-- diff --git a/docs/error/contact.html.var b/docs/error/contact.html.var new file mode 100644 index 0000000000..f39136b637 --- /dev/null +++ b/docs/error/contact.html.var @@ -0,0 +1,30 @@ +Content-language: de +Content-type: text/html +Body:----------de-- + Sofern Sie dies für eine Fehlfunktion des Servers halten, + informieren Sie bitte den + <A HREF="mailto:<!--#echo encoding="none" var="SERVER_ADMIN" -->">Webmaster</A> + hierüber. +----------de-- + +Content-language: en +Content-type: text/html +Body:----------en-- + In case you think this is a server error, please contact + the <A HREF="mailto:<!--#echo encoding="none" var="SERVER_ADMIN" -->">Webmaster</A> +----------en-- + +Content-language: es +Content-type: text/html +Body:----------es-- +Favor de contactar al +<A HREF="mailto:<!--#echo encoding="none" var="SERVER_ADMIN" -->">Webmaster</A> +en caso de que usted crea que existe un error en el servidor. +----------es-- + +Content-language: fr +Content-type: text/html +Body:----------fr-- +Si vous pensez qu'il s'agit d'une erreur du serveur, veuillez contacter +le <A HREF="mailto:<!--#echo encoding="none" var="SERVER_ADMIN" -->">gestionnaire du site</A> +----------fr-- diff --git a/docs/error/include/bottom.html b/docs/error/include/bottom.html new file mode 100644 index 0000000000..a3b23698d3 --- /dev/null +++ b/docs/error/include/bottom.html @@ -0,0 +1,12 @@ +<!--#include virtual="spacer.html" --> +<!--#include virtual="../contact" --> +</DL> +<!--#if expr="$INCLUDE_LOGO=yes" --> + <BR CLEAR=<!--#if expr="$ALIGN_LOGO=left" -->right<!--#else -->left<!--#endif -->> +<!--#endif --> + +<TABLE BORDER=0 CELLPADDING=6 WIDTH="100%"> +<TR><TD ALIGN=CENTER BGCOLOR="#000000"> +<FONT SIZE="+2" COLOR="#FFFFFF"><strong>Error <!--#echo encoding="none" var="REDIRECT_STATUS" --></strong></FONT> +</TD></TR></TABLE> +<!--#include virtual="footer.html" --> diff --git a/docs/error/include/spacer.html b/docs/error/include/spacer.html new file mode 100644 index 0000000000..70c5b25f4a --- /dev/null +++ b/docs/error/include/spacer.html @@ -0,0 +1 @@ +</DL><DL><DD> diff --git a/docs/error/include/top.html b/docs/error/include/top.html new file mode 100644 index 0000000000..3e24e48615 --- /dev/null +++ b/docs/error/include/top.html @@ -0,0 +1,12 @@ +<!--#include virtual="header.html" --> +<BODY BGCOLOR="#FFFFFF" TEXT="#000000" LINK="#0000CC"> +<BR> +<TABLE BORDER=0 CELLPADDING=6 WIDTH="100%"> +<TR><TD ALIGN=CENTER BGCOLOR="#000000"> +<FONT SIZE="+2" COLOR="#FFFFFF"><strong><!--#echo encoding="none" var="TITLE" --></strong></FONT> +</TD></TR></TABLE> +<!--#if expr="$INCLUDE_LOGO=yes" --> + <IMG ALIGN=<!--#echo encoding="none" var="ALIGN_LOGO" --> SRC="/error/images/logo.gif" ALT=""> +<!--#endif --> +<DL> +<DD> diff --git a/docs/icons/a.png b/docs/icons/a.png Binary files differnew file mode 100644 index 0000000000..c1840256dc --- /dev/null +++ b/docs/icons/a.png diff --git a/docs/icons/alert.black.png b/docs/icons/alert.black.png Binary files differnew file mode 100644 index 0000000000..af6b1246ad --- /dev/null +++ b/docs/icons/alert.black.png diff --git a/docs/icons/alert.red.png b/docs/icons/alert.red.png Binary files differnew file mode 100644 index 0000000000..b9222fd524 --- /dev/null +++ b/docs/icons/alert.red.png diff --git a/docs/icons/apache_pb.png b/docs/icons/apache_pb.png Binary files differnew file mode 100644 index 0000000000..eb99a8cd39 --- /dev/null +++ b/docs/icons/apache_pb.png diff --git a/docs/icons/apache_pb2.png b/docs/icons/apache_pb2.png Binary files differnew file mode 100644 index 0000000000..28baa70fb8 --- /dev/null +++ b/docs/icons/apache_pb2.png diff --git a/docs/icons/back.png b/docs/icons/back.png Binary files differnew file mode 100644 index 0000000000..2d8d353bbc --- /dev/null +++ b/docs/icons/back.png diff --git a/docs/icons/ball.gray.png b/docs/icons/ball.gray.png Binary files differnew file mode 100644 index 0000000000..7b756f2d82 --- /dev/null +++ b/docs/icons/ball.gray.png diff --git a/docs/icons/ball.red.png b/docs/icons/ball.red.png Binary files differnew file mode 100644 index 0000000000..05f3e50629 --- /dev/null +++ b/docs/icons/ball.red.png diff --git a/docs/icons/binary.png b/docs/icons/binary.png Binary files differnew file mode 100644 index 0000000000..c5119d1e1e --- /dev/null +++ b/docs/icons/binary.png diff --git a/docs/icons/binhex.png b/docs/icons/binhex.png Binary files differnew file mode 100644 index 0000000000..eff532202d --- /dev/null +++ b/docs/icons/binhex.png diff --git a/docs/icons/blank.png b/docs/icons/blank.png Binary files differnew file mode 100644 index 0000000000..3802c03c9c --- /dev/null +++ b/docs/icons/blank.png diff --git a/docs/icons/bomb.png b/docs/icons/bomb.png Binary files differnew file mode 100644 index 0000000000..5261a0575e --- /dev/null +++ b/docs/icons/bomb.png diff --git a/docs/icons/box1.png b/docs/icons/box1.png Binary files differnew file mode 100644 index 0000000000..c55fccf8dc --- /dev/null +++ b/docs/icons/box1.png diff --git a/docs/icons/box2.png b/docs/icons/box2.png Binary files differnew file mode 100644 index 0000000000..26d14325d9 --- /dev/null +++ b/docs/icons/box2.png diff --git a/docs/icons/broken.png b/docs/icons/broken.png Binary files differnew file mode 100644 index 0000000000..e8fd150a33 --- /dev/null +++ b/docs/icons/broken.png diff --git a/docs/icons/burst.png b/docs/icons/burst.png Binary files differnew file mode 100644 index 0000000000..2329898f2a --- /dev/null +++ b/docs/icons/burst.png diff --git a/docs/icons/c.png b/docs/icons/c.png Binary files differnew file mode 100644 index 0000000000..41593b36b3 --- /dev/null +++ b/docs/icons/c.png diff --git a/docs/icons/comp.blue.png b/docs/icons/comp.blue.png Binary files differnew file mode 100644 index 0000000000..60ff156deb --- /dev/null +++ b/docs/icons/comp.blue.png diff --git a/docs/icons/comp.gray.png b/docs/icons/comp.gray.png Binary files differnew file mode 100644 index 0000000000..01538f8f31 --- /dev/null +++ b/docs/icons/comp.gray.png diff --git a/docs/icons/compressed.png b/docs/icons/compressed.png Binary files differnew file mode 100644 index 0000000000..de7276dbc0 --- /dev/null +++ b/docs/icons/compressed.png diff --git a/docs/icons/continued.png b/docs/icons/continued.png Binary files differnew file mode 100644 index 0000000000..8f656e27f2 --- /dev/null +++ b/docs/icons/continued.png diff --git a/docs/icons/dir.png b/docs/icons/dir.png Binary files differnew file mode 100644 index 0000000000..6b97905067 --- /dev/null +++ b/docs/icons/dir.png diff --git a/docs/icons/down.png b/docs/icons/down.png Binary files differnew file mode 100644 index 0000000000..be3904b045 --- /dev/null +++ b/docs/icons/down.png diff --git a/docs/icons/dvi.png b/docs/icons/dvi.png Binary files differnew file mode 100644 index 0000000000..19c417f227 --- /dev/null +++ b/docs/icons/dvi.png diff --git a/docs/icons/f.png b/docs/icons/f.png Binary files differnew file mode 100644 index 0000000000..c946f5b316 --- /dev/null +++ b/docs/icons/f.png diff --git a/docs/icons/folder.open.png b/docs/icons/folder.open.png Binary files differnew file mode 100644 index 0000000000..dd2d7e0cc2 --- /dev/null +++ b/docs/icons/folder.open.png diff --git a/docs/icons/folder.png b/docs/icons/folder.png Binary files differnew file mode 100644 index 0000000000..6b97905067 --- /dev/null +++ b/docs/icons/folder.png diff --git a/docs/icons/folder.sec.png b/docs/icons/folder.sec.png Binary files differnew file mode 100644 index 0000000000..833f59ac51 --- /dev/null +++ b/docs/icons/folder.sec.png diff --git a/docs/icons/forward.png b/docs/icons/forward.png Binary files differnew file mode 100644 index 0000000000..c5584a4c30 --- /dev/null +++ b/docs/icons/forward.png diff --git a/docs/icons/generic.png b/docs/icons/generic.png Binary files differnew file mode 100644 index 0000000000..0227cabb5c --- /dev/null +++ b/docs/icons/generic.png diff --git a/docs/icons/generic.red.png b/docs/icons/generic.red.png Binary files differnew file mode 100644 index 0000000000..be63249beb --- /dev/null +++ b/docs/icons/generic.red.png diff --git a/docs/icons/generic.sec.png b/docs/icons/generic.sec.png Binary files differnew file mode 100644 index 0000000000..0bd3d96bdc --- /dev/null +++ b/docs/icons/generic.sec.png diff --git a/docs/icons/hand.right.png b/docs/icons/hand.right.png Binary files differnew file mode 100644 index 0000000000..93035c658a --- /dev/null +++ b/docs/icons/hand.right.png diff --git a/docs/icons/hand.up.png b/docs/icons/hand.up.png Binary files differnew file mode 100644 index 0000000000..1405a6f156 --- /dev/null +++ b/docs/icons/hand.up.png diff --git a/docs/icons/icon.sheet.png b/docs/icons/icon.sheet.png Binary files differnew file mode 100644 index 0000000000..b875cb6b1c --- /dev/null +++ b/docs/icons/icon.sheet.png diff --git a/docs/icons/image1.png b/docs/icons/image1.png Binary files differnew file mode 100644 index 0000000000..c1374fde33 --- /dev/null +++ b/docs/icons/image1.png diff --git a/docs/icons/image2.png b/docs/icons/image2.png Binary files differnew file mode 100644 index 0000000000..606d4fb87e --- /dev/null +++ b/docs/icons/image2.png diff --git a/docs/icons/image3.png b/docs/icons/image3.png Binary files differnew file mode 100644 index 0000000000..701fb1e135 --- /dev/null +++ b/docs/icons/image3.png diff --git a/docs/icons/index.png b/docs/icons/index.png Binary files differnew file mode 100644 index 0000000000..9a0de35058 --- /dev/null +++ b/docs/icons/index.png diff --git a/docs/icons/layout.png b/docs/icons/layout.png Binary files differnew file mode 100644 index 0000000000..0a97c1c475 --- /dev/null +++ b/docs/icons/layout.png diff --git a/docs/icons/left.png b/docs/icons/left.png Binary files differnew file mode 100644 index 0000000000..d6e2404a81 --- /dev/null +++ b/docs/icons/left.png diff --git a/docs/icons/link.png b/docs/icons/link.png Binary files differnew file mode 100644 index 0000000000..4714d0ef40 --- /dev/null +++ b/docs/icons/link.png diff --git a/docs/icons/movie.png b/docs/icons/movie.png Binary files differnew file mode 100644 index 0000000000..5615180de8 --- /dev/null +++ b/docs/icons/movie.png diff --git a/docs/icons/p.png b/docs/icons/p.png Binary files differnew file mode 100644 index 0000000000..3fbe0e8801 --- /dev/null +++ b/docs/icons/p.png diff --git a/docs/icons/patch.png b/docs/icons/patch.png Binary files differnew file mode 100644 index 0000000000..808ed7865f --- /dev/null +++ b/docs/icons/patch.png diff --git a/docs/icons/pdf.png b/docs/icons/pdf.png Binary files differnew file mode 100644 index 0000000000..516142bb47 --- /dev/null +++ b/docs/icons/pdf.png diff --git a/docs/icons/pie0.png b/docs/icons/pie0.png Binary files differnew file mode 100644 index 0000000000..12e0200c97 --- /dev/null +++ b/docs/icons/pie0.png diff --git a/docs/icons/pie1.png b/docs/icons/pie1.png Binary files differnew file mode 100644 index 0000000000..c44c793ed8 --- /dev/null +++ b/docs/icons/pie1.png diff --git a/docs/icons/pie2.png b/docs/icons/pie2.png Binary files differnew file mode 100644 index 0000000000..e0b7167d91 --- /dev/null +++ b/docs/icons/pie2.png diff --git a/docs/icons/pie3.png b/docs/icons/pie3.png Binary files differnew file mode 100644 index 0000000000..820a3c35fa --- /dev/null +++ b/docs/icons/pie3.png diff --git a/docs/icons/pie4.png b/docs/icons/pie4.png Binary files differnew file mode 100644 index 0000000000..35490d857c --- /dev/null +++ b/docs/icons/pie4.png diff --git a/docs/icons/pie5.png b/docs/icons/pie5.png Binary files differnew file mode 100644 index 0000000000..359b7d377f --- /dev/null +++ b/docs/icons/pie5.png diff --git a/docs/icons/pie6.png b/docs/icons/pie6.png Binary files differnew file mode 100644 index 0000000000..4b293eae18 --- /dev/null +++ b/docs/icons/pie6.png diff --git a/docs/icons/pie7.png b/docs/icons/pie7.png Binary files differnew file mode 100644 index 0000000000..6bfa2d06ae --- /dev/null +++ b/docs/icons/pie7.png diff --git a/docs/icons/pie8.png b/docs/icons/pie8.png Binary files differnew file mode 100644 index 0000000000..716cf2822b --- /dev/null +++ b/docs/icons/pie8.png diff --git a/docs/icons/portal.png b/docs/icons/portal.png Binary files differnew file mode 100644 index 0000000000..937c0f87cd --- /dev/null +++ b/docs/icons/portal.png diff --git a/docs/icons/ps.png b/docs/icons/ps.png Binary files differnew file mode 100644 index 0000000000..ccccf730b6 --- /dev/null +++ b/docs/icons/ps.png diff --git a/docs/icons/quill.png b/docs/icons/quill.png Binary files differnew file mode 100644 index 0000000000..b697770a88 --- /dev/null +++ b/docs/icons/quill.png diff --git a/docs/icons/right.png b/docs/icons/right.png Binary files differnew file mode 100644 index 0000000000..41f8529a84 --- /dev/null +++ b/docs/icons/right.png diff --git a/docs/icons/screw1.png b/docs/icons/screw1.png Binary files differnew file mode 100644 index 0000000000..11673ab97d --- /dev/null +++ b/docs/icons/screw1.png diff --git a/docs/icons/screw2.png b/docs/icons/screw2.png Binary files differnew file mode 100644 index 0000000000..5d7d2cf65e --- /dev/null +++ b/docs/icons/screw2.png diff --git a/docs/icons/script.png b/docs/icons/script.png Binary files differnew file mode 100644 index 0000000000..2520570a77 --- /dev/null +++ b/docs/icons/script.png diff --git a/docs/icons/small/back.png b/docs/icons/small/back.png Binary files differnew file mode 100644 index 0000000000..2257df2140 --- /dev/null +++ b/docs/icons/small/back.png diff --git a/docs/icons/small/binary.png b/docs/icons/small/binary.png Binary files differnew file mode 100644 index 0000000000..2e2e1b073d --- /dev/null +++ b/docs/icons/small/binary.png diff --git a/docs/icons/small/binhex.png b/docs/icons/small/binhex.png Binary files differnew file mode 100644 index 0000000000..9deab419b6 --- /dev/null +++ b/docs/icons/small/binhex.png diff --git a/docs/icons/small/blank.png b/docs/icons/small/blank.png Binary files differnew file mode 100644 index 0000000000..86f57a504f --- /dev/null +++ b/docs/icons/small/blank.png diff --git a/docs/icons/small/broken.png b/docs/icons/small/broken.png Binary files differnew file mode 100644 index 0000000000..79c998c8c3 --- /dev/null +++ b/docs/icons/small/broken.png diff --git a/docs/icons/small/burst.png b/docs/icons/small/burst.png Binary files differnew file mode 100644 index 0000000000..2b21436c78 --- /dev/null +++ b/docs/icons/small/burst.png diff --git a/docs/icons/small/comp1.png b/docs/icons/small/comp1.png Binary files differnew file mode 100644 index 0000000000..6d8c3459ed --- /dev/null +++ b/docs/icons/small/comp1.png diff --git a/docs/icons/small/comp2.png b/docs/icons/small/comp2.png Binary files differnew file mode 100644 index 0000000000..57f7ad197b --- /dev/null +++ b/docs/icons/small/comp2.png diff --git a/docs/icons/small/compressed.png b/docs/icons/small/compressed.png Binary files differnew file mode 100644 index 0000000000..43acd8b943 --- /dev/null +++ b/docs/icons/small/compressed.png diff --git a/docs/icons/small/continued.png b/docs/icons/small/continued.png Binary files differnew file mode 100644 index 0000000000..db17c42465 --- /dev/null +++ b/docs/icons/small/continued.png diff --git a/docs/icons/small/dir.png b/docs/icons/small/dir.png Binary files differnew file mode 100644 index 0000000000..9bd6256bdb --- /dev/null +++ b/docs/icons/small/dir.png diff --git a/docs/icons/small/dir2.png b/docs/icons/small/dir2.png Binary files differnew file mode 100644 index 0000000000..836daf49ef --- /dev/null +++ b/docs/icons/small/dir2.png diff --git a/docs/icons/small/doc.png b/docs/icons/small/doc.png Binary files differnew file mode 100644 index 0000000000..c560df21d3 --- /dev/null +++ b/docs/icons/small/doc.png diff --git a/docs/icons/small/forward.png b/docs/icons/small/forward.png Binary files differnew file mode 100644 index 0000000000..4ddbc61e14 --- /dev/null +++ b/docs/icons/small/forward.png diff --git a/docs/icons/small/generic.png b/docs/icons/small/generic.png Binary files differnew file mode 100644 index 0000000000..16374a12bb --- /dev/null +++ b/docs/icons/small/generic.png diff --git a/docs/icons/small/generic2.png b/docs/icons/small/generic2.png Binary files differnew file mode 100644 index 0000000000..40d60c1df2 --- /dev/null +++ b/docs/icons/small/generic2.png diff --git a/docs/icons/small/generic3.png b/docs/icons/small/generic3.png Binary files differnew file mode 100644 index 0000000000..aa38963afa --- /dev/null +++ b/docs/icons/small/generic3.png diff --git a/docs/icons/small/image.png b/docs/icons/small/image.png Binary files differnew file mode 100644 index 0000000000..d92f0a5fcc --- /dev/null +++ b/docs/icons/small/image.png diff --git a/docs/icons/small/image2.png b/docs/icons/small/image2.png Binary files differnew file mode 100644 index 0000000000..4049bda561 --- /dev/null +++ b/docs/icons/small/image2.png diff --git a/docs/icons/small/index.png b/docs/icons/small/index.png Binary files differnew file mode 100644 index 0000000000..080453e215 --- /dev/null +++ b/docs/icons/small/index.png diff --git a/docs/icons/small/key.png b/docs/icons/small/key.png Binary files differnew file mode 100644 index 0000000000..1a45f67df3 --- /dev/null +++ b/docs/icons/small/key.png diff --git a/docs/icons/small/movie.png b/docs/icons/small/movie.png Binary files differnew file mode 100644 index 0000000000..7c126042c9 --- /dev/null +++ b/docs/icons/small/movie.png diff --git a/docs/icons/small/patch.png b/docs/icons/small/patch.png Binary files differnew file mode 100644 index 0000000000..c39f14435a --- /dev/null +++ b/docs/icons/small/patch.png diff --git a/docs/icons/small/ps.png b/docs/icons/small/ps.png Binary files differnew file mode 100644 index 0000000000..5c604230d0 --- /dev/null +++ b/docs/icons/small/ps.png diff --git a/docs/icons/small/rainbow.png b/docs/icons/small/rainbow.png Binary files differnew file mode 100644 index 0000000000..175053cb43 --- /dev/null +++ b/docs/icons/small/rainbow.png diff --git a/docs/icons/small/sound.png b/docs/icons/small/sound.png Binary files differnew file mode 100644 index 0000000000..6e3e95d3d0 --- /dev/null +++ b/docs/icons/small/sound.png diff --git a/docs/icons/small/sound2.png b/docs/icons/small/sound2.png Binary files differnew file mode 100644 index 0000000000..bc46eb48fe --- /dev/null +++ b/docs/icons/small/sound2.png diff --git a/docs/icons/small/tar.png b/docs/icons/small/tar.png Binary files differnew file mode 100644 index 0000000000..12f0347bf9 --- /dev/null +++ b/docs/icons/small/tar.png diff --git a/docs/icons/small/text.png b/docs/icons/small/text.png Binary files differnew file mode 100644 index 0000000000..b4e30f466d --- /dev/null +++ b/docs/icons/small/text.png diff --git a/docs/icons/small/transfer.png b/docs/icons/small/transfer.png Binary files differnew file mode 100644 index 0000000000..324048170a --- /dev/null +++ b/docs/icons/small/transfer.png diff --git a/docs/icons/small/unknown.png b/docs/icons/small/unknown.png Binary files differnew file mode 100644 index 0000000000..cad7e7a7aa --- /dev/null +++ b/docs/icons/small/unknown.png diff --git a/docs/icons/small/uu.png b/docs/icons/small/uu.png Binary files differnew file mode 100644 index 0000000000..ef87c82ee6 --- /dev/null +++ b/docs/icons/small/uu.png diff --git a/docs/icons/sound1.png b/docs/icons/sound1.png Binary files differnew file mode 100644 index 0000000000..7a766be6cc --- /dev/null +++ b/docs/icons/sound1.png diff --git a/docs/icons/sound2.png b/docs/icons/sound2.png Binary files differnew file mode 100644 index 0000000000..4511290939 --- /dev/null +++ b/docs/icons/sound2.png diff --git a/docs/icons/sphere1.png b/docs/icons/sphere1.png Binary files differnew file mode 100644 index 0000000000..2198ae89ec --- /dev/null +++ b/docs/icons/sphere1.png diff --git a/docs/icons/sphere2.png b/docs/icons/sphere2.png Binary files differnew file mode 100644 index 0000000000..257632ba46 --- /dev/null +++ b/docs/icons/sphere2.png diff --git a/docs/icons/tar.png b/docs/icons/tar.png Binary files differnew file mode 100644 index 0000000000..6c40521ff8 --- /dev/null +++ b/docs/icons/tar.png diff --git a/docs/icons/tex.png b/docs/icons/tex.png Binary files differnew file mode 100644 index 0000000000..906622d384 --- /dev/null +++ b/docs/icons/tex.png diff --git a/docs/icons/text.png b/docs/icons/text.png Binary files differnew file mode 100644 index 0000000000..34d0edf86e --- /dev/null +++ b/docs/icons/text.png diff --git a/docs/icons/transfer.png b/docs/icons/transfer.png Binary files differnew file mode 100644 index 0000000000..efaf17b682 --- /dev/null +++ b/docs/icons/transfer.png diff --git a/docs/icons/unknown.png b/docs/icons/unknown.png Binary files differnew file mode 100644 index 0000000000..7c241c383c --- /dev/null +++ b/docs/icons/unknown.png diff --git a/docs/icons/up.png b/docs/icons/up.png Binary files differnew file mode 100644 index 0000000000..a69ea00c5b --- /dev/null +++ b/docs/icons/up.png diff --git a/docs/icons/uu.png b/docs/icons/uu.png Binary files differnew file mode 100644 index 0000000000..b1d1a8579d --- /dev/null +++ b/docs/icons/uu.png diff --git a/docs/icons/uuencoded.png b/docs/icons/uuencoded.png Binary files differnew file mode 100644 index 0000000000..b1d1a8579d --- /dev/null +++ b/docs/icons/uuencoded.png diff --git a/docs/icons/world1.png b/docs/icons/world1.png Binary files differnew file mode 100644 index 0000000000..3a65c00d84 --- /dev/null +++ b/docs/icons/world1.png diff --git a/docs/icons/world2.png b/docs/icons/world2.png Binary files differnew file mode 100644 index 0000000000..9f8a3ea4b3 --- /dev/null +++ b/docs/icons/world2.png diff --git a/docs/manual/developer/documenting.html b/docs/manual/developer/documenting.html new file mode 100644 index 0000000000..8f8163f426 --- /dev/null +++ b/docs/manual/developer/documenting.html @@ -0,0 +1,64 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<html> +<head> +<title>Documenting Apache 2.0</title> +</head> +<!-- Background white, links blue (unvisited), navy (visited), red (active) --> +<BODY + BGCOLOR="#FFFFFF" + TEXT="#000000" + LINK="#0000FF" + VLINK="#000080" + ALINK="#FF0000" +> + +<!--#include virtual="header.html" --> + +<h1 align="center">Documentating Apache 2.0</h1> + +<p>Apache 2.0 uses DoxyGen to document the API's and global variables in the + the code. This will explain the basics of how to document using DoxyGen. + +<p>To start a documentation block, use /**<br /> + To end a documentation block, use */</p> + +<p>In the middle of the block, there are multiple tags we can use:</p> +<pre> + Description of this functions purpose + @param parameter_name description +</p> + +<p>The deffunc is not always necessary. DoxyGen does not have a full parser + in it, so any prototype that use a macro in the return type declaration + is too complex for scandoc. Those functions require a deffunc.</p> + +<p>An example (using &> rather than >):</p> +<pre> +/** + * return the final element of the pathname + * @param pathname The path to get the final element of + * @return the final element of the path + * @tip Examples: + * <pre> + * "/foo/bar/gum" -&> "gum" + * "/foo/bar/gum/" -&> "" + * "gum" -&> "gum" + * "wi\\n32\\stuff" -&> "stuff" + * </pre> + * @deffunc const char * ap_filename_of_pathname(const char *pathname) + */ +</pre> + +<p>At the top of the header file, always include:</p> +<pre> +/** + * @package Name of library header + */ +</pre> + +<p>ScanDoc uses a new html file for each package. The html files are named + {Name_of_library_header}.html, so try to be concise with your names.</p> + +<!--#include virtual="footer.html" --> +</body> +</html> diff --git a/docs/manual/developer/request.html b/docs/manual/developer/request.html new file mode 100644 index 0000000000..6becc938a4 --- /dev/null +++ b/docs/manual/developer/request.html @@ -0,0 +1,217 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<HTML> +<HEAD> +<TITLE>Request Processing in Apache 2.0</TITLE> +</HEAD> + +<!-- Background white, links blue (unvisited), navy (visited), red (active) --> +<BODY + BGCOLOR="#FFFFFF" + TEXT="#000000" + LINK="#0000FF" + VLINK="#000080" + ALINK="#FF0000" +> + +<!--#include virtual="header.html" --> + +<h1>Request Processing in Apache 2.0</h1> + +<p>Warning - this is a first (fast) draft that needs further revision!</p> + +<p>Several changes in Apache 2.0 affect the internal request processing + mechanics. Module authors need to be aware of these changes so they + may take advantage of the optimizations and security enhancements.</p> + +<p>The first major change is to the subrequest and redirect mechanisms. + There were a number of different code paths in Apache 1.3 to attempt + to optimize subrequest or redirect behavior. As patches were introduced + to 2.0, these optimizations (and the server behavior) were quickly broken + due to this duplication of code. All duplicate code has been folded + back into <code>ap_process_internal_request()</code> to prevent the + code from falling out of sync again.</p> + +<p>This means that much of the existing code was 'unoptimized'. It is + the Apache HTTP Project's first goal to create a robust and correct + implementation of the HTTP server RFC. Additional goals include + security, scalability and optimization. New methods were sought to + optimize the server (beyond the performance of Apache 1.3) without + introducing fragile or insecure code.</p> + +<h2>The Request Processing Cycle</h2> + +<p>All requests pass through <code>ap_process_request_internal()</code> + in request.c, including subrequests and redirects. If a module doesn't + pass generated requests through this code, the author is cautioned that + the module may be broken by future changes to request processing.</p> + +<p>To streamline requests, the module author can take advantage of the + hooks offered to drop out of the request cycle early, or to bypass + core Apache hooks which are irrelevant (and costly in terms of CPU.)</p> + +<h2>The Request Parsing Phase</h3> + +<h3>Unescapes the URL</h3> + +<p>The request's parsed_uri path is unescaped, once and only once, at the + beginning of internal request processing.</p> + +<p>This step is bypassed if the proxyreq flag is set, or the parsed_uri.path + element is unset. The module has no further control of this one-time + unescape operation, either failing to unescape or multiply unescaping + the URL leads to security reprecussions.</p> + +<h3>Strips Parent and This Elements from the URI</h3> + +<p>All <code>/../</code> and <code>/./</code> elements are removed by + <code>ap_getparents()</code>. This helps to ensure the path is (nearly) + absolute before the request processing continues.</p> + +<p>This step cannot be bypassed.</p> + +<h3>Initial URI Location Walk</h3> + +<p>Every request is subject to an <code>ap_location_walk()</code> call. + This ensures that <Location > sections are consistently enforced for + all requests. If the request is an internal redirect or a sub-request, + it may borrow some or all of the processing from the previous or parent + request's ap_location_walk, so this step is generally very efficient + after processing the main request.</p> + +<h3>Hook: translate_name</h3> + +<p>Modules can determine the file name, or alter the given URI in this step. + For example, mod_vhost_alias will translate the URI's path into the + configured virtual host, mod_alias will translate the path to an alias + path, and if the request falls back on the core, the DocumentRoot is + prepended to the request resource. + +<p>If all modules DECLINE this phase, an error 500 is returned to the browser, + and a "couldn't translate name" error is logged automatically.</p> + +<h3>Hook: map_to_storage</h3> + +<p>After the file or correct URI was determined, the appropriate per-dir + configurations are merged together. For example, mod_proxy compares + and merges the appropriate <Proxy > sections. If the URI is nothing + more than a local (non-proxy) TRACE request, the core handles the + request and returns DONE. If no module answers this hook with OK or + DONE, the core will run the request filename against the <Directory > + and <Files > sections. If the request 'filename' isn't an absolute, + legal filename, a note is set for later termination.</p> + +<h3>Initial URI Location Walk</h3> + +<p>Every request is hardened by a second <code>ap_location_walk()</code> + call. This reassures that a translated request is still subjected to + the configured <Location > sections. The request again borrows + some or all of the processing from it's previous location_walk above, + so this step is almost always very efficient unless the translated URI + mapped to a substantially different path or Virtual Host.</p> + +<h3>Hook: header_parser</h3> + +<p>The main request then parses the client's headers. This prepares +the remaining request processing steps to better serve the client's +request.</p> + +<h2>The Security Phase</h3> + +<p>Needs Documentation. Code is;</p> +<pre> + switch (ap_satisfies(r)) { + case SATISFY_ALL: + case SATISFY_NOSPEC: + if ((access_status = ap_run_access_checker(r)) != 0) { + return decl_die(access_status, "check access", r); + } + if (ap_some_auth_required(r)) { + if (((access_status = ap_run_check_user_id(r)) != 0) || !ap_auth_type(r)) { + return decl_die(access_status, ap_auth_type(r) + ? "check user. No user file?" + : "perform authentication. AuthType not set!", r); + } + if (((access_status = ap_run_auth_checker(r)) != 0) || !ap_auth_type(r)) { + return decl_die(access_status, ap_auth_type(r) + ? "check access. No groups file?" + : "perform authentication. AuthType not set!", r); + } + } + break; + case SATISFY_ANY: + if (((access_status = ap_run_access_checker(r)) != 0) || !ap_auth_type(r)) { + if (!ap_some_auth_required(r)) { + return decl_die(access_status, ap_auth_type(r) + ? "check access" + : "perform authentication. AuthType not set!", r); + } + if (((access_status = ap_run_check_user_id(r)) != 0) || !ap_auth_type(r)) { + return decl_die(access_status, ap_auth_type(r) + ? "check user. No user file?" + : "perform authentication. AuthType not set!", r); + } + if (((access_status = ap_run_auth_checker(r)) != 0) || !ap_auth_type(r)) { + return decl_die(access_status, ap_auth_type(r) + ? "check access. No groups file?" + : "perform authentication. AuthType not set!", r); + } + } + break; + } +</pre> +<h2>The Preparation Phase</h2> + +<h3>Hook: type_checker</h3> + +<p>The modules have an opportunity to test the URI or filename against + the target resource, and set mime information for the request. Both + mod_mime and mod_mime_magic use this phase to compare the file name + or contents against the administrator's configuration and set the + content type, language, character set and request handler. Some + modules may set up their filters or other request handling parameters + at this time.</p> + +<p>If all modules DECLINE this phase, an error 500 is returned to the browser, + and a "couldn't find types" error is logged automatically.</p> + +<h3>Hook: fixups</h3> + +<p>Many modules are 'trounced' by some phase above. The fixups phase is + used by modules to 'reassert' their ownership or force the request's + fields to their appropriate values. It isn't always the cleanest + mechanism, but occasionally it's the only option.</p> + +<h3>Hook: insert_filter</h3> + +<p>Modules that transform the content in some way can insert their values + and override existing filters, such that if the user configured a more + advanced filter out-of-order, then the module can move it's order as + need be. + +<h2>The Handler Phase</h2> + +<p>This phase is <strong><em>not</em></strong> part of the processing in + <code>ap_process_request_internal()</code>. Many modules prepare one + or more subrequests prior to creating any content at all. After the + core, or a module calls <code>ap_process_request_internal()</code> it + then calls <code>ap_invoke_handler()</code> to generate the request.</p> + +<h3>Hook: handler</h3> + +<p>The module finally has a chance to serve the request in it's handler + hook. Note that not every prepared request is sent to the handler + hook. Many modules, such as mod_autoindex, will create subrequests + for a given URI, and then never serve the subrequest, but simply + lists it for the user. Remember not to put required teardown from + the hooks above into this module, but register pool cleanups against + the request pool to free resources as required.</p> + +<!--#include virtual="footer.html" --> + +</body> +</html> + + + + + diff --git a/docs/manual/faq/index.html b/docs/manual/faq/index.html new file mode 100644 index 0000000000..90c19b7bb9 --- /dev/null +++ b/docs/manual/faq/index.html @@ -0,0 +1,67 @@ +<!--#if expr="$QUERY_STRING = ONEPAGE" --> + <!--#set var="ONEPAGE" value="YES" --> +<!--#else --> + <!--#set var="ONEPAGE" value="" --> +<!--#endif --> +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<HTML> + <HEAD> + <TITLE>Apache Server Frequently Asked Questions</TITLE> + </HEAD> + <BODY + BGCOLOR="#FFFFFF" + TEXT="#000000" + LINK="#0000FF" + VLINK="#000080" + ALINK="#FF0000" + > + <!--#include virtual="header.html" --> + <H1 ALIGN="CENTER">Frequently Asked Questions</H1> + + <P> + The latest version of this FAQ is always available from the main + Apache web site, at <<A + HREF="http://httpd.apache.org/docs-2.0/faq/" REL="Help" + ><SAMP>http://httpd.apache.org/docs-2.0/faq/</SAMP></A>>. + <!--#if expr="!$ONEPAGE" --> In addition, you can view this FAQ <a + href="./?ONEPAGE">all in one page</a> for easy searching and + printing.<!--#endif --> + </P> + + <p>Since Apache 2.0 is very new, we don't yet know what the + <em>Frequently Asked Questions</em> will be. While this section + fills up, you should also consult the <a + href="http://httpd.apache.org/docs/misc/FAQ.html">Apache 1.3 FAQ</a> + to see if your question is answered there.</p> + + + + <H2>Categories</H2> + +<!--#if expr="!$ONEPAGE" --> + +<dl> + +<dt><a href="support.html">Support</a></dt> +<dd>What do I do when I have problems?</dd> + +</dl> + +<!--#else --> + +<dl> + +<dt><a href="#support.html">Support</a></dt> +<dd>What do I do when I have problems?</dd> + +</dl> + +<hr> + +<!--#include virtual="support.html" --> + +<!--#endif --> + +<!--#include virtual="footer.html" --> +</BODY> +</HTML> diff --git a/docs/manual/faq/support.html b/docs/manual/faq/support.html new file mode 100644 index 0000000000..95aa420132 --- /dev/null +++ b/docs/manual/faq/support.html @@ -0,0 +1,143 @@ +<!--#if expr="!$ONEPAGE" --> +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<HTML> + <HEAD> + <TITLE>Support - Apache Server Frequently Asked Questions</TITLE> + </HEAD> +<!-- Background white, links blue (unvisited), navy (visited), red (active) --> + <BODY + BGCOLOR="#FFFFFF" + TEXT="#000000" + LINK="#0000FF" + VLINK="#000080" + ALINK="#FF0000" + > + <!--#include virtual="header.html" --> + <H1 ALIGN="CENTER">Apache Server Frequently Asked Questions</H1> +<!--#endif --> + + <H2><a name="support.html">Support</a></H2> + +<UL> + <LI><A HREF="#what2do">"Why can't I ...? Why won't ... + work?" What to do in case of problems</A> + </LI> + <LI><A HREF="#support">Whom do I contact for support?</A> + </LI> + +</UL> + +<HR> + + <H3><A NAME="what2do">"Why can't I ...? Why won't ... work?" + What to do in case of problems</A></H3> + <P> + If you are having trouble with your Apache server software, you should + take the following steps: + </P> + <OL> + <LI><STRONG>Check the errorlog!</STRONG> + <P> + Apache tries to be helpful when it encounters a problem. In many + cases, it will provide some details by writing one or messages to + the server error log. Sometimes this is enough for you to diagnose + & fix the problem yourself (such as file permissions or the like). + The default location of the error log is + <SAMP>/usr/local/apache/logs/error_log</SAMP>, but see the + <A HREF="../mod/core.html#errorlog"><SAMP>ErrorLog</SAMP></A> + directive in your config files for the location on your server. + </P> + </LI> + <LI><STRONG>Check the + <A HREF="http://httpd.apache.org/docs/misc/FAQ.html">FAQ</A>!</STRONG> + <P> + The latest version of the Apache Frequently-Asked Questions list can + always be found at the main Apache web site. + </P> + </LI> + <LI><STRONG>Check the Apache bug database</STRONG> + <P> + Most problems that get reported to The Apache Group are recorded in + the + <A HREF="http://bugs.apache.org/">bug database</A>. + <EM><STRONG>Please</STRONG> check the existing reports, open + <STRONG>and</STRONG> closed, before adding one.</EM> If you find + that your issue has already been reported, please <EM>don't</EM> add + a "me, too" report. If the original report isn't closed + yet, we suggest that you check it periodically. You might also + consider contacting the original submitter, because there may be an + email exchange going on about the issue that isn't getting recorded + in the database. + </P> + </LI> + <LI><STRONG>Ask in the <SAMP>comp.infosystems.www.servers.unix</SAMP> + or <SAMP>comp.infosystems.www.servers.ms-windows</SAMP> USENET + newsgroup (as appropriate for the platform you use).</STRONG> + <P> + A lot of common problems never make it to the bug database because + there's already high Q&A traffic about them in the + <A HREF="news:comp.infosystems.www.servers.unix" + ><SAMP>comp.infosystems.www.servers.unix</SAMP></A> + newsgroup. Many Apache users, and some of the developers, can be + found roaming its virtual halls, so it is suggested that you seek + wisdom there. The chances are good that you'll get a faster answer + there than from the bug database, even if you <EM>don't</EM> see + your question already posted. + </P> + </LI> + <LI><STRONG>If all else fails, report the problem in the bug + database</STRONG> + <P> + If you've gone through those steps above that are appropriate and + have obtained no relief, then please <EM>do</EM> let The Apache + Group know about the problem by + <A HREF="http://httpd.apache.org/bug_report.html">logging a bug report</A>. + </P> + <P> + If your problem involves the server crashing and generating a core + dump, please include a backtrace (if possible). As an example, + </P> + <P> + <DL> + <DD><CODE># cd <EM>ServerRoot</EM><BR> + # dbx httpd core<BR> + (dbx) where</CODE> + </DD> + </DL> + <P></P> + <P> + (Substitute the appropriate locations for your + <SAMP>ServerRoot</SAMP> and your <SAMP>httpd</SAMP> and + <SAMP>core</SAMP> files. You may have to use <CODE>gdb</CODE> + instead of <CODE>dbx</CODE>.) + </P> + </LI> + </OL> + <HR> + + <H3><A NAME="support">Whom do I contact for support?</A></H3> + <P> + There is no official support for Apache. None of the developers want to + be swamped by a flood of trivial questions that can be resolved elsewhere. + Bug reports and suggestions should be sent <EM>via</EM> + <A HREF="http://www.apache.org/bug_report.html">the bug report page</A>. + Other questions should be directed to the + <A HREF="news:comp.infosystems.www.servers.unix" + >comp.infosystems.www.servers.unix</A> or <A HREF= + "news:comp.infosystems.www.servers.ms-windows" + >comp.infosystems.www.servers.ms-windows</A> + newsgroup (as appropriate for the platform you use), where some of the + Apache team lurk, in the company of many other httpd gurus who + should be able to help. + </P> + <P> + Commercial support for Apache is, however, available from a number + of third parties. + </P> + +<!--#if expr="!$ONEPAGE" --> + +<!--#include virtual="footer.html" --> +</BODY> +</HTML> +<!--#endif --> diff --git a/docs/manual/howto/auth.html b/docs/manual/howto/auth.html new file mode 100644 index 0000000000..9311ffcf81 --- /dev/null +++ b/docs/manual/howto/auth.html @@ -0,0 +1,318 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> + +<html> + <head> + <meta name="generator" content="HTML Tidy, see www.w3.org"> + + <title>Authentication</title> + <link rev="made" href="mailto:rbowen@rcbowen.com"> + </head> + <!-- Background white, links blue (unvisited), navy (visited), red (active) --> + + <body bgcolor="#FFFFFF" text="#000000" link="#0000FF" vlink= + "#000080" alink="#FF0000"> + <!--#include virtual="header.html" --> + + <h1 align="CENTER">Authentication</h1> + <a name="__index__"></a> <!-- INDEX BEGIN --> + + + <ul> + <li><a href="#introduction">Introduction</a></li> + + <li><a href="#the prerequisites">The prerequisites</a></li> + + <li><a href="#getting it working.">Getting it + working.</a></li> + + <li><a href="#letting more than one person in">Letting more + than one person in</a></li> + + <li><a href="#possible problems">Possible problems</a></li> + + <li><a href="#what other neat stuff can i do">What other neat + stuff can I do?</a></li> + + <li><a href="#more information">More information</a></li> + </ul> + <!-- INDEX END --> + <hr> + +<table border="1"> +<tr> +<td valign="top"><strong>Related Modules</strong><br> +<br> + <a href="../mod/mod_auth.html">mod_auth</a><br> + </td> +<td valign="top"><strong>Related Directives</strong><br> +<br> + <a href="../mod/mod_access.html#allow">Allow</a><br> + <a href="../mod/mod_auth.html#authgroupfile">AuthGroupFile</a><br> + <a href="../mod/core.html#authname">AuthName</a><br> + <a href="../mod/core.html#authtype">AuthType</a><br> + <a href="../mod/mod_auth.html#authuserfile">AuthUserFile</a><br> + <a href="../mod/mod_access.html#deny">Deny</a><br> + <a href="../mod/core.html#options">Options</a><br> + <a href="../mod/core.html#require">Require</a><br> + + </td> +</tr> +</table> + + + <h1><a name="authentication">Authentication</a></h1> + + <p>Authentication is any process by which you verify that + someone is who they claim they are. Authorization is any + process by which someone is allowed to be where they want to + go, or to have information that they want to have.</p> + + <h2><a name="introduction">Introduction</a></h2> + + <p>If you have information on your web site that is sensitive + or intended for only a small group of people, the techniques in + this article will help you make sure that the people that see + those pages are the people that you wanted to see them.</p> + + <p>This article covers the "standard" way of protecting parts of your + web site that most of you are going to use.</p> + + <h2><a name="the prerequisites">The prerequisites</a></h2> + + <p>The directives discussed in this article will need to go either + in your main server configuration file, or in per-directory + configuration files (<code>.htaccess</code> files).</p> + + <p>If you plan to use <code>.htaccess</code> files, you will need to + have a server configuration that permits putting authentication + directives in these files. This is done with the + <code><a href="../mod/core.html#allowoverride">AllowOverride</a></code> + directive, which specifies which directives, if any, may be put in + per-directory configuration files.</p> + + <p>Since we're talking here about authentication, you will need an + <code>AllowOverride</code> directive like the following:</p> + +<pre> + AllowOverride AuthConfig +</pre> + + <p>Or, if you are just going to put the directives directly in your + main server configuration file, you will of course need to have + write permission to that file.</p> + + <p>And you'll need to know a little bit about the directory + structure of your server, in order to know where some files are + kept. This should not be terribly difficult, and I'll try to + make this clear when we come to that point.</p> + + <h2><a name="getting it working.">Getting it working.</a></h2> + + <p>Here's the basics of password protecting a directory on your + server.</p> + + <p>You'll need to create a password file. This file should be + placed somewhere outside of your document directory. This is so + that folks cannot download the password file. For example, if + your documents are served out of + <code>/usr/local/apache/htdocs</code> you might want to put the + password file(s) in <code>/usr/local/apache/passwd</code>.</p> + + <p>To create the file, use the <code>htpasswd</code> utility + that came with Apache. This be located in the <code>bin</code> + directory of wherever you installed Apache. To create the file, + type:</p> +<pre> + htpasswd -c /usr/local/apache/passwd/password rbowen +</pre> + + <p><code>htpasswd</code> will ask you for the password, and + then ask you to type it again to confirm it:</p> +<pre> + # htpasswd -c /usr/local/apache/passwd/passwords rbowen + New password: mypassword + Re-type new password: mypassword + Adding password for user rbowen +</pre> + + <p>If <code>htpasswd</code> is not in your path, of course + you'll have to type the full path to the file to get it to run. + On my server, it's located at + <code>/usr/local/apache/bin/htpasswd</code></p> + + <p>Next, you'll need to create a file in the directory you want + to protect. This file is usually called <code>.htaccess</code>, + although on Windows it's called <code>htaccess</code> (without + the leading period.) <code>.htaccess</code> needs to contain + the following lines:</p> +<pre> + AuthType Basic + AuthName "By Invitation Only" + AuthUserFile /usr/local/apache/passwd/passwords + AuthGroupFile /dev/null + require user rbowen +</pre> + + <p>The next time that you load a file from that directory, you + should see the familiar username/password dialog box pop up. If + you don't chances are pretty good that you are not permitted to + use <code>.htaccess</code> files in the directory in + question.</p> + + <h2><a name="letting more than one person in">Letting more than + one person in</a></h2> + + <p>The directives above only let one person (specifically + someone with a username of <code>rbowen</code>) into the + directory. In most cases, you'll want to let more than one + person in. This is where the <code>AuthGroupFile</code> comes + in. In the example above, we've pointed + <code>AuthGroupFile</code> to <code>/dev/null</code>, which is + Unix-speak for "nowhere", or "off into space." (The Windows + NT equivalent of this is <code>nul</code>.)</p> + + <p>If you want to let more than one person in, you'll need to + create a group file that associates group names with a list of + users in that group. The format of this file is pretty simple, + and you can create it with your favorite editor. The contents + of the file will look like this:</p> +<pre> + GroupName: rbowen dpitts sungo rshersey +</pre> + + <p>That's just a list of the members of the group in a long + line separated by spaces.</p> + + <p>To add a user to your already existing password file, + type:</p> +<pre> + htpasswd /usr/local/apache/passwd/password dpitts +</pre> + + <p>You'll get the same response as before, but it will be + appended to the existing file, rather than creating a new file. + (It's the <code>-c</code> that makes it create a new password + file.</p> + + <p>Now, you need to modify your <code>.htaccess</code> file to + look like the following:</p> +<pre> + AuthType Basic + AuthName "By Invitation Only" + AuthUserFile /usr/local/apache/passwd/passwords + AuthGroupFile /usr/local/apache/passwd/groups + require group GroupName +</pre> + + <p>Now, anyone that is listed in the group + <code>GroupName</code>, and has an entry in the + <code>password</code> file, will be let in, if they type the + correct password.</p> + + <p>There's another way to let multiple users in that is less + specific. Rather than creating a group file, you can just use + the following directive:</p> +<pre> + require valid-user +</pre> + + <p>Using that rather than the <code>require user rbowen</code> + line will allow anyone in that is listed in the password file, + and who correctly enters their password. You can even emulate + the group behavior here, by just keeping a separate password + file for each group. The advantage of this approach is that + Apache only has to check one file, rather than two. The + disadvantage is that you have to maintain a bunch of password + files, and remember to reference th right one in the + <code>AuthUserFile</code> directive.</p> + + <h2><a name="possible problems">Possible problems</a></h2> + + <p>Because of the way that Basic authentication is specified, + your username and password must be verified every time you + request a document from the server. This is even if you're + reloading the same page, and for every image on the page (if + they come from a protected directory). As you can imagine, this + slows things down a little. The amount that it slows things + down is proportional to the size of the password file, because + it has to open up that file, and go down the list of users + until it gets to your name. And it has to do this every time a + page is loaded.</p> + + <p>A consequence of this is that there's a practical limit to how many + users you can put in one password file. This limit will vary + depending on the performance of your particular server machine, but + you can expect to see slowdowns once you get above a few hundred + entries, and may wish to consider a different authentication method + at that time.</p> + + <h2><a name="what other neat stuff can i do">What other neat + stuff can I do?</a></h2> + + <p>Authentication by username and password is only part of the + story. Frequently you want to let people in based on something + other than who they are. Something such as where they are + coming from.</p> + + <p>The <code>allow</code> and <code>deny</code> directives let + you allow and deny access based on the host name, or host + address, of the machine requesting a document. The directive + goes hand-in-hand with these is the <code>order</code> + directive, which tells Apache in which order to apply the + filters.</p> + + <p>The usage of these directives is:</p> +<pre> + allow from address +</pre> + + <p>where <em>address</em> is an IP address (or a partial IP + address) or a fully qualified domain name (or a partial domain + name).</p> + + <p>For example, if you have someone spamming your message + board, and you want to keep them out, you could do the + following:</p> +<pre> + deny from 205.252.46.165 +</pre> + + <p>Visitors coming from that address will not be able to see + the content behind this directive. If, instead, you have a + machine name, rather than an IP address, you can use that.</p> +<pre> + deny from host.example.com +</pre> + + <p>And, if you'd like to block access from an entire domain, + you can specify just part of an address or domain name:</p> +<pre> + deny from 192.101.205 + deny from cyberthugs.com + deny from ke +</pre> + + <p>Using <code>order</code> will let you be sure that you are + actually restricting things to the group that you want to let + in, by combining a <code>deny</code> and an <code>allow</code> + directive:</p> +<pre> + order deny,allow + deny from all + allow from dev.example.com +</pre> + + <p>Listing just the <code>allow</code> directive would not do + what you want, because it will let folks from that host in, in + addition to letting everyone in. What you want is to let + <em>only</em> those folks in.</p> + + <h2><a name="more information">More information</a></h2> + + <p>You should also read the documentation for + <code><a href="../mod/mod_auth.html">mod_auth</a></code> + which contains + some more information about how this all works.</p> + </body> +</html> + diff --git a/docs/manual/howto/auth.html.en b/docs/manual/howto/auth.html.en new file mode 100644 index 0000000000..9311ffcf81 --- /dev/null +++ b/docs/manual/howto/auth.html.en @@ -0,0 +1,318 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> + +<html> + <head> + <meta name="generator" content="HTML Tidy, see www.w3.org"> + + <title>Authentication</title> + <link rev="made" href="mailto:rbowen@rcbowen.com"> + </head> + <!-- Background white, links blue (unvisited), navy (visited), red (active) --> + + <body bgcolor="#FFFFFF" text="#000000" link="#0000FF" vlink= + "#000080" alink="#FF0000"> + <!--#include virtual="header.html" --> + + <h1 align="CENTER">Authentication</h1> + <a name="__index__"></a> <!-- INDEX BEGIN --> + + + <ul> + <li><a href="#introduction">Introduction</a></li> + + <li><a href="#the prerequisites">The prerequisites</a></li> + + <li><a href="#getting it working.">Getting it + working.</a></li> + + <li><a href="#letting more than one person in">Letting more + than one person in</a></li> + + <li><a href="#possible problems">Possible problems</a></li> + + <li><a href="#what other neat stuff can i do">What other neat + stuff can I do?</a></li> + + <li><a href="#more information">More information</a></li> + </ul> + <!-- INDEX END --> + <hr> + +<table border="1"> +<tr> +<td valign="top"><strong>Related Modules</strong><br> +<br> + <a href="../mod/mod_auth.html">mod_auth</a><br> + </td> +<td valign="top"><strong>Related Directives</strong><br> +<br> + <a href="../mod/mod_access.html#allow">Allow</a><br> + <a href="../mod/mod_auth.html#authgroupfile">AuthGroupFile</a><br> + <a href="../mod/core.html#authname">AuthName</a><br> + <a href="../mod/core.html#authtype">AuthType</a><br> + <a href="../mod/mod_auth.html#authuserfile">AuthUserFile</a><br> + <a href="../mod/mod_access.html#deny">Deny</a><br> + <a href="../mod/core.html#options">Options</a><br> + <a href="../mod/core.html#require">Require</a><br> + + </td> +</tr> +</table> + + + <h1><a name="authentication">Authentication</a></h1> + + <p>Authentication is any process by which you verify that + someone is who they claim they are. Authorization is any + process by which someone is allowed to be where they want to + go, or to have information that they want to have.</p> + + <h2><a name="introduction">Introduction</a></h2> + + <p>If you have information on your web site that is sensitive + or intended for only a small group of people, the techniques in + this article will help you make sure that the people that see + those pages are the people that you wanted to see them.</p> + + <p>This article covers the "standard" way of protecting parts of your + web site that most of you are going to use.</p> + + <h2><a name="the prerequisites">The prerequisites</a></h2> + + <p>The directives discussed in this article will need to go either + in your main server configuration file, or in per-directory + configuration files (<code>.htaccess</code> files).</p> + + <p>If you plan to use <code>.htaccess</code> files, you will need to + have a server configuration that permits putting authentication + directives in these files. This is done with the + <code><a href="../mod/core.html#allowoverride">AllowOverride</a></code> + directive, which specifies which directives, if any, may be put in + per-directory configuration files.</p> + + <p>Since we're talking here about authentication, you will need an + <code>AllowOverride</code> directive like the following:</p> + +<pre> + AllowOverride AuthConfig +</pre> + + <p>Or, if you are just going to put the directives directly in your + main server configuration file, you will of course need to have + write permission to that file.</p> + + <p>And you'll need to know a little bit about the directory + structure of your server, in order to know where some files are + kept. This should not be terribly difficult, and I'll try to + make this clear when we come to that point.</p> + + <h2><a name="getting it working.">Getting it working.</a></h2> + + <p>Here's the basics of password protecting a directory on your + server.</p> + + <p>You'll need to create a password file. This file should be + placed somewhere outside of your document directory. This is so + that folks cannot download the password file. For example, if + your documents are served out of + <code>/usr/local/apache/htdocs</code> you might want to put the + password file(s) in <code>/usr/local/apache/passwd</code>.</p> + + <p>To create the file, use the <code>htpasswd</code> utility + that came with Apache. This be located in the <code>bin</code> + directory of wherever you installed Apache. To create the file, + type:</p> +<pre> + htpasswd -c /usr/local/apache/passwd/password rbowen +</pre> + + <p><code>htpasswd</code> will ask you for the password, and + then ask you to type it again to confirm it:</p> +<pre> + # htpasswd -c /usr/local/apache/passwd/passwords rbowen + New password: mypassword + Re-type new password: mypassword + Adding password for user rbowen +</pre> + + <p>If <code>htpasswd</code> is not in your path, of course + you'll have to type the full path to the file to get it to run. + On my server, it's located at + <code>/usr/local/apache/bin/htpasswd</code></p> + + <p>Next, you'll need to create a file in the directory you want + to protect. This file is usually called <code>.htaccess</code>, + although on Windows it's called <code>htaccess</code> (without + the leading period.) <code>.htaccess</code> needs to contain + the following lines:</p> +<pre> + AuthType Basic + AuthName "By Invitation Only" + AuthUserFile /usr/local/apache/passwd/passwords + AuthGroupFile /dev/null + require user rbowen +</pre> + + <p>The next time that you load a file from that directory, you + should see the familiar username/password dialog box pop up. If + you don't chances are pretty good that you are not permitted to + use <code>.htaccess</code> files in the directory in + question.</p> + + <h2><a name="letting more than one person in">Letting more than + one person in</a></h2> + + <p>The directives above only let one person (specifically + someone with a username of <code>rbowen</code>) into the + directory. In most cases, you'll want to let more than one + person in. This is where the <code>AuthGroupFile</code> comes + in. In the example above, we've pointed + <code>AuthGroupFile</code> to <code>/dev/null</code>, which is + Unix-speak for "nowhere", or "off into space." (The Windows + NT equivalent of this is <code>nul</code>.)</p> + + <p>If you want to let more than one person in, you'll need to + create a group file that associates group names with a list of + users in that group. The format of this file is pretty simple, + and you can create it with your favorite editor. The contents + of the file will look like this:</p> +<pre> + GroupName: rbowen dpitts sungo rshersey +</pre> + + <p>That's just a list of the members of the group in a long + line separated by spaces.</p> + + <p>To add a user to your already existing password file, + type:</p> +<pre> + htpasswd /usr/local/apache/passwd/password dpitts +</pre> + + <p>You'll get the same response as before, but it will be + appended to the existing file, rather than creating a new file. + (It's the <code>-c</code> that makes it create a new password + file.</p> + + <p>Now, you need to modify your <code>.htaccess</code> file to + look like the following:</p> +<pre> + AuthType Basic + AuthName "By Invitation Only" + AuthUserFile /usr/local/apache/passwd/passwords + AuthGroupFile /usr/local/apache/passwd/groups + require group GroupName +</pre> + + <p>Now, anyone that is listed in the group + <code>GroupName</code>, and has an entry in the + <code>password</code> file, will be let in, if they type the + correct password.</p> + + <p>There's another way to let multiple users in that is less + specific. Rather than creating a group file, you can just use + the following directive:</p> +<pre> + require valid-user +</pre> + + <p>Using that rather than the <code>require user rbowen</code> + line will allow anyone in that is listed in the password file, + and who correctly enters their password. You can even emulate + the group behavior here, by just keeping a separate password + file for each group. The advantage of this approach is that + Apache only has to check one file, rather than two. The + disadvantage is that you have to maintain a bunch of password + files, and remember to reference th right one in the + <code>AuthUserFile</code> directive.</p> + + <h2><a name="possible problems">Possible problems</a></h2> + + <p>Because of the way that Basic authentication is specified, + your username and password must be verified every time you + request a document from the server. This is even if you're + reloading the same page, and for every image on the page (if + they come from a protected directory). As you can imagine, this + slows things down a little. The amount that it slows things + down is proportional to the size of the password file, because + it has to open up that file, and go down the list of users + until it gets to your name. And it has to do this every time a + page is loaded.</p> + + <p>A consequence of this is that there's a practical limit to how many + users you can put in one password file. This limit will vary + depending on the performance of your particular server machine, but + you can expect to see slowdowns once you get above a few hundred + entries, and may wish to consider a different authentication method + at that time.</p> + + <h2><a name="what other neat stuff can i do">What other neat + stuff can I do?</a></h2> + + <p>Authentication by username and password is only part of the + story. Frequently you want to let people in based on something + other than who they are. Something such as where they are + coming from.</p> + + <p>The <code>allow</code> and <code>deny</code> directives let + you allow and deny access based on the host name, or host + address, of the machine requesting a document. The directive + goes hand-in-hand with these is the <code>order</code> + directive, which tells Apache in which order to apply the + filters.</p> + + <p>The usage of these directives is:</p> +<pre> + allow from address +</pre> + + <p>where <em>address</em> is an IP address (or a partial IP + address) or a fully qualified domain name (or a partial domain + name).</p> + + <p>For example, if you have someone spamming your message + board, and you want to keep them out, you could do the + following:</p> +<pre> + deny from 205.252.46.165 +</pre> + + <p>Visitors coming from that address will not be able to see + the content behind this directive. If, instead, you have a + machine name, rather than an IP address, you can use that.</p> +<pre> + deny from host.example.com +</pre> + + <p>And, if you'd like to block access from an entire domain, + you can specify just part of an address or domain name:</p> +<pre> + deny from 192.101.205 + deny from cyberthugs.com + deny from ke +</pre> + + <p>Using <code>order</code> will let you be sure that you are + actually restricting things to the group that you want to let + in, by combining a <code>deny</code> and an <code>allow</code> + directive:</p> +<pre> + order deny,allow + deny from all + allow from dev.example.com +</pre> + + <p>Listing just the <code>allow</code> directive would not do + what you want, because it will let folks from that host in, in + addition to letting everyone in. What you want is to let + <em>only</em> those folks in.</p> + + <h2><a name="more information">More information</a></h2> + + <p>You should also read the documentation for + <code><a href="../mod/mod_auth.html">mod_auth</a></code> + which contains + some more information about how this all works.</p> + </body> +</html> + diff --git a/docs/manual/images/custom_errordocs.png b/docs/manual/images/custom_errordocs.png Binary files differnew file mode 100644 index 0000000000..11553cbdee --- /dev/null +++ b/docs/manual/images/custom_errordocs.png diff --git a/docs/manual/images/mod_rewrite_fig1.png b/docs/manual/images/mod_rewrite_fig1.png Binary files differnew file mode 100644 index 0000000000..f3a68d9e4c --- /dev/null +++ b/docs/manual/images/mod_rewrite_fig1.png diff --git a/docs/manual/images/mod_rewrite_fig2.png b/docs/manual/images/mod_rewrite_fig2.png Binary files differnew file mode 100644 index 0000000000..bc141be769 --- /dev/null +++ b/docs/manual/images/mod_rewrite_fig2.png diff --git a/docs/manual/logs.html b/docs/manual/logs.html new file mode 100644 index 0000000000..d3ebf480c5 --- /dev/null +++ b/docs/manual/logs.html @@ -0,0 +1,580 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<HTML> +<HEAD> +<TITLE>Log Files - Apache HTTP Server</TITLE> +</HEAD> + +<!-- Background white, links blue (unvisited), navy (visited), red (active) --> +<BODY + BGCOLOR="#FFFFFF" + TEXT="#000000" + LINK="#0000FF" + VLINK="#000080" + ALINK="#FF0000" +> +<!--#include virtual="header.html" --> +<h1 align="center">Log Files</h1> + +<p>In order to effectively manage a web server, it is necessary to get +feedback about the activity and performance of the server as well as +any problems that may be occuring. The Apache HTTP Server provides +very comprehensive and flexible logging capabilities. This document +describes how to configure the various log files, and how to +understand what the logs contain.</p> + +<ul> +<li><a href="#security">Security Warning</a></li> +<li><a href="#errorlog">Error Log</a></li> +<li><a href="#accesslog">Access Log</a> + <ul> + <li><a href="#common">Common Log Format</a></li> + <li><a href="#combined">Combined Log Format</a></li> + <li><a href="#multiple">Multiple Access Logs</a></li> + <li><a href="#conditional">Conditional Logging</a></li> + </ul></li> +<li><a href="#rotation">Log Rotation</a></li> +<li><a href="#piped">Piped Logs</a></li> +<li><a href="#virtualhosts">VirtualHosts</a> +<li><a href="#other">Other Log Files</a> + <ul> + <li><a href="#pidfile">PID File</a></li> + <li><a href="#scriptlog">Script Log</a></li> + <li><a href="#rewritelog">Rewrite Log</a></li> + </ul></li> +</ul> + +<hr> + +<h2><a name="security">Security Warning</a></h2> + +<p>Anyone who can write to the directory where Apache is writing a +log file can almost certainly gain access to the uid that the server is +started as, which is normally root. Do <EM>NOT</EM> give people write +access to the directory the logs are stored in without being aware of +the consequences; see the <A HREF="misc/security_tips.html">security tips</A> +document for details.</p> + +<p>In addition, log files may contain information supplied directly +by the client, without escaping. Therefore, it is possible for +malicious clients to insert control-characters in the log files, so +care must be taken in dealing with raw logs.</p> + +<hr> + +<h2><a name="errorlog">Error Log</a></h2> + +<table border="1"> +<tr><td valign="top"> +<strong>Related Directives</strong><br><br> + +<a href="mod/core.html#errorlog">ErrorLog</a><br> +<a href="mod/core.html#loglevel">LogLevel</a> +</td></tr></table> + +<p>The server error log, whose name and location is set by the <a +href="mod/core.html#errorlog">ErrorLog</a> directive, is the most +important log file. This is the place where Apache HTTPD will send +diagnostic information and record any errors that it encounters in +processing requests. It is the first place to look when a problem +occurs with starting the server or with the operation of the server, +since it will often contain details of what went wrong and how to fix +it.</p> + +<p>The error log is usually written to a file (typically +<code>error_log</code> on unix systems and <code>error.log</code> on +Windows and OS/2). On unix systems it is also possible to have the +server send errors to the <code>syslog</code> or <a href="#pipe">pipe +them through a program</a>.</p> + +<p>The format of the error log is relatively free-form and +descriptive. But there is certain information that is contained +in most error log entries. For example, here is a typical message.</p> + +<blockquote><code> +[Wed Oct 11 14:32:52 2000] [error] [client 127.0.0.1] client denied by server configuration: /export/home/live/ap/htdocs/test +</code></blockquote> + +<p>The first item in the log entry is the date and time of the +message. The second entry lists the severity of the error being +reported. The <a href="mod/core.html#loglevel">LogLevel</a> directive +is used to control the types of errors that are sent to the error log +by restricting the severity level. The third entry gives the IP +address of the client that generated the error. Beyond that is the +message itself, which in this case indicates that the server has been +configured to deny the client access. The server reports the +file-system path (as opposed to the web path) of the requested +document.</p> + +<p>A very wide variety of different messages can appear in the error +log. Most look similar to the example above. The error log will also +contain debugging output from CGI scripts. Any information written to +<code>stderr</code> by a CGI script will be copied directly to the +error log.</p> + +<p>It is not possible to customize the error log by adding or removing +information. However, error log entries dealing with particular +requests have corresponding entries in the <a href="accesslog">access +log</a>. For example, the above example entry corresponds to an +access log entry with status code 403. Since it is possible to +customize the access log, you can obtain more information about error +conditions using that log file.</p> + +<p>During testing, it is often useful to continuously monitor the +error log for any problems. On unix systems, you can accomplish this +using:</p> +<blockquote><code> +tail -f error_log +</code></blockquote> + +<hr> + +<h2><a name="accesslog">Access Log</a></h2> + +<table border=1><tr><td valign="top"> +<strong>Related Modules</strong><br><br> + +<a href="mod/mod_log_config.html">mod_log_config</a><br> + +</td><td valign="top"> +<strong>Related Directives</strong><br><br> + +<a href="mod/mod_log_config.html#customlog">CustomLog</a><br> +<a href="mod/mod_log_config.html#logformat">LogFormat</a><br> +<a href="mod/mod_setenvif.html#setenvif">SetEnvIf</a> + +</td></tr></table> + +<p>The server access log records all requests processed by the server. +The location of the access log as well as its contents are controlled +by the <a href="mod/mod_log_config.html#customlog">CustomLog</a> +directive. The <a +href="mod/mod_log_config.html#logformat">LogFormat</a> directive can +be used to simplify the selection of the contents of the logs. +This section describes how to configure the server to record +information in the access log.</p> + +<p>Of course, storing the information in the access log is only the +start of log management. The next step is to analyze this information +to produce useful statistics. Log analysis in general is beyond the +scope of this document, and not really part of the job of the +web server itself. For more information about this topic, and for +applications which perform log analysis, check the <a +href="http://dmoz.org/Computers/Software/Internet/Site_Management/Log_analysis/" +>Open Directory</a> or <a +href="http://dir.yahoo.com/Computers_and_Internet/Software/Internet/World_Wide_Web/Servers/Log_Analysis_Tools/" +>Yahoo</a>.</p> + +<p>Various versions of Apache HTTPD have used other modules and +directives to control access logging, including mod_log_referer, +mod_log_agent, and the <code>TransferLog</code> directive. The +<code>CustomLog</code> directive now subsumes the functionality of all +the older directives.</p> + +<p>The format of the access log is highly configurable. The format is +specified using a <a href="mod/mod_log_config.html#format">format +string</a> that looks much like a C-style printf(1) format string. +Some examples are presented in the next sections. For a complete list +of the possible contents of the format string, see the <a +href="mod/mod_log_config.html">mod_log_config documentation</a>.</p> + +<h3><a name="common">Common Log Format</a></h3> + +<p>A typical configuration for the access log might look +as follows.</p> + +<blockquote><code> +LogFormat "%h %l %u %t \"%r\" %>s %b" common<br> +CustomLog logs/access_log common +</code></blockquote> + +<p>This defines the <em>nickname</em> <code>common</code> and +associates it with a particular log format string. The format string +consists of percent directives, each of which tell the server to log a +particular piece of information. Literal characters may also be +placed in the format string and will be copied directly into the log +output. The quote character (<code>"</code>) must be escaped by +placing a back-slash before it to prevent it from being interpreted as +the end of the format string. The format string may also contain the +special control characters "<code>\n</code>" for new-line and +"<code>\t</code>" for tab.</p> + +<p>The <code>CustomLog</code> directive sets up a new log file using +the defined <em>nickname</em>. The filename for the access log is +relative to the <a href="mod/core.html#serverroot">ServerRoot</a> +unless it begins with a slash.</p> + +<p>The above configuration will write log entries in a format known as +the Common Log Format (CLF). This standard format can be produced by +many different web servers and read by many log analysis programs. +The log file entries produced in CLF will look something like +this:</p> + +<blockquote><code> +127.0.0.1 - frank [10/Oct/2000:13:55:36 -0700] "GET /apache_pb.gif HTTP/1.0" 200 2326 +</code></blockquote> + +<p>Each part of this log entry is described below.</p> + +<dl> +<dt><code>127.0.0.1</code> (<code>%h</code>)</dt> <dd>This is the IP +address of the client (remote host) which made the request to the +server. If <a +href="mod/core.html#hostnamelookups">HostNameLookups</a> is set to +<code>On</code>, then the server will try to determine the hostname +and log it in place of the IP address. However, this configuration is +not recommended since it can significantly slow the server. Instead, +it is best to use a log post-processor such as <a +href="programs/logresolve.html">logresolve</a> to determine the +hostnames. The IP address reported here is not necessarily the +address of the machine at which the user is sitting. If a proxy +server exists between the user and the server, this address will be +the address of the proxy, rather than the originating machine.</dd> + +<dt><code>-</code> (<code>%l</code>)</dt> <dd>The "hyphen" in the +output indicates that the requested piece of information is not +available. In this case, the information that is not available is the +RFC 1413 identity of the client determined by <code>identd</code> on +the clients machine. This information is highly unreliable and should +almost never be used except on tightly controlled internal networks. +Apache HTTPD will not even attempt to determine this information +unless <a href="mod/core.html#identitycheck">IdentityCheck</a> is set +to <code>On</code>.</dd> + +<dt><code>frank</code> (<code>%u</code>)</dt> <dd>This is the userid +of the person requesting the document as determined by HTTP +authentication. The same value is typically provided to CGI scripts +in the <code>REMOTE_USER</code> environment variable. If the status +code for the request (see below) is 401, then this value should not be +trusted because the user is not yet authenticated. If the document is +not password protected, this entry will be "<code>-</code>" just like +the previous one.</dd> + +<dt><code>[10/Oct/2000:13:55:36 -0700]</code> (<code>%t</code>)</dt> +<dd>The time that the server finished processing the request. The +format is: +<BLOCKQUOTE><CODE> [day/month/year:hour:minute:second zone] <BR> +day = 2*digit<BR> +month = 3*letter<BR> +year = 4*digit<BR> +hour = 2*digit<BR> +minute = 2*digit<BR> +second = 2*digit<BR> +zone = (`+' | `-') 4*digit</CODE></BLOCKQUOTE> +It is possible to have the time displayed in another format +by specifying <code>%{format}t</code> in the log format string, where +<code>format</code> is as in <code>strftime(3)</code> from the C +standard library. +</dd> + +<dt><code>"GET /apache_pb.gif HTTP/1.0"</code> +(<code>\"%r\"</code>)</dt> <dd>The request line from the client is +given in double quotes. The request line contains a great deal of +useful information. First, the method used by the client is +<code>GET</code>. Second, the client requested the resource +<code>/apache_pb.gif</code>, and third, the client used the protocol +<code>HTTP/1.0</code>.</dd> It is also possible to log one or more +parts of the request line independently. For example, the format +string "<code>%m %U%q %H</code>" will log the method, path, +query-string, and protocol, resulting in exactly the same output as +"<code>%r</code>".</dd> + +<dt><code>200</code></dt> (<code>%>s</code>) <dd>This is the status +code that the server sends back to the client. This information is +very valuable, because it reveals whether the request resulted in a +successful response (codes beginning in 2), a redirection (codes +beginning in 3), an error caused by the client (codes beginning in 4), +or an error in the server (codes beginning in 5). +The full list of possible status codes can be +found in the <a href="http://www.w3.org/Protocols/rfc2616/rfc2616.txt" +>HTTP specification</a> (RFC2616 section 10).</dd> + +<dt><code>2326</code> (<code>%b</code>) +<dd>The last entry indicates the size of the object returned to +the client, not including the response headers. If no content +was returned to the client, this value will be "<code>-</code>". +To log "<code>0</code>" for no content, use <code>%B</code> +instead.</dd> + +</dl> + +<h4><a name="combined">Combined Log Format</a></h4> + +<p>Another commonly used format string is called the +Combined Log Format. It can be used as follows.</p> + +<blockquote><code> +LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-agent}i\"" combined<br> +CustomLog log/acces_log combined +</code></blockquote> + +<p>This format is exactly the same as the Common Log Format, +with the addition of two more fields. The access log under this +format will look like:</p> + +<blockquote><code> +127.0.0.1 - frank [10/Oct/2000:13:55:36 -0700] "GET /apache_pb.gif HTTP/1.0" 200 2326 "http://www.example.com/start.html" "Mozilla/4.08 [en] (Win98; I ;Nav)" +</code></blockquote> + +<p>The additional fields are:</p> + +<dl> + +<dt><code>"http://www.example.com/start.html"</code> +(<code>\"%{Referer}i\"</code>)</dt> <dd>The "Referer" (sic) HTTP +request header. This gives the site that the client reports having +been referred from. (This should be the page that links to or includes +<code>/apache_pb.gif</code>). + +<dt><code>"Mozilla/4.08 [en] (Win98; I ;Nav)"</code> +(<code>\"%{User-agent}i\"</code>)</dt> <dd>The User-Agent HTTP request +header. This is the identifying information that the client browser +reports about itself.</dd> + +</dl> + +<h3><a name="multiple">Multiple Access Logs</a></h3> + +<p>Multiple access logs can be created simply by specifying multiple +<code>CustomLog</code> directives in the configuration file. For +example, the following directives will create three access logs. The +first contains the basic CLF information, while the second and third +contain referer and browser information. The last two +<code>CustomLog</code> lines show how to mimic the effects of the +<code>ReferLog</code> and <code>AgentLog</code> directives.</p> + +<blockquote><code> +LogFormat "%h %l %u %t \"%r\" %>s %b" common<br> +CustomLog logs/access_log common<br> +CustomLog logs/referer_log "%{Referer}i -> %U"<br> +CustomLog logs/agent_log "%{User-agent}i" +</code></blockquote> + +<p>This example also shows that it is not necessary to define a +nickname with the <code>LogFormat</code> directive. Instead, the log +format can be specified directly in the <code>CustomLog</code> +directive.</p> + +<h3><a name="conditional">Conditional Logging</a></h3> + +<p>There are times when it is convenient to exclude certain entries +from the access logs based on characteristics of the client request. +This is easily accomplished with the help of <a +href="env.html">environment variables</a>. First, an environment +variable must be set to indicate that the request meets certain +conditions. This is usually accomplished with <a +href="mod/mod_setenvif.html#setenvif">SetEnvIf</a>. Then the +<code>env=</code> clause of the <code>CustomLog</code> directive is +used to include or exclude requests where the environment variable is +set. Some examples:</p> + +<blockquote><code> +# Mark requests from the loop-back interface<br> +SetEnvIf Remote_Addr "127\.0\.0\.1" dontlog<br> +# Mark requests for the robots.txt file<br> +SetEnvIf Request_URI "^/robots\.txt$" dontlog<br> +# Log what remains<br> +CustomLog logs/access_log common env=!dontlog +</code></blockquote> + +<p>As another example, consider logging requests from english-speakers +to one log file, and non-english speakers to a different log file.</p> + +<blockquote><code> +SetEnvIf Accept-Language "en" english<br> +CustomLog logs/english_log common env=english<br> +CustomLog logs/non_english_log common env=!english +</code></blockquote> + +<p>Although we have just shown that conditional logging is very +powerful and flexibly, it is not the only way to control the contents +of the logs. Log files are more useful when they contain a complete +record of server activity. It is often easier to simply post-process +the log files to remove requests that you do not want to consider.</p> + +<hr> + +<h2><a name="rotation">Log Rotation</a></h2> + +<p>On even a moderately busy server, the quantity of information +stored in the log files is very large. The access log file typically +grows 1 MB or more per 10,000 requests. It will consequently be +necessary to periodically rotate the log files by moving or deleting +the existing logs. This cannot be done while the server is running, +because Apache will continue writing to the old log file as long as it +holds the file open. Instead, the server must be <a +href="stopping.html">restarted</a> after the log files are moved or +deleted so that it will open new log files.</p> + +<p>By using a <em>graceful</em> restart, the server can be instructed +to open new log files without losing any existing or pending +connections from clients. However, in order to accomplish this, the +server must continue to write to the old log files while it finishes +serving old requests. It is therefore necessary to wait for some time +after the restart before doing any processing on the log files. A +typical scenario that simply rotates the logs and compresses the old +logs to save space is:</p> + +<blockquote><code> +mv access_log access_log.old<br> +mv error_log error_log.old<br> +apachectl graceful<br> +sleep 600<br> +gzip access_log.old error_log.old +</code></blockquote> + +<p>Another way to perform log rotation is using <a href="#piped">piped +logs</a> as discussed in the next section.</p> + +<hr> + +<h2><a name="piped">Piped Logs</a></h2> + +<p>Apache HTTPD is capable of writing error and access log files +through a pipe to another process, rather than directly to a file. +This capability dramatically increases the flexibility of logging, +without adding code to the main server. In order to write logs to a +pipe, simply replace the filename with the pipe character +"<code>|</code>", followed by the name of the executable which should +accept log entries on its standard input. Apache will start the +piped-log process when the server starts, and will restart it if it +crashes while the server is running. (This last feature is why we can +refer to this technique as "reliable piped logging".)</p> + +<p>Piped log processes are spawned by the parent Apache HTTPD process, +and inherit the userid of that process. This means that piped log +programs usually run as root. It is therefore very important to keep +the programs simple and secure.</p> + +<p>Some simple examples using piped logs:</p> + +<blockquote><code> +# compressed logs<br> +CustomLog "|/usr/bin/gzip -c >> /var/log/access_log.gz" common<br> +# almost-real-time name resolution<br> +CustomLog "|/usr/local/apache/bin/logresolve >> /var/log/access_log" common +</code></blockquote> + +<p>Notice that quotes are used to enclose the entire command +that will be called for the pipe. Although these examples are +for the access log, the same technique can be used for the +error log.</p> + +<p>One important use of piped logs is to allow log rotation without +having to restart the server. Apache HTTPD includes a simple program +called <a href="programs/rotatelogs.html">rotatelogs</a> for this +purpose. For example, to rotate the logs every 24 hours, you can +use:</p> + +<blockquote><code> +CustomLog "|/usr/local/apache/bin/rotatelogs /var/log/access_log 86400" common +</code></blockquote> + +<p>A similar, but much more flexible log rotation program +called <a href="http://www.ford-mason.co.uk/resources/cronolog/">cronolog</a> +is available at an external site.</p> + +<p>As with conditional logging, piped logs are a very powerful tool, +but they should not be used where a simpler solution like +off-line post-processing is available.</p> + +<hr> + +<h2><a name="virtualhosts">Virtual Hosts</a></h2> + +<p>When running a server with many <a href="vhosts/">virtual +hosts</a>, there are several options for dealing with log files. +First, it is possible to use logs exactly as in a single-host server. +Simply by placing the logging directives outside the +<code><VirtualHost></code> sections in the main server context, +it is possible to log all requests in the same access log and error +log. This technique does not allow for easy collection of statistics +on individual virtual hosts.</p> + +<p>If <code>CustomLog</code> or <code>ErrorLog</code> directives are +placed inside a <code><VirtualHost></code> section, all requests +or errors for that virtual host will be logged only to the specified +file. Any virtual host which does not have logging directives will +still have its requests sent to the main server logs. This technique +is very useful for a small number of virtual hosts, but if the number +of hosts is very large, it can be complicated to manage. In addition, +it can often create problems with <a +href="vhosts/fd-limits.html">insufficient file descriptors</a>.</p> + +<p>For the access log, there is a very good compromise. By adding +information on the virtual host to the log format string, +it is possible to log all hosts to the same log, and later +split the log into individual files. For example, consider the +following directives.</p> + +<blockquote><code> +LogFormat "%v %l %u %t \"%r\" %>s %b" comonvhost<br> +CustomLog logs/access_log comonvhost +</code></blockquote> + +<p>The <code>%v</code> is used to log the name of the virtual host +that is serving the request. Then a program like <a +href="programs/other.html">split-logfile</a> can be used to +post-process the access log in order to split it into one file per +virtual host.</p> + +<p>Unfortunately, no similar technique is available for the error log, +so you must choose between mixing all virtual hosts in the same error +log and using one error log per virtual host.</p> + +<hr> + +<h2><a name="other">Other Log Files</a></h2> + +<table border=1><tr><td valign="top"> +<strong>Related Modules</strong><br><br> + +<a href="mod/mod_cgi.html">mod_cgi</a><br> +<a href="mod/mod_rewrite.html">mod_rewrite</a> + +</td><td valign="top"> +<strong>Related Directives</strong><br><br> + +<a href="mod/core.html#pidfile">PidFile</a><br> +<a href="mod/mod_rewrite.html#RewriteLog">RewriteLog</a></br> +<a href="mod/mod_rewrite.html#RewriteLogLevel">RewriteLogLevel</a></br> +<a href="mod/mod_cgi.html#scriptlog">ScriptLog</a><br> +<a href="mod/mod_cgi.html#scriptloglength">ScriptLogLength</a><br> +<a href="mod/mod_cgi.html#scriptlogbuffer">ScriptLogBuffer</a> + +</td></tr></table> + +<h3><a name="pidfile">PID File</a></h3> + +<p>On startup, Apache HTTPD saves the process id of the parent httpd +process to the file <code>logs/httpd.pid</code>. This filename can be +changed with the <A HREF="mod/core.html#pidfile">PidFile</A> +directive. The process-id is for use by the administrator in +restarting and terminating the daemon by sending signals +to the parent process; on Windows, use the -k command line +option instead. For more information see the <A +HREF="stopping.html">Stopping and Restarting</A> page. + +<h3><a name="scriptlog">Script Log</a></h3> + +<p>In order to aid in debugging, the +<a href="mod/mod_cgi.html#scriptlog">ScriptLog</a> +directive allows you to record the input to and output from +CGI scripts. This should only be used in testing - not for +live servers. More information is available in the +<a href="mod/mod_cgi.html">mod_cgi documentation</a>. + +<h3><a name="rewritelog">Rewrite Log</a></h3> + +<p>When using the powerful and complex features of <a +href="mod/mod_rewrite.html">mod_rewrite</a>, it is almost always +necessary to use the <a +href="mod/mod_rewrite.html#RewriteLog">RewriteLog</a> to help in +debugging. This log file produces a detailed analysis of how the +rewriting engine transforms requests. The level of detail is +controlled by the <a +href="mod/mod_rewrite.html#RewriteLogLevel">RewriteLogLevel</a> +directive.</p> + +<!--#include virtual="footer.html" --> +</BODY> +</HTML> diff --git a/docs/manual/logs.html.en b/docs/manual/logs.html.en new file mode 100644 index 0000000000..d3ebf480c5 --- /dev/null +++ b/docs/manual/logs.html.en @@ -0,0 +1,580 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<HTML> +<HEAD> +<TITLE>Log Files - Apache HTTP Server</TITLE> +</HEAD> + +<!-- Background white, links blue (unvisited), navy (visited), red (active) --> +<BODY + BGCOLOR="#FFFFFF" + TEXT="#000000" + LINK="#0000FF" + VLINK="#000080" + ALINK="#FF0000" +> +<!--#include virtual="header.html" --> +<h1 align="center">Log Files</h1> + +<p>In order to effectively manage a web server, it is necessary to get +feedback about the activity and performance of the server as well as +any problems that may be occuring. The Apache HTTP Server provides +very comprehensive and flexible logging capabilities. This document +describes how to configure the various log files, and how to +understand what the logs contain.</p> + +<ul> +<li><a href="#security">Security Warning</a></li> +<li><a href="#errorlog">Error Log</a></li> +<li><a href="#accesslog">Access Log</a> + <ul> + <li><a href="#common">Common Log Format</a></li> + <li><a href="#combined">Combined Log Format</a></li> + <li><a href="#multiple">Multiple Access Logs</a></li> + <li><a href="#conditional">Conditional Logging</a></li> + </ul></li> +<li><a href="#rotation">Log Rotation</a></li> +<li><a href="#piped">Piped Logs</a></li> +<li><a href="#virtualhosts">VirtualHosts</a> +<li><a href="#other">Other Log Files</a> + <ul> + <li><a href="#pidfile">PID File</a></li> + <li><a href="#scriptlog">Script Log</a></li> + <li><a href="#rewritelog">Rewrite Log</a></li> + </ul></li> +</ul> + +<hr> + +<h2><a name="security">Security Warning</a></h2> + +<p>Anyone who can write to the directory where Apache is writing a +log file can almost certainly gain access to the uid that the server is +started as, which is normally root. Do <EM>NOT</EM> give people write +access to the directory the logs are stored in without being aware of +the consequences; see the <A HREF="misc/security_tips.html">security tips</A> +document for details.</p> + +<p>In addition, log files may contain information supplied directly +by the client, without escaping. Therefore, it is possible for +malicious clients to insert control-characters in the log files, so +care must be taken in dealing with raw logs.</p> + +<hr> + +<h2><a name="errorlog">Error Log</a></h2> + +<table border="1"> +<tr><td valign="top"> +<strong>Related Directives</strong><br><br> + +<a href="mod/core.html#errorlog">ErrorLog</a><br> +<a href="mod/core.html#loglevel">LogLevel</a> +</td></tr></table> + +<p>The server error log, whose name and location is set by the <a +href="mod/core.html#errorlog">ErrorLog</a> directive, is the most +important log file. This is the place where Apache HTTPD will send +diagnostic information and record any errors that it encounters in +processing requests. It is the first place to look when a problem +occurs with starting the server or with the operation of the server, +since it will often contain details of what went wrong and how to fix +it.</p> + +<p>The error log is usually written to a file (typically +<code>error_log</code> on unix systems and <code>error.log</code> on +Windows and OS/2). On unix systems it is also possible to have the +server send errors to the <code>syslog</code> or <a href="#pipe">pipe +them through a program</a>.</p> + +<p>The format of the error log is relatively free-form and +descriptive. But there is certain information that is contained +in most error log entries. For example, here is a typical message.</p> + +<blockquote><code> +[Wed Oct 11 14:32:52 2000] [error] [client 127.0.0.1] client denied by server configuration: /export/home/live/ap/htdocs/test +</code></blockquote> + +<p>The first item in the log entry is the date and time of the +message. The second entry lists the severity of the error being +reported. The <a href="mod/core.html#loglevel">LogLevel</a> directive +is used to control the types of errors that are sent to the error log +by restricting the severity level. The third entry gives the IP +address of the client that generated the error. Beyond that is the +message itself, which in this case indicates that the server has been +configured to deny the client access. The server reports the +file-system path (as opposed to the web path) of the requested +document.</p> + +<p>A very wide variety of different messages can appear in the error +log. Most look similar to the example above. The error log will also +contain debugging output from CGI scripts. Any information written to +<code>stderr</code> by a CGI script will be copied directly to the +error log.</p> + +<p>It is not possible to customize the error log by adding or removing +information. However, error log entries dealing with particular +requests have corresponding entries in the <a href="accesslog">access +log</a>. For example, the above example entry corresponds to an +access log entry with status code 403. Since it is possible to +customize the access log, you can obtain more information about error +conditions using that log file.</p> + +<p>During testing, it is often useful to continuously monitor the +error log for any problems. On unix systems, you can accomplish this +using:</p> +<blockquote><code> +tail -f error_log +</code></blockquote> + +<hr> + +<h2><a name="accesslog">Access Log</a></h2> + +<table border=1><tr><td valign="top"> +<strong>Related Modules</strong><br><br> + +<a href="mod/mod_log_config.html">mod_log_config</a><br> + +</td><td valign="top"> +<strong>Related Directives</strong><br><br> + +<a href="mod/mod_log_config.html#customlog">CustomLog</a><br> +<a href="mod/mod_log_config.html#logformat">LogFormat</a><br> +<a href="mod/mod_setenvif.html#setenvif">SetEnvIf</a> + +</td></tr></table> + +<p>The server access log records all requests processed by the server. +The location of the access log as well as its contents are controlled +by the <a href="mod/mod_log_config.html#customlog">CustomLog</a> +directive. The <a +href="mod/mod_log_config.html#logformat">LogFormat</a> directive can +be used to simplify the selection of the contents of the logs. +This section describes how to configure the server to record +information in the access log.</p> + +<p>Of course, storing the information in the access log is only the +start of log management. The next step is to analyze this information +to produce useful statistics. Log analysis in general is beyond the +scope of this document, and not really part of the job of the +web server itself. For more information about this topic, and for +applications which perform log analysis, check the <a +href="http://dmoz.org/Computers/Software/Internet/Site_Management/Log_analysis/" +>Open Directory</a> or <a +href="http://dir.yahoo.com/Computers_and_Internet/Software/Internet/World_Wide_Web/Servers/Log_Analysis_Tools/" +>Yahoo</a>.</p> + +<p>Various versions of Apache HTTPD have used other modules and +directives to control access logging, including mod_log_referer, +mod_log_agent, and the <code>TransferLog</code> directive. The +<code>CustomLog</code> directive now subsumes the functionality of all +the older directives.</p> + +<p>The format of the access log is highly configurable. The format is +specified using a <a href="mod/mod_log_config.html#format">format +string</a> that looks much like a C-style printf(1) format string. +Some examples are presented in the next sections. For a complete list +of the possible contents of the format string, see the <a +href="mod/mod_log_config.html">mod_log_config documentation</a>.</p> + +<h3><a name="common">Common Log Format</a></h3> + +<p>A typical configuration for the access log might look +as follows.</p> + +<blockquote><code> +LogFormat "%h %l %u %t \"%r\" %>s %b" common<br> +CustomLog logs/access_log common +</code></blockquote> + +<p>This defines the <em>nickname</em> <code>common</code> and +associates it with a particular log format string. The format string +consists of percent directives, each of which tell the server to log a +particular piece of information. Literal characters may also be +placed in the format string and will be copied directly into the log +output. The quote character (<code>"</code>) must be escaped by +placing a back-slash before it to prevent it from being interpreted as +the end of the format string. The format string may also contain the +special control characters "<code>\n</code>" for new-line and +"<code>\t</code>" for tab.</p> + +<p>The <code>CustomLog</code> directive sets up a new log file using +the defined <em>nickname</em>. The filename for the access log is +relative to the <a href="mod/core.html#serverroot">ServerRoot</a> +unless it begins with a slash.</p> + +<p>The above configuration will write log entries in a format known as +the Common Log Format (CLF). This standard format can be produced by +many different web servers and read by many log analysis programs. +The log file entries produced in CLF will look something like +this:</p> + +<blockquote><code> +127.0.0.1 - frank [10/Oct/2000:13:55:36 -0700] "GET /apache_pb.gif HTTP/1.0" 200 2326 +</code></blockquote> + +<p>Each part of this log entry is described below.</p> + +<dl> +<dt><code>127.0.0.1</code> (<code>%h</code>)</dt> <dd>This is the IP +address of the client (remote host) which made the request to the +server. If <a +href="mod/core.html#hostnamelookups">HostNameLookups</a> is set to +<code>On</code>, then the server will try to determine the hostname +and log it in place of the IP address. However, this configuration is +not recommended since it can significantly slow the server. Instead, +it is best to use a log post-processor such as <a +href="programs/logresolve.html">logresolve</a> to determine the +hostnames. The IP address reported here is not necessarily the +address of the machine at which the user is sitting. If a proxy +server exists between the user and the server, this address will be +the address of the proxy, rather than the originating machine.</dd> + +<dt><code>-</code> (<code>%l</code>)</dt> <dd>The "hyphen" in the +output indicates that the requested piece of information is not +available. In this case, the information that is not available is the +RFC 1413 identity of the client determined by <code>identd</code> on +the clients machine. This information is highly unreliable and should +almost never be used except on tightly controlled internal networks. +Apache HTTPD will not even attempt to determine this information +unless <a href="mod/core.html#identitycheck">IdentityCheck</a> is set +to <code>On</code>.</dd> + +<dt><code>frank</code> (<code>%u</code>)</dt> <dd>This is the userid +of the person requesting the document as determined by HTTP +authentication. The same value is typically provided to CGI scripts +in the <code>REMOTE_USER</code> environment variable. If the status +code for the request (see below) is 401, then this value should not be +trusted because the user is not yet authenticated. If the document is +not password protected, this entry will be "<code>-</code>" just like +the previous one.</dd> + +<dt><code>[10/Oct/2000:13:55:36 -0700]</code> (<code>%t</code>)</dt> +<dd>The time that the server finished processing the request. The +format is: +<BLOCKQUOTE><CODE> [day/month/year:hour:minute:second zone] <BR> +day = 2*digit<BR> +month = 3*letter<BR> +year = 4*digit<BR> +hour = 2*digit<BR> +minute = 2*digit<BR> +second = 2*digit<BR> +zone = (`+' | `-') 4*digit</CODE></BLOCKQUOTE> +It is possible to have the time displayed in another format +by specifying <code>%{format}t</code> in the log format string, where +<code>format</code> is as in <code>strftime(3)</code> from the C +standard library. +</dd> + +<dt><code>"GET /apache_pb.gif HTTP/1.0"</code> +(<code>\"%r\"</code>)</dt> <dd>The request line from the client is +given in double quotes. The request line contains a great deal of +useful information. First, the method used by the client is +<code>GET</code>. Second, the client requested the resource +<code>/apache_pb.gif</code>, and third, the client used the protocol +<code>HTTP/1.0</code>.</dd> It is also possible to log one or more +parts of the request line independently. For example, the format +string "<code>%m %U%q %H</code>" will log the method, path, +query-string, and protocol, resulting in exactly the same output as +"<code>%r</code>".</dd> + +<dt><code>200</code></dt> (<code>%>s</code>) <dd>This is the status +code that the server sends back to the client. This information is +very valuable, because it reveals whether the request resulted in a +successful response (codes beginning in 2), a redirection (codes +beginning in 3), an error caused by the client (codes beginning in 4), +or an error in the server (codes beginning in 5). +The full list of possible status codes can be +found in the <a href="http://www.w3.org/Protocols/rfc2616/rfc2616.txt" +>HTTP specification</a> (RFC2616 section 10).</dd> + +<dt><code>2326</code> (<code>%b</code>) +<dd>The last entry indicates the size of the object returned to +the client, not including the response headers. If no content +was returned to the client, this value will be "<code>-</code>". +To log "<code>0</code>" for no content, use <code>%B</code> +instead.</dd> + +</dl> + +<h4><a name="combined">Combined Log Format</a></h4> + +<p>Another commonly used format string is called the +Combined Log Format. It can be used as follows.</p> + +<blockquote><code> +LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-agent}i\"" combined<br> +CustomLog log/acces_log combined +</code></blockquote> + +<p>This format is exactly the same as the Common Log Format, +with the addition of two more fields. The access log under this +format will look like:</p> + +<blockquote><code> +127.0.0.1 - frank [10/Oct/2000:13:55:36 -0700] "GET /apache_pb.gif HTTP/1.0" 200 2326 "http://www.example.com/start.html" "Mozilla/4.08 [en] (Win98; I ;Nav)" +</code></blockquote> + +<p>The additional fields are:</p> + +<dl> + +<dt><code>"http://www.example.com/start.html"</code> +(<code>\"%{Referer}i\"</code>)</dt> <dd>The "Referer" (sic) HTTP +request header. This gives the site that the client reports having +been referred from. (This should be the page that links to or includes +<code>/apache_pb.gif</code>). + +<dt><code>"Mozilla/4.08 [en] (Win98; I ;Nav)"</code> +(<code>\"%{User-agent}i\"</code>)</dt> <dd>The User-Agent HTTP request +header. This is the identifying information that the client browser +reports about itself.</dd> + +</dl> + +<h3><a name="multiple">Multiple Access Logs</a></h3> + +<p>Multiple access logs can be created simply by specifying multiple +<code>CustomLog</code> directives in the configuration file. For +example, the following directives will create three access logs. The +first contains the basic CLF information, while the second and third +contain referer and browser information. The last two +<code>CustomLog</code> lines show how to mimic the effects of the +<code>ReferLog</code> and <code>AgentLog</code> directives.</p> + +<blockquote><code> +LogFormat "%h %l %u %t \"%r\" %>s %b" common<br> +CustomLog logs/access_log common<br> +CustomLog logs/referer_log "%{Referer}i -> %U"<br> +CustomLog logs/agent_log "%{User-agent}i" +</code></blockquote> + +<p>This example also shows that it is not necessary to define a +nickname with the <code>LogFormat</code> directive. Instead, the log +format can be specified directly in the <code>CustomLog</code> +directive.</p> + +<h3><a name="conditional">Conditional Logging</a></h3> + +<p>There are times when it is convenient to exclude certain entries +from the access logs based on characteristics of the client request. +This is easily accomplished with the help of <a +href="env.html">environment variables</a>. First, an environment +variable must be set to indicate that the request meets certain +conditions. This is usually accomplished with <a +href="mod/mod_setenvif.html#setenvif">SetEnvIf</a>. Then the +<code>env=</code> clause of the <code>CustomLog</code> directive is +used to include or exclude requests where the environment variable is +set. Some examples:</p> + +<blockquote><code> +# Mark requests from the loop-back interface<br> +SetEnvIf Remote_Addr "127\.0\.0\.1" dontlog<br> +# Mark requests for the robots.txt file<br> +SetEnvIf Request_URI "^/robots\.txt$" dontlog<br> +# Log what remains<br> +CustomLog logs/access_log common env=!dontlog +</code></blockquote> + +<p>As another example, consider logging requests from english-speakers +to one log file, and non-english speakers to a different log file.</p> + +<blockquote><code> +SetEnvIf Accept-Language "en" english<br> +CustomLog logs/english_log common env=english<br> +CustomLog logs/non_english_log common env=!english +</code></blockquote> + +<p>Although we have just shown that conditional logging is very +powerful and flexibly, it is not the only way to control the contents +of the logs. Log files are more useful when they contain a complete +record of server activity. It is often easier to simply post-process +the log files to remove requests that you do not want to consider.</p> + +<hr> + +<h2><a name="rotation">Log Rotation</a></h2> + +<p>On even a moderately busy server, the quantity of information +stored in the log files is very large. The access log file typically +grows 1 MB or more per 10,000 requests. It will consequently be +necessary to periodically rotate the log files by moving or deleting +the existing logs. This cannot be done while the server is running, +because Apache will continue writing to the old log file as long as it +holds the file open. Instead, the server must be <a +href="stopping.html">restarted</a> after the log files are moved or +deleted so that it will open new log files.</p> + +<p>By using a <em>graceful</em> restart, the server can be instructed +to open new log files without losing any existing or pending +connections from clients. However, in order to accomplish this, the +server must continue to write to the old log files while it finishes +serving old requests. It is therefore necessary to wait for some time +after the restart before doing any processing on the log files. A +typical scenario that simply rotates the logs and compresses the old +logs to save space is:</p> + +<blockquote><code> +mv access_log access_log.old<br> +mv error_log error_log.old<br> +apachectl graceful<br> +sleep 600<br> +gzip access_log.old error_log.old +</code></blockquote> + +<p>Another way to perform log rotation is using <a href="#piped">piped +logs</a> as discussed in the next section.</p> + +<hr> + +<h2><a name="piped">Piped Logs</a></h2> + +<p>Apache HTTPD is capable of writing error and access log files +through a pipe to another process, rather than directly to a file. +This capability dramatically increases the flexibility of logging, +without adding code to the main server. In order to write logs to a +pipe, simply replace the filename with the pipe character +"<code>|</code>", followed by the name of the executable which should +accept log entries on its standard input. Apache will start the +piped-log process when the server starts, and will restart it if it +crashes while the server is running. (This last feature is why we can +refer to this technique as "reliable piped logging".)</p> + +<p>Piped log processes are spawned by the parent Apache HTTPD process, +and inherit the userid of that process. This means that piped log +programs usually run as root. It is therefore very important to keep +the programs simple and secure.</p> + +<p>Some simple examples using piped logs:</p> + +<blockquote><code> +# compressed logs<br> +CustomLog "|/usr/bin/gzip -c >> /var/log/access_log.gz" common<br> +# almost-real-time name resolution<br> +CustomLog "|/usr/local/apache/bin/logresolve >> /var/log/access_log" common +</code></blockquote> + +<p>Notice that quotes are used to enclose the entire command +that will be called for the pipe. Although these examples are +for the access log, the same technique can be used for the +error log.</p> + +<p>One important use of piped logs is to allow log rotation without +having to restart the server. Apache HTTPD includes a simple program +called <a href="programs/rotatelogs.html">rotatelogs</a> for this +purpose. For example, to rotate the logs every 24 hours, you can +use:</p> + +<blockquote><code> +CustomLog "|/usr/local/apache/bin/rotatelogs /var/log/access_log 86400" common +</code></blockquote> + +<p>A similar, but much more flexible log rotation program +called <a href="http://www.ford-mason.co.uk/resources/cronolog/">cronolog</a> +is available at an external site.</p> + +<p>As with conditional logging, piped logs are a very powerful tool, +but they should not be used where a simpler solution like +off-line post-processing is available.</p> + +<hr> + +<h2><a name="virtualhosts">Virtual Hosts</a></h2> + +<p>When running a server with many <a href="vhosts/">virtual +hosts</a>, there are several options for dealing with log files. +First, it is possible to use logs exactly as in a single-host server. +Simply by placing the logging directives outside the +<code><VirtualHost></code> sections in the main server context, +it is possible to log all requests in the same access log and error +log. This technique does not allow for easy collection of statistics +on individual virtual hosts.</p> + +<p>If <code>CustomLog</code> or <code>ErrorLog</code> directives are +placed inside a <code><VirtualHost></code> section, all requests +or errors for that virtual host will be logged only to the specified +file. Any virtual host which does not have logging directives will +still have its requests sent to the main server logs. This technique +is very useful for a small number of virtual hosts, but if the number +of hosts is very large, it can be complicated to manage. In addition, +it can often create problems with <a +href="vhosts/fd-limits.html">insufficient file descriptors</a>.</p> + +<p>For the access log, there is a very good compromise. By adding +information on the virtual host to the log format string, +it is possible to log all hosts to the same log, and later +split the log into individual files. For example, consider the +following directives.</p> + +<blockquote><code> +LogFormat "%v %l %u %t \"%r\" %>s %b" comonvhost<br> +CustomLog logs/access_log comonvhost +</code></blockquote> + +<p>The <code>%v</code> is used to log the name of the virtual host +that is serving the request. Then a program like <a +href="programs/other.html">split-logfile</a> can be used to +post-process the access log in order to split it into one file per +virtual host.</p> + +<p>Unfortunately, no similar technique is available for the error log, +so you must choose between mixing all virtual hosts in the same error +log and using one error log per virtual host.</p> + +<hr> + +<h2><a name="other">Other Log Files</a></h2> + +<table border=1><tr><td valign="top"> +<strong>Related Modules</strong><br><br> + +<a href="mod/mod_cgi.html">mod_cgi</a><br> +<a href="mod/mod_rewrite.html">mod_rewrite</a> + +</td><td valign="top"> +<strong>Related Directives</strong><br><br> + +<a href="mod/core.html#pidfile">PidFile</a><br> +<a href="mod/mod_rewrite.html#RewriteLog">RewriteLog</a></br> +<a href="mod/mod_rewrite.html#RewriteLogLevel">RewriteLogLevel</a></br> +<a href="mod/mod_cgi.html#scriptlog">ScriptLog</a><br> +<a href="mod/mod_cgi.html#scriptloglength">ScriptLogLength</a><br> +<a href="mod/mod_cgi.html#scriptlogbuffer">ScriptLogBuffer</a> + +</td></tr></table> + +<h3><a name="pidfile">PID File</a></h3> + +<p>On startup, Apache HTTPD saves the process id of the parent httpd +process to the file <code>logs/httpd.pid</code>. This filename can be +changed with the <A HREF="mod/core.html#pidfile">PidFile</A> +directive. The process-id is for use by the administrator in +restarting and terminating the daemon by sending signals +to the parent process; on Windows, use the -k command line +option instead. For more information see the <A +HREF="stopping.html">Stopping and Restarting</A> page. + +<h3><a name="scriptlog">Script Log</a></h3> + +<p>In order to aid in debugging, the +<a href="mod/mod_cgi.html#scriptlog">ScriptLog</a> +directive allows you to record the input to and output from +CGI scripts. This should only be used in testing - not for +live servers. More information is available in the +<a href="mod/mod_cgi.html">mod_cgi documentation</a>. + +<h3><a name="rewritelog">Rewrite Log</a></h3> + +<p>When using the powerful and complex features of <a +href="mod/mod_rewrite.html">mod_rewrite</a>, it is almost always +necessary to use the <a +href="mod/mod_rewrite.html#RewriteLog">RewriteLog</a> to help in +debugging. This log file produces a detailed analysis of how the +rewriting engine transforms requests. The level of detail is +controlled by the <a +href="mod/mod_rewrite.html#RewriteLogLevel">RewriteLogLevel</a> +directive.</p> + +<!--#include virtual="footer.html" --> +</BODY> +</HTML> diff --git a/docs/manual/mod/mod_cgid.html b/docs/manual/mod/mod_cgid.html new file mode 100644 index 0000000000..396d608d5c --- /dev/null +++ b/docs/manual/mod/mod_cgid.html @@ -0,0 +1,101 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<HTML> +<HEAD> +<TITLE>Apache module mod_cgi</TITLE> +</HEAD> + +<!-- Background white, links blue (unvisited), navy (visited), red (active) --> +<BODY + BGCOLOR="#FFFFFF" + TEXT="#000000" + LINK="#0000FF" + VLINK="#000080" + ALINK="#FF0000" +> +<!--#include virtual="header.html" --> + +<H1 ALIGN="CENTER">Module mod_cgid</H1> + +<p>This module provides for execution of CGI scripts using an external +CGI daemon.</p> + +<P><A +HREF="module-dict.html#Status" +REL="Help" +><STRONG>Status:</STRONG></A> Base (unix threaded MPMs only) +<BR> +<A +HREF="module-dict.html#SourceFile" +REL="Help" +><STRONG>Source File:</STRONG></A> mod_cgid.c +<BR> +<A +HREF="module-dict.html#ModuleIdentifier" +REL="Help" +><STRONG>Module Identifier:</STRONG></A> cgid_module +</P> + + +<H2>Summary</H2> + +<p>On certain unix operating systems, forking a process from a +multi-threaded server is a very expensive operation because the new +process will replicate all the threads of the parent process. In +order to avoid incurring this expense on each CGI invocation, mod_cgid +creates an external daemon that is responsible for forking child +processes to run CGI scripts. The main server communicates with this +daemon using a unix domain socket.</p> + +<p>This module is used by default whenever a multi-threaded MPM is +selected during the compilation process. At the user level, this +module is identical in configuration and operation to <a +href="mod_cgi.html">mod_cgi</a>. The only exception is the additional +directive <code>ScriptSock</code> which gives the name of the socket +to use for communication with the cgi daemon.</p> + +<h2>Directives</h2> + +<ul> +<li><a href="mod_cgi.html#scriptlog">ScriptLog</a></li> +<li><a href="mod_cgi.html#scriptloglength">ScriptLogLength</a></li> +<li><a href="mod_cgi.html#scriptlogbuffer">ScriptLogBuffer</a></li> +<li><a href="#scriptsock">ScriptSock</a></li> +</ul> + +<hr> + +<H3><A NAME="scriptsock">ScriptSock</A> directive</H3> + +<p><A + HREF="directive-dict.html#Syntax" + REL="Help" +><STRONG>Syntax:</STRONG></A> Scriptsock <EM>filename</EM><BR> +<A + HREF="directive-dict.html#Default" + REL="Help" +><STRONG>Default:</STRONG></A> logs/cgisock<BR> +<A + HREF="directive-dict.html#Context" + REL="Help" +><STRONG>Context:</STRONG></A> server config<BR> +<A + HREF="directive-dict.html#Status" + REL="Help" +><STRONG>Status:</STRONG></A> Base<br> +<A + HREF="directive-dict.html#Module" + REL="Help" +><STRONG>Module:</STRONG></A> mod_cgid</p> + +<p>This directive sets the name of the socket to use for communication +with the CGI daemon. The socket will be opened using the permissions +of the user who starts Apache (usually root). To maintain the security +of communications with CGI scripts, it is important that no other +user has permission to write in the directory where the socket is +located.</p> + + +<!--#include virtual="footer.html" --> +</BODY> +</HTML> + diff --git a/docs/manual/mod/mod_suexec.html b/docs/manual/mod/mod_suexec.html new file mode 100644 index 0000000000..0910999f1a --- /dev/null +++ b/docs/manual/mod/mod_suexec.html @@ -0,0 +1,95 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<HTML> +<HEAD> +<TITLE>Apache module mod_suexec</TITLE> +</HEAD> + +<!-- Background white, links blue (unvisited), navy (visited), red (active) --> +<BODY + BGCOLOR="#FFFFFF" + TEXT="#000000" + LINK="#0000FF" + VLINK="#000080" + ALINK="#FF0000" +> +<!--#include virtual="header.html" --> +<H1 ALIGN="CENTER">Module mod_suexec</H1> + +<P> +This module provides support for <A HREF="../suexec.html"> +running CGI scripts as a specified User and Group</A>. +</P> + +<P><A +HREF="module-dict.html#Status" +REL="Help" +><STRONG>Status:</STRONG></A> Extension +<BR> +<A +HREF="module-dict.html#SourceFile" +REL="Help" +><STRONG>Source File:</STRONG></A> mod_suexec.c +<BR> +<A +HREF="module-dict.html#ModuleIdentifier" +REL="Help" +><STRONG>Module Identifier:</STRONG></A> suexec_module +<BR> +<A +HREF="module-dict.html#Compatibility" +REL="Help" +><STRONG>Compatibility:</STRONG></A> Available in Apache 2.0 and later. +</P> + +<h2>Summary</h2> + +<p>This module allows CGI scripts to run as a specified user and Group.</p> + +<H2>Directives</H2> +<UL> + <LI><A HREF="#suexecusergroup">SuexecUserGroup</A> +</UL> + +<H2><A NAME="suexecusergroup">SuexecUserGroup directive</A></H2> +<P> +<A + HREF="directive-dict.html#Syntax" + REL="Help" +><STRONG>Syntax:</STRONG></A> SuexecUserGroup <EM>User Group</EM><BR> +<A + HREF="directive-dict.html#Default" + REL="Help" +><STRONG>Default:</STRONG></A> None<BR> +<A + HREF="directive-dict.html#Context" + REL="Help" +><STRONG>Context:</STRONG></A> server config, virtual host<BR> +<A + HREF="directive-dict.html#Status" + REL="Help" +><STRONG>Status:</STRONG></A> Extension<BR> +<A + HREF="directive-dict.html#Module" + REL="Help" +><STRONG>Module:</STRONG></A> mod_suexec<BR> +<A + HREF="directive-dict.html#Compatibility" + REL="Help" +><STRONG>Compatibility:</STRONG></A> SuexecUserGroup is only available in 2.0 and later.</P> +<P> +The <CODE>SuexecUserGroup</CODE> directive allows you to specify a user and +group for CGI programs to run as. Non-CGI requests are still processes +with the user specified in the User directive. This directive replaces +using the User and Group directives inside of VirtualHosts. +</P> +<HR> + +<H3 ALIGN="CENTER"> + Apache HTTP Server Version 2.0 +</H3> + +<A HREF="./"><IMG SRC="../images/index.gif" ALT="Index"></A> +<A HREF="../"><IMG SRC="../images/home.gif" ALT="Home"></A> + +</BODY> +</HTML> diff --git a/include/util_ldap.h b/include/util_ldap.h new file mode 100644 index 0000000000..10dc2eabe5 --- /dev/null +++ b/include/util_ldap.h @@ -0,0 +1,275 @@ +/* ==================================================================== + * The Apache Software License, Version 1.1 + * + * Copyright (c) 2000-2001 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 + * <http://www.apache.org/>. + */ + +#ifndef UTIL_LDAP_H +#define UTIL_LDAP_H + +#include <apr_ldap.h> + +/* this whole thing disappears if LDAP is not enabled */ +#ifdef APU_HAS_LDAP + +/* APR header files */ +#include <apr_lock.h> +#include <apr_tables.h> +#include <apr_time.h> + +/* Apache header files */ +#include "ap_config.h" +#include "httpd.h" +#include "http_config.h" +#include "http_core.h" +#include "http_log.h" +#include "http_protocol.h" +#include "http_request.h" + + +/* + * LDAP Connections + */ + +/* Values that the deref member can have */ +typedef enum { + never=LDAP_DEREF_NEVER, + searching=LDAP_DEREF_SEARCHING, + finding=LDAP_DEREF_FINDING, + always=LDAP_DEREF_ALWAYS +} deref_options; + +/* Structure representing an LDAP connection */ +typedef struct util_ldap_connection_t { + LDAP *ldap; + apr_lock_t *lock; /* Lock to indicate this connection is in use */ + int bound; /* Flag to indicate whether this connection is bound yet */ + + const char *host; /* Name of the LDAP server (or space separated list) */ + int port; /* Port of the LDAP server */ + deref_options deref; /* how to handle alias dereferening */ + + const char *binddn; /* DN to bind to server (can be NULL) */ + const char *bindpw; /* Password to bind to server (can be NULL) */ + + int netscapessl; /* True if use Netscape SSL connection */ + const char *certtdb; /* Path to Netscape CA database */ + + int starttls; /* True if StartTLS is enabled */ + int withtls; /* True if StartTLS on this connection */ + + const char *reason; /* Reason for an error failure */ + + struct util_ldap_connection_t *next; +} util_ldap_connection_t; + +/* LDAP cache state information */ +typedef struct util_ldap_state_t { + apr_pool_t *pool; /* pool from which this state is allocated */ + apr_lock_t *mutex; /* mutex lock for the connection list */ + + apr_size_t cache_bytes; /* Size (in bytes) of shared memory cache */ + long search_cache_ttl; /* TTL for search cache */ + long search_cache_size; /* Size (in entries) of search cache */ + long compare_cache_ttl; /* TTL for compare cache */ + long compare_cache_size; /* Size (in entries) of compare cache */ + + struct util_ldap_connection_t *connections; +#ifdef APU_HAS_LDAP_NETSCAPE_SSL + int have_certdb; +#endif +} util_ldap_state_t; + + +/** + * Open a connection to an LDAP server + * @param ldc A structure containing the expanded details of the server + * to connect to. The handle to the LDAP connection is returned + * as ldc->ldap. + * @tip This function connects to the LDAP server and binds. It does not + * connect if already connected (ldc->ldap != NULL). Does not bind + * if already bound. + * @return If successful LDAP_SUCCESS is returned. + * @deffunc int util_ldap_connection_open(util_ldap_connection_t *ldc) + */ +int util_ldap_connection_open(util_ldap_connection_t *ldc); + +/** + * Close a connection to an LDAP server + * @param ldc A structure containing the expanded details of the server + that was connected. + * @tip This function unbinds from the LDAP server, and clears ldc->ldap. + * It is possible to rebind to this server again using the same ldc + * structure, using apr_ldap_open_connection(). + * @deffunc util_ldap_close_connection(util_ldap_connection_t *ldc) + */ +void util_ldap_connection_close(util_ldap_connection_t *ldc); + +/** + * Find a connection in a list of connections + * @param r The request record + * @param host The hostname to connect to (multiple hosts space separated) + * @param port The port to connect to + * @param binddn The DN to bind with + * @param bindpw The password to bind with + * @param deref The dereferencing behavior + * @param netscapessl Start SSL on the connection using ldapssl_client_init() [0|1] + * @param starttls Start TLS using STARTTLS parameter [0|1] + * @tip Once a connection is found and returned, a lock will be acquired to + * lock that particular connection, so that another thread does not try and + * use this connection while it is busy. Once you are finished with a connection, + * apr_ldap_connection_close() must be called to release this connection. + * @deffunc util_ldap_connection_t *util_ldap_connection_find(request_rec *r, const char *host, int port, + * const char *binddn, const char *bindpw, deref_options deref, + * int netscapessl, int starttls) + */ +util_ldap_connection_t *util_ldap_connection_find(request_rec *r, const char *host, int port, + const char *binddn, const char *bindpw, deref_options deref, + int netscapessl, int starttls); + + +/** + * Compare two DNs for sameness + * @param r The request record + * @param ldc The LDAP connection being used. + * @param url The URL of the LDAP connection - used for deciding which cache to use. + * @param dn The first DN to compare. + * @param reqdn The DN to compare the first DN to. + * @param compare_dn_on_server Flag to determine whether the DNs should be checked using + * LDAP calls or with a direct string comparision. A direct + * string comparison is faster, but not as accurate - false + * negative comparisons are possible. + * @tip Two DNs can be equal and still fail a string comparison. Eg "dc=example,dc=com" + * and "dc=example, dc=com". Use the compare_dn_on_server unless there are serious + * performance issues. + * @deffunc int util_ldap_cache_comparedn(request_rec *r, util_ldap_connection_t *ldc, + * const char *url, const char *dn, const char *reqdn, + * int compare_dn_on_server) + */ +int util_ldap_cache_comparedn(request_rec *r, util_ldap_connection_t *ldc, + const char *url, const char *dn, const char *reqdn, + int compare_dn_on_server); + +/** + * A generic LDAP compare function + * @param r The request record + * @param ldc The LDAP connection being used. + * @param url The URL of the LDAP connection - used for deciding which cache to use. + * @param dn The DN of the object in which we do the compare. + * @param attrib The attribute within the object we are comparing for. + * @param value The value of the attribute we are trying to compare for. + * @tip Use this function to determine whether an attribute/value pair exists within an + * object. Typically this would be used to determine LDAP group membership. + * @deffunc int util_ldap_cache_compare(request_rec *r, util_ldap_connection_t *ldc, + * const char *url, const char *dn, const char *attrib, const char *value) + */ +int util_ldap_cache_compare(request_rec *r, util_ldap_connection_t *ldc, + const char *url, const char *dn, const char *attrib, const char *value); + +/** + * Checks a username/password combination by binding to the LDAP server + * @param r The request record + * @param ldc The LDAP connection being used. + * @param url The URL of the LDAP connection - used for deciding which cache to use. + * @param basedn The Base DN to search for the user in. + * @param scope LDAP scope of the search. + * @param filter The user to search for in the form of an LDAP filter. This filter must return + * exactly one user for the check to be successful. + * @param bindpw The user password to bind as. + * @param binddn The DN of the user will be returned in this variable. + * @tip The filter supplied will be searched for. If a single entry is returned, an attempt + * is made to bind as that user. If this bind succeeds, the user is not validated. + * @deffunc int util_ldap_cache_checkuserid(request_rec *r, util_ldap_connection_t *ldc, + * char *url, const char *basedn, int scope, + * char *filter, char *bindpw, char **binddn) + */ +int util_ldap_cache_checkuserid(request_rec *r, util_ldap_connection_t *ldc, + const char *url, const char *basedn, int scope, + const char *filter, const char *bindpw, const char **binddn); + +/* from apr_ldap_cache.c */ + +/** + * Init the LDAP cache + * @param pool The pool to use to initialise the cache + * @param reqsize The size of the shared memory segement to request. A size + * of zero requests the max size possible from + * apr_shmem_init() + * @deffunc void util_ldap_cache_init(apr_pool_t *p) + * @return The status code returned is the status code of the + * apr_smmem_init() call. Regardless of the status, the cache + * will be set up at least for in-process or in-thread operation. + */ +apr_status_t util_ldap_cache_init(apr_pool_t *pool, apr_size_t reqsize); + +/** + * Display formatted stats for cache + * @param The pool to allocate the returned string from + * @tip This function returns a string allocated from the provided pool that describes + * various stats about the cache. + * @deffunc char *util_ald_cache_display(apr_pool_t *pool) + */ +char *util_ald_cache_display(apr_pool_t *pool); + + +/* from apr_ldap_cache_mgr.c */ + +/** + * Display formatted stats for cache + * @param The pool to allocate the returned string from + * @tip This function returns a string allocated from the provided pool that describes + * various stats about the cache. + * @deffunc char *util_ald_cache_display(apr_pool_t *pool) + */ +char *util_ald_cache_display(apr_pool_t *pool); + +#endif /* APU_HAS_LDAP */ +#endif /* UTIL_LDAP_H */ diff --git a/include/util_time.h b/include/util_time.h new file mode 100644 index 0000000000..704d0e02d5 --- /dev/null +++ b/include/util_time.h @@ -0,0 +1,111 @@ +/* ==================================================================== + * The Apache Software License, Version 1.1 + * + * Copyright (c) 2001 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 + * <http://www.apache.org/>. + * + */ + +#ifndef APACHE_UTIL_TIME_H +#define APACHE_UTIL_TIME_H + +#include "apr.h" +#include "apr_time.h" +#include "httpd.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @package Apache date/time handling functions + */ + +/* Maximum delta from the current time, in seconds, for a past time + * to qualify as "recent" for use in the ap_explode_recent_*() functions: + */ +#define AP_TIME_RECENT_THRESHOLD 15 + +/** + * convert a recent time to its human readable components in local timezone + * @param tm the exploded time + * @param t the time to explode: MUST be within the last + * AP_TIME_RECENT_THRESHOLD seconds + * @note This is a faster alternative to apr_explode_localtime that uses + * a cache of pre-exploded time structures. It is useful for things + * that need to explode the current time multiple times per second, + * like loggers. + * @return APR_SUCCESS iff successful + * @deffunc apr_status_t ap_explode_recent_localtime(apr_exploded_time_t *tm, apr_time_t t); + */ +AP_DECLARE(apr_status_t) ap_explode_recent_localtime(apr_exploded_time_t *tm, + apr_time_t t); + +/** + * convert a recent time to its human readable components in GMT timezone + * @param tm the exploded time + * @param t the time to explode: MUST be within the last + * AP_TIME_RECENT_THRESHOLD seconds + * @note This is a faster alternative to apr_explode_gmt that uses + * a cache of pre-exploded time structures. It is useful for things + * that need to explode the current time multiple times per second, + * like loggers. + * @return APR_SUCCESS iff successful + * @deffunc apr_status_t ap_explode_recent_gmt(apr_exploded_time_t *tm, apr_time_t t); + */ +AP_DECLARE(apr_status_t) ap_explode_recent_gmt(apr_exploded_time_t *tm, + apr_time_t t); + + +#ifdef __cplusplus +} +#endif + +#endif /* !APACHE_UTIL_TIME_H */ diff --git a/modules/experimental/cache_storage.c b/modules/experimental/cache_storage.c new file mode 100644 index 0000000000..050628dad1 --- /dev/null +++ b/modules/experimental/cache_storage.c @@ -0,0 +1,276 @@ +/* ==================================================================== + * The Apache Software License, Version 1.1 + * + * Copyright (c) 2000-2001 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 + * <http://www.apache.org/>. + * + * Portions of this software are based upon public domain software + * originally written at the National Center for Supercomputing Applications, + * University of Illinois, Urbana-Champaign. + */ + +#define CORE_PRIVATE + +#include "mod_cache.h" + +APR_HOOK_STRUCT( + APR_HOOK_LINK(remove_url) + APR_HOOK_LINK(create_entity) + APR_HOOK_LINK(open_entity) +) + +module AP_MODULE_DECLARE_DATA tcache_module; + +/* -------------------------------------------------------------- */ + +/* + * delete all URL entities from the cache + * + */ +int cache_remove_url(request_rec *r, const char *types, char *url) +{ + const char *next = types; + const char *type; + + /* for each specified cache type, delete the URL */ + while ((type = ap_cache_tokstr(r->pool, next, &next))) { + cache_run_remove_url(type, url); + } + return OK; +} + + +/* + * create a new URL entity in the cache + * + * It is possible to store more than once entity per URL. This + * function will always create a new entity, regardless of whether + * other entities already exist for the same URL. + * + * The size of the entity is provided so that a cache module can + * decide whether or not it wants to cache this particular entity. + * If the size is unknown, a size of -1 should be set. + */ +int cache_create_entity(request_rec *r, const char *types, char *url, apr_size_t size) +{ + cache_handle *h; + const char *next = types; + const char *type; + apr_status_t rv; + cache_request_rec *cache = (cache_request_rec *) ap_get_module_config(r->request_config, + &tcache_module); + + /* for each specified cache type, delete the URL */ + while (next) { + type = ap_cache_tokstr(r->pool, next, &next); + switch (rv = cache_run_create_entity(&h, type, url, size)) { + case OK: { + cache->handle = h; + return OK; + } + case DECLINED: { + continue; + } + default: { + return rv; + } + } + } + return DECLINED; +} + +/* + * remove a specific URL entity from the cache + * + * The specific entity referenced by the cache_handle is removed + * from the cache, and the cache_handle is closed. + */ +int cache_remove_entity(request_rec *r, const char *types, cache_handle *h) +{ + const char *next = types; + const char *type; + + while (next) { + type = ap_cache_tokstr(r->pool, next, &next); + } + return 1; +} + +/* + * select a specific URL entity in the cache + * + * It is possible to store more than one entity per URL. Content + * negotiation is used to select an entity. Once an entity is + * selected, details of it are stored in the per request + * config to save time when serving the request later. + * + * This function returns OK if successful, DECLINED if no + * cached entity fits the bill. + */ +int cache_select_url(request_rec *r, const char *types, char *url) +{ + cache_handle *h; + const char *next = types; + const char *type; + apr_status_t rv; + cache_request_rec *cache = (cache_request_rec *) ap_get_module_config(r->request_config, + &tcache_module); + + /* go through the cache types till we get a match */ + while (next) { + type = ap_cache_tokstr(r->pool, next, &next); + switch ((rv = cache_run_open_entity(&h, type, url))) { + case OK: { + /* cool bananas! */ + cache->handle = h; +/*** loop through returned entities */ +/*** do freshness calculation here */ + cache->fresh = 1; +/*** do content negotiation here */ + return OK; + } + case DECLINED: { + /* try again with next cache type */ + continue; + } + default: { + /* oo-er! an error */ + return rv; + } + } + } + return DECLINED; +} + +apr_status_t cache_write_entity_headers(cache_handle *h, request_rec *r, cache_info *info, + apr_table_t *headers_in, apr_table_t *headers_out) +{ + const char *ct; + + ct = ap_table_get(r->headers_out, "Content-Type"); + info->content_type = ct; + h->write_headers(h, r, info); + return APR_SUCCESS; +} +apr_status_t cache_write_entity_body(cache_handle *h, apr_bucket_brigade *b) +{ + apr_status_t rv = APR_SUCCESS; + if (h->write_body(h, b) != OK) { + } + return rv; +} + +apr_status_t cache_read_entity_headers(cache_handle *h, request_rec *r, + apr_table_t **headers) +{ + cache_info *info; + + /* Be careful to not modify info. */ + h->read_headers(h, r, &info); + + /* Build the header table from info in the info struct */ + *headers = apr_table_make(r->pool, 15); + /* Content-Length */ + if (info->len) + apr_table_set(*headers, "Content-Length", apr_psprintf(r->pool, "%lu", info->len)); + + /* Last-Modified */ + if (info->lastmod) { + } + /* Expires */ + if (info->expire) { + } + if (info->content_type) { + r->content_type = apr_pstrdup(r->pool, info->content_type); + } + /* Date */ + + return APR_SUCCESS; +} +apr_status_t cache_read_entity_body(cache_handle *h, apr_bucket_brigade *b) +{ + h->read_body(h, b); + return APR_SUCCESS; +} + +APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(cache, CACHE, int, create_entity, + (cache_handle **hp, const char *type, + char *url, apr_size_t len),(hp,type,url,len),DECLINED) +APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(cache, CACHE, int, open_entity, + (cache_handle **hp, const char *type, + char *url),(hp,type,url),DECLINED) +APR_IMPLEMENT_EXTERNAL_HOOK_RUN_ALL(cache, CACHE, int, remove_url, + (const char *type, char *url),(type,url),OK,DECLINED) +#if 0 +/* BillS doesn't think these should be hooks. + * All functions which accept a cache_handle * argument should use + * function pointers in the cache_handle. Leave them here for now as + * points for discussion... + */ + +APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(cache, CACHE, int, remove_entity, + (cache_handle *h),(h),DECLINED) + +APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(cache, CACHE, int, read_entity_headers, + (cache_handle *h, request_rec *r, + apr_table_t **headers), + (h,info,headers_in,headers_out),DECLINED) +APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(cache, CACHE, int, read_entity_body, + (cache_handle *h, + apr_bucket_brigade *out),(h,out),DECLINED) +APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(cache, CACHE, int, write_entity_headers, + (cache_handle *h, cache_info *info, + apr_table_t *headers_in, + apr_table_t *headers_out), + (h,info,headers_in,headers_out),DECLINED) +APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(cache, CACHE, int, write_entity_body, + (cache_handle *h, + apr_bucket_brigade *in),(h,in),DECLINED) +#endif diff --git a/modules/experimental/cache_util.c b/modules/experimental/cache_util.c new file mode 100644 index 0000000000..cc37aa972c --- /dev/null +++ b/modules/experimental/cache_util.c @@ -0,0 +1,206 @@ +/* ==================================================================== + * The Apache Software License, Version 1.1 + * + * Copyright (c) 2000-2001 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 + * <http://www.apache.org/>. + * + * Portions of this software are based upon public domain software + * originally written at the National Center for Supercomputing Applications, + * University of Illinois, Urbana-Champaign. + */ + +#define CORE_PRIVATE + +#include "mod_cache.h" + + + +/* -------------------------------------------------------------- */ + +/* return true if the request is conditional */ +int ap_cache_request_is_conditional(request_rec *r) +{ + if (apr_table_get(r->headers_in, "If-Match") || + apr_table_get(r->headers_in, "If-None-Match") || + apr_table_get(r->headers_in, "If-Modified-Since") || + apr_table_get(r->headers_in, "If-Unmodified-Since")) { + + return 1; + } + return 0; +} + + +/* remove other filters from filter stack */ +void ap_cache_reset_output_filters(request_rec *r) +{ + ap_filter_t *f = r->output_filters; + + while (f) { + if (!strcasecmp(f->frec->name, "CORE") || + !strcasecmp(f->frec->name, "CONTENT_LENGTH") || + !strcasecmp(f->frec->name, "HTTP_HEADER")) { + f = f->next; + continue; + } + else { + ap_remove_output_filter(f); + f = f->next; + } + } +} + +const char *ap_cache_get_cachetype(request_rec *r, cache_server_conf *conf, const char *url) +{ + const char *type = NULL; + int i; + + /* loop through all the cacheenable entries */ + for (i = 0; i < conf->cacheenable->nelts; i++) { + struct cache_enable *ent = (struct cache_enable *) conf->cacheenable->elts; + const char *thisurl = ent[i].url; + const char *thistype = ent[i].type; + if ((thisurl) && !strncasecmp(thisurl, url, strlen(thisurl))) { + if (!type) { + type = thistype; + } + else { + type = apr_pstrcat(r->pool, type, ",", thistype, NULL); + } + } + } + + /* then loop through all the cachedisable entries */ + for (i = 0; i < conf->cachedisable->nelts; i++) { + struct cache_disable *ent = (struct cache_disable *) conf->cachedisable->elts; + const char *thisurl = ent[i].url; + if ((thisurl) && !strncasecmp(thisurl, url, strlen(thisurl))) { + type = NULL; + } + } + + return type; +} + +/* + * list is a comma-separated list of case-insensitive tokens, with + * optional whitespace around the tokens. + * The return returns 1 if the token val is found in the list, or 0 + * otherwise. + */ +int ap_cache_liststr(const char *list, const char *key, char **val) +{ + int len, i; + char *p; + char valbuf[HUGE_STRING_LEN]; + valbuf[sizeof(valbuf)-1] = 0; /* safety terminating zero */ + + len = strlen(key); + + while (list != NULL) { + p = strchr((char *) list, ','); + if (p != NULL) { + i = p - list; + do + p++; + while (ap_isspace(*p)); + } + else + i = strlen(list); + + while (i > 0 && ap_isspace(list[i - 1])) + i--; + if (i == len && strncasecmp(list, key, len) == 0) { + if (val) { + p = strchr((char *) list, ','); + while (ap_isspace(*list)) { + list++; + } + if ('=' == list[0]) + list++; + while (ap_isspace(*list)) { + list++; + } + strncpy(valbuf, list, MIN(p-list, sizeof(valbuf)-1)); + *val = valbuf; + } + return 1; + } + list = p; + } + return 0; +} + +/* return each comma separated token, one at a time */ +const char *ap_cache_tokstr(apr_pool_t *p, const char *list, const char **str) +{ + apr_off_t len, i; + const char *s; + + s = ap_strchr_c(list, ','); + if (s != NULL) { + i = s - list; + do + s++; + while (apr_isspace(*s)); + } + else + i = strlen(list); + + while (i > 0 && apr_isspace(list[i - 1])) + i--; + + *str = s; + if (len) + return apr_pstrndup(p, list, len); + else + return NULL; + +} diff --git a/modules/experimental/mod_auth_ldap.c b/modules/experimental/mod_auth_ldap.c new file mode 100644 index 0000000000..6be891cf17 --- /dev/null +++ b/modules/experimental/mod_auth_ldap.c @@ -0,0 +1,862 @@ +/* ==================================================================== + * The Apache Software License, Version 1.1 + * + * Copyright (c) 2000-2001 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 + * <http://www.apache.org/>. + */ + +/* + * mod_auth_ldap.c: LDAP authentication module + * + * Original code from auth_ldap module for Apache v1.3: + * Copyright 1998, 1999 Enbridge Pipelines Inc. + * Copyright 1999-2001 Dave Carrigan + */ + +#include <apr_ldap.h> +#include <apr_strings.h> + +#include "ap_config.h" +#if APR_HAVE_UNISTD_H +/* for getpid() */ +#include <unistd.h> +#endif +#include <ctype.h> + +#include "httpd.h" +#include "http_config.h" +#include "http_core.h" +#include "http_log.h" +#include "http_protocol.h" +#include "http_request.h" +#include "util_ldap.h" + +/* per directory configuration */ +typedef struct { + apr_pool_t *pool; /* Pool that this config is allocated from */ + apr_lock_t *lock; /* Lock for this config */ + int auth_authoritative; /* Is this auth method the one and only? */ + int enabled; /* Is auth_ldap enabled in this directory? */ + + /* These parameters are all derived from the AuthLDAPURL directive */ + char *url; /* String representation of the URL */ + + char *host; /* Name of the LDAP server (or space separated list) */ + int port; /* Port of the LDAP server */ + char *basedn; /* Base DN to do all searches from */ + char *attribute; /* Attribute to search for */ + char **attributes; /* Array of all the attributes to return */ + int scope; /* Scope of the search */ + char *filter; /* Filter to further limit the search */ + deref_options deref; /* how to handle alias dereferening */ + char *binddn; /* DN to bind to server (can be NULL) */ + char *bindpw; /* Password to bind to server (can be NULL) */ + + int frontpage_hack; /* Hack for frontpage support */ + int user_is_dn; /* If true, connection->user is DN instead of userid */ + int compare_dn_on_server; /* If true, will use server to do DN compare */ + + int have_ldap_url; /* Set if we have found an LDAP url */ + + apr_array_header_t *groupattr; /* List of Group attributes */ + int group_attrib_is_dn; /* If true, the group attribute is the DN, otherwise, + it's the exact string passed by the HTTP client */ + + int netscapessl; /* True if Netscape SSL is enabled */ + int starttls; /* True if StartTLS is enabled */ +} mod_auth_ldap_config_t; + +typedef struct mod_auth_ldap_request_t { + char *dn; /* The saved dn from a successful search */ + char *user; /* The username provided by the client */ +} mod_auth_ldap_request_t; + +/* maximum group elements supported */ +#define GROUPATTR_MAX_ELTS 10 + +struct mod_auth_ldap_groupattr_entry_t { + char *name; +}; + +module AP_MODULE_DECLARE_DATA auth_ldap_module; + +/* function prototypes */ +void mod_auth_ldap_build_filter(char *filtbuf, + request_rec *r, + mod_auth_ldap_config_t *sec); +int mod_auth_ldap_check_user_id(request_rec *r); +int mod_auth_ldap_auth_checker(request_rec *r); +void *mod_auth_ldap_create_dir_config(apr_pool_t *p, char *d); + +/* ---------------------------------------- */ + + +/* + * Build the search filter, or at least as much of the search filter that + * will fit in the buffer. We don't worry about the buffer not being able + * to hold the entire filter. If the buffer wasn't big enough to hold the + * filter, ldap_search_s will complain, but the only situation where this + * is likely to happen is if the client sent a really, really long + * username, most likely as part of an attack. + * + * The search filter consists of the filter provided with the URL, + * combined with a filter made up of the attribute provided with the URL, + * and the actual username passed by the HTTP client. For example, assume + * that the LDAP URL is + * + * ldap://ldap.airius.com/ou=People, o=Airius?uid??(posixid=*) + * + * Further, assume that the userid passed by the client was `userj'. The + * search filter will be (&(posixid=*)(uid=userj)). + */ +#define FILTER_LENGTH MAX_STRING_LEN +void mod_auth_ldap_build_filter(char *filtbuf, + request_rec *r, + mod_auth_ldap_config_t *sec) +{ + char *p, *q, *filtbuf_end; + /* + * Create the first part of the filter, which consists of the + * config-supplied portions. + */ + apr_snprintf(filtbuf, FILTER_LENGTH, "(&(%s)(%s=", sec->filter, sec->attribute); + + /* + * Now add the client-supplied username to the filter, ensuring that any + * LDAP filter metachars are escaped. + */ + filtbuf_end = filtbuf + FILTER_LENGTH - 1; + for (p = r->user, q=filtbuf + strlen(filtbuf); + *p && q < filtbuf_end; *q++ = *p++) { + if (strchr("*()\\", *p) != NULL) { + *q++ = '\\'; + if (q >= filtbuf_end) { + break; + } + } + } + *q = '\0'; + + /* + * Append the closing parens of the filter, unless doing so would + * overrun the buffer. + */ + if (q + 2 <= filtbuf_end) + strcat(filtbuf, "))"); +} + + +/* + * Authentication Phase + * -------------------- + * + * This phase authenticates the credentials the user has sent with + * the request (ie the username and password are checked). This is done + * by making an attempt to bind to the LDAP server using this user's + * DN and the supplied password. + * + */ +int mod_auth_ldap_check_user_id(request_rec *r) +{ + const char **vals = NULL; + char filtbuf[FILTER_LENGTH]; + mod_auth_ldap_config_t *sec = + (mod_auth_ldap_config_t *)ap_get_module_config(r->per_dir_config, &auth_ldap_module); + + util_ldap_connection_t *ldc = NULL; + const char *sent_pw; + int result = 0; + const char *dn = NULL; + + mod_auth_ldap_request_t *req = + (mod_auth_ldap_request_t *)apr_pcalloc(r->pool, sizeof(mod_auth_ldap_request_t)); + ap_set_module_config(r->request_config, &auth_ldap_module, req); + + if (!sec->enabled) { + return DECLINED; + } + + /* + * Basic sanity checks before any LDAP operations even happen. + */ + if (!sec->have_ldap_url) { + return DECLINED; + } + + /* There is a good AuthLDAPURL, right? */ + if (sec->host) { + ldc = util_ldap_connection_find(r, sec->host, sec->port, + sec->binddn, sec->bindpw, sec->deref, + sec->netscapessl, sec->starttls); + } + else { + ap_log_rerror(APLOG_MARK, APLOG_WARNING|APLOG_NOERRNO, 0, r, + "[%d] auth_ldap authenticate: no sec->host - weird...?", getpid()); + return sec->auth_authoritative? HTTP_UNAUTHORIZED : DECLINED; + } + + ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r, + "[%d] auth_ldap authenticate: using URL %s", getpid(), sec->url); + + /* Get the password that the client sent */ + if ((result = ap_get_basic_auth_pw(r, &sent_pw))) { + ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r, + "[%d] auth_ldap authenticate: " + "ap_get_basic_auth_pw() returns %d", getpid(), result); + util_ldap_connection_close(ldc); + return result; + } + + /* build the username filter */ + mod_auth_ldap_build_filter(filtbuf, r, sec); + + /* do the user search */ + result = util_ldap_cache_checkuserid(r, ldc, sec->url, sec->basedn, sec->scope, + sec->attributes, filtbuf, sent_pw, &dn, &vals); + util_ldap_connection_close(ldc); + + if (result != LDAP_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_WARNING|APLOG_NOERRNO, 0, r, + "[%d] auth_ldap authenticate: " + "user %s authentication failed; URI %s [%s][%s]", + getpid(), r->user, r->uri, ldc->reason, ldap_err2string(result)); + if (LDAP_INVALID_CREDENTIALS == result) { + ap_note_basic_auth_failure(r); + return HTTP_UNAUTHORIZED; + } + else { + return sec->auth_authoritative? HTTP_UNAUTHORIZED: DECLINED; + } + } + + /* mark the user and DN */ + req->dn = apr_pstrdup(r->pool, dn); + req->user = r->user; + if (sec->user_is_dn) { + r->user = req->dn; + } + + /* add environment variables */ + if (sec->attributes && vals) { + apr_table_t *e = r->subprocess_env; + int i = 0; + while (sec->attributes[i]) { + char *str = apr_pstrcat(r->pool, "AUTHENTICATE_", sec->attributes[i], NULL); + int j = 13; + while (str[j]) { + if (str[j] >= 'a' && str[j] <= 'z') { + str[j] = str[j] - ('a' - 'A'); + } + j++; + } + apr_table_setn(e, str, vals[i]); + i++; + } + } + + ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r, + "[%d] auth_ldap authenticate: accepting %s", getpid(), r->user); + + return OK; +} + + +/* + * Authorisation Phase + * ------------------- + * + * After checking whether the username and password are correct, we need + * to check whether that user is authorised to view this resource. The + * require directive is used to do this: + * + * require valid-user Any authenticated is allowed in. + * require user <username> This particular user is allowed in. + * require group <groupname> The user must be a member of this group + * in order to be allowed in. + * require dn <dn> The user must have the following DN in the + * LDAP tree to be let in. + * + */ +int mod_auth_ldap_auth_checker(request_rec *r) +{ + int result = 0; + mod_auth_ldap_request_t *req = + (mod_auth_ldap_request_t *)ap_get_module_config(r->request_config, + &auth_ldap_module); + mod_auth_ldap_config_t *sec = + (mod_auth_ldap_config_t *)ap_get_module_config(r->per_dir_config, + &auth_ldap_module); + + util_ldap_connection_t *ldc = NULL; + int m = r->method_number; + + const apr_array_header_t *reqs_arr = ap_requires(r); + require_line *reqs = reqs_arr ? (require_line *)reqs_arr->elts : NULL; + + register int x; + const char *t; + char *w; + int method_restricted = 0; + + if (!sec->enabled) { + return DECLINED; + } + + if (!sec->have_ldap_url) { + return DECLINED; + } + + if (sec->host) { + ldc = util_ldap_connection_find(r, sec->host, sec->port, + sec->binddn, sec->bindpw, sec->deref, + sec->netscapessl, sec->starttls); + } + else { + ap_log_rerror(APLOG_MARK, APLOG_WARNING|APLOG_NOERRNO, 0, r, + "[%d] auth_ldap authorise: no sec->host - weird...?", getpid()); + return sec->auth_authoritative? HTTP_UNAUTHORIZED : DECLINED; + } + + /* + * If there are no elements in the group attribute array, the default should be + * member and uniquemember; populate the array now. + */ + if (sec->groupattr->nelts == 0) { + struct mod_auth_ldap_groupattr_entry_t *grp; + apr_lock_acquire(sec->lock); + grp = apr_array_push(sec->groupattr); + grp->name = "member"; + grp = apr_array_push(sec->groupattr); + grp->name = "uniquemember"; + apr_lock_release(sec->lock); + } + + if (!reqs_arr) { + ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r, + "[%d] auth_ldap authorise: no requirements array", getpid()); + return sec->auth_authoritative? HTTP_UNAUTHORIZED : DECLINED; + } + + /* Loop through the requirements array until there's no elements + * left, or something causes a return from inside the loop */ + for(x=0; x < reqs_arr->nelts; x++) { + if (! (reqs[x].method_mask & (1 << m))) { + continue; + } + method_restricted = 1; + + t = reqs[x].requirement; + w = ap_getword(r->pool, &t, ' '); + + if (strcmp(w, "valid-user") == 0) { + /* + * Valid user will always be true if we authenticated with ldap, + * but when using front page, valid user should only be true if + * he exists in the frontpage password file. This hack will get + * auth_ldap to look up the user in the the pw file to really be + * sure that he's valid. Naturally, it requires mod_auth to be + * compiled in, but if mod_auth wasn't in there, then the need + * for this hack wouldn't exist anyway. + */ + if (sec->frontpage_hack) { + ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r, + "[%d] auth_ldap authorise: " + "deferring authorisation to mod_auth (FP Hack)", + getpid()); + return OK; + } + else { + ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r, + "[%d] auth_ldap authorise: " + "successful authorisation because user " + "is valid-user", getpid()); + return OK; + } + } + else if (strcmp(w, "user") == 0) { + if (req->dn == NULL || strlen(req->dn) == 0) { + ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r, + "[%d] auth_ldap authorise: " + "require user: user's DN has not been defined; failing authorisation", + getpid()); + return sec->auth_authoritative? HTTP_UNAUTHORIZED : DECLINED; + } + /* + * First do a whole-line compare, in case it's something like + * require user Babs Jensen + */ + result = util_ldap_cache_compare(r, ldc, sec->url, req->dn, sec->attribute, t); + switch(result) { + case LDAP_COMPARE_TRUE: { + ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r, + "[%d] auth_ldap authorise: " + "require user: authorisation successful", getpid()); + return OK; + } + default: { + ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r, + "[%d] auth_ldap authorise: require user: " + "authorisation failed [%s][%s]", getpid(), + ldc->reason, ldap_err2string(result)); + } + } + /* + * Now break apart the line and compare each word on it + */ + while (t[0]) { + w = ap_getword_conf(r->pool, &t); + result = util_ldap_cache_compare(r, ldc, sec->url, req->dn, sec->attribute, w); + switch(result) { + case LDAP_COMPARE_TRUE: { + ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r, + "[%d] auth_ldap authorise: " + "require user: authorisation successful", getpid()); + return OK; + } + default: { + ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r, + "[%d] auth_ldap authorise: " + "require user: authorisation failed [%s][%s]", + getpid(), ldc->reason, ldap_err2string(result)); + } + } + } + } + else if (strcmp(w, "dn") == 0) { + if (req->dn == NULL || strlen(req->dn) == 0) { + ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r, + "[%d] auth_ldap authorise: " + "require dn: user's DN has not been defined; failing authorisation", + getpid()); + return sec->auth_authoritative? HTTP_UNAUTHORIZED : DECLINED; + } + + result = util_ldap_cache_comparedn(r, ldc, sec->url, req->dn, t, sec->compare_dn_on_server); + switch(result) { + case LDAP_COMPARE_TRUE: { + ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r, + "[%d] auth_ldap authorise: " + "require dn: authorisation successful", getpid()); + return OK; + } + default: { + ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r, + "[%d] auth_ldap authorise: " + "require dn: LDAP error [%s][%s]", + getpid(), ldc->reason, ldap_err2string(result)); + } + } + } + else if (strcmp(w, "group") == 0) { + struct mod_auth_ldap_groupattr_entry_t *ent = (struct mod_auth_ldap_groupattr_entry_t *) sec->groupattr->elts; + int i; + + if (sec->group_attrib_is_dn) { + if (req->dn == NULL || strlen(req->dn) == 0) { + ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r, + "[%d] auth_ldap authorise: require group: user's DN has not been defined; failing authorisation", + getpid()); + return sec->auth_authoritative? HTTP_UNAUTHORIZED : DECLINED; + } + } + else { + if (req->user == NULL || strlen(req->user) == 0) { + /* We weren't called in the authentication phase, so we didn't have a + * chance to set the user field. Do so now. */ + req->user = r->user; + } + } + + ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r, + "[%d] auth_ldap authorise: require group: testing for group membership in `%s'", + getpid(), t); + + for (i = 0; i < sec->groupattr->nelts; i++) { + ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r, + "[%d] auth_ldap authorise: require group: testing for %s: %s (%s)", getpid(), + ent[i].name, sec->group_attrib_is_dn ? req->dn : req->user, t); + + result = util_ldap_cache_compare(r, ldc, sec->url, t, ent[i].name, + sec->group_attrib_is_dn ? req->dn : req->user); + switch(result) { + case LDAP_COMPARE_TRUE: { + ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r, + "[%d] auth_ldap authorise: require group: " + "authorisation successful (attribute %s) [%s][%s]", + getpid(), ent[i].name, ldc->reason, ldap_err2string(result)); + return OK; + } + default: { + ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r, + "[%d] auth_ldap authorise: require group: " + "authorisation failed [%s][%s]", + getpid(), ldc->reason, ldap_err2string(result)); + } + } + } + } + } + + if (!method_restricted) { + ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r, + "[%d] auth_ldap authorise: agreeing because non-restricted", + getpid()); + return OK; + } + + if (!sec->auth_authoritative) { + ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r, + "[%d] auth_ldap authorise: declining to authorise", getpid()); + return DECLINED; + } + + ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, r, + "[%d] auth_ldap authorise: authorisation denied", getpid()); + ap_note_basic_auth_failure (r); + + return HTTP_UNAUTHORIZED; +} + + +/* ---------------------------------------- */ +/* config directives */ + + +void *mod_auth_ldap_create_dir_config(apr_pool_t *p, char *d) +{ + mod_auth_ldap_config_t *sec = + (mod_auth_ldap_config_t *)apr_pcalloc(p, sizeof(mod_auth_ldap_config_t)); + + sec->pool = p; + apr_lock_create(&sec->lock, APR_MUTEX, APR_INTRAPROCESS, NULL, p); + sec->auth_authoritative = 1; + sec->enabled = 1; + sec->groupattr = apr_array_make(p, GROUPATTR_MAX_ELTS, + sizeof(struct mod_auth_ldap_groupattr_entry_t)); + + sec->have_ldap_url = 0; + sec->url = ""; + sec->host = NULL; + sec->binddn = NULL; + sec->bindpw = NULL; + sec->deref = always; + sec->group_attrib_is_dn = 1; + + sec->frontpage_hack = 0; + sec->netscapessl = 0; + sec->starttls = 0; + + sec->user_is_dn = 0; + sec->compare_dn_on_server = 0; + + return sec; +} + +/* + * Use the ldap url parsing routines to break up the ldap url into + * host and port. + */ +static const char *mod_auth_ldap_parse_url(cmd_parms *cmd, + void *config, + const char *url) +{ + int result; + LDAPURLDesc *urld; + + mod_auth_ldap_config_t *sec = config; + + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, + cmd->server, "[%d] auth_ldap url parse: `%s'", + getpid(), url); + + result = ldap_url_parse(url, &(urld)); + if (result != LDAP_SUCCESS) { + switch (result) { + case LDAP_URL_ERR_NOTLDAP: + return "LDAP URL does not begin with ldap://"; + case LDAP_URL_ERR_NODN: + return "LDAP URL does not have a DN"; + case LDAP_URL_ERR_BADSCOPE: + return "LDAP URL has an invalid scope"; + case LDAP_URL_ERR_MEM: + return "Out of memory parsing LDAP URL"; + default: + return "Could not parse LDAP URL"; + } + } + sec->url = apr_pstrdup(cmd->pool, url); + + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, + cmd->server, "[%d] auth_ldap url parse: Host: %s", getpid(), urld->lud_host); + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, + cmd->server, "[%d] auth_ldap url parse: Port: %d", getpid(), urld->lud_port); + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, + cmd->server, "[%d] auth_ldap url parse: DN: %s", getpid(), urld->lud_dn); + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, + cmd->server, "[%d] auth_ldap url parse: attrib: %s", getpid(), urld->lud_attrs? urld->lud_attrs[0] : "(null)"); + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, + cmd->server, "[%d] auth_ldap url parse: scope: %s", getpid(), + (urld->lud_scope == LDAP_SCOPE_SUBTREE? "subtree" : + urld->lud_scope == LDAP_SCOPE_BASE? "base" : + urld->lud_scope == LDAP_SCOPE_ONELEVEL? "onelevel" : "unknown")); + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, + cmd->server, "[%d] auth_ldap url parse: filter: %s", getpid(), urld->lud_filter); + + /* Set all the values, or at least some sane defaults */ + if (sec->host) { + char *p = apr_palloc(cmd->pool, strlen(sec->host) + strlen(urld->lud_host) + 2); + strcpy(p, urld->lud_host); + strcat(p, " "); + strcat(p, sec->host); + sec->host = p; + } + else { + sec->host = urld->lud_host? apr_pstrdup(cmd->pool, urld->lud_host) : "localhost"; + } + sec->basedn = urld->lud_dn? apr_pstrdup(cmd->pool, urld->lud_dn) : ""; + if (urld->lud_attrs && urld->lud_attrs[0]) { + int i = 1; + while (urld->lud_attrs[i]) { + i++; + } + sec->attributes = apr_pcalloc(cmd->pool, sizeof(char *) * (i+1)); + i = 0; + while (urld->lud_attrs[i]) { + sec->attributes[i] = apr_pstrdup(cmd->pool, urld->lud_attrs[i]); + i++; + } + sec->attribute = sec->attributes[0]; + } + else { + sec->attribute = "uid"; + } + + sec->scope = urld->lud_scope == LDAP_SCOPE_ONELEVEL ? + LDAP_SCOPE_ONELEVEL : LDAP_SCOPE_SUBTREE; + + if (urld->lud_filter) { + if (urld->lud_filter[0] == '(') { + /* + * Get rid of the surrounding parens; later on when generating the + * filter, they'll be put back. + */ + sec->filter = apr_pstrdup(cmd->pool, urld->lud_filter+1); + sec->filter[strlen(sec->filter)-1] = '\0'; + } + else { + sec->filter = apr_pstrdup(cmd->pool, urld->lud_filter); + } + } + else { + sec->filter = "objectclass=*"; + } + if (strncmp(url, "ldaps", 5) == 0) { + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, + cmd->server, "[%d] auth_ldap parse url: requesting secure LDAP", getpid()); +#ifdef APU_HAS_LDAP_STARTTLS + sec->port = urld->lud_port? urld->lud_port : LDAPS_PORT; + sec->starttls = 1; +#else +#ifdef APU_HAS_LDAP_NETSCAPE_SSL + sec->port = urld->lud_port? urld->lud_port : LDAPS_PORT; + sec->netscapessl = 1; +#else + return "Secure LDAP (ldaps://) not supported. Rebuild APR-Util"; +#endif +#endif + } + else { + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, + cmd->server, "[%d] auth_ldap parse url: not requesting secure LDAP", getpid()); + sec->netscapessl = 0; + sec->starttls = 0; + sec->port = urld->lud_port? urld->lud_port : LDAP_PORT; + } + + sec->have_ldap_url = 1; + ldap_free_urldesc(urld); + return NULL; +} + +static const char *mod_auth_ldap_set_deref(cmd_parms *cmd, void *config, const char *arg) +{ + mod_auth_ldap_config_t *sec = config; + + if (strcmp(arg, "never") == 0 || strcasecmp(arg, "off") == 0) { + sec->deref = never; + } + else if (strcmp(arg, "searching") == 0) { + sec->deref = searching; + } + else if (strcmp(arg, "finding") == 0) { + sec->deref = finding; + } + else if (strcmp(arg, "always") == 0 || strcasecmp(arg, "on") == 0) { + sec->deref = always; + } + else { + return "Unrecognized value for AuthLDAPAliasDereference directive"; + } + return NULL; +} + +static const char *mod_auth_ldap_add_group_attribute(cmd_parms *cmd, void *config, const char *arg) +{ + struct mod_auth_ldap_groupattr_entry_t *new; + + mod_auth_ldap_config_t *sec = config; + + if (sec->groupattr->nelts > GROUPATTR_MAX_ELTS) + return "Too many AuthLDAPGroupAttribute directives"; + + new = apr_array_push(sec->groupattr); + new->name = apr_pstrdup(cmd->pool, arg); + + return NULL; +} + +command_rec mod_auth_ldap_cmds[] = { + AP_INIT_TAKE1("AuthLDAPURL", mod_auth_ldap_parse_url, NULL, OR_AUTHCFG, + "URL to define LDAP connection. This should be an RFC 2255 complaint\n" + "URL of the form ldap://host[:port]/basedn[?attrib[?scope[?filter]]].\n" + "<ul>\n" + "<li>Host is the name of the LDAP server. Use a space separated list of hosts \n" + "to specify redundant servers.\n" + "<li>Port is optional, and specifies the port to connect to.\n" + "<li>basedn specifies the base DN to start searches from\n" + "<li>Attrib specifies what attribute to search for in the directory. If not " + "provided, it defaults to <b>uid</b>.\n" + "<li>Scope is the scope of the search, and can be either <b>sub</b> or " + "<b>one</b>. If not provided, the default is <b>sub</b>.\n" + "<li>Filter is a filter to use in the search. If not provided, " + "defaults to <b>(objectClass=*)</b>.\n" + "</ul>\n" + "Searches are performed using the attribute and the filter combined. " + "For example, assume that the\n" + "LDAP URL is <b>ldap://ldap.airius.com/ou=People, o=Airius?uid?sub?(posixid=*)</b>. " + "Searches will\n" + "be done using the filter <b>(&((posixid=*))(uid=<i>username</i>))</b>, " + "where <i>username</i>\n" + "is the user name passed by the HTTP client. The search will be a subtree " + "search on the branch <b>ou=People, o=Airius</b>."), + + AP_INIT_TAKE1("AuthLDAPBindDN", ap_set_string_slot, + (void *)APR_XtOffsetOf(mod_auth_ldap_config_t, binddn), OR_AUTHCFG, + "DN to use to bind to LDAP server. If not provided, will do an anonymous bind."), + + AP_INIT_TAKE1("AuthLDAPBindPassword", ap_set_string_slot, + (void *)APR_XtOffsetOf(mod_auth_ldap_config_t, bindpw), OR_AUTHCFG, + "Password to use to bind to LDAP server. If not provided, will do an anonymous bind."), + + AP_INIT_FLAG("AuthLDAPRemoteUserIsDN", ap_set_flag_slot, + (void *)APR_XtOffsetOf(mod_auth_ldap_config_t, user_is_dn), OR_AUTHCFG, + "Set to 'on' to set the REMOTE_USER environment variable to be the full " + "DN of the remote user. By default, this is set to off, meaning that " + "the REMOTE_USER variable will contain whatever value the remote user sent."), + + AP_INIT_FLAG("AuthLDAPAuthoritative", ap_set_flag_slot, + (void *)APR_XtOffsetOf(mod_auth_ldap_config_t, auth_authoritative), OR_AUTHCFG, + "Set to 'off' to allow access control to be passed along to lower modules if " + "the UserID and/or group is not known to this module"), + + AP_INIT_FLAG("AuthLDAPCompareDNOnServer", ap_set_flag_slot, + (void *)APR_XtOffsetOf(mod_auth_ldap_config_t, compare_dn_on_server), OR_AUTHCFG, + "Set to 'on' to force auth_ldap to do DN compares (for the \"require dn\" " + "directive) using the server, and set it 'off' to do the compares locally " + "(at the expense of possible false matches). See the documentation for " + "a complete description of this option."), + + AP_INIT_ITERATE("AuthLDAPGroupAttribute", mod_auth_ldap_add_group_attribute, NULL, OR_AUTHCFG, + "A list of attributes used to define group membership - defaults to " + "member and uniquemember"), + + AP_INIT_FLAG("AuthLDAPGroupAttributeIsDN", ap_set_flag_slot, + (void *)APR_XtOffsetOf(mod_auth_ldap_config_t, group_attrib_is_dn), OR_AUTHCFG, + "If set to 'on', auth_ldap uses the DN that is retrieved from the server for" + "subsequent group comparisons. If set to 'off', auth_ldap uses the string" + "provided by the client directly. Defaults to 'on'."), + + AP_INIT_TAKE1("AuthLDAPDereferenceAliases", mod_auth_ldap_set_deref, NULL, OR_AUTHCFG, + "Determines how aliases are handled during a search. Can bo one of the" + "values \"never\", \"searching\", \"finding\", or \"always\". " + "Defaults to always."), + + AP_INIT_FLAG("AuthLDAPEnabled", ap_set_flag_slot, + (void *)APR_XtOffsetOf(mod_auth_ldap_config_t, enabled), OR_AUTHCFG, + "Set to off to disable auth_ldap, even if it's been enabled in a higher tree"), + + AP_INIT_FLAG("AuthLDAPFrontPageHack", ap_set_flag_slot, + (void *)APR_XtOffsetOf(mod_auth_ldap_config_t, frontpage_hack), OR_AUTHCFG, + "Set to 'on' to support Microsoft FrontPage"), + +#ifdef APU_HAS_LDAP_STARTTLS + AP_INIT_FLAG("AuthLDAPStartTLS", ap_set_flag_slot, + (void *)APR_XtOffsetOf(mod_auth_ldap_config_t, starttls), OR_AUTHCFG, + "Set to 'on' to start TLS after connecting to the LDAP server."), +#endif /* APU_HAS_LDAP_STARTTLS */ + + {NULL} +}; + +static void mod_auth_ldap_register_hooks(apr_pool_t *p) +{ + ap_hook_check_user_id(mod_auth_ldap_check_user_id, NULL, NULL, APR_HOOK_MIDDLE); + ap_hook_auth_checker(mod_auth_ldap_auth_checker, NULL, NULL, APR_HOOK_MIDDLE); +} + +module auth_ldap_module = { + STANDARD20_MODULE_STUFF, + mod_auth_ldap_create_dir_config, /* dir config creater */ + NULL, /* dir merger --- default is to override */ + NULL, /* server config */ + NULL, /* merge server config */ + mod_auth_ldap_cmds, /* command table */ + mod_auth_ldap_register_hooks, /* set up request processing hooks */ +}; diff --git a/modules/experimental/mod_cache.dsp b/modules/experimental/mod_cache.dsp new file mode 100644 index 0000000000..f62455d62d --- /dev/null +++ b/modules/experimental/mod_cache.dsp @@ -0,0 +1,123 @@ +# Microsoft Developer Studio Project File - Name="mod_cache" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=mod_cache - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "mod_cache.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "mod_cache.mak" CFG="mod_cache - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "mod_cache - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "mod_cache - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "mod_cache - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "MOD_CACHE_EXPORTS" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /O2 /I "../../srclib/apr-util/include" /I "../../srclib/apr/include" /I "../../include" /I "../../os/win32" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "CACHE_DECLARE_EXPORT" /D "MOD_CACHE_EXPORTS" /YX /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 /out:"Release/mod_cache.so" + +!ELSEIF "$(CFG)" == "mod_cache - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "MOD_CACHE_EXPORTS" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "../../srclib/apr-util/include" /I "../../srclib/apr/include" /I "../../include" /I "../../os/win32" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "CACHE_DECLARE_EXPORT" /D "MOD_CACHE_EXPORTS" /YX /FD /GZ /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /out:"Debug/mod_cache.so" /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "mod_cache - Win32 Release" +# Name "mod_cache - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\cache_storage.c +# End Source File +# Begin Source File + +SOURCE=.\cache_util.c +# End Source File +# Begin Source File + +SOURCE=.\mod_cache.c +# End Source File +# Begin Source File + +SOURCE=.\mod_mem_cache.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\mod_cache.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/modules/experimental/mod_case_filter_in.c b/modules/experimental/mod_case_filter_in.c new file mode 100644 index 0000000000..e4ab97feab --- /dev/null +++ b/modules/experimental/mod_case_filter_in.c @@ -0,0 +1,131 @@ +// Ben messing around some more... + +#include "httpd.h" +#include "http_config.h" +#include "apr_general.h" +#include "util_filter.h" +#include "apr_buckets.h" +#include "http_request.h" + +#include <ctype.h> + +static const char s_szCaseFilterName[]="CaseFilter"; +module case_filter_in_module; + +typedef struct +{ + int bEnabled; +} CaseFilterInConfig; + +typedef struct +{ + apr_bucket_brigade *pbbTmp; +} CaseFilterInContext; + +static void *CaseFilterInCreateServerConfig(apr_pool_t *p,server_rec *s) +{ + CaseFilterInConfig *pConfig=apr_pcalloc(p,sizeof *pConfig); + + pConfig->bEnabled=0; + + return pConfig; +} + +static void CaseFilterInInsertFilter(request_rec *r) +{ + CaseFilterInConfig *pConfig=ap_get_module_config(r->server->module_config, + &case_filter_in_module); + CaseFilterInContext *pCtx; + + if(!pConfig->bEnabled) + return; + + pCtx=apr_palloc(r->pool,sizeof *pCtx); + pCtx->pbbTmp=apr_brigade_create(r->pool); + ap_add_input_filter(s_szCaseFilterName,pCtx,r,NULL); +} + +static apr_status_t CaseFilterInFilter(ap_filter_t *f, + apr_bucket_brigade *pbbOut, + ap_input_mode_t eMode,apr_size_t nBytes) +{ + CaseFilterInContext *pCtx=f->ctx; + apr_status_t ret; + + ap_assert(APR_BRIGADE_EMPTY(pCtx->pbbTmp)); + + ret=ap_get_brigade(f->next,pCtx->pbbTmp,eMode,nBytes); + if(eMode == AP_MODE_PEEK || ret != APR_SUCCESS) + return ret; + + while(!APR_BRIGADE_EMPTY(pCtx->pbbTmp)) { + apr_bucket *pbktIn=APR_BRIGADE_FIRST(pCtx->pbbTmp); + apr_bucket *pbktOut; + const char *data; + apr_size_t len; + char *buf; + int n; + + // It is tempting to do this... + //APR_BUCKET_REMOVE(pB); + //APR_BRIGADE_INSERT_TAIL(pbbOut,pB); + // and change the case of the bucket data, but that would be wrong + // for a file or socket buffer, for example... + + if(APR_BUCKET_IS_EOS(pbktIn)) { + APR_BUCKET_REMOVE(pbktIn); + APR_BRIGADE_INSERT_TAIL(pbbOut,pbktIn); + break; + } + + ret=apr_bucket_read(pbktIn,&data,&len,eMode); + if(ret != APR_SUCCESS) + return ret; + + buf=malloc(len); + for(n=0 ; n < len ; ++n) + buf[n]=toupper(data[n]); + + pbktOut=apr_bucket_heap_create(buf,len,0,NULL); + APR_BRIGADE_INSERT_TAIL(pbbOut,pbktOut); + apr_bucket_delete(pbktIn); + } + + return APR_SUCCESS; +} + + +static const char *CaseFilterEnable(cmd_parms *cmd, void *dummy, int arg) + { + CaseFilterInConfig *pConfig + =ap_get_module_config(cmd->server->module_config,&case_filter_in_module); + pConfig->bEnabled=arg; + + return NULL; + } + +static const command_rec CaseFilterInCmds[] = + { + AP_INIT_FLAG("CaseFilterIn", CaseFilterEnable, NULL, RSRC_CONF, + "Run an input case filter on this host"), + { NULL } + }; + + +static void CaseFilterInRegisterHooks(apr_pool_t *p) + { + ap_hook_insert_filter(CaseFilterInInsertFilter,NULL,NULL,APR_HOOK_MIDDLE); + ap_register_input_filter(s_szCaseFilterName,CaseFilterInFilter, + AP_FTYPE_CONTENT); + } + +module case_filter_in_module = +{ + STANDARD20_MODULE_STUFF, + NULL, + NULL, + CaseFilterInCreateServerConfig, + NULL, + CaseFilterInCmds, + CaseFilterInRegisterHooks +}; diff --git a/modules/experimental/mod_mem_cache.c b/modules/experimental/mod_mem_cache.c new file mode 100644 index 0000000000..2d2781a1a2 --- /dev/null +++ b/modules/experimental/mod_mem_cache.c @@ -0,0 +1,509 @@ +/* ==================================================================== + * The Apache Software License, Version 1.1 + * + * Copyright (c) 2000-2001 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 + * <http://www.apache.org/>. + * + * Portions of this software are based upon public domain software + * originally written at the National Center for Supercomputing Applications, + * University of Illinois, Urbana-Champaign. + */ + +#define CORE_PRIVATE + +#include "mod_cache.h" +#define MAX_CACHE 5000 +module AP_MODULE_DECLARE_DATA mem_cache_module; + +/* + * XXX + * This cache uses apr_hash functions which leak storage when something is removed + * from the cache. This can be fixed in the apr_hash functions by making them use + * malloc/free rather than pools to manage their storage requirements. + */ + +/* + * XXX Introduce a type field that identifies whether the cache obj + * references malloc'ed or mmap storage or a file descriptor + */ +typedef enum { + CACHE_TYPE_FILE = 1, + CACHE_TYPE_MALLOC, + CACHE_TYPE_MMAP +} cache_type_e; + +typedef struct { + cache_type_e type; + char *key; + void *m; + apr_size_t m_len; + cache_info info; + int complete; +} cache_object_t; + +typedef struct { + apr_lock_t *lock; + apr_hash_t *cacheht; + int space; + apr_time_t maxexpire; + apr_time_t defaultexpire; +} mem_cache_conf; +static mem_cache_conf *sconf; + +#define DEFAULT_CACHE_SPACE 100*1024 +#define CACHEFILE_LEN 20 + +/* Forward declarations */ +static int remove_entity(cache_handle *h); +static int write_headers(cache_handle *h, request_rec *r, cache_info *i); +static int write_body(cache_handle *h, apr_bucket_brigade *b); +static int read_headers(cache_handle *h, request_rec *r, cache_info **info); +static int read_body(cache_handle *h, apr_bucket_brigade *bb); + +static void cleanup_cache_object(cache_object_t *obj) +{ + /* The cache object has been removed from the cache. Now clean + * it up, freeing any storage, closing file descriptors, etc. + */ + /* XXX - + * The action of freeing a cache entry is asynchronous with the rest of + * the operation of the cache. Frees can be driven by garbage collection, + * the result of some command or an HTTP request. It is okay to remove + * an entry from the cache at anytime but we need a mechanism to keep + * us from cleaning up the cache entry out from under other threads + * that may still be referencing it. + * + * Bill thinks that we need a special purpose reference counted + * bucket (or three). When an entry is removed from the cache, the + * bucket for that entry is marked for cleanup. A bucket marked for + * cleanup is freed by the last routine referencing the bucket, + * either during brigade destroy or this routine. + */ + + /* + * Ref count decrementing and checking needs to be atomic + + obj->ref_count--; + if (obj->ref_count) { + defer_cleanup (let the brigade cleanup free the bucket) + } + else { + free the bucket + } + */ + + if (obj->info.content_type) + free(obj->info.content_type); + if (obj->key) + free(obj->key); + if (obj->m) + free(obj->m); + + free(obj); +} + +static apr_status_t cleanup_cache_mem(void *sconfv) +{ + cache_object_t *obj; + apr_hash_index_t *hi; + mem_cache_conf *co = (mem_cache_conf*) sconfv; + + if (!co) { + return APR_SUCCESS; + } + + /* Iterate over the frag hash table and clean up each entry */ + /* XXX need to lock the hash */ + for (hi = apr_hash_first(NULL, co->cacheht); hi; hi=apr_hash_next(hi)) { + apr_hash_this(hi, NULL, NULL, (void **)&obj); + if (obj) + cleanup_cache_object(obj); + } + return APR_SUCCESS; +} +static void *create_cache_config(apr_pool_t *p, server_rec *s) +{ + sconf = apr_pcalloc(p, sizeof(mem_cache_conf)); + sconf->space = DEFAULT_CACHE_SPACE; +#if 0 + sconf->maxexpire = DEFAULT_CACHE_MAXEXPIRE; + sconf->defaultexpire = DEFAULT_CACHE_EXPIRE; +#endif + + apr_lock_create(&sconf->lock, APR_MUTEX, APR_INTRAPROCESS, "foo", p); + sconf->cacheht = apr_hash_make(p); + apr_pool_cleanup_register(p, NULL, cleanup_cache_mem, apr_pool_cleanup_null); + + return sconf; +} + +static int create_entity(cache_handle **hp, const char *type, char *key, apr_size_t len) +{ + apr_status_t rv; + cache_object_t *obj; + cache_handle *h; + + /* Create the cache handle and begin populating it. + */ + if (strcasecmp(type, "mem")) { + return DECLINED; + } + + /* Check len to see if it is withing acceptable bounds + * XXX max cache check should be configurable variable. + */ + if (len < 0 || len > MAX_CACHE) { + return DECLINED; + } + /* Check total cache size and number of entries. Are they within the + * configured limits? If not, kick off garbage collection thread. + */ + + /* Allocate the cache_handle and set up call back functions specific to + * this cache handler. + */ + h = malloc(sizeof(cache_handle)); + *hp = h; + if (!h) { + /* handle the error */ + return DECLINED; + } + h->read_body = &read_body; + h->read_headers = &read_headers; + h->write_body = &write_body; + h->write_headers = &write_headers; + + /* Allocate and initialize the cache object. The cache object is + * unique to this implementation. + */ + obj = malloc(sizeof(*obj)); + if (!obj) { + /* Handle ther error */ + free(h); + return DECLINED; + } + + obj->key = malloc(strlen(key)); + if (!obj->key) { + /* XXX Uuugh, there has got to be a better way to manage memory. + */ + free(h); + free(obj); + return DECLINED; + } + obj->m_len = len; /* One of these len fields can go */ + obj->info.len = len; + strcpy(obj->key, key); + h->cache_obj = (void *) obj; + + /* Mark the cache object as incomplete and put it into the cache */ + obj->complete = 0; + + /* XXX Need a way to insert into the cache w/o sch coarse grained locking */ + apr_lock_acquire(sconf->lock); + apr_hash_set(sconf->cacheht, obj->key, strlen(obj->key), obj); + apr_lock_release(sconf->lock); +} + +static int open_entity(cache_handle **hp, const char *type, char *key) +{ + cache_object_t *obj; + cache_handle *h; + + /* Look up entity keyed to 'url' */ + if (strcasecmp(type, "mem")) { + return DECLINED; + } + apr_lock_acquire(sconf->lock); + obj = (cache_object_t *) apr_hash_get(sconf->cacheht, key, APR_HASH_KEY_STRING); + apr_lock_release(sconf->lock); + + if (!obj || !(obj->complete)) { + return DECLINED; + } + + /* Allocate the cache_handle and initialize it */ + h = malloc(sizeof(cache_handle)); + *hp = h; + if (!h) { + /* handle the error */ + return DECLINED; + } + h->read_body = &read_body; + h->read_headers = &read_headers; + h->write_body = &write_body; + h->write_headers = &write_headers; + h->cache_obj = obj; + if (!obj || !(obj->complete)) { + return DECLINED; + } + return OK; +} + +static int remove_entity(cache_handle *h) +{ + cache_object_t *obj = (cache_object_t *) h->cache_obj; + + apr_lock_acquire(sconf->lock); + apr_hash_set(sconf->cacheht, obj->key, strlen(obj->key), NULL); + apr_lock_release(sconf->lock); + + cleanup_cache_object(obj); + + /* Reinit the cache_handle fields? */ + h->cache_obj = NULL; + + /* The caller should free the cache_handle ? */ + free(h); +} + +/* Define request processing hook handlers */ +static int remove_url(const char *type, char *key) +{ + cache_object_t *obj; + + if (strcasecmp(type, "mem")) { + return DECLINED; + } + + /* WIBNIF + * apr_hash_set(..,..,..,NULL) returned pointer to the object just removed. + * That way, we could free the object w/o doing another call to an + * apr_hash function. + */ + + /* First, find the object in the cache */ + apr_lock_acquire(sconf->lock); + obj = (cache_object_t *) apr_hash_get(sconf->cacheht, key, APR_HASH_KEY_STRING); + apr_lock_release(sconf->lock); + + if (!obj) { + return DECLINED; + } + + /* Found it. Now take it out of the cache and free it. */ + apr_lock_acquire(sconf->lock); + apr_hash_set(sconf->cacheht, obj->key, strlen(obj->key), NULL); + apr_lock_release(sconf->lock); + + cleanup_cache_object(obj); + + return OK; +} + +static int read_headers(cache_handle *h, request_rec *r, cache_info **info) +{ + cache_object_t *obj = (cache_object_t*) h->cache_obj; + + *info = &(obj->info); + + return OK; +} + +static int read_body(cache_handle *h, apr_bucket_brigade *bb) +{ + apr_bucket *b; + cache_object_t *obj = h->cache_obj; + + b = apr_bucket_immortal_create(obj->m, obj->m_len); + APR_BRIGADE_INSERT_TAIL(bb, b); + b = apr_bucket_eos_create(); + APR_BRIGADE_INSERT_TAIL(bb, b); + + return OK; +} + +static int write_headers(cache_handle *h, request_rec *r, cache_info *info) +{ + apr_size_t len; + cache_object_t *obj = (cache_object_t*) h->cache_obj; + if (info->date) { + obj->info.date = info->date; + } + if (info->lastmod) { + obj->info.lastmod = info->lastmod; + } + if (info->expire) { + obj->info.expire = info->expire; + } + if (info->content_type) { + obj->info.content_type = (char*) malloc(strlen(info->content_type)); + if (obj->info.content_type) + strcpy(obj->info.content_type, info->content_type); + } + + return OK; +} + +static int write_body(cache_handle *h, apr_bucket_brigade *b) +{ + apr_status_t rv; + apr_size_t idx = 0; + cache_object_t *obj = (cache_object_t *) h->cache_obj; + apr_read_type_e eblock = APR_BLOCK_READ; + apr_bucket *e; + + /* XXX mmap, malloc or file? + * Enable this decision to be configured.... + */ + char *m = malloc(obj->m_len); + obj->m = m; + if (!m) { + /* Cleanup cache entry and return */ + } + obj->type = CACHE_TYPE_MALLOC; + + /* Iterate accross the brigade and populate the cache storage */ + /* XXX doesn't handle multiple brigades */ + APR_BRIGADE_FOREACH(e, b) { + const char *s; + apr_size_t len; + + rv = apr_bucket_read(e, &s, &len, eblock); + if (rv != APR_SUCCESS) { + /* Big problem! Cleanup cache entry and return */ + } + /* XXX Check for overflow */ + if (len ) { + memcpy(m, s, len); + m+=len; + } + } + + /* XXX - Check for EOS before setting obj->complete + * Open for business. This entry can be served from the cache + */ + obj->complete = 1; + return OK; +} + +static const char +*set_cache_size(cmd_parms *parms, char *struct_ptr, char *arg) +{ + int val; + + if (sscanf(arg, "%d", &val) != 1) + return "CacheSize value must be an integer (kBytes)"; + sconf->space = val; + return NULL; +} +#if 0 +static const char +*set_cache_factor(cmd_parms *parms, void *dummy, char *arg) +{ + double val; + + if (sscanf(arg, "%lg", &val) != 1) + return "CacheLastModifiedFactor value must be a float"; + sconf->lmfactor = val; + + return NULL; +} +#endif +#if 0 +static const char +*set_cache_maxex(cmd_parms *parms, void *dummy, char *arg) +{ + mem_cache_conf *pc = ap_get_module_config(parms->server->module_config, &mem_cache_module); + double val; + + if (sscanf(arg, "%lg", &val) != 1) + return "CacheMaxExpire value must be a float"; + sconf->maxexpire = (apr_time_t) (val * MSEC_ONE_HR); + return NULL; +} +#endif +#if 0 +static const char +*set_cache_defex(cmd_parms *parms, void *dummy, char *arg) +{ + mem_cache_conf *pc = ap_get_module_config(parms->server->module_config, &mem_cache_module); + double val; + + if (sscanf(arg, "%lg", &val) != 1) + return "CacheDefaultExpire value must be a float"; + pc->defaultexpire = (apr_time_t) (val * MSEC_ONE_HR); + return NULL; +} +#endif +static const command_rec cache_cmds[] = +{ + /* XXX + * What config directives does this module need? + * Should this module manage expire policy for its entries? + * Certainly cache limits like max number of entries, + * max entry size, and max size of the cache should + * be managed by this module. + */ + AP_INIT_TAKE1("CacheSizeMem", set_cache_size, NULL, RSRC_CONF, + "The maximum disk space used by the cache in Kb"), + {NULL} +}; + +static void register_hooks(apr_pool_t *p) +{ + /* cache initializer */ +/* cache_hook_cache_init(cache_init, NULL, NULL, AP_HOOK_FIRST); */ + cache_hook_create_entity(create_entity, NULL, NULL, APR_HOOK_MIDDLE); + cache_hook_open_entity(open_entity, NULL, NULL, APR_HOOK_MIDDLE); + cache_hook_remove_url(remove_url, NULL, NULL, APR_HOOK_MIDDLE); +} + +module AP_MODULE_DECLARE_DATA mem_cache_module = +{ + STANDARD20_MODULE_STUFF, + NULL, /* create per-directory config structure */ + NULL, /* merge per-directory config structures */ + create_cache_config, /* create per-server config structure */ + NULL, /* merge per-server config structures */ + cache_cmds, /* command apr_table_t */ + register_hooks +}; + diff --git a/modules/experimental/util_ldap.c b/modules/experimental/util_ldap.c new file mode 100644 index 0000000000..4e9ca0e501 --- /dev/null +++ b/modules/experimental/util_ldap.c @@ -0,0 +1,1105 @@ +/* ==================================================================== + * The Apache Software License, Version 1.1 + * + * Copyright (c) 2000-2001 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 + * <http://www.apache.org/>. + */ + +/* + * util_ldap.c: LDAP things + * + * Original code from auth_ldap module for Apache v1.3: + * Copyright 1998, 1999 Enbridge Pipelines Inc. + * Copyright 1999-2001 Dave Carrigan + */ + +#include <apr_ldap.h> + +#ifdef APU_HAS_LDAP + +#include <apr_lock.h> +#include <apr_strings.h> + +#include "ap_config.h" +#include "httpd.h" +#include "http_config.h" +#include "http_core.h" +#include "http_log.h" +#include "http_protocol.h" +#include "http_request.h" +#include "util_ldap.h" +#include "util_ldap_cache.h" + +#if APR_HAVE_UNISTD_H +#include <unistd.h> +#endif + +module AP_MODULE_DECLARE_DATA ldap_module; + +int util_ldap_handler(request_rec *r); +void *util_ldap_create_config(apr_pool_t *p, server_rec *s); + + +/* + * Some definitions to help between various versions of apache. + */ + +#ifndef DOCTYPE_HTML_2_0 +#define DOCTYPE_HTML_2_0 "<!DOCTYPE HTML PUBLIC \"-//IETF//" \ + "DTD HTML 2.0//EN\">\n" +#endif + +#ifndef DOCTYPE_HTML_3_2 +#define DOCTYPE_HTML_3_2 "<!DOCTYPE HTML PUBLIC \"-//W3C//" \ + "DTD HTML 3.2 Final//EN\">\n" +#endif + +#ifndef DOCTYPE_HTML_4_0S +#define DOCTYPE_HTML_4_0S "<!DOCTYPE HTML PUBLIC \"-//W3C//" \ + "DTD HTML 4.0//EN\"\n" \ + "\"http://www.w3.org/TR/REC-html40/strict.dtd\">\n" +#endif + +#ifndef DOCTYPE_HTML_4_0T +#define DOCTYPE_HTML_4_0T "<!DOCTYPE HTML PUBLIC \"-//W3C//" \ + "DTD HTML 4.0 Transitional//EN\"\n" \ + "\"http://www.w3.org/TR/REC-html40/loose.dtd\">\n" +#endif + +#ifndef DOCTYPE_HTML_4_0F +#define DOCTYPE_HTML_4_0F "<!DOCTYPE HTML PUBLIC \"-//W3C//" \ + "DTD HTML 4.0 Frameset//EN\"\n" \ + "\"http://www.w3.org/TR/REC-html40/frameset.dtd\">\n" +#endif + +/* + * Status Handler + * -------------- + * + * This handler generates a status page about the current performance of + * the LDAP cache. It is enabled as follows: + * + * <Location /ldap-status> + * SetHandler ldap-status + * </Location> + * + */ +int util_ldap_handler(request_rec *r) +{ + + r->allowed |= (1 << M_GET); + if (r->method_number != M_GET) + return DECLINED; + + if (strcmp(r->handler, "ldap-status")) { + return DECLINED; + } + + r->content_type = "text/html"; + if (r->header_only) + return OK; + + ap_rputs(DOCTYPE_HTML_3_2 + "<html><head><title>LDAP Cache Information</title></head>\n", r); + ap_rputs("<body bgcolor='#ffffff'><h1 align=center>LDAP Cache Information</h1>\n", r); + + ap_rputs("<p>\n" + "<table border='0'>\n" + "<tr bgcolor='#000000'>\n" + "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Cache Name</b></font></td>" + "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Entries</b></font></td>" + "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Avg. Chain Len.</b></font></td>" + "<td colspan='2'><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Hits</b></font></td>" + "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Ins/Rem</b></font></td>" + "<td colspan='2'><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Purges</b></font></td>" + "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Avg Purge Time</b></font></td>" + "</tr>\n", r + ); + + ap_rputs(util_ald_cache_display(r->pool), r); + + ap_rputs("</table>\n</p>\n", r); + + return OK; +} + +/* ------------------------------------------------------------------ */ + + +/* + * Closes an LDAP connection by unlocking it. The next time + * util_ldap_connection_find() is called this connection will be + * available for reuse. + */ +void util_ldap_connection_close(util_ldap_connection_t *ldc) +{ + + /* + * QUESTION: + * + * Is it safe leaving bound connections floating around between the + * different modules? Keeping the user bound is a performance boost, + * but it is also a potential security problem - maybe. + * + * For now we unbind the user when we finish with a connection, but + * we don't have to... + */ + + /* mark our connection as available for reuse */ + apr_lock_release(ldc->lock); + +} + + +/* + * Destroys an LDAP connection by unbinding. This function is registered + * with the pool cleanup function - causing the LDAP connections to be + * shut down cleanly on graceful restart. + */ +apr_status_t util_ldap_connection_destroy(void *param) +{ + util_ldap_connection_t *ldc = param; + + /* unbinding from the LDAP server */ + if (ldc->ldap) { + ldap_unbind_s(ldc->ldap); + ldc->bound = 0; + ldc->ldap = NULL; + } + + /* release the lock we were using */ + apr_lock_release(ldc->lock); + + return APR_SUCCESS; +} + + +/* + * Connect to the LDAP server and binds. Does not connect if already + * connected (i.e. ldc->ldap is non-NULL.) Does not bind if already bound. + * + * Returns LDAP_SUCCESS on success; and an error code on failure + */ +int util_ldap_connection_open(util_ldap_connection_t *ldc) +{ + int result = 0; + int failures = 0; + + +start_over: + if (failures++ > 10) { + /* too many failures - leave */ + return result; + } + + if (!ldc->ldap) { + ldc->bound = 0; + + /* opening connection to LDAP server */ + if ((ldc->ldap = ldap_init(ldc->host, ldc->port)) == NULL) { + /* couldn't connect */ + ldc->reason = "ldap_init() failed"; + return -1; + } + + /* add the cleanup to the pool */ + apr_pool_cleanup_register(ldc->pool, ldc, + util_ldap_connection_destroy, + apr_pool_cleanup_null); + + /* Set the alias dereferencing option */ +#if LDAP_VERSION_MAX == 2 + ldc->ldap->ld_deref = ldc->deref; +#else + result = ldap_set_option(ldc->ldap, LDAP_OPT_DEREF, &(ldc->deref)); + if (result != LDAP_SUCCESS) { + /* setting LDAP dereference option failed */ + /* we ignore this error */ + } +#endif /* LDAP_VERSION_MAX */ + +#ifdef APU_HAS_LDAP_NETSCAPE_SSL + if (ldc->netscapessl) { + if (!ldc->certdb) { + /* secure LDAP requested, but no CA cert defined */ + ldc->reason = "secure LDAP requested, but no CA cert defined"; + return -1; + } else { + result = ldapssl_install_routines(ldc->ldap); + if (result != LDAP_SUCCESS) { + /* SSL initialisation failed */ + ldc->reason = "ldapssl_install_routines() failed"; + return result; + } + result = ldap_set_option(ldc->ldap, LDAP_OPT_SSL, LDAP_OPT_ON); + if (result != LDAP_SUCCESS) { + /* SSL option failed */ + ldc->reason = "ldap_set_option() failed trying to set LDAP_OPT_SSL"; + return result; + } + } + } +#endif /* APU_HAS_LDAP_NETSCAPE_SSL */ + +#ifdef APU_HAS_LDAP_STARTTLS + if (ldc->starttls) { + int version = LDAP_VERSION3; + + /* Also we have to set the connection to use protocol version 3, + * since we're using TLS. */ + if ((result = ldap_set_option(ldc->ldap, LDAP_OPT_PROTOCOL_VERSION, + &version)) != LDAP_SUCCESS) { + /* setting LDAP version failed - ignore error */ + } + + /* + * In util_ldap_connection_find, we compare ldc->withtls to + * sec->starttls to see if we have a cache match. On the off + * chance that apache's config processing rotines set starttls to + * some other true value besides 1, we set it to 1 here to ensure + * that the comparison succeeds. + */ + ldc->starttls = 1; + + result = ldap_start_tls_s(ldc->ldap, NULL, NULL); + if (result != LDAP_SUCCESS) { + /* start TLS failed */ + ldc->withtls = 0; + ldc->reason = "ldap_start_tls_s() failed"; + return result; + } + ldc->withtls = 1; + } else { + ldc->withtls = 0; + } +#endif /* APU_HAS_LDAP_STARTTLS */ + } + + /* + * At this point the LDAP connection is guaranteed alive. If bound says + * that we're bound already, we can just return. + */ + if (ldc->bound) { + ldc->reason = "LDAP connection open successful (already bound)"; + return LDAP_SUCCESS; + } + + /* + * Now bind with the username/password provided by the + * configuration. It will be an anonymous bind if no u/p was + * provided. + */ + if ((result = ldap_simple_bind_s(ldc->ldap, ldc->binddn, ldc->bindpw)) + == LDAP_SERVER_DOWN) { + /* couldn't connect - try again */ + ldc->reason = "ldap_simple_bind_s() failed with server down"; + goto start_over; + } + + if (result != LDAP_SUCCESS) { + /* LDAP fatal error occured */ + ldc->reason = "ldap_simple_bind_s() failed"; + return result; + } + + /* note how we are bound */ + ldc->bound = 1; + + ldc->reason = "LDAP connection open successful"; + return LDAP_SUCCESS; +} + + +/* + * Find an existing ldap connection struct that matches the + * provided ldap connection parameters. + * + * If not found in the cache, a new ldc structure will be allocated from st->pool + * and returned to the caller. If found in the cache, a pointer to the existing + * ldc structure will be returned. + */ +util_ldap_connection_t *util_ldap_connection_find(request_rec *r, const char *host, int port, + const char *binddn, const char *bindpw, deref_options deref, + int netscapessl, int starttls) +{ + struct util_ldap_connection_t *l, *p; /* To traverse the linked list */ + + util_ldap_state_t *st = + (util_ldap_state_t *)ap_get_module_config(r->server->module_config, + &ldap_module); + + + /* mutex lock this function */ + if (!st->mutex) { + apr_lock_create(&st->mutex, APR_MUTEX, APR_INTRAPROCESS, NULL, st->pool); + } + apr_lock_acquire(st->mutex); + + /* Search for an exact connection match in the list that is not + * being used. + */ + for (l=st->connections,p=NULL; l; l=l->next) { + if ( (APR_SUCCESS == apr_lock_tryacquire(l->lock)) + && l->port == port + && strcmp(l->host, host) == 0 + && ( (!l->binddn && !binddn) || (l->binddn && binddn && !strcmp(l->binddn, binddn)) ) + && ( (!l->bindpw && !bindpw) || (l->bindpw && bindpw && !strcmp(l->bindpw, bindpw)) ) + && l->deref == deref +#ifdef APU_HAS_LDAP_NETSCAPE_SSL + && l->netscapessl == netscapessl +#endif +#ifdef APU_HAS_LDAP_STARTTLS + && l->withtls == starttls +#endif + ) + break; + p = l; + } + + /* If nothing found, search again, but we don't care about the + * binddn and bindpw this time. + */ + if (!l) { + for (l=st->connections,p=NULL; l; l=l->next) { + if ( (APR_SUCCESS == apr_lock_tryacquire(l->lock)) + && l->port == port + && strcmp(l->host, host) == 0 + && l->deref == deref +#ifdef APU_HAS_LDAP_NETSCAPE_SSL + && l->netscapessl == netscapessl +#endif +#ifdef APU_HAS_LDAP_STARTTLS + && l->withtls == starttls +#endif + ) { + /* the bind credentials have changed */ + l->bound = 0; + l->binddn = apr_pstrdup(st->pool, binddn); + l->bindpw = apr_pstrdup(st->pool, bindpw); + break; + } + p = l; + } + } + +/* artificially disable cache */ +//l = NULL; + + /* If no connection what found after the second search, we + * must create one. + */ + if (!l) { + + /* + * Add the new connection entry to the linked list. Note that we + * don't actually establish an LDAP connection yet; that happens + * the first time authentication is requested. + */ + /* create the details to the pool in st */ + l = apr_pcalloc(st->pool, sizeof(util_ldap_connection_t)); + apr_lock_create(&l->lock, APR_MUTEX, APR_INTRAPROCESS, NULL, st->pool); + apr_lock_acquire(l->lock); + l->pool = st->pool; + l->bound = 0; + l->host = apr_pstrdup(st->pool, host); + l->port = port; + l->deref = deref; + l->binddn = apr_pstrdup(st->pool, binddn); + l->bindpw = apr_pstrdup(st->pool, bindpw); + l->netscapessl = netscapessl; + l->starttls = starttls; + l->withtls = 0; + + if (p) { + p->next = l; + } + else { + st->connections = l; + } + } + + apr_lock_release(st->mutex); + return l; +} + +/* ------------------------------------------------------------------ */ + +/* + * Compares two DNs to see if they're equal. The only way to do this correctly is to + * search for the dn and then do ldap_get_dn() on the result. This should match the + * initial dn, since it would have been also retrieved with ldap_get_dn(). This is + * expensive, so if the configuration value compare_dn_on_server is + * false, just does an ordinary strcmp. + * + * The lock for the ldap cache should already be acquired. + */ +int util_ldap_cache_comparedn(request_rec *r, util_ldap_connection_t *ldc, + const char *url, const char *dn, const char *reqdn, + int compare_dn_on_server) +{ + int result = 0; + util_url_node_t *curl; + util_url_node_t curnode; + util_dn_compare_node_t *node; + util_dn_compare_node_t newnode; + int failures = 0; + LDAPMessage *res, *entry; + char *searchdn; + + util_ldap_state_t *st = + (util_ldap_state_t *)ap_get_module_config(r->server->module_config, + &ldap_module); + + + /* read lock this function */ + if (!util_ldap_cache_lock) { + apr_lock_create(&util_ldap_cache_lock, APR_READWRITE, APR_INTRAPROCESS, NULL, st->pool); + } + + /* get cache entry (or create one) */ + apr_lock_acquire_rw(util_ldap_cache_lock, APR_WRITER); + curnode.url = url; + curl = util_ald_cache_fetch(util_ldap_cache, &curnode); + if (curl == NULL) { + curl = util_ald_create_caches(st, url); + } + apr_lock_release(util_ldap_cache_lock); + + /* a simple compare? */ + if (!compare_dn_on_server) { + /* unlock this read lock */ + if (strcmp(dn, reqdn)) { + ldc->reason = "DN Comparison FALSE (direct strcmp())"; + return LDAP_COMPARE_FALSE; + } + else { + ldc->reason = "DN Comparison TRUE (direct strcmp())"; + return LDAP_COMPARE_TRUE; + } + } + + /* no - it's a server side compare */ + apr_lock_acquire_rw(util_ldap_cache_lock, APR_READER); + + /* is it in the compare cache? */ + newnode.reqdn = (char *)reqdn; + node = util_ald_cache_fetch(curl->dn_compare_cache, &newnode); + if (node != NULL) { + /* If it's in the cache, it's good */ + /* unlock this read lock */ + apr_lock_release(util_ldap_cache_lock); + ldc->reason = "DN Comparison TRUE (cached)"; + return LDAP_COMPARE_TRUE; + } + + /* unlock this read lock */ + apr_lock_release(util_ldap_cache_lock); + +start_over: + if (failures++ > 10) { + /* too many failures */ + return result; + } + + /* make a server connection */ + if (LDAP_SUCCESS != (result = util_ldap_connection_open(ldc))) { + /* connect to server failed */ + return result; + } + + /* search for reqdn */ + if ((result = ldap_search_ext_s(ldc->ldap, const_cast(reqdn), LDAP_SCOPE_BASE, + "(objectclass=*)", NULL, 1, + NULL, NULL, NULL, -1, &res)) == LDAP_SERVER_DOWN) { + util_ldap_connection_close(ldc); + ldc->reason = "DN Comparison ldap_search_ext_s() failed with server down"; + goto start_over; + } + if (result != LDAP_SUCCESS) { + /* search for reqdn failed - no match */ + ldc->reason = "DN Comparison ldap_search_ext_s() failed"; + return result; + } + + entry = ldap_first_entry(ldc->ldap, res); + searchdn = ldap_get_dn(ldc->ldap, entry); + + ldap_msgfree(res); + if (strcmp(dn, searchdn) != 0) { + /* compare unsuccessful */ + ldc->reason = "DN Comparison FALSE (checked on server)"; + result = LDAP_COMPARE_FALSE; + } + else { + /* compare successful - add to the compare cache */ + apr_lock_acquire_rw(util_ldap_cache_lock, APR_READER); + newnode.reqdn = (char *)reqdn; + newnode.dn = (char *)dn; + util_ald_cache_insert(curl->dn_compare_cache, &newnode); + apr_lock_release(util_ldap_cache_lock); + ldc->reason = "DN Comparison TRUE (checked on server)"; + result = LDAP_COMPARE_TRUE; + } + ldap_memfree(searchdn); + return result; + +} + +/* + * Does an generic ldap_compare operation. It accepts a cache that it will use + * to lookup the compare in the cache. We cache two kinds of compares + * (require group compares) and (require user compares). Each compare has a different + * cache node: require group includes the DN; require user does not because the + * require user cache is owned by the + * + */ +int util_ldap_cache_compare(request_rec *r, util_ldap_connection_t *ldc, + const char *url, const char *dn, + const char *attrib, const char *value) +{ + int result = 0; + util_url_node_t *curl; + util_url_node_t curnode; + util_compare_node_t *compare_nodep; + util_compare_node_t the_compare_node; + apr_time_t curtime; + int failures = 0; + + util_ldap_state_t *st = + (util_ldap_state_t *)ap_get_module_config(r->server->module_config, + &ldap_module); + + + /* read lock this function */ + if (!util_ldap_cache_lock) { + apr_lock_create(&util_ldap_cache_lock, APR_READWRITE, APR_INTRAPROCESS, NULL, st->pool); + } + + /* get cache entry (or create one) */ + apr_lock_acquire_rw(util_ldap_cache_lock, APR_WRITER); + curnode.url = url; + curl = util_ald_cache_fetch(util_ldap_cache, &curnode); + if (curl == NULL) { + curl = util_ald_create_caches(st, url); + } + apr_lock_release(util_ldap_cache_lock); + + /* make a comparison to the cache */ + apr_lock_acquire_rw(util_ldap_cache_lock, APR_READER); + curtime = apr_time_now(); + + the_compare_node.dn = (char *)dn; + the_compare_node.attrib = (char *)attrib; + the_compare_node.value = (char *)value; + the_compare_node.result = 0; + + compare_nodep = util_ald_cache_fetch(curl->compare_cache, &the_compare_node); + + if (compare_nodep != NULL) { + /* found it... */ + if (curtime - compare_nodep->lastcompare > st->compare_cache_ttl) { + /* ...but it is too old */ + util_ald_cache_remove(curl->compare_cache, compare_nodep); + } + else { + /* ...and it is good */ + /* unlock this read lock */ + apr_lock_release(util_ldap_cache_lock); + if (LDAP_COMPARE_TRUE == compare_nodep->result) { + ldc->reason = "Comparison true (cached)"; + return compare_nodep->result; + } + else if (LDAP_COMPARE_FALSE == compare_nodep->result) { + ldc->reason = "Comparison false (cached)"; + return compare_nodep->result; + } + else if (LDAP_NO_SUCH_ATTRIBUTE == compare_nodep->result) { + ldc->reason = "Comparison no such attribute (cached)"; + return compare_nodep->result; + } + else { + ldc->reason = "Comparison undefined (cached)"; + return compare_nodep->result; + } + } + } + /* unlock this read lock */ + apr_lock_release(util_ldap_cache_lock); + + +start_over: + if (failures++ > 10) { + /* too many failures */ + return result; + } + if (LDAP_SUCCESS != (result = util_ldap_connection_open(ldc))) { + /* connect failed */ + return result; + } + + if ((result = ldap_compare_s(ldc->ldap, const_cast(dn), + const_cast(attrib), const_cast(value))) + == LDAP_SERVER_DOWN) { + /* connection failed - try again */ + util_ldap_connection_close(ldc); + ldc->reason = "ldap_compare_s() failed with server down"; + goto start_over; + } + + ldc->reason = "Comparison complete"; + if ((LDAP_COMPARE_TRUE == result) || + (LDAP_COMPARE_FALSE == result) || + (LDAP_NO_SUCH_ATTRIBUTE == result)) { + /* compare completed; caching result */ + apr_lock_acquire_rw(util_ldap_cache_lock, APR_WRITER); + the_compare_node.lastcompare = curtime; + the_compare_node.result = result; + util_ald_cache_insert(curl->compare_cache, &the_compare_node); + apr_lock_release(util_ldap_cache_lock); + if (LDAP_COMPARE_TRUE == result) { + ldc->reason = "Comparison true (adding to cache)"; + return LDAP_COMPARE_TRUE; + } + else if (LDAP_COMPARE_FALSE == result) { + ldc->reason = "Comparison false (adding to cache)"; + return LDAP_COMPARE_FALSE; + } + else { + ldc->reason = "Comparison no such attribute (adding to cache)"; + return LDAP_NO_SUCH_ATTRIBUTE; + } + } + return result; +} + +int util_ldap_cache_checkuserid(request_rec *r, util_ldap_connection_t *ldc, + const char *url, const char *basedn, int scope, char **attrs, + const char *filter, const char *bindpw, const char **binddn, + const char ***retvals) +{ + const char **vals = NULL; + int result = 0; + LDAPMessage *res, *entry; + char *dn; + int count; + int failures = 0; + util_url_node_t *curl; /* Cached URL node */ + util_url_node_t curnode; + util_search_node_t *search_nodep; /* Cached search node */ + util_search_node_t the_search_node; + apr_time_t curtime; + + util_ldap_state_t *st = + (util_ldap_state_t *)ap_get_module_config(r->server->module_config, + &ldap_module); + + /* read lock this function */ + if (!util_ldap_cache_lock) { + apr_lock_create(&util_ldap_cache_lock, APR_READWRITE, APR_INTRAPROCESS, NULL, st->pool); + } + + /* Get the cache node for this url */ + apr_lock_acquire_rw(util_ldap_cache_lock, APR_WRITER); + curnode.url = url; + curl = (util_url_node_t *)util_ald_cache_fetch(util_ldap_cache, &curnode); + if (curl == NULL) { + curl = util_ald_create_caches(st, url); + } + apr_lock_release(util_ldap_cache_lock); + + apr_lock_acquire_rw(util_ldap_cache_lock, APR_READER); + the_search_node.username = filter; + search_nodep = util_ald_cache_fetch(curl->search_cache, &the_search_node); + if (search_nodep != NULL && search_nodep->bindpw) { + + /* found entry in search cache... */ + curtime = apr_time_now(); + + /* + * Remove this item from the cache if its expired, or if the + * sent password doesn't match the storepassword. + */ + if ((curtime - search_nodep->lastbind) > st->search_cache_ttl) { + /* ...but entry is too old */ + util_ald_cache_remove(curl->search_cache, search_nodep); + } + else if (strcmp(search_nodep->bindpw, bindpw) != 0) { + /* ...but cached password doesn't match sent password */ + util_ald_cache_remove(curl->search_cache, search_nodep); + } + else { + /* ...and entry is valid */ + *binddn = search_nodep->dn; + *retvals = search_nodep->vals; + apr_lock_release(util_ldap_cache_lock); + ldc->reason = "Authentication successful (cached)"; + return LDAP_SUCCESS; + } + } + /* unlock this read lock */ + apr_lock_release(util_ldap_cache_lock); + + + /* + * At this point, there is no valid cached search, so lets do the search. + */ + + /* + * If any LDAP operation fails due to LDAP_SERVER_DOWN, control returns here. + */ +start_over: + if (failures++ > 10) { + return result; + } + if (LDAP_SUCCESS != (result = util_ldap_connection_open(ldc))) { + return result; + } + + /* try do the search */ + if ((result = ldap_search_ext_s(ldc->ldap, + basedn, scope, + filter, attrs, 0, + NULL, NULL, NULL, -1, &res)) == LDAP_SERVER_DOWN) { + ldc->reason = "ldap_search_ext_s() for user failed with server down"; + goto start_over; + } + + /* if there is an error (including LDAP_NO_SUCH_OBJECT) return now */ + if (result != LDAP_SUCCESS) { + ldc->reason = "ldap_search_ext_s() for user failed"; + return result; + } + + /* + * We should have found exactly one entry; to find a different + * number is an error. + */ + count = ldap_count_entries(ldc->ldap, res); + if (count != 1) { + ldap_msgfree(res); + ldc->reason = "User is not unique (search found two or more matches)"; + return LDAP_NO_SUCH_OBJECT; + } + + entry = ldap_first_entry(ldc->ldap, res); + + /* Grab the dn, copy it into the pool, and free it again */ + dn = ldap_get_dn(ldc->ldap, entry); + *binddn = apr_pstrdup(st->pool, dn); + ldap_memfree(dn); + + /* + * A bind to the server with an empty password always succeeds, so + * we check to ensure that the password is not empty. This implies + * that users who actually do have empty passwords will never be + * able to authenticate with this module. I don't see this as a big + * problem. + */ + if (strlen(bindpw) <= 0) { + ldap_msgfree(res); + ldc->reason = "Empty password not allowed"; + return LDAP_INVALID_CREDENTIALS; + } + + /* + * Attempt to bind with the retrieved dn and the password. If the bind + * fails, it means that the password is wrong (the dn obviously + * exists, since we just retrieved it) + */ + if ((result = + ldap_simple_bind_s(ldc->ldap, *binddn, bindpw)) == + LDAP_SERVER_DOWN) { + ldc->reason = "ldap_simple_bind_s() to check user credentials failed with server down"; + goto start_over; + } + + /* failure? if so - return */ + if (result != LDAP_SUCCESS) { + ldc->reason = "ldap_simple_bind_s() to check user credentials failed"; + return result; + } + + /* + * Get values for the provided attributes. + */ + if (attrs) { + int k = 0; + int i = 0; + while (attrs[k++]); + vals = apr_pcalloc(r->pool, sizeof(char *) * (k+1)); + while (attrs[i]) { + char **values; + int j = 0; + char *str = NULL; + /* get values */ + values = ldap_get_values(ldc->ldap, entry, attrs[i]); + while (values && values[j]) { + str = str ? apr_pstrcat(r->pool, str, "; ", values[j], NULL) : apr_pstrdup(r->pool, values[j]); + j++; + } + vals[i] = str; + i++; + } + *retvals = vals; + } + + /* + * Add the new username to the search cache. + */ + apr_lock_acquire_rw(util_ldap_cache_lock, APR_WRITER); + the_search_node.username = filter; + the_search_node.dn = *binddn; + the_search_node.bindpw = bindpw; + the_search_node.lastbind = apr_time_now(); + the_search_node.vals = vals; + util_ald_cache_insert(curl->search_cache, &the_search_node); + ldap_msgfree(res); + apr_lock_release(util_ldap_cache_lock); + + ldc->reason = "Authentication successful"; + return LDAP_SUCCESS; +} + +#endif /* APU_HAS_LDAP */ + + + +/* ---------------------------------------- */ +/* config directives */ + + +static const char *util_ldap_set_cache_bytes(cmd_parms *cmd, void *dummy, const char *bytes) +{ + util_ldap_state_t *st = + (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config, + &ldap_module); + + st->cache_bytes = atol(bytes); + + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, cmd->server, + "[%d] ldap cache: Setting shared memory cache size to %d bytes.", + getpid(), st->cache_bytes); + + return NULL; +} + +static const char *util_ldap_set_cache_ttl(cmd_parms *cmd, void *dummy, const char *ttl) +{ + util_ldap_state_t *st = + (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config, + &ldap_module); + + st->search_cache_ttl = atol(ttl) * 1000000; + + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, cmd->server, + "[%d] ldap cache: Setting cache TTL to %ld microseconds.", + getpid(), st->search_cache_ttl); + + return NULL; +} + +static const char *util_ldap_set_cache_entries(cmd_parms *cmd, void *dummy, const char *size) +{ + util_ldap_state_t *st = + (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config, + &ldap_module); + + + st->search_cache_size = atol(size); + if (st->search_cache_size < 0) { + st->search_cache_size = 0; + } + + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, cmd->server, + "[%d] ldap cache: Setting search cache size to %ld entries.", + getpid(), st->search_cache_size); + + return NULL; +} + +static const char *util_ldap_set_opcache_ttl(cmd_parms *cmd, void *dummy, const char *ttl) +{ + util_ldap_state_t *st = + (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config, + &ldap_module); + + st->compare_cache_ttl = atol(ttl) * 1000000; + + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, cmd->server, + "[%d] ldap cache: Setting operation cache TTL to %ld microseconds.", + getpid(), st->compare_cache_ttl); + + return NULL; +} + +static const char *util_ldap_set_opcache_entries(cmd_parms *cmd, void *dummy, const char *size) +{ + util_ldap_state_t *st = + (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config, + &ldap_module); + + st->compare_cache_size = atol(size); + if (st->compare_cache_size < 0) { + st->compare_cache_size = 0; + } + + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, cmd->server, + "[%d] ldap cache: Setting operation cache size to %ld entries.", + getpid(), st->compare_cache_size); + + return NULL; +} + +#ifdef APU_HAS_LDAPSSL_CLIENT_INIT +static const char *util_ldap_set_certdbpath(cmd_parms *cmd, void *dummy, const char *path) +{ + util_ldap_state_t *st = + (util_ldap_state_t *)ap_get_module_config(cmd->server->module_config, + &ldap_module); + + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, 0, cmd->server, + "[%d] ldap cache: Setting LDAP SSL client certificate dbpath to %s.", + getpid(), path); + + st->have_certdb = 1; + if (ldapssl_client_init(path, NULL) != 0) { + return "Could not initialize SSL client"; + } + else { + return NULL; + } +} +#endif + +void *util_ldap_create_config(apr_pool_t *p, server_rec *s) +{ + util_ldap_state_t *st = + (util_ldap_state_t *)apr_pcalloc(p, sizeof(util_ldap_state_t)); + + st->pool = p; + + st->cache_bytes = 100000; + st->search_cache_ttl = 600000000; + st->search_cache_size = 1024; + st->compare_cache_ttl = 600000000; + st->compare_cache_size = 1024; + + st->connections = NULL; +#ifdef APU_HAS_LDAP_NETSCAPE_SSL + st->have_certdb = 0; +#endif + + return st; +} + +static void util_ldap_init_module(apr_pool_t *pool, server_rec *s) +{ + util_ldap_state_t *st = + (util_ldap_state_t *)ap_get_module_config(s->module_config, + &ldap_module); + + apr_status_t result = util_ldap_cache_init(pool, st->cache_bytes); + char buf[MAX_STRING_LEN]; + + apr_strerror(result, buf, sizeof(buf)); + ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, result, s, + "[%d] ldap cache init: %s", + getpid(), buf); +} + + +command_rec util_ldap_cmds[] = { + AP_INIT_TAKE1("LDAPSharedCacheSize", util_ldap_set_cache_bytes, NULL, RSRC_CONF, + "Sets the size of the shared memory cache in bytes. " + "Zero means disable the shared memory cache. Defaults to 100KB."), + + AP_INIT_TAKE1("LDAPCacheEntries", util_ldap_set_cache_entries, NULL, RSRC_CONF, + "Sets the maximum number of entries that are possible in the LDAP " + "search cache. " + "Zero means no limit; -1 disables the cache. Defaults to 1024 entries."), + + AP_INIT_TAKE1("LDAPCacheTTL", util_ldap_set_cache_ttl, NULL, RSRC_CONF, + "Sets the maximum time (in seconds) that an item can be cached in the LDAP " + "search cache. Zero means no limit. Defaults to 600 seconds (10 minutes)."), + + AP_INIT_TAKE1("LDAPOpCacheEntries", util_ldap_set_opcache_entries, NULL, RSRC_CONF, + "Sets the maximum number of entries that are possible in the LDAP " + "compare cache. " + "Zero means no limit; -1 disables the cache. Defaults to 1024 entries."), + + AP_INIT_TAKE1("LDAPOpCacheTTL", util_ldap_set_opcache_ttl, NULL, RSRC_CONF, + "Sets the maximum time (in seconds) that an item is cached in the LDAP " + "operation cache. Zero means no limit. Defaults to 600 seconds (10 minutes)."), + +#ifdef APU_HAS_LDAPSSL_CLIENT_INIT + AP_INIT_TAKE1("LDAPCertDBPath", util_ldap_set_certdbpath, NULL, RSRC_CONF, + "Specifies the file containing Certificate Authority certificates " + "for validating secure LDAP server certificates. This file must be the " + "cert7.db database used by Netscape Communicator"), +#endif + + {NULL} +}; + +static void util_ldap_register_hooks(apr_pool_t *p) +{ + ap_hook_child_init(util_ldap_init_module, NULL, NULL, APR_HOOK_MIDDLE); + ap_hook_handler(util_ldap_handler, NULL, NULL, APR_HOOK_MIDDLE); +} + +module ldap_module = { + STANDARD20_MODULE_STUFF, + NULL, /* dir config creater */ + NULL, /* dir merger --- default is to override */ + util_ldap_create_config, /* server config */ + NULL, /* merge server config */ + util_ldap_cmds, /* command table */ + util_ldap_register_hooks, /* set up request processing hooks */ +}; diff --git a/modules/experimental/util_ldap_cache.c b/modules/experimental/util_ldap_cache.c new file mode 100644 index 0000000000..9d9844ddd6 --- /dev/null +++ b/modules/experimental/util_ldap_cache.c @@ -0,0 +1,309 @@ +/* ==================================================================== + * The Apache Software License, Version 1.1 + * + * Copyright (c) 2000-2001 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 + * <http://www.apache.org/>. + */ + +/* + * util_ldap_cache.c: LDAP cache things + * + * Original code from auth_ldap module for Apache v1.3: + * Copyright 1998, 1999 Enbridge Pipelines Inc. + * Copyright 1999-2001 Dave Carrigan + */ + +#include <apr_ldap.h> +#include "util_ldap.h" +#include "util_ldap_cache.h" + +#ifdef APU_HAS_LDAP + + + + +/* ------------------------------------------------------------------ */ + +unsigned long util_ldap_url_node_hash(void *n) +{ + util_url_node_t *node = (util_url_node_t *)n; + return util_ald_hash_string(1, node->url); +} + +int util_ldap_url_node_compare(void *a, void *b) +{ + util_url_node_t *na = (util_url_node_t *)a; + util_url_node_t *nb = (util_url_node_t *)b; + + return(strcmp(na->url, nb->url) == 0); +} + +void *util_ldap_url_node_copy(void *c) +{ + util_url_node_t *n = (util_url_node_t *)c; + util_url_node_t *node = (util_url_node_t *)util_ald_alloc(sizeof(util_url_node_t)); + + if (node) { + if (!(node->url = util_ald_strdup(n->url))) { + util_ald_free(node->url); + return NULL; + } + node->search_cache = n->search_cache; + node->compare_cache = n->compare_cache; + node->dn_compare_cache = n->dn_compare_cache; + return node; + } + else { + return NULL; + } +} + +void util_ldap_url_node_free(void *n) +{ + util_url_node_t *node = (util_url_node_t *)n; + + util_ald_free(node->url); + util_ald_destroy_cache(node->search_cache); + util_ald_destroy_cache(node->compare_cache); + util_ald_destroy_cache(node->dn_compare_cache); + util_ald_free(node); +} + +/* ------------------------------------------------------------------ */ + +/* Cache functions for search nodes */ +unsigned long util_ldap_search_node_hash(void *n) +{ + util_search_node_t *node = (util_search_node_t *)n; + return util_ald_hash_string(1, ((util_search_node_t *)(node))->username); +} + +int util_ldap_search_node_compare(void *a, void *b) +{ + return(strcmp(((util_search_node_t *)a)->username, + ((util_search_node_t *)b)->username) == 0); +} + +void *util_ldap_search_node_copy(void *c) +{ + util_search_node_t *node = (util_search_node_t *)c; + util_search_node_t *newnode = util_ald_alloc(sizeof(util_search_node_t)); + + /* safety check */ + if (newnode) { + + /* copy vals */ + if (node->vals) { + int k = 0; + int i = 0; + while (node->vals[k++]); + if (!(newnode->vals = util_ald_alloc(sizeof(char *) * (k+1)))) { + util_ldap_search_node_free(newnode); + return NULL; + } + while (node->vals[i]) { + if (!(newnode->vals[i] = util_ald_strdup(node->vals[i]))) { + util_ldap_search_node_free(newnode); + return NULL; + } + i++; + } + } + else { + newnode->vals = NULL; + } + if (!(newnode->username = util_ald_strdup(node->username)) || + !(newnode->dn = util_ald_strdup(node->dn)) || + !(newnode->bindpw = util_ald_strdup(node->bindpw)) ) { + util_ldap_search_node_free(newnode); + return NULL; + } + newnode->lastbind = node->lastbind; + + } + return (void *)newnode; +} + +void util_ldap_search_node_free(void *n) +{ + int i = 0; + util_search_node_t *node = (util_search_node_t *)n; + if (node->vals) { + while (node->vals[i]) { + util_ald_free(node->vals[i++]); + } + util_ald_free(node->vals); + } + util_ald_free(node->username); + util_ald_free(node->dn); + util_ald_free(node->bindpw); + util_ald_free(node); +} + +/* ------------------------------------------------------------------ */ + +unsigned long util_ldap_compare_node_hash(void *n) +{ + util_compare_node_t *node = (util_compare_node_t *)n; + return util_ald_hash_string(3, node->dn, node->attrib, node->value); +} + +int util_ldap_compare_node_compare(void *a, void *b) +{ + util_compare_node_t *na = (util_compare_node_t *)a; + util_compare_node_t *nb = (util_compare_node_t *)b; + return (strcmp(na->dn, nb->dn) == 0 && + strcmp(na->attrib, nb->attrib) == 0 && + strcmp(na->value, nb->value) == 0); +} + +void *util_ldap_compare_node_copy(void *c) +{ + util_compare_node_t *n = (util_compare_node_t *)c; + util_compare_node_t *node = (util_compare_node_t *)util_ald_alloc(sizeof(util_compare_node_t)); + + if (node) { + if (!(node->dn = util_ald_strdup(n->dn)) || + !(node->attrib = util_ald_strdup(n->attrib)) || + !(node->value = util_ald_strdup(n->value))) { + util_ldap_compare_node_free(node); + return NULL; + } + node->lastcompare = n->lastcompare; + node->result = n->result; + return node; + } + else { + return NULL; + } +} + +void util_ldap_compare_node_free(void *n) +{ + util_compare_node_t *node = (util_compare_node_t *)n; + util_ald_free(node->dn); + util_ald_free(node->attrib); + util_ald_free(node->value); + util_ald_free(node); +} + +/* ------------------------------------------------------------------ */ + +unsigned long util_ldap_dn_compare_node_hash(void *n) +{ + return util_ald_hash_string(1, ((util_dn_compare_node_t *)n)->reqdn); +} + +int util_ldap_dn_compare_node_compare(void *a, void *b) +{ + return (strcmp(((util_dn_compare_node_t *)a)->reqdn, + ((util_dn_compare_node_t *)b)->reqdn) == 0); +} + +void *util_ldap_dn_compare_node_copy(void *c) +{ + util_dn_compare_node_t *n = (util_dn_compare_node_t *)c; + util_dn_compare_node_t *node = (util_dn_compare_node_t *)util_ald_alloc(sizeof(util_dn_compare_node_t)); + if (node) { + if (!(node->reqdn = util_ald_strdup(n->reqdn)) || + !(node->dn = util_ald_strdup(n->dn))) { + util_ldap_dn_compare_node_free(node); + return NULL; + } + return node; + } + else { + return NULL; + } +} + +void util_ldap_dn_compare_node_free(void *n) +{ + util_dn_compare_node_t *node = (util_dn_compare_node_t *)n; + util_ald_free(node->reqdn); + util_ald_free(node->dn); + util_ald_free(node); +} + + +/* ------------------------------------------------------------------ */ +apr_status_t util_ldap_cache_child_kill(void *data); +apr_status_t util_ldap_cache_module_kill(void *data); + +apr_status_t util_ldap_cache_module_kill(void *data) +{ +#if APR_HAS_SHARED_MEMORY + if (util_ldap_shm != NULL) { + apr_status_t result = apr_shm_destroy(util_ldap_shm); + util_ldap_shm = NULL; + return result; + } +#endif + return APR_SUCCESS; +} + +apr_status_t util_ldap_cache_init(apr_pool_t *pool, apr_size_t reqsize) +{ + apr_status_t result = APR_SUCCESS; + apr_pool_cleanup_register(pool, NULL, util_ldap_cache_module_kill, apr_pool_cleanup_null); + +#if APR_HAS_SHARED_MEMORY + result = apr_shm_init(&util_ldap_shm, reqsize, "/tmp/ldap_cache", pool); +#endif + util_ldap_cache = util_ald_create_cache(50, + util_ldap_url_node_hash, + util_ldap_url_node_compare, + util_ldap_url_node_copy, + util_ldap_url_node_free); + return result; +} + + +#endif /* APU_HAS_LDAP */ diff --git a/modules/experimental/util_ldap_cache.h b/modules/experimental/util_ldap_cache.h new file mode 100644 index 0000000000..18790eb854 --- /dev/null +++ b/modules/experimental/util_ldap_cache.h @@ -0,0 +1,214 @@ +/* ==================================================================== + * The Apache Software License, Version 1.1 + * + * Copyright (c) 2000-2001 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 + * <http://www.apache.org/>. + */ + + +#ifndef APU_LDAP_CACHE_H +#define APU_LDAP_CACHE_H + +/* + * This switches LDAP support on or off. + */ + +/* this whole thing disappears if LDAP is not enabled */ +#ifdef APU_HAS_LDAP + + +/* + * LDAP Cache Manager + */ + +#include <apr_shmem.h> + +typedef struct util_cache_node_t { + void *payload; /* Pointer to the payload */ + time_t add_time; /* Time node was added to cache */ + struct util_cache_node_t *next; +} util_cache_node_t; + +typedef struct util_ald_cache_t { + unsigned long size; /* Size of cache array */ + unsigned long maxentries; /* Maximum number of cache entries */ + unsigned long numentries; /* Current number of cache entries */ + unsigned long fullmark; /* Used to keep track of when cache becomes 3/4 full */ + time_t marktime; /* Time that the cache became 3/4 full */ + unsigned long (*hash)(void *); /* Func to hash the payload */ + int (*compare)(void *, void *); /* Func to compare two payloads */ + void * (*copy)(void *); /* Func to alloc mem and copy payload to new mem */ + void (*free)(void *); /* Func to free mem used by the payload */ + util_cache_node_t **nodes; + + unsigned long numpurges; /* No. of times the cache has been purged */ + double avg_purgetime; /* Average time to purge the cache */ + time_t last_purge; /* Time of the last purge */ + unsigned long npurged; /* Number of elements purged in last purge. This is not + obvious: it won't be 3/4 the size of the cache if + there were a lot of expired entries. */ + + unsigned long fetches; /* Number of fetches */ + unsigned long hits; /* Number of cache hits */ + unsigned long inserts; /* Number of inserts */ + unsigned long removes; /* Number of removes */ +} util_ald_cache_t; + +#if APR_HAS_SHARED_MEMORY +apr_shmem_t *util_ldap_shm; +#endif +util_ald_cache_t *util_ldap_cache; +apr_lock_t *util_ldap_cache_lock; + +#ifndef WIN32 +#define ALD_MM_FILE_MODE ( S_IRUSR|S_IWUSR ) +#else +#define ALD_MM_FILE_MODE ( _S_IREAD|_S_IWRITE ) +#endif + + +/* + * LDAP Cache + */ + +/* + * Maintain a cache of LDAP URLs that the server handles. Each node in + * the cache contains the search cache for that URL, and a compare cache + * for the URL. The compare cash is populated when doing require group + * compares. + */ +typedef struct util_url_node_t { + const char *url; + util_ald_cache_t *search_cache; + util_ald_cache_t *compare_cache; + util_ald_cache_t *dn_compare_cache; +} util_url_node_t; + +/* + * We cache every successful search and bind operation, using the username + * as the key. Each node in the cache contains the returned DN, plus the + * password used to bind. + */ +typedef struct util_search_node_t { + const char *username; /* Cache key */ + const char *dn; /* DN returned from search */ + const char *bindpw; /* The most recently used bind password; + NULL if the bind failed */ + apr_time_t lastbind; /* Time of last successful bind */ + const char **vals; /* Values of queried attributes */ +} util_search_node_t; + +/* + * We cache every successful compare operation, using the DN, attrib, and + * value as the key. + */ +typedef struct util_compare_node_t { + const char *dn; /* DN, attrib and value combine to be the key */ + const char *attrib; + const char *value; + apr_time_t lastcompare; + int result; +} util_compare_node_t; + +/* + * We cache every successful compare dn operation, using the dn in the require + * statement and the dn fetched based on the client-provided username. + */ +typedef struct util_dn_compare_node_t { + const char *reqdn; /* The DN in the require dn statement */ + const char *dn; /* The DN found in the search */ +} util_dn_compare_node_t; + + +/* + * Function prototypes for LDAP cache + */ + +/* util_ldap_cache.c */ +unsigned long util_ldap_url_node_hash(void *n); +int util_ldap_url_node_compare(void *a, void *b); +void *util_ldap_url_node_copy(void *c); +void util_ldap_url_node_free(void *n); +unsigned long util_ldap_search_node_hash(void *n); +int util_ldap_search_node_compare(void *a, void *b); +void *util_ldap_search_node_copy(void *c); +void util_ldap_search_node_free(void *n); +unsigned long util_ldap_compare_node_hash(void *n); +int util_ldap_compare_node_compare(void *a, void *b); +void *util_ldap_compare_node_copy(void *c); +void util_ldap_compare_node_free(void *n); +unsigned long util_ldap_dn_compare_node_hash(void *n); +int util_ldap_dn_compare_node_compare(void *a, void *b); +void *util_ldap_dn_compare_node_copy(void *c); +void util_ldap_dn_compare_node_free(void *n); + + +/* util_ldap_cache_mgr.c */ + +void util_ald_free(const void *ptr); +void *util_ald_alloc(unsigned long size); +const char *util_ald_strdup(const char *s); +unsigned long util_ald_hash_string(int nstr, ...); +void util_ald_cache_purge(util_ald_cache_t *cache); +util_url_node_t *util_ald_create_caches(util_ldap_state_t *s, const char *url); +util_ald_cache_t *util_ald_create_cache(unsigned long maxentries, + unsigned long (*hashfunc)(void *), + int (*comparefunc)(void *, void *), + void * (*copyfunc)(void *), + void (*freefunc)(void *)); +void util_ald_destroy_cache(util_ald_cache_t *cache); +void *util_ald_cache_fetch(util_ald_cache_t *cache, void *payload); +void util_ald_cache_insert(util_ald_cache_t *cache, void *payload); +void util_ald_cache_remove(util_ald_cache_t *cache, void *payload); +char *util_ald_cache_display_stats(apr_pool_t *p, util_ald_cache_t *cache, + char *name); + +#endif /* APU_HAS_LDAP */ +#endif /* APU_LDAP_CACHE_H */ diff --git a/modules/experimental/util_ldap_cache_mgr.c b/modules/experimental/util_ldap_cache_mgr.c new file mode 100644 index 0000000000..49eece8958 --- /dev/null +++ b/modules/experimental/util_ldap_cache_mgr.c @@ -0,0 +1,537 @@ +/* ==================================================================== + * The Apache Software License, Version 1.1 + * + * Copyright (c) 2000-2001 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 + * <http://www.apache.org/>. + */ + +/* + * util_ldap_cache_mgr.c: LDAP cache manager things + * + * Original code from auth_ldap module for Apache v1.3: + * Copyright 1998, 1999 Enbridge Pipelines Inc. + * Copyright 1999-2001 Dave Carrigan + */ + +#include <apr_ldap.h> +#include "util_ldap.h" +#include "util_ldap_cache.h" +#include <apr_strings.h> + +#ifdef APU_HAS_LDAP + +/* only here until strdup is gone */ +#include <string.h> + +/* here till malloc is gone */ +#include <stdlib.h> + +static const int primes[] = +{ + 11, + 19, + 37, + 73, + 109, + 163, + 251, + 367, + 557, + 823, + 1237, + 1861, + 2777, + 4177, + 6247, + 9371, + 14057, + 21089, + 31627, + 47431, + 71143, + 106721, + 160073, + 240101, + 360163, + 540217, + 810343, + 1215497, + 1823231, + 2734867, + 4102283, + 6153409, + 9230113, + 13845163, + 0 +}; + +void util_ald_free(const void *ptr) +{ +#if APR_HAS_SHARED_MEMORY + if (util_ldap_shm) { + if (ptr) + apr_shm_free(util_ldap_shm, (void *)ptr); + } else { + if (ptr) + free((void *)ptr); + } +#else + if (ptr) + free((void *)ptr); +#endif +} + +void *util_ald_alloc(unsigned long size) +{ + if (0 == size) + return NULL; +#if APR_HAS_SHARED_MEMORY + if (util_ldap_shm) { + return (void *)apr_shm_calloc(util_ldap_shm, size); + } else { + return (void *)calloc(sizeof(char), size); + } +#else + return (void *)calloc(sizeof(char), size); +#endif +} + +const char *util_ald_strdup(const char *s) +{ +#if APR_HAS_SHARED_MEMORY + if (util_ldap_shm) { + char *buf = apr_shm_malloc(util_ldap_shm, strlen(s)+1); + if (buf) { + strcpy(buf, s); + return buf; + } + else { + return NULL; + } + } else { + return strdup(s); + } +#else + return strdup(s); +#endif +} + + +/* + * Computes the hash on a set of strings. The first argument is the number + * of strings to hash, the rest of the args are strings. + * Algorithm taken from glibc. + */ +unsigned long util_ald_hash_string(int nstr, ...) +{ + int i; + va_list args; + unsigned long h=0, g; + char *str, *p; + + va_start(args, nstr); + for (i=0; i < nstr; ++i) { + str = va_arg(args, char *); + for (p = str; *p; ++p) { + h = ( h << 4 ) + *p; + if ( ( g = h & 0xf0000000 ) ) { + h = h ^ (g >> 24); + h = h ^ g; + } + } + } + va_end(args); + + return h; +} + + +/* + Purges a cache that has gotten full. We keep track of the time that we + added the entry that made the cache 3/4 full, then delete all entries + that were added before that time. It's pretty simplistic, but time to + purge is only O(n), which is more important. +*/ +void util_ald_cache_purge(util_ald_cache_t *cache) +{ + int i; + util_cache_node_t *p, *q; + apr_time_t t; + + if (!cache) + return; + + cache->last_purge = apr_time_now(); + cache->npurged = 0; + cache->numpurges++; + + for (i=0; i < cache->size; ++i) { + p = cache->nodes[i]; + while (p != NULL) { + if (p->add_time < cache->marktime) { + q = p->next; + (*cache->free)(p->payload); + util_ald_free(p); + cache->numentries--; + cache->npurged++; + p = q; + } + else { + p = p->next; + } + } + } + + t = apr_time_now(); + cache->avg_purgetime = + ((t - cache->last_purge) + (cache->avg_purgetime * (cache->numpurges-1))) / + cache->numpurges; +} + + +/* + * create caches + */ +util_url_node_t *util_ald_create_caches(util_ldap_state_t *st, const char *url) +{ + util_url_node_t *curl = NULL; + util_ald_cache_t *search_cache; + util_ald_cache_t *compare_cache; + util_ald_cache_t *dn_compare_cache; + + /* create the three caches */ + search_cache = util_ald_create_cache(st->search_cache_size, + util_ldap_search_node_hash, + util_ldap_search_node_compare, + util_ldap_search_node_copy, + util_ldap_search_node_free); + compare_cache = util_ald_create_cache(st->compare_cache_size, + util_ldap_compare_node_hash, + util_ldap_compare_node_compare, + util_ldap_compare_node_copy, + util_ldap_compare_node_free); + dn_compare_cache = util_ald_create_cache(st->compare_cache_size, + util_ldap_dn_compare_node_hash, + util_ldap_dn_compare_node_compare, + util_ldap_dn_compare_node_copy, + util_ldap_dn_compare_node_free); + + /* check that all the caches initialised successfully */ + if (search_cache && compare_cache && dn_compare_cache) { + + curl = (util_url_node_t *)apr_pcalloc(st->pool, sizeof(util_url_node_t)); + curl->url = url; + curl->search_cache = search_cache; + curl->compare_cache = compare_cache; + curl->dn_compare_cache = dn_compare_cache; + + util_ald_cache_insert(util_ldap_cache, curl); + + } + + return curl; +} + + +util_ald_cache_t *util_ald_create_cache(unsigned long maxentries, + unsigned long (*hashfunc)(void *), + int (*comparefunc)(void *, void *), + void * (*copyfunc)(void *), + void (*freefunc)(void *)) +{ + util_ald_cache_t *cache; + int i; + + if (maxentries <= 0) + return NULL; + + cache = (util_ald_cache_t *)util_ald_alloc(sizeof(util_ald_cache_t)); + if (!cache) + return NULL; + + cache->maxentries = maxentries; + cache->numentries = 0; + cache->size = maxentries / 3; + if (cache->size < 64) cache->size = 64; + for (i = 0; primes[i] && primes[i] < cache->size; ++i) ; + cache->size = primes[i]? primes[i] : primes[i-1]; + + cache->nodes = (util_cache_node_t **)util_ald_alloc(cache->size * sizeof(util_cache_node_t *)); + if (!cache->nodes) { + util_ald_free(cache); + return NULL; + } + + for (i=0; i < cache->size; ++i) + cache->nodes[i] = NULL; + + cache->hash = hashfunc; + cache->compare = comparefunc; + cache->copy = copyfunc; + cache->free = freefunc; + + cache->fullmark = cache->maxentries / 4 * 3; + cache->marktime = 0; + cache->avg_purgetime = 0.0; + cache->numpurges = 0; + cache->last_purge = 0; + cache->npurged = 0; + + cache->fetches = 0; + cache->hits = 0; + cache->inserts = 0; + cache->removes = 0; + + return cache; +} + +void util_ald_destroy_cache(util_ald_cache_t *cache) +{ + int i; + util_cache_node_t *p, *q; + + if (cache == NULL) + return; + + for (i = 0; i < cache->size; ++i) { + p = cache->nodes[i]; + q = NULL; + while (p != NULL) { + q = p->next; + (*cache->free)(p->payload); + util_ald_free(p); + p = q; + } + } + util_ald_free(cache->nodes); +} + +void *util_ald_cache_fetch(util_ald_cache_t *cache, void *payload) +{ + int hashval; + util_cache_node_t *p; + + if (cache == NULL) + return NULL; + + cache->fetches++; + + hashval = (*cache->hash)(payload) % cache->size; + for (p = cache->nodes[hashval]; + p && !(*cache->compare)(p->payload, payload); + p = p->next) ; + + if (p != NULL) { + cache->hits++; + return p->payload; + } + else { + return NULL; + } +} + +/* + * Insert an item into the cache. + * *** Does not catch duplicates!!! *** + */ +void util_ald_cache_insert(util_ald_cache_t *cache, void *payload) +{ + int hashval; + util_cache_node_t *node; + + if (cache == NULL || payload == NULL) + return; + + cache->inserts++; + hashval = (*cache->hash)(payload) % cache->size; + node = (util_cache_node_t *)util_ald_alloc(sizeof(util_cache_node_t)); + node->add_time = apr_time_now(); + node->payload = (*cache->copy)(payload); + node->next = cache->nodes[hashval]; + cache->nodes[hashval] = node; + if (++cache->numentries == cache->fullmark) + cache->marktime=apr_time_now(); + if (cache->numentries >= cache->maxentries) + util_ald_cache_purge(cache); +} + +void util_ald_cache_remove(util_ald_cache_t *cache, void *payload) +{ + int hashval; + util_cache_node_t *p, *q; + + if (cache == NULL) + return; + + cache->removes++; + hashval = (*cache->hash)(payload) % cache->size; + for (p = cache->nodes[hashval], q=NULL; + p && !(*cache->compare)(p->payload, payload); + p = p->next) { + q = p; + } + + /* If p is null, it means that we couldn't find the node, so just return */ + if (p == NULL) + return; + + if (q == NULL) { + /* We found the node, and it's the first in the list */ + cache->nodes[hashval] = p->next; + } + else { + /* We found the node and it's not the first in the list */ + q->next = p->next; + } + (*cache->free)(p->payload); + util_ald_free(p); + cache->numentries--; +} + +char *util_ald_cache_display_stats(apr_pool_t *p, util_ald_cache_t *cache, char *name) +{ + int i; + int totchainlen = 0; + int nchains = 0; + double chainlen; + util_cache_node_t *n; + char *buf; + + if (cache == NULL) { + return ""; + } + + for (i=0; i < cache->size; ++i) { + if (cache->nodes[i] != NULL) { + nchains++; + for (n = cache->nodes[i]; n != NULL; n = n->next) + totchainlen++; + } + } + chainlen = nchains? (double)totchainlen / (double)nchains : 0; + + buf = apr_psprintf(p, + "<tr valign='top'>" + "<td nowrap>%s</td>" + "<td align='right' nowrap>%lu (%.0f%% full)</td>" + "<td align='right'>%.1f</td>" + "<td align='right'>%lu/%lu</td>" + "<td align='right'>%.0f%%</td>" + "<td align='right'>%lu/%lu</td>", + name, + cache->numentries, + (double)cache->numentries / (double)cache->maxentries * 100.0, + chainlen, + cache->hits, + cache->fetches, + (cache->fetches > 0 ? (double)(cache->hits) / (double)(cache->fetches) * 100.0 : 100.0), + cache->inserts, + cache->removes); + + if (cache->numpurges) { + char str_ctime[APR_CTIME_LEN]; + + apr_ctime(str_ctime, cache->last_purge); + buf = apr_psprintf(p, + "%s" + "<td align='right'>%lu</td>\n" + "<td align='right' nowrap>%s</td>\n", + buf, + cache->numpurges, + str_ctime); + } + else { + buf = apr_psprintf(p, + "%s<td colspan='2' align='center'>(none)</td>\n", + buf); + } + + buf = apr_psprintf(p, "%s<td align='right'>%.2g</td>\n</tr>", buf, cache->avg_purgetime); + + return buf; +} + +char *util_ald_cache_display(apr_pool_t *pool) +{ + int i; + char *buf, *t1, *t2, *t3; + + if (!util_ldap_cache) { + return "<tr valign='top'><td nowrap colspan=7>Cache has not been enabled/initialised.</td></tr>"; + } + + buf = util_ald_cache_display_stats(pool, util_ldap_cache, "LDAP URL Cache"); + + for (i=0; i < util_ldap_cache->size; ++i) { + util_cache_node_t *p; + for (p = util_ldap_cache->nodes[i]; p != NULL; p = p->next) { + util_url_node_t *n; + + n = (util_url_node_t *)p->payload; + + t1 = apr_psprintf(pool, "%s (Searches)", n->url); + t2 = apr_psprintf(pool, "%s (Compares)", n->url); + t3 = apr_psprintf(pool, "%s (DNCompares)", n->url); + + buf = apr_psprintf(pool, "%s\n\n" + "%s\n\n" + "%s\n\n" + "%s\n\n", + buf, + util_ald_cache_display_stats(pool, n->search_cache, t1), + util_ald_cache_display_stats(pool, n->compare_cache, t2), + util_ald_cache_display_stats(pool, n->dn_compare_cache, t3) + ); + } + } + return buf; +} + +#endif /* APU_HAS_LDAP */ diff --git a/modules/generators/mod_cgi.h b/modules/generators/mod_cgi.h new file mode 100644 index 0000000000..4eadfaa58a --- /dev/null +++ b/modules/generators/mod_cgi.h @@ -0,0 +1,79 @@ +/* ==================================================================== + * The Apache Software License, Version 1.1 + * + * Copyright (c) 2000-2001 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 + * <http://www.apache.org/>. + * + * 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 _MOD_CGI_H +#define _MOD_CGI_H 1 + + +/** + * Registerable optional function to override CGI behavior; + * Reprocess the command and arguments to execute the given CGI script. + * @param cmd Pointer to the command to execute (may be overridden) + * @param argv Pointer to the arguments to pass (may be overridden) + * @param r The current request + * @param p The pool to allocate correct cmd/argv elements within. + * @deffunc apr_status_t ap_cgi_build_command(const char **cmd, const char ***argv, request_rec *r, apr_pool_t *p) + * @tip This callback may be registered by the os-specific module + * to correct the command and arguments for apr_proc_create invocation + * on a given os. mod_cgi will call the function if registered. + */ +APR_DECLARE_OPTIONAL_FN(apr_status_t, ap_cgi_build_command, + (const char **cmd, const char ***argv, + request_rec *r, apr_pool_t *p)); + +#endif /* _MOD_CGI_H */ diff --git a/modules/mappers/mod_vhost_alias.dsp b/modules/mappers/mod_vhost_alias.dsp new file mode 100644 index 0000000000..7a892bf83d --- /dev/null +++ b/modules/mappers/mod_vhost_alias.dsp @@ -0,0 +1,128 @@ +# Microsoft Developer Studio Project File - Name="mod_vhost_alias" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=mod_vhost_alias - Win32 Release +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "mod_vhost_alias.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "mod_vhost_alias.mak" CFG="mod_vhost_alias - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "mod_vhost_alias - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "mod_vhost_alias - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "mod_vhost_alias - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MD /W3 /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FD /c +# ADD CPP /nologo /MD /W3 /O2 /I "..\..\include" /I "..\..\os\win32" /I "..\..\srclib\apr\include" /I "../../srclib/apr-util/include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Release\mod_vhost_alias" /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib /nologo /subsystem:windows /dll /map /machine:I386 /out:"Release/mod_vhost_alias.so" /base:@..\..\os\win32\BaseAddr.ref,mod_vhost_alias +# ADD LINK32 kernel32.lib /nologo /subsystem:windows /dll /map /machine:I386 /out:"Release/mod_vhost_alias.so" /base:@..\..\os\win32\BaseAddr.ref,mod_vhost_alias + +!ELSEIF "$(CFG)" == "mod_vhost_alias - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MDd /W3 /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FD /c +# ADD CPP /nologo /MDd /W3 /GX /Zi /Od /I "..\..\include" /I "..\..\os\win32" /I "..\..\srclib\apr\include" /I "../../srclib/apr-util/include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Debug\mod_vhost_alias" /FD /c +# ADD BASE MTL /nologo /D "_DEBUG" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /map /debug /machine:I386 /out:"Debug/mod_vhost_alias.so" /base:@..\..\os\win32\BaseAddr.ref,mod_vhost_alias +# ADD LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /map /debug /machine:I386 /out:"Debug/mod_vhost_alias.so" /base:@..\..\os\win32\BaseAddr.ref,mod_vhost_alias + +!ENDIF + +# Begin Target + +# Name "mod_vhost_alias - Win32 Release" +# Name "mod_vhost_alias - Win32 Debug" +# Begin Source File + +SOURCE=.\mod_vhost_alias.c +# End Source File +# Begin Source File + +SOURCE=.\mod_vhost_alias.rc +# End Source File +# Begin Source File + +SOURCE=..\..\build\win32\win32ver.awk + +!IF "$(CFG)" == "mod_vhost_alias - Win32 Release" + +# PROP Ignore_Default_Tool 1 +# Begin Custom Build - Creating Version Resource +InputPath=..\..\build\win32\win32ver.awk + +".\mod_vhost_alias.rc" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + awk -f ../../build/win32/win32ver.awk mod_vhost_alias "vhost_alias_module for Apache" ../../include/ap_release.h > .\mod_vhost_alias.rc + +# End Custom Build + +!ELSEIF "$(CFG)" == "mod_vhost_alias - Win32 Debug" + +# PROP Ignore_Default_Tool 1 +# Begin Custom Build - Creating Version Resource +InputPath=..\..\build\win32\win32ver.awk + +".\mod_vhost_alias.rc" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + awk -f ../../build/win32/win32ver.awk mod_vhost_alias "vhost_alias_module for Apache" ../../include/ap_release.h > .\mod_vhost_alias.rc + +# End Custom Build + +!ENDIF + +# End Source File +# End Target +# End Project diff --git a/modules/metadata/mod_mime_magic.dsp b/modules/metadata/mod_mime_magic.dsp new file mode 100644 index 0000000000..f831ae4ec8 --- /dev/null +++ b/modules/metadata/mod_mime_magic.dsp @@ -0,0 +1,128 @@ +# Microsoft Developer Studio Project File - Name="mod_mime_magic" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=mod_mime_magic - Win32 Release +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "mod_mime_magic.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "mod_mime_magic.mak" CFG="mod_mime_magic - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "mod_mime_magic - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "mod_mime_magic - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "mod_mime_magic - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MD /W3 /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FD /c +# ADD CPP /nologo /MD /W3 /O2 /I "..\..\include" /I "..\..\os\win32" /I "..\..\srclib\apr\include" /I "../../srclib/apr-util/include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Release\mod_mime_magic" /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib ws2_32.lib /nologo /subsystem:windows /dll /map /machine:I386 /out:"Release/mod_mime_magic.so" /base:@..\..\os\win32\BaseAddr.ref,mod_mime_magic +# ADD LINK32 kernel32.lib ws2_32.lib /nologo /subsystem:windows /dll /map /machine:I386 /out:"Release/mod_mime_magic.so" /base:@..\..\os\win32\BaseAddr.ref,mod_mime_magic + +!ELSEIF "$(CFG)" == "mod_mime_magic - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MDd /W3 /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FD /c +# ADD CPP /nologo /MDd /W3 /GX /Zi /Od /I "..\..\include" /I "..\..\os\win32" /I "..\..\srclib\apr\include" /I "../../srclib/apr-util/include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Debug\mod_mime_magic" /FD /c +# ADD BASE MTL /nologo /D "_DEBUG" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib ws2_32.lib /nologo /subsystem:windows /dll /incremental:no /map /debug /machine:I386 /out:"Debug/mod_mime_magic.so" /base:@..\..\os\win32\BaseAddr.ref,mod_mime_magic +# ADD LINK32 kernel32.lib ws2_32.lib /nologo /subsystem:windows /dll /incremental:no /map /debug /machine:I386 /out:"Debug/mod_mime_magic.so" /base:@..\..\os\win32\BaseAddr.ref,mod_mime_magic + +!ENDIF + +# Begin Target + +# Name "mod_mime_magic - Win32 Release" +# Name "mod_mime_magic - Win32 Debug" +# Begin Source File + +SOURCE=.\mod_mime_magic.c +# End Source File +# Begin Source File + +SOURCE=.\mod_mime_magic.rc +# End Source File +# Begin Source File + +SOURCE=..\..\build\win32\win32ver.awk + +!IF "$(CFG)" == "mod_mime_magic - Win32 Release" + +# PROP Ignore_Default_Tool 1 +# Begin Custom Build - Creating Version Resource +InputPath=..\..\build\win32\win32ver.awk + +".\mod_mime_magic.rc" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + awk -f ../../build/win32/win32ver.awk mod_mime_magic "mime_magic_module for Apache" ../../include/ap_release.h > .\mod_mime_magic.rc + +# End Custom Build + +!ELSEIF "$(CFG)" == "mod_mime_magic - Win32 Debug" + +# PROP Ignore_Default_Tool 1 +# Begin Custom Build - Creating Version Resource +InputPath=..\..\build\win32\win32ver.awk + +".\mod_mime_magic.rc" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + awk -f ../../build/win32/win32ver.awk mod_mime_magic "mime_magic_module for Apache" ../../include/ap_release.h > .\mod_mime_magic.rc + +# End Custom Build + +!ENDIF + +# End Source File +# End Target +# End Project diff --git a/modules/metadata/mod_unique_id.dsp b/modules/metadata/mod_unique_id.dsp new file mode 100644 index 0000000000..3045d2fc23 --- /dev/null +++ b/modules/metadata/mod_unique_id.dsp @@ -0,0 +1,128 @@ +# Microsoft Developer Studio Project File - Name="mod_unique_id" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=mod_unique_id - Win32 Release +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "mod_unique_id.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "mod_unique_id.mak" CFG="mod_unique_id - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "mod_unique_id - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "mod_unique_id - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "mod_unique_id - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MD /W3 /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FD /c +# ADD CPP /nologo /MD /W3 /O2 /I "..\..\include" /I "..\..\os\win32" /I "..\..\srclib\apr\include" /I "../../srclib/apr-util/include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Release\mod_unique_id" /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib ws2_32.lib /nologo /subsystem:windows /dll /map /machine:I386 /out:"Release/mod_unique_id.so" /base:@..\..\os\win32\BaseAddr.ref,mod_unique_id +# ADD LINK32 kernel32.lib ws2_32.lib /nologo /subsystem:windows /dll /map /machine:I386 /out:"Release/mod_unique_id.so" /base:@..\..\os\win32\BaseAddr.ref,mod_unique_id + +!ELSEIF "$(CFG)" == "mod_unique_id - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MDd /W3 /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FD /c +# ADD CPP /nologo /MDd /W3 /GX /Zi /Od /I "..\..\include" /I "..\..\os\win32" /I "..\..\srclib\apr\include" /I "../../srclib/apr-util/include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Debug\mod_unique_id" /FD /c +# ADD BASE MTL /nologo /D "_DEBUG" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib ws2_32.lib /nologo /subsystem:windows /dll /incremental:no /map /debug /machine:I386 /out:"Debug/mod_unique_id.so" /base:@..\..\os\win32\BaseAddr.ref,mod_unique_id +# ADD LINK32 kernel32.lib ws2_32.lib /nologo /subsystem:windows /dll /incremental:no /map /debug /machine:I386 /out:"Debug/mod_unique_id.so" /base:@..\..\os\win32\BaseAddr.ref,mod_unique_id + +!ENDIF + +# Begin Target + +# Name "mod_unique_id - Win32 Release" +# Name "mod_unique_id - Win32 Debug" +# Begin Source File + +SOURCE=.\mod_unique_id.c +# End Source File +# Begin Source File + +SOURCE=.\mod_unique_id.rc +# End Source File +# Begin Source File + +SOURCE=..\..\build\win32\win32ver.awk + +!IF "$(CFG)" == "mod_unique_id - Win32 Release" + +# PROP Ignore_Default_Tool 1 +# Begin Custom Build - Creating Version Resource +InputPath=..\..\build\win32\win32ver.awk + +".\mod_unique_id.rc" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + awk -f ../../build/win32/win32ver.awk mod_unique_id "unique_id_module for Apache" ../../include/ap_release.h > .\mod_unique_id.rc + +# End Custom Build + +!ELSEIF "$(CFG)" == "mod_unique_id - Win32 Debug" + +# PROP Ignore_Default_Tool 1 +# Begin Custom Build - Creating Version Resource +InputPath=..\..\build\win32\win32ver.awk + +".\mod_unique_id.rc" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + awk -f ../../build/win32/win32ver.awk mod_unique_id "unique_id_module for Apache" ../../include/ap_release.h > .\mod_unique_id.rc + +# End Custom Build + +!ENDIF + +# End Source File +# End Target +# End Project diff --git a/modules/proxy/CHANGES b/modules/proxy/CHANGES new file mode 100644 index 0000000000..d03f68c932 --- /dev/null +++ b/modules/proxy/CHANGES @@ -0,0 +1,134 @@ + +mod_proxy changes for httpd 2.0.18-dev + + *) Reverse previous patch since the core reverted. + [Chuck Murcko <chuck@topsail.org>] + + *) Remove indirection on number of bytes to read for input filters. + [Chuck Murcko <chuck@topsail.org>] + + *) Fixed a problem with directory listing corruption in the + PROXY_DIR filter. + [Graham Leggett <minfrin@sharp.fm>] + + *) mod_proxy and the proxy submodules now build properly as DSOs. + [Graham Leggett <minfrin@sharp.fm>] + + *) Stopped the HTTP proxy from trying to read entity bodies when there + wasn't one (response was 1xx, 204, 205 or 304). + [Graham Leggett <minfrin@sharp.fm>] + + *) Made sure dates were canonicalised correctly when passed to the client + browser through the HTTP proxy. + [Graham Leggett <minfrin@sharp.fm>] + + *) Split each individual proxy protocol into separate modules. + [Graham Leggett <minfrin@sharp.fm>] + + *) Added Max-Forwards support for all request types so as to prevent + loops. + [Graham Leggett <minfrin@sharp.fm>] + + *) Fix warnings about byte count type on Darwin (connect handler). + [Chuck Murcko <chuck@topsail.org>] + + *) IPV6 EPSV support for IPV6 in FTP proxy. + [Graham Leggett <minfrin@sharp.fm>] + + *) FTP directory filter works now. + [Graham Leggett <minfrin@sharp.fm>] + + *) Fixed some thread-safety issues with the HTTP proxy in mod_proxy. + [Graham Leggett <minfrin@sharp.fm>] + + *) PASV FTP works now. + [Graham Leggett <minfrin@sharp.fm>] + + *) Reworked the line-at-a-time read from the control connection to + workaround a stray empty bucket returned by the HTTP_IN filter. + [Graham Leggett <minfrin@sharp.fm>] + + *) Stopped the CORE filter from sending off an HTTP response when a + CONNECT tunnel was closed. + [Graham Leggett <minfrin@sharp.fm>] + + *) Fixed the poll() loop in proxy_connect.c -> it works now!!! + [Graham Leggett <minfrin@sharp.fm>] + + *) Converted send_dir() to ap_proxy_send_dir_filter() in proxy_ftp.c. + [Graham Leggett <minfrin@sharp.fm>] + + *) Major rework of ap_proxy_ftp_handler() to use filters (begone foul + BUFF!!!). It compiles, but is untested, and the build environment needs + to be fixed to include proxy_ftp.c. + [Graham Leggett <minfrin@sharp.fm>] + + *) Cleanup of dead functions within proxy_util.c. + [Graham Leggett <minfrin@sharp.fm>] + + *) Reworked the storage of the client socket between keepalive connections + to fix some nasty problems with the socket lasting longer than the + memory pool it was allocated from. + [Graham Leggett <minfrin@sharp.fm>] + + *) Fixed bug where a hostname without a "." in it (such as "localhost") + would not trigger an IP address check with ProxyBlock. + [Graham Leggett <minfrin@sharp.fm>] + + *) Fixed ProxyBlock bugs with ap_proxy_http_handler() and + ap_proxy_connect_handler(). + [Graham Leggett <minfrin@sharp.fm>] + + *) Updated ap_proxy_connect_handler() to support APR, while + moving some common code between http_handler and connect_handler + to proxy_util.c. + [Graham Leggett <minfrin@sharp.fm>] + + *) Updated mod_proxy.html docs to include v2.0 configuration. + [Graham Leggett <minfrin@sharp.fm>] + + *) Fixed problem where responses without entity bodies would cause + the directly following proxy keepalive request to fail. + [Graham Leggett <minfrin@sharp.fm>] + + *) Added support for downstream keepalives in mod_proxy. + [Graham Leggett <minfrin@sharp.fm>] + + *) Changed mod_proxy ap_proxy_http_handler() to support APR properly. + [Graham Leggett <minfrin@sharp.fm>] + + *) Fix problem where incoming response headers were not being returned + to the client in mod_proxy. + [Graham Leggett <minfrin@sharp.fm>] + + *) Added X-Forwarded-For, X-Forwarded-Host and X-Forwarded-Server to + reverse proxied request headers in mod_proxy. + [Graham Leggett <minfrin@sharp.fm>] + + *) replace INADDR_NONE with APR_INADDR_NONE [Ian Holsman <IanH@cnet.com>] + + *) Fix problem with proxy configuration where globally set + configuration options were overridden inside virtual hosts. + [Graham Leggett <minfrin@sharp.fm>] + + *) Fix ProxyReceiveBufferSize where default value was left + uninitialised. + [Graham Leggett <minfrin@sharp.fm>] + + *) Some small changes: + - Ensured hop-by-hop headers were stripped as per + RFC2616 13.5.1. + - Upgraded version code to HTTP/1.1. + - Added Connection: close until Keepalives come. + - Some cosmetic fixes and commenting. + [Graham Leggett <minfrin@sharp.fm>] + +mod_proxy changes for 2.0.14 alpha + + *) removed ProxyNoCache and ProxyCacheForceCompletion config directives, + since we no longer directly cache from this module [Chuck Murcko] + + *) removed cache [Chuck Murcko] + + *) initial rerebuild for 2.0 [Chuck Murcko] + diff --git a/modules/ssl/Makefile.in b/modules/ssl/Makefile.in new file mode 100644 index 0000000000..2207d48f3c --- /dev/null +++ b/modules/ssl/Makefile.in @@ -0,0 +1,50 @@ +## ==================================================================== +## The Apache Software License, Version 1.1 +## +## Copyright (c) 2000-2001 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. +## ==================================================================== + +include $(top_srcdir)/build/special.mk + diff --git a/modules/ssl/config.m4 b/modules/ssl/config.m4 new file mode 100644 index 0000000000..4eeefd7b79 --- /dev/null +++ b/modules/ssl/config.m4 @@ -0,0 +1,160 @@ +dnl ## ==================================================================== +dnl ## The Apache Software License, Version 1.1 +dnl ## +dnl ## Copyright (c) 2000-2001 The Apache Software Foundation. All rights +dnl ## reserved. +dnl ## +dnl ## Redistribution and use in source and binary forms, with or without +dnl ## modification, are permitted provided that the following conditions +dnl ## are met: +dnl ## +dnl ## 1. Redistributions of source code must retain the above copyright +dnl ## notice, this list of conditions and the following disclaimer. +dnl ## +dnl ## 2. Redistributions in binary form must reproduce the above copyright +dnl ## notice, this list of conditions and the following disclaimer in +dnl ## the documentation and/or other materials provided with the +dnl ## distribution. +dnl ## +dnl ## 3. The end-user documentation included with the redistribution, +dnl ## if any, must include the following acknowledgment: +dnl ## "This product includes software developed by the +dnl ## Apache Software Foundation (http://www.apache.org/)." +dnl ## Alternately, this acknowledgment may appear in the software itself, +dnl ## if and wherever such third-party acknowledgments normally appear. +dnl ## +dnl ## 4. The names "Apache" and "Apache Software Foundation" must +dnl ## not be used to endorse or promote products derived from this +dnl ## software without prior written permission. For written +dnl ## permission, please contact apache@apache.org. +dnl ## +dnl ## 5. Products derived from this software may not be called "Apache", +dnl ## nor may "Apache" appear in their name, without prior written +dnl ## permission of the Apache Software Foundation. +dnl ## +dnl ## THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED +dnl ## WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +dnl ## OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +dnl ## DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR +dnl ## ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +dnl ## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +dnl ## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +dnl ## USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +dnl ## ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +dnl ## OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +dnl ## OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +dnl ## SUCH DAMAGE. +dnl ## ==================================================================== + +dnl # start of module specific part +APACHE_MODPATH_INIT(ssl) + +dnl # list of module object files +ssl_objs="mod_ssl.lo" +ssl_objs="$ssl_objs ssl_engine_config.lo" + +dnl # hook module into the Autoconf mechanism (--enable-ssl option) +APACHE_MODULE(ssl, [SSL/TLS support (mod_ssl)], $ssl_objs, , no, [ + + dnl # hook into Autoconf mechanism (--with-ssl[=DIR] option) + AC_MSG_CHECKING(for SSL/TLS toolkit base) + ssltk_base="SYSTEM" + AC_ARG_WITH(ssl, [ --with-ssl[=DIR] SSL/TLS toolkit (OpenSSL)], [ + if test ".$withval" != .yes -a ".$withval" != .; then + ssltk_base="$withval" + if test ! -d $ssltk_base; then + AC_MSG_ERROR([invalid SSL/TLS toolkit base directory $ssltk_base]) + fi + fi + ]) + AC_MSG_RESULT($ssltk_base) + + dnl # determine SSL/TLS toolkit frontend (openssl binary) + AC_MSG_CHECKING(for SSL/TLS toolkit frontend) + ssltk_frontend="" + if test ".$ssltk_base" = .SYSTEM; then + for p in . `echo $PATH | sed -e 's/:/ /g'`; do + if test -f "$p/openssl"; then + ssltk_frontend="$p/openssl" + break + fi + done + if test ".$ssltk_frontend" = .; then + AC_MSG_ERROR(['openssl' not found in $PATH]) + fi + else + if test -f "$ssltk_base/bin/openssl"; then + ssltk_frontend="$ssltk_base/bin/openssl" + else + AC_MSG_ERROR(['openssl' not found in $ssltk_base/bin/]) + fi + fi + AC_MSG_RESULT($ssltk_frontend) + + dnl # determine SSL/TLS toolkit version + AC_MSG_CHECKING(for SSL/TLS toolkit version) + ssltk_version="`$ssltk_frontend version`" + case "$ssltk_version" in + *0.9.[[6789]]* ) ;; + * ) AC_MSG_ERROR([SSL/TLS toolkit version $ssltk_version not supported]) ;; + esac + AC_MSG_RESULT($ssltk_version) + + dnl # determine SSL/TLS toolkit include directory + AC_MSG_CHECKING(for SSL/TLS toolkit includes) + ssltk_incdir="" + if test ".$ssltk_base" = .SYSTEM; then + for p in . /usr/include /usr/include/ssl/ /usr/local/include /usr/local/include/ssl; do + if test -f "$p/openssl/ssl.h"; then + ssltk_incdir="$p" + break + fi + done + if test ".$ssltk_incdir" = .; then + AC_MSG_ERROR([OpenSSL headers not found]) + fi + else + if test -f "$ssltk_base/include/openssl/ssl.h"; then + ssltk_incdir="$ssltk_base/include" + else + AC_MSG_ERROR([OpenSSL headers not found under $ssltk_base]) + fi + fi + AC_MSG_RESULT($ssltk_incdir) + + dnl # determine SSL/TLS toolkit library directory + AC_MSG_CHECKING(for SSL/TLS toolkit libraries) + ssltk_libdir="" + if test ".$ssltk_base" = .SYSTEM; then + for p in . /lib /usr/lib /usr/local/lib; do + if test -f "$p/libssl.a" -o -f "$p/libssl.so"; then + ssltk_libdir="$p" + break + fi + done + if test ".$ssltk_libdir" = .; then + AC_MSG_ERROR([OpenSSL libraries not found]) + fi + else + if test -f "$ssltk_base/libssl.a" -o -f "$ssltk_base/libssl.so"; then + ssltk_libdir="$ssltk_base" + elif test -f "$ssltk_base/lib/libssl.a" -o -f "$ssltk_base/lib/libssl.so"; then + ssltk_libdir="$ssltk_base/lib" + else + AC_MSG_ERROR([OpenSSL libraries not found under $ssltk_base]) + fi + fi + AC_MSG_RESULT($ssltk_libdir) + + dnl # annotate the Apache build environment with determined information + if test ".$ssltk_incdir" != "./usr/include"; then + INCLUDES="$INCLUDES -I$ssltk_incdir" + fi + if test ".$ssltk_libdir" != "./usr/lib"; then + LIBS="$LIBS -L$ssltk_libdir -lssl -lcrypto" + fi +]) + +dnl # end of module specific part +APACHE_MODPATH_FINISH + diff --git a/modules/ssl/mod_ssl.dsp b/modules/ssl/mod_ssl.dsp new file mode 100644 index 0000000000..c1f46dfb83 --- /dev/null +++ b/modules/ssl/mod_ssl.dsp @@ -0,0 +1,217 @@ +# Microsoft Developer Studio Project File - Name="mod_ssl" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=mod_ssl - Win32 Release +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "mod_ssl.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "mod_ssl.mak" CFG="mod_ssl - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "mod_ssl - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "mod_ssl - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "mod_ssl - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MD /W3 /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FD /c +# ADD CPP /nologo /MD /W3 /O2 /I "..\..\include" /I "..\..\os\win32" /I "..\..\server\mpm\winnt" /I "..\..\srclib\apr\include" /I "../../srclib/apr-util/include" /I "..\..\srclib\openssl\inc32" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Release\mod_ssl" /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x809 /d "NDEBUG" +# ADD RSC /l 0x809 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /map /out:"Release/mod_ssl.so" /machine:I386 /base:@..\..\os\win32\BaseAddr.ref,mod_ssl +# ADD LINK32 kernel32.lib ssleay32.lib libeay32.lib /nologo /libpath:"..\..\srclib\openssl\out32dll" /subsystem:windows /dll /incremental:no /map /out:"Release/mod_ssl.so" /machine:I386 /base:@..\..\os\win32\BaseAddr.ref,mod_ssl + +!ELSEIF "$(CFG)" == "mod_ssl - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MDd /W3 /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FD /c +# ADD CPP /nologo /MDd /W3 /GX /ZI /Od /I "..\..\include" /I "..\..\os\win32" /I "..\..\server\mpm\winnt" /I "..\..\srclib\apr\include" /I "..\..\srclib\apr-util\include" /I "..\..\srclib\openssl\inc32" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Debug\mod_ssl" /FD /c +# ADD BASE MTL /nologo /D "_DEBUG" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x809 /d "_DEBUG" +# ADD RSC /l 0x809 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /map /debug /out:"Debug/mod_ssl.so" /machine:I386 /base:@..\..\os\win32\BaseAddr.ref,mod_ssl +# ADD LINK32 kernel32.lib ssleay32.lib libeay32.lib /nologo /libpath:"..\..\srclib\openssl\out32dll" /subsystem:windows /dll /incremental:no /map /debug /out:"Debug/mod_ssl.so" /machine:I386 /base:@..\..\os\win32\BaseAddr.ref,mod_ssl + +!ENDIF + +# Begin Target + +# Name "mod_ssl - Win32 Release" +# Name "mod_ssl - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "*.c" +# Begin Source File + +SOURCE=.\mod_ssl.c +# End Source File +# Begin Source File + +SOURCE=.\ssl_engine_config.c +# End Source File +# Begin Source File + +SOURCE=.\ssl_engine_dh.c +# End Source File +# Begin Source File + +SOURCE=.\ssl_engine_ds.c +# End Source File +# Begin Source File + +SOURCE=.\ssl_engine_ext.c +# End Source File +# Begin Source File + +SOURCE=.\ssl_engine_init.c +# End Source File +# Begin Source File + +SOURCE=.\ssl_engine_io.c +# End Source File +# Begin Source File + +SOURCE=.\ssl_engine_kernel.c +# End Source File +# Begin Source File + +SOURCE=.\ssl_engine_log.c +# End Source File +# Begin Source File + +SOURCE=.\ssl_engine_mutex.c +# End Source File +# Begin Source File + +SOURCE=.\ssl_engine_pphrase.c +# End Source File +# Begin Source File + +SOURCE=.\ssl_engine_rand.c +# End Source File +# Begin Source File + +SOURCE=.\ssl_engine_vars.c +# End Source File +# Begin Source File + +SOURCE=.\ssl_expr.c +# End Source File +# Begin Source File + +SOURCE=.\ssl_expr_eval.c +# End Source File +# Begin Source File + +SOURCE=.\ssl_expr_parse.c +# PROP Exclude_From_Build 1 +# End Source File +# Begin Source File + +SOURCE=.\ssl_expr_scan.c +# PROP Exclude_From_Build 1 +# End Source File +# Begin Source File + +SOURCE=.\ssl_scache.c +# End Source File +# Begin Source File + +SOURCE=.\ssl_scache_dbm.c +# End Source File +# Begin Source File + +SOURCE=.\ssl_scache_shmcb.c +# End Source File +# Begin Source File + +SOURCE=.\ssl_scache_shmht.c +# End Source File +# Begin Source File + +SOURCE=.\ssl_util.c +# End Source File +# Begin Source File + +SOURCE=.\ssl_util_ssl.c +# End Source File +# Begin Source File + +SOURCE=.\ssl_util_table.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "*.h" +# Begin Source File + +SOURCE=.\mod_ssl.h +# End Source File +# Begin Source File + +SOURCE=.\ssl_expr.h +# End Source File +# Begin Source File + +SOURCE=.\ssl_expr_parse.h +# End Source File +# Begin Source File + +SOURCE=.\ssl_util_ssl.h +# End Source File +# Begin Source File + +SOURCE=.\ssl_util_table.h +# End Source File +# End Group +# End Target +# End Project diff --git a/modules/test/Makefile.in b/modules/test/Makefile.in new file mode 100644 index 0000000000..7c5c149d85 --- /dev/null +++ b/modules/test/Makefile.in @@ -0,0 +1,3 @@ +# a modules Makefile has no explicit targets -- they will be defined by +# whatever modules are enabled. just grab special.mk to deal with this. +include $(top_srcdir)/build/special.mk diff --git a/modules/test/config.m4 b/modules/test/config.m4 new file mode 100644 index 0000000000..01bc0fa971 --- /dev/null +++ b/modules/test/config.m4 @@ -0,0 +1,9 @@ + +APACHE_MODPATH_INIT(test) + +APACHE_MODULE(optional_hook_export, example optional hook exporter, , , no) +APACHE_MODULE(optional_hook_import, example optional hook importer, , , no) +APACHE_MODULE(optional_fn_import, example optional function importer, , , no) +APACHE_MODULE(optional_fn_export, example optional function exporter, , , no) + +APACHE_MODPATH_FINISH diff --git a/modules/test/mod_optional_hook_export.c b/modules/test/mod_optional_hook_export.c new file mode 100644 index 0000000000..34ef97b543 --- /dev/null +++ b/modules/test/mod_optional_hook_export.c @@ -0,0 +1,83 @@ +/* ==================================================================== + * The Apache Software License, Version 1.1 + * + * Copyright (c) 2000-2001 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 + * <http://www.apache.org/>. + */ + +#include "httpd.h" +#include "http_config.h" +#include "mod_optional_hook_export.h" +#include "http_protocol.h" + +APR_IMPLEMENT_OPTIONAL_HOOK_RUN_ALL(ap,AP_MODULE,int,generic_hook_test, + (const char *szStr), + (szStr),OK,DECLINED) + +static int ExportLogTransaction(request_rec *r) +{ + return ap_run_generic_hook_test(r->the_request); +} + +static void ExportRegisterHooks(apr_pool_t *p) +{ + ap_hook_log_transaction(ExportLogTransaction,NULL,NULL,APR_HOOK_MIDDLE); +} + +module optional_hook_export_module = +{ + STANDARD20_MODULE_STUFF, + NULL, + NULL, + NULL, + NULL, + NULL, + ExportRegisterHooks +}; diff --git a/modules/test/mod_optional_hook_export.h b/modules/test/mod_optional_hook_export.h new file mode 100644 index 0000000000..7494078cdd --- /dev/null +++ b/modules/test/mod_optional_hook_export.h @@ -0,0 +1,62 @@ +/* ==================================================================== + * The Apache Software License, Version 1.1 + * + * Copyright (c) 2000-2001 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 + * <http://www.apache.org/>. + */ + +#ifndef MOD_GENERIC_HOOK_EXPORT_H +#define MOD_GENERIC_HOOK_EXPORT_H + +#include "apr_optional_hooks.h" + +APR_DECLARE_EXTERNAL_HOOK(ap,AP_MODULE,int,generic_hook_test,(const char *)) + +#endif /* def MOD_GENERIC_HOOK_EXPORT_H */ diff --git a/modules/test/mod_optional_hook_import.c b/modules/test/mod_optional_hook_import.c new file mode 100644 index 0000000000..ebf0158610 --- /dev/null +++ b/modules/test/mod_optional_hook_import.c @@ -0,0 +1,83 @@ +/* ==================================================================== + * The Apache Software License, Version 1.1 + * + * Copyright (c) 2000-2001 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 + * <http://www.apache.org/>. + */ + +#include "httpd.h" +#include "http_config.h" +#include "http_log.h" +#include "mod_optional_hook_export.h" + +static int ImportGenericHookTestHook(const char *szStr) +{ + ap_log_error(APLOG_MARK,APLOG_ERR,OK,NULL,"Generic hook test said: %s", + szStr); + + return OK; +} + +static void ImportRegisterHooks(apr_pool_t *p) +{ + APR_OPTIONAL_HOOK(ap,generic_hook_test,ImportGenericHookTestHook,NULL,NULL, + APR_HOOK_MIDDLE); +} + +module optional_hook_import_module= +{ + STANDARD20_MODULE_STUFF, + NULL, + NULL, + NULL, + NULL, + NULL, + ImportRegisterHooks +}; diff --git a/server/mpm/mpmt_os2/.cvsignore b/server/mpm/mpmt_os2/.cvsignore new file mode 100644 index 0000000000..dc53ac865d --- /dev/null +++ b/server/mpm/mpmt_os2/.cvsignore @@ -0,0 +1,5 @@ +*.lo +*.la +Makefile +.deps +.libs diff --git a/server/mpm/mpmt_os2/Makefile.in b/server/mpm/mpmt_os2/Makefile.in new file mode 100644 index 0000000000..38e598edf3 --- /dev/null +++ b/server/mpm/mpmt_os2/Makefile.in @@ -0,0 +1,5 @@ + +LTLIBRARY_NAME = libmpmt_os2.la +LTLIBRARY_SOURCES = mpmt_os2.c mpmt_os2_child.c + +include $(top_srcdir)/build/ltlib.mk diff --git a/server/mpm/mpmt_os2/config5.m4 b/server/mpm/mpmt_os2/config5.m4 new file mode 100644 index 0000000000..b27c296d2a --- /dev/null +++ b/server/mpm/mpmt_os2/config5.m4 @@ -0,0 +1,5 @@ +if test "$MPM_NAME" = "mpmt_os2" ; then + AC_CACHE_SAVE + APACHE_FAST_OUTPUT(server/mpm/$MPM_NAME/Makefile) + APR_ADDTO(CFLAGS,-Zmt) +fi diff --git a/server/mpm/mpmt_os2/mpm.h b/server/mpm/mpmt_os2/mpm.h new file mode 100644 index 0000000000..37d2f6c20c --- /dev/null +++ b/server/mpm/mpmt_os2/mpm.h @@ -0,0 +1,74 @@ +/* ==================================================================== + * The Apache Software License, Version 1.1 + * + * Copyright (c) 2000-2001 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 + * <http://www.apache.org/>. + * + * 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 APACHE_MPM_MPMT_OS2_H +#define APACHE_MPM_MPMT_OS2_H + +#define MPMT_OS2_MPM + +#include "httpd.h" +#include "mpm_default.h" +#include "scoreboard.h" + +#define MPM_NAME "MPMT_OS2" + +extern server_rec *ap_server_conf; +#define AP_MPM_WANT_SET_PIDFILE +#define AP_MPM_WANT_SET_MAX_REQUESTS + +#endif /* APACHE_MPM_SPMT_OS2_H */ diff --git a/server/mpm/mpmt_os2/mpm_default.h b/server/mpm/mpmt_os2/mpm_default.h new file mode 100644 index 0000000000..100beb2e61 --- /dev/null +++ b/server/mpm/mpmt_os2/mpm_default.h @@ -0,0 +1,116 @@ +/* ==================================================================== + * The Apache Software License, Version 1.1 + * + * Copyright (c) 2000-2001 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 + * <http://www.apache.org/>. + * + * 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 APACHE_MPM_DEFAULT_H +#define APACHE_MPM_DEFAULT_H + +/* Number of servers processes to spawn off by default + */ +#ifndef DEFAULT_START_DAEMON +#define DEFAULT_START_DAEMON 2 +#endif + +/* We don't need many processes, + * they're only for redundancy in the event of a crash + */ +#define HARD_SERVER_LIMIT 10 + +/* Limit on the total number of threads per process + */ +#ifndef HARD_THREAD_LIMIT +#define HARD_THREAD_LIMIT 256 +#endif + +/* Maximum number of *free* server threads --- more than this, and + * they will die off. + */ + +#ifndef DEFAULT_MAX_SPARE_THREAD +#define DEFAULT_MAX_SPARE_THREAD 10 +#endif + +/* Minimum --- fewer than this, and more will be created */ + +#ifndef DEFAULT_MIN_SPARE_THREAD +#define DEFAULT_MIN_SPARE_THREAD 5 +#endif + +/* Where the main/parent process's pid is logged */ +#ifndef DEFAULT_PIDLOG +#define DEFAULT_PIDLOG "logs/httpd.pid" +#endif + +/* + * Interval, in microseconds, between scoreboard maintenance. + */ +#ifndef SCOREBOARD_MAINTENANCE_INTERVAL +#define SCOREBOARD_MAINTENANCE_INTERVAL 1000000 +#endif + +/* Number of requests to try to handle in a single process. If <= 0, + * the children don't die off. + */ +#ifndef DEFAULT_MAX_REQUESTS_PER_CHILD +#define DEFAULT_MAX_REQUESTS_PER_CHILD 10000 +#endif + +/* AP_CHILD_THREAD_FROM_ID is used by the scoreboard. */ +#define AP_CHILD_THREAD_FROM_ID(i) (i / HARD_THREAD_LIMIT), (i % HARD_THREAD_LIMIT) +#define AP_ID_FROM_CHILD_THREAD(c, t) ((c * HARD_THREAD_LIMIT) + t) + +#endif /* AP_MPM_DEFAULT_H */ diff --git a/server/mpm/mpmt_os2/mpmt_os2.c b/server/mpm/mpmt_os2/mpmt_os2.c new file mode 100644 index 0000000000..99806d1c8e --- /dev/null +++ b/server/mpm/mpmt_os2/mpmt_os2.c @@ -0,0 +1,587 @@ +/* ==================================================================== + * The Apache Software License, Version 1.1 + * + * Copyright (c) 2000-2001 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 + * <http://www.apache.org/>. + * + * Portions of this software are based upon public domain software + * originally written at the National Center for Supercomputing Applications, + * University of Illinois, Urbana-Champaign. + */ + +/* Multi-process, multi-threaded MPM for OS/2 + * + * Server consists of + * - a main, parent process + * - a small, static number of child processes + * + * The parent process's job is to manage the child processes. This involves + * spawning children as required to ensure there are always ap_daemons_to_start + * processes accepting connections. + * + * Each child process consists of a a pool of worker threads and a + * main thread that accepts connections & passes them to the workers via + * a work queue. The worker thread pool is dynamic, managed by a maintanence + * thread so that the number of idle threads is kept between + * min_spare_threads & max_spare_threads. + * + */ + +/* + Todo list + - Fix log file clashing between child processes + - Enforce MaxClients somehow + - Catch thread exceptions & initiate graceful shutdown of child process +*/ +#define CORE_PRIVATE +#define INCL_NOPMAPI +#define INCL_DOS +#define INCL_DOSERRORS + +#include "ap_config.h" +#include "httpd.h" +#include "mpm_default.h" +#include "http_main.h" +#include "http_log.h" +#include "http_config.h" +#include "http_core.h" /* for get_remote_host */ +#include "http_connection.h" +#include "mpm.h" +#include "ap_mpm.h" +#include "ap_listen.h" +#include "apr_portable.h" +#include "mpm_common.h" +#include "apr_strings.h" +#include <os2.h> +#include <process.h> + +server_rec *ap_server_conf; +static apr_pool_t *pconf = NULL; /* Pool for config stuff */ +static const char *ap_pid_fname=NULL; + +/* Config globals */ +static int one_process = 0; +static int ap_daemons_to_start = 0; +static int ap_thread_limit = 0; +static int ap_max_requests_per_child = 0; +int ap_min_spare_threads = 0; +int ap_max_spare_threads = 0; + +/* Keep track of a few interesting statistics */ +int ap_max_daemons_limit = -1; + +/* volatile just in case */ +static int volatile shutdown_pending; +static int volatile restart_pending; +static int volatile is_graceful = 0; +ap_generation_t volatile ap_my_generation=0; /* Used by the scoreboard */ +static int is_parent_process=TRUE; +HMTX ap_mpm_accept_mutex = 0; + +/* An array of these is stored in a shared memory area for passing + * sockets from the parent to child processes + */ +typedef struct { + struct sockaddr_in name; + apr_os_sock_t listen_fd; +} listen_socket_t; + +typedef struct { + apr_os_file_t errorlog_fd; + apr_time_t restart_time; + HMTX accept_mutex; + listen_socket_t listeners[1]; +} parent_info_t; + +static char master_main(); +static void spawn_child(int slot); +void ap_mpm_child_main(apr_pool_t *pconf); +static void set_signals(); + + +int ap_mpm_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s ) +{ + char *listener_shm_name; + parent_info_t *parent_info; + ULONG rc; + pconf = _pconf; + ap_server_conf = s; + restart_pending = 0; + + DosSetMaxFH(ap_thread_limit * 2); + listener_shm_name = apr_psprintf(pconf, "/sharemem/httpd/parent_info.%d", getppid()); + rc = DosGetNamedSharedMem((PPVOID)&parent_info, listener_shm_name, PAG_READ); + is_parent_process = rc != 0; + ap_scoreboard_fname = apr_psprintf(pconf, "/sharemem/httpd/scoreboard.%d", is_parent_process ? getpid() : getppid()); + + if (rc == 0) { + /* Child process */ + ap_listen_rec *lr; + int num_listeners = 0; + + apr_file_close(ap_server_conf->error_log); + apr_os_file_put(&ap_server_conf->error_log, &parent_info->errorlog_fd, pconf); + ap_restart_time = parent_info->restart_time; + ap_mpm_accept_mutex = parent_info->accept_mutex; + + /* Set up a default listener if necessary */ + if (ap_listeners == NULL) { + ap_listen_rec *lr = apr_pcalloc(s->process->pool, sizeof(ap_listen_rec)); + ap_listeners = lr; + apr_sockaddr_info_get(&lr->bind_addr, "0.0.0.0", APR_UNSPEC, + DEFAULT_HTTP_PORT, 0, s->process->pool); + apr_socket_create(&lr->sd, lr->bind_addr->sa.sin.sin_family, + SOCK_STREAM, s->process->pool); + } + + for (lr = ap_listeners; lr; lr = lr->next) { + apr_os_sock_put(&lr->sd, &parent_info->listeners[num_listeners].listen_fd, pconf); + num_listeners++; + } + + DosFreeMem(parent_info); + + /* Do the work */ + ap_mpm_child_main(pconf); + + /* Outta here */ + return 1; + } + else { + /* Parent process */ + char restart; + is_parent_process = TRUE; + ap_log_pid(pconf, ap_pid_fname); + + if (ap_setup_listeners(ap_server_conf) < 1) { + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ALERT, 0, s, + "no listening sockets available, shutting down"); + return 1; + } + + restart = master_main(); + ++ap_my_generation; + ap_scoreboard_image->global.running_generation = ap_my_generation; + + if (!restart) { + const char *pidfile = ap_server_root_relative(pconf, ap_pid_fname); + + if (pidfile != NULL && remove(pidfile) == 0) { + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, APR_SUCCESS, + ap_server_conf, "removed PID file %s (pid=%d)", + pidfile, getpid()); + } + + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, 0, ap_server_conf, + "caught SIGTERM, shutting down"); + return 1; + } + } /* Parent process */ + + return 0; /* Restart */ +} + + + +/* Main processing of the parent process + * returns TRUE if restarting + */ +static char master_main() +{ + server_rec *s = ap_server_conf; + ap_listen_rec *lr; + parent_info_t *parent_info; + char *listener_shm_name; + int listener_num, num_listeners, slot; + ULONG rc; + + printf("%s \n", ap_get_server_version()); + set_signals(); + + if (ap_setup_listeners(ap_server_conf) < 1) { + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ALERT, 0, s, + "no listening sockets available, shutting down"); + return FALSE; + } + + /* Allocate a shared memory block for the array of listeners */ + for (num_listeners = 0, lr = ap_listeners; lr; lr = lr->next) { + num_listeners++; + } + + listener_shm_name = apr_psprintf(pconf, "/sharemem/httpd/parent_info.%d", getpid()); + rc = DosAllocSharedMem((PPVOID)&parent_info, listener_shm_name, + sizeof(parent_info_t) + num_listeners * sizeof(listen_socket_t), + PAG_READ|PAG_WRITE|PAG_COMMIT); + + if (rc) { + ap_log_error(APLOG_MARK, APLOG_ALERT, APR_FROM_OS_ERROR(rc), s, + "failure allocating shared memory, shutting down"); + return FALSE; + } + + /* Store the listener sockets in the shared memory area for our children to see */ + for (listener_num = 0, lr = ap_listeners; lr; lr = lr->next, listener_num++) { + apr_os_sock_get(&parent_info->listeners[listener_num].listen_fd, lr->sd); + } + + /* Create mutex to prevent multiple child processes from detecting + * a connection with apr_poll() + */ + + rc = DosCreateMutexSem(NULL, &ap_mpm_accept_mutex, DC_SEM_SHARED, FALSE); + + if (rc) { + ap_log_error(APLOG_MARK, APLOG_ALERT, APR_FROM_OS_ERROR(rc), s, + "failure creating accept mutex, shutting down"); + return FALSE; + } + + ap_restart_time = apr_time_now(); + parent_info->restart_time = ap_restart_time; + parent_info->accept_mutex = ap_mpm_accept_mutex; + apr_os_file_get(&parent_info->errorlog_fd, s->error_log); + + /* Allocate shared memory for scoreboard */ + if (ap_scoreboard_image == NULL) { + rc = DosAllocSharedMem((PPVOID)&ap_scoreboard_image, ap_scoreboard_fname, + sizeof(scoreboard), PAG_COMMIT|PAG_READ|PAG_WRITE); + + if (rc) { + ap_log_error(APLOG_MARK, APLOG_ERR, APR_FROM_OS_ERROR(rc), ap_server_conf, + "unable to allocate shared memory for scoreboard , exiting"); + return FALSE; + } + + memset(ap_scoreboard_image, 0, sizeof(scoreboard)); + } + + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, 0, ap_server_conf, + "%s configured -- resuming normal operations", + ap_get_server_version()); + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, 0, ap_server_conf, + "Server built: %s", ap_get_server_built()); + + if (one_process) { + ap_scoreboard_image->parent[0].pid = getpid(); + ap_mpm_child_main(pconf); + return FALSE; + } + + while (!restart_pending && !shutdown_pending) { + RESULTCODES proc_rc; + PID child_pid; + int active_children = 0; + + /* Count number of active children */ + for (slot=0; slot < HARD_SERVER_LIMIT; slot++) { + active_children += ap_scoreboard_image->parent[slot].pid != 0 && + !ap_scoreboard_image->parent[slot].quiescing; + } + + /* Spawn children if needed */ + for (slot=0; slot < HARD_SERVER_LIMIT && active_children < ap_daemons_to_start; slot++) { + if (ap_scoreboard_image->parent[slot].pid == 0) { + spawn_child(slot); + active_children++; + } + } + + rc = DosWaitChild(DCWA_PROCESSTREE, DCWW_NOWAIT, &proc_rc, &child_pid, 0); + + if (rc == 0) { + /* A child has terminated, remove its scoreboard entry & terminate if necessary */ + for (slot=0; ap_scoreboard_image->parent[slot].pid != child_pid && slot < HARD_SERVER_LIMIT; slot++); + + if (slot < HARD_SERVER_LIMIT) { + ap_scoreboard_image->parent[slot].pid = 0; + ap_scoreboard_image->parent[slot].quiescing = 0; + + if (proc_rc.codeTerminate == TC_EXIT) { + /* Child terminated normally, check its exit code and + * terminate server if child indicates a fatal error + */ + if (proc_rc.codeResult == APEXIT_CHILDFATAL) + break; + } + } + } else if (rc == ERROR_CHILD_NOT_COMPLETE) { + /* No child exited, lets sleep for a while.... */ + apr_sleep(SCOREBOARD_MAINTENANCE_INTERVAL); + } + } + + /* Signal children to shut down, either gracefully or immediately */ + for (slot=0; slot<HARD_SERVER_LIMIT; slot++) { + kill(ap_scoreboard_image->parent[slot].pid, is_graceful ? SIGHUP : SIGTERM); + } + + DosFreeMem(parent_info); + return restart_pending; +} + + + +static void spawn_child(int slot) +{ + PPIB ppib; + PTIB ptib; + char fail_module[100]; + char progname[CCHMAXPATH]; + RESULTCODES proc_rc; + ULONG rc; + + ap_scoreboard_image->parent[slot].generation = ap_my_generation; + DosGetInfoBlocks(&ptib, &ppib); + DosQueryModuleName(ppib->pib_hmte, sizeof(progname), progname); + rc = DosExecPgm(fail_module, sizeof(fail_module), EXEC_ASYNCRESULT, + ppib->pib_pchcmd, NULL, &proc_rc, progname); + + if (rc) { + ap_log_error(APLOG_MARK, APLOG_ERR, APR_FROM_OS_ERROR(rc), ap_server_conf, + "error spawning child, slot %d", slot); + } + + if (ap_max_daemons_limit < slot) { + ap_max_daemons_limit = slot; + } + + ap_scoreboard_image->parent[slot].pid = proc_rc.codeTerminate; +} + + + +/* Signal handling routines */ + +static void sig_term(int sig) +{ + shutdown_pending = 1; +} + + + +static void sig_restart(int sig) +{ + if (sig == SIGUSR1) { + is_graceful = 1; + } + + restart_pending = 1; +} + + + +static void set_signals() +{ + struct sigaction sa; + + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + sa.sa_handler = sig_term; + + if (sigaction(SIGTERM, &sa, NULL) < 0) + ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGTERM)"); + + if (sigaction(SIGINT, &sa, NULL) < 0) + ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGINT)"); + + sa.sa_handler = sig_restart; + + if (sigaction(SIGHUP, &sa, NULL) < 0) + ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGHUP)"); + if (sigaction(SIGUSR1, &sa, NULL) < 0) + ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGUSR1)"); +} + + + +/* Enquiry functions used get MPM status info */ + +AP_DECLARE(apr_status_t) ap_mpm_query(int query_code, int *result) +{ + switch (query_code) { + case AP_MPMQ_MAX_DAEMON_USED: + *result = ap_max_daemons_limit; + return APR_SUCCESS; + case AP_MPMQ_IS_THREADED: + *result = AP_MPMQ_DYNAMIC; + return APR_SUCCESS; + case AP_MPMQ_IS_FORKED: + *result = AP_MPMQ_NOT_SUPPORTED; + return APR_SUCCESS; + case AP_MPMQ_HARD_LIMIT_DAEMONS: + *result = HARD_SERVER_LIMIT; + return APR_SUCCESS; + case AP_MPMQ_HARD_LIMIT_THREADS: + *result = HARD_THREAD_LIMIT; + return APR_SUCCESS; + case AP_MPMQ_MIN_SPARE_DEAMONS: + *result = 0; + return APR_SUCCESS; + case AP_MPMQ_MAX_SPARE_DAEMONS: + *result = 0; + return APR_SUCCESS; + case AP_MPMQ_MAX_REQUESTS_DEAMON: + *result = ap_max_requests_per_child; + return APR_SUCCESS; + } + return APR_ENOTIMPL; +} + + + +int ap_graceful_stop_signalled(void) +{ + return is_graceful; +} + + + +/* Configuration handling stuff */ + +static void mpmt_os2_pre_config(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *ptemp) +{ + one_process = !!ap_exists_config_define("ONE_PROCESS"); + is_graceful = 0; + ap_listen_pre_config(); + ap_daemons_to_start = DEFAULT_START_DAEMON; + ap_thread_limit = HARD_THREAD_LIMIT; + ap_pid_fname = DEFAULT_PIDLOG; + ap_max_requests_per_child = DEFAULT_MAX_REQUESTS_PER_CHILD; + ap_extended_status = 0; + ap_min_spare_threads = DEFAULT_MIN_SPARE_THREAD; + ap_max_spare_threads = DEFAULT_MAX_SPARE_THREAD; +} + + + +static void mpmt_os2_hooks(apr_pool_t *p) +{ + ap_hook_pre_config(mpmt_os2_pre_config, NULL, NULL, APR_HOOK_MIDDLE); +} + + + +static const char *set_daemons_to_start(cmd_parms *cmd, void *dummy, const char *arg) +{ + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + + if (err != NULL) { + return err; + } + + ap_daemons_to_start = atoi(arg); + return NULL; +} + + + +static const char *set_min_spare_threads(cmd_parms *cmd, void *dummy, + const char *arg) +{ + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + + if (err != NULL) { + return err; + } + + ap_min_spare_threads = atoi(arg); + + if (ap_min_spare_threads <= 0) { + ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL, + "WARNING: detected MinSpareThreads set to non-positive."); + ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL, + "Resetting to 1 to avoid almost certain Apache failure."); + ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL, + "Please read the documentation."); + ap_min_spare_threads = 1; + } + + return NULL; +} + + + +static const char *set_max_spare_threads(cmd_parms *cmd, void *dummy, + const char *arg) +{ + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + + if (err != NULL) { + return err; + } + + ap_max_spare_threads = atoi(arg); + return NULL; +} + + + +static const command_rec mpmt_os2_cmds[] = { +LISTEN_COMMANDS +AP_INIT_TAKE1( "StartServers", set_daemons_to_start, NULL, RSRC_CONF, + "Number of child processes launched at server startup" ), +AP_INIT_TAKE1("MinSpareThreads", set_min_spare_threads, NULL, RSRC_CONF, + "Minimum number of idle children, to handle request spikes"), +AP_INIT_TAKE1("MaxSpareThreads", set_max_spare_threads, NULL, RSRC_CONF, + "Maximum number of idle children"), +{ NULL } +}; + +module AP_MODULE_DECLARE_DATA mpm_mpmt_os2_module = { + MPM20_MODULE_STUFF, + NULL, /* hook to run before apache parses args */ + NULL, /* create per-directory config structure */ + NULL, /* merge per-directory config structures */ + NULL, /* create per-server config structure */ + NULL, /* merge per-server config structures */ + mpmt_os2_cmds, /* command apr_table_t */ + mpmt_os2_hooks, /* register_hooks */ +}; diff --git a/server/mpm/mpmt_os2/mpmt_os2_child.c b/server/mpm/mpmt_os2/mpmt_os2_child.c new file mode 100644 index 0000000000..36a96ca939 --- /dev/null +++ b/server/mpm/mpmt_os2/mpmt_os2_child.c @@ -0,0 +1,463 @@ +/* ==================================================================== + * The Apache Software License, Version 1.1 + * + * Copyright (c) 2000-2001 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 + * <http://www.apache.org/>. + * + * Portions of this software are based upon public domain software + * originally written at the National Center for Supercomputing Applications, + * University of Illinois, Urbana-Champaign. + */ + +#define CORE_PRIVATE +#define INCL_NOPMAPI +#define INCL_DOS +#define INCL_DOSERRORS + +#include "ap_config.h" +#include "httpd.h" +#include "mpm_default.h" +#include "http_main.h" +#include "http_log.h" +#include "http_config.h" +#include "http_core.h" /* for get_remote_host */ +#include "http_connection.h" +#include "mpm.h" +#include "ap_mpm.h" +#include "ap_listen.h" +#include "apr_portable.h" +#include "mpm_common.h" +#include "apr_strings.h" +#include <os2.h> +#include <process.h> + +typedef struct { + apr_pool_t *pconn; + apr_socket_t *conn_sd; +} worker_args_t; + +#define WORKTYPE_CONN 0 +#define WORKTYPE_EXIT 1 + +static apr_pool_t *pchild = NULL; +static int child_slot; +static int shutdown_pending = 0; +extern int ap_my_generation; +static int volatile is_graceful = 1; +HEV shutdown_event; /* signaled when this child is shutting down */ + +/* grab some MPM globals */ +extern int ap_min_spare_threads; +extern int ap_max_spare_threads; +extern HMTX ap_mpm_accept_mutex; + +static void worker_main(void *vpArg); +static void clean_child_exit(int code); +static void set_signals(); +static void server_maintenance(void *vpArg); + + +static void clean_child_exit(int code) +{ + if (pchild) { + apr_pool_destroy(pchild); + } + + exit(code); +} + + + +void ap_mpm_child_main(apr_pool_t *pconf) +{ + ap_listen_rec *lr = NULL; + ap_listen_rec *first_lr = NULL; + int requests_this_child = 0; + apr_socket_t *sd = ap_listeners->sd; + int nsds, rv = 0; + unsigned long ulTimes; + int my_pid = getpid(); + ULONG rc, c; + HQUEUE workq; + apr_pollfd_t *pollset; + int num_listeners; + TID server_maint_tid; + + /* Stop Ctrl-C/Ctrl-Break signals going to child processes */ + DosSetSignalExceptionFocus(0, &ulTimes); + set_signals(); + + /* Create pool for child */ + apr_pool_create(&pchild, pconf); + + /* Create an event semaphore used to trigger other threads to shutdown */ + rc = DosCreateEventSem(NULL, &shutdown_event, 0, FALSE); + + if (rc) { + ap_log_error(APLOG_MARK, APLOG_ERR, APR_FROM_OS_ERROR(rc), ap_server_conf, + "unable to create shutdown semaphore, exiting"); + clean_child_exit(APEXIT_CHILDFATAL); + } + + /* Gain access to the scoreboard. */ + rc = DosGetNamedSharedMem((PPVOID)&ap_scoreboard_image, ap_scoreboard_fname, + PAG_READ|PAG_WRITE); + + if (rc) { + ap_log_error(APLOG_MARK, APLOG_ERR, APR_FROM_OS_ERROR(rc), ap_server_conf, + "scoreboard not readable in child, exiting"); + clean_child_exit(APEXIT_CHILDFATAL); + } + + /* Gain access to the accpet mutex */ + rc = DosOpenMutexSem(NULL, &ap_mpm_accept_mutex); + + if (rc) { + ap_log_error(APLOG_MARK, APLOG_ERR, APR_FROM_OS_ERROR(rc), ap_server_conf, + "accept mutex couldn't be accessed in child, exiting"); + clean_child_exit(APEXIT_CHILDFATAL); + } + + /* Find our pid in the scoreboard so we know what slot our parent allocated us */ + for (child_slot = 0; ap_scoreboard_image->parent[child_slot].pid != my_pid && child_slot < HARD_SERVER_LIMIT; child_slot++); + + if (child_slot == HARD_SERVER_LIMIT) { + ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, ap_server_conf, + "child pid not found in scoreboard, exiting"); + clean_child_exit(APEXIT_CHILDFATAL); + } + + ap_my_generation = ap_scoreboard_image->parent[child_slot].generation; + + /* Set up an OS/2 queue for passing connections & termination requests + * to worker threads + */ + rc = DosCreateQueue(&workq, QUE_FIFO, apr_psprintf(pchild, "/queues/httpd/work.%d", my_pid)); + + if (rc) { + ap_log_error(APLOG_MARK, APLOG_ERR, APR_FROM_OS_ERROR(rc), ap_server_conf, + "unable to create work queue, exiting"); + clean_child_exit(APEXIT_CHILDFATAL); + } + + /* Create initial pool of worker threads */ + for (c = 0; c < ap_min_spare_threads; c++) { +// ap_scoreboard_image->servers[child_slot][c].tid = _beginthread(worker_main, NULL, 128*1024, (void *)c); + } + + /* Start maintenance thread */ + server_maint_tid = _beginthread(server_maintenance, NULL, 32768, NULL); + + /* Set up poll */ + for (num_listeners = 0, lr = ap_listeners; lr; lr = lr->next) { + num_listeners++; + } + + apr_poll_setup(&pollset, num_listeners, pchild); + + for (lr = ap_listeners; lr; lr = lr->next) { + apr_poll_socket_add(pollset, lr->sd, APR_POLLIN); + } + + /* Main connection accept loop */ + do { + apr_pool_t *pconn; + worker_args_t *worker_args; + + apr_pool_create(&pconn, pchild); + worker_args = apr_palloc(pconn, sizeof(worker_args_t)); + worker_args->pconn = pconn; + + if (num_listeners == 1) { + rv = apr_accept(&worker_args->conn_sd, ap_listeners->sd, pconn); + } else { + rc = DosRequestMutexSem(ap_mpm_accept_mutex, SEM_INDEFINITE_WAIT); + + if (shutdown_pending) { + DosReleaseMutexSem(ap_mpm_accept_mutex); + break; + } + + rv = APR_FROM_OS_ERROR(rc); + + if (rv == APR_SUCCESS) { + rv = apr_poll(pollset, &nsds, -1); + DosReleaseMutexSem(ap_mpm_accept_mutex); + } + + if (rv == APR_SUCCESS) { + if (first_lr == NULL) { + first_lr = ap_listeners; + } + + lr = first_lr; + + do { + apr_int16_t event; + + apr_poll_revents_get(&event, lr->sd, pollset); + + if (event == APR_POLLIN) { + apr_sockaddr_t *sa; + apr_port_t port; + apr_socket_addr_get(&sa, APR_LOCAL, lr->sd); + apr_sockaddr_port_get(&port, sa); + first_lr = lr->next; + break; + } + lr = lr->next; + + if (!lr) { + lr = ap_listeners; + } + } while (lr != first_lr); + + if (lr == first_lr) { + continue; + } + + sd = lr->sd; + rv = apr_accept(&worker_args->conn_sd, sd, pconn); + } + } + + if (rv != APR_SUCCESS) { + if (!APR_STATUS_IS_EINTR(rv)) { + ap_log_error(APLOG_MARK, APLOG_ERR, rv, ap_server_conf, + "apr_accept"); + clean_child_exit(APEXIT_CHILDFATAL); + } + } else { + DosWriteQueue(workq, WORKTYPE_CONN, sizeof(worker_args_t), worker_args, 0); + requests_this_child++; + } + + if (ap_max_requests_per_child != 0 && requests_this_child >= ap_max_requests_per_child) + break; + } while (!shutdown_pending && ap_my_generation == ap_scoreboard_image->global.running_generation); + + ap_scoreboard_image->parent[child_slot].quiescing = 1; + DosPostEventSem(shutdown_event); + DosWaitThread(&server_maint_tid, DCWW_WAIT); + + if (is_graceful) { + char someleft; + + /* tell our worker threads to exit */ + for (c=0; c<HARD_THREAD_LIMIT; c++) { + if (ap_scoreboard_image->servers[child_slot][c].status != SERVER_DEAD) { + DosWriteQueue(workq, WORKTYPE_EXIT, 0, NULL, 0); + } + } + + do { + someleft = 0; + + for (c=0; c<HARD_THREAD_LIMIT; c++) { + if (ap_scoreboard_image->servers[child_slot][c].status != SERVER_DEAD) { + someleft = 1; + DosSleep(1000); + break; + } + } + } while (someleft); + } else { + DosPurgeQueue(workq); + + for (c=0; c<HARD_THREAD_LIMIT; c++) { + if (ap_scoreboard_image->servers[child_slot][c].status != SERVER_DEAD) { + DosKillThread(ap_scoreboard_image->servers[child_slot][c].tid); + } + } + } + + apr_pool_destroy(pchild); +} + + + +void add_worker() +{ + int thread_slot; + + /* Find a free thread slot */ + for (thread_slot=0; thread_slot < HARD_THREAD_LIMIT; thread_slot++) { + if (ap_scoreboard_image->servers[child_slot][thread_slot].status == SERVER_DEAD) { + ap_scoreboard_image->servers[child_slot][thread_slot].status = SERVER_STARTING; + ap_scoreboard_image->servers[child_slot][thread_slot].tid = + _beginthread(worker_main, NULL, 128*1024, (void *)thread_slot); + break; + } + } +} + + + +static void worker_main(void *vpArg) +{ + long conn_id; + conn_rec *current_conn; + apr_pool_t *pconn; + worker_args_t *worker_args; + HQUEUE workq; + PID owner; + int rc; + REQUESTDATA rd; + ULONG len; + BYTE priority; + int thread_slot = (int)vpArg; + + rc = DosOpenQueue(&owner, &workq, + apr_psprintf(pchild, "/queues/httpd/work.%d", getpid())); + + if (rc) { + ap_log_error(APLOG_MARK, APLOG_ERR, APR_FROM_OS_ERROR(rc), ap_server_conf, + "unable to open work queue, exiting"); + ap_scoreboard_image->servers[child_slot][thread_slot].tid = 0; + } + + conn_id = AP_ID_FROM_CHILD_THREAD(child_slot, thread_slot); + ap_update_child_status(child_slot, thread_slot, SERVER_READY, NULL); + + while (rc = DosReadQueue(workq, &rd, &len, (PPVOID)&worker_args, 0, DCWW_WAIT, &priority, NULLHANDLE), + rc == 0 && rd.ulData != WORKTYPE_EXIT) { + pconn = worker_args->pconn; + ap_sock_disable_nagle(worker_args->conn_sd); + current_conn = ap_new_connection(pconn, ap_server_conf, worker_args->conn_sd, conn_id); + + if (current_conn) { + ap_process_connection(current_conn); + ap_lingering_close(current_conn); + } + + apr_pool_destroy(pconn); + ap_update_child_status(child_slot, thread_slot, SERVER_READY, NULL); + } + + ap_update_child_status(child_slot, thread_slot, SERVER_DEAD, NULL); +} + + + +static void server_maintenance(void *vpArg) +{ + int num_idle, num_needed; + ULONG num_pending = 0; + int threadnum; + HQUEUE workq; + ULONG rc; + PID owner; + + rc = DosOpenQueue(&owner, &workq, + apr_psprintf(pchild, "/queues/httpd/work.%d", getpid())); + + if (rc) { + ap_log_error(APLOG_MARK, APLOG_ERR, APR_FROM_OS_ERROR(rc), ap_server_conf, + "unable to open work queue in maintenance thread"); + return; + } + + do { + for (num_idle=0, threadnum=0; threadnum < HARD_THREAD_LIMIT; threadnum++) { + num_idle += ap_scoreboard_image->servers[child_slot][threadnum].status == SERVER_READY; + } + + DosQueryQueue(workq, &num_pending); + num_needed = ap_min_spare_threads - num_idle + num_pending; + + if (num_needed > 0) { + for (threadnum=0; threadnum < num_needed; threadnum++) { + add_worker(); + } + } + + if (num_idle - num_pending > ap_max_spare_threads) { + DosWriteQueue(workq, WORKTYPE_EXIT, 0, NULL, 0); + } + } while (DosWaitEventSem(shutdown_event, 500) == ERROR_TIMEOUT); +} + + + +/* Signal handling routines */ + +static void sig_term(int sig) +{ + shutdown_pending = 1; + is_graceful = 0; + signal(SIGTERM, SIG_DFL); +} + + + +static void sig_hup(int sig) +{ + shutdown_pending = 1; + is_graceful = 1; +} + + + +static void set_signals() +{ + struct sigaction sa; + + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + sa.sa_handler = sig_term; + + if (sigaction(SIGTERM, &sa, NULL) < 0) + ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGTERM)"); + + sa.sa_handler = sig_hup; + + if (sigaction(SIGHUP, &sa, NULL) < 0) + ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGHUP)"); +} diff --git a/server/mpm/worker/.cvsignore b/server/mpm/worker/.cvsignore new file mode 100644 index 0000000000..84df257214 --- /dev/null +++ b/server/mpm/worker/.cvsignore @@ -0,0 +1,5 @@ +.deps +.libs +*.lo +*.la +Makefile diff --git a/server/mpm/worker/Makefile.in b/server/mpm/worker/Makefile.in new file mode 100644 index 0000000000..64a02436f3 --- /dev/null +++ b/server/mpm/worker/Makefile.in @@ -0,0 +1,5 @@ + +LTLIBRARY_NAME = libworker.la +LTLIBRARY_SOURCES = worker.c fdqueue.c + +include $(top_srcdir)/build/ltlib.mk diff --git a/server/mpm/worker/config5.m4 b/server/mpm/worker/config5.m4 new file mode 100644 index 0000000000..52ab50e82c --- /dev/null +++ b/server/mpm/worker/config5.m4 @@ -0,0 +1,5 @@ +dnl ## XXX - Need a more thorough check of the proper flags to use + +if test "$MPM_NAME" = "worker" ; then + APACHE_FAST_OUTPUT(server/mpm/$MPM_NAME/Makefile) +fi diff --git a/server/mpm/worker/fdqueue.c b/server/mpm/worker/fdqueue.c new file mode 100644 index 0000000000..147a3b7a94 --- /dev/null +++ b/server/mpm/worker/fdqueue.c @@ -0,0 +1,161 @@ +/* ==================================================================== + * The Apache Software License, Version 1.1 + * + * Copyright (c) 2000-2001 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 + * <http://www.apache.org/>. + * + * Portions of this software are based upon public domain software + * originally written at the National Center for Supercomputing Applications, + * University of Illinois, Urbana-Champaign. + */ + +#include "fdqueue.h" +#include "apr_pools.h" + +/* Assumption: queue itself is allocated by the user */ +/* Assumption: increment and decrement are atomic on int */ + +int ap_queue_size(FDQueue *queue) { + return ((queue->tail - queue->head + queue->bounds) % queue->bounds); +} + +int ap_queue_full(FDQueue *queue) { + return(queue->blanks <= 0); +} + +int ap_block_on_queue(FDQueue *queue) { +#if 0 + if (pthread_mutex_lock(&queue->one_big_mutex) != 0) { + return FD_QUEUE_FAILURE; + } +#endif + if (ap_queue_full(queue)) { + pthread_cond_wait(&queue->not_full, &queue->one_big_mutex); + } +#if 0 + if (pthread_mutex_unlock(&queue->one_big_mutex) != 0) { + return FD_QUEUE_FAILURE; + } +#endif + return FD_QUEUE_SUCCESS; +} + +static int increase_blanks(FDQueue *queue) { + queue->blanks++; + return FD_QUEUE_SUCCESS; +} + +static apr_status_t ap_queue_destroy(void *data) { + FDQueue *queue = data; + /* Ignore errors here, we can't do anything about them anyway */ + pthread_cond_destroy(&queue->not_empty); + pthread_cond_destroy(&queue->not_full); + pthread_mutex_destroy(&queue->one_big_mutex); + return FD_QUEUE_SUCCESS; +} + +int ap_queue_init(FDQueue *queue, int queue_capacity, apr_pool_t *a) { + int i; + int bounds = queue_capacity + 1; + pthread_mutex_init(&queue->one_big_mutex, NULL); + pthread_cond_init(&queue->not_empty, NULL); + pthread_cond_init(&queue->not_full, NULL); + queue->head = queue->tail = 0; + queue->data = apr_palloc(a, bounds * sizeof(FDQueueElement)); + queue->bounds = bounds; + queue->blanks = queue_capacity; + apr_pool_cleanup_register(a, queue, ap_queue_destroy, apr_pool_cleanup_null); + for (i=0; i < bounds; ++i) + queue->data[i].sd = NULL; + return FD_QUEUE_SUCCESS; +} + +int ap_queue_push(FDQueue *queue, apr_socket_t *sd, apr_pool_t *p) { + queue->data[queue->tail].sd = sd; + queue->data[queue->tail].p = p; + queue->tail = (queue->tail + 1) % queue->bounds; + queue->blanks--; + pthread_cond_signal(&queue->not_empty); +#if 0 + if (queue->head == (queue->tail + 1) % queue->bounds) { +#endif + if (ap_queue_full(queue)) { + pthread_cond_wait(&queue->not_full, &queue->one_big_mutex); + } + return FD_QUEUE_SUCCESS; +} + +apr_status_t ap_queue_pop(FDQueue *queue, apr_socket_t **sd, apr_pool_t **p, int block_if_empty) { + increase_blanks(queue); + /* We have just removed one from the queue. By definition, it is + * no longer full. We can ALWAYS signal the listener thread at + * this point. However, the original code didn't do it this way, + * so I am leaving the original code in, just commented out. BTW, + * originally, the increase_blanks wasn't in this function either. + * + if (queue->blanks > 0) { + */ + pthread_cond_signal(&queue->not_full); + + /* } */ + if (queue->head == queue->tail) { + if (block_if_empty) { + pthread_cond_wait(&queue->not_empty, &queue->one_big_mutex); + } + } + + *sd = queue->data[queue->head].sd; + *p = queue->data[queue->head].p; + queue->data[queue->head].sd = NULL; + if (*sd != NULL) { + queue->head = (queue->head + 1) % queue->bounds; + } + return APR_SUCCESS; +} diff --git a/server/mpm/worker/fdqueue.h b/server/mpm/worker/fdqueue.h new file mode 100644 index 0000000000..b1cab2d018 --- /dev/null +++ b/server/mpm/worker/fdqueue.h @@ -0,0 +1,95 @@ +/* ==================================================================== + * The Apache Software License, Version 1.1 + * + * Copyright (c) 2000-2001 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 + * <http://www.apache.org/>. + * + * 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 FDQUEUE_H +#define FDQUEUE_H +#include "httpd.h" +#include <stdlib.h> +#include <unistd.h> +#include <pthread.h> +#include <sys/types.h> +#include <sys/socket.h> + +#define FD_QUEUE_SUCCESS 0 +#define FD_QUEUE_FAILURE -1 /* Needs to be an invalid file descriptor because + of queue_pop semantics */ + +typedef struct fd_queue_elem { + apr_socket_t *sd; + apr_pool_t *p; +} FDQueueElement; + +typedef struct fd_queue { + int head; + int tail; + FDQueueElement *data; + int bounds; + int blanks; + pthread_mutex_t one_big_mutex; + pthread_cond_t not_empty; + pthread_cond_t not_full; +} FDQueue; + +int ap_queue_init(FDQueue *queue, int queue_size, apr_pool_t *a); +int ap_queue_push(FDQueue *queue, apr_socket_t *sd, apr_pool_t *p); +apr_status_t ap_queue_pop(FDQueue *queue, apr_socket_t **sd, apr_pool_t **p, int block_if_empty); +int ap_queue_size(FDQueue *queue); +int ap_queue_full(FDQueue *queue); +int ap_block_on_queue(FDQueue *queue); + +#endif /* FDQUEUE_H */ diff --git a/server/mpm/worker/mpm.h b/server/mpm/worker/mpm.h new file mode 100644 index 0000000000..23ed27c1ea --- /dev/null +++ b/server/mpm/worker/mpm.h @@ -0,0 +1,78 @@ +/* ==================================================================== + * The Apache Software License, Version 1.1 + * + * Copyright (c) 2000-2001 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 + * <http://www.apache.org/>. + * + * Portions of this software are based upon public domain software + * originally written at the National Center for Supercomputing Applications, + * University of Illinois, Urbana-Champaign. + */ +#include "scoreboard.h" +#include "unixd.h" + +#ifndef APACHE_MPM_WORKER_H +#define APACHE_MPM_WORKER_H + +#define WORKER_MPM + +#define MPM_NAME "Worker" + +#define AP_MPM_NEEDS_RECLAIM_CHILD_PROCESSES 1 +#define MPM_SYNC_CHILD_TABLE() (ap_sync_scoreboard_image()) +#define MPM_CHILD_PID(i) (ap_scoreboard_image->parent[i].pid) +#define MPM_NOTE_CHILD_KILLED(i) (MPM_CHILD_PID(i) = 0) + +extern int ap_threads_per_child; +extern int ap_max_daemons_limit; +extern server_rec *ap_server_conf; +extern char ap_coredump_dir[MAX_STRING_LEN]; + +#endif /* APACHE_MPM_WORKER_H */ diff --git a/server/mpm/worker/mpm_default.h b/server/mpm/worker/mpm_default.h new file mode 100644 index 0000000000..84c1304bfa --- /dev/null +++ b/server/mpm/worker/mpm_default.h @@ -0,0 +1,154 @@ +/* ==================================================================== + * The Apache Software License, Version 1.1 + * + * Copyright (c) 2000-2001 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 + * <http://www.apache.org/>. + * + * 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 APACHE_MPM_DEFAULT_H +#define APACHE_MPM_DEFAULT_H + +#define AP_ID_FROM_CHILD_THREAD(c, t) ((c * HARD_THREAD_LIMIT) + t) +#define AP_CHILD_THREAD_FROM_ID(i) (i / HARD_THREAD_LIMIT), (i % HARD_THREAD_LIMIT) + +/* Number of servers to spawn off by default --- also, if fewer than + * this free when the caretaker checks, it will spawn more. + */ +#ifndef DEFAULT_START_DAEMON +#define DEFAULT_START_DAEMON 3 +#endif + +/* Maximum number of *free* server processes --- more than this, and + * they will die off. + */ + +#ifndef DEFAULT_MAX_FREE_DAEMON +#define DEFAULT_MAX_FREE_DAEMON 10 +#endif + +/* Minimum --- fewer than this, and more will be created */ + +#ifndef DEFAULT_MIN_FREE_DAEMON +#define DEFAULT_MIN_FREE_DAEMON 3 +#endif + +/* Limit on the total --- clients will be locked out if more servers than + * this are needed. It is intended solely to keep the server from crashing + * when things get out of hand. + * + * We keep a hard maximum number of servers, for two reasons --- first off, + * in case something goes seriously wrong, we want to stop the fork bomb + * short of actually crashing the machine we're running on by filling some + * kernel table. Secondly, it keeps the size of the scoreboard file small + * enough that we can read the whole thing without worrying too much about + * the overhead. + */ +#ifdef NO_THREADS +#define HARD_SERVER_LIMIT 256 +#endif +#ifndef HARD_SERVER_LIMIT +#define HARD_SERVER_LIMIT 8 +#endif + +/* Limit on the threads per process. Clients will be locked out if more than + * this * HARD_SERVER_LIMIT are needed. + * + * We keep this for one reason it keeps the size of the scoreboard file small + * enough that we can read the whole thing without worrying too much about + * the overhead. + */ +#ifdef NO_THREADS +#define HARD_THREAD_LIMIT 1 +#endif +#ifndef HARD_THREAD_LIMIT +#define HARD_THREAD_LIMIT 64 +#endif + +#ifdef NO_THREADS +#define DEFAULT_THREADS_PER_CHILD 1 +#endif +#ifndef DEFAULT_THREADS_PER_CHILD +#define DEFAULT_THREADS_PER_CHILD 25 +#endif + +/* File used for accept locking, when we use a file */ +#ifndef DEFAULT_LOCKFILE +#define DEFAULT_LOCKFILE "logs/accept.lock" +#endif + +/* Scoreboard file, if there is one */ +#ifndef DEFAULT_SCOREBOARD +#define DEFAULT_SCOREBOARD "logs/apache_runtime_status" +#endif + +/* Where the main/parent process's pid is logged */ +#ifndef DEFAULT_PIDLOG +#define DEFAULT_PIDLOG "logs/httpd.pid" +#endif + +/* + * Interval, in microseconds, between scoreboard maintenance. + */ +#ifndef SCOREBOARD_MAINTENANCE_INTERVAL +#define SCOREBOARD_MAINTENANCE_INTERVAL 1000000 +#endif + +/* Number of requests to try to handle in a single process. If <= 0, + * the children don't die off. + */ +#ifndef DEFAULT_MAX_REQUESTS_PER_CHILD +#define DEFAULT_MAX_REQUESTS_PER_CHILD 10000 +#endif + +#endif /* AP_MPM_DEFAULT_H */ diff --git a/server/mpm/worker/worker.c b/server/mpm/worker/worker.c new file mode 100644 index 0000000000..9f969af51c --- /dev/null +++ b/server/mpm/worker/worker.c @@ -0,0 +1,1660 @@ +/* ==================================================================== + * The Apache Software License, Version 1.1 + * + * Copyright (c) 2000-2001 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 + * <http://www.apache.org/>. + * + * Portions of this software are based upon public domain software + * originally written at the National Center for Supercomputing Applications, + * University of Illinois, Urbana-Champaign. + */ + +#include "apr.h" +#include "apr_portable.h" +#include "apr_strings.h" +#include "apr_file_io.h" +#include "apr_thread_proc.h" +#include "apr_signal.h" +#define APR_WANT_STRFUNC +#include "apr_want.h" + +#if APR_HAVE_UNISTD_H +#include <unistd.h> +#endif +#if APR_HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif +#if APR_HAVE_SYS_WAIT_H +#include <sys/wait.h> +#endif +#ifdef HAVE_SYS_PROCESSOR_H +#include <sys/processor.h> /* for bindprocessor() */ +#endif + +#if !APR_HAS_THREADS +#error The Worker MPM requires APR threads, but they are unavailable. +#endif + +#define CORE_PRIVATE + +#include "ap_config.h" +#include "httpd.h" +#include "http_main.h" +#include "http_log.h" +#include "http_config.h" /* for read_config */ +#include "http_core.h" /* for get_remote_host */ +#include "http_connection.h" +#include "ap_mpm.h" +#include "unixd.h" +#include "mpm_common.h" +#include "ap_listen.h" +#include "scoreboard.h" +#include "fdqueue.h" + +#include <signal.h> +#include <limits.h> /* for INT_MAX */ + +/* + * Actual definitions of config globals + */ + +int ap_threads_per_child=0; /* Worker threads per child */ +static int ap_max_requests_per_child=0; +static const char *ap_pid_fname=NULL; +static int ap_daemons_to_start=0; +static int min_spare_threads=0; +static int max_spare_threads=0; +static int ap_daemons_limit=0; +static int dying = 0; +static int workers_may_exit = 0; +static int requests_this_child; +static int num_listensocks = 0; +static apr_socket_t **listensocks; +static FDQueue *worker_queue; + +/* The structure used to pass unique initialization info to each thread */ +typedef struct { + int pid; + int tid; + int sd; + apr_pool_t *tpool; /* "pthread" would be confusing */ +} proc_info; + +/* Structure used to pass information to the thread responsible for + * creating the rest of the threads. + */ +typedef struct { + apr_thread_t **threads; + int child_num_arg; + apr_threadattr_t *threadattr; +} thread_starter; + +/* + * The max child slot ever assigned, preserved across restarts. Necessary + * to deal with MaxClients changes across SIGWINCH restarts. We use this + * value to optimize routines that have to scan the entire scoreboard. + */ +int ap_max_daemons_limit = -1; + +char ap_coredump_dir[MAX_STRING_LEN]; + +static apr_file_t *pipe_of_death_in = NULL; +static apr_file_t *pipe_of_death_out = NULL; +static apr_lock_t *pipe_of_death_mutex; /* insures that a child process only + consumes one character */ + +/* *Non*-shared http_main globals... */ + +server_rec *ap_server_conf; + +/* one_process --- debugging mode variable; can be set from the command line + * with the -X flag. If set, this gets you the child_main loop running + * in the process which originally started up (no detach, no make_child), + * which is a pretty nice debugging environment. (You'll get a SIGHUP + * early in standalone_main; just continue through. This is the server + * trying to kill off any child processes which it might have lying + * around --- Apache doesn't keep track of their pids, it just sends + * SIGHUP to the process group, ignoring it in the root process. + * Continue through and you'll be fine.). + */ + +static int one_process = 0; + +#ifdef DEBUG_SIGSTOP +int raise_sigstop_flags; +#endif + +static apr_pool_t *pconf; /* Pool for config stuff */ +static apr_pool_t *pchild; /* Pool for httpd child stuff */ + +static pid_t ap_my_pid; /* Linux getpid() doesn't work except in main + thread. Use this instead */ +/* Keep track of the number of worker threads currently active */ +static int worker_thread_count; +static apr_lock_t *worker_thread_count_mutex; + +/* Locks for accept serialization */ +static apr_lock_t *accept_mutex; +static apr_lockmech_e_np accept_lock_mech = APR_LOCK_DEFAULT; +static const char *lock_fname; + +#ifdef NO_SERIALIZED_ACCEPT +#define SAFE_ACCEPT(stmt) APR_SUCCESS +#else +#define SAFE_ACCEPT(stmt) (stmt) +#endif + +AP_DECLARE(apr_status_t) ap_mpm_query(int query_code, int *result) +{ + switch(query_code){ + case AP_MPMQ_MAX_DAEMON_USED: + *result = ap_max_daemons_limit; + return APR_SUCCESS; + case AP_MPMQ_IS_THREADED: + *result = AP_MPMQ_STATIC; + return APR_SUCCESS; + case AP_MPMQ_IS_FORKED: + *result = AP_MPMQ_DYNAMIC; + return APR_SUCCESS; + case AP_MPMQ_HARD_LIMIT_DAEMONS: + *result = HARD_SERVER_LIMIT; + return APR_SUCCESS; + case AP_MPMQ_HARD_LIMIT_THREADS: + *result = HARD_THREAD_LIMIT; + return APR_SUCCESS; + case AP_MPMQ_MAX_THREADS: + *result = ap_threads_per_child; + return APR_SUCCESS; + case AP_MPMQ_MIN_SPARE_DEAMONS: + *result = 0; + return APR_SUCCESS; + case AP_MPMQ_MIN_SPARE_THREADS: + *result = min_spare_threads; + return APR_SUCCESS; + case AP_MPMQ_MAX_SPARE_DAEMONS: + *result = 0; + return APR_SUCCESS; + case AP_MPMQ_MAX_SPARE_THREADS: + *result = max_spare_threads; + return APR_SUCCESS; + case AP_MPMQ_MAX_REQUESTS_DEAMON: + *result = ap_max_requests_per_child; + return APR_SUCCESS; + case AP_MPMQ_MAX_DAEMONS: + *result = ap_daemons_limit; + return APR_SUCCESS; + } + return APR_ENOTIMPL; +} + +/* a clean exit from a child with proper cleanup */ +static void clean_child_exit(int code) __attribute__ ((noreturn)); +static void clean_child_exit(int code) +{ + if (pchild) { + apr_pool_destroy(pchild); + } + exit(code); +} + +/* handle all varieties of core dumping signals */ +static void sig_coredump(int sig) +{ + chdir(ap_coredump_dir); + apr_signal(sig, SIG_DFL); + kill(ap_my_pid, sig); + /* At this point we've got sig blocked, because we're still inside + * the signal handler. When we leave the signal handler it will + * be unblocked, and we'll take the signal... and coredump or whatever + * is appropriate for this particular Unix. In addition the parent + * will see the real signal we received -- whereas if we called + * abort() here, the parent would only see SIGABRT. + */ +} + +static void just_die(int sig) +{ + clean_child_exit(0); +} + +/***************************************************************** + * Connection structures and accounting... + */ + +/* volatile just in case */ +static int volatile shutdown_pending; +static int volatile restart_pending; +static int volatile is_graceful; +ap_generation_t volatile ap_my_generation; + +/* + * ap_start_shutdown() and ap_start_restart(), below, are a first stab at + * functions to initiate shutdown or restart without relying on signals. + * Previously this was initiated in sig_term() and restart() signal handlers, + * but we want to be able to start a shutdown/restart from other sources -- + * e.g. on Win32, from the service manager. Now the service manager can + * call ap_start_shutdown() or ap_start_restart() as appropiate. Note that + * these functions can also be called by the child processes, since global + * variables are no longer used to pass on the required action to the parent. + * + * These should only be called from the parent process itself, since the + * parent process will use the shutdown_pending and restart_pending variables + * to determine whether to shutdown or restart. The child process should + * call signal_parent() directly to tell the parent to die -- this will + * cause neither of those variable to be set, which the parent will + * assume means something serious is wrong (which it will be, for the + * child to force an exit) and so do an exit anyway. + */ + +static void ap_start_shutdown(void) +{ + if (shutdown_pending == 1) { + /* Um, is this _probably_ not an error, if the user has + * tried to do a shutdown twice quickly, so we won't + * worry about reporting it. + */ + return; + } + shutdown_pending = 1; +} + +/* do a graceful restart if graceful == 1 */ +static void ap_start_restart(int graceful) +{ + + if (restart_pending == 1) { + /* Probably not an error - don't bother reporting it */ + return; + } + restart_pending = 1; + is_graceful = graceful; + if (is_graceful) { + apr_pool_cleanup_kill(pconf, NULL, ap_cleanup_scoreboard); + } +} + +static void sig_term(int sig) +{ + ap_start_shutdown(); +} + +static void restart(int sig) +{ + ap_start_restart(sig == SIGWINCH); +} + +static void set_signals(void) +{ +#ifndef NO_USE_SIGACTION + struct sigaction sa; + + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + + if (!one_process) { + sa.sa_handler = sig_coredump; +#if defined(SA_ONESHOT) + sa.sa_flags = SA_ONESHOT; +#elif defined(SA_RESETHAND) + sa.sa_flags = SA_RESETHAND; +#endif + if (sigaction(SIGSEGV, &sa, NULL) < 0) + ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGSEGV)"); +#ifdef SIGBUS + if (sigaction(SIGBUS, &sa, NULL) < 0) + ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGBUS)"); +#endif +#ifdef SIGABORT + if (sigaction(SIGABORT, &sa, NULL) < 0) + ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGABORT)"); +#endif +#ifdef SIGABRT + if (sigaction(SIGABRT, &sa, NULL) < 0) + ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGABRT)"); +#endif +#ifdef SIGILL + if (sigaction(SIGILL, &sa, NULL) < 0) + ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGILL)"); +#endif + sa.sa_flags = 0; + } + sa.sa_handler = sig_term; + if (sigaction(SIGTERM, &sa, NULL) < 0) + ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGTERM)"); +#ifdef SIGINT + if (sigaction(SIGINT, &sa, NULL) < 0) + ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGINT)"); +#endif +#ifdef SIGXCPU + sa.sa_handler = SIG_DFL; + if (sigaction(SIGXCPU, &sa, NULL) < 0) + ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGXCPU)"); +#endif +#ifdef SIGXFSZ + sa.sa_handler = SIG_DFL; + if (sigaction(SIGXFSZ, &sa, NULL) < 0) + ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGXFSZ)"); +#endif +#ifdef SIGPIPE + sa.sa_handler = SIG_IGN; + if (sigaction(SIGPIPE, &sa, NULL) < 0) + ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGPIPE)"); +#endif + + /* we want to ignore HUPs and WINCH while we're busy processing one */ + sigaddset(&sa.sa_mask, SIGHUP); + sigaddset(&sa.sa_mask, SIGWINCH); + sa.sa_handler = restart; + if (sigaction(SIGHUP, &sa, NULL) < 0) + ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGHUP)"); + if (sigaction(SIGWINCH, &sa, NULL) < 0) + ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "sigaction(SIGWINCH)"); +#else + if (!one_process) { + apr_signal(SIGSEGV, sig_coredump); +#ifdef SIGBUS + apr_signal(SIGBUS, sig_coredump); +#endif /* SIGBUS */ +#ifdef SIGABORT + apr_signal(SIGABORT, sig_coredump); +#endif /* SIGABORT */ +#ifdef SIGABRT + apr_signal(SIGABRT, sig_coredump); +#endif /* SIGABRT */ +#ifdef SIGILL + apr_signal(SIGILL, sig_coredump); +#endif /* SIGILL */ +#ifdef SIGXCPU + apr_signal(SIGXCPU, SIG_DFL); +#endif /* SIGXCPU */ +#ifdef SIGXFSZ + apr_signal(SIGXFSZ, SIG_DFL); +#endif /* SIGXFSZ */ + } + + apr_signal(SIGTERM, sig_term); +#ifdef SIGHUP + apr_signal(SIGHUP, restart); +#endif /* SIGHUP */ +#ifdef SIGWINCH + apr_signal(SIGWINCH, restart); +#endif /* SIGWINCH */ +#ifdef SIGPIPE + apr_signal(SIGPIPE, SIG_IGN); +#endif /* SIGPIPE */ + +#endif +} + +/***************************************************************** + * Here follows a long bunch of generic server bookkeeping stuff... + */ + +int ap_graceful_stop_signalled(void) +{ + /* XXX - Does this really work? - Manoj */ + return is_graceful; +} + +/***************************************************************** + * Child process main loop. + */ + +static void process_socket(apr_pool_t *p, apr_socket_t *sock, int my_child_num, int my_thread_num) +{ + conn_rec *current_conn; + long conn_id = AP_ID_FROM_CHILD_THREAD(my_child_num, my_thread_num); + int csd; + + (void) apr_os_sock_get(&csd, sock); + + if (csd >= FD_SETSIZE) { + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, 0, NULL, + "new file descriptor %d is too large; you probably need " + "to rebuild Apache with a larger FD_SETSIZE " + "(currently %d)", + csd, FD_SETSIZE); + apr_socket_close(sock); + return; + } + + ap_sock_disable_nagle(sock); + + current_conn = ap_new_connection(p, ap_server_conf, sock, conn_id); + if (current_conn) { + ap_process_connection(current_conn); + ap_lingering_close(current_conn); + } +} + +/* requests_this_child has gone to zero or below. See if the admin coded + "MaxRequestsPerChild 0", and keep going in that case. Doing it this way + simplifies the hot path in worker_thread */ + +static void check_infinite_requests(void) +{ + if (ap_max_requests_per_child) { + workers_may_exit = 1; + } + else { + /* wow! if you're executing this code, you may have set a record. + * either this child process has served over 2 billion requests, or + * you're running a threaded 2.0 on a 16 bit machine. + * + * I'll buy pizza and beers at Apachecon for the first person to do + * the former without cheating (dorking with INT_MAX, or running with + * uncommitted performance patches, for example). + * + * for the latter case, you probably deserve a beer too. Greg Ames + */ + + requests_this_child = INT_MAX; /* keep going */ + } +} + +/* Sets workers_may_exit if we received a character on the pipe_of_death */ +static void check_pipe_of_death(void) +{ + apr_lock_acquire(pipe_of_death_mutex); + if (!workers_may_exit) { + apr_status_t ret; + char pipe_read_char; + apr_size_t n = 1; + + ret = apr_recv(listensocks[0], &pipe_read_char, &n); + if (APR_STATUS_IS_EAGAIN(ret)) { + /* It lost the lottery. It must continue to suffer + * through a life of servitude. */ + } + else { + /* It won the lottery (or something else is very + * wrong). Embrace death with open arms. */ + workers_may_exit = 1; + } + } + apr_lock_release(pipe_of_death_mutex); +} + +static void *listener_thread(apr_thread_t *thd, void * dummy) +{ + proc_info * ti = dummy; + int process_slot = ti->pid; + int thread_slot = ti->tid; + apr_pool_t *tpool = ti->tpool; + apr_socket_t *csd = NULL; + apr_pool_t *ptrans; /* Pool for per-transaction stuff */ + apr_socket_t *sd = NULL; + int n; + int curr_pollfd, last_pollfd = 0; + apr_pollfd_t *pollset; + apr_status_t rv; + + free(ti); + + apr_pool_create(&ptrans, tpool); + + apr_lock_acquire(worker_thread_count_mutex); + worker_thread_count++; + apr_lock_release(worker_thread_count_mutex); + + apr_poll_setup(&pollset, num_listensocks+1, tpool); + for(n=0 ; n <= num_listensocks ; ++n) + apr_poll_socket_add(pollset, listensocks[n], APR_POLLIN); + + worker_queue = apr_pcalloc(pchild, sizeof(*worker_queue)); + ap_queue_init(worker_queue, ap_threads_per_child, pchild); + + /* TODO: Switch to a system where threads reuse the results from earlier + poll calls - manoj */ + while (1) { + if (requests_this_child <= 0) { + check_infinite_requests(); + } + if (workers_may_exit) break; + + if ((rv = SAFE_ACCEPT(apr_lock_acquire(accept_mutex))) + != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_EMERG, rv, ap_server_conf, + "apr_lock_acquire failed. Attempting to shutdown " + "process gracefully."); + workers_may_exit = 1; + } + + while (!workers_may_exit) { + apr_status_t ret; + apr_int16_t event; + + ret = apr_poll(pollset, &n, -1); + if (ret != APR_SUCCESS) { + if (APR_STATUS_IS_EINTR(ret)) { + continue; + } + + /* apr_poll() will only return errors in catastrophic + * circumstances. Let's try exiting gracefully, for now. */ + ap_log_error(APLOG_MARK, APLOG_ERR, ret, (const server_rec *) + ap_server_conf, "apr_poll: (listen)"); + workers_may_exit = 1; + } + + if (workers_may_exit) break; + + apr_poll_revents_get(&event, listensocks[0], pollset); + if (event & APR_POLLIN) { + /* A process got a signal on the shutdown pipe. Check if we're + * the lucky process to die. */ + check_pipe_of_death(); + continue; + } + + if (num_listensocks == 1) { + sd = ap_listeners->sd; + goto got_fd; + } + else { + /* find a listener */ + curr_pollfd = last_pollfd; + do { + curr_pollfd++; + if (curr_pollfd > num_listensocks) { + curr_pollfd = 1; + } + /* XXX: Should we check for POLLERR? */ + apr_poll_revents_get(&event, listensocks[curr_pollfd], pollset); + if (event & APR_POLLIN) { + last_pollfd = curr_pollfd; + sd=listensocks[curr_pollfd]; + goto got_fd; + } + } while (curr_pollfd != last_pollfd); + } + } + got_fd: + if (!workers_may_exit) { + if ((rv = apr_accept(&csd, sd, ptrans)) != APR_SUCCESS) { + csd = NULL; + ap_log_error(APLOG_MARK, APLOG_ERR, rv, ap_server_conf, + "apr_accept"); + } + if ((rv = SAFE_ACCEPT(apr_lock_release(accept_mutex))) + != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_EMERG, rv, ap_server_conf, + "apr_lock_release failed. Attempting to shutdown " + "process gracefully."); + workers_may_exit = 1; + } + if (csd != NULL) { + ap_queue_push(worker_queue, csd, ptrans); + ap_block_on_queue(worker_queue); + } + } + else { + if ((rv = SAFE_ACCEPT(apr_lock_release(accept_mutex))) + != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_EMERG, rv, ap_server_conf, + "apr_lock_release failed. Attempting to shutdown " + "process gracefully."); + workers_may_exit = 1; + } + break; + } + } + + apr_pool_destroy(tpool); + ap_update_child_status(process_slot, thread_slot, (dying) ? SERVER_DEAD : SERVER_GRACEFUL, + (request_rec *) NULL); + dying = 1; + apr_lock_acquire(worker_thread_count_mutex); + worker_thread_count--; + if (worker_thread_count == 0) { + /* All the threads have exited, now finish the shutdown process + * by signalling the sigwait thread */ + kill(ap_my_pid, SIGTERM); + } + apr_lock_release(worker_thread_count_mutex); + + return NULL; +} + +static void *worker_thread(apr_thread_t *thd, void * dummy) +{ + proc_info * ti = dummy; + int process_slot = ti->pid; + int thread_slot = ti->tid; + apr_pool_t *tpool = ti->tpool; + apr_socket_t *csd = NULL; + apr_pool_t *ptrans; /* Pool for per-transaction stuff */ + apr_socket_t *sd = NULL; + int n; + int curr_pollfd, last_pollfd = 0; + apr_pollfd_t *pollset; + apr_status_t rv; + + free(ti); + + while (!workers_may_exit) { + ap_queue_pop(worker_queue, &csd, &ptrans, 1); + process_socket(ptrans, csd, process_slot, thread_slot); + requests_this_child--; + apr_pool_clear(ptrans); + } + + apr_pool_destroy(tpool); + ap_update_child_status(process_slot, thread_slot, (dying) ? SERVER_DEAD : SERVER_GRACEFUL, + (request_rec *) NULL); + apr_lock_acquire(worker_thread_count_mutex); + if (!dying) { + /* this is the first thread to exit */ + if (ap_my_pid == ap_scoreboard_image->parent[process_slot].pid) { + /* tell the parent that it may use this scoreboard slot */ + ap_scoreboard_image->parent[process_slot].quiescing = 1; + } + dying = 1; + } + worker_thread_count--; + if (worker_thread_count == 0) { + /* All the threads have exited, now finish the shutdown process + * by signalling the sigwait thread */ + kill(ap_my_pid, SIGTERM); + } + apr_lock_release(worker_thread_count_mutex); + + return NULL; +} + +static int check_signal(int signum) +{ + switch (signum) { + case SIGTERM: + case SIGINT: + return 1; + } + return 0; +} + +static void *start_threads(apr_thread_t *thd, void * dummy) +{ + thread_starter *ts = dummy; + apr_thread_t **threads = ts->threads; + apr_threadattr_t *thread_attr = ts->threadattr; + int child_num_arg = ts->child_num_arg; + int i; + int my_child_num = child_num_arg; + proc_info *my_info = NULL; + apr_status_t rv; + int threads_created = 0; + apr_thread_t *listener; + + while (1) { + my_info = (proc_info *)malloc(sizeof(proc_info)); + my_info->pid = my_child_num; + my_info->tid = i; + my_info->sd = 0; + apr_pool_create(&my_info->tpool, pchild); + apr_thread_create(&listener, thread_attr, listener_thread, my_info, pchild); + for (i=0; i < ap_threads_per_child; i++) { + int status = ap_scoreboard_image->servers[child_num_arg][i].status; + + if (status != SERVER_GRACEFUL && status != SERVER_DEAD) { + continue; + } + + my_info = (proc_info *)malloc(sizeof(proc_info)); + if (my_info == NULL) { + ap_log_error(APLOG_MARK, APLOG_ALERT, errno, ap_server_conf, + "malloc: out of memory"); + clean_child_exit(APEXIT_CHILDFATAL); + } + my_info->pid = my_child_num; + my_info->tid = i; + my_info->sd = 0; + apr_pool_create(&my_info->tpool, pchild); + + /* We are creating threads right now */ + (void) ap_update_child_status(my_child_num, i, SERVER_STARTING, + (request_rec *) NULL); + /* We let each thread update its own scoreboard entry. This is + * done because it lets us deal with tid better. + */ + if ((rv = apr_thread_create(&threads[i], thread_attr, worker_thread, my_info, pchild))) { + ap_log_error(APLOG_MARK, APLOG_ALERT, rv, ap_server_conf, + "apr_thread_create: unable to create worker thread"); + /* In case system resources are maxxed out, we don't want + Apache running away with the CPU trying to fork over and + over and over again if we exit. */ + sleep(10); + clean_child_exit(APEXIT_CHILDFATAL); + } + threads_created++; + } + if (workers_may_exit || threads_created == ap_threads_per_child) { + break; + } + sleep(1); /* wait for previous generation to clean up an entry */ + } + + /* What state should this child_main process be listed as in the scoreboard...? + * ap_update_child_status(my_child_num, i, SERVER_STARTING, (request_rec *) NULL); + * + * This state should be listed separately in the scoreboard, in some kind + * of process_status, not mixed in with the worker threads' status. + * "life_status" is almost right, but it's in the worker's structure, and + * the name could be clearer. gla + */ + return NULL; +} + +static void child_main(int child_num_arg) +{ + apr_thread_t **threads; + int i; + ap_listen_rec *lr; + apr_status_t rv; + thread_starter *ts; + apr_threadattr_t *thread_attr; + apr_thread_t *start_thread_id; + + ap_my_pid = getpid(); + apr_pool_create(&pchild, pconf); + + /*stuff to do before we switch id's, so we have permissions.*/ + reopen_scoreboard(pchild); + + rv = SAFE_ACCEPT(apr_lock_child_init(&accept_mutex, lock_fname, + pchild)); + if (rv != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_EMERG, rv, ap_server_conf, + "Couldn't initialize cross-process lock in child"); + clean_child_exit(APEXIT_CHILDFATAL); + } + + if (unixd_setup_child()) { + clean_child_exit(APEXIT_CHILDFATAL); + } + + ap_run_child_init(pchild, ap_server_conf); + + /*done with init critical section */ + + rv = apr_setup_signal_thread(); + if (rv != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_EMERG, rv, ap_server_conf, + "Couldn't initialize signal thread"); + clean_child_exit(APEXIT_CHILDFATAL); + } + + if (ap_max_requests_per_child) { + requests_this_child = ap_max_requests_per_child; + } + else { + /* coding a value of zero means infinity */ + requests_this_child = INT_MAX; + } + + /* Set up the pollfd array */ + listensocks = apr_pcalloc(pchild, + sizeof(*listensocks) * (num_listensocks + 1)); +#if APR_FILES_AS_SOCKETS + apr_socket_from_file(&listensocks[0], pipe_of_death_in); +#endif + for (lr = ap_listeners, i = 1; i <= num_listensocks; lr = lr->next, ++i) + listensocks[i]=lr->sd; + + /* Setup worker threads */ + + /* clear the storage; we may not create all our threads immediately, and we want + * a 0 entry to indicate a thread which was not created + */ + threads = (apr_thread_t **)calloc(1, sizeof(apr_thread_t *) * ap_threads_per_child); + if (threads == NULL) { + ap_log_error(APLOG_MARK, APLOG_ALERT, errno, ap_server_conf, + "malloc: out of memory"); + clean_child_exit(APEXIT_CHILDFATAL); + } + worker_thread_count = 0; + apr_lock_create(&worker_thread_count_mutex, APR_MUTEX, APR_INTRAPROCESS, + NULL, pchild); + apr_lock_create(&pipe_of_death_mutex, APR_MUTEX, APR_INTRAPROCESS, + NULL, pchild); + + ts = apr_palloc(pchild, sizeof(*ts)); + + apr_threadattr_create(&thread_attr, pchild); + apr_threadattr_detach_set(thread_attr, 0); /* 0 means PTHREAD_CREATE_JOINABLE */ + + ts->threads = threads; + ts->child_num_arg = child_num_arg; + ts->threadattr = thread_attr; + + if ((rv = apr_thread_create(&start_thread_id, thread_attr, start_threads, ts, pchild))) { + ap_log_error(APLOG_MARK, APLOG_ALERT, rv, ap_server_conf, + "apr_thread_create: unable to create worker thread"); + /* In case system resources are maxxed out, we don't want + Apache running away with the CPU trying to fork over and + over and over again if we exit. */ + sleep(10); + clean_child_exit(APEXIT_CHILDFATAL); + } + + apr_signal_thread(check_signal); + + workers_may_exit = 1; /* helps us terminate a little more quickly when + * the dispatch of the signal thread + * beats the Pipe of Death and the browsers + */ + + /* A terminating signal was received. Now join each of the workers to clean them up. + * If the worker already exited, then the join frees their resources and returns. + * If the worker hasn't exited, then this blocks until they have (then cleans up). + */ + apr_thread_join(&rv, start_thread_id); + for (i = 0; i < ap_threads_per_child; i++) { + if (threads[i]) { /* if we ever created this thread */ + apr_thread_join(&rv, threads[i]); + } + } + + free(threads); + + clean_child_exit(0); +} + +static int make_child(server_rec *s, int slot) +{ + int pid; + + if (slot + 1 > ap_max_daemons_limit) { + ap_max_daemons_limit = slot + 1; + } + + if (one_process) { + set_signals(); + ap_scoreboard_image->parent[slot].pid = getpid(); + child_main(slot); + } + + if ((pid = fork()) == -1) { + ap_log_error(APLOG_MARK, APLOG_ERR, errno, s, "fork: Unable to fork new process"); + + /* fork didn't succeed. Fix the scoreboard or else + * it will say SERVER_STARTING forever and ever + */ + (void) ap_update_child_status(slot, 0, SERVER_DEAD, (request_rec *) NULL); + + /* In case system resources are maxxed out, we don't want + Apache running away with the CPU trying to fork over and + over and over again. */ + sleep(10); + + return -1; + } + + if (!pid) { +#ifdef HAVE_BINDPROCESSOR + /* By default, AIX binds to a single processor. This bit unbinds + * children which will then bind to another CPU. + */ + int status = bindprocessor(BINDPROCESS, (int)getpid(), + PROCESSOR_CLASS_ANY); + if (status != OK) + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, errno, ap_server_conf, + "processor unbind failed %d", status); +#endif + RAISE_SIGSTOP(MAKE_CHILD); + + apr_signal(SIGTERM, just_die); + child_main(slot); + + clean_child_exit(0); + } + /* else */ + ap_scoreboard_image->parent[slot].quiescing = 0; + ap_scoreboard_image->parent[slot].pid = pid; + return 0; +} + +/* If there aren't many connections coming in from the network, the child + * processes may need to be awakened from their network i/o waits. + * The pipe of death is an effective prod. + */ + +static void wake_up_and_die(void) +{ + int i; + char char_of_death = '!'; + apr_size_t one = 1; + apr_status_t rv; + + for (i = 0; i < ap_daemons_limit;) { + if ((rv = apr_file_write(pipe_of_death_out, &char_of_death, &one)) + != APR_SUCCESS) { + if (APR_STATUS_IS_EINTR(rv)) continue; + ap_log_error(APLOG_MARK, APLOG_WARNING, rv, ap_server_conf, + "write pipe_of_death"); + } + i++; + } +} + +/* start up a bunch of children */ +static void startup_children(int number_to_start) +{ + int i; + + for (i = 0; number_to_start && i < ap_daemons_limit; ++i) { + if (ap_scoreboard_image->parent[i].pid != 0) { + continue; + } + if (make_child(ap_server_conf, i) < 0) { + break; + } + --number_to_start; + } +} + + +/* + * idle_spawn_rate is the number of children that will be spawned on the + * next maintenance cycle if there aren't enough idle servers. It is + * doubled up to MAX_SPAWN_RATE, and reset only when a cycle goes by + * without the need to spawn. + */ +static int idle_spawn_rate = 1; +#ifndef MAX_SPAWN_RATE +#define MAX_SPAWN_RATE (32) +#endif +static int hold_off_on_exponential_spawning; + +static void perform_idle_server_maintenance(void) +{ + int i, j; + int idle_thread_count; + worker_score *ws; + process_score *ps; + int free_length; + int free_slots[MAX_SPAWN_RATE]; + int last_non_dead; + int total_non_dead; + apr_size_t one = 1; + apr_status_t rv; + + /* initialize the free_list */ + free_length = 0; + + idle_thread_count = 0; + last_non_dead = -1; + total_non_dead = 0; + + ap_sync_scoreboard_image(); + for (i = 0; i < ap_daemons_limit; ++i) { + /* Initialization to satisfy the compiler. It doesn't know + * that ap_threads_per_child is always > 0 */ + int status = SERVER_DEAD; + int any_dying_threads = 0; + int any_dead_threads = 0; + + if (i >= ap_max_daemons_limit && free_length == idle_spawn_rate) + break; + ps = &ap_scoreboard_image->parent[i]; + for (j = 0; j < ap_threads_per_child; j++) { + ws = &ap_scoreboard_image->servers[i][j]; + status = ws->status; + + any_dying_threads = any_dying_threads || (status == SERVER_GRACEFUL); + any_dead_threads = any_dead_threads || (status == SERVER_DEAD); + + /* We consider a starting server as idle because we started it + * at least a cycle ago, and if it still hasn't finished starting + * then we're just going to swamp things worse by forking more. + * So we hopefully won't need to fork more if we count it. + * This depends on the ordering of SERVER_READY and SERVER_STARTING. + */ + if (status <= SERVER_READY && status != SERVER_DEAD && + ps->generation == ap_my_generation && + /* XXX the following shouldn't be necessary if we clean up + * properly after seg faults, but we're not yet GLA + */ + ps->pid != 0) { + ++idle_thread_count; + } + } + /* XXX any_dead_threads may not be needed any more GLA */ + if (any_dead_threads && free_length < idle_spawn_rate + && (!ps->pid /* no process in the slot */ + || ps->quiescing)) { /* or at least one is going away */ + free_slots[free_length] = i; + ++free_length; + } + if (!any_dying_threads) { + last_non_dead = i; + ++total_non_dead; + } + } + ap_max_daemons_limit = last_non_dead + 1; + + if (idle_thread_count > max_spare_threads) { + char char_of_death = '!'; + /* Kill off one child */ + if ((rv = apr_file_write(pipe_of_death_out, &char_of_death, &one)) != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_WARNING, rv, ap_server_conf, "write pipe_of_death"); + } + idle_spawn_rate = 1; + } + else if (idle_thread_count < min_spare_threads) { + /* terminate the free list */ + if (free_length == 0) { + /* only report this condition once */ + static int reported = 0; + + if (!reported) { + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, ap_server_conf, + "server reached MaxClients setting, consider" + " raising the MaxClients setting"); + reported = 1; + } + idle_spawn_rate = 1; + } + else { + + if (idle_spawn_rate >= 8) { + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, 0, ap_server_conf, + "server seems busy, (you may need " + "to increase StartServers, ThreadsPerChild " + "or Min/MaxSpareThreads), " + "spawning %d children, there are around %d idle " + "threads, and %d total children", free_length, + idle_thread_count, total_non_dead); + } + for (i = 0; i < free_length; ++i) { + make_child(ap_server_conf, free_slots[i]); + } + /* the next time around we want to spawn twice as many if this + * wasn't good enough, but not if we've just done a graceful + */ + if (hold_off_on_exponential_spawning) { + --hold_off_on_exponential_spawning; + } + else if (idle_spawn_rate < MAX_SPAWN_RATE) { + idle_spawn_rate *= 2; + } + } + } + else { + idle_spawn_rate = 1; + } +} + +static void server_main_loop(int remaining_children_to_start) +{ + int child_slot; + apr_wait_t status; + apr_proc_t pid; + int i; + + while (!restart_pending && !shutdown_pending) { + ap_wait_or_timeout(&status, &pid, pconf); + + if (pid.pid != -1) { + ap_process_child_status(&pid, status); + /* non-fatal death... note that it's gone in the scoreboard. */ + child_slot = find_child_by_pid(&pid); + if (child_slot >= 0) { + for (i = 0; i < ap_threads_per_child; i++) + ap_update_child_status(child_slot, i, SERVER_DEAD, (request_rec *) NULL); + + ap_scoreboard_image->parent[child_slot].pid = 0; + ap_scoreboard_image->parent[child_slot].quiescing = 0; + if (remaining_children_to_start + && child_slot < ap_daemons_limit) { + /* we're still doing a 1-for-1 replacement of dead + * children with new children + */ + make_child(ap_server_conf, child_slot); + --remaining_children_to_start; + } +#if APR_HAS_OTHER_CHILD + } + else if (apr_proc_other_child_read(&pid, status) == 0) { + /* handled */ +#endif + } + else if (is_graceful) { + /* Great, we've probably just lost a slot in the + * scoreboard. Somehow we don't know about this child. + */ + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, 0, + ap_server_conf, + "long lost child came home! (pid %ld)", + (long)pid.pid); + } + /* Don't perform idle maintenance when a child dies, + * only do it when there's a timeout. Remember only a + * finite number of children can die, and it's pretty + * pathological for a lot to die suddenly. + */ + continue; + } + else if (remaining_children_to_start) { + /* we hit a 1 second timeout in which none of the previous + * generation of children needed to be reaped... so assume + * they're all done, and pick up the slack if any is left. + */ + startup_children(remaining_children_to_start); + remaining_children_to_start = 0; + /* In any event we really shouldn't do the code below because + * few of the servers we just started are in the IDLE state + * yet, so we'd mistakenly create an extra server. + */ + continue; + } + + perform_idle_server_maintenance(); + } +} + +int ap_mpm_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s) +{ + int remaining_children_to_start; + apr_status_t rv; + + pconf = _pconf; + ap_server_conf = s; + + rv = apr_file_pipe_create(&pipe_of_death_in, &pipe_of_death_out, pconf); + if (rv != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_ERR, rv, + (const server_rec*) ap_server_conf, + "apr_file_pipe_create (pipe_of_death)"); + exit(1); + } + + if ((rv = apr_file_pipe_timeout_set(pipe_of_death_in, 0)) != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_ERR, rv, + (const server_rec*) ap_server_conf, + "apr_file_pipe_timeout_set (pipe_of_death)"); + exit(1); + } + + if ((num_listensocks = ap_setup_listeners(ap_server_conf)) < 1) { + /* XXX: hey, what's the right way for the mpm to indicate a fatal error? */ + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ALERT, 0, s, + "no listening sockets available, shutting down"); + return 1; + } + ap_log_pid(pconf, ap_pid_fname); + + /* Initialize cross-process accept lock */ + lock_fname = apr_psprintf(_pconf, "%s.%" APR_OS_PROC_T_FMT, + ap_server_root_relative(_pconf, lock_fname), + ap_my_pid); + rv = apr_lock_create_np(&accept_mutex, APR_MUTEX, APR_LOCKALL, + accept_lock_mech, lock_fname, _pconf); + if (rv != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s, + "Couldn't create accept lock"); + return 1; + } + + if (!is_graceful) { + ap_run_pre_mpm(pconf, SB_SHARED); + } + + set_signals(); + /* Don't thrash... */ + if (max_spare_threads < min_spare_threads + ap_threads_per_child) + max_spare_threads = min_spare_threads + ap_threads_per_child; + + /* If we're doing a graceful_restart then we're going to see a lot + * of children exiting immediately when we get into the main loop + * below (because we just sent them SIGWINCH). This happens pretty + * rapidly... and for each one that exits we'll start a new one until + * we reach at least daemons_min_free. But we may be permitted to + * start more than that, so we'll just keep track of how many we're + * supposed to start up without the 1 second penalty between each fork. + */ + remaining_children_to_start = ap_daemons_to_start; + if (remaining_children_to_start > ap_daemons_limit) { + remaining_children_to_start = ap_daemons_limit; + } + if (!is_graceful) { + startup_children(remaining_children_to_start); + remaining_children_to_start = 0; + } + else { + /* give the system some time to recover before kicking into + * exponential mode */ + hold_off_on_exponential_spawning = 10; + } + + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, 0, ap_server_conf, + "%s configured -- resuming normal operations", + ap_get_server_version()); + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, 0, ap_server_conf, + "Server built: %s", ap_get_server_built()); + restart_pending = shutdown_pending = 0; + + server_main_loop(remaining_children_to_start); + + if (shutdown_pending) { + /* Time to gracefully shut down: + * Kill child processes, tell them to call child_exit, etc... + */ + wake_up_and_die(); + + if (unixd_killpg(getpgrp(), SIGTERM) < 0) { + ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "killpg SIGTERM"); + } + ap_reclaim_child_processes(1); /* Start with SIGTERM */ + + /* cleanup pid file on normal shutdown */ + { + const char *pidfile = NULL; + pidfile = ap_server_root_relative (pconf, ap_pid_fname); + if ( pidfile != NULL && unlink(pidfile) == 0) + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, 0, + ap_server_conf, + "removed PID file %s (pid=%ld)", + pidfile, (long)getpid()); + } + + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, 0, ap_server_conf, + "caught SIGTERM, shutting down"); + + return 1; + } + + /* we've been told to restart */ + apr_signal(SIGHUP, SIG_IGN); + + if (one_process) { + /* not worth thinking about */ + return 1; + } + + /* advance to the next generation */ + /* XXX: we really need to make sure this new generation number isn't in + * use by any of the children. + */ + ++ap_my_generation; + ap_scoreboard_image->global.running_generation = ap_my_generation; + update_scoreboard_global(); + + /* wake up the children...time to die. But we'll have more soon */ + wake_up_and_die(); + + if (is_graceful) { + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, 0, ap_server_conf, + "SIGWINCH received. Doing graceful restart"); + + /* This is mostly for debugging... so that we know what is still + * gracefully dealing with existing request. + */ + + } + else { + /* Kill 'em all. Since the child acts the same on the parents SIGTERM + * and a SIGHUP, we may as well use the same signal, because some user + * pthreads are stealing signals from us left and right. + */ + if (unixd_killpg(getpgrp(), SIGTERM) < 0) { + ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "killpg SIGTERM"); + } + ap_reclaim_child_processes(1); /* Start with SIGTERM */ + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, 0, ap_server_conf, + "SIGHUP received. Attempting to restart"); + } + return 0; +} + +static void worker_pre_config(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *ptemp) +{ + static int restart_num = 0; + int no_detach = 0; + + one_process = !!ap_exists_config_define("ONE_PROCESS"); + no_detach = !!ap_exists_config_define("NO_DETACH"); + + /* sigh, want this only the second time around */ + if (restart_num++ == 1) { + is_graceful = 0; + + if (!one_process && !no_detach) { + apr_proc_detach(); + } + ap_my_pid = getpid(); + } + + unixd_pre_config(ptemp); + ap_listen_pre_config(); + ap_daemons_to_start = DEFAULT_START_DAEMON; + min_spare_threads = DEFAULT_MIN_FREE_DAEMON * DEFAULT_THREADS_PER_CHILD; + max_spare_threads = DEFAULT_MAX_FREE_DAEMON * DEFAULT_THREADS_PER_CHILD; + ap_daemons_limit = HARD_SERVER_LIMIT; + ap_threads_per_child = DEFAULT_THREADS_PER_CHILD; + ap_pid_fname = DEFAULT_PIDLOG; + ap_scoreboard_fname = DEFAULT_SCOREBOARD; + lock_fname = DEFAULT_LOCKFILE; + ap_max_requests_per_child = DEFAULT_MAX_REQUESTS_PER_CHILD; + ap_extended_status = 0; + + apr_cpystrn(ap_coredump_dir, ap_server_root, sizeof(ap_coredump_dir)); +} + +static void worker_hooks(apr_pool_t *p) +{ + one_process = 0; + + ap_hook_pre_config(worker_pre_config, NULL, NULL, APR_HOOK_MIDDLE); +} + + +static const char *set_pidfile(cmd_parms *cmd, void *dummy, const char *arg) +{ + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + if (err != NULL) { + return err; + } + + if (cmd->server->is_virtual) { + return "PidFile directive not allowed in <VirtualHost>"; + } + ap_pid_fname = arg; + return NULL; +} + +static const char *set_scoreboard(cmd_parms *cmd, void *dummy, + const char *arg) +{ + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + if (err != NULL) { + return err; + } + + ap_scoreboard_fname = arg; + return NULL; +} + +static const char *set_lockfile(cmd_parms *cmd, void *dummy, const char *arg) +{ + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + if (err != NULL) { + return err; + } + + lock_fname = arg; + return NULL; +} + +static const char *set_daemons_to_start(cmd_parms *cmd, void *dummy, + const char *arg) +{ + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + if (err != NULL) { + return err; + } + + ap_daemons_to_start = atoi(arg); + return NULL; +} + +static const char *set_min_spare_threads(cmd_parms *cmd, void *dummy, + const char *arg) +{ + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + if (err != NULL) { + return err; + } + + min_spare_threads = atoi(arg); + if (min_spare_threads <= 0) { + ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL, + "WARNING: detected MinSpareThreads set to non-positive."); + ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL, + "Resetting to 1 to avoid almost certain Apache failure."); + ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL, + "Please read the documentation."); + min_spare_threads = 1; + } + + return NULL; +} + +static const char *set_max_spare_threads(cmd_parms *cmd, void *dummy, + const char *arg) +{ + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + if (err != NULL) { + return err; + } + + max_spare_threads = atoi(arg); + return NULL; +} + +static const char *set_server_limit (cmd_parms *cmd, void *dummy, + const char *arg) +{ + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + if (err != NULL) { + return err; + } + + ap_daemons_limit = atoi(arg); + if (ap_daemons_limit > HARD_SERVER_LIMIT) { + ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL, + "WARNING: MaxClients of %d exceeds compile time limit " + "of %d servers,", ap_daemons_limit, HARD_SERVER_LIMIT); + ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL, + " lowering MaxClients to %d. To increase, please " + "see the", HARD_SERVER_LIMIT); + ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL, + " HARD_SERVER_LIMIT define in %s.", + AP_MPM_HARD_LIMITS_FILE); + ap_daemons_limit = HARD_SERVER_LIMIT; + } + else if (ap_daemons_limit < 1) { + ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL, "WARNING: Require MaxClients > 0, setting to 1"); + ap_daemons_limit = 1; + } + return NULL; +} + +static const char *set_threads_per_child (cmd_parms *cmd, void *dummy, + const char *arg) +{ + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + if (err != NULL) { + return err; + } + + ap_threads_per_child = atoi(arg); + if (ap_threads_per_child > HARD_THREAD_LIMIT) { + ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL, + "WARNING: ThreadsPerChild of %d exceeds compile time " + "limit of %d threads,", ap_threads_per_child, + HARD_THREAD_LIMIT); + ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL, + " lowering ThreadsPerChild to %d. To increase, please" + " see the", HARD_THREAD_LIMIT); + ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL, + " HARD_THREAD_LIMIT define in %s.", + AP_MPM_HARD_LIMITS_FILE); + ap_threads_per_child = HARD_THREAD_LIMIT; + } + else if (ap_threads_per_child < 1) { + ap_log_error(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, NULL, + "WARNING: Require ThreadsPerChild > 0, setting to 1"); + ap_threads_per_child = 1; + } + return NULL; +} + +static const char *set_max_requests(cmd_parms *cmd, void *dummy, + const char *arg) +{ + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + if (err != NULL) { + return err; + } + + ap_max_requests_per_child = atoi(arg); + + return NULL; +} + +static const char *set_coredumpdir (cmd_parms *cmd, void *dummy, + const char *arg) +{ + apr_finfo_t finfo; + const char *fname; + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + if (err != NULL) { + return err; + } + + fname = ap_server_root_relative(cmd->pool, arg); + if ((apr_stat(&finfo, fname, APR_FINFO_TYPE, cmd->pool) != APR_SUCCESS) + || (finfo.filetype != APR_DIR)) { + return apr_pstrcat(cmd->pool, "CoreDumpDirectory ", fname, + " does not exist or is not a directory", NULL); + } + apr_cpystrn(ap_coredump_dir, fname, sizeof(ap_coredump_dir)); + return NULL; +} + +static const char *set_accept_lock_mech(cmd_parms *cmd, void *dummy, const char *arg) +{ + const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY); + if (err != NULL) { + return err; + } + + if (!strcasecmp(arg, "default")) { + accept_lock_mech = APR_LOCK_DEFAULT; + } +#if APR_HAS_FLOCK_SERIALIZE + else if (!strcasecmp(arg, "flock")) { + accept_lock_mech = APR_LOCK_FLOCK; + } +#endif +#if APR_HAS_FCNTL_SERIALIZE + else if (!strcasecmp(arg, "fcntl")) { + accept_lock_mech = APR_LOCK_FCNTL; + } +#endif +#if APR_HAS_SYSVSEM_SERIALIZE + else if (!strcasecmp(arg, "sysvsem")) { + accept_lock_mech = APR_LOCK_SYSVSEM; + } +#endif +#if APR_HAS_PROC_PTHREAD_SERIALIZE + else if (!strcasecmp(arg, "proc_pthread")) { + accept_lock_mech = APR_LOCK_PROC_PTHREAD; + } +#endif + else { + return apr_pstrcat(cmd->pool, arg, " is an invalid mutex mechanism; valid " + "ones for this platform are: default" +#if APR_HAS_FLOCK_SERIALIZE + ", flock" +#endif +#if APR_HAS_FCNTL_SERIALIZE + ", fcntl" +#endif +#if APR_HAS_SYSVSEM_SERIALIZE + ", sysvsem" +#endif +#if APR_HAS_PROC_PTHREAD_SERIALIZE + ", proc_pthread" +#endif + , NULL); + } + return NULL; +} + +static const command_rec worker_cmds[] = { +UNIX_DAEMON_COMMANDS +LISTEN_COMMANDS +AP_INIT_TAKE1("PidFile", set_pidfile, NULL, RSRC_CONF, + "A file for logging the server process ID"), +AP_INIT_TAKE1("ScoreBoardFile", set_scoreboard, NULL, RSRC_CONF, + "A file for Apache to maintain runtime process management information"), +AP_INIT_TAKE1("LockFile", set_lockfile, NULL, RSRC_CONF, + "The lockfile used when Apache needs to lock the accept() call"), +AP_INIT_TAKE1("StartServers", set_daemons_to_start, NULL, RSRC_CONF, + "Number of child processes launched at server startup"), +AP_INIT_TAKE1("MinSpareThreads", set_min_spare_threads, NULL, RSRC_CONF, + "Minimum number of idle children, to handle request spikes"), +AP_INIT_TAKE1("MaxSpareThreads", set_max_spare_threads, NULL, RSRC_CONF, + "Maximum number of idle children"), +AP_INIT_TAKE1("MaxClients", set_server_limit, NULL, RSRC_CONF, + "Maximum number of children alive at the same time"), +AP_INIT_TAKE1("ThreadsPerChild", set_threads_per_child, NULL, RSRC_CONF, + "Number of threads each child creates"), +AP_INIT_TAKE1("MaxRequestsPerChild", set_max_requests, NULL, RSRC_CONF, + "Maximum number of requests a particular child serves before dying."), +AP_INIT_TAKE1("CoreDumpDirectory", set_coredumpdir, NULL, RSRC_CONF, + "The location of the directory Apache changes to before dumping core"), +AP_INIT_TAKE1("AcceptMutex", set_accept_lock_mech, NULL, RSRC_CONF, + "The system mutex implementation to use for the accept mutex"), +{ NULL } +}; + +module AP_MODULE_DECLARE_DATA mpm_worker_module = { + MPM20_MODULE_STUFF, + NULL, /* hook to run before apache parses args */ + NULL, /* create per-directory config structure */ + NULL, /* merge per-directory config structures */ + NULL, /* create per-server config structure */ + NULL, /* merge per-server config structures */ + worker_cmds, /* command apr_table_t */ + worker_hooks /* register_hooks */ +}; + diff --git a/server/util_time.c b/server/util_time.c new file mode 100644 index 0000000000..3db076bcea --- /dev/null +++ b/server/util_time.c @@ -0,0 +1,175 @@ +/* ==================================================================== + * The Apache Software License, Version 1.1 + * + * Copyright (c) 2001 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 + * <http://www.apache.org/>. + * + */ + +#include "util_time.h" + +/* Cache for exploded values of recent timestamps + */ + +struct exploded_time_cache_element { + apr_int64_t t; + apr_exploded_time_t xt; + apr_int64_t t_validate; /* please see comments in cached_explode() */ +}; + +/* the "+ 1" is for the current second: */ +#define TIME_CACHE_SIZE (AP_TIME_RECENT_THRESHOLD + 1) + +static struct exploded_time_cache_element exploded_cache_localtime[TIME_CACHE_SIZE]; +static struct exploded_time_cache_element exploded_cache_gmt[TIME_CACHE_SIZE]; + + +static apr_status_t cached_explode(apr_exploded_time_t *xt, apr_time_t t, + struct exploded_time_cache_element *cache, + int use_gmt) +{ + apr_int64_t seconds = t / APR_USEC_PER_SEC; + struct exploded_time_cache_element *cache_element = + &(cache[seconds % TIME_CACHE_SIZE]); + struct exploded_time_cache_element cache_element_snapshot; + + /* The cache is implemented as a ring buffer. Each second, + * it uses a different element in the buffer. The timestamp + * in the element indicates whether the element contains the + * exploded time for the current second (vs the time + * 'now - AP_TIME_RECENT_THRESHOLD' seconds ago). If the + * cached value is for the current time, we use it. Otherwise, + * we compute the apr_exploded_time_t and store it in this + * cache element. Note that the timestamp in the cache + * element is updated only after the exploded time. Thus + * if two threads hit this cache element simultaneously + * at the start of a new second, they'll both explode the + * time and store it. I.e., the writers will collide, but + * they'll be writing the same value. + */ + if (cache_element->t >= seconds) { + /* There is an intentional race condition in this design: + * in a multithreaded app, one thread might be reading + * from this cache_element to resolve a timestamp from + * TIME_CACHE_SIZE seconds ago at the same time that + * another thread is copying the exploded form of the + * current time into the same cache_element. (I.e., the + * first thread might hit this element of the ring buffer + * just as the element is being recycled.) This can + * also happen at the start of a new second, if a + * reader accesses the cache_element after a writer + * has updated cache_element.t but before the writer + * has finished updating the whole cache_element. + * + * Rather than trying to prevent this race condition + * with locks, we allow it to happen and then detect + * and correct it. The detection works like this: + * Step 1: Take a "snapshot" of the cache element by + * copying it into a temporary buffer. + * Step 2: Check whether the snapshot contains consistent + * data: the timestamps at the start and end of + * the cache_element should both match the 'seconds' + * value that we computed from the input time. + * If these three don't match, then the snapshot + * shows the cache_element in the middle of an + * update, and its contents are invalid. + * Step 3: If the snapshot is valid, use it. Otherwise, + * just give up on the cache and explode the + * input time. + */ + memcpy(&cache_element_snapshot, cache_element, + sizeof(struct exploded_time_cache_element)); + if ((seconds != cache_element_snapshot.t) || + (seconds != cache_element_snapshot.t_validate)) { + /* Invalid snapshot */ + if (use_gmt) { + return apr_explode_gmt(xt, t); + } + else { + return apr_explode_localtime(xt, t); + } + } + else { + /* Valid snapshot */ + memcpy(xt, &(cache_element_snapshot.xt), + sizeof(apr_exploded_time_t)); + } + } + else { + apr_status_t r; + if (use_gmt) { + r = apr_explode_gmt(xt, t); + } + else { + r = apr_explode_localtime(xt, t); + } + if (!APR_STATUS_IS_SUCCESS(r)) { + return r; + } + cache_element->t = seconds; + memcpy(&(cache_element->xt), xt, sizeof(apr_exploded_time_t)); + cache_element->t_validate = seconds; + } + xt->tm_usec = t % APR_USEC_PER_SEC; + return APR_SUCCESS; +} + + +AP_DECLARE(apr_status_t) ap_explode_recent_localtime(apr_exploded_time_t * tm, + apr_time_t t) +{ + return cached_explode(tm, t, exploded_cache_localtime, 0); +} + +AP_DECLARE(apr_status_t) ap_explode_recent_gmt(apr_exploded_time_t * tm, + apr_time_t t) +{ + return cached_explode(tm, t, exploded_cache_gmt, 1); +} diff --git a/support/abs.dsp b/support/abs.dsp new file mode 100644 index 0000000000..cd8467a594 --- /dev/null +++ b/support/abs.dsp @@ -0,0 +1,131 @@ +# Microsoft Developer Studio Project File - Name="abs" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=abs - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "abs.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "abs.mak" CFG="abs - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "abs - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "abs - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "abs - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MD /W3 /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /D "APR_DECLARE_STATIC" /D "APU_DECLARE_STATIC" /D "SSL" /FD /c +# ADD CPP /nologo /MD /W3 /O2 /I "../srclib/apr/include" /I "../srclib/apr-util/include" /I "../include" /I "../srclib/openssl/inc32" /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "APR_DECLARE_STATIC" /D "APU_DECLARE_STATIC" /D "USE_SSL" /Fd"Release/abs" /FD /c +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib wsock32.lib ws2_32.lib ssleay32.lib libeay32.lib /nologo /subsystem:console /map /machine:I386 /libpath:"../srclib/openssl/out32dll" +# ADD LINK32 kernel32.lib advapi32.lib wsock32.lib ws2_32.lib ssleay32.lib libeay32.lib /nologo /subsystem:console /map /machine:I386 /libpath:"../srclib/openssl/out32dll" + +!ELSEIF "$(CFG)" == "abs - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MDd /W3 /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /D "APR_DECLARE_STATIC" /D "APU_DECLARE_STATIC" /D "SSL" /FD /c +# ADD CPP /nologo /MDd /W3 /GX /Zi /Od /I "../srclib/apr/include" /I "../srclib/apr-util/include" /I "../include" /I "../srclib/openssl/inc32" /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "APR_DECLARE_STATIC" /D "APU_DECLARE_STATIC" /D "USE_SSL" /Fd"Debug/abs" /FD /c +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib advapi32.lib wsock32.lib ws2_32.lib ssleay32.lib libeay32.lib /nologo /subsystem:console /incremental:no /map /debug /machine:I386 /libpath:"../srclib/openssl/out32dll" +# ADD LINK32 kernel32.lib advapi32.lib wsock32.lib ws2_32.lib ssleay32.lib libeay32.lib /nologo /subsystem:console /incremental:no /map /debug /machine:I386 /libpath:"../srclib/openssl/out32dll" + +!ENDIF + +# Begin Target + +# Name "abs - Win32 Release" +# Name "abs - Win32 Debug" +# Begin Source File + +SOURCE=.\ab.c + +!IF "$(CFG)" == "abs - Win32 Release" +# ADD CPP /Fo"Release/abs.obj" + +!ELSEIF "$(CFG)" == "abs - Win32 Debug" +# ADD CPP /Fo"Debug/abs.obj" + +!ENDIF +# End Source File +# Begin Source File + +SOURCE=.\abs.rc +# End Source File +# Begin Source File + +SOURCE=..\build\win32\win32ver.awk + +!IF "$(CFG)" == "abs - Win32 Release" + +# PROP Ignore_Default_Tool 1 +# Begin Custom Build - Creating Version Resource +InputPath=..\build\win32\win32ver.awk + +".\abs.rc" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + awk -f ../build/win32/win32ver.awk ab "ApacheBench/SSL Utility" ../include/ap_release.h > .\abs.rc + +# End Custom Build + +!ELSEIF "$(CFG)" == "abs - Win32 Debug" + +# PROP Ignore_Default_Tool 1 +# Begin Custom Build - Creating Version Resource +InputPath=..\build\win32\win32ver.awk + +".\abs.rc" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + awk -f ../build/win32/win32ver.awk ab "ApacheBench/SSL Utility" ../include/ap_release.h > .\abs.rc + +# End Custom Build + +!ENDIF + +# End Source File +# End Target +# End Project diff --git a/support/config.m4 b/support/config.m4 new file mode 100644 index 0000000000..ce377479b6 --- /dev/null +++ b/support/config.m4 @@ -0,0 +1,45 @@ + + +htpasswd_LTFLAGS="" +htdigest_LTFLAGS="" +rotatelogs_LTFLAGS="" +logresolve_LTFLAGS="" +ab_LTFLAGS="" + +dnl XXX Should we change the foo_LTFLAGS="-static" settings below +dnl to something like APR_ADDTO? -aaron + +AC_ARG_ENABLE(static-htpasswd,[ --enable-static-htpasswd Build a statically linked version of htpasswd],[ +if test "$enableval" = "yes" ; then + htpasswd_LTFLAGS="-static" +fi +]) +APACHE_SUBST(htpasswd_LTFLAGS) + +AC_ARG_ENABLE(static-htdigest,[ --enable-static-htdigest Build a statically linked version of htdigest],[ +if test "$enableval" = "yes" ; then + htdigest_LTFLAGS="-static" +fi +]) +APACHE_SUBST(htdigest_LTFLAGS) + +AC_ARG_ENABLE(static-rotatelogs,[ --enable-static-rotatelogs Build a statically linked version of rotatelogs],[ +if test "$enableval" = "yes" ; then + rotatelogs_LTFLAGS="-static" +fi +]) +APACHE_SUBST(rotatelogs_LTFLAGS) + +AC_ARG_ENABLE(static-logresolve,[ --enable-static-logresolve Build a statically linked version of logresolve],[ +if test "$enableval" = "yes" ; then + logresolve_LTFLAGS="-static" +fi +]) +APACHE_SUBST(logresolve_LTFLAGS) + +AC_ARG_ENABLE(static-ab,[ --enable-static-ab Build a statically linked version of ab],[ +if test "$enableval" = "yes" ; then + ab_LTFLAGS="-static" +fi +]) +APACHE_SUBST(ab_LTFLAGS) diff --git a/support/list_hooks.pl b/support/list_hooks.pl new file mode 100755 index 0000000000..c52c567142 --- /dev/null +++ b/support/list_hooks.pl @@ -0,0 +1,77 @@ +#!/usr/bin/perl -w + +use strict; + +use Carp; + +my $path=shift; + +findInDir($path); + +foreach my $hook (sort keys %::Hooks) { + my $h=$::Hooks{$hook}; + for my $x (qw(declared implemented type args)) { + croak "$hook datum '$x' missing" if !exists $h->{$x}; + } + print "$hook\n"; + print " declared in $h->{declared}\n"; + print " implemented in $h->{implemented}\n"; + print " type is $h->{type}\n"; + print " $h->{ret} $hook($h->{args})\n"; + print "\n"; +} + +sub findInDir { + my $path=shift; + + local(*D); + opendir(D,$path) || croak "Can't open $path: $!"; + while(my $f=readdir D) { + next if $f=~/^\./; + my $file="$path/$f"; + + if(-d $file) { + findInDir($file); + next; + } + next if $file !~ /\.[ch]$/; + + scanFile($file); + } + closedir D; +} + +sub scanFile { + my $file=shift; + +# print "scanning $file\n"; + + open(F,$file) || croak "Can't open $file: $!"; + while(<F>) { + next if /\#define/; + next if /\@deffunc/; + if(/AP_DECLARE_HOOK\((.*)\)/) { + my $def=$1; + my($ret,$name,$args)=$def=~/([^,\s]+)\s*,\s*([^,\s]+)\s*,\s*\((.*)\)/; + croak "Don't understand $def in $file" if !defined $args; +# print "found $ret $name($args) in $file\n"; + + croak "$name declared twice! ($_)" + if exists $::Hooks{$name}->{declared}; + $::Hooks{$name}->{declared}=$file; + $::Hooks{$name}->{ret}=$ret; + $::Hooks{$name}->{args}=$args; + } + if(/AP_IMPLEMENT_HOOK_()(VOID)\(([^,\s]+)/ + || /AP_IMPLEMENT(_OPTIONAL|)_HOOK_(.*?)\([^,]+?\s*,\s*([^,\s]+)/) { + my($type,$name)=($1 ? "OPTIONAL $2" : $2,$3); + +# print "found $name $type in $file\n"; + + croak "$name implemented twice ($::Hooks{$name}->{implemented} and $file) ($_)" + if exists $::Hooks{$name}->{implemented}; + $::Hooks{$name}->{implemented}=$file; + $::Hooks{$name}->{type}=$type; + } + } +} diff --git a/support/win32/.cvsignore b/support/win32/.cvsignore new file mode 100644 index 0000000000..7658ff5525 --- /dev/null +++ b/support/win32/.cvsignore @@ -0,0 +1,3 @@ +Release +Debug +*.plg diff --git a/support/win32/ApacheMonitor.c b/support/win32/ApacheMonitor.c new file mode 100644 index 0000000000..156f896abc --- /dev/null +++ b/support/win32/ApacheMonitor.c @@ -0,0 +1,1367 @@ +/* ==================================================================== + * 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 + * <http://www.apache.org/>. + * + * Portions of this software are based upon public domain software + * originally written at the National Center for Supercomputing Applications, + * University of Illinois, Urbana-Champaign. + */ + +/* ==================================================================== + * ApacheService.c Simple program to manage and monitor Apache services. + * + * Contributed by Mladen Turk <mturk@mappingsoft.com> + * + * 05 Aug 2001 + * ==================================================================== + */ + +#define _WIN32_WINNT 0x0400 +#ifndef STRICT +#define STRICT +#endif + +#include <windows.h> +#include <windowsx.h> +#include <commctrl.h> +#include <stdlib.h> +#include <stdio.h> +#include "ApacheMonitor.h" + + +#define OS_VERSION_WINNT 1 +#define OS_VERSION_WIN9X 2 +#define OS_VERSION_WIN2K 3 +/* Should be enough */ +#define MAX_APACHE_SERVICES 128 + +#define WM_TRAYMESSAGE (WM_APP+1) +#define WM_UPDATEMESSAGE (WM_USER+1) +#define SERVICE_APACHE_RESTART 128 +#define XBITMAP 16 +#define YBITMAP 16 +#define MAX_LOADSTRING 100 + +#ifndef SERVICE_RUNS_IN_SYSTEM_PROCESS +#define SERVICE_RUNS_IN_SYSTEM_PROCESS 0x00000001 + +typedef struct _SERVICE_STATUS_PROCESS { + DWORD dwServiceType; + DWORD dwCurrentState; + DWORD dwControlsAccepted; + DWORD dwWin32ExitCode; + DWORD dwServiceSpecificExitCode; + DWORD dwCheckPoint; + DWORD dwWaitHint; + DWORD dwProcessId; + DWORD dwServiceFlags; +} SERVICE_STATUS_PROCESS, *LPSERVICE_STATUS_PROCESS; + +typedef enum _SC_STATUS_TYPE { + SC_STATUS_PROCESS_INFO = 0 +} SC_STATUS_TYPE; + +#endif + +typedef BOOL (WINAPI *QUERYSERVICESTATUSEX)(SC_HANDLE, SC_STATUS_TYPE, + LPBYTE, DWORD, LPDWORD); + +typedef struct _st_APACHE_SERVICE +{ + LPSTR szServiceName; + LPSTR szDisplayName; + LPSTR szDescription; + LPSTR szImagePath; + DWORD dwPid; +} ST_APACHE_SERVICE; + +/* Global variables */ +HINSTANCE ap_hInstance = NULL; +HWND ap_hwndAboutDlg = NULL; +TCHAR szTitle[MAX_LOADSTRING]; /* The title bar text */ +TCHAR szWindowClass[MAX_LOADSTRING]; /* Window Class Name */ +HICON ap_icoStop; +HICON ap_icoRun; +UINT ap_uiTaskbarCreated; +DWORD ap_OSVersion; +BOOL dlgAboutOn = FALSE; +BOOL dlgServiceOn = FALSE; +ST_APACHE_SERVICE ap_stServices[MAX_APACHE_SERVICES]; + +HBITMAP hbmpStart, hbmpStop; +HBITMAP hbmpPicture, hbmpOld; +HWND ap_hServiceDlg; +BOOL ap_rescanServices; +HWND ap_hServiceDlg; + + +void ap_ClearServicesSt() +{ + int i; + for (i = 0; i < MAX_APACHE_SERVICES; i++) + { + if (ap_stServices[i].szServiceName) + free(ap_stServices[i].szServiceName); + if (ap_stServices[i].szDisplayName) + free(ap_stServices[i].szDisplayName); + if (ap_stServices[i].szDescription) + free(ap_stServices[i].szDescription); + if (ap_stServices[i].szImagePath) + free(ap_stServices[i].szImagePath); + + } + ZeroMemory(ap_stServices, sizeof(ST_APACHE_SERVICE) * MAX_APACHE_SERVICES); + +} + +void ErrorMessage(DWORD dwError) +{ + LPVOID lpMsgBuf; + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + dwError == ERROR_SUCCESS ? GetLastError() : dwError, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR) &lpMsgBuf, 0, NULL); + MessageBox(NULL, (LPCTSTR)lpMsgBuf, "Error", MB_OK | MB_ICONERROR); + LocalFree(lpMsgBuf); + +} + +LPTSTR GetStringRes(int id) +{ + static TCHAR buffer[MAX_PATH]; + + buffer[0] = 0; + LoadString(GetModuleHandle (NULL), id, buffer, MAX_PATH); + return buffer; +} + +BOOL GetSystemOSVersion(LPSTR szVersion, LPDWORD dwVersion) +{ + OSVERSIONINFOEX osvi; + BOOL bOsVersionInfoEx; + char szBuff[256]; + HKEY hKey; + char szProductType[80]; + DWORD dwBufLen; + + /* + Try calling GetVersionEx using the OSVERSIONINFOEX structure. + If that fails, try using the OSVERSIONINFO structure. + */ + ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX)); + osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); + + if (!(bOsVersionInfoEx = GetVersionEx((OSVERSIONINFO *) &osvi))) + { + /* If OSVERSIONINFOEX doesn't work, try OSVERSIONINFO. */ + osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + if (!GetVersionEx((OSVERSIONINFO *) &osvi)) + return FALSE; + } + + switch (osvi.dwPlatformId) + { + case VER_PLATFORM_WIN32_NT: + /* Test for the product. */ + if (szVersion!= NULL) + { + if (osvi.dwMajorVersion <= 4) + strcpy(szVersion, "MS Windows NT "); + else if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0) + strcpy(szVersion, "MS Windows 2000 "); + else if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 1) + strcpy(szVersion, "Whistler "); + /* Test for product type.*/ +#ifdef VER_VORKSTATION_NT + if (bOsVersionInfoEx) + { + if (osvi.wProductType == VER_NT_WORKSTATION) + { +#ifdef VER_SUITE_PERSONAL + if (osvi.wSuiteMask & VER_SUITE_PERSONAL) + strcat(szVersion, "Personal "); + else +#endif + strcat(szVersion, "Professional "); + } + else if (osvi.wProductType == VER_NT_SERVER) + { + if (osvi.wSuiteMask & VER_SUITE_DATACENTER) + strcat(szVersion, "DataCenter Server "); + else if (osvi.wSuiteMask & VER_SUITE_ENTERPRISE) + strcat(szVersion, "Advanced Server "); + else + strcat(szVersion, "Server "); + } + } + else + { +#endif + RegOpenKeyEx(HKEY_LOCAL_MACHINE, + "SYSTEM\\CurrentControlSet\\Control\\ProductOptions", + 0, KEY_QUERY_VALUE, &hKey); + RegQueryValueEx(hKey, "ProductType", NULL, NULL, + (LPBYTE) szProductType, &dwBufLen); + RegCloseKey(hKey); + if (lstrcmpi("WINNT", szProductType) == 0) + strcat(szVersion, "Workstation "); + if (lstrcmpi("SERVERNT", szProductType) == 0) + strcat(szVersion, "Server "); +#ifdef VER_VORKSTATION_NT + } +#endif + /* Get version, service pack (if any), and build number. */ + if (osvi.dwMajorVersion <= 4) + { + sprintf(szBuff, "version %d.%d %s (Build-%d)\n", + osvi.dwMajorVersion, + osvi.dwMinorVersion, + osvi.szCSDVersion, + osvi.dwBuildNumber & 0xFFFF); + } + else + { + sprintf(szBuff, "%s (Build-%d)\n", + osvi.szCSDVersion, + osvi.dwBuildNumber & 0xFFFF); + } + strcat(szVersion, szBuff); + } + else if (dwVersion != NULL) + { + if (osvi.dwMajorVersion <= 4) + *dwVersion = OS_VERSION_WINNT; + else if (osvi.dwMajorVersion == 5) + *dwVersion = OS_VERSION_WIN2K; + else + return FALSE; + + } + break; + + case VER_PLATFORM_WIN32_WINDOWS: + if (szVersion != NULL) + { + if (osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 0) + { + strcpy(szVersion, "MS Windows 95 "); + if (osvi.szCSDVersion[1] == 'C') + strcat(szVersion, "OSR2 "); + } + + if(osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 10) + { + strcpy(szVersion, "MS Windows 98 "); + if (osvi.szCSDVersion[1] == 'A') + strcat(szVersion, "SE "); + } + + if(osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 90) + { + strcpy(szVersion, "MS Windows Me "); + } + } + if (dwVersion != NULL) + *dwVersion = OS_VERSION_WIN9X; + + break; + + case VER_PLATFORM_WIN32s: + if (szVersion != NULL) + strcpy(szVersion, "Microsoft Win32s "); + if (dwVersion != NULL) + *dwVersion = OS_VERSION_WIN9X; + break; + default: + return FALSE; + break; + } + return TRUE; +} + +static VOID ShowNotifyIcon(HWND hWnd, DWORD dwMessage) +{ + + NOTIFYICONDATA nid; + int i = 0, n = 0; + + ZeroMemory(&nid,sizeof(nid)); + nid.cbSize = sizeof(NOTIFYICONDATA); + nid.hWnd = hWnd; + nid.uID = 0xFF; + nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP; + nid.uCallbackMessage = WM_TRAYMESSAGE; + + while (ap_stServices[i].szServiceName != NULL) + { + if (ap_stServices[i].dwPid != 0) + ++n; + ++i; + } + if (dwMessage != NIM_DELETE) + { + if (n) + nid.hIcon = ap_icoRun; + else + nid.hIcon = ap_icoStop; + } + else + nid.hIcon = NULL; + + sprintf(nid.szTip, "Running: %d Services", n); + Shell_NotifyIcon(dwMessage, &nid); + +} + +void ShowTryPopupMenu(HWND hWnd) +{ + /* create popup menu */ + HMENU hMenu = CreatePopupMenu(); + POINT pt; + + if (hMenu) + { + AppendMenu(hMenu, MF_STRING, IDM_ABOUT, "&About..."); + AppendMenu(hMenu, MF_STRING, IDM_RESTORE, "&Show Services..."); + AppendMenu(hMenu, MF_SEPARATOR, 0, ""); + AppendMenu(hMenu, MF_STRING, IDM_EXIT, "&Exit..."); + + GetCursorPos(&pt); + SetForegroundWindow(NULL); + TrackPopupMenu(hMenu, TPM_LEFTALIGN|TPM_RIGHTBUTTON, pt.x, pt.y, 0, hWnd, NULL); + } +} + +BOOL CenterWindow(HWND hwndChild) +{ + RECT rChild, rWorkArea; + int wChild, hChild; + int xNew, yNew; + BOOL bResult; + + /* Get the Height and Width of the child window */ + GetWindowRect(hwndChild, &rChild); + wChild = rChild.right - rChild.left; + hChild = rChild.bottom - rChild.top; + + /* Get the limits of the 'workarea' */ + bResult = SystemParametersInfo( + SPI_GETWORKAREA, /* system parameter to query or set */ + sizeof(RECT), + &rWorkArea, + 0); + if (!bResult) { + rWorkArea.left = rWorkArea.top = 0; + rWorkArea.right = GetSystemMetrics(SM_CXSCREEN); + rWorkArea.bottom = GetSystemMetrics(SM_CYSCREEN); + } + + /* Calculate new X position, then adjust for workarea */ + xNew = (rWorkArea.right - wChild)/2; + yNew = (rWorkArea.bottom - hChild)/2; + return SetWindowPos (hwndChild, HWND_TOP, xNew, yNew, 0, 0, SWP_NOSIZE | SWP_SHOWWINDOW); +} + +static void addItem(HWND hDlg, LPSTR lpStr, HBITMAP hBmp) +{ + int nItem; + + nItem = SendMessage(hDlg, LB_ADDSTRING, 0, (LPARAM)lpStr); + SendMessage(hDlg, LB_SETITEMDATA, nItem, (LPARAM)hBmp); +} + + +BOOL RunAndForgetConsole(LPTSTR szCmdLine, + LPDWORD nRetValue, + BOOL showConsole) +{ + + + STARTUPINFO stInfo; + PROCESS_INFORMATION prInfo; + BOOL bResult; + ZeroMemory(&stInfo, sizeof(stInfo)); + stInfo.cb = sizeof(stInfo); + stInfo.dwFlags = STARTF_USESHOWWINDOW; + stInfo.wShowWindow = showConsole ? SW_SHOWNORMAL : SW_HIDE; + + bResult = CreateProcess(NULL, + szCmdLine, + NULL, + NULL, + TRUE, + CREATE_NEW_CONSOLE , + NULL, + NULL , + &stInfo, + &prInfo); + if (nRetValue) + *nRetValue = GetLastError(); + + CloseHandle(prInfo.hThread); + CloseHandle(prInfo.hProcess); + if (!bResult) + return FALSE; + else + return TRUE; +} + + +BOOL ApacheManageService(LPCSTR szServiceName, LPCSTR szImagePath, DWORD dwCommand) +{ + + CHAR szBuf[MAX_PATH]; + LPSTR sPos; + DWORD retCode; + BOOL retValue; + BOOL ntService = TRUE; + SC_HANDLE schService; + SC_HANDLE schSCManager; + SERVICE_STATUS schSStatus; + LPSTR *args; + int ticks; + + if (ap_OSVersion == OS_VERSION_WIN9X) + { + sPos = strstr(szImagePath, "-k start"); + if (sPos) + { + lstrcpyn(szBuf, szImagePath, sPos - szImagePath); + switch (dwCommand) + { + case SERVICE_CONTROL_STOP: + lstrcat(szBuf, " -k stop -n "); + break; + case SERVICE_CONTROL_CONTINUE: + lstrcat(szBuf, " -k start -n "); + break; + case SERVICE_APACHE_RESTART: + lstrcat(szBuf, " -k restart -n "); + break; + case SERVICE_CONTROL_SHUTDOWN: + lstrcat(szBuf, " -k uninstall -n "); + break; + default: + return FALSE; + } + lstrcat(szBuf, szServiceName); + } + else + return FALSE; + if (!RunAndForgetConsole(szBuf, &retCode, FALSE)) + { + ErrorMessage(retCode); + return FALSE; + } + } + else + { + sPos = strstr(szImagePath, "--ntservice"); + if (!sPos) + { + sPos = strstr(szImagePath, "-k runservice"); + ntService = FALSE; + } + if (sPos) + { + lstrcpyn(szBuf, szImagePath, sPos - szImagePath); + if (dwCommand == SERVICE_CONTROL_SHUTDOWN) + { + lstrcat(szBuf, " -k uninstall -n "); + lstrcat(szBuf, szServiceName); + if (!RunAndForgetConsole(szBuf, &retCode, FALSE)) + { + ErrorMessage(retCode); + return FALSE; + } + else + return TRUE; + } + } + else + return FALSE; + schSCManager = OpenSCManager( + NULL, + NULL, + SC_MANAGER_ALL_ACCESS + ); + if (!schSCManager) + return FALSE; + + schService = OpenService(schSCManager, szServiceName, SERVICE_ALL_ACCESS); + if (schService != NULL) + { + retValue = FALSE; + switch (dwCommand) + { + case SERVICE_CONTROL_STOP: + if(ControlService(schService, SERVICE_CONTROL_STOP, &schSStatus)) + { + Sleep(1000); + while (QueryServiceStatus(schService, &schSStatus)) + { + if (schSStatus.dwCurrentState == SERVICE_STOP_PENDING) + Sleep(1000); + else + break; + } + } + if (QueryServiceStatus(schService, &schSStatus)) + { + if(schSStatus.dwCurrentState == SERVICE_STOPPED) + retValue = TRUE; + } + break; + case SERVICE_CONTROL_CONTINUE: + args = (char **)malloc(3 * sizeof(char*)); + args[0] = szBuf; + if (ntService) + args[1] = "--ntservice"; + else + { + args[1] = "-k"; + args[2] = "runservice"; + } + if(StartService(schService, ntService ? 2 : 3, args)) + { + Sleep(1000); + while (QueryServiceStatus(schService, &schSStatus)) + { + if (schSStatus.dwCurrentState == SERVICE_START_PENDING) + Sleep(1000); + else + break; + } + } + if (QueryServiceStatus(schService, &schSStatus)) + { + if(schSStatus.dwCurrentState == SERVICE_RUNNING) + retValue = TRUE; + } + /* is this OK to do? */ + free(args); + break; + case SERVICE_APACHE_RESTART: + if(ControlService(schService, SERVICE_APACHE_RESTART, &schSStatus)) + { + ticks = 60; + while(schSStatus.dwCurrentState == SERVICE_START_PENDING) + { + Sleep(1000); + if(!QueryServiceStatus(schService, &schSStatus)) + { + CloseServiceHandle(schService); + CloseServiceHandle(schSCManager); + return FALSE; + } + if (!--ticks) + break; + } + } + if(schSStatus.dwCurrentState == SERVICE_RUNNING) + retValue = TRUE; + break; + } + CloseServiceHandle(schService); + CloseServiceHandle(schSCManager); + return retValue; + + } + else + ap_rescanServices = TRUE; + + CloseServiceHandle(schSCManager); + return FALSE; + } + + return FALSE; +} + +BOOL IsServiceRunning(LPCSTR szServiceName, LPDWORD lpdwPid) +{ + + DWORD dwPid, dwBytes; + HWND hWnd; + SC_HANDLE schService; + SC_HANDLE schSCManager; + SERVICE_STATUS schSStatus; + SERVICE_STATUS_PROCESS schSProcess; + HANDLE hAdvapi; + QUERYSERVICESTATUSEX pQueryServiceStatusEx = NULL; + + if (ap_OSVersion == OS_VERSION_WIN9X) + { + hWnd = FindWindow("ApacheWin95ServiceMonitor", szServiceName); + if (hWnd && GetWindowThreadProcessId(hWnd, &dwPid)) + { + *lpdwPid = dwPid; + return TRUE; + } + else + return FALSE; + } + else + { + + dwPid = 0; + schSCManager = OpenSCManager( + NULL, + NULL, + SC_MANAGER_ALL_ACCESS + ); + if (!schSCManager) + return FALSE; + + schService = OpenService(schSCManager, szServiceName, SERVICE_QUERY_STATUS); + if (schService != NULL) + { + if (QueryServiceStatus(schService, &schSStatus)) + { + + dwPid = schSStatus.dwCurrentState; + if (lpdwPid) + *lpdwPid = 1; + } + if (ap_OSVersion == OS_VERSION_WIN2K) + { + hAdvapi = LoadLibrary("ADVAPI32.DLL"); + if (hAdvapi != NULL) + pQueryServiceStatusEx = (QUERYSERVICESTATUSEX)GetProcAddress(hAdvapi, + "QueryServiceStatusEx"); + if (hAdvapi != NULL && pQueryServiceStatusEx != NULL) + { + if (pQueryServiceStatusEx(schService, SC_STATUS_PROCESS_INFO, + (LPBYTE)&schSProcess, sizeof(SERVICE_STATUS_PROCESS), &dwBytes)) + { + dwPid = schSProcess.dwCurrentState; + if (lpdwPid) + *lpdwPid = schSProcess.dwProcessId; + } + } + if (hAdvapi != NULL) + FreeLibrary(hAdvapi); + } + CloseServiceHandle(schService); + CloseServiceHandle(schSCManager); + return dwPid == SERVICE_RUNNING ? TRUE : FALSE; + } + else + ap_rescanServices = TRUE; + + CloseServiceHandle(schSCManager); + return FALSE; + + } + + return FALSE; +} + +BOOL FindRunningServices() +{ + int i = 0; + DWORD dwPid; + BOOL rv = FALSE; + while (ap_stServices[i].szServiceName != NULL) + { + if (!IsServiceRunning(ap_stServices[i].szServiceName, &dwPid)) + dwPid = 0; + if (ap_stServices[i].dwPid != dwPid) + rv = TRUE; + ap_stServices[i].dwPid = dwPid; + ++i; + } + return rv; +} + +BOOL GetApacheServicesStatus() +{ + + CHAR szKey[MAX_PATH]; + CHAR achKey[MAX_PATH]; + CHAR szImagePath[MAX_PATH]; + CHAR szBuf[MAX_PATH]; + + HKEY hKey, hSubKey; + DWORD retCode, rv, dwKeyType; + DWORD dwBufLen = MAX_PATH; + + int i, stPos = 0; + ap_rescanServices = FALSE; + + retCode = RegOpenKeyEx(HKEY_LOCAL_MACHINE, + "System\\CurrentControlSet\\Services\\", + 0, KEY_READ, &hKey); + if (retCode != ERROR_SUCCESS) + { + ErrorMessage(retCode); + return FALSE; + } + ap_ClearServicesSt(); + for (i = 0, retCode = ERROR_SUCCESS; retCode == ERROR_SUCCESS; i++) + { + + retCode = RegEnumKey(hKey, i, achKey, MAX_PATH); + if (retCode == ERROR_SUCCESS) + { + lstrcpy(szKey, "System\\CurrentControlSet\\Services\\"); + lstrcat(szKey, achKey); + + if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, szKey, 0, + KEY_QUERY_VALUE, &hSubKey) == ERROR_SUCCESS) + { + dwBufLen = MAX_PATH; + rv = RegQueryValueEx(hSubKey, "ImagePath", NULL, + &dwKeyType, szImagePath, &dwBufLen); + + if (rv == ERROR_SUCCESS && (dwKeyType == REG_SZ || dwKeyType == REG_EXPAND_SZ) && dwBufLen) + { + lstrcpy(szBuf, szImagePath); + CharLower(szBuf); + if (strstr(szBuf, "\\apache.exe") != NULL) + { + ap_stServices[stPos].szServiceName = strdup(achKey); + ap_stServices[stPos].szImagePath = strdup(szImagePath); + dwBufLen = MAX_PATH; + if (RegQueryValueEx(hSubKey, "Description", NULL, + &dwKeyType, szBuf, &dwBufLen) == ERROR_SUCCESS) + ap_stServices[stPos].szDescription = strdup(szBuf); + + dwBufLen = MAX_PATH; + if (RegQueryValueEx(hSubKey, "DisplayName", NULL, + &dwKeyType, szBuf, &dwBufLen) == ERROR_SUCCESS) + ap_stServices[stPos].szDisplayName= strdup(szBuf); + ++stPos; + if (stPos >= MAX_APACHE_SERVICES) + retCode = !ERROR_SUCCESS; + } + } + RegCloseKey(hSubKey); + } + } + } + RegCloseKey(hKey); + FindRunningServices(); + return TRUE; +} + +LRESULT CALLBACK ServiceDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + + CHAR tchBuffer[MAX_PATH]; + CHAR tsbBuffer[MAX_PATH]; + CHAR szBuf[64]; + HWND hListBox; + static HWND hStatusBar; + TEXTMETRIC tm; + int i, y; + HDC hdcMem; + LPMEASUREITEMSTRUCT lpmis; + LPDRAWITEMSTRUCT lpdis; + RECT rcBitmap; + UINT nItem; + + switch (message) + { + + case WM_INITDIALOG: + ShowWindow(hDlg, SW_HIDE); + ap_hServiceDlg = hDlg; + hbmpStart = LoadBitmap(ap_hInstance, MAKEINTRESOURCE(IDB_BMPRUN)); + hbmpStop = LoadBitmap(ap_hInstance, MAKEINTRESOURCE(IDB_BMPSTOP)); + + Button_Enable(GetDlgItem(hDlg, IDC_SSTART), FALSE); + Button_Enable(GetDlgItem(hDlg, IDC_SSTOP), FALSE); + Button_Enable(GetDlgItem(hDlg, IDC_SRESTART), FALSE); + Button_Enable(GetDlgItem(hDlg, IDC_SUNINSTALL), FALSE); + hListBox = GetDlgItem(hDlg, IDL_SERVICES); + hStatusBar = CreateStatusWindow(SBT_TOOLTIPS | WS_CHILD | WS_VISIBLE, + "", hDlg, IDC_STATBAR); + if (GetApacheServicesStatus()) + { + i = 0; + while (ap_stServices[i].szServiceName != NULL) + { + addItem(hListBox, ap_stServices[i].szDisplayName, + ap_stServices[i].dwPid == 0 ? hbmpStop : hbmpStart); + ++i; + } + } + CenterWindow(hDlg); + ShowWindow(hDlg, SW_SHOW); + SetFocus(hListBox); + SendMessage(hListBox, LB_SETCURSEL, 0, 0); + return TRUE; + break; + case WM_UPDATEMESSAGE: + hListBox = GetDlgItem(hDlg, IDL_SERVICES); + SendMessage(hListBox, LB_RESETCONTENT, 0, 0); + Button_Enable(GetDlgItem(hDlg, IDC_SSTART), FALSE); + Button_Enable(GetDlgItem(hDlg, IDC_SSTOP), FALSE); + Button_Enable(GetDlgItem(hDlg, IDC_SRESTART), FALSE); + Button_Enable(GetDlgItem(hDlg, IDC_SUNINSTALL), FALSE); + i = 0; + while (ap_stServices[i].szServiceName != NULL) + { + addItem(hListBox, ap_stServices[i].szDisplayName, + ap_stServices[i].dwPid == 0 ? hbmpStop : hbmpStart); + ++i; + } + SendMessage(hListBox, LB_SETCURSEL, 0, 0); + /* Dirty hack to bring the window to the foreground */ + SetWindowPos(hDlg, HWND_TOPMOST, 0, 0, 0, 0, + SWP_NOSIZE | SWP_NOMOVE | SWP_SHOWWINDOW); + SetWindowPos(hDlg, HWND_NOTOPMOST, 0, 0, 0, 0, + SWP_NOSIZE | SWP_NOMOVE | SWP_SHOWWINDOW); + SetFocus(hListBox); + return TRUE; + break; + case WM_MEASUREITEM: + + lpmis = (LPMEASUREITEMSTRUCT) lParam; + lpmis->itemHeight = 16; + return TRUE; + + case WM_DRAWITEM: + + lpdis = (LPDRAWITEMSTRUCT) lParam; + if (lpdis->itemID == -1) + { + break; + } + switch (lpdis->itemAction) + { + case ODA_SELECT: + case ODA_DRAWENTIRE: + hbmpPicture = (HBITMAP)SendMessage(lpdis->hwndItem, + LB_GETITEMDATA, lpdis->itemID, (LPARAM) 0); + + hdcMem = CreateCompatibleDC(lpdis->hDC); + hbmpOld = SelectObject(hdcMem, hbmpPicture); + + BitBlt(lpdis->hDC, + lpdis->rcItem.left, lpdis->rcItem.top, + lpdis->rcItem.right - lpdis->rcItem.left, + lpdis->rcItem.bottom - lpdis->rcItem.top, + hdcMem, 0, 0, SRCCOPY); + SendMessage(lpdis->hwndItem, LB_GETTEXT, + lpdis->itemID, (LPARAM) tchBuffer); + + GetTextMetrics(lpdis->hDC, &tm); + y = (lpdis->rcItem.bottom + lpdis->rcItem.top - + tm.tmHeight) / 2; + + SelectObject(hdcMem, hbmpOld); + DeleteDC(hdcMem); + + rcBitmap.left = lpdis->rcItem.left + XBITMAP; + rcBitmap.top = lpdis->rcItem.top; + rcBitmap.right = lpdis->rcItem.right; + rcBitmap.bottom = lpdis->rcItem.top + YBITMAP; + + if (lpdis->itemState & ODS_SELECTED) + { + if (hbmpPicture == hbmpStop) + { + Button_Enable(GetDlgItem(hDlg, IDC_SSTART), TRUE); + Button_Enable(GetDlgItem(hDlg, IDC_SSTOP), FALSE); + Button_Enable(GetDlgItem(hDlg, IDC_SRESTART), FALSE); + Button_Enable(GetDlgItem(hDlg, IDC_SUNINSTALL), TRUE); + + } + else if (hbmpPicture == hbmpStart) + { + Button_Enable(GetDlgItem(hDlg, IDC_SSTART), FALSE); + Button_Enable(GetDlgItem(hDlg, IDC_SSTOP), TRUE); + Button_Enable(GetDlgItem(hDlg, IDC_SRESTART), TRUE); + Button_Enable(GetDlgItem(hDlg, IDC_SUNINSTALL), FALSE); + + } + i = 0; + while (ap_stServices[i].szServiceName != NULL) + { + if (lstrcmp(ap_stServices[i].szDisplayName, tchBuffer) == 0) + { + if (ap_stServices[i].szDescription) + lstrcpy(tsbBuffer, ap_stServices[i].szDescription); + else + lstrcpy(tsbBuffer, ap_stServices[i].szImagePath); + if (ap_stServices[i].dwPid != 0) + { + if (ap_stServices[i].dwPid & 0xFF000000) + sprintf(szBuf, " PID : 0x%08X", ap_stServices[i].dwPid); + else + sprintf(szBuf, " PID : %d", ap_stServices[i].dwPid); + lstrcat(tsbBuffer, szBuf); + } + SendMessage(hStatusBar, SB_SETTEXT, 0, (LPARAM)tsbBuffer); + break; + } + ++i; + } + + SetTextColor(lpdis->hDC, GetSysColor(COLOR_HIGHLIGHTTEXT)); + SetBkColor(lpdis->hDC, GetSysColor(COLOR_HIGHLIGHT)); + FillRect(lpdis->hDC, &rcBitmap, (HBRUSH)(COLOR_HIGHLIGHTTEXT)); + } + else + { + SetTextColor(lpdis->hDC, GetSysColor(COLOR_MENUTEXT)); + SetBkColor(lpdis->hDC, GetSysColor(COLOR_WINDOW)); + FillRect(lpdis->hDC, &rcBitmap, (HBRUSH)(COLOR_WINDOW+1)); + } + TextOut(lpdis->hDC, + XBITMAP + 6, + y, + tchBuffer, + strlen(tchBuffer)); + break; + + case ODA_FOCUS: + break; + } + return TRUE; + case WM_COMMAND: + switch (LOWORD(wParam)) + { + case IDL_SERVICES: + switch (HIWORD(wParam)) + { + case LBN_DBLCLK: + GetApacheServicesStatus(); + SendMessage(hDlg, WM_UPDATEMESSAGE, 0, 0); + return TRUE; + } + break; + case IDOK: + EndDialog(hDlg, TRUE); + return TRUE; + break; + case IDC_SSTART: + Button_Enable(GetDlgItem(hDlg, IDC_SSTART), FALSE); + hListBox = GetDlgItem(hDlg, IDL_SERVICES); + nItem = SendMessage(hListBox, LB_GETCURSEL, 0, 0); + if (nItem != LB_ERR) + { + ApacheManageService(ap_stServices[nItem].szServiceName, + ap_stServices[nItem].szImagePath, + SERVICE_CONTROL_CONTINUE); + } + Button_Enable(GetDlgItem(hDlg, IDC_SSTART), TRUE); + return TRUE; + break; + case IDC_SSTOP: + Button_Enable(GetDlgItem(hDlg, IDC_SSTOP), FALSE); + hListBox = GetDlgItem(hDlg, IDL_SERVICES); + nItem = SendMessage(hListBox, LB_GETCURSEL, 0, 0); + if (nItem != LB_ERR) + { + ApacheManageService(ap_stServices[nItem].szServiceName, + ap_stServices[nItem].szImagePath, + SERVICE_CONTROL_STOP); + } + Button_Enable(GetDlgItem(hDlg, IDC_SSTOP), TRUE); + return TRUE; + break; + case IDC_SRESTART: + Button_Enable(GetDlgItem(hDlg, IDC_SRESTART), FALSE); + hListBox = GetDlgItem(hDlg, IDL_SERVICES); + nItem = SendMessage(hListBox, LB_GETCURSEL, 0, 0); + if (nItem != LB_ERR) + { + ApacheManageService(ap_stServices[nItem].szServiceName, + ap_stServices[nItem].szImagePath, + SERVICE_APACHE_RESTART); + } + Button_Enable(GetDlgItem(hDlg, IDC_SRESTART), TRUE); + return TRUE; + break; + case IDC_SUNINSTALL: + Button_Enable(GetDlgItem(hDlg, IDC_SUNINSTALL), FALSE); + hListBox = GetDlgItem(hDlg, IDL_SERVICES); + nItem = SendMessage(hListBox, LB_GETCURSEL, 0, 0); + if (nItem != LB_ERR) + { + ApacheManageService(ap_stServices[nItem].szServiceName, + ap_stServices[nItem].szImagePath, + SERVICE_CONTROL_SHUTDOWN); + } + ap_rescanServices = TRUE; + Button_Enable(GetDlgItem(hDlg, IDC_SUNINSTALL), TRUE); + return TRUE; + break; + } + break; + case WM_SIZE: + switch (LOWORD(wParam)) + { + case SIZE_MINIMIZED: + EndDialog(hDlg, TRUE); + return TRUE; + break; + } + break; + case WM_ERASEBKGND: + + break; + case WM_CLOSE: + EndDialog(hDlg, TRUE); + return TRUE; + case WM_DESTROY: + DeleteObject(hbmpStart); + DeleteObject(hbmpStop); + return TRUE; + + default: + return FALSE; + } + return FALSE; +} + +/* About Box from MS Generic Sample */ +LRESULT CALLBACK AboutDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + static HFONT hfontDlg; /* Font for dialog text */ + static HFONT hFinePrint; /* Font for 'fine print' in dialog */ + DWORD dwVerInfoSize; /* Size of version information block */ + LPSTR lpVersion; /* String pointer to 'version' text */ + DWORD dwVerHnd=0; /* An 'ignored' parameter, always '0' */ + UINT uVersionLen; + WORD wRootLen; + BOOL bRetCode; + int i; + char szFullPath[256]; + char szResult[256]; + char szGetName[256]; + char szVersion[256]; + DWORD dwResult; + + switch (message) { + case WM_INITDIALOG: + ShowWindow(hDlg, SW_HIDE); + ap_hwndAboutDlg = hDlg; + + hfontDlg = CreateFont(14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + VARIABLE_PITCH | FF_SWISS, ""); + hFinePrint = CreateFont(11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + VARIABLE_PITCH | FF_SWISS, ""); + + CenterWindow(hDlg); + GetModuleFileName(ap_hInstance, szFullPath, sizeof(szFullPath)); + + /* Now lets dive in and pull out the version information: */ + dwVerInfoSize = GetFileVersionInfoSize(szFullPath, &dwVerHnd); + if (dwVerInfoSize) { + LPSTR lpstrVffInfo; + HANDLE hMem; + hMem = GlobalAlloc(GMEM_MOVEABLE, dwVerInfoSize); + lpstrVffInfo = GlobalLock(hMem); + GetFileVersionInfo(szFullPath, dwVerHnd, dwVerInfoSize, lpstrVffInfo); + lstrcpy(szGetName, GetStringRes(IDS_VER_INFO_LANG)); + + wRootLen = lstrlen(szGetName); /* Save this position */ + + /* Set the title of the dialog: */ + lstrcat(szGetName, "ProductName"); + bRetCode = VerQueryValue((LPVOID)lpstrVffInfo, + (LPSTR)szGetName, + (LPVOID)&lpVersion, + (UINT *)&uVersionLen); + + /* Notice order of version and string... */ + lstrcpy(szResult, "About "); + lstrcat(szResult, lpVersion); + + SetWindowText(hDlg, szResult); + + /* Walk through the dialog items that we want to replace: */ + for (i = DLG_VERFIRST; i <= DLG_VERLAST; i++) { + GetDlgItemText(hDlg, i, szResult, sizeof(szResult)); + szGetName[wRootLen] = (char)0; + lstrcat(szGetName, szResult); + uVersionLen = 0; + lpVersion = NULL; + bRetCode = VerQueryValue((LPVOID)lpstrVffInfo, + (LPSTR)szGetName, + (LPVOID)&lpVersion, + (UINT *)&uVersionLen); + + if (bRetCode && uVersionLen && lpVersion) { + /* Replace dialog item text with version info */ + lstrcpy(szResult, lpVersion); + SetDlgItemText(hDlg, i, szResult); + } + else + { + dwResult = GetLastError(); + + wsprintf(szResult, GetStringRes(IDS_VERSION_ERROR), dwResult); + SetDlgItemText(hDlg, i, szResult); + } + SendMessage(GetDlgItem(hDlg, i), WM_SETFONT, + (UINT)((i==DLG_VERLAST)?hFinePrint:hfontDlg), + TRUE); + } + + + GlobalUnlock(hMem); + GlobalFree(hMem); + + } + + SendMessage(GetDlgItem(hDlg, IDC_LABEL), WM_SETFONT, + (WPARAM)hfontDlg,(LPARAM)TRUE); + if (!GetSystemOSVersion(szVersion, NULL)) + strcpy(szVersion, "Unknown Version"); + SetWindowText(GetDlgItem(hDlg, IDC_OSVERSION), szVersion); + ShowWindow(hDlg, SW_SHOW); + return (TRUE); + + case WM_COMMAND: + if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) { + EndDialog(hDlg, TRUE); + DeleteObject(hfontDlg); + DeleteObject(hFinePrint); + return (TRUE); + } + break; + } + + return FALSE; +} + +VOID CALLBACK MainTimerProc(HWND hWnd, UINT uMsg, UINT idEvent, DWORD dwTime) +{ + if (ap_rescanServices) + { + GetApacheServicesStatus(); + ShowNotifyIcon(hWnd, NIM_MODIFY); + if (ap_hServiceDlg) + { + SendMessage(ap_hServiceDlg, WM_UPDATEMESSAGE, 0, 0); + + } + } + else if (FindRunningServices()) + { + ShowNotifyIcon(hWnd, NIM_MODIFY); + if (ap_hServiceDlg) + { + SendMessage(ap_hServiceDlg, WM_UPDATEMESSAGE, 0, 0); + + } + } +} + + +LRESULT CALLBACK WndProc(HWND hWnd, UINT message, + WPARAM wParam, LPARAM lParam) +{ + if (message == ap_uiTaskbarCreated) + { + /* reinstall tray icon */ + ShowNotifyIcon(hWnd, NIM_ADD); + return DefWindowProc(hWnd, message, wParam, lParam); + } + switch (message) + { + case WM_CREATE: + GetSystemOSVersion(NULL, &ap_OSVersion); + GetApacheServicesStatus(); + ShowNotifyIcon(hWnd, NIM_ADD); + SetTimer(hWnd, 10, 1000, (TIMERPROC)MainTimerProc); + ap_hServiceDlg = NULL; + break; + case WM_QUIT: + ShowNotifyIcon(hWnd, NIM_DELETE); + break; + case WM_TRAYMESSAGE: + switch(lParam) + { + case WM_LBUTTONDBLCLK: + if (!dlgServiceOn) + { + dlgServiceOn = TRUE; + DialogBox(ap_hInstance, MAKEINTRESOURCE(IDD_APSRVMON_DIALOG), + hWnd, (DLGPROC)ServiceDlgProc); + dlgServiceOn = FALSE; + ap_hServiceDlg = NULL; + } + else if (ap_hServiceDlg) + { + /* Dirty hack to bring the window to the foreground */ + SetWindowPos(ap_hServiceDlg, HWND_TOPMOST, 0, 0, 0, 0, + SWP_NOSIZE | SWP_NOMOVE | SWP_SHOWWINDOW); + SetWindowPos(ap_hServiceDlg, HWND_NOTOPMOST, 0, 0, 0, 0, + SWP_NOSIZE | SWP_NOMOVE | SWP_SHOWWINDOW); + SetFocus(ap_hServiceDlg); + } + break; + case WM_RBUTTONUP: + ShowTryPopupMenu(hWnd); + break; + } + break; + case WM_COMMAND: + switch (LOWORD(wParam)) + { + case IDM_RESTORE: + if (!dlgServiceOn) + { + dlgServiceOn = TRUE; + DialogBox(ap_hInstance, MAKEINTRESOURCE(IDD_APSRVMON_DIALOG), + hWnd, (DLGPROC)ServiceDlgProc); + dlgServiceOn = FALSE; + ap_hServiceDlg = NULL; + } + else if (ap_hServiceDlg) + SetFocus(ap_hServiceDlg); + break; + case IDM_ABOUT: + if (!dlgAboutOn) + { + dlgAboutOn = TRUE; + DialogBox(ap_hInstance, MAKEINTRESOURCE(IDD_ABOUTBOX), + hWnd, (DLGPROC)AboutDlgProc); + dlgAboutOn = FALSE; + ap_hwndAboutDlg = NULL; + } + else if (ap_hwndAboutDlg) + SetFocus(ap_hwndAboutDlg); + + break; + case IDM_EXIT: + PostQuitMessage(0); + return TRUE; + break; + } + default: + return DefWindowProc(hWnd, message, wParam, lParam); + } + + return FALSE; +} + +/* Create main invisible window */ +HWND CreateMainWindow(HINSTANCE hInstance) +{ + HWND hWnd = NULL; + WNDCLASSEX wcex; + + wcex.cbSize = sizeof(WNDCLASSEX); + + wcex.style = CS_HREDRAW | CS_VREDRAW; + wcex.lpfnWndProc = (WNDPROC)WndProc; + wcex.cbClsExtra = 0; + wcex.cbWndExtra = 0; + wcex.hInstance = hInstance; + wcex.hIcon = LoadIcon(hInstance, (LPCTSTR)IDI_APSRVMON); + wcex.hCursor = LoadCursor(NULL, IDC_ARROW); + wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); + wcex.lpszMenuName = (LPCSTR)IDC_APSRVMON; + wcex.lpszClassName = szWindowClass; + wcex.hIconSm = LoadIcon(wcex.hInstance, (LPCTSTR)IDI_APSMALL); + + if (RegisterClassEx(&wcex)) + { + hWnd = CreateWindow(szWindowClass, szTitle, + 0, 0, 0, 0, 0, + NULL, NULL, hInstance, NULL); + } + + return hWnd; + +} + + +int WINAPI WinMain(HINSTANCE hInstance, + HINSTANCE hPrevInstance, + LPTSTR lpCmdLine, + int nCmdShow) +{ + HWND hwnd; + MSG msg; + /* single instance mutex */ + HANDLE hMutex = CreateMutex(NULL, FALSE, "APSRVMON_MUTEX"); + if((hMutex == NULL) || (GetLastError() == ERROR_ALREADY_EXISTS)) + { + if (hMutex) + CloseHandle(hMutex); + + return 0; + } + + InitCommonControls(); + ap_hInstance = hInstance; + + LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING); + LoadString(hInstance, IDC_APSRVMON, szWindowClass, MAX_LOADSTRING); + ap_icoStop = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_ICOSTOP)); + ap_icoRun = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_ICORUN)); + ap_uiTaskbarCreated = RegisterWindowMessage("TaskbarCreated"); + + ZeroMemory(ap_stServices, sizeof(ST_APACHE_SERVICE) * MAX_APACHE_SERVICES); + hwnd = CreateMainWindow(hInstance); + if (hwnd != NULL) + { + while (GetMessage(&msg, NULL, 0, 0) == TRUE) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + ap_ClearServicesSt(); + } + CloseHandle(hMutex); + return 0; +} diff --git a/support/win32/ApacheMonitor.dsp b/support/win32/ApacheMonitor.dsp new file mode 100644 index 0000000000..8699a75baa --- /dev/null +++ b/support/win32/ApacheMonitor.dsp @@ -0,0 +1,143 @@ +# Microsoft Developer Studio Project File - Name="ApacheMonitor" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Application" 0x0101 + +CFG=ApacheMonitor - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "ApacheMonitor.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "ApacheMonitor.mak" CFG="ApacheMonitor - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "ApacheMonitor - Win32 Release" (based on "Win32 (x86) Application") +!MESSAGE "ApacheMonitor - Win32 Debug" (based on "Win32 (x86) Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "ApacheMonitor - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FD /c +# ADD CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /Fd"Release/ApacheMonitor" /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o NUL /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o NUL /win32 +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib advapi32.lib comctl32.lib shell32.lib version.lib /nologo /subsystem:windows /map /machine:I386 + +!ELSEIF "$(CFG)" == "ApacheMonitor - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FD /c +# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /Fd"Debug/ApacheMonitor" /FD /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o NUL /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o NUL /win32 +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib advapi32.lib comctl32.lib shell32.lib version.lib /nologo /subsystem:windows /incremental:no /debug /machine:I386 +# SUBTRACT LINK32 /pdb:none + +!ENDIF + +# Begin Target + +# Name "ApacheMonitor - Win32 Release" +# Name "ApacheMonitor - Win32 Debug" +# Begin Group "Resource Files" + +# PROP Default_Filter "*.rc,*.ico,*.bmp" +# Begin Source File + +SOURCE=.\apache_header.bmp +# End Source File +# Begin Source File + +SOURCE=.\ApacheMonitor.ico +# End Source File +# Begin Source File + +SOURCE=.\ApacheMonitor.rc + +!IF "$(CFG)" == "ApacheMonitor - Win32 Release" + +!ELSEIF "$(CFG)" == "ApacheMonitor - Win32 Debug" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\aprun.ico +# End Source File +# Begin Source File + +SOURCE=.\apsmall.ico +# End Source File +# Begin Source File + +SOURCE=.\apsrvmon.ico +# End Source File +# Begin Source File + +SOURCE=.\apstop.ico +# End Source File +# Begin Source File + +SOURCE=.\srun.bmp +# End Source File +# Begin Source File + +SOURCE=.\sstop.bmp +# End Source File +# End Group +# Begin Source File + +SOURCE=.\ApacheMonitor.c +# End Source File +# End Target +# End Project diff --git a/support/win32/ApacheMonitor.h b/support/win32/ApacheMonitor.h new file mode 100644 index 0000000000..4bdfc15914 --- /dev/null +++ b/support/win32/ApacheMonitor.h @@ -0,0 +1,49 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by ApSrvmon.rc +// +#define IDD_APSRVMON_DIALOG 101 +#define IDD_ABOUTBOX 102 +#define IDS_APP_TITLE 103 +#define IDM_RESTORE 104 +#define IDM_ABOUT 105 +#define IDM_EXIT 106 +#define IDI_APSRVMONM 107 +#define IDI_APSRVMON 108 +#define IDI_APSMALL 109 +#define IDC_APSRVMON 110 +#define IDS_VERSION_ERROR 111 +#define IDS_VER_INFO_LANG 112 +#define IDR_MAINFRAME 128 +#define IDI_ICOSTOP 129 +#define IDI_ICORUN 130 +#define IDC_STATBAR 134 +#define DLG_VERFIRST 140 +#define IDC_COMPANY 140 +#define IDC_FILEDESC 141 +#define IDC_PRODVER 142 +#define IDC_COPYRIGHT 143 +#define IDC_OSVERSION 144 +#define IDC_TRADEMARK 145 +#define DLG_VERLAST 145 +#define IDC_LABEL 146 +#define IDB_BMPSTOP 155 +#define IDB_BMPRUN 156 +#define IDB_BMPHEADER 158 +#define IDL_SERVICES 1003 +#define IDC_SSTART 1004 +#define IDC_SSTOP 1005 +#define IDC_SRESTART 1006 +#define IDC_SUNINSTALL 1008 +#define IDC_STATIC -1 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 159 +#define _APS_NEXT_COMMAND_VALUE 32771 +#define _APS_NEXT_CONTROL_VALUE 1009 +#define _APS_NEXT_SYMED_VALUE 110 +#endif +#endif diff --git a/support/win32/ApacheMonitor.ico b/support/win32/ApacheMonitor.ico Binary files differnew file mode 100644 index 0000000000..cd28dc520c --- /dev/null +++ b/support/win32/ApacheMonitor.ico diff --git a/support/win32/apache_header.bmp b/support/win32/apache_header.bmp Binary files differnew file mode 100644 index 0000000000..7b116fc3da --- /dev/null +++ b/support/win32/apache_header.bmp diff --git a/support/win32/aprun.ico b/support/win32/aprun.ico Binary files differnew file mode 100644 index 0000000000..dbd5832602 --- /dev/null +++ b/support/win32/aprun.ico diff --git a/support/win32/apstop.ico b/support/win32/apstop.ico Binary files differnew file mode 100644 index 0000000000..fba49ad292 --- /dev/null +++ b/support/win32/apstop.ico diff --git a/support/win32/srun.bmp b/support/win32/srun.bmp Binary files differnew file mode 100644 index 0000000000..90ecd46f17 --- /dev/null +++ b/support/win32/srun.bmp diff --git a/support/win32/sstop.bmp b/support/win32/sstop.bmp Binary files differnew file mode 100644 index 0000000000..ba73d87aec --- /dev/null +++ b/support/win32/sstop.bmp diff --git a/support/win32/wintty.c b/support/win32/wintty.c new file mode 100644 index 0000000000..28c5979cce --- /dev/null +++ b/support/win32/wintty.c @@ -0,0 +1,328 @@ +/* ==================================================================== + * The Apache Software License, Version 1.1 + * + * Copyright (c) 2000-2001 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. + * ==================================================================== + */ + +/* -------------------------------------------------------------------- + * + * wintty : a Apache/WinNT support utility for monitoring and + * reflecting user feedback from the Apache process via + * stdin/stdout, even as running within the service context. + * + * Originally contributed by William Rowe <wrowe@covalent.net> + * + * Note: this implementation is _very_ experimental, and error handling + * is far from complete. Using it as a cgi or pipe process allows the + * programmer to discover if facilities such as reliable piped logs + * are working as expected, or answer operator prompts that would + * otherwise be discarded by the service process. + * + * Also note the isservice detection semantics, which far exceed any + * mechanism we have discovered thus far. + * + * -------------------------------------------------------------------- + */ + +#define WIN32_LEAN_AND_MEAN +#include <windows.h> + +const char *options = +"Syntax: wintty [opts] [-?]\n\n" +" opts: -c{haracter} or -l{ine} input\n" +"\t-q{uiet} or -e{cho} input\n" +"\topts: -u{nprocessed} or -p{rocessed} input\n" +"\topts: -n{owrap} or -w{rap} output lines\n" +"\topts: -f{ormatted} or -r{aw} output lines\n" +"\topts: -v{erbose} error checking\n" +"\topts: -? for this message\n\n"; + +HANDLE herrout; +BOOL verbose = FALSE; + +void printerr(char *fmt, ...) +{ + char str[1024]; + va_list args; + DWORD len; + if (!verbose) + return; + va_start(args, fmt); + wvsprintf(str, fmt, args); + WriteFile(herrout, str, len = strlen(str), &len, NULL); +} + +DWORD WINAPI feedback(LPVOID pipeout); + +int main(int argc, char** argv) +{ + char str[1024], *contitle; + HANDLE hproc, thread; + HANDLE hwinsta, hsavewinsta; + HANDLE hdesk, hsavedesk; + HANDLE conin, conout; + HANDLE pipein, pipeout; + HANDLE hstdin, hstdout, hstderr; + DWORD conmode; + DWORD newinmode = 0, notinmode = 0; + DWORD newoutmode = 0, notoutmode = 0; + DWORD tid; + DWORD len; + BOOL isservice = FALSE; + + while (--argc) { + ++argv; + if (**argv == '/' || **argv == '-') { + switch (tolower((*argv)[1])) { + case 'c': + notinmode |= ENABLE_LINE_INPUT; break; + case 'l': + newinmode |= ENABLE_LINE_INPUT; break; + case 'q': + notinmode |= ENABLE_ECHO_INPUT; break; + case 'e': + newinmode |= ENABLE_ECHO_INPUT; break; + case 'u': + notinmode |= ENABLE_PROCESSED_INPUT; break; + case 'p': + newinmode |= ENABLE_PROCESSED_INPUT; break; + case 'n': + notoutmode |= ENABLE_WRAP_AT_EOL_OUTPUT; break; + case 'w': + newoutmode |= ENABLE_WRAP_AT_EOL_OUTPUT; break; + case 'r': + notoutmode |= ENABLE_PROCESSED_OUTPUT; break; + case 'f': + newoutmode |= ENABLE_PROCESSED_OUTPUT; break; + case 'v': + verbose = TRUE; + break; + case 't': + contitle = *(++argv); + --argc; + break; + case '?': + printf(options); + exit(1); + default: + printf("wintty option %s not recognized, use -? for help.\n\n", *argv); + exit(1); + } + } + else { + printf("wintty argument %s not understood, use -? for help.\n\n", *argv); + exit(1); + } + } + + hproc = GetCurrentProcess(); + herrout = hstderr = GetStdHandle(STD_ERROR_HANDLE); + if (!hstderr || hstderr == INVALID_HANDLE_VALUE) { + printerr("GetStdHandle(STD_ERROR_HANDLE) failed (%d)\n", GetLastError()); + } + else if (!DuplicateHandle(hproc, hstderr, + hproc, &herrout, 0, FALSE, + DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) { + printerr("DupHandle(stderr) failed (%d)\n", GetLastError()); + } + + hstdin = GetStdHandle(STD_INPUT_HANDLE); + if (!hstdin || hstdin == INVALID_HANDLE_VALUE) { + printerr("GetStdHandle(STD_INPUT_HANDLE) failed (%d)\n", GetLastError()); + } + else if (!DuplicateHandle(hproc, hstdin, + hproc, &pipein, 0, FALSE, + DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) { + printerr("DupHandle(stdin) failed (%d)\n", GetLastError()); + } + + hstdout = GetStdHandle(STD_OUTPUT_HANDLE); + if (!hstdout || hstdout == INVALID_HANDLE_VALUE) { + printerr("GetStdHandle(STD_OUTPUT_HANDLE) failed (%d)\n", GetLastError()); + } + else if (!DuplicateHandle(hproc, hstdout, + hproc, &pipeout, 0, FALSE, + DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) { + printerr("DupHandle(stdout) failed (%d)\n", GetLastError()); + } + + hsavewinsta = GetProcessWindowStation(); + if (!hsavewinsta || hsavewinsta == INVALID_HANDLE_VALUE) { + printerr("GetProcWinSta() failed (%d)\n", GetLastError()); + } + else if (!GetUserObjectInformation(hsavewinsta, UOI_NAME, str, sizeof(str), &len)) { + printerr("GetUserObjectInfo(GetProcWinSta) failed (%d)\n", GetLastError()); + CloseHandle(hsavewinsta); + } + else if (strnicmp(str, "Service-", 8) == 0) { + isservice = TRUE; + } + else + CloseHandle(hsavewinsta); + SetLastError(0); + + if (!FreeConsole()) + printerr("DupHandle(stdout) failed (%d)\n", GetLastError()); + + if (isservice) { + hwinsta = OpenWindowStation("WinSta0", TRUE, + WINSTA_ACCESSCLIPBOARD + | WINSTA_ACCESSGLOBALATOMS + | WINSTA_ENUMDESKTOPS + | WINSTA_ENUMERATE + | WINSTA_READATTRIBUTES + | WINSTA_READSCREEN + | WINSTA_WRITEATTRIBUTES); + if (!hwinsta || hwinsta == INVALID_HANDLE_VALUE) { + printerr("OpenWinSta(WinSta0) failed (%d)\n", GetLastError()); + } + else if (!SetProcessWindowStation(hwinsta)) { + printerr("SetProcWinSta(WinSta0) failed (%d)\n", GetLastError()); + } + hsavedesk = GetThreadDesktop(GetCurrentThreadId()); + hdesk = OpenDesktop("Default", 0, TRUE, + DESKTOP_READOBJECTS + | DESKTOP_CREATEWINDOW + | DESKTOP_CREATEMENU + | DESKTOP_HOOKCONTROL + | DESKTOP_JOURNALRECORD + | DESKTOP_JOURNALPLAYBACK + | DESKTOP_ENUMERATE + | DESKTOP_WRITEOBJECTS); + if (!hdesk || hdesk == INVALID_HANDLE_VALUE) { + printerr("OpenDesktop(Default) failed (%d)\n", GetLastError()); + } + else if (!SetThreadDesktop(hdesk)) { + printerr("SetThreadDesktop(Default) failed (%d)\n", GetLastError()); + } + } + + if (!AllocConsole()) { + printerr("AllocConsole(Default) failed (%d)\n", GetLastError()); + } + + if (contitle && !SetConsoleTitle(contitle)) { + printerr("SetConsoleTitle() failed (%d)\n", GetLastError()); + } + + conout = GetStdHandle(STD_OUTPUT_HANDLE); + if (!conout || conout == INVALID_HANDLE_VALUE) { + printerr("GetStdHandle(STD_OUTPUT_HANDLE) failed (%d)\n", GetLastError()); + } + else if (!GetConsoleMode(conout, &conmode)) { + printerr("GetConsoleMode(CONOUT) failed (%d)\n", GetLastError()); + } + else if (!SetConsoleMode(conout, conmode = ((conmode | newoutmode) & ~notoutmode))) { + printerr("SetConsoleMode(CONOUT, 0x%x) failed (%d)\n", conmode, GetLastError()); + } + + conin = GetStdHandle(STD_INPUT_HANDLE); + if (!conin || conin == INVALID_HANDLE_VALUE) { + printerr("GetStdHandle(STD_INPUT_HANDLE) failed (%d)\n", GetLastError()); + } + else if (!GetConsoleMode(conin, &conmode)) { + printerr("GetConsoleMode(CONOUT) failed (%d)\n", GetLastError()); + } + else if (!SetConsoleMode(conin, conmode = ((conmode | newinmode) & ~notinmode))) { + printerr("SetConsoleMode(CONIN, 0x%x) failed (%d)\n", conmode, GetLastError()); + } + + thread = CreateThread(NULL, 0, feedback, (LPVOID)pipeout, 0, &tid); + + while (ReadFile(pipein, str, sizeof(str), &len, NULL)) + if (!len || !WriteFile(conout, str, len, &len, NULL)) + break; + + printerr("[EOF] from stdin (%d)\n", GetLastError()); + + CloseHandle(pipeout); + if (!GetConsoleTitle(str, sizeof(str))) { + printerr("SetConsoleTitle() failed (%d)\n", GetLastError()); + } + else { + strcat(str, " - [Finished]"); + if (!SetConsoleTitle(str)) { + printerr("SetConsoleTitle() failed (%d)\n", GetLastError()); + } + } + + WaitForSingleObject(thread, INFINITE); + FreeConsole(); + CloseHandle(herrout); + if (isservice) { + if (!SetProcessWindowStation(hsavewinsta)) { + len = GetLastError(); + } + if (!SetThreadDesktop(hsavedesk)) { + len = GetLastError(); + } + CloseDesktop(hdesk); + CloseWindowStation(hwinsta); + } + return 0; +} + + +DWORD WINAPI feedback(LPVOID arg) +{ + HANDLE conin; + HANDLE pipeout = (HANDLE)arg; + char *str[1024]; + DWORD len; + + conin = GetStdHandle(STD_INPUT_HANDLE); + if (!conin) { + len = GetLastError(); + } + + while (ReadFile(conin, str, sizeof(str), &len, NULL)) + if (!len || !WriteFile(pipeout, str, len, &len, NULL)) + break; + + printerr("[EOF] from Console (%d)\n", GetLastError()); + + return 0; +} diff --git a/support/win32/wintty.dsp b/support/win32/wintty.dsp new file mode 100644 index 0000000000..bf62b5e80c --- /dev/null +++ b/support/win32/wintty.dsp @@ -0,0 +1,90 @@ +# Microsoft Developer Studio Project File - Name="wintty" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=wintty - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "wintty.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "wintty.mak" CFG="wintty - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "wintty - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "wintty - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "wintty - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MD /W3 /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /D "APR_DECLARE_STATIC" /D "APU_DECLARE_STATIC" /FD /c +# ADD CPP /nologo /MD /W3 /O2 /I "../srclib/apr/include" /I "../srclib/apr-util/include" /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "APR_DECLARE_STATIC" /D "APU_DECLARE_STATIC" /Fd"Release/wintty" /FD /c +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib advapi32.lib /nologo /subsystem:console /incremental:no /map /machine:I386 +# ADD LINK32 kernel32.lib user32.lib advapi32.lib /nologo /subsystem:console /incremental:no /map /machine:I386 + +!ELSEIF "$(CFG)" == "wintty - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MDd /W3 /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /D "APR_DECLARE_STATIC" /D "APU_DECLARE_STATIC" /FD /c +# ADD CPP /nologo /MDd /W3 /GX /ZI /Od /I "../srclib/apr/include" /I "../srclib/apr-util/include" /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "APR_DECLARE_STATIC" /D "APU_DECLARE_STATIC" /Fd"Debug/wintty" /FD /c +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib advapi32.lib /nologo /subsystem:console /incremental:no /map /debug /machine:I386 +# ADD LINK32 kernel32.lib user32.lib advapi32.lib /nologo /subsystem:console /incremental:no /map /debug /machine:I386 + +!ENDIF + +# Begin Target + +# Name "wintty - Win32 Release" +# Name "wintty - Win32 Debug" +# Begin Source File + +SOURCE=.\wintty.c +# End Source File +# End Target +# End Project |