diff options
60 files changed, 12585 insertions, 1942 deletions
diff --git a/.cdtproject b/.cdtproject new file mode 100644 index 0000000..5491763 --- /dev/null +++ b/.cdtproject | |||
| @@ -0,0 +1,65 @@ | |||
| 1 | <?xml version="1.0" encoding="UTF-8"?> | ||
| 2 | <?eclipse-cdt version="2.0"?> | ||
| 3 | |||
| 4 | <cdtproject id="org.eclipse.cdt.make.core.make"> | ||
| 5 | <extension id="org.eclipse.cdt.core.ELF" point="org.eclipse.cdt.core.BinaryParser"/> | ||
| 6 | <extension id="org.eclipse.cdt.core.GNU_ELF" point="org.eclipse.cdt.core.BinaryParser"> | ||
| 7 | <attribute key="addr2line" value="addr2line"/> | ||
| 8 | <attribute key="c++filt" value="c++filt"/> | ||
| 9 | </extension> | ||
| 10 | <extension id="org.eclipse.cdt.core.domsourceindexer" point="org.eclipse.cdt.core.CIndexer"/> | ||
| 11 | <data> | ||
| 12 | <item id="scannerConfiguration"> | ||
| 13 | <autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId="org.eclipse.cdt.make.core.GCCStandardMakePerProjectProfile"/> | ||
| 14 | <profile id="org.eclipse.cdt.make.core.GCCStandardMakePerProjectProfile"> | ||
| 15 | <buildOutputProvider> | ||
| 16 | <openAction enabled="true" filePath=""/> | ||
| 17 | <parser enabled="true"/> | ||
| 18 | </buildOutputProvider> | ||
| 19 | <scannerInfoProvider id="specsFile"> | ||
| 20 | <runAction arguments="-E -P -v -dD ${plugin_state_location}/${specs_file}" command="gcc" useDefault="true"/> | ||
| 21 | <parser enabled="true"/> | ||
| 22 | </scannerInfoProvider> | ||
| 23 | </profile> | ||
| 24 | <profile id="org.eclipse.cdt.make.core.GCCStandardMakePerFileProfile"> | ||
| 25 | <buildOutputProvider> | ||
| 26 | <openAction enabled="false" filePath=""/> | ||
| 27 | <parser enabled="true"/> | ||
| 28 | </buildOutputProvider> | ||
| 29 | <scannerInfoProvider id="makefileGenerator"> | ||
| 30 | <runAction arguments="-f ${project_name}_scd.mk" command="make" useDefault="true"/> | ||
| 31 | <parser enabled="false"/> | ||
| 32 | </scannerInfoProvider> | ||
| 33 | </profile> | ||
| 34 | <profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfile"> | ||
| 35 | <buildOutputProvider> | ||
| 36 | <openAction enabled="false" filePath=""/> | ||
| 37 | <parser enabled="true"/> | ||
| 38 | </buildOutputProvider> | ||
| 39 | <scannerInfoProvider id="specsFile"> | ||
| 40 | <runAction arguments="-E -P -v -dD ${plugin_state_location}/${specs_file}" command="gcc" useDefault="true"/> | ||
| 41 | <parser enabled="false"/> | ||
| 42 | </scannerInfoProvider> | ||
| 43 | </profile> | ||
| 44 | <profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfile"> | ||
| 45 | <buildOutputProvider> | ||
| 46 | <openAction enabled="false" filePath=""/> | ||
| 47 | <parser enabled="true"/> | ||
| 48 | </buildOutputProvider> | ||
| 49 | <scannerInfoProvider id="specsFile"> | ||
| 50 | <runAction arguments="-E -P -v -dD ${plugin_state_location}/${specs_file}" command="gcc" useDefault="true"/> | ||
| 51 | <parser enabled="false"/> | ||
| 52 | </scannerInfoProvider> | ||
| 53 | </profile> | ||
| 54 | </item> | ||
| 55 | <item id="org.eclipse.cdt.core.pathentry"> | ||
| 56 | <pathentry include="/home/urkle/Projects/Apache/apache_include/2.0/apr-0" kind="inc" path="" system="true"/> | ||
| 57 | <pathentry include="/home/urkle/Projects/Apache/apache_include/2.0/http" kind="inc" path="" system="true"/> | ||
| 58 | <pathentry include="/usr/include/mysql" kind="inc" path="" system="true"/> | ||
| 59 | <pathentry kind="mac" name="WITH_APACHE20" path="" value=""/> | ||
| 60 | <pathentry kind="src" path=""/> | ||
| 61 | <pathentry kind="out" path=""/> | ||
| 62 | <pathentry kind="con" path="org.eclipse.cdt.make.core.DISCOVERED_SCANNER_INFO"/> | ||
| 63 | </item> | ||
| 64 | </data> | ||
| 65 | </cdtproject> | ||
diff --git a/.cproject b/.cproject new file mode 100644 index 0000000..ce89a76 --- /dev/null +++ b/.cproject | |||
| @@ -0,0 +1,362 @@ | |||
| 1 | <?xml version="1.0" encoding="UTF-8"?> | ||
| 2 | <?fileVersion 4.0.0?> | ||
| 3 | |||
| 4 | <cproject> | ||
| 5 | <storageModule moduleId="org.eclipse.cdt.core.settings"> | ||
| 6 | <cconfiguration id="converted.config.350824298"> | ||
| 7 | |||
| 8 | |||
| 9 | <storageModule buildSystemId="org.eclipse.cdt.managedbuilder.core.configurationDataProvider" id="converted.config.350824298" moduleId="org.eclipse.cdt.core.settings" name="Linux GCC"> | ||
| 10 | <externalSettings/> | ||
| 11 | <extensions> | ||
| 12 | <extension id="org.eclipse.cdt.core.domsourceindexer" point="org.eclipse.cdt.core.CIndexer"/> | ||
| 13 | <extension id="org.eclipse.cdt.core.ELF" point="org.eclipse.cdt.core.BinaryParser"/> | ||
| 14 | <extension id="org.eclipse.cdt.core.GNU_ELF" point="org.eclipse.cdt.core.BinaryParser"/> | ||
| 15 | <extension id="org.eclipse.cdt.core.GASErrorParser" point="org.eclipse.cdt.core.ErrorParser"/> | ||
| 16 | <extension id="org.eclipse.cdt.core.GCCErrorParser" point="org.eclipse.cdt.core.ErrorParser"/> | ||
| 17 | <extension id="org.eclipse.cdt.core.GLDErrorParser" point="org.eclipse.cdt.core.ErrorParser"/> | ||
| 18 | <extension id="org.eclipse.cdt.core.MakeErrorParser" point="org.eclipse.cdt.core.ErrorParser"/> | ||
| 19 | </extensions> | ||
| 20 | </storageModule> | ||
| 21 | <storageModule moduleId="org.eclipse.cdt.core.language.mapping"> | ||
| 22 | <project-mappings/> | ||
| 23 | </storageModule> | ||
| 24 | <storageModule moduleId="org.eclipse.cdt.core.externalSettings"/> | ||
| 25 | |||
| 26 | <storageModule moduleId="cdtBuildSystem" version="4.0.0"> | ||
| 27 | <configuration artifactExtension="" artifactName="mod_log_sql" buildProperties="" description="" errorParsers="" id="converted.config.350824298" name="Linux GCC" parent="org.eclipse.cdt.build.core.prefbase.cfg"> | ||
| 28 | <folderInfo id="converted.config.350824298." name="/" resourcePath=""> | ||
| 29 | <toolChain id="cdt.managedbuild.toolchain.gnu.base.1351398655" name="Linux GCC" resourceTypeBasedDiscovery="true" superClass="cdt.managedbuild.toolchain.gnu.base"> | ||
| 30 | <targetPlatform archList="all" binaryParser="org.eclipse.cdt.core.ELF;org.eclipse.cdt.core.GNU_ELF" id="cdt.managedbuild.target.gnu.platform.base.2070600171" name="Debug Platform" osList="linux,hpux,aix,qnx" superClass="cdt.managedbuild.target.gnu.platform.base"/> | ||
| 31 | <builder appendEnvironment="true" arguments="${project_name} make" autoBuildTarget="all" buildPath="${workspace_loc:/mod_log_sql/Linux GCC}" cleanBuildTarget="clean" command="${HOME}/bin/sshmake" enableAutoBuild="false" enableCleanBuild="true" enabledIncrementalBuild="true" environment="" id="cdt.managedbuild.target.gnu.builder.base.1704124595" incrementalBuildTarget="all" keepEnvironmentInBuildfile="false" managedBuildOn="false" name="Gnu Make Builder" stopOnErr="false" superClass="cdt.managedbuild.target.gnu.builder.base"/> | ||
| 32 | <tool id="cdt.managedbuild.tool.gnu.c.compiler.base.264929639" name="GCC C Compiler" superClass="cdt.managedbuild.tool.gnu.c.compiler.base"> | ||
| 33 | <option id="gnu.c.compiler.option.include.paths.277599024" name="Include paths (-I)" superClass="gnu.c.compiler.option.include.paths" valueType="includePath"> | ||
| 34 | <listOptionValue builtIn="false" value=""${workspace_loc:/includes/2.2/apr-1}""/> | ||
| 35 | <listOptionValue builtIn="false" value=""${workspace_loc:/includes/2.2/httpd}""/> | ||
| 36 | <listOptionValue builtIn="false" value=""${workspace_loc:/Includes/mysql}""/> | ||
| 37 | <listOptionValue builtIn="false" value="/usr/include"/> | ||
| 38 | </option> | ||
| 39 | <option id="gnu.c.compiler.option.preprocessor.def.symbols.1267853547" name="Defined symbols (-D)" superClass="gnu.c.compiler.option.preprocessor.def.symbols" valueType="definedSymbols"> | ||
| 40 | <listOptionValue builtIn="false" value="WITH_APACHE20=1"/> | ||
| 41 | <listOptionValue builtIn="false" value="HAVE_CONFIG_H=1"/> | ||
| 42 | </option> | ||
| 43 | <option id="gnu.c.compiler.option.preprocessor.nostdinc.444721609" name="Do not search system directories (-nostdinc)" superClass="gnu.c.compiler.option.preprocessor.nostdinc"/> | ||
| 44 | <option id="gnu.c.compiler.option.preprocessor.preprocess.1423240087" name="Preprocess only (-E)" superClass="gnu.c.compiler.option.preprocessor.preprocess"/> | ||
| 45 | <option id="gnu.c.compiler.option.preprocessor.undef.symbol.732547404" name="Undefined symbols (-U)" superClass="gnu.c.compiler.option.preprocessor.undef.symbol"/> | ||
| 46 | <option id="gnu.c.compiler.option.optimization.level.92192040" name="Optimization Level" superClass="gnu.c.compiler.option.optimization.level"/> | ||
| 47 | <option id="gnu.c.compiler.option.optimization.flags.893054260" name="Other optimization flags" superClass="gnu.c.compiler.option.optimization.flags"/> | ||
| 48 | <option id="gnu.c.compiler.option.debugging.level.1989991712" name="Debug Level" superClass="gnu.c.compiler.option.debugging.level"/> | ||
| 49 | <option id="gnu.c.compiler.option.debugging.other.1416137166" name="Other debugging flags" superClass="gnu.c.compiler.option.debugging.other"/> | ||
| 50 | <option id="gnu.c.compiler.option.debugging.gprof.817771115" name="Generate gprof information (-pg)" superClass="gnu.c.compiler.option.debugging.gprof"/> | ||
| 51 | <option id="gnu.c.compiler.option.debugging.prof.1130721011" name="Generate prof information (-p)" superClass="gnu.c.compiler.option.debugging.prof"/> | ||
| 52 | <option id="gnu.c.compiler.option.warnings.syntax.1338013879" name="Check syntax only (-fsyntax-only)" superClass="gnu.c.compiler.option.warnings.syntax"/> | ||
| 53 | <option id="gnu.c.compiler.option.warnings.pedantic.662901430" name="Pedantic (-pedantic)" superClass="gnu.c.compiler.option.warnings.pedantic"/> | ||
| 54 | <option id="gnu.c.compiler.option.warnings.pedantic.error.1267513833" name="Pedantic warnings as errors (-pedantic-errors)" superClass="gnu.c.compiler.option.warnings.pedantic.error"/> | ||
| 55 | <option id="gnu.c.compiler.option.warnings.nowarn.805873345" name="Inhibit all warnings (-w)" superClass="gnu.c.compiler.option.warnings.nowarn"/> | ||
| 56 | <option id="gnu.c.compiler.option.warnings.allwarn.910485376" name="All warnings (-Wall)" superClass="gnu.c.compiler.option.warnings.allwarn"/> | ||
| 57 | <option id="gnu.c.compiler.option.warnings.toerrors.1479340869" name="Warnings as errors (-Werror)" superClass="gnu.c.compiler.option.warnings.toerrors"/> | ||
| 58 | <option id="gnu.c.compiler.option.misc.other.1562816435" name="Other flags" superClass="gnu.c.compiler.option.misc.other"/> | ||
| 59 | <option id="gnu.c.compiler.option.misc.verbose.1265706406" name="Verbose (-v)" superClass="gnu.c.compiler.option.misc.verbose"/> | ||
| 60 | <option id="gnu.c.compiler.option.misc.ansi.1838461503" name="Support ANSI programs (-ansi)" superClass="gnu.c.compiler.option.misc.ansi"/> | ||
| 61 | <inputType id="cdt.managedbuild.tool.gnu.c.compiler.input.616458355" superClass="cdt.managedbuild.tool.gnu.c.compiler.input"/> | ||
| 62 | </tool> | ||
| 63 | <tool id="cdt.managedbuild.tool.gnu.cpp.linker.base.1761639329" name="GCC C++ Linker" superClass="cdt.managedbuild.tool.gnu.cpp.linker.base"/> | ||
| 64 | <tool id="cdt.managedbuild.tool.gnu.cpp.compiler.base.1017302318" name="GCC C++ Compiler" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.base"/> | ||
| 65 | <tool id="com.redhat.eclipse.cdt.autotools.tool.autogen.131560145" name="autogen.sh" superClass="com.redhat.eclipse.cdt.autotools.tool.autogen"/> | ||
| 66 | <tool id="com.redhat.eclipse.cdt.autotools.tool.configure.1351107831" name="configure" superClass="com.redhat.eclipse.cdt.autotools.tool.configure"/> | ||
| 67 | <tool id="cdt.managedbuild.tool.gnu.archiver.base.1647937860" name="GCC Archiver" superClass="cdt.managedbuild.tool.gnu.archiver.base"/> | ||
| 68 | <tool id="cdt.managedbuild.tool.gnu.c.linker.base.78392638" name="GCC C Linker" superClass="cdt.managedbuild.tool.gnu.c.linker.base"> | ||
| 69 | <option id="gnu.c.link.option.nostart.1696010990" name="Do not use standard start files (-nostartfiles)" superClass="gnu.c.link.option.nostart"/> | ||
| 70 | <option id="gnu.c.link.option.nodeflibs.149057311" name="Do not use default libraries (-nodefaultlibs)" superClass="gnu.c.link.option.nodeflibs"/> | ||
| 71 | <option id="gnu.c.link.option.nostdlibs.127927094" name="No startup or default libs (-nostdlib)" superClass="gnu.c.link.option.nostdlibs"/> | ||
| 72 | <option id="gnu.c.link.option.strip.899862009" name="Omit all symbol information (-s)" superClass="gnu.c.link.option.strip"/> | ||
| 73 | <option id="gnu.c.link.option.noshared.2764585" name="No shared libraries (-static)" superClass="gnu.c.link.option.noshared"/> | ||
| 74 | <option id="gnu.c.link.option.libs.2040864973" name="Libraries (-l)" superClass="gnu.c.link.option.libs"/> | ||
| 75 | <option id="gnu.c.link.option.paths.1741038220" name="Library search path (-L)" superClass="gnu.c.link.option.paths"/> | ||
| 76 | <option id="gnu.c.link.option.ldflags.132803838" name="Linker flags" superClass="gnu.c.link.option.ldflags"/> | ||
| 77 | <option id="gnu.c.link.option.other.378428644" name="Other options (-Xlinker [option])" superClass="gnu.c.link.option.other"/> | ||
| 78 | <option id="gnu.c.link.option.userobjs.495685024" name="Other objects" superClass="gnu.c.link.option.userobjs"/> | ||
| 79 | <option id="gnu.c.link.option.shared.626871050" name="Shared (-shared)" superClass="gnu.c.link.option.shared"/> | ||
| 80 | <option id="gnu.c.link.option.soname.1808779260" name="Shared object name (-Wl,-soname=)" superClass="gnu.c.link.option.soname"/> | ||
| 81 | <option id="gnu.c.link.option.implname.1206228182" name="Import Library name (-Wl,--out-implib=)" superClass="gnu.c.link.option.implname"/> | ||
| 82 | <option id="gnu.c.link.option.defname.2102205174" name="DEF file name (-Wl,--output-def=)" superClass="gnu.c.link.option.defname"/> | ||
| 83 | <inputType id="cdt.managedbuild.tool.gnu.c.linker.input.2076963094" superClass="cdt.managedbuild.tool.gnu.c.linker.input"> | ||
| 84 | <additionalInput kind="additionalinputdependency" paths="$(USER_OBJS)"/> | ||
| 85 | <additionalInput kind="additionalinput" paths="$(LIBS)"/> | ||
| 86 | </inputType> | ||
| 87 | </tool> | ||
| 88 | <tool id="cdt.managedbuild.tool.gnu.assembler.base.467752860" name="GCC Assembler" superClass="cdt.managedbuild.tool.gnu.assembler.base"> | ||
| 89 | <option id="gnu.both.asm.option.include.paths.73704182" name="Include paths (-I)" superClass="gnu.both.asm.option.include.paths"/> | ||
| 90 | <inputType id="cdt.managedbuild.tool.gnu.assembler.input.1742318494" superClass="cdt.managedbuild.tool.gnu.assembler.input"/> | ||
| 91 | </tool> | ||
| 92 | </toolChain> | ||
| 93 | </folderInfo> | ||
| 94 | </configuration> | ||
| 95 | </storageModule> | ||
| 96 | <storageModule moduleId="org.eclipse.cdt.core.pathentry"> | ||
| 97 | <pathentry kind="src" path=""/> | ||
| 98 | <pathentry kind="out" path=""/> | ||
| 99 | <pathentry kind="con" path="org.eclipse.cdt.make.core.DISCOVERED_SCANNER_INFO"/> | ||
| 100 | <pathentry base-path="includes" include="2.0/apr-0" kind="inc" path="" system="true"/> | ||
| 101 | <pathentry base-path="includes" include="2.0/httpd" kind="inc" path="" system="true"/> | ||
| 102 | <pathentry kind="mac" name="WITH_APACHE20" path="" value="1"/> | ||
| 103 | <pathentry kind="mac" name="HAVE_CONFIG_H" path="" value="1"/> | ||
| 104 | </storageModule> | ||
| 105 | <storageModule moduleId="org.eclipse.cdt.make.core.buildtargets"/> | ||
| 106 | |||
| 107 | |||
| 108 | <storageModule moduleId="scannerConfiguration"> | ||
| 109 | <autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId="org.eclipse.cdt.make.core.GCCStandardMakePerProjectProfile"/> | ||
| 110 | <profile id="org.eclipse.cdt.make.core.GCCStandardMakePerProjectProfile"> | ||
| 111 | <buildOutputProvider> | ||
| 112 | <openAction enabled="true" filePath=""/> | ||
| 113 | <parser enabled="true"/> | ||
| 114 | </buildOutputProvider> | ||
| 115 | <scannerInfoProvider id="specsFile"> | ||
| 116 | <runAction arguments="-E -P -v -dD ${plugin_state_location}/${specs_file}" command="gcc" useDefault="true"/> | ||
| 117 | <parser enabled="true"/> | ||
| 118 | </scannerInfoProvider> | ||
| 119 | </profile> | ||
| 120 | <profile id="org.eclipse.cdt.make.core.GCCStandardMakePerFileProfile"> | ||
| 121 | <buildOutputProvider> | ||
| 122 | <openAction enabled="false" filePath=""/> | ||
| 123 | <parser enabled="true"/> | ||
| 124 | </buildOutputProvider> | ||
| 125 | <scannerInfoProvider id="makefileGenerator"> | ||
| 126 | <runAction arguments="-f ${project_name}_scd.mk" command="make" useDefault="true"/> | ||
| 127 | <parser enabled="false"/> | ||
| 128 | </scannerInfoProvider> | ||
| 129 | </profile> | ||
| 130 | <profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfile"> | ||
| 131 | <buildOutputProvider> | ||
| 132 | <openAction enabled="false" filePath=""/> | ||
| 133 | <parser enabled="true"/> | ||
| 134 | </buildOutputProvider> | ||
| 135 | <scannerInfoProvider id="specsFile"> | ||
| 136 | <runAction arguments="-E -P -v -dD ${plugin_state_location}/${specs_file}" command="gcc" useDefault="true"/> | ||
| 137 | <parser enabled="false"/> | ||
| 138 | </scannerInfoProvider> | ||
| 139 | </profile> | ||
| 140 | <profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfile"> | ||
| 141 | <buildOutputProvider> | ||
| 142 | <openAction enabled="false" filePath=""/> | ||
| 143 | <parser enabled="true"/> | ||
| 144 | </buildOutputProvider> | ||
| 145 | <scannerInfoProvider id="specsFile"> | ||
| 146 | <runAction arguments="-E -P -v -dD ${plugin_state_location}/${specs_file}" command="gcc" useDefault="true"/> | ||
| 147 | <parser enabled="false"/> | ||
| 148 | </scannerInfoProvider> | ||
| 149 | </profile> | ||
| 150 | <profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfileCPP"> | ||
| 151 | <buildOutputProvider> | ||
| 152 | <openAction enabled="false" filePath=""/> | ||
| 153 | <parser enabled="true"/> | ||
| 154 | </buildOutputProvider> | ||
| 155 | <scannerInfoProvider id="specsFile"> | ||
| 156 | <runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.cpp" command="g++" useDefault="true"/> | ||
| 157 | <parser enabled="false"/> | ||
| 158 | </scannerInfoProvider> | ||
| 159 | </profile> | ||
| 160 | <profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfileC"> | ||
| 161 | <buildOutputProvider> | ||
| 162 | <openAction enabled="false" filePath=""/> | ||
| 163 | <parser enabled="true"/> | ||
| 164 | </buildOutputProvider> | ||
| 165 | <scannerInfoProvider id="specsFile"> | ||
| 166 | <runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.c" command="gcc" useDefault="true"/> | ||
| 167 | <parser enabled="false"/> | ||
| 168 | </scannerInfoProvider> | ||
| 169 | </profile> | ||
| 170 | <profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfileCPP"> | ||
| 171 | <buildOutputProvider> | ||
| 172 | <openAction enabled="false" filePath=""/> | ||
| 173 | <parser enabled="true"/> | ||
| 174 | </buildOutputProvider> | ||
| 175 | <scannerInfoProvider id="specsFile"> | ||
| 176 | <runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.cpp" command="g++" useDefault="true"/> | ||
| 177 | <parser enabled="false"/> | ||
| 178 | </scannerInfoProvider> | ||
| 179 | </profile> | ||
| 180 | <profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfileC"> | ||
| 181 | <buildOutputProvider> | ||
| 182 | <openAction enabled="false" filePath=""/> | ||
| 183 | <parser enabled="true"/> | ||
| 184 | </buildOutputProvider> | ||
| 185 | <scannerInfoProvider id="specsFile"> | ||
| 186 | <runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.c" command="gcc" useDefault="true"/> | ||
| 187 | <parser enabled="false"/> | ||
| 188 | </scannerInfoProvider> | ||
| 189 | </profile> | ||
| 190 | <scannerConfigBuildInfo instanceId="converted.config.350824298"> | ||
| 191 | <autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId="org.eclipse.cdt.make.core.GCCStandardMakePerProjectProfile"/> | ||
| 192 | <profile id="org.eclipse.cdt.make.core.GCCStandardMakePerProjectProfile"> | ||
| 193 | <buildOutputProvider> | ||
| 194 | <openAction enabled="true" filePath=""/> | ||
| 195 | <parser enabled="true"/> | ||
| 196 | </buildOutputProvider> | ||
| 197 | <scannerInfoProvider id="specsFile"> | ||
| 198 | <runAction arguments="-E -P -v -dD ${plugin_state_location}/${specs_file}" command="gcc" useDefault="true"/> | ||
| 199 | <parser enabled="true"/> | ||
| 200 | </scannerInfoProvider> | ||
| 201 | </profile> | ||
| 202 | <profile id="org.eclipse.cdt.make.core.GCCStandardMakePerFileProfile"> | ||
| 203 | <buildOutputProvider> | ||
| 204 | <openAction enabled="true" filePath=""/> | ||
| 205 | <parser enabled="true"/> | ||
| 206 | </buildOutputProvider> | ||
| 207 | <scannerInfoProvider id="makefileGenerator"> | ||
| 208 | <runAction arguments="-f ${project_name}_scd.mk" command="make" useDefault="true"/> | ||
| 209 | <parser enabled="true"/> | ||
| 210 | </scannerInfoProvider> | ||
| 211 | </profile> | ||
| 212 | <profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfile"> | ||
| 213 | <buildOutputProvider> | ||
| 214 | <openAction enabled="true" filePath=""/> | ||
| 215 | <parser enabled="true"/> | ||
| 216 | </buildOutputProvider> | ||
| 217 | <scannerInfoProvider id="specsFile"> | ||
| 218 | <runAction arguments="-E -P -v -dD ${plugin_state_location}/${specs_file}" command="gcc" useDefault="true"/> | ||
| 219 | <parser enabled="true"/> | ||
| 220 | </scannerInfoProvider> | ||
| 221 | </profile> | ||
| 222 | <profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfileCPP"> | ||
| 223 | <buildOutputProvider> | ||
| 224 | <openAction enabled="true" filePath=""/> | ||
| 225 | <parser enabled="true"/> | ||
| 226 | </buildOutputProvider> | ||
| 227 | <scannerInfoProvider id="specsFile"> | ||
| 228 | <runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.cpp" command="g++" useDefault="true"/> | ||
| 229 | <parser enabled="true"/> | ||
| 230 | </scannerInfoProvider> | ||
| 231 | </profile> | ||
| 232 | <profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfileC"> | ||
| 233 | <buildOutputProvider> | ||
| 234 | <openAction enabled="true" filePath=""/> | ||
| 235 | <parser enabled="true"/> | ||
| 236 | </buildOutputProvider> | ||
| 237 | <scannerInfoProvider id="specsFile"> | ||
| 238 | <runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.c" command="gcc" useDefault="true"/> | ||
| 239 | <parser enabled="true"/> | ||
| 240 | </scannerInfoProvider> | ||
| 241 | </profile> | ||
| 242 | <profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfile"> | ||
| 243 | <buildOutputProvider> | ||
| 244 | <openAction enabled="true" filePath=""/> | ||
| 245 | <parser enabled="true"/> | ||
| 246 | </buildOutputProvider> | ||
| 247 | <scannerInfoProvider id="specsFile"> | ||
| 248 | <runAction arguments="-E -P -v -dD ${plugin_state_location}/${specs_file}" command="gcc" useDefault="true"/> | ||
| 249 | <parser enabled="true"/> | ||
| 250 | </scannerInfoProvider> | ||
| 251 | </profile> | ||
| 252 | <profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfileCPP"> | ||
| 253 | <buildOutputProvider> | ||
| 254 | <openAction enabled="true" filePath=""/> | ||
| 255 | <parser enabled="true"/> | ||
| 256 | </buildOutputProvider> | ||
| 257 | <scannerInfoProvider id="specsFile"> | ||
| 258 | <runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.cpp" command="g++" useDefault="true"/> | ||
| 259 | <parser enabled="true"/> | ||
| 260 | </scannerInfoProvider> | ||
| 261 | </profile> | ||
| 262 | <profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfileC"> | ||
| 263 | <buildOutputProvider> | ||
| 264 | <openAction enabled="true" filePath=""/> | ||
| 265 | <parser enabled="true"/> | ||
| 266 | </buildOutputProvider> | ||
| 267 | <scannerInfoProvider id="specsFile"> | ||
| 268 | <runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.c" command="gcc" useDefault="true"/> | ||
| 269 | <parser enabled="true"/> | ||
| 270 | </scannerInfoProvider> | ||
| 271 | </profile> | ||
| 272 | </scannerConfigBuildInfo> | ||
| 273 | <scannerConfigBuildInfo instanceId="converted.config.350824298;converted.config.350824298.;cdt.managedbuild.tool.gnu.c.compiler.base.264929639;cdt.managedbuild.tool.gnu.c.compiler.input.616458355"> | ||
| 274 | <autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfileC"/> | ||
| 275 | <profile id="org.eclipse.cdt.make.core.GCCStandardMakePerProjectProfile"> | ||
| 276 | <buildOutputProvider> | ||
| 277 | <openAction enabled="true" filePath=""/> | ||
| 278 | <parser enabled="true"/> | ||
| 279 | </buildOutputProvider> | ||
| 280 | <scannerInfoProvider id="specsFile"> | ||
| 281 | <runAction arguments="-E -P -v -dD ${plugin_state_location}/${specs_file}" command="gcc" useDefault="true"/> | ||
| 282 | <parser enabled="true"/> | ||
| 283 | </scannerInfoProvider> | ||
| 284 | </profile> | ||
| 285 | <profile id="org.eclipse.cdt.make.core.GCCStandardMakePerFileProfile"> | ||
| 286 | <buildOutputProvider> | ||
| 287 | <openAction enabled="false" filePath=""/> | ||
| 288 | <parser enabled="true"/> | ||
| 289 | </buildOutputProvider> | ||
| 290 | <scannerInfoProvider id="makefileGenerator"> | ||
| 291 | <runAction arguments="-f ${project_name}_scd.mk" command="make" useDefault="true"/> | ||
| 292 | <parser enabled="false"/> | ||
| 293 | </scannerInfoProvider> | ||
| 294 | </profile> | ||
| 295 | <profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfile"> | ||
| 296 | <buildOutputProvider> | ||
| 297 | <openAction enabled="true" filePath=""/> | ||
| 298 | <parser enabled="true"/> | ||
| 299 | </buildOutputProvider> | ||
| 300 | <scannerInfoProvider id="specsFile"> | ||
| 301 | <runAction arguments="-E -P -v -dD ${plugin_state_location}/${specs_file}" command="gcc" useDefault="true"/> | ||
| 302 | <parser enabled="false"/> | ||
| 303 | </scannerInfoProvider> | ||
| 304 | </profile> | ||
| 305 | <profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfile"> | ||
| 306 | <buildOutputProvider> | ||
| 307 | <openAction enabled="true" filePath=""/> | ||
| 308 | <parser enabled="true"/> | ||
| 309 | </buildOutputProvider> | ||
| 310 | <scannerInfoProvider id="specsFile"> | ||
| 311 | <runAction arguments="-E -P -v -dD ${plugin_state_location}/${specs_file}" command="gcc" useDefault="true"/> | ||
| 312 | <parser enabled="false"/> | ||
| 313 | </scannerInfoProvider> | ||
| 314 | </profile> | ||
| 315 | <profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfileCPP"> | ||
| 316 | <buildOutputProvider> | ||
| 317 | <openAction enabled="true" filePath=""/> | ||
| 318 | <parser enabled="true"/> | ||
| 319 | </buildOutputProvider> | ||
| 320 | <scannerInfoProvider id="specsFile"> | ||
| 321 | <runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.cpp" command="g++" useDefault="true"/> | ||
| 322 | <parser enabled="false"/> | ||
| 323 | </scannerInfoProvider> | ||
| 324 | </profile> | ||
| 325 | <profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfileC"> | ||
| 326 | <buildOutputProvider> | ||
| 327 | <openAction enabled="true" filePath=""/> | ||
| 328 | <parser enabled="true"/> | ||
| 329 | </buildOutputProvider> | ||
| 330 | <scannerInfoProvider id="specsFile"> | ||
| 331 | <runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.c" command="gcc" useDefault="true"/> | ||
| 332 | <parser enabled="false"/> | ||
| 333 | </scannerInfoProvider> | ||
| 334 | </profile> | ||
| 335 | <profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfileCPP"> | ||
| 336 | <buildOutputProvider> | ||
| 337 | <openAction enabled="true" filePath=""/> | ||
| 338 | <parser enabled="true"/> | ||
| 339 | </buildOutputProvider> | ||
| 340 | <scannerInfoProvider id="specsFile"> | ||
| 341 | <runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.cpp" command="g++" useDefault="true"/> | ||
| 342 | <parser enabled="false"/> | ||
| 343 | </scannerInfoProvider> | ||
| 344 | </profile> | ||
| 345 | <profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfileC"> | ||
| 346 | <buildOutputProvider> | ||
| 347 | <openAction enabled="true" filePath=""/> | ||
| 348 | <parser enabled="true"/> | ||
| 349 | </buildOutputProvider> | ||
| 350 | <scannerInfoProvider id="specsFile"> | ||
| 351 | <runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.c" command="gcc" useDefault="true"/> | ||
| 352 | <parser enabled="false"/> | ||
| 353 | </scannerInfoProvider> | ||
| 354 | </profile> | ||
| 355 | </scannerConfigBuildInfo> | ||
| 356 | </storageModule> | ||
| 357 | </cconfiguration> | ||
| 358 | </storageModule> | ||
| 359 | <storageModule moduleId="cdtBuildSystem" version="4.0.0"> | ||
| 360 | <project id="mod_log_sql.null.887965423" name="mod_log_sql"/> | ||
| 361 | </storageModule> | ||
| 362 | </cproject> | ||
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..82f7596 --- /dev/null +++ b/.gitignore | |||
| @@ -0,0 +1,14 @@ | |||
| 1 | /aclocal.m4 | ||
| 2 | /autom4te.cache | ||
| 3 | /config.h* | ||
| 4 | /config.nice | ||
| 5 | /config.status | ||
| 6 | /config.log | ||
| 7 | .libs | ||
| 8 | Makefile | ||
| 9 | /configure | ||
| 10 | /stamp-h* | ||
| 11 | *.o | ||
| 12 | *.lo | ||
| 13 | *.slo | ||
| 14 | *.la | ||
diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..bc62d0d --- /dev/null +++ b/.gitmodules | |||
| @@ -0,0 +1,3 @@ | |||
| 1 | [submodule "m4"] | ||
| 2 | path = m4 | ||
| 3 | url = gitosis@git.outoforder.cc:autoconf.git | ||
diff --git a/.project b/.project new file mode 100644 index 0000000..9af17e9 --- /dev/null +++ b/.project | |||
| @@ -0,0 +1,86 @@ | |||
| 1 | <?xml version="1.0" encoding="UTF-8"?> | ||
| 2 | <projectDescription> | ||
| 3 | <name>mod_log_sql</name> | ||
| 4 | <comment></comment> | ||
| 5 | <projects> | ||
| 6 | <project>includes</project> | ||
| 7 | </projects> | ||
| 8 | <buildSpec> | ||
| 9 | <buildCommand> | ||
| 10 | <name>org.eclipse.cdt.managedbuilder.core.genmakebuilder</name> | ||
| 11 | <triggers>clean,full,incremental,</triggers> | ||
| 12 | <arguments> | ||
| 13 | <dictionary> | ||
| 14 | <key>org.eclipse.cdt.make.core.fullBuildTarget</key> | ||
| 15 | <value>all</value> | ||
| 16 | </dictionary> | ||
| 17 | <dictionary> | ||
| 18 | <key>org.eclipse.cdt.make.core.enableAutoBuild</key> | ||
| 19 | <value>false</value> | ||
| 20 | </dictionary> | ||
| 21 | <dictionary> | ||
| 22 | <key>org.eclipse.cdt.make.core.environment</key> | ||
| 23 | <value></value> | ||
| 24 | </dictionary> | ||
| 25 | <dictionary> | ||
| 26 | <key>org.eclipse.cdt.make.core.buildLocation</key> | ||
| 27 | <value>${workspace_loc:/mod_log_sql/Linux GCC}</value> | ||
| 28 | </dictionary> | ||
| 29 | <dictionary> | ||
| 30 | <key>?name?</key> | ||
| 31 | <value></value> | ||
| 32 | </dictionary> | ||
| 33 | <dictionary> | ||
| 34 | <key>org.eclipse.cdt.make.core.enableFullBuild</key> | ||
| 35 | <value>true</value> | ||
| 36 | </dictionary> | ||
| 37 | <dictionary> | ||
| 38 | <key>org.eclipse.cdt.make.core.enableCleanBuild</key> | ||
| 39 | <value>true</value> | ||
| 40 | </dictionary> | ||
| 41 | <dictionary> | ||
| 42 | <key>org.eclipse.cdt.make.core.cleanBuildTarget</key> | ||
| 43 | <value>clean</value> | ||
| 44 | </dictionary> | ||
| 45 | <dictionary> | ||
| 46 | <key>org.eclipse.cdt.make.core.append_environment</key> | ||
| 47 | <value>true</value> | ||
| 48 | </dictionary> | ||
| 49 | <dictionary> | ||
| 50 | <key>org.eclipse.cdt.make.core.contents</key> | ||
| 51 | <value>org.eclipse.cdt.make.core.activeConfigSettings</value> | ||
| 52 | </dictionary> | ||
| 53 | <dictionary> | ||
| 54 | <key>org.eclipse.cdt.make.core.useDefaultBuildCmd</key> | ||
| 55 | <value>false</value> | ||
| 56 | </dictionary> | ||
| 57 | <dictionary> | ||
| 58 | <key>org.eclipse.cdt.make.core.buildArguments</key> | ||
| 59 | <value>${project_name} make -k</value> | ||
| 60 | </dictionary> | ||
| 61 | <dictionary> | ||
| 62 | <key>org.eclipse.cdt.make.core.buildCommand</key> | ||
| 63 | <value>${HOME}/bin/sshmake</value> | ||
| 64 | </dictionary> | ||
| 65 | <dictionary> | ||
| 66 | <key>org.eclipse.cdt.make.core.autoBuildTarget</key> | ||
| 67 | <value>all</value> | ||
| 68 | </dictionary> | ||
| 69 | <dictionary> | ||
| 70 | <key>org.eclipse.cdt.make.core.stopOnError</key> | ||
| 71 | <value>false</value> | ||
| 72 | </dictionary> | ||
| 73 | </arguments> | ||
| 74 | </buildCommand> | ||
| 75 | <buildCommand> | ||
| 76 | <name>org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder</name> | ||
| 77 | <arguments> | ||
| 78 | </arguments> | ||
| 79 | </buildCommand> | ||
| 80 | </buildSpec> | ||
| 81 | <natures> | ||
| 82 | <nature>org.eclipse.cdt.core.cnature</nature> | ||
| 83 | <nature>org.eclipse.cdt.managedbuilder.core.ScannerConfigNature</nature> | ||
| 84 | <nature>org.eclipse.cdt.managedbuilder.core.managedBuildNature</nature> | ||
| 85 | </natures> | ||
| 86 | </projectDescription> | ||
| @@ -0,0 +1,11 @@ | |||
| 1 | Edward Rudd <eddie at omegaware dot com> | ||
| 2 | Apache 2.0 port. Current Maintainer as of February, 2004 | ||
| 3 | |||
| 4 | Christopher B. Powell <chris at grubbybaby dot com> | ||
| 5 | Maintainer since version 1.06 | ||
| 6 | |||
| 7 | Zeev Suraski <bourbon at netvision.net dot il> | ||
| 8 | Adding the mysql routines (intial revisions) | ||
| 9 | |||
| 10 | The Apache Foundation | ||
| 11 | standard apache logging module for which this module is based on | ||
| @@ -1,20 +1,128 @@ | |||
| 1 | $Id: CHANGELOG,v 1.20 2002/12/18 01:39:28 helios Exp $ | 1 | 1.101: 2005-02-18? |
| 2 | 2 | * fix empty comparison on itemsets for solaris issue #41 | |
| 3 | TODO: | 3 | |
| 4 | * Rethink documentation flow and rewrite? | 4 | 1.100: 2005-01-11 |
| 5 | * Port connection portion to other DBMS? Genericize the module? Start with | 5 | * fixes for NULL fields (empty request_args) |
| 6 | PostgreSQL. | 6 | * fixed a mysql reconnect bug |
| 7 | * GNU autoconf | 7 | * wrong hostname was logged when using mass virtual hosting |
| 8 | * merge server config into vh config | 8 | Use, the new 'V' logformat |
| 9 | * port to Apache 2.x | 9 | * formatted massvirutalhosting tables to swap - with _. |
| 10 | * does determining table name in massvirtual mode upon every request | 10 | * included winconfig.h in makefile |
| 11 | cause performance degradation? If so fix. | 11 | * fixed quoting of unique_id |
| 12 | * LogSQLRotateLogs directive with daily/monthly/weekly/etc. | 12 | * converted documentation to OOO Docbook ( a subset of the full docbook DTD) |
| 13 | * new format char: IP as bigint? | 13 | |
| 14 | * socket-based middleman daemon with configurable conns, or connect/disconnect. | 14 | 1.99: 2004-07-28 |
| 15 | * ignore by cookie | 15 | * Added DBI support (not completed yet) |
| 16 | 16 | * fixed segfault in the mysql driver escape_string function | |
| 17 | CHANGES: | 17 | * fixed segfault when forcepreserve is set on. |
| 18 | * switched escape_string driver function to include the string quotes. | ||
| 19 | * On Database connection failure Database errors are printed at ERR loglevel | ||
| 20 | instead of debug | ||
| 21 | * included build-apache(13|2).bat scripts in the distribution | ||
| 22 | |||
| 23 | 1.98: 2004-05-12 | ||
| 24 | * re-fixed apache.m4 to better detect APR and APU code | ||
| 25 | * fixed for win32 compilation under apache 2 | ||
| 26 | * lowered minumum required apache 2 version to 2.0.40 (RH9) | ||
| 27 | * Added LogSQLDisablePreserve to disable the preserve file. | ||
| 28 | * Changed default preserve file location to logs/mod_log_sql-preserve | ||
| 29 | And made LogSQLPreserveFile root relative. | ||
| 30 | * fixed bug where preserve file wouldn't be created in apache 2 | ||
| 31 | * finished TransferLog documentation | ||
| 32 | * changed strstr to ap_strstr calls and strchr to ap_strchr calls to | ||
| 33 | stop warning when compiling with AP_DEBUG enabled in apache 2 | ||
| 34 | * Updated apache m4 to not pass for apache 1.3 if apache 2 is found | ||
| 35 | |||
| 36 | 1.97: 2004-04-08 | ||
| 37 | * fixed apache.m4 to work with apache 2 setups with different include | ||
| 38 | directories for APR and APU then core Apache | ||
| 39 | * cleaned up configuration documentation (updated due to deprecated commands) | ||
| 40 | * LogSQLLoginInfo updated to support a connection URI | ||
| 41 | * new module "registration" macros to clean up new driver modules | ||
| 42 | * mysql driver completely separted from core module | ||
| 43 | |||
| 44 | 1.96: 2004-03-04 | ||
| 45 | * fixed LogSQLPreserveFile config parameter | ||
| 46 | * reworked safe_create_tables and core SQL insert routine. | ||
| 47 | * renamed log_sql_* variables and typedefs to logsql_* | ||
| 48 | * separated all mysql specific code to separate code module (DB abstraction) | ||
| 49 | * separated TODO from Changelog | ||
| 50 | * fixed default value of socket file. it's really /var/lib/mysql/mysql.sock not | ||
| 51 | /tmp/mysql.sock | ||
| 52 | * Documentation completely converted to Docbook | ||
| 53 | * Documentation updated. (still needs more work done) | ||
| 54 | * LogSQLDelayedInserts config option removed | ||
| 55 | * Added DB generic LogSQLDBParam. Usage is LogSQLDBParam param value | ||
| 56 | example: LogSQLDBParam tcpport 12345 | ||
| 57 | * notes, cookes, headersin, headersout tables are no longer created unless they | ||
| 58 | are used. | ||
| 59 | |||
| 60 | 1.95: 2004-02-05 | ||
| 61 | * audit and update of extract_* functions to acheive same output as | ||
| 62 | mod_log_config. | ||
| 63 | |||
| 64 | 1.94: 2004-02-03 | ||
| 65 | * ssl loggin back in as separate module under 1.3 and 2.0 | ||
| 66 | * preparsing of format tags is now done to speed up main logging loop. | ||
| 67 | * fixed a nasty bug with pointer arithmetic (ick) that caused a segfault with | ||
| 68 | LogSQLWhichCookies | ||
| 69 | |||
| 70 | 1.93: 2004-01-20 | ||
| 71 | * Compiles for apache 1.3 AND 2.0 | ||
| 72 | * split apache version specific functions to seperate header files | ||
| 73 | * split apache version specific includes to seperate header files | ||
| 74 | * added wrapper defines for apache 1.3 | ||
| 75 | * updated configure m4 scripts to detect both apache versions at the same time | ||
| 76 | and assign defines as to which version was found. | ||
| 77 | * made install default to not activate module in configuration files. | ||
| 78 | use make activate instead | ||
| 79 | * split SSL logging to separate module (still needs to be finished) | ||
| 80 | |||
| 81 | 1.92: 2004-01-05 | ||
| 82 | * fixed compilation issue with mysql 4.x where mysql_error returns const char * | ||
| 83 | * Have SSL support compiling (though not really tested) | ||
| 84 | * updated configure macros to detect mod_ssl.h | ||
| 85 | * configure now uses --with-apxs instead of --with-apache | ||
| 86 | * Added documentation from 1.19b (needs to be update) | ||
| 87 | * Fixed an issue with dependencies in Makefile.in | ||
| 88 | |||
| 89 | 1.91: 2003-12-23 | ||
| 90 | * Added checks for MySQL to autoconf | ||
| 91 | * fixed merge code to work correctly. | ||
| 92 | * Merged in 1.19b1 changes | ||
| 93 | * Hostnames are now converted to lowercase in the mass-virtual naming section. | ||
| 94 | The loop that converts dots to underscores has been optimized as well. | ||
| 95 | * New directive LogSQLTableType allows one to specify the kind of table that | ||
| 96 | the module makes during table creation (e.g. InnoDB, MyISAM). Thanks to Jim | ||
| 97 | Turner for the suggestion and patch. If your MySQL server does not support | ||
| 98 | the specified type, it will create a MyISAM table instead. | ||
| 99 | * Directives can now be placed in the 'main' server config and will be | ||
| 100 | inherited by the virtual hosts. This means a LOT less repetition: you only | ||
| 101 | specify the item once to have it inherited, but it can still be overridden | ||
| 102 | on a virtualhost level. These can be specified in this manner: | ||
| 103 | |||
| 104 | LogSQLTransferLogTable | ||
| 105 | LogSQLTransferLogFormat | ||
| 106 | LogSQLPreserveFile | ||
| 107 | LogSQLNotesLogTable | ||
| 108 | LogSQLHeadersInLogTable | ||
| 109 | LogSQLHeadersOutLogTable | ||
| 110 | LogSQLCookieLogTable | ||
| 111 | LogSQLRemhostIgnore | ||
| 112 | LogSQLRequestIgnore | ||
| 113 | LogSQLRequestAccept | ||
| 114 | LogSQLWhichNotes | ||
| 115 | LogSQLWhichHeadersOut | ||
| 116 | LogSQLWhichHeadersIn | ||
| 117 | LogSQLWhichCookies | ||
| 118 | LogSQLWhichCookie | ||
| 119 | |||
| 120 | 1.90: 2003-12-22 | ||
| 121 | * updated code to compile under apache 2.0 | ||
| 122 | * rewrote and consolidate configuration handler routines | ||
| 123 | * made all functions static. | ||
| 124 | * made delayed insert configurable, instead of compile time | ||
| 125 | * moved to my autoconfigure support | ||
| 18 | 126 | ||
| 19 | 1.18: | 127 | 1.18: |
| 20 | * Delayed inserts (a MySQL extension) are now available at compile-time. | 128 | * Delayed inserts (a MySQL extension) are now available at compile-time. |
| @@ -351,5 +459,3 @@ to MySQL | |||
| 351 | * Segmentation fault in case of certain parameters lacking fixed. | 459 | * Segmentation fault in case of certain parameters lacking fixed. |
| 352 | * Worked around the SIGPIPE signal that's sent in certain events from | 460 | * Worked around the SIGPIPE signal that's sent in certain events from |
| 353 | * mysql_query(). Minor modifications | 461 | * mysql_query(). Minor modifications |
| 354 | |||
| 355 | |||
diff --git a/Documentation/README b/Documentation/README deleted file mode 100644 index 349b0ac..0000000 --- a/Documentation/README +++ /dev/null | |||
| @@ -1,13 +0,0 @@ | |||
| 1 | $Id: README,v 1.2 2002/12/10 20:37:29 helios Exp $ | ||
| 2 | |||
| 3 | The "original" document is the LyX file "documentation.lyx" -- all other | ||
| 4 | files here are derived from it. The most visually accurate document | ||
| 5 | and the one I'd recommend reading is the PostScript (.ps) file, although | ||
| 6 | a whole host of formats are made available here to suit your preference. | ||
| 7 | |||
| 8 | To read the HTML docs, open HTML/documentation.html in your browser. | ||
| 9 | To read the .dvi file, use Xdvi or kdvi. | ||
| 10 | To read the .ps file, use gv or kghostview. | ||
| 11 | To read the .lyx file, use LyX. | ||
| 12 | To read the .txt file, use less. | ||
| 13 | |||
| @@ -1,5 +1,20 @@ | |||
| 1 | $Id: INSTALL,v 1.12 2002/11/14 22:52:54 helios Exp $ | ||
| 2 | |||
| 3 | This document has been superseded by the new documentation in the | 1 | This document has been superseded by the new documentation in the |
| 4 | Documentation/ directory. There you will find the docs in a variety of | 2 | docs/ directory. There you will find the docs in a variety of |
| 5 | formats, including PostScript, plaintext, and HTML. | 3 | formats, including PostScript, plaintext, and HTML. |
| 4 | |||
| 5 | Basic overview is.. | ||
| 6 | |||
| 7 | ./configure --with-apxs=/path/to/apxs --enable-ssl | ||
| 8 | |||
| 9 | gmake | ||
| 10 | |||
| 11 | gmake install | ||
| 12 | |||
| 13 | |||
| 14 | edit httpd.conf and add the following. | ||
| 15 | |||
| 16 | LoadModule log_sql_module modules/mod_log_sql.so | ||
| 17 | LoadModule log_sql_mysql_module modules/mod_log_sql_mysql.so | ||
| 18 | <IfModule mod_ssl.c> | ||
| 19 | LoadModule log_sql_ssl_module modules/mod_log_sql_ssl.so | ||
| 20 | </IfModule> | ||
| @@ -1,7 +1,7 @@ | |||
| 1 | $Id: LICENSE,v 1.4 2002/06/27 20:09:17 helios Exp $ | ||
| 2 | 1 | ||
| 2 | Copyright (c) 2004-2006 Edward M. Rudd. All rights reserved. | ||
| 3 | Copyright (c) 2002 Christopher B. Powell. All rights reserved. | 3 | Copyright (c) 2002 Christopher B. Powell. All rights reserved. |
| 4 | Portions copyright (c) 2000 The Apache Software Foundation. | 4 | Portions copyright (c) 2000-2005 The Apache Software Foundation. |
| 5 | 5 | ||
| 6 | 6 | ||
| 7 | Preamble | 7 | Preamble |
| @@ -117,5 +117,3 @@ WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF | |||
| 117 | MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. | 117 | MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. |
| 118 | 118 | ||
| 119 | The End | 119 | The End |
| 120 | |||
| 121 | |||
diff --git a/Makefile b/Makefile deleted file mode 100644 index af38739..0000000 --- a/Makefile +++ /dev/null | |||
| @@ -1,112 +0,0 @@ | |||
| 1 | # $Id: Makefile,v 1.21 2002/12/18 22:56:37 helios Exp $ | ||
| 2 | |||
| 3 | ########################################################################### | ||
| 4 | # Important: | ||
| 5 | # Adjust these values as outlined in section "Installation" in the docs. | ||
| 6 | # Not all are needed at all times. | ||
| 7 | |||
| 8 | APACHESOURCE = /usr/local/src/apache_1.3.27-dso | ||
| 9 | APACHEINSTALLED = /usr/local/Apache | ||
| 10 | APACHEHEADERS = /usr/local/Apache/include | ||
| 11 | APXS = $(APACHEINSTALLED)/bin/apxs | ||
| 12 | |||
| 13 | MYSQLLIBRARIES = /usr/lib | ||
| 14 | MYSQLHEADERS = /usr/include/mysql | ||
| 15 | |||
| 16 | #MODSSLHEADERS = /usr/local/src/apache_1.3.27-dso/src/modules/ssl | ||
| 17 | #DB1HEADERS = /usr/include/db1 | ||
| 18 | |||
| 19 | ########################################################################### | ||
| 20 | # Don't uncomment this without reading the "Optimizing for a busy database" | ||
| 21 | # section in the documentation (under "Advanced logging scenarios"). | ||
| 22 | |||
| 23 | #MYSQLDELAYED = -DWANT_DELAYED_MYSQL_INSERT | ||
| 24 | |||
| 25 | ########################################################################### | ||
| 26 | # Rarely if ever have to touch below here. | ||
| 27 | |||
| 28 | MLMVERS = 1.18 | ||
| 29 | #APXSGDB = -Wc,-g | ||
| 30 | APXSOPTS = -Wc,-O2 -Wc,-Wall | ||
| 31 | STATOPTS = -fpic -O2 -Wall | ||
| 32 | CC = gcc | ||
| 33 | INSTALL = /usr/bin/install -m 664 | ||
| 34 | RM = /bin/rm | ||
| 35 | LYX = /usr/bin/lyx | ||
| 36 | LATEX = /usr/bin/latex | ||
| 37 | DVIPS = /usr/bin/dvips | ||
| 38 | LINKS = /usr/bin/lynx | ||
| 39 | L2H = /usr/bin/latex2html | ||
| 40 | WEBSERV = gw0.corp | ||
| 41 | |||
| 42 | STATFLAGS = -I$(APACHEHEADERS) | ||
| 43 | SOFLAGS = -L$(MYSQLLIBRARIES) -lmysqlclient -lz | ||
| 44 | ifdef MODSSLHEADERS | ||
| 45 | FLAGS = -DEAPI -I$(MYSQLHEADERS) $(MYSQLDELAYED) -I$(MODSSLHEADERS) -I$(DB1HEADERS) -DWANT_SSL_LOGGING | ||
| 46 | else | ||
| 47 | FLAGS = -DEAPI -I$(MYSQLHEADERS) $(MYSQLDELAYED) | ||
| 48 | endif | ||
| 49 | |||
| 50 | all: | ||
| 51 | @echo "You can choose to make mod_log_sql as a static or dynamic module." | ||
| 52 | @echo "Either 'make dso' or 'make static'." | ||
| 53 | @echo | ||
| 54 | @echo "Please first read the documentation carefully!" | ||
| 55 | |||
| 56 | dso: mod_log_sql.so | ||
| 57 | |||
| 58 | static: mod_log_sql.o | ||
| 59 | |||
| 60 | mod_log_sql.so: mod_log_sql.c Makefile | ||
| 61 | $(APXS) -c $(APXSGDB) $(APXSOPTS) $(FLAGS) $(SOFLAGS) mod_log_sql.c | ||
| 62 | |||
| 63 | mod_log_sql.o: mod_log_sql.c Makefile | ||
| 64 | $(CC) $(STATOPTS) $(FLAGS) $(STATFLAGS) -c mod_log_sql.c | ||
| 65 | |||
| 66 | dsoinstall: dso | ||
| 67 | $(APXS) -i mod_log_sql.so | ||
| 68 | |||
| 69 | statinstall: static | ||
| 70 | $(INSTALL) -d -m 755 $(APACHESOURCE)/src/modules/sql | ||
| 71 | $(INSTALL) mod_log_sql.c $(APACHESOURCE)/src/modules/sql/mod_log_sql.c | ||
| 72 | $(INSTALL) Makefile $(APACHESOURCE)/src/modules/sql/Makefile | ||
| 73 | $(INSTALL) mod_log_sql.o $(APACHESOURCE)/src/modules/sql/mod_log_sql.o | ||
| 74 | |||
| 75 | clean: | ||
| 76 | $(RM) -rf *.o *.so | ||
| 77 | $(RM) -f Documentation/HTML/*.html Documentation/HTML/*.css Documentation/HTML/*.png | ||
| 78 | $(RM) -f Documentation/*.tex | ||
| 79 | $(RM) -f Documentation/*.dvi | ||
| 80 | $(RM) -f Documentation/*.ps | ||
| 81 | $(RM) -f Documentation/*.txt | ||
| 82 | |||
| 83 | distro: documentation | ||
| 84 | @scp CHANGELOG $(WEBSERV):$(APACHEINSTALLED)/html/mod_log_sql/docs | ||
| 85 | @scp Documentation/*.ps $(WEBSERV):$(APACHEINSTALLED)/html/mod_log_sql/docs | ||
| 86 | @scp Documentation/HTML/*.html $(WEBSERV):$(APACHEINSTALLED)/html/mod_log_sql/docs/ | ||
| 87 | @scp Documentation/HTML/*.png $(WEBSERV):$(APACHEINSTALLED)/html/mod_log_sql/docs/ | ||
| 88 | @cd ..; tar jcf mod_log_sql-$(MLMVERS).tar.bz2 --exclude mod_log_sql/CVS --exclude mod_log_sql/Documentation/CVS --exclude mod_log_sql/Documentation/HTML/CVS --exclude ".directory" mod_log_sql/; scp mod_log_sql-$(MLMVERS).tar.bz2 $(WEBSERV):$(APACHEINSTALLED)/html/mod_log_sql/; rm -f mod_log_sql-$(MLMVERS).tar.bz2 | ||
| 89 | @ssh $(WEBSERV) "ln -sf mod_log_sql-$(MLMVERS).tar.bz2 $(APACHEINSTALLED)/html/mod_log_sql/mod_log_sql.tar.bz2" | ||
| 90 | |||
| 91 | pre-distro: documentation | ||
| 92 | @cd ..; tar jcf mod_log_sql-$(MLMVERS).tar.bz2 --exclude mod_log_sql/CVS --exclude mod_log_sql/Documentation/CVS --exclude mod_log_sql/Documentation/HTML/CVS --exclude ".directory" mod_log_sql/; scp mod_log_sql-$(MLMVERS).tar.bz2 $(WEBSERV):$(APACHEINSTALLED)/html/mod_log_sql/; rm -f mod_log_sql-$(MLMVERS).tar.bz2 | ||
| 93 | |||
| 94 | documentation: Documentation/documentation.lyx | ||
| 95 | @echo "Creating LaTeX docs..." | ||
| 96 | @$(LYX) --export latex Documentation/documentation.lyx 2>/dev/null | ||
| 97 | @echo "Creating cross-references...run 1" | ||
| 98 | @cd Documentation ; $(LATEX) documentation.tex >/dev/null 2>&1 | ||
| 99 | @echo "Creating cross-references...run 2" | ||
| 100 | @cd Documentation ; $(LATEX) documentation.tex >/dev/null 2>&1 | ||
| 101 | @echo "Creating cross-references...run 3" | ||
| 102 | @cd Documentation ; $(LATEX) documentation.tex >/dev/null 2>&1 | ||
| 103 | @echo "Creating PostScript docs..." | ||
| 104 | @$(DVIPS) Documentation/documentation.dvi -o Documentation/documentation.ps 2>/dev/null | ||
| 105 | @echo "Creating HTML docs..." | ||
| 106 | @$(L2H) -local_icons -show_section_numbers -split 4 -navigation -noindex_in_navigation -contents_in_navigation -dir Documentation/HTML Documentation/documentation.tex >/dev/null 2>&1 | ||
| 107 | @echo "Creating plain text docs..." | ||
| 108 | @$(L2H) -show_section_numbers -split 0 -dir Documentation/ Documentation/documentation.tex >/dev/null 2>&1 | ||
| 109 | @$(LINKS) -dump -nolist -width=120 -dump Documentation/documentation.html > Documentation/documentation.txt 2>/dev/null | ||
| 110 | @echo "Cleaning up..." | ||
| 111 | @$(RM) -f Documentation/*.html Documentation/WARNINGS Documentation/*.pl Documentation/*.aux Documentation/*.css Documentation/*.toc Documentation/*.log Documentation/*.old Documentation/*.png Documentation/images.tex | ||
| 112 | @$(RM) -f Documentation/HTML/WARNINGS Documentation/HTML/*.pl Documentation/HTML/*.log Documentation/HTML/*.aux Documentation/HTML/*.tex Documentation/HTML/*.old Documentation/HTML/index.html | ||
diff --git a/Makefile.in b/Makefile.in new file mode 100644 index 0000000..4c60869 --- /dev/null +++ b/Makefile.in | |||
| @@ -0,0 +1,122 @@ | |||
| 1 | # @configure_input@ | ||
| 2 | |||
| 3 | # Modify these top variables. | ||
| 4 | SUBDIRS = docs contrib src utility | ||
| 5 | |||
| 6 | EXTRA_DIST = AUTHORS INSTALL TODO LICENSE CHANGELOG \ | ||
| 7 | build-apache13.bat build-apache2.bat \ | ||
| 8 | |||
| 9 | #Don't modify anything below here | ||
| 10 | |||
| 11 | top_srcdir = @top_srcdir@ | ||
| 12 | srcdir = @abs_srcdir@ | ||
| 13 | builddir = @abs_builddir@ | ||
| 14 | |||
| 15 | STD_DIST = install-sh \ | ||
| 16 | config.sub \ | ||
| 17 | config.guess \ | ||
| 18 | aclocal.m4 \ | ||
| 19 | Makefile.in \ | ||
| 20 | configure.ac \ | ||
| 21 | configure \ | ||
| 22 | stamp-h.in \ | ||
| 23 | include/config.h.in | ||
| 24 | |||
| 25 | DISTFILES = $(STD_DIST) $(EXTRA_DIST) | ||
| 26 | |||
| 27 | all: all-subdirs | ||
| 28 | |||
| 29 | all-subdirs install-subdirs activate-subdirs clean-subdirs distclean-subdirs: | ||
| 30 | @otarget=`echo $@|sed s/-subdirs//`; \ | ||
| 31 | list=' $(SUBDIRS)'; \ | ||
| 32 | for i in $$list; do \ | ||
| 33 | if test -d "$$i"; then \ | ||
| 34 | target="$$otarget"; \ | ||
| 35 | echo "Making $$target in $$i"; \ | ||
| 36 | if test "$$i" = "."; then \ | ||
| 37 | made_local=yes; \ | ||
| 38 | target="local-$$target"; \ | ||
| 39 | fi; \ | ||
| 40 | (cd $$i && $(MAKE) $$target) || exit 1; \ | ||
| 41 | fi; \ | ||
| 42 | done; | ||
| 43 | |||
| 44 | TODO: TODO.in | ||
| 45 | @./m4/scripts/gen_todo.pl | ||
| 46 | |||
| 47 | install: install-subdirs | ||
| 48 | echo "*************************************************************************"; \ | ||
| 49 | echo "*** The mod_log_sql modules have been installed."; \ | ||
| 50 | echo "*** Please edit your Apache configuration files and"; \ | ||
| 51 | echo "*** add the appropriate LoadModule directives per the documentation"; \ | ||
| 52 | echo "*** in docs/manual.html"; \ | ||
| 53 | echo "*** If you have previously used 1.18 or lower then you must change"; \ | ||
| 54 | echo "*** >LoadModule sql_log_module modules/mod_log_sql.so"; \ | ||
| 55 | echo "*** to"; \ | ||
| 56 | echo "*** >LoadModule log_sql_module modules/mod_log_sql.so"; \ | ||
| 57 | echo "*** in your httpd.conf as the internal name of the module has changed."; \ | ||
| 58 | echo "*** "; \ | ||
| 59 | echo "*** Also read the documentation about using SSL support and new "; \ | ||
| 60 | echo "*** configuration directives."; \ | ||
| 61 | echo "*************************************************************************"; | ||
| 62 | |||
| 63 | activate: activate-subdirs | ||
| 64 | |||
| 65 | clean: clean-subdirs | ||
| 66 | |||
| 67 | distclean: clean distclean-subdirs | ||
| 68 | $(RM) config.status config.log include/config.h include/config.h.in \ | ||
| 69 | configure stamp-h stamp-h.in Makefile aclocal.m4 | ||
| 70 | $(RM) -r autom4te-2.53.cache | ||
| 71 | |||
| 72 | DESTDIR = @PACKAGE_NAME@-@PACKAGE_VERSION@ | ||
| 73 | DESTTGZ = $(DESTDIR).tar.gz | ||
| 74 | dist: | ||
| 75 | @rm -rf $(DESTDIR); \ | ||
| 76 | list=' $(SUBDIRS)'; \ | ||
| 77 | for i in $$list; do \ | ||
| 78 | if test -d "$$i"; then \ | ||
| 79 | target=local-dist; \ | ||
| 80 | echo "Making $$target in $$i"; \ | ||
| 81 | if test "$$i" = "."; then \ | ||
| 82 | made_local=yes; \ | ||
| 83 | target="local-dist"; \ | ||
| 84 | fi; \ | ||
| 85 | NEWDESTDIR=$(builddir)/$(DESTDIR)/$$i; \ | ||
| 86 | echo $(NEWDESTDIR); \ | ||
| 87 | (cd $$i && $(MAKE) DESTDIR=$(builddir)/$(DESTDIR)/$$i $$target) || exit 1; \ | ||
| 88 | fi; \ | ||
| 89 | done; \ | ||
| 90 | if test "$$made_local" != "yes"; then \ | ||
| 91 | $(MAKE) "local-dist" || exit 1; \ | ||
| 92 | fi; \ | ||
| 93 | tar -zcf $(DESTTGZ) $(DESTDIR); \ | ||
| 94 | rm -rf $(DESTDIR); \ | ||
| 95 | |||
| 96 | local-dist: $(DISTFILES) | ||
| 97 | mkdir -p $(DESTDIR) | ||
| 98 | cp -dp --parents $(DISTFILES) $(DESTDIR) | ||
| 99 | |||
| 100 | .PHONY: include all-subdirs activate-subdirs install-subdirs \ | ||
| 101 | clean-subdirs distclean-subdirs dist | ||
| 102 | |||
| 103 | # Regenerate makefiles | ||
| 104 | # autoheader might not change config.h.in, so touch a stamp file. | ||
| 105 | $(srcdir)/config.h.in: stamp-h.in | ||
| 106 | $(srcdir)/stamp-h.in: configure.ac aclocal.m4 | ||
| 107 | cd $(srcdir) && autoheader-2.53 | ||
| 108 | echo timestamp > $(srcdir)/stamp-h.in | ||
| 109 | |||
| 110 | config.h: stamp-h | ||
| 111 | |||
| 112 | stamp-h: config.h.in config.status | ||
| 113 | ./config.status | ||
| 114 | |||
| 115 | $(srcdir)/configure: configure.ac aclocal.m4 | ||
| 116 | cd $(srcdir) && autoconf | ||
| 117 | |||
| 118 | Makefile: Makefile.in config.status | ||
| 119 | ./config.status | ||
| 120 | |||
| 121 | config.status: configure | ||
| 122 | ./config.status --recheck | ||
| @@ -0,0 +1,18 @@ | |||
| 1 | TODO: | ||
| 2 | * restructure to be more friendly toward DBD pooling | ||
| 3 | * validate table names before trying to log them. | ||
| 4 | * write alternate DB driver (PostgreSQL, libDBI, mod_*_pool) | ||
| 5 | * look at forcing table name to ServerName instead of on of the names in | ||
| 6 | ServerAlias? | ||
| 7 | * LogSQLRotateLogs directive with daily/monthly/weekly/etc. | ||
| 8 | * socket-based middleman daemon with configurable conns, or connect/disconnect. | ||
| 9 | * DBI connection pooling. | ||
| 10 | * ignore by cookie | ||
| 11 | * investigate thread safety issues | ||
| 12 | Use libmysqlclient_r for threaded MPM (or always?) | ||
| 13 | Add thread locks if using standard mysqlclient | ||
| 14 | Check locking issues with the preserve file | ||
| 15 | * rewrite main core logging function to optimize for speed. | ||
| 16 | * Clean up table creation code. | ||
| 17 | support DB independent way of defining the tables | ||
| 18 | ----- | ||
diff --git a/autogen.sh b/autogen.sh new file mode 100755 index 0000000..869d535 --- /dev/null +++ b/autogen.sh | |||
| @@ -0,0 +1,17 @@ | |||
| 1 | #!/bin/sh | ||
| 2 | |||
| 3 | if [ -z $ACLOCAL ]; then | ||
| 4 | ACLOCAL=aclocal | ||
| 5 | fi | ||
| 6 | if [ -z $AUTOCONF ]; then | ||
| 7 | AUTOCONF=autoconf | ||
| 8 | fi | ||
| 9 | if [ -z $AUTOHEADER ]; then | ||
| 10 | AUTOHEADER=autoheader | ||
| 11 | fi | ||
| 12 | rm -rf autom4te.cache | ||
| 13 | $ACLOCAL -I m4 | ||
| 14 | $AUTOHEADER | ||
| 15 | libtoolize -c --force | ||
| 16 | $AUTOCONF | ||
| 17 | touch stamp-h.in | ||
diff --git a/build-apache13.bat b/build-apache13.bat new file mode 100644 index 0000000..77461ee --- /dev/null +++ b/build-apache13.bat | |||
| @@ -0,0 +1,47 @@ | |||
| 1 | @echo off | ||
| 2 | cls | ||
| 3 | rem path to Microsoft SDK installation | ||
| 4 | SET DIR_MSSDK=C:\Program Files\Microsoft SDK | ||
| 5 | rem path to apache2 installation | ||
| 6 | SET DIR_APACHE=C:\Program Files\Apache Group\Apache | ||
| 7 | rem path to mysql 4.0 installation | ||
| 8 | SET DIR_MYSQL=C:\MySQL | ||
| 9 | rem Can be set to opt or debug | ||
| 10 | SET LIB_MYSQL=opt | ||
| 11 | |||
| 12 | copy /Y winconfig.h config.h | ||
| 13 | mkdir Release | ||
| 14 | cd Release | ||
| 15 | Rem Compile all the source code | ||
| 16 | echo /MD /W3 /Zi /Ze /O2 > RESP_c.txt | ||
| 17 | echo /DNDEBUG /D_WINDOWS /DWIN32 >> RESP_c.txt | ||
| 18 | echo /Fd"mod_log_sql" /FD >> RESP_c.txt | ||
| 19 | echo /DHAVE_CONFIG_H /DWITH_APACHE13 /DLOGSQL_DECLARE_EXPORT >> RESP_c.txt | ||
| 20 | echo /DSHARED_MODULE >> RESP_c.txt | ||
| 21 | echo /I.. >> RESP_c.txt | ||
| 22 | echo /I"%DIR_MSSDK%\Include" >> RESP_c.txt | ||
| 23 | echo /I"%DIR_APACHE%\Include" >> RESP_c.txt | ||
| 24 | echo /I"%DIR_MYSQL%\Include" >> RESP_c.txt | ||
| 25 | cl @RESP_c.txt /c ..\mod_log_sql.c ..\mod_log_sql_mysql.c | ||
| 26 | |||
| 27 | rem link main module | ||
| 28 | echo /MACHINE:I386 /SUBSYSTEM:windows > RESP_l.txt | ||
| 29 | echo /OUT:mod_log_sql.so /DLL /OPT:REF /DEBUG >> RESP_l.txt | ||
| 30 | echo /LIBPATH:"%DIR_APACHE%\lib" >> RESP_l.txt | ||
| 31 | echo /LIBPATH:"%DIR_APACHE%\libexec" >> RESP_l.txt | ||
| 32 | echo /LIBPATH:"%DIR_MSSDK%\lib" >> RESP_l.txt | ||
| 33 | echo ApacheCore.lib >> RESP_l.txt | ||
| 34 | link @RESP_l.txt mod_log_sql.obj | ||
| 35 | |||
| 36 | rem link mysql module | ||
| 37 | echo /MACHINE:I386 /SUBSYSTEM:windows > RESP_l.txt | ||
| 38 | echo /OUT:mod_log_sql_mysql.so /DLL /OPT:REF /DEBUG >> RESP_l.txt | ||
| 39 | echo /LIBPATH:"%DIR_APACHE%\lib" >> RESP_l.txt | ||
| 40 | echo /LIBPATH:"%DIR_APACHE%\libexec" >> RESP_l.txt | ||
| 41 | echo /LIBPATH:"%DIR_MYSQL%\lib\%LIB_MYSQL%" >> RESP_l.txt | ||
| 42 | echo /LIBPATH:"%DIR_MSSDK%\lib" >> RESP_l.txt | ||
| 43 | echo /NODEFAULTLIB:LIBCMT.lib >> RESP_l.txt | ||
| 44 | echo ApacheCore.lib >> RESP_l.txt | ||
| 45 | echo libmysql.lib mod_log_sql.lib >> RESP_l.txt | ||
| 46 | link @RESP_l.txt mod_log_sql_mysql.obj | ||
| 47 | cd .. | ||
diff --git a/build-apache2.bat b/build-apache2.bat new file mode 100644 index 0000000..c18fa23 --- /dev/null +++ b/build-apache2.bat | |||
| @@ -0,0 +1,62 @@ | |||
| 1 | @echo off | ||
| 2 | rem path to Microsoft SDK installation | ||
| 3 | SET DIR_MSSDK=C:\Program Files\Microsoft SDK | ||
| 4 | rem path to apache2 installation | ||
| 5 | SET DIR_APACHE=C:\Program Files\Apache Group\Apache2 | ||
| 6 | rem path to mysql 4.0 installation | ||
| 7 | SET DIR_MYSQL=C:\MySQL | ||
| 8 | rem Can be set to opt or debug | ||
| 9 | SET LIB_MYSQL=opt | ||
| 10 | rem path to OpenSSL installation | ||
| 11 | SET DIR_OPENSSL=C:\OpenSSL | ||
| 12 | rem Should be set to VC | ||
| 13 | SET LIB_OPENSSL=VC | ||
| 14 | |||
| 15 | copy /Y winconfig.h config.h | ||
| 16 | mkdir Release | ||
| 17 | cd Release | ||
| 18 | Rem Compile all the source code | ||
| 19 | echo /MD /W3 /Zi /O2 /DNDEBUG /D_WINDOWS /DWIN32 > RESP_c.txt | ||
| 20 | echo /Fd"mod_log_sql" /FD >> RESP_c.txt | ||
| 21 | echo /DHAVE_CONFIG_H /DWITH_APACHE20 /DLOGSQL_DECLARE_EXPORT >> RESP_c.txt | ||
| 22 | echo /I.. >> RESP_c.txt | ||
| 23 | echo /I"%DIR_MSSDK%\Include" >> RESP_c.txt | ||
| 24 | echo /I"%DIR_APACHE%\Include" >> RESP_c.txt | ||
| 25 | echo /I"%DIR_MYSQL%\Include" >> RESP_c.txt | ||
| 26 | echo /I"%DIR_OPENSSL%\Include" >> RESP_c.txt | ||
| 27 | echo /I"%DIR_OPENSSL%\Include\openssl" >> RESP_c.txt | ||
| 28 | cl @RESP_c.txt /c ..\mod_log_sql.c ..\mod_log_sql_mysql.c | ||
| 29 | |||
| 30 | if not exist "%DIR_APACHE%\Include\mod_ssl.h" goto nossl | ||
| 31 | cl @RESP_C.txt /c ..\mod_log_sql_ssl.c | ||
| 32 | :nossl | ||
| 33 | |||
| 34 | rem link main module | ||
| 35 | echo /MACHINE:I386 /SUBSYSTEM:windows > RESP_l.txt | ||
| 36 | echo /OUT:mod_log_sql.so /DLL /OPT:REF /DEBUG >> RESP_l.txt | ||
| 37 | echo /LIBPATH:"%DIR_APACHE%\lib" >> RESP_l.txt | ||
| 38 | echo libapr.lib libaprutil.lib libhttpd.lib >> RESP_l.txt | ||
| 39 | link @RESP_l.txt mod_log_sql.obj | ||
| 40 | |||
| 41 | rem link mysql module | ||
| 42 | echo /MACHINE:I386 /SUBSYSTEM:windows > RESP_l.txt | ||
| 43 | echo /OUT:mod_log_sql_mysql.so /DLL /OPT:REF /DEBUG >> RESP_l.txt | ||
| 44 | echo /LIBPATH:"%DIR_APACHE%\lib" >> RESP_l.txt | ||
| 45 | echo /LIBPATH:"%DIR_MYSQL%\lib\%LIB_MYSQL%" >> RESP_l.txt | ||
| 46 | echo /NODEFAULTLIB:LIBCMT.lib >> RESP_l.txt | ||
| 47 | echo libapr.lib libaprutil.lib libhttpd.lib >> RESP_l.txt | ||
| 48 | echo libmysql.lib mod_log_sql.lib >> RESP_l.txt | ||
| 49 | link @RESP_l.txt mod_log_sql_mysql.obj | ||
| 50 | |||
| 51 | if not exist "%DIR_APACHE%\Include\mod_ssl.h" goto done | ||
| 52 | rem link ssl module | ||
| 53 | echo /MACHINE:I386 /SUBSYSTEM:windows > RESP_l.txt | ||
| 54 | echo /OUT:mod_log_sql_ssl.so /DLL /OPT:REF /DEBUG >> RESP_l.txt | ||
| 55 | echo /LIBPATH:"%DIR_APACHE%\lib" >> RESP_l.txt | ||
| 56 | echo /LIBPATH:"%DIR_OPENSSL%\lib\%LIB_OPENSSL%" >> RESP_l.txt | ||
| 57 | echo libapr.lib libaprutil.lib libhttpd.lib >> RESP_l.txt | ||
| 58 | echo mod_log_sql.lib >> RESP_l.txt | ||
| 59 | link @RESP_l.txt mod_log_sql_ssl.obj | ||
| 60 | |||
| 61 | :done | ||
| 62 | cd .. | ||
diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..395398f --- /dev/null +++ b/configure.ac | |||
| @@ -0,0 +1,127 @@ | |||
| 1 | dnl Required initializer | ||
| 2 | AC_INIT([mod_log_sql],[1.102]) | ||
| 3 | OOO_CONFIG_NICE(config.nice) | ||
| 4 | AC_PREREQ(2.59) | ||
| 5 | AC_CONFIG_HEADERS(include/autoconfig.h) | ||
| 6 | AC_CONFIG_SRCDIR(src/mod_log_sql.c) | ||
| 7 | |||
| 8 | OOO_MAINTAIN_MODE | ||
| 9 | |||
| 10 | dnl Add a test for a compiler. | ||
| 11 | AC_PROG_CC | ||
| 12 | AC_PROG_LIBTOOL | ||
| 13 | |||
| 14 | PKG_CHECK_MODULES(PCRE,libpcre) | ||
| 15 | |||
| 16 | APACHE20_VERSION=2.0.40 | ||
| 17 | APACHE13_VERSION=1.3.20 | ||
| 18 | CHECK_APACHE($APACHE13_VERSION,$APACHE20_VERSION, | ||
| 19 | :, | ||
| 20 | :, | ||
| 21 | AC_MSG_ERROR([*** The correct version Apache was not found!]) | ||
| 22 | AC_MSG_ERROR([*** You need either Apache 1.3 version $APACHE13_VERSION or greater]) | ||
| 23 | AC_MSG_ERROR([*** or Apache 2.0/2.2 version $APACHE20_VERSION or greater!]) | ||
| 24 | ) | ||
| 25 | |||
| 26 | if test $AP_VERSION = "2.0"; then | ||
| 27 | WANT_LOGIO_MOD=1 | ||
| 28 | else | ||
| 29 | WANT_LOGIO_MOD=0 | ||
| 30 | fi | ||
| 31 | |||
| 32 | AC_SUBST(WANT_LOGIO_MOD) | ||
| 33 | |||
| 34 | CHECK_APU_HEADERS([apr_dbd.h], | ||
| 35 | WANT_DBD_MOD=1, | ||
| 36 | WANT_DBD_MOD=0) | ||
| 37 | |||
| 38 | AC_SUBST(WANT_DBD_MOD) | ||
| 39 | CHECK_APU_FUNCS(apr_dbd_transaction_mode_get) | ||
| 40 | |||
| 41 | CHECK_MYSQL( | ||
| 42 | WANT_MYSQL_MOD=1, | ||
| 43 | AC_MSG_WARN([*** Mysql client libraries not found!]) | ||
| 44 | WANT_MYSQL_MOD=0 | ||
| 45 | ) | ||
| 46 | |||
| 47 | AC_SUBST(WANT_MYSQL_MOD) | ||
| 48 | |||
| 49 | CHECK_LIBDBI(WANT_DBI_MOD=1, | ||
| 50 | AC_MSG_WARN([** libDBI client libraries not found!]) | ||
| 51 | WANT_DBI_MOD=0 | ||
| 52 | ) | ||
| 53 | AC_SUBST(WANT_DBI_MOD) | ||
| 54 | |||
| 55 | dnl to write, checking for pgsql libs | ||
| 56 | WANT_PGSQL_MOD=0 | ||
| 57 | AC_SUBST(WANT_PGSQL_MOD) | ||
| 58 | |||
| 59 | |||
| 60 | CHECK_MOD_SSL( | ||
| 61 | WANT_SSL_MOD=1, | ||
| 62 | AC_MSG_WARN([** mod_ssl.h not found or missing SSL headers!]) | ||
| 63 | WANT_SSL_MOD=0 | ||
| 64 | ) | ||
| 65 | |||
| 66 | AC_SUBST(WANT_SSL_MOD) | ||
| 67 | |||
| 68 | case "$target" in | ||
| 69 | *-*-solaris* | *-*-osf* ) | ||
| 70 | if test $AP_VERSION = "1.3"; then | ||
| 71 | RT_LIBS=-lrt | ||
| 72 | fi | ||
| 73 | ;; | ||
| 74 | *) | ||
| 75 | RT_LIBS="" | ||
| 76 | ;; | ||
| 77 | esac | ||
| 78 | |||
| 79 | AC_SUBST(RT_LIBS) | ||
| 80 | |||
| 81 | |||
| 82 | AC_CHECK_HEADERS(limits.h) | ||
| 83 | |||
| 84 | AC_CONFIG_FILES([stamp-h], [echo timestamp > stamp-h]) | ||
| 85 | |||
| 86 | dnl Write config.status and the Makefile | ||
| 87 | |||
| 88 | AC_CONFIG_FILES([Makefile | ||
| 89 | src/Makefile | ||
| 90 | docs/Makefile | ||
| 91 | utility/Makefile | ||
| 92 | contrib/Makefile]) | ||
| 93 | AC_OUTPUT | ||
| 94 | |||
| 95 | |||
| 96 | AC_MSG_RESULT([------------------------------------]) | ||
| 97 | AC_MSG_RESULT([Apache version : $AP_VERSION]) | ||
| 98 | if test $WANT_SSL_MOD -eq 1; then | ||
| 99 | AC_MSG_RESULT([SSL Support : yes]) | ||
| 100 | else | ||
| 101 | AC_MSG_RESULT([SSL Support : no]) | ||
| 102 | AC_MSG_RESULT([*** Make sure OpenSSL headers, and mod_ssl.h are installed.]) | ||
| 103 | fi | ||
| 104 | if test $WANT_LOGIO_MOD -eq 1; then | ||
| 105 | AC_MSG_RESULT([LogIO Module : yes]) | ||
| 106 | else | ||
| 107 | AC_MSG_RESULT([LogIO Module : no]) | ||
| 108 | AC_MSG_RESULT([*** Logio does not work with Apache 1.3.]) | ||
| 109 | fi | ||
| 110 | |||
| 111 | AC_MSG_RESULT([Enabled drivers :]) | ||
| 112 | if test $WANT_MYSQL_MOD -eq 1; then | ||
| 113 | AC_MSG_RESULT([ MySQL Driver]) | ||
| 114 | fi | ||
| 115 | if test $WANT_PGSQL_MOD -eq 1; then | ||
| 116 | AC_MSG_RESULT([ PostgreSQL Driver]) | ||
| 117 | fi | ||
| 118 | if test $WANT_DBI_MOD -eq 1; then | ||
| 119 | AC_MSG_RESULT([ libDBI Driver]) | ||
| 120 | fi | ||
| 121 | if test $WANT_DBD_MOD -eq 1; then | ||
| 122 | AC_MSG_RESULT([ APR DBD Driver]) | ||
| 123 | fi | ||
| 124 | if test $OOO_MAINTAIN -eq 1; then | ||
| 125 | AC_MSG_RESULT([Maintainer mode is on. -Werror is in effect]) | ||
| 126 | fi | ||
| 127 | AC_MSG_RESULT([------------------------------------]) | ||
diff --git a/contrib/.cvsignore b/contrib/.cvsignore new file mode 100644 index 0000000..f3c7a7c --- /dev/null +++ b/contrib/.cvsignore | |||
| @@ -0,0 +1 @@ | |||
| Makefile | |||
diff --git a/contrib/Makefile.in b/contrib/Makefile.in new file mode 100644 index 0000000..c2bd203 --- /dev/null +++ b/contrib/Makefile.in | |||
| @@ -0,0 +1,36 @@ | |||
| 1 | # @configure_input@ | ||
| 2 | |||
| 3 | # Modify these top variables. | ||
| 4 | SUBDIRS = | ||
| 5 | |||
| 6 | EXTRA_DIST = README \ | ||
| 7 | create_tables.sql \ | ||
| 8 | make_combined_log.pl \ | ||
| 9 | mysql_import_combined_log.pl | ||
| 10 | |||
| 11 | #Don't modify anything below here | ||
| 12 | |||
| 13 | srcdir = @abs_srcdir@ | ||
| 14 | builddir = @abs_builddir@ | ||
| 15 | |||
| 16 | STD_DIST = Makefile.in | ||
| 17 | |||
| 18 | DISTFILES = $(STD_DIST) $(EXTRA_DIST) | ||
| 19 | |||
| 20 | all: | ||
| 21 | |||
| 22 | install: | ||
| 23 | |||
| 24 | update: | ||
| 25 | |||
| 26 | clean: | ||
| 27 | |||
| 28 | distclean: clean | ||
| 29 | $(RM) Makefile | ||
| 30 | |||
| 31 | local-dist: $(DISTFILES) | ||
| 32 | mkdir -p $(DESTDIR) | ||
| 33 | cp -dp --parents $(DISTFILES) $(DESTDIR) | ||
| 34 | |||
| 35 | .PHONY: include all-subdirs update-subdirs install-subdirs \ | ||
| 36 | clean-subdirs distclean-subdirs dist | ||
diff --git a/contrib/README b/contrib/README new file mode 100644 index 0000000..bf0e65f --- /dev/null +++ b/contrib/README | |||
| @@ -0,0 +1,38 @@ | |||
| 1 | This directory contains contributed scripts/programs/utilites for mod_log_sql. | ||
| 2 | |||
| 3 | * create_tables.sql | ||
| 4 | |||
| 5 | This is the create table SQL commands to create the access, headers_in, | ||
| 6 | headers_out, cookies, and notes tables in the MySQL database. | ||
| 7 | Use it like this. | ||
| 8 | mysql -u user -h host -p apachelogdatabase < create_tables.sql | ||
| 9 | Where: | ||
| 10 | user is the username to log in as, | ||
| 11 | host is the hostname the server is on, | ||
| 12 | apachelogdatabase is the database to put the tables into | ||
| 13 | -p will have mysql ask you for a password for the user | ||
| 14 | |||
| 15 | *make_combined_log.pl | ||
| 16 | |||
| 17 | This perl script will extract the data from mod_log_sql's tables in the | ||
| 18 | database and export a standard Apache combined log file. Use this to run | ||
| 19 | logs through a program like webalizer. | ||
| 20 | |||
| 21 | You must edit the perl script to configure variables before you run it. | ||
| 22 | Usage: | ||
| 23 | ./make_combined_log.pl days virtualhost | ||
| 24 | Where: | ||
| 25 | days is the number of days to fetch (starting from now and going back | ||
| 26 | in time) | ||
| 27 | virtualhost is the name of the virtualhost to retrieve | ||
| 28 | |||
| 29 | Example: | ||
| 30 | ./make_combined_log.pl 2 example.com | ||
| 31 | |||
| 32 | *mysql_import_combined_log.pl | ||
| 33 | |||
| 34 | This is a perl script written by Aaron Jenson that imports a combined log file | ||
| 35 | from apache into a SQL database table.. You can use this script to import logs | ||
| 36 | from a webserver you are converting over from the standard Apache log system to | ||
| 37 | mod_log_sql. A Usage statement can be fetch by running the program with no | ||
| 38 | parameters or with --help or -?. | ||
diff --git a/create_tables.sql b/contrib/create_tables.sql index dbcfbe4..0fe0f4b 100644 --- a/create_tables.sql +++ b/contrib/create_tables.sql | |||
| @@ -15,7 +15,7 @@ create table access_log ( | |||
| 15 | request_method varchar(10) , | 15 | request_method varchar(10) , |
| 16 | request_protocol varchar(10) , | 16 | request_protocol varchar(10) , |
| 17 | request_time char(28), | 17 | request_time char(28), |
| 18 | request_uri varchar(50), | 18 | request_uri varchar(255), |
| 19 | request_args varchar(255), | 19 | request_args varchar(255), |
| 20 | server_port smallint unsigned, | 20 | server_port smallint unsigned, |
| 21 | ssl_cipher varchar(25), | 21 | ssl_cipher varchar(25), |
| @@ -23,7 +23,7 @@ create table access_log ( | |||
| 23 | ssl_maxkeysize smallint unsigned, | 23 | ssl_maxkeysize smallint unsigned, |
| 24 | status smallint unsigned , | 24 | status smallint unsigned , |
| 25 | time_stamp int unsigned , | 25 | time_stamp int unsigned , |
| 26 | virtual_host varchar(50) | 26 | virtual_host varchar(255) |
| 27 | ); | 27 | ); |
| 28 | 28 | ||
| 29 | create table notes ( | 29 | create table notes ( |
diff --git a/make_combined_log.pl b/contrib/make_combined_log.pl index 1741dbf..2c55271 100755..100644 --- a/make_combined_log.pl +++ b/contrib/make_combined_log.pl | |||
| @@ -1,6 +1,6 @@ | |||
| 1 | #!/usr/bin/perl | 1 | #!/usr/bin/perl |
| 2 | 2 | ||
| 3 | # $Id: make_combined_log.pl,v 1.5 2002/11/14 03:51:34 helios Exp $ | 3 | # $Id: make_combined_log.pl,v 1.2 2004/02/12 23:32:55 urkle Exp $ |
| 4 | # | 4 | # |
| 5 | # make_combined_log.pl | 5 | # make_combined_log.pl |
| 6 | # | 6 | # |
| @@ -34,14 +34,9 @@ $| = 1; | |||
| 34 | 34 | ||
| 35 | use DBI; | 35 | use DBI; |
| 36 | 36 | ||
| 37 | # Remember, $#ARGV is parameters minus one... | 37 | # Remove the # in front of this line when you have |
| 38 | if ($#ARGV != 1) { | 38 | # edited the variables below. |
| 39 | die "Incorrect usage, please read the perl source code for correct usage." | 39 | #$has_edited_source = 1; |
| 40 | } | ||
| 41 | |||
| 42 | $days = $ARGV[0]; | ||
| 43 | $virthost = $ARGV[1]; | ||
| 44 | |||
| 45 | # | 40 | # |
| 46 | # Set up the proper variables to permit database access | 41 | # Set up the proper variables to permit database access |
| 47 | # | 42 | # |
| @@ -52,6 +47,20 @@ $serverPass = "somepass"; | |||
| 52 | $serverTbl = "acc_log_tbl"; | 47 | $serverTbl = "acc_log_tbl"; |
| 53 | $serverDb = "apache"; | 48 | $serverDb = "apache"; |
| 54 | 49 | ||
| 50 | if (!defined($has_edited_source)) { | ||
| 51 | print "Please edit this file and configure it first.\n"; | ||
| 52 | print "This program is $0\n"; | ||
| 53 | exit 1; | ||
| 54 | } | ||
| 55 | # Remember, $#ARGV is parameters minus one... | ||
| 56 | if ($#ARGV != 1) { | ||
| 57 | print "Usage $0 days virtualhost\n"; | ||
| 58 | exit 1; | ||
| 59 | } | ||
| 60 | |||
| 61 | $days = $ARGV[0]; | ||
| 62 | $virthost = $ARGV[1]; | ||
| 63 | |||
| 55 | # | 64 | # |
| 56 | # Other constants | 65 | # Other constants |
| 57 | # | 66 | # |
| @@ -128,4 +137,3 @@ while (@data = $records->fetchrow_array) { | |||
| 128 | # Done | 137 | # Done |
| 129 | # | 138 | # |
| 130 | $records->finish; | 139 | $records->finish; |
| 131 | |||
diff --git a/contrib/mysql_import_combined_log.pl b/contrib/mysql_import_combined_log.pl new file mode 100644 index 0000000..bffec20 --- /dev/null +++ b/contrib/mysql_import_combined_log.pl | |||
| @@ -0,0 +1,224 @@ | |||
| 1 | #!/usr/bin/perl -w | ||
| 2 | # $Id: mysql_import_combined_log.pl,v 1.4 2004/02/21 18:09:50 urkle Exp $ | ||
| 3 | # Written by Aaron Jenson. | ||
| 4 | # Original source: http://www.visualprose.com/software.php | ||
| 5 | # Updated to work under Perl 5.6.1 by Edward Rudd | ||
| 6 | use strict; | ||
| 7 | use Getopt::Long qw(:config bundling); | ||
| 8 | use DBI; | ||
| 9 | use Date::Parse; | ||
| 10 | |||
| 11 | my %options = (); | ||
| 12 | my $i = 0; | ||
| 13 | my $sql = ''; | ||
| 14 | my $valuesSql = ''; | ||
| 15 | my $line = ''; | ||
| 16 | my $dbh = 0; | ||
| 17 | my $sth = 0; | ||
| 18 | my @parts = (); | ||
| 19 | my $part; | ||
| 20 | my $TIMESTAMP = 3; | ||
| 21 | my $REQUEST_LINE = 4; | ||
| 22 | my @cols = ( | ||
| 23 | 'remote_host', ## 0 | ||
| 24 | 'remote_logname', ## 1 | ||
| 25 | 'remote_user', ## 2 | ||
| 26 | 'request_time', ## 3.string | ||
| 27 | 'time_stamp', ## 3.posix | ||
| 28 | 'request_line', ## 5 | ||
| 29 | 'request_method', ## 6 | ||
| 30 | 'request_uri', ## 7 | ||
| 31 | 'request_args', ## 8 | ||
| 32 | 'request_protocol', ## 9 | ||
| 33 | 'status', ## 10 | ||
| 34 | 'bytes_sent', ## 11 | ||
| 35 | 'referer', ## 12 | ||
| 36 | 'agent' ## 13 | ||
| 37 | ); | ||
| 38 | my $col = ''; | ||
| 39 | |||
| 40 | GetOptions (\%options, | ||
| 41 | "version" => sub { VERSION_MESSAGE(); exit 0; }, | ||
| 42 | "help|?" => sub { HELP_MESSAGE(); exit 0; }, | ||
| 43 | "host|h=s", | ||
| 44 | "database|d=s", | ||
| 45 | "table|t=s", | ||
| 46 | "username|u=s", | ||
| 47 | "password|p=s", | ||
| 48 | "logfile|f=s"); | ||
| 49 | |||
| 50 | $options{host} ||= 'localhost'; | ||
| 51 | $options{database} ||= ''; | ||
| 52 | $options{username} ||= ''; | ||
| 53 | $options{password} ||= ''; | ||
| 54 | $options{logfile} ||= ''; | ||
| 55 | |||
| 56 | if( ! $options{database} ) | ||
| 57 | { | ||
| 58 | HELP_MESSAGE(); | ||
| 59 | print "Must supply a database to connect to.\n"; | ||
| 60 | exit 1; | ||
| 61 | } | ||
| 62 | |||
| 63 | if( ! $options{table} ) | ||
| 64 | { | ||
| 65 | HELP_MESSAGE(); | ||
| 66 | print "Must supply table name.\n"; | ||
| 67 | exit 1; | ||
| 68 | } | ||
| 69 | |||
| 70 | if( $options{logfile} ) | ||
| 71 | { | ||
| 72 | if( ! -e $options{logfile} ) | ||
| 73 | { | ||
| 74 | print "File '$options{logfile}' doesn't exist.\n"; | ||
| 75 | exit 1; | ||
| 76 | } | ||
| 77 | open(STDIN, "<$options{logfile}") || die "Can't open $options{logfile} for reading."; | ||
| 78 | } | ||
| 79 | |||
| 80 | $dbh = Connect(); | ||
| 81 | if (! $dbh) { | ||
| 82 | exit 1; | ||
| 83 | } | ||
| 84 | |||
| 85 | $sql = "INSERT INTO $options{table} ("; | ||
| 86 | foreach $col (@cols) | ||
| 87 | { | ||
| 88 | $sql .= "$col," if( $col ); | ||
| 89 | } | ||
| 90 | chop($sql); | ||
| 91 | $sql .= ') VALUES ('; | ||
| 92 | my ($linecount,$insertcount) = (0,0); | ||
| 93 | while($line = <STDIN>) | ||
| 94 | { | ||
| 95 | $linecount++; | ||
| 96 | @parts = SplitLogLine( $line ); | ||
| 97 | next if( $parts[$TIMESTAMP+1] == 0 ); | ||
| 98 | $valuesSql = ''; | ||
| 99 | for( $i = 0; $i < @cols; ++$i ) | ||
| 100 | { | ||
| 101 | $parts[$i] =~ s/\\/\\\\/g; | ||
| 102 | $parts[$i] =~ s/'/\\'/g; | ||
| 103 | $valuesSql .= "'$parts[$i]'," if( $cols[$i] ); | ||
| 104 | } | ||
| 105 | chop($valuesSql); | ||
| 106 | |||
| 107 | $sth = $dbh->prepare("$sql$valuesSql)"); | ||
| 108 | if( ! $sth->execute() ) | ||
| 109 | { | ||
| 110 | print "Unable to perform specified query.\n$sql$valuesSql\n" . $sth->errstr() . "\n"; | ||
| 111 | } else { | ||
| 112 | $insertcount++; | ||
| 113 | } | ||
| 114 | $sth->finish(); | ||
| 115 | } | ||
| 116 | print "Parsed $linecount Log lines\n"; | ||
| 117 | print "Inserted $insertcount records\n"; | ||
| 118 | print "to table '$options{table}' in database '$options{database}' on '$options{host}'\n"; | ||
| 119 | |||
| 120 | # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # | ||
| 121 | # Connects to a MySQL database and returns the connection. | ||
| 122 | # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # | ||
| 123 | sub Connect | ||
| 124 | { | ||
| 125 | my $dsn = "DBI:mysql:$options{database};hostname=$options{host}"; | ||
| 126 | return DBI->connect( $dsn, $options{username}, $options{password} ); | ||
| 127 | } | ||
| 128 | |||
| 129 | |||
| 130 | # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # | ||
| 131 | # Splits up a log line into its parts. | ||
| 132 | # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # | ||
| 133 | sub SplitLogLine | ||
| 134 | { | ||
| 135 | my $line = shift; | ||
| 136 | my $i = 0; | ||
| 137 | my $inQuote = 0; | ||
| 138 | my $char = ''; | ||
| 139 | my $part = ''; | ||
| 140 | my @parts = (); | ||
| 141 | my $count = 0; | ||
| 142 | chomp($line); | ||
| 143 | for( $i = 0; $i < length($line); ++$i ) | ||
| 144 | { | ||
| 145 | $char = substr($line, $i, 1); | ||
| 146 | if( $char eq ' ' && ! $inQuote ) | ||
| 147 | { | ||
| 148 | ## print "Found part $part.\n"; | ||
| 149 | if( $count == $TIMESTAMP ) | ||
| 150 | { | ||
| 151 | push(@parts, "[".$part."]"); | ||
| 152 | $part = str2time($part); | ||
| 153 | } | ||
| 154 | push(@parts, $part); | ||
| 155 | if( $count == $REQUEST_LINE ) | ||
| 156 | { | ||
| 157 | my @request = split(/[ ?]/, $part); | ||
| 158 | push(@parts, $request[0]); | ||
| 159 | push(@parts, $request[1]); | ||
| 160 | if( $request[3] ) | ||
| 161 | { | ||
| 162 | push(@parts, $request[2]); | ||
| 163 | push(@parts, $request[3]); | ||
| 164 | } | ||
| 165 | else | ||
| 166 | { | ||
| 167 | push(@parts, ''); | ||
| 168 | push(@parts, $request[2]); | ||
| 169 | } | ||
| 170 | $count += 5; | ||
| 171 | } | ||
| 172 | else | ||
| 173 | { | ||
| 174 | ++$count; | ||
| 175 | } | ||
| 176 | $part = ''; | ||
| 177 | } | ||
| 178 | elsif( $char eq '"' || $char eq '[' || $char eq ']' ) | ||
| 179 | { | ||
| 180 | $inQuote = !$inQuote; | ||
| 181 | } | ||
| 182 | else | ||
| 183 | { | ||
| 184 | $part .= $char; | ||
| 185 | } | ||
| 186 | } | ||
| 187 | push(@parts,$part) if $part; | ||
| 188 | |||
| 189 | return @parts; | ||
| 190 | } | ||
| 191 | |||
| 192 | |||
| 193 | # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # | ||
| 194 | # Prints the usage/help message for this program. | ||
| 195 | # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # | ||
| 196 | sub HELP_MESSAGE | ||
| 197 | { | ||
| 198 | print<<EOF; | ||
| 199 | Imports an Apache combined log into a MySQL database. | ||
| 200 | Usage: mysql_import_combined_log.pl -d <database name> -t <table name> [-h <hostname>] [-u <username>] [-p <password>] [-f <filename] | ||
| 201 | --host|-h <host name> The host to connect to. Default is localhost. | ||
| 202 | --database|-d <database name> The database to use. Required. | ||
| 203 | --username|-u <username> The user to connect as. | ||
| 204 | --password|-p <password> The user's password. | ||
| 205 | --table|-t <table name> The name of the table in which to insert data. | ||
| 206 | --logfile|-f <file name> The file to read from. If not given, data is read from stdin. | ||
| 207 | --help|-? Print out this help message. | ||
| 208 | --version Print out the version of this software. | ||
| 209 | EOF | ||
| 210 | } | ||
| 211 | |||
| 212 | |||
| 213 | |||
| 214 | # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # | ||
| 215 | # Prints the version information for this program | ||
| 216 | # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # | ||
| 217 | sub VERSION_MESSAGE | ||
| 218 | { | ||
| 219 | print "mysql_import_combined_log.pl version 1.2\n"; | ||
| 220 | print "Version 1.0 Written by Aaron Jenson.\n"; | ||
| 221 | print "Update to work with perl 5.6.1 by Edward Rudd\n"; | ||
| 222 | } | ||
| 223 | |||
| 224 | 1; | ||
diff --git a/docs/.cvsignore b/docs/.cvsignore new file mode 100644 index 0000000..73cd04a --- /dev/null +++ b/docs/.cvsignore | |||
| @@ -0,0 +1,3 @@ | |||
| 1 | Makefile | ||
| 2 | manual.html | ||
| 3 | manual.pdf | ||
diff --git a/docs/Makefile.in b/docs/Makefile.in new file mode 100644 index 0000000..d88e72b --- /dev/null +++ b/docs/Makefile.in | |||
| @@ -0,0 +1,60 @@ | |||
| 1 | # @configure_input@ | ||
| 2 | |||
| 3 | # Modify these top variables. | ||
| 4 | SUBDIRS = | ||
| 5 | |||
| 6 | EXTRA_DIST = README \ | ||
| 7 | manual.xml \ | ||
| 8 | manual.html | ||
| 9 | |||
| 10 | #Don't modify anything below here | ||
| 11 | |||
| 12 | srcdir = @abs_srcdir@ | ||
| 13 | builddir = @abs_builddir@ | ||
| 14 | |||
| 15 | STD_DIST = Makefile.in | ||
| 16 | |||
| 17 | DISTFILES = $(STD_DIST) $(EXTRA_DIST) | ||
| 18 | |||
| 19 | all: all-subdirs | ||
| 20 | |||
| 21 | %.html: %.xml | ||
| 22 | @xmlto xhtml-nochunks $< | ||
| 23 | |||
| 24 | %.pdf: %.xml | ||
| 25 | @xmlto pdf $< | ||
| 26 | |||
| 27 | all-subdirs install-subdirs update-subdirs clean-subdirs distclean-subdirs: | ||
| 28 | @otarget=`echo $@|sed s/-subdirs//`; \ | ||
| 29 | list=' $(SUBDIRS)'; \ | ||
| 30 | for i in $$list; do \ | ||
| 31 | if test -d "$$i"; then \ | ||
| 32 | target="$$otarget"; \ | ||
| 33 | echo "Making $$target in $$i"; \ | ||
| 34 | if test "$$i" = "."; then \ | ||
| 35 | made_local=yes; \ | ||
| 36 | target="local-$$target"; \ | ||
| 37 | fi; \ | ||
| 38 | (cd $$i && $(MAKE) $$target) || exit 1; \ | ||
| 39 | fi; \ | ||
| 40 | done; \ | ||
| 41 | |||
| 42 | include: | ||
| 43 | rm -rf include | ||
| 44 | ln -s @APACHE_INCDIR@ include | ||
| 45 | |||
| 46 | install: install-subdirs | ||
| 47 | |||
| 48 | update: update-subdirs | ||
| 49 | |||
| 50 | clean: clean-subdirs | ||
| 51 | |||
| 52 | distclean: clean distclean-subdirs | ||
| 53 | $(RM) Makefile | ||
| 54 | |||
| 55 | local-dist: $(DISTFILES) | ||
| 56 | mkdir -p $(DESTDIR) | ||
| 57 | cp -dp --parents $(DISTFILES) $(DESTDIR) | ||
| 58 | |||
| 59 | .PHONY: include all-subdirs update-subdirs install-subdirs \ | ||
| 60 | clean-subdirs distclean-subdirs dist | ||
diff --git a/docs/README b/docs/README new file mode 100644 index 0000000..dd40351 --- /dev/null +++ b/docs/README | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | The "original" document is the Docbook file "manual.xml" -- all other | ||
| 2 | files here are derived from it. | ||
| 3 | |||
| 4 | To read the HTML docs, open manual.html in your browser. | ||
| 5 | |||
| 6 | To generate other formats of the documentation use xmlto or your favorite | ||
| 7 | xslt docbook converter to convert the xml file. | ||
diff --git a/Documentation/documentation.lyx b/docs/documentation.lyx index a678315..eeb6af5 100644 --- a/Documentation/documentation.lyx +++ b/docs/documentation.lyx | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | #LyX 1.2 created this file. For more info see http://www.lyx.org/ | 1 | #LyX 1.3 created this file. For more info see http://www.lyx.org/ |
| 2 | \lyxformat 220 | 2 | \lyxformat 221 |
| 3 | \textclass article | 3 | \textclass article |
| 4 | \language english | 4 | \language english |
| 5 | \inputencoding default | 5 | \inputencoding default |
| @@ -162,7 +162,7 @@ Note which directives go in the 'main server config' and which directives | |||
| 162 | This is made clear in the directive documentation. | 162 | This is made clear in the directive documentation. |
| 163 | \layout Itemize | 163 | \layout Itemize |
| 164 | 164 | ||
| 165 | The 'time_stamp' field is stored in an UNSIGNED INTEGER column, in the standard | 165 | The 'time_stamp' field is stored in an UNSIGNED INTEGER format, in the standard |
| 166 | unix | 166 | unix |
| 167 | \begin_inset Quotes eld | 167 | \begin_inset Quotes eld |
| 168 | \end_inset | 168 | \end_inset |
| @@ -173,9 +173,9 @@ seconds since the epoch | |||
| 173 | 173 | ||
| 174 | format. | 174 | format. |
| 175 | This is superior to storing the access time as a string due to size requirement | 175 | This is superior to storing the access time as a string due to size requirement |
| 176 | s: an UNSIGNED INT requires 4 bytes, whereas an Apache date string -- e.g. | 176 | s: an UNSIGNED INT requires 4 bytes, whereas an Apache date string (e.g. |
| 177 | "18/Nov/2001:13:59:52 -0800" -- requires 26 bytes: those extra 22 bytes | 177 | "18/Nov/2001:13:59:52 -0800") requires 26 bytes: those extra 22 bytes become |
| 178 | become significant when multiplied by thousands of accesses on a busy server. | 178 | significant when multiplied by thousands of accesses on a busy server. |
| 179 | Besides, an INT type is far more flexible for comparisons, etc. | 179 | Besides, an INT type is far more flexible for comparisons, etc. |
| 180 | \begin_deeper | 180 | \begin_deeper |
| 181 | \layout Standard | 181 | \layout Standard |
| @@ -301,6 +301,11 @@ These installation documents assume a relatively modern GNU/Linux scenario. | |||
| 301 | compiling the module for those platforms. | 301 | compiling the module for those platforms. |
| 302 | \layout Subsubsection | 302 | \layout Subsubsection |
| 303 | 303 | ||
| 304 | |||
| 305 | \begin_inset LatexCommand \label{sub:Solaris} | ||
| 306 | |||
| 307 | \end_inset | ||
| 308 | |||
| 304 | Solaris | 309 | Solaris |
| 305 | \layout Standard | 310 | \layout Standard |
| 306 | 311 | ||
| @@ -387,6 +392,143 @@ contact | |||
| 387 | \end_inset | 392 | \end_inset |
| 388 | 393 | ||
| 389 | and help fill in this section. | 394 | and help fill in this section. |
| 395 | \layout Subsubsection | ||
| 396 | |||
| 397 | OS X | ||
| 398 | \layout Standard | ||
| 399 | |||
| 400 | mod_log_sql should compile and work out-of-the-box on this platform. | ||
| 401 | Here are some notes from a user successfully running the module on OS X: | ||
| 402 | \layout Quote | ||
| 403 | |||
| 404 | |||
| 405 | \emph on | ||
| 406 | The only changes I had to make were to where I had the various libraries | ||
| 407 | installed. | ||
| 408 | \layout Quote | ||
| 409 | |||
| 410 | |||
| 411 | \emph on | ||
| 412 | Here are the changes I made to the head of the Makefile: | ||
| 413 | \layout LyX-Code | ||
| 414 | |||
| 415 | APACHESOURCE = /usr/local/src/apache_1.3.27 | ||
| 416 | \layout Quote | ||
| 417 | |||
| 418 | |||
| 419 | \emph on | ||
| 420 | (Wasn't sure if this was really needed or not, so I downloaded the Apache | ||
| 421 | source just in case) | ||
| 422 | \layout LyX-Code | ||
| 423 | |||
| 424 | APACHEINSTALLED = /usr/sbin | ||
| 425 | \layout LyX-Code | ||
| 426 | |||
| 427 | APACHEHEADERS = /usr/include/httpd | ||
| 428 | \layout LyX-Code | ||
| 429 | |||
| 430 | APXS = $(APACHEINSTALLED)/apxs | ||
| 431 | \layout LyX-Code | ||
| 432 | |||
| 433 | MYSQLLIBRARIES = /usr/local/mysql/lib | ||
| 434 | \layout LyX-Code | ||
| 435 | |||
| 436 | MYSQLHEADERS = /usr/local/mysql/include | ||
| 437 | \layout Quote | ||
| 438 | |||
| 439 | |||
| 440 | \emph on | ||
| 441 | I'm using a binary installation of MySQL and the default apache installation | ||
| 442 | on OS X Client 10.2.3, the locations of these files may vary depending on | ||
| 443 | how you've installed MySQL and will almost certainly be different if you're | ||
| 444 | using OS X Server. | ||
| 445 | \layout Standard | ||
| 446 | |||
| 447 | My thanks to Tom Wiebe for being the first (to my knowlege) mod_log_sql | ||
| 448 | user on OS X and for providing these notes. | ||
| 449 | \layout Subsubsection | ||
| 450 | |||
| 451 | Digital Unix | ||
| 452 | \layout Standard | ||
| 453 | |||
| 454 | Digital Unix, like Solaris, needs to be linked against librt; see section | ||
| 455 | |||
| 456 | \begin_inset LatexCommand \ref{sub:Solaris} | ||
| 457 | |||
| 458 | \end_inset | ||
| 459 | |||
| 460 | . | ||
| 461 | Here are further notes from a user successfully running the module on Digital | ||
| 462 | Unix: | ||
| 463 | \layout Quote | ||
| 464 | |||
| 465 | |||
| 466 | \emph on | ||
| 467 | Instead of trying to get the module to remember where the MySQL libraries | ||
| 468 | were, I instead compiled apache with the information: | ||
| 469 | \layout Quote | ||
| 470 | |||
| 471 | |||
| 472 | \emph on | ||
| 473 | LDFLAGS='-rpath /isp/mysql/lib/mysql' ./configure ... | ||
| 474 | \layout Quote | ||
| 475 | |||
| 476 | |||
| 477 | \emph on | ||
| 478 | Everything worked as expected after that. | ||
| 479 | (The error I got without this was "/sbin/loader: Fatal Error: cannot map | ||
| 480 | libmysqlclient.so" ) | ||
| 481 | \layout Quote | ||
| 482 | |||
| 483 | |||
| 484 | \emph on | ||
| 485 | Digital Unix (v4.0f, at least ) appears to follow the same requirements needed | ||
| 486 | by Solaris, so simply adding librt to the module made it compile without | ||
| 487 | errors. | ||
| 488 | \layout Quote | ||
| 489 | |||
| 490 | |||
| 491 | \emph on | ||
| 492 | As for the warnings, here's the text: | ||
| 493 | \layout LyX-Code | ||
| 494 | |||
| 495 | |||
| 496 | \emph on | ||
| 497 | mod_log_sql.c: In function `extract_request_duration': | ||
| 498 | \layout LyX-Code | ||
| 499 | |||
| 500 | |||
| 501 | \emph on | ||
| 502 | mod_log_sql.c:292: warning: long int format, different type arg (arg 4) | ||
| 503 | \layout LyX-Code | ||
| 504 | |||
| 505 | |||
| 506 | \emph on | ||
| 507 | mod_log_sql.c: In function `extract_request_timestamp': | ||
| 508 | \layout LyX-Code | ||
| 509 | |||
| 510 | |||
| 511 | \emph on | ||
| 512 | mod_log_sql.c:497: warning: long int format, different type arg (arg 4) | ||
| 513 | \layout Quote | ||
| 514 | |||
| 515 | |||
| 516 | \emph on | ||
| 517 | Poking around in the code, it looks like the compiler was complaining that | ||
| 518 | what time() is returning doesn't play nicely with %ld by default. | ||
| 519 | I just typecast them as (long)'s and the warnings went away ( not that | ||
| 520 | the module wasn't working correctly without them ). | ||
| 521 | \layout Quote | ||
| 522 | |||
| 523 | |||
| 524 | \emph on | ||
| 525 | The module works very well so far in testing... | ||
| 526 | hasn't dropped a single log entry yet. | ||
| 527 | |||
| 528 | \layout Standard | ||
| 529 | |||
| 530 | My thanks to Jim Turner for permitting me to quote him here, and for being | ||
| 531 | the first known user of mod_log_sql on Digital Unix. | ||
| 390 | \layout Subsection | 532 | \layout Subsection |
| 391 | 533 | ||
| 392 | Do I want a DSO or a static module? | 534 | Do I want a DSO or a static module? |
| @@ -919,10 +1061,24 @@ mysql> create database apachelogs; | |||
| 919 | \end_inset | 1061 | \end_inset |
| 920 | 1062 | ||
| 921 | If you want to hand-create the tables, run the enclosed 'create-tables' | 1063 | If you want to hand-create the tables, run the enclosed 'create-tables' |
| 922 | SQL script as follows: | 1064 | SQL script as follows ( |
| 1065 | \begin_inset Quotes eld | ||
| 1066 | \end_inset | ||
| 1067 | |||
| 1068 | create_tables.sql | ||
| 1069 | \begin_inset Quotes erd | ||
| 1070 | \end_inset | ||
| 1071 | |||
| 1072 | needs to be in your current working directory). | ||
| 923 | \begin_deeper | 1073 | \begin_deeper |
| 924 | \layout LyX-Code | 1074 | \layout LyX-Code |
| 925 | 1075 | ||
| 1076 | mysql> use apachelogs | ||
| 1077 | \layout LyX-Code | ||
| 1078 | |||
| 1079 | Database changed | ||
| 1080 | \layout LyX-Code | ||
| 1081 | |||
| 926 | mysql> source create_tables.sql | 1082 | mysql> source create_tables.sql |
| 927 | \end_deeper | 1083 | \end_deeper |
| 928 | \layout Enumerate | 1084 | \layout Enumerate |
| @@ -939,9 +1095,7 @@ Create a specific MySQL userid that httpd will use to authenticate and enter | |||
| 939 | \layout LyX-Code | 1095 | \layout LyX-Code |
| 940 | 1096 | ||
| 941 | mysql> grant insert,create on apachelogs.* to loguser@my.apachemachine.com | 1097 | mysql> grant insert,create on apachelogs.* to loguser@my.apachemachine.com |
| 942 | \layout LyX-Code | 1098 | identified by 'l0gger'; |
| 943 | |||
| 944 | identified by 'l0gger'; | ||
| 945 | \end_deeper | 1099 | \end_deeper |
| 946 | \layout Enumerate | 1100 | \layout Enumerate |
| 947 | 1101 | ||
| @@ -4817,24 +4971,29 @@ Context: virtual host | |||
| 4817 | In HTTP, cookies have names to distinguish them from each other. | 4971 | In HTTP, cookies have names to distinguish them from each other. |
| 4818 | Using mod_usertrack, for example, you can give your user-tracking cookies | 4972 | Using mod_usertrack, for example, you can give your user-tracking cookies |
| 4819 | a name with the CookieName directive. | 4973 | a name with the CookieName directive. |
| 4974 | |||
| 4820 | \layout Standard | 4975 | \layout Standard |
| 4821 | 4976 | ||
| 4822 | You must include a 'c' character in | 4977 | mod_log_sql allows you to log cookie information. |
| 4823 | \noun on | 4978 | |
| 4824 | LogSQLTransferLogFormat | ||
| 4825 | \noun default | ||
| 4826 | for this directive to take effect; once you specify 'c', | ||
| 4827 | \noun on | 4979 | \noun on |
| 4828 | LogSQLWhichCookie | 4980 | LogSQLWhichCookie |
| 4829 | \noun default | 4981 | \noun default |
| 4830 | tells mod_log_sql which cookie to log. | 4982 | tells mod_log_sql which cookie to log. |
| 4831 | This is necessary because you will usually be setting and receiving more | 4983 | This is necessary because you will usually be setting and receiving more |
| 4832 | than one cookie from a client; this cookie designates which one to log. | 4984 | than one cookie from a client. |
| 4985 | \layout Standard | ||
| 4986 | |||
| 4987 | You must include a 'c' character in | ||
| 4988 | \noun on | ||
| 4989 | LogSQLTransferLogFormat | ||
| 4990 | \noun default | ||
| 4991 | for this directive to take effect. | ||
| 4833 | \layout Standard | 4992 | \layout Standard |
| 4834 | 4993 | ||
| 4835 | Note: although this was intended for people who are using mod_usertrack | 4994 | Note: although this was origintally intended for people using mod_usertrack |
| 4836 | to set user-tracking cookies, you aren't restricted in any way. | 4995 | to create user-tracking cookies, you aren't restricted in any way. |
| 4837 | You can choose which cookie you wish to log to the database --any cookie | 4996 | You can choose which cookie you wish to log to the database -- any cookie |
| 4838 | at all -- and it doesn't necessarily have to have anything to do with mod_usert | 4997 | at all -- and it doesn't necessarily have to have anything to do with mod_usert |
| 4839 | rack. | 4998 | rack. |
| 4840 | \layout Subsubsection | 4999 | \layout Subsubsection |
| @@ -5742,7 +5901,7 @@ Please contact | |||
| 5742 | Problems | 5901 | Problems |
| 5743 | \layout Subsubsection | 5902 | \layout Subsubsection |
| 5744 | 5903 | ||
| 5745 | Apache segfaults when using PHP and mod_log_sql | 5904 | Apache segfaults or has other problems when using PHP and mod_log_sql |
| 5746 | \layout Standard | 5905 | \layout Standard |
| 5747 | 5906 | ||
| 5748 | This occurs if you compiled PHP with MySQL database support. | 5907 | This occurs if you compiled PHP with MySQL database support. |
| @@ -5759,8 +5918,9 @@ real | |||
| 5759 | 5918 | ||
| 5760 | \layout Standard | 5919 | \layout Standard |
| 5761 | 5920 | ||
| 5762 | The solution is to configure PHP to link against the real MySQL libraries | 5921 | PHP and mod_log_sql can be configured to happily coexist. |
| 5763 | and recompile mod_php. | 5922 | The solution is to configure PHP to link against the real MySQL libraries: |
| 5923 | recompile PHP using --with-mysql=/your/path. | ||
| 5764 | Apache will run properly once the modules are all using the same version | 5924 | Apache will run properly once the modules are all using the same version |
| 5765 | of the MySQL libraries. | 5925 | of the MySQL libraries. |
| 5766 | \layout Subsubsection | 5926 | \layout Subsubsection |
| @@ -5983,6 +6143,83 @@ Reference: | |||
| 5983 | \end_inset | 6143 | \end_inset |
| 5984 | 6144 | ||
| 5985 | 6145 | ||
| 6146 | \layout Subsubsection | ||
| 6147 | |||
| 6148 | Sometimes a single VirtualHost gets logged to two different tables (e.g. | ||
| 6149 | access_foo_com, access_www_foo_com). | ||
| 6150 | Or, accesses to an unqualified hostname (e.g. | ||
| 6151 | |||
| 6152 | \begin_inset Quotes eld | ||
| 6153 | \end_inset | ||
| 6154 | |||
| 6155 | http://intranet/index.html | ||
| 6156 | \begin_inset Quotes erd | ||
| 6157 | \end_inset | ||
| 6158 | |||
| 6159 | ) get logged in separate tables. | ||
| 6160 | \layout Standard | ||
| 6161 | |||
| 6162 | Proper usage of the Apache runtime | ||
| 6163 | \noun on | ||
| 6164 | ServerName | ||
| 6165 | \noun default | ||
| 6166 | directive and the directive | ||
| 6167 | \noun on | ||
| 6168 | UseCanonicalName On | ||
| 6169 | \noun default | ||
| 6170 | (or | ||
| 6171 | \noun on | ||
| 6172 | DNS | ||
| 6173 | \noun default | ||
| 6174 | ) are necessary to prevent this problem. | ||
| 6175 | |||
| 6176 | \begin_inset Quotes eld | ||
| 6177 | \end_inset | ||
| 6178 | |||
| 6179 | On | ||
| 6180 | \begin_inset Quotes erd | ||
| 6181 | \end_inset | ||
| 6182 | |||
| 6183 | is the default for | ||
| 6184 | \noun on | ||
| 6185 | UseCanonicalName | ||
| 6186 | \noun default | ||
| 6187 | , and specifies that self-referential URLs are generated from the | ||
| 6188 | \noun on | ||
| 6189 | ServerName | ||
| 6190 | \noun default | ||
| 6191 | part of your VirtualHost: | ||
| 6192 | \layout Quote | ||
| 6193 | |||
| 6194 | With UseCanonicalName on (and in all versions prior to 1.3) Apache will use | ||
| 6195 | the ServerName and Port directives to construct the canonical name for | ||
| 6196 | the server. | ||
| 6197 | With UseCanonicalName off Apache will form self-referential URLs using | ||
| 6198 | the hostname and port supplied by the client if any are supplied (otherwise | ||
| 6199 | it will use the canonical name, as defined above). | ||
| 6200 | [From | ||
| 6201 | \begin_inset LatexCommand \url[the Apache documentation]{http://httpd.apache.org/docs/mod/core.html#usecanonicalname} | ||
| 6202 | |||
| 6203 | \end_inset | ||
| 6204 | |||
| 6205 | ] | ||
| 6206 | \layout Standard | ||
| 6207 | |||
| 6208 | The module inherits Apache's | ||
| 6209 | \begin_inset Quotes eld | ||
| 6210 | \end_inset | ||
| 6211 | |||
| 6212 | knowledge | ||
| 6213 | \begin_inset Quotes erd | ||
| 6214 | \end_inset | ||
| 6215 | |||
| 6216 | about the server name being accessed. | ||
| 6217 | As long as those two directives are properly configured, mod_log_sql will | ||
| 6218 | log to only one table per virtual host while using | ||
| 6219 | \noun on | ||
| 6220 | LogSQLMassVirtualHosting | ||
| 6221 | \noun default | ||
| 6222 | . | ||
| 5986 | \layout Subsection | 6223 | \layout Subsection |
| 5987 | 6224 | ||
| 5988 | Performance and Tuning | 6225 | Performance and Tuning |
| @@ -6236,7 +6473,7 @@ not | |||
| 6236 | correct | 6473 | correct |
| 6237 | \series default | 6474 | \series default |
| 6238 | to assume that 20 Apache children with a VSZ of 7MB each equals | 6475 | to assume that 20 Apache children with a VSZ of 7MB each equals |
| 6239 | \begin_inset Formula $(20\times 7MB)$ | 6476 | \begin_inset Formula $(20\times7MB)$ |
| 6240 | \end_inset | 6477 | \end_inset |
| 6241 | 6478 | ||
| 6242 | of memory consumption -- the real answer is much, much lower. | 6479 | of memory consumption -- the real answer is much, much lower. |
| @@ -6359,74 +6596,6 @@ How do I...? | |||
| 6359 | -- accomplishing certain tasks | 6596 | -- accomplishing certain tasks |
| 6360 | \layout Subsubsection | 6597 | \layout Subsubsection |
| 6361 | 6598 | ||
| 6362 | I am using LogSQLMassVirtualHosting, and sometimes a single VirtualHost | ||
| 6363 | gets logged to two different tables. | ||
| 6364 | How do I prevent that? | ||
| 6365 | \layout Standard | ||
| 6366 | |||
| 6367 | Proper usage of the Apache runtime | ||
| 6368 | \noun on | ||
| 6369 | ServerName | ||
| 6370 | \noun default | ||
| 6371 | directive and the directive | ||
| 6372 | \noun on | ||
| 6373 | UseCanonicalName On | ||
| 6374 | \noun default | ||
| 6375 | (or | ||
| 6376 | \noun on | ||
| 6377 | DNS | ||
| 6378 | \noun default | ||
| 6379 | ) are necessary to prevent this problem. | ||
| 6380 | |||
| 6381 | \begin_inset Quotes eld | ||
| 6382 | \end_inset | ||
| 6383 | |||
| 6384 | On | ||
| 6385 | \begin_inset Quotes erd | ||
| 6386 | \end_inset | ||
| 6387 | |||
| 6388 | is the default for | ||
| 6389 | \noun on | ||
| 6390 | UseCanonicalName | ||
| 6391 | \noun default | ||
| 6392 | , and specifies that self-referential URLs are generated from the | ||
| 6393 | \noun on | ||
| 6394 | ServerName | ||
| 6395 | \noun default | ||
| 6396 | part of your VirtualHost: | ||
| 6397 | \layout Quote | ||
| 6398 | |||
| 6399 | With UseCanonicalName on (and in all versions prior to 1.3) Apache will use | ||
| 6400 | the ServerName and Port directives to construct the canonical name for | ||
| 6401 | the server. | ||
| 6402 | With UseCanonicalName off Apache will form self-referential URLs using | ||
| 6403 | the hostname and port supplied by the client if any are supplied (otherwise | ||
| 6404 | it will use the canonical name, as defined above). | ||
| 6405 | [From | ||
| 6406 | \begin_inset LatexCommand \url[the Apache documentation]{http://httpd.apache.org/docs/mod/core.html#usecanonicalname} | ||
| 6407 | |||
| 6408 | \end_inset | ||
| 6409 | |||
| 6410 | ] | ||
| 6411 | \layout Standard | ||
| 6412 | |||
| 6413 | The module inherits Apache's | ||
| 6414 | \begin_inset Quotes eld | ||
| 6415 | \end_inset | ||
| 6416 | |||
| 6417 | knowledge | ||
| 6418 | \begin_inset Quotes erd | ||
| 6419 | \end_inset | ||
| 6420 | |||
| 6421 | about the server name being accessed. | ||
| 6422 | As long as those two directives are properly configured, mod_log_sql will | ||
| 6423 | log to only one table per virtual host while using | ||
| 6424 | \noun on | ||
| 6425 | LogSQLMassVirtualHosting | ||
| 6426 | \noun default | ||
| 6427 | . | ||
| 6428 | \layout Subsubsection | ||
| 6429 | |||
| 6430 | How do I extract the data in a format that my analysis tool can understand? | 6599 | How do I extract the data in a format that my analysis tool can understand? |
| 6431 | \layout Standard | 6600 | \layout Standard |
| 6432 | 6601 | ||
diff --git a/docs/manual.xml b/docs/manual.xml new file mode 100644 index 0000000..9019e80 --- /dev/null +++ b/docs/manual.xml | |||
| @@ -0,0 +1,3927 @@ | |||
| 1 | <?xml version="1.0" encoding="UTF-8"?> | ||
| 2 | <?xml-stylesheet href="file://localhost/home/urkle/Documents/DocBook/docbook.css" type="text/css"?> | ||
| 3 | <!DOCTYPE article PUBLIC "-//OOOCC//DTD Simplified DocBook XML V1.1 Variant V1.0//EN" "http://outoforder.cc/dtds/odocbook/1.1/odocbook.dtd" [ | ||
| 4 | <!ENTITY EmailContact "<email>urkle <at> outoforder <dot> cc</email>"> | ||
| 5 | ]> | ||
| 6 | <article> | ||
| 7 | <articleinfo> | ||
| 8 | <title>mod_log_sql Manual</title> | ||
| 9 | <author> | ||
| 10 | <firstname>Edward</firstname> | ||
| 11 | <surname>Rudd</surname> | ||
| 12 | <contrib>Conversion from Lyx to DocBook</contrib> | ||
| 13 | <contrib>Current Maintainer</contrib> | ||
| 14 | <authorblurb> | ||
| 15 | <simpara> | ||
| 16 | &EmailContact; | ||
| 17 | </simpara> | ||
| 18 | </authorblurb> | ||
| 19 | </author> | ||
| 20 | <author> | ||
| 21 | <firstname>Christopher</firstname> | ||
| 22 | <othername>B.</othername> | ||
| 23 | <surname>Powell</surname> | ||
| 24 | <contrib>Original documentation author.</contrib> | ||
| 25 | <authorblurb> | ||
| 26 | <simpara> | ||
| 27 | <email>chris <at> grubbybaby <dot> com</email> | ||
| 28 | </simpara> | ||
| 29 | </authorblurb> | ||
| 30 | </author> | ||
| 31 | <copyright> | ||
| 32 | <year>2001</year> | ||
| 33 | <year>2002</year> | ||
| 34 | <year>2003</year> | ||
| 35 | <holder>Christopher B. Powell</holder> | ||
| 36 | </copyright> | ||
| 37 | <copyright> | ||
| 38 | <year>2004</year> | ||
| 39 | <year>2005</year> | ||
| 40 | <year>2006</year> | ||
| 41 | <holder>Edward Rudd</holder> | ||
| 42 | </copyright> | ||
| 43 | <revhistory> | ||
| 44 | <revision> | ||
| 45 | <revnumber>1.5</revnumber> | ||
| 46 | <date>2006-11-04</date> | ||
| 47 | <revremark>Added documentation about logio parameters and added DBParam Mysql driver parameters (including tabletype)</revremark> | ||
| 48 | </revision> | ||
| 49 | <revision> | ||
| 50 | <revnumber>1.4</revnumber> | ||
| 51 | <date>2006-02-13</date> | ||
| 52 | <revremark>Added missing logformat types, switched to simplified docbook 1.1</revremark> | ||
| 53 | </revision> | ||
| 54 | <revision> | ||
| 55 | <revnumber>1.3</revnumber> | ||
| 56 | <date>2005-01-11</date> | ||
| 57 | <revremark>Updated for mod_log_sql v1.100</revremark> | ||
| 58 | </revision> | ||
| 59 | <revision> | ||
| 60 | <revnumber>1.2</revnumber> | ||
| 61 | <date>2004-04-08</date> | ||
| 62 | <revremark>Updated for mod_log_sql v1.97</revremark> | ||
| 63 | </revision> | ||
| 64 | <revision> | ||
| 65 | <revnumber>1.1</revnumber> | ||
| 66 | <date>2004-03-02</date> | ||
| 67 | <revremark>Updated for mod_log_sql v1.96</revremark> | ||
| 68 | </revision> | ||
| 69 | <revision> | ||
| 70 | <revnumber>1.0</revnumber> | ||
| 71 | <date>2004-01-22</date> | ||
| 72 | <revremark>Initial Conversion from Lyx to Docbook</revremark> | ||
| 73 | </revision> | ||
| 74 | </revhistory> | ||
| 75 | </articleinfo> | ||
| 76 | <section> | ||
| 77 | <title>Introduction</title> | ||
| 78 | <section tocstyle="fragment"> | ||
| 79 | <title>Summary</title> | ||
| 80 | <para> | ||
| 81 | This Apache module will permit you to log to a SQL database; it | ||
| 82 | can log each access request as well as data associated with each | ||
| 83 | request: cookies, notes, and inbound/outbound headers. Unlike | ||
| 84 | logging to a flat text file -- which is standard in Apache -- a | ||
| 85 | SQL-based log exhibits tremendous flexibility and power of data | ||
| 86 | extraction. (See FAQ entry | ||
| 87 | <xref linkend="FAQ.WhyLogToSQL" /> | ||
| 88 | for further discussion and examples of the advantages to SQL.) | ||
| 89 | </para> | ||
| 90 | <para> | ||
| 91 | This module can either replace or happily coexist with | ||
| 92 | mod_log_config, Apache's text file logging facility. In addition | ||
| 93 | to being more configurable than the standard module, mod_log_sql | ||
| 94 | is much more flexible. | ||
| 95 | </para> | ||
| 96 | </section> | ||
| 97 | <section tocstyle="fragment"> | ||
| 98 | <title>Approach</title> | ||
| 99 | <para> | ||
| 100 | This project was formerly known as "mod_log_mysql." It was | ||
| 101 | renamed "mod_log_sql" in order to reflect the project goal of | ||
| 102 | database in-specificity. The module currently supports MySQL, | ||
| 103 | but support for other database back-ends is underway. | ||
| 104 | </para> | ||
| 105 | <para> | ||
| 106 | In order to save speed and overhead, links are kept alive in | ||
| 107 | between queries. This module uses one dedicated SQL link per | ||
| 108 | httpd child, opened by each child process when it is born. Among | ||
| 109 | other things, this means that this module supports logging into | ||
| 110 | only one MySQL server, and for now, also, only one SQL database. | ||
| 111 | But that's a small tradeoff compared to the blinding speed of | ||
| 112 | this module. Error reporting is robust throughout the module and | ||
| 113 | will inform the administrator of database issues in the Apache | ||
| 114 | ErrorLog for the server/virtual server. | ||
| 115 | </para> | ||
| 116 | <para> | ||
| 117 | Virtual hosts are supported in the same manner they are in the | ||
| 118 | regular logging modules. The administrator defines some basic | ||
| 119 | 'global' directives in the main server config, then defines more | ||
| 120 | specific 'local' directives inside each VirtualHost stanza. | ||
| 121 | </para> | ||
| 122 | <para> | ||
| 123 | A robust "preserve" capability has now been implemented. This | ||
| 124 | permits the module to preserve any failed INSERT commands to a | ||
| 125 | local file on its machine. In any situation that the database is | ||
| 126 | unavailable -- e.g. the network fails or the database host is | ||
| 127 | rebooted -- mod_log_sql will note this in the error log and | ||
| 128 | begin appending its log entries to the preserve file (which is | ||
| 129 | created with the user and group ID of the running Apache | ||
| 130 | process, e.g. "nobody/nobody" on many Linux installations). When | ||
| 131 | database availability returns, mod_log_sql seamlessly resumes | ||
| 132 | logging to it. When convenient for the sysadmin, he/she can | ||
| 133 | easily import the preserve file into the database because it is | ||
| 134 | simply a series of SQL insert statements. | ||
| 135 | </para> | ||
| 136 | </section> | ||
| 137 | <section tocstyle="fragment"> | ||
| 138 | <title>What gets logged by default?</title> | ||
| 139 | <para> | ||
| 140 | All the data that would be contained in the "Combined Log | ||
| 141 | Format" is logged by default, plus a little extra. Your best bet | ||
| 142 | is to begin by accepting this default, then later customize the | ||
| 143 | log configuration based on your needs. The documentation of the | ||
| 144 | run-time directives includes a full explanation of what you can | ||
| 145 | log, including examples -- see section | ||
| 146 | <xref endterm="Sect.ConfigReference.title" | ||
| 147 | linkend="Sect.ConfigReference" /> | ||
| 148 | . | ||
| 149 | </para> | ||
| 150 | </section> | ||
| 151 | <section tocstyle="fragment"> | ||
| 152 | <title>Miscellaneous Notes</title> | ||
| 153 | <itemizedlist> | ||
| 154 | <listitem> | ||
| 155 | <para> | ||
| 156 | Note which directives go in the 'main server config' and | ||
| 157 | which directives apply to the 'virtual host config'. This is | ||
| 158 | made clear in the directive documentation. | ||
| 159 | </para> | ||
| 160 | </listitem> | ||
| 161 | <listitem> | ||
| 162 | <para> | ||
| 163 | The 'time_stamp' field is stored in an UNSIGNED INTEGER | ||
| 164 | format, in the standard unix "seconds since the epoch" | ||
| 165 | format. This is superior to storing the access time as a | ||
| 166 | string due to size requirements: an UNSIGNED INT requires 4 | ||
| 167 | bytes, whereas an Apache date string (e.g. | ||
| 168 | "18/Nov/2001:13:59:52 -0800") requires 26 bytes: those extra | ||
| 169 | 22 bytes become significant when multiplied by thousands of | ||
| 170 | accesses on a busy server. Besides, an INT type is far more | ||
| 171 | flexible for comparisons, etc. | ||
| 172 | </para> | ||
| 173 | <para> | ||
| 174 | In MySQL 3.21 and above you can easily convert this to a | ||
| 175 | human readable format using from_unixtime(), e.g.: | ||
| 176 | </para> | ||
| 177 | <programlisting>SELECT remote_host,request_uri,from_unixtime(time_stamp) | ||
| 178 | FROM access_log;</programlisting> | ||
| 179 | <para> | ||
| 180 | The enclosed perl program "make_combined_log.pl" extracts | ||
| 181 | your access log in a format that is completely compatible | ||
| 182 | with the Combined Log Format. You can then feed this to your | ||
| 183 | favorite web log analysis tool. | ||
| 184 | </para> | ||
| 185 | </listitem> | ||
| 186 | <listitem> | ||
| 187 | <para> | ||
| 188 | The table's string values can be CHAR or VARCHAR, at a | ||
| 189 | length of your choice. VARCHAR is superior because it | ||
| 190 | truncates long strings; CHAR types are fixed-length and will | ||
| 191 | be padded with spaces, resulting in waste. Just like the | ||
| 192 | time_stamp issue described above, that kind of space waste | ||
| 193 | multiplies over thousands of records. | ||
| 194 | </para> | ||
| 195 | </listitem> | ||
| 196 | <listitem> | ||
| 197 | <para> | ||
| 198 | Be careful not to go overboard setting fields to NOT NULL. | ||
| 199 | If a field is marked NOT NULL then it must contain data in | ||
| 200 | the INSERT statement, or the INSERT will fail. These | ||
| 201 | mysterious failures can be quite frustrating and difficult | ||
| 202 | to debug. | ||
| 203 | </para> | ||
| 204 | </listitem> | ||
| 205 | <listitem> | ||
| 206 | <para> | ||
| 207 | When Apache logs a numeric field, it uses a '-' character to | ||
| 208 | mean "not applicable," e.g. the number of bytes returned on | ||
| 209 | a 304 (unchanged) request. Since '-' is an illegal character | ||
| 210 | in an SQL numeric field, such fields are assigned the value | ||
| 211 | 0 instead of '-' which, of course, makes perfect sense | ||
| 212 | anyway. | ||
| 213 | </para> | ||
| 214 | </listitem> | ||
| 215 | </itemizedlist> | ||
| 216 | </section> | ||
| 217 | <section tocstyle="fragment"> | ||
| 218 | <title>Author / Maintainer</title> | ||
| 219 | <para> | ||
| 220 | The actual logging code was taken from the already existing flat | ||
| 221 | file text modules, so all that credit goes to the Apache | ||
| 222 | Software Foundation. | ||
| 223 | </para> | ||
| 224 | <para> | ||
| 225 | The MySQL routines and directives were added by Zeev Suraski | ||
| 226 | <bourbon@netvision.net.il>. | ||
| 227 | </para> | ||
| 228 | <para> | ||
| 229 | All changes from 1.06+ and the new documentation were added by | ||
| 230 | Chris Powell | ||
| 231 | <email>chris <at> grubbybaby <dot> com</email> | ||
| 232 | . It seems that the module had fallen into the "un-maintained" | ||
| 233 | category -- it had not been updated since 1998 -- so Chris | ||
| 234 | adopted it as the new maintainer. | ||
| 235 | </para> | ||
| 236 | <para> | ||
| 237 | In December of 2003, Edward Rudd | ||
| 238 | &EmailContact; | ||
| 239 | porting the module to Apache 2.0, cleaning up the code, | ||
| 240 | converting the documentation to DocBook, optimizing the main | ||
| 241 | logging loop, and added the much anticipated database | ||
| 242 | abstraction layer. | ||
| 243 | </para> | ||
| 244 | <para> | ||
| 245 | As of February 2004, Chris Powell handed over maintenance of the | ||
| 246 | module over to Edward Rudd. So you should contact Edward Rudd | ||
| 247 | about the module from now on. | ||
| 248 | </para> | ||
| 249 | </section> | ||
| 250 | <section id="Sect.MailingLists" tocstyle="fragment"> | ||
| 251 | <title id="Sect.MailingLists.title">Mailing Lists</title> | ||
| 252 | <para> | ||
| 253 | A general discussion and support mailing list is provided for | ||
| 254 | mod_log_sq at lists.outoforder.cc. To subscribe to the mailing | ||
| 255 | list send a blank e-mail to | ||
| 256 | mod_log_sql-subscribe@lists.outoforder.cc. The list archives can | ||
| 257 | be accessed via Gmane.org's mailng list gateway via any new | ||
| 258 | reader | ||
| 259 | <ulink | ||
| 260 | url="news://news.gmane.org/gmane.comp.apache.mod-log-sql"> | ||
| 261 | news://news.gmane.org/gmane.comp.apache.mod-log-sql | ||
| 262 | </ulink> | ||
| 263 | , or via a web browser at | ||
| 264 | <ulink | ||
| 265 | url="http://news.gmane.org/gmane.comp.apache.mod-log-sql"> | ||
| 266 | http://news.gmane.org/gmane.comp.apache.mod-log-sql | ||
| 267 | </ulink> | ||
| 268 | . | ||
| 269 | </para> | ||
| 270 | </section> | ||
| 271 | </section> | ||
| 272 | <section> | ||
| 273 | <title>Installation</title> | ||
| 274 | <section tocstyle="fragment"> | ||
| 275 | <title>Requirements</title> | ||
| 276 | <itemizedlist> | ||
| 277 | <listitem> | ||
| 278 | <para> | ||
| 279 | A compatible system. mod_log_sql was authored and tested on | ||
| 280 | systems based on Red Hat Linux (Red Hat, Mandrake), but the | ||
| 281 | module should easily adapt to any modern distribution. | ||
| 282 | mod_log_sql has also been ported successfully to Solaris and | ||
| 283 | FreeBSD. | ||
| 284 | </para> | ||
| 285 | </listitem> | ||
| 286 | <listitem> | ||
| 287 | <para> | ||
| 288 | Apache 1.3 or 2.0, 1.2 is no longer supported, but may still | ||
| 289 | compile. Ideally you should already have successfully | ||
| 290 | compiled Apache and understand the process, but this | ||
| 291 | document tries to make it simple for beginners. | ||
| 292 | </para> | ||
| 293 | </listitem> | ||
| 294 | <listitem> | ||
| 295 | <para> | ||
| 296 | The MySQL development headers. This package is called | ||
| 297 | different things on different distributions. For example, | ||
| 298 | Red Hat 6.x calls this RPM "MySQL-devel" whereas Mandrake | ||
| 299 | calls it "libmysql10-devel." Both MySQL 3.23.x and 4.x are | ||
| 300 | supported. | ||
| 301 | </para> | ||
| 302 | </listitem> | ||
| 303 | <listitem> | ||
| 304 | <para> | ||
| 305 | MySQL >= 3.23.15 configured, installed and running on | ||
| 306 | either localhost or an accessible networked machine. You | ||
| 307 | should already have a basic understanding of MySQL and how | ||
| 308 | it functions. | ||
| 309 | </para> | ||
| 310 | </listitem> | ||
| 311 | <listitem> | ||
| 312 | <para> | ||
| 313 | Optionally, if you want to be able to log SSL information | ||
| 314 | such as keysize or cipher, you need OpenSSL and mod_ssl | ||
| 315 | installed. | ||
| 316 | </para> | ||
| 317 | </listitem> | ||
| 318 | </itemizedlist> | ||
| 319 | </section> | ||
| 320 | <section> | ||
| 321 | <title>Compiling and Installing</title> | ||
| 322 | <orderedlist> | ||
| 323 | <listitem> | ||
| 324 | <para>Unpack the archive into a working directory.</para> | ||
| 325 | <programlisting>$ tar -xzf mod_log_sql-1.94.tar.gz | ||
| 326 | $ cd mod_log_sql-1.94</programlisting> | ||
| 327 | </listitem> | ||
| 328 | <listitem> | ||
| 329 | <para>run configure to configure the source directory.</para> | ||
| 330 | <programlisting>$ ./configure</programlisting> | ||
| 331 | <para> | ||
| 332 | The | ||
| 333 | <filename>configure</filename> | ||
| 334 | script should automatically detect all the required | ||
| 335 | libraries and program if the are installed in standard | ||
| 336 | locations.. If it returns an error, here is a description of | ||
| 337 | the arguments you can specify when you run | ||
| 338 | <filename>configure</filename> | ||
| 339 | . | ||
| 340 | </para> | ||
| 341 | <variablelist> | ||
| 342 | <varlistentry> | ||
| 343 | <term>--with-apxs=/usr/sbin/apxs</term> | ||
| 344 | <listitem> | ||
| 345 | <para> | ||
| 346 | This is the full path to the apxs binary, or the | ||
| 347 | directory which contains the program. This program is | ||
| 348 | part of the Apache 1.3 and 2.0 installation. | ||
| 349 | </para> | ||
| 350 | <para> | ||
| 351 | The default is to search | ||
| 352 | <filename>/usr/bin/apxs</filename> | ||
| 353 | and | ||
| 354 | <filename>/usr/sbin/apxs</filename> | ||
| 355 | . | ||
| 356 | </para> | ||
| 357 | <para> | ||
| 358 | Specifying a directory here will search | ||
| 359 | $directory/apxs, $directory/bin/apxs, and | ||
| 360 | $directory/sbin/apxs | ||
| 361 | </para> | ||
| 362 | <para> | ||
| 363 | If you have more than one version of Apache installed, | ||
| 364 | you need to specify the correct apxs binary for the | ||
| 365 | one you wish to compile for. | ||
| 366 | </para> | ||
| 367 | </listitem> | ||
| 368 | </varlistentry> | ||
| 369 | <varlistentry> | ||
| 370 | <term>--with-mysql=/path/to/mysql</term> | ||
| 371 | <listitem> | ||
| 372 | <para> | ||
| 373 | This is the directory to search for the | ||
| 374 | <filename>libmysqlclient</filename> | ||
| 375 | library and the | ||
| 376 | <application>MySQL</application> | ||
| 377 | headers. | ||
| 378 | </para> | ||
| 379 | <para> | ||
| 380 | The default is to search | ||
| 381 | <filename>/usr/include</filename> | ||
| 382 | , | ||
| 383 | <filename>/usr/include/mysql</filename> | ||
| 384 | , | ||
| 385 | <filename>/usr/local/include</filename> | ||
| 386 | , and | ||
| 387 | <filename>/usr/local/include/mysql</filename> | ||
| 388 | for | ||
| 389 | <application>MySQL</application> | ||
| 390 | headers.. And | ||
| 391 | <filename>/usr/lib</filename> | ||
| 392 | . | ||
| 393 | <filename>/usr/lib/mysql</filename> | ||
| 394 | , | ||
| 395 | <filename>/usr/local/lib</filename> | ||
| 396 | , and | ||
| 397 | <filename>/usr/local/lin/mysql</filename> | ||
| 398 | for the | ||
| 399 | <application>MySQL</application> | ||
| 400 | libraries. | ||
| 401 | </para> | ||
| 402 | <para> | ||
| 403 | Specifying this testargument will search | ||
| 404 | $directory/include and $directory/mysql for | ||
| 405 | <application>MySQL</application> | ||
| 406 | headers. And $directory/lib and $directory/lib/mysql | ||
| 407 | for | ||
| 408 | <application>MySQL</application> | ||
| 409 | libraries. | ||
| 410 | </para> | ||
| 411 | </listitem> | ||
| 412 | </varlistentry> | ||
| 413 | <varlistentry> | ||
| 414 | <term>--enable-ssl</term> | ||
| 415 | <listitem> | ||
| 416 | <para> | ||
| 417 | Specifying this argument will enable the search for | ||
| 418 | mod_ssl and SSL headers, and if found will enable | ||
| 419 | compilation of SSL support into mod_log_sql. SSL | ||
| 420 | support is compiled into a separate module that can be | ||
| 421 | loaded after the main mod_log_sql. | ||
| 422 | </para> | ||
| 423 | </listitem> | ||
| 424 | </varlistentry> | ||
| 425 | <varlistentry> | ||
| 426 | <term>--with-ssl-inc=/usr/include/openssl</term> | ||
| 427 | <listitem> | ||
| 428 | <para> | ||
| 429 | This is the path to the SSL toolkit header files that | ||
| 430 | were used to compile mod_ssl. If you want SSL support | ||
| 431 | you most likely need to specify this. | ||
| 432 | </para> | ||
| 433 | <para> | ||
| 434 | The default is to search | ||
| 435 | <filename>/usr/include</filename> | ||
| 436 | and | ||
| 437 | <filename>/usr/include/openssl</filename> | ||
| 438 | . | ||
| 439 | </para> | ||
| 440 | <para> | ||
| 441 | Specifying this argument will search that directory | ||
| 442 | for the SSL headers. | ||
| 443 | </para> | ||
| 444 | </listitem> | ||
| 445 | </varlistentry> | ||
| 446 | <varlistentry> | ||
| 447 | <term>--with-db-inc=/usr/include/db1</term> | ||
| 448 | <listitem> | ||
| 449 | <para> | ||
| 450 | This argument is only needed when compiling SSL | ||
| 451 | support for Apache 1.3, and needs to be the directory | ||
| 452 | which contains the ndbm.h header file. You can find | ||
| 453 | this by using | ||
| 454 | </para> | ||
| 455 | <programlisting>$ locate ndbm.h | ||
| 456 | /usr/include/db1/ndbm.h | ||
| 457 | /usr/include/gdbm/ndbm.h</programlisting> | ||
| 458 | <para> | ||
| 459 | As far as I can tell, there is no difference as to | ||
| 460 | which you specify, but it should be the one that you | ||
| 461 | compiled mod_ssl with. | ||
| 462 | </para> | ||
| 463 | <para> | ||
| 464 | The default is | ||
| 465 | <filename>/usr/include/db1</filename> | ||
| 466 | , which should work on most systems. | ||
| 467 | </para> | ||
| 468 | </listitem> | ||
| 469 | </varlistentry> | ||
| 470 | <varlistentry> | ||
| 471 | <term>--disable-apachetest</term> | ||
| 472 | <listitem> | ||
| 473 | <para> | ||
| 474 | This will disable the apache version test. However | ||
| 475 | there is a side affect if you specify this where I | ||
| 476 | will not be able to determine which version of Apache | ||
| 477 | you are compiling for. So don't specify this.. If you | ||
| 478 | are having troubles with the script detecting your | ||
| 479 | Apache version, then send a bug report along with your | ||
| 480 | system OS version and versions of related packages. | ||
| 481 | </para> | ||
| 482 | </listitem> | ||
| 483 | </varlistentry> | ||
| 484 | <varlistentry> | ||
| 485 | <term>--disable-mysqltest</term> | ||
| 486 | <listitem> | ||
| 487 | <para> | ||
| 488 | This will disable the MySQL compile test. Specify this | ||
| 489 | if for some reason the test fail but you know you have | ||
| 490 | specified the correct directories. If mod_los_sql also | ||
| 491 | fails to compile report a bug along with your system | ||
| 492 | OS version and versions of related packages. | ||
| 493 | </para> | ||
| 494 | </listitem> | ||
| 495 | </varlistentry> | ||
| 496 | </variablelist> | ||
| 497 | </listitem> | ||
| 498 | <listitem> | ||
| 499 | <para> | ||
| 500 | Now compile the module with GNU make. You may have to | ||
| 501 | specify gmake on some systems like FreeBSD. | ||
| 502 | </para> | ||
| 503 | <programlisting>$ gmake</programlisting> | ||
| 504 | </listitem> | ||
| 505 | <listitem> | ||
| 506 | <para> | ||
| 507 | If there were no errors, you can now install the module(s). | ||
| 508 | If you compiled as a non-root user you may need to switch | ||
| 509 | users with | ||
| 510 | <application>su</application> | ||
| 511 | or | ||
| 512 | <application>sudo</application> | ||
| 513 | . | ||
| 514 | </para> | ||
| 515 | <programlisting>$ su -c "gmake install" | ||
| 516 | Password:</programlisting> | ||
| 517 | </listitem> | ||
| 518 | <listitem> | ||
| 519 | <para> | ||
| 520 | Now edit your Apache configuration and load the modules. | ||
| 521 | </para> | ||
| 522 | <note> | ||
| 523 | <itemizedlist> | ||
| 524 | <listitem> | ||
| 525 | <para> | ||
| 526 | If you are loading the SSL logging module, you need to | ||
| 527 | make sure it is loaded after mod_ssl and mod_log_sql. | ||
| 528 | </para> | ||
| 529 | </listitem> | ||
| 530 | <listitem> | ||
| 531 | <para> | ||
| 532 | If you have previously used mod_log_sql version 1.18, | ||
| 533 | the name of the module has changed from sql_log_module | ||
| 534 | to log_sql_module (the first parameter to LoadModule) | ||
| 535 | </para> | ||
| 536 | </listitem> | ||
| 537 | <listitem> | ||
| 538 | <para> | ||
| 539 | If you are upgrading from any release earlier than | ||
| 540 | 1.97 you need to add an extra LoadModule directive to | ||
| 541 | load the database driver (ie mysql). | ||
| 542 | </para> | ||
| 543 | </listitem> | ||
| 544 | </itemizedlist> | ||
| 545 | </note> | ||
| 546 | <orderedlist> | ||
| 547 | <listitem> | ||
| 548 | <para> | ||
| 549 | Insert these lines to either the main | ||
| 550 | <filename>httpd.conf</filename> | ||
| 551 | or a file included via an include directive. | ||
| 552 | </para> | ||
| 553 | <programlisting>LoadModule log_sql_module modules/mod_log_sql.so | ||
| 554 | LoadModule log_sql_mysql_module modules/mod_log_sql_mysql.so | ||
| 555 | <IfModule mod_ssl.c> | ||
| 556 | LoadModule log_sql_ssl_module moduels/mod_log_sql_ssl.so | ||
| 557 | </IfModule></programlisting> | ||
| 558 | <note> | ||
| 559 | <para> | ||
| 560 | If you did not compile SSL support in mod_log_sql, do | ||
| 561 | not include the lines between the <IfModule> | ||
| 562 | directives. | ||
| 563 | </para> | ||
| 564 | </note> | ||
| 565 | </listitem> | ||
| 566 | <listitem> | ||
| 567 | <para> | ||
| 568 | If you are using Apache 1.3 you may need add these lines | ||
| 569 | later in the configuration. | ||
| 570 | </para> | ||
| 571 | <programlisting>AddModule mod_log_sql.c | ||
| 572 | AddModule mod_log_sql_mysql.c | ||
| 573 | <IfModule mod_ssl.c> | ||
| 574 | AddModule mod_log_sql_ssl.c | ||
| 575 | </IfModule></programlisting> | ||
| 576 | <note> | ||
| 577 | <para> | ||
| 578 | If you did not compile SSL support in mod_log_sql, do | ||
| 579 | not include the lines between the <IfModule> | ||
| 580 | directives. | ||
| 581 | </para> | ||
| 582 | </note> | ||
| 583 | </listitem> | ||
| 584 | </orderedlist> | ||
| 585 | </listitem> | ||
| 586 | </orderedlist> | ||
| 587 | </section> | ||
| 588 | </section> | ||
| 589 | <section id="Sect.Configuration"> | ||
| 590 | <title id="Sect.Configuration.title">Configuration</title> | ||
| 591 | <section id="Sect.Preperation"> | ||
| 592 | <title id="Sect.Preperation.title"> | ||
| 593 | Preparing MySQL for logging | ||
| 594 | </title> | ||
| 595 | <para> | ||
| 596 | You have to prepare the database to receive data from | ||
| 597 | <application>mod_log_sql</application> | ||
| 598 | , and set up run-time directives in | ||
| 599 | <filename>httpd.conf</filename> | ||
| 600 | to control how and what | ||
| 601 | <application>mod_log_sql</application> | ||
| 602 | logs. | ||
| 603 | </para> | ||
| 604 | <para> | ||
| 605 | This section will discuss how to get started with a basic | ||
| 606 | configuration. Full documentation of all available run-time | ||
| 607 | directives is available in section | ||
| 608 | <xref endterm="Sect.ConfigReference.title" | ||
| 609 | linkend="Sect.ConfigReference" /> | ||
| 610 | . | ||
| 611 | </para> | ||
| 612 | <orderedlist> | ||
| 613 | <listitem> | ||
| 614 | <para> | ||
| 615 | mod_log_sql can make its own tables on-the-fly, or you can | ||
| 616 | pre-make the tables by hand. The advantage of letting the | ||
| 617 | module make the tables is ease-of-use, but for raw | ||
| 618 | performance you will want to pre-make the tables in order to | ||
| 619 | save some overhead. In this basic setup we'll just let the | ||
| 620 | module create tables for us. | ||
| 621 | </para> | ||
| 622 | </listitem> | ||
| 623 | <listitem> | ||
| 624 | <para> | ||
| 625 | We still need to have a logging database created and ready, | ||
| 626 | so run the MySQL command line client and create a database: | ||
| 627 | </para> | ||
| 628 | <programlisting># mysql -uadmin -p | ||
| 629 | Enter password: | ||
| 630 | mysql> create database apachelogs;</programlisting> | ||
| 631 | </listitem> | ||
| 632 | <listitem id="Item.CreateTable"> | ||
| 633 | <para> | ||
| 634 | If you want to hand-create the tables, run the enclosed | ||
| 635 | 'create-tables' SQL script as follows ("create_tables.sql" | ||
| 636 | needs to be in your current working directory). | ||
| 637 | </para> | ||
| 638 | <programlisting>mysql> use apachelogs | ||
| 639 | Database changed | ||
| 640 | mysql> source create_tables.sql</programlisting> | ||
| 641 | </listitem> | ||
| 642 | <listitem> | ||
| 643 | <para> | ||
| 644 | Create a specific | ||
| 645 | <application>MySQL</application> | ||
| 646 | userid that | ||
| 647 | <application>httpd</application> | ||
| 648 | will use to authenticate and enter data. This userid need | ||
| 649 | not be an actual Unix user. It is a userid internal to | ||
| 650 | <application>MySQL</application> | ||
| 651 | with specific privileges. In the following example command, | ||
| 652 | "apachelogs" is the database, "loguser" is the userid to | ||
| 653 | create, "my.apachemachine.com" is the name of the Apache | ||
| 654 | machine, and "l0gger" is the password to assign. Choose | ||
| 655 | values that are different from these examples. | ||
| 656 | </para> | ||
| 657 | <programlisting>mysql> grant insert,create on apachelogs.* to loguser@my.apachemachine.com identified by 'l0gger';</programlisting> | ||
| 658 | </listitem> | ||
| 659 | <listitem> | ||
| 660 | <para> | ||
| 661 | You may be especially security-paranoid and want "loguser" | ||
| 662 | to not have "create" capability within the "apachelogs" | ||
| 663 | database. You can disable that privilege, but the cost is | ||
| 664 | that you will not be able to use the module's on-the-fly | ||
| 665 | table creation feature. If that cost is acceptable, | ||
| 666 | hand-create the tables as described in step | ||
| 667 | <xref linkend="Item.CreateTable" /> | ||
| 668 | and use the following GRANT statement instead of the one | ||
| 669 | above: | ||
| 670 | </para> | ||
| 671 | <programlisting>mysql> grant insert on apachelogs.* to loguser@my.apachemachine.com identified by 'l0gger';</programlisting> | ||
| 672 | </listitem> | ||
| 673 | <listitem id="Item.EnableLogging"> | ||
| 674 | <para> | ||
| 675 | Enable full logging of your | ||
| 676 | <application>MySQL</application> | ||
| 677 | daemon (at least temporarily for debugging purposes) if you | ||
| 678 | don't do this already. Edit /etc/my.cnf and add the | ||
| 679 | following line to your [mysqld] section: | ||
| 680 | </para> | ||
| 681 | <programlisting>log=/var/log/mysql-messages</programlisting> | ||
| 682 | <para> | ||
| 683 | Then restart | ||
| 684 | <application>MySQL</application> | ||
| 685 | </para> | ||
| 686 | <programlisting># /etc/rc.d/init.d/mysql restart</programlisting> | ||
| 687 | </listitem> | ||
| 688 | </orderedlist> | ||
| 689 | </section> | ||
| 690 | <section> | ||
| 691 | <title>A very basic logging setup in Apache</title> | ||
| 692 | <orderedlist> | ||
| 693 | <listitem> | ||
| 694 | <para> | ||
| 695 | Tell the module what database to use and the appropriate | ||
| 696 | authentication information. | ||
| 697 | </para> | ||
| 698 | <para> | ||
| 699 | So, edit httpd.conf and insert the following lines somewhere | ||
| 700 | after any LoadModule / AddModule statements. Make sure these | ||
| 701 | statements are "global," i.e. not inside any VirtualHost | ||
| 702 | stanza. You will also note that you are embedding a password | ||
| 703 | in the file. Therefore you are advised to "chmod 660 | ||
| 704 | httpd.conf" to prevent unauthorized regular users from | ||
| 705 | viewing your database user and password. | ||
| 706 | </para> | ||
| 707 | <para> | ||
| 708 | Use the | ||
| 709 | <application>MySQL</application> | ||
| 710 | database called "apachelogs" running on "dbmachine.foo.com". | ||
| 711 | Use username "loguser" and password "l0gg3r" to authenticate | ||
| 712 | to the database. Permit the module create tables for us. | ||
| 713 | </para> | ||
| 714 | <example> | ||
| 715 | <title>Basic Example</title> | ||
| 716 | <programlisting>LogSQLLoginInfo mysql://loguser:l0gg3r@dbmachine.foo.com/apachelogs | ||
| 717 | LogSQLCreateTables on</programlisting> | ||
| 718 | </example> | ||
| 719 | <para> | ||
| 720 | If your database resides on localhost instead of another | ||
| 721 | host, specify the MySQL server's socket file as follows: | ||
| 722 | </para> | ||
| 723 | <programlisting>LogSQLDBParam socketfile /your/path/to/mysql.sock</programlisting> | ||
| 724 | <para> | ||
| 725 | If your database is listening on a port other than 3306, | ||
| 726 | specify the correct TCP port as follows: | ||
| 727 | </para> | ||
| 728 | <programlisting>LogSQLDBParam port 1234</programlisting> | ||
| 729 | </listitem> | ||
| 730 | <listitem> | ||
| 731 | <para> | ||
| 732 | The actual logging is set up on a virtual-host-by-host | ||
| 733 | basis. So, skip down to the virtual host you want to set up. | ||
| 734 | Instruct this virtual host to log entries to the table | ||
| 735 | "access_log" by inserting a LogSQLTransferLogTable | ||
| 736 | directive. (The LogSQLTransferLogTable directive is the | ||
| 737 | minimum required to log -- other directives that you will | ||
| 738 | learn about later simply tune the module's behavior.) | ||
| 739 | </para> | ||
| 740 | <programlisting><VirtualHost 1.2.3.4> | ||
| 741 | [snip] | ||
| 742 | LogSQLTransferLogTable access_log | ||
| 743 | [snip] | ||
| 744 | </VirtualHost></programlisting> | ||
| 745 | </listitem> | ||
| 746 | <listitem> | ||
| 747 | <para>Restart apache.</para> | ||
| 748 | <programlisting># /etc/rc.d/init.d/httpd stop | ||
| 749 | # /etc/rc.d/init.d/httpd start</programlisting> | ||
| 750 | </listitem> | ||
| 751 | </orderedlist> | ||
| 752 | </section> | ||
| 753 | <section> | ||
| 754 | <title>Testing the basic setup</title> | ||
| 755 | <orderedlist> | ||
| 756 | <listitem> | ||
| 757 | <para> | ||
| 758 | Visit your web site in a browser to trigger some hits, then | ||
| 759 | confirm that the entries are being successfully logged: | ||
| 760 | </para> | ||
| 761 | <programlisting># mysql -hdbmachine.foo.com -umysqladmin -p -e "SELECT * FROM access_log" apachelogs | ||
| 762 | Enter password:</programlisting> | ||
| 763 | <para> | ||
| 764 | Several lines of output should follow, corresponding to your | ||
| 765 | hits on the site. You now have basic functionality. Don't | ||
| 766 | disable your regular Apache logs until you feel comfortable | ||
| 767 | that the database is behaving as you'd like and that things | ||
| 768 | are going well. If you do not see any entries in the | ||
| 769 | access_log, please consult section | ||
| 770 | <xref linkend="FAQ.NothingLogged" /> | ||
| 771 | of the FAQ on how to debug and fix the situation. | ||
| 772 | </para> | ||
| 773 | </listitem> | ||
| 774 | <listitem> | ||
| 775 | <para> | ||
| 776 | You can now activate the advanced features of mod_log_sql, | ||
| 777 | which are described in the next section. | ||
| 778 | </para> | ||
| 779 | </listitem> | ||
| 780 | </orderedlist> | ||
| 781 | </section> | ||
| 782 | <section> | ||
| 783 | <title>How to tune logging with run-time directives</title> | ||
| 784 | <section tocstyle="fragment"> | ||
| 785 | <title>Instructing the module what to log</title> | ||
| 786 | <para> | ||
| 787 | The most basic directive for the module is | ||
| 788 | LogSQLTransferLogFormat, which tells the module which | ||
| 789 | information to send to the database; logging to the database | ||
| 790 | will not take place without it. Place a | ||
| 791 | LogSQLTransferLogFormat directive in the VirtualHost stanza of | ||
| 792 | each virtual host that you want to activate. | ||
| 793 | </para> | ||
| 794 | <para> | ||
| 795 | After LogSQLTransferLogFormat you supply a string of | ||
| 796 | characters that tell the module what information to log. In | ||
| 797 | the configuration directive reference (section | ||
| 798 | <xref linkend="Conf.LogSQLTransferLogFormat" /> | ||
| 799 | ) there is a table which clearly defines all the possible | ||
| 800 | things to log. Let's say you want to log only the "request | ||
| 801 | time," the "remote host," and the "request"; you'd use: | ||
| 802 | </para> | ||
| 803 | <programlisting>LogSQLTransferLogFormat hUS</programlisting> | ||
| 804 | <para>But a more appropriate string to use is</para> | ||
| 805 | <programlisting>LogSQLTransferLogFormat AbHhmRSsTUuv</programlisting> | ||
| 806 | <para> | ||
| 807 | which logs all the information required to be compatible with | ||
| 808 | the Combined Log Format (CLF). | ||
| 809 | </para> | ||
| 810 | <para> | ||
| 811 | If you don't choose to log everything that is available, | ||
| 812 | that's fine. Fields in the unused columns in your table will | ||
| 813 | simply contain NULL. | ||
| 814 | </para> | ||
| 815 | <para> | ||
| 816 | Some of the LogSQLTransferLogFormat characters require a | ||
| 817 | little extra configuration: | ||
| 818 | </para> | ||
| 819 | <itemizedlist> | ||
| 820 | <listitem> | ||
| 821 | <para> | ||
| 822 | If you specify 'c' to indicate that you want to log the | ||
| 823 | cookie value, you must also tell the module which cookie | ||
| 824 | you mean by using LogSQLWhichCookie -- after all, there | ||
| 825 | could be many cookies associated with a given request. | ||
| 826 | Fail to specify LogSQLWhichCookie, and no cookie | ||
| 827 | information at all will be logged. | ||
| 828 | </para> | ||
| 829 | </listitem> | ||
| 830 | <listitem> | ||
| 831 | <para> | ||
| 832 | If you specify 'M' to indicate that you want to log the | ||
| 833 | machine ID, you must also tell the module this machine's | ||
| 834 | identity using the LogSQLMachineID directive. Fail to | ||
| 835 | specify LogSQLMachineID, and a simple '-' character will | ||
| 836 | be logged in the machine_id column. | ||
| 837 | </para> | ||
| 838 | </listitem> | ||
| 839 | </itemizedlist> | ||
| 840 | </section> | ||
| 841 | <section id="Sect.Ignore"> | ||
| 842 | <title id="Sect.Ignore.title"> | ||
| 843 | Instructing the module what NOT to log using filtering | ||
| 844 | directives | ||
| 845 | </title> | ||
| 846 | <para> | ||
| 847 | One "accept" and two "ignore" directives allow you to | ||
| 848 | fine-tune what the module should not log. These are very handy | ||
| 849 | for keeping your database as uncluttered as possible and | ||
| 850 | keeping your statistics free of unneeded numbers. Think of | ||
| 851 | each one as a gatekeeper. | ||
| 852 | </para> | ||
| 853 | <para> | ||
| 854 | <emphasis> | ||
| 855 | It is important to remember that each of these three | ||
| 856 | directives is purely optional. mod_log_sql's default is to | ||
| 857 | log everything. | ||
| 858 | </emphasis> | ||
| 859 | </para> | ||
| 860 | <para> | ||
| 861 | When a request comes in, the contents of LogSQLRequestAccept | ||
| 862 | are evaluated first. This optional, "blanket" directive lets | ||
| 863 | you specify that only certain things are to be accepted for | ||
| 864 | logging, and everything else discarded. Because it is | ||
| 865 | evaluated before LogSQLRequestIgnore and LogSQLRemhostIgnore | ||
| 866 | it can halt logging before those two filtering directives "get | ||
| 867 | their chance." | ||
| 868 | </para> | ||
| 869 | <para> | ||
| 870 | Once a request makes it past LogSQLRequestAccept, it still can | ||
| 871 | be excluded based on LogSQLRemhostIgnore and | ||
| 872 | LogSQLRequestIgnore. A good way to use LogSQLRemhostIgnore is | ||
| 873 | to prevent the module from logging the traffic that your | ||
| 874 | internal hosts generate. LogSQLRequestIgnore is great for | ||
| 875 | preventing things like requests for "favicon.ico" from | ||
| 876 | cluttering up your database, as well as excluding the various | ||
| 877 | requests that worms make, etc. | ||
| 878 | </para> | ||
| 879 | <para> | ||
| 880 | You can specify a series of strings after each directive. Do | ||
| 881 | not use any type of globbing or regular-expression syntax -- | ||
| 882 | each string is considered a match | ||
| 883 | <emphasis> | ||
| 884 | if it is a substring of the larger request or remote-host; | ||
| 885 | the comarison is case-sensitive | ||
| 886 | </emphasis> | ||
| 887 | . This means that "LogSQLRemhostIgnore micro" will ignore | ||
| 888 | requests from "microsoft.com," "microworld.net," | ||
| 889 | "mymicroscope.org," etc. "LogSQLRequestIgnore gif" will | ||
| 890 | instruct the module to ignore requests for "leftbar.gif," | ||
| 891 | "bluedot.gif" and even "giftwrap.jpg" -- but "RED.GIF" and | ||
| 892 | "Tree.Gif" would still get logged because of case sensitivity. | ||
| 893 | </para> | ||
| 894 | <para>A summary of the decision flow:</para> | ||
| 895 | <orderedlist> | ||
| 896 | <listitem> | ||
| 897 | <para> | ||
| 898 | If LogSQLRequestAccept exists and a request does not match | ||
| 899 | anything in that list, it is discarded. | ||
| 900 | </para> | ||
| 901 | </listitem> | ||
| 902 | <listitem> | ||
| 903 | <para> | ||
| 904 | If a request matches anything in the LogSQLRequestIgnore | ||
| 905 | list, it is discarded. | ||
| 906 | </para> | ||
| 907 | </listitem> | ||
| 908 | <listitem> | ||
| 909 | <para> | ||
| 910 | If a reqiest matches anything in the LogSQLRemhostIgnore | ||
| 911 | list, it is discarded. | ||
| 912 | </para> | ||
| 913 | </listitem> | ||
| 914 | <listitem> | ||
| 915 | <para>Otherwise the request is logged.</para> | ||
| 916 | </listitem> | ||
| 917 | </orderedlist> | ||
| 918 | <para> | ||
| 919 | This means that you can have a series of directives similar to | ||
| 920 | the following: | ||
| 921 | </para> | ||
| 922 | <programlisting>LogSQLRequestAccept .html .gif .jpg | ||
| 923 | LogSQLRequestIgnore statistics.html bluedot.jpg</programlisting> | ||
| 924 | <para> | ||
| 925 | So the first line instructs the module to only log files with | ||
| 926 | html, gif and jpg suffixes; requests for "formail.cgi" and | ||
| 927 | "shopping-cart.pl" will never be considered for logging. | ||
| 928 | ("LeftArrow.JPG" will also never be considered for logging -- | ||
| 929 | remember, the comparison is case sensitive.) The second line | ||
| 930 | prunes the list further -- you never want to log requests for | ||
| 931 | those two objects. | ||
| 932 | </para> | ||
| 933 | <note role="tip"> | ||
| 934 | <itemizedlist> | ||
| 935 | <listitem> | ||
| 936 | <para> | ||
| 937 | If you want to match all the hosts in your domain such | ||
| 938 | as "host1.corp.foo.com" and "server.dmz.foo.com", simply | ||
| 939 | specify: | ||
| 940 | </para> | ||
| 941 | <programlisting>LogSQLRemhostIgnore foo.com</programlisting> | ||
| 942 | </listitem> | ||
| 943 | <listitem> | ||
| 944 | <para> | ||
| 945 | A great way to catch the vast majority of worm-attack | ||
| 946 | requests and prevent them from being logged is to | ||
| 947 | specify: | ||
| 948 | </para> | ||
| 949 | <programlisting>LogSQLRequestIgnore root.exe cmd.exe default.ida</programlisting> | ||
| 950 | </listitem> | ||
| 951 | <listitem> | ||
| 952 | <para> | ||
| 953 | To prevent the logging of requests for common graphic | ||
| 954 | types, make sure to put a '.' before the suffix to avoid | ||
| 955 | matches that you didn't intend: | ||
| 956 | </para> | ||
| 957 | <programlisting>LogSQLRequestIgnore .gif .jpg</programlisting> | ||
| 958 | </listitem> | ||
| 959 | </itemizedlist> | ||
| 960 | </note> | ||
| 961 | </section> | ||
| 962 | </section> | ||
| 963 | <section> | ||
| 964 | <title>Advanced logging scenarios</title> | ||
| 965 | <section tocstyle="fragment"> | ||
| 966 | <title>Using the module in an ISP environment</title> | ||
| 967 | <para>mod_log_sql has three basic tiers of operation:</para> | ||
| 968 | <orderedlist> | ||
| 969 | <listitem> | ||
| 970 | <para> | ||
| 971 | The administrator creates all necessary tables by hand and | ||
| 972 | configures each Apache VirtualHost by hand. | ||
| 973 | (LogSQLCreateTables Off) | ||
| 974 | </para> | ||
| 975 | </listitem> | ||
| 976 | <listitem> | ||
| 977 | <para> | ||
| 978 | The module is permitted to create necessary tables | ||
| 979 | on-the-fly, but the administrator configures each Apache | ||
| 980 | VirtualHost by hand. (LogSQLCreateTables On) | ||
| 981 | </para> | ||
| 982 | </listitem> | ||
| 983 | <listitem> | ||
| 984 | <para> | ||
| 985 | The module is permitted to create all necessary tables and | ||
| 986 | to make intelligent, on-the-fly configuration of each | ||
| 987 | VirtualHost. (LogSQLMassVirtualHosting On) | ||
| 988 | </para> | ||
| 989 | </listitem> | ||
| 990 | </orderedlist> | ||
| 991 | <para> | ||
| 992 | Many users are happy to use the module in its most minimal | ||
| 993 | form: they hand-create any necessary tables (using | ||
| 994 | "create_tables.sql"), and they configure each VirtualHost by | ||
| 995 | hand to suit their needs. However, some administrators need | ||
| 996 | extra features due to a large and growing number of | ||
| 997 | VirtualHosts. The LogSQLMassVirtualHosting directive activates | ||
| 998 | module capabilities that make it far easier to manage an ISP | ||
| 999 | environment, or any situation characterized by a large and | ||
| 1000 | varying number of virtual servers. | ||
| 1001 | </para> | ||
| 1002 | <itemizedlist> | ||
| 1003 | <listitem> | ||
| 1004 | <para> | ||
| 1005 | the on-the-fly table creation feature is activated | ||
| 1006 | automatically | ||
| 1007 | </para> | ||
| 1008 | </listitem> | ||
| 1009 | <listitem> | ||
| 1010 | <para> | ||
| 1011 | the transfer log table name is dynamically set from the | ||
| 1012 | virtual host's name (example: a virtual host | ||
| 1013 | "www.grubbybaby.com" gets logged to table | ||
| 1014 | "access_www_grubbybaby_com") | ||
| 1015 | </para> | ||
| 1016 | </listitem> | ||
| 1017 | </itemizedlist> | ||
| 1018 | <para> | ||
| 1019 | There are numerous benefits. The admin will not need to create | ||
| 1020 | new tables for every new VirtualHost. (Although the admin will | ||
| 1021 | still need to drop the tables of virtual hosts that are | ||
| 1022 | removed.) The admin will not need to set | ||
| 1023 | LogSQLTransferLogTable for each virtual host -- it will be | ||
| 1024 | configured automatically based on the host's name. Because | ||
| 1025 | each virtual host will log to its own segregated table, data | ||
| 1026 | about one virtual server will segregate from others; an admin | ||
| 1027 | can grant users access to the tables they need, and they will | ||
| 1028 | be unable to view data about another user's virtual host. | ||
| 1029 | </para> | ||
| 1030 | <para> | ||
| 1031 | In an ISP scenario the admin is likely to have a cluster of | ||
| 1032 | many front-end webservers logging to a back-end database. | ||
| 1033 | mod_log_sql has a feature that permits analysis of how well | ||
| 1034 | the web servers are loadbalancing: the LogSQLMachineID | ||
| 1035 | directive. The administrator uses this directive to assign a | ||
| 1036 | unique identifier to each machine in the web cluster, e.g. | ||
| 1037 | "LogSQLMachineID web01," "LogSQLMachineID web02," etc. Used in | ||
| 1038 | conjunction with the 'M' character in LogSQLTransferLogFormat, | ||
| 1039 | each entry in the SQL log will include the machine ID of the | ||
| 1040 | machine that created the entry. This permits the administrator | ||
| 1041 | to count the entries made by each particular machine and | ||
| 1042 | thereby analyze the front-end loadbalancing algorithm. | ||
| 1043 | </para> | ||
| 1044 | </section> | ||
| 1045 | <section id="Sect.MultiTable"> | ||
| 1046 | <title id="Sect.MultiTable.title"> | ||
| 1047 | Logging many-to-one data in separate tables | ||
| 1048 | </title> | ||
| 1049 | <para> | ||
| 1050 | A given HTTP request can have a one-to-many relationship with | ||
| 1051 | certain kinds of data. For example, a single HTTP request can | ||
| 1052 | have 4 cookies, 3 headers and 5 "mod_gzip" notes associated | ||
| 1053 | with it. mod_log_sql is capable of logging these relationships | ||
| 1054 | due to the elegance of SQL relational data. | ||
| 1055 | </para> | ||
| 1056 | <para> | ||
| 1057 | You already have a single table containing access requests. | ||
| 1058 | One of the columns in that table is 'id' which is intended to | ||
| 1059 | contain the unique request ID supplied by the standard Apache | ||
| 1060 | module mod_unique_id -- all you need to do is compile in that | ||
| 1061 | module and employ the LogSQLTransferLogFormat character 'I'. | ||
| 1062 | Thereafter, each request gets a unique ID that can be thought | ||
| 1063 | of as a primary key within the database, useful for joining | ||
| 1064 | multiple tables. So let's envision several new tables: a notes | ||
| 1065 | table, a cookies table, and a table for inbound and outbound | ||
| 1066 | headers. | ||
| 1067 | </para> | ||
| 1068 | <table> | ||
| 1069 | <title><tblAcc>access_log</title> | ||
| 1070 | <tgroup cols="6"> | ||
| 1071 | <colspec colname="1" /> | ||
| 1072 | <colspec colname="2" /> | ||
| 1073 | <colspec colname="3" /> | ||
| 1074 | <colspec colname="4" /> | ||
| 1075 | <colspec colname="5" colwidth="40" /> | ||
| 1076 | <colspec colname="6" colwidth="70" /> | ||
| 1077 | <thead> | ||
| 1078 | <row> | ||
| 1079 | <entry colname="1">id</entry> | ||
| 1080 | <entry colname="2">remote_host</entry> | ||
| 1081 | <entry colname="3">request_uri</entry> | ||
| 1082 | <entry colname="4">time_stamp</entry> | ||
| 1083 | <entry colname="5">status</entry> | ||
| 1084 | <entry colname="6">bytes_sent</entry> | ||
| 1085 | </row> | ||
| 1086 | </thead> | ||
| 1087 | <tbody> | ||
| 1088 | <row> | ||
| 1089 | <entry colname="1">PPIDskBRH30AAGPtAsg</entry> | ||
| 1090 | <entry colname="2">zerberus.aiacs.net</entry> | ||
| 1091 | <entry colname="3">/mod_log_sql/index.html</entry> | ||
| 1092 | <entry colname="4">1022493617</entry> | ||
| 1093 | <entry colname="5">200</entry> | ||
| 1094 | <entry colname="6">2215</entry> | ||
| 1095 | </row> | ||
| 1096 | </tbody> | ||
| 1097 | </tgroup> | ||
| 1098 | </table> | ||
| 1099 | <table> | ||
| 1100 | <title><tblNotes>notes_log</title> | ||
| 1101 | <tgroup cols="3"> | ||
| 1102 | <colspec colname="1" /> | ||
| 1103 | <colspec colname="2" /> | ||
| 1104 | <colspec colname="3" colwidth="30" /> | ||
| 1105 | <thead> | ||
| 1106 | <row> | ||
| 1107 | <entry colname="1">id</entry> | ||
| 1108 | <entry colname="2">item</entry> | ||
| 1109 | <entry colname="3">val</entry> | ||
| 1110 | </row> | ||
| 1111 | </thead> | ||
| 1112 | <tbody> | ||
| 1113 | <row> | ||
| 1114 | <entry colname="1">PPIDskBRH30AAGPtAsg</entry> | ||
| 1115 | <entry colname="2">mod_gzip_result</entry> | ||
| 1116 | <entry colname="3">OK</entry> | ||
| 1117 | </row> | ||
| 1118 | <row> | ||
| 1119 | <entry colname="1">PPIDskBRH30AAGPtAsg</entry> | ||
| 1120 | <entry colname="2">mod_gzip_compression_ratio</entry> | ||
| 1121 | <entry colname="3">69</entry> | ||
| 1122 | </row> | ||
| 1123 | </tbody> | ||
| 1124 | </tgroup> | ||
| 1125 | </table> | ||
| 1126 | <table> | ||
| 1127 | <title><tblHdr>headers_log</title> | ||
| 1128 | <tgroup cols="3"> | ||
| 1129 | <colspec colname="1" colnum="1" /> | ||
| 1130 | <colspec colname="2" colnum="2" /> | ||
| 1131 | <colspec colname="3" colnum="3" /> | ||
| 1132 | <thead> | ||
| 1133 | <row> | ||
| 1134 | <entry colname="1">id</entry> | ||
| 1135 | <entry colname="2">item</entry> | ||
| 1136 | <entry colname="3">val</entry> | ||
| 1137 | </row> | ||
| 1138 | </thead> | ||
| 1139 | <tbody> | ||
| 1140 | <row> | ||
| 1141 | <entry colname="1">PPIDskBRH30AAGPtAsg</entry> | ||
| 1142 | <entry colname="2">Content-Type</entry> | ||
| 1143 | <entry colname="3">text/html</entry> | ||
| 1144 | </row> | ||
| 1145 | <row> | ||
| 1146 | <entry colname="1">PPIDskBRH30AAGPtAsg</entry> | ||
| 1147 | <entry colname="2">Accept-Encoding</entry> | ||
| 1148 | <entry colname="3">gzip, deflate</entry> | ||
| 1149 | </row> | ||
| 1150 | <row> | ||
| 1151 | <entry colname="1">PPIDskBRH30AAGPtAsg</entry> | ||
| 1152 | <entry colname="2">Expires</entry> | ||
| 1153 | <entry colname="3">Tue, 28 May 2002 10:00:18 GMT</entry> | ||
| 1154 | </row> | ||
| 1155 | <row> | ||
| 1156 | <entry colname="1">PPIDskBRH30AAGPtAsg</entry> | ||
| 1157 | <entry colname="2">Cache-Control</entry> | ||
| 1158 | <entry colname="3">max-age=86400</entry> | ||
| 1159 | </row> | ||
| 1160 | </tbody> | ||
| 1161 | </tgroup> | ||
| 1162 | </table> | ||
| 1163 | <para> | ||
| 1164 | We have a certain request, and its unique ID is | ||
| 1165 | "PPIDskBRH30AAGPtAsg". Within each separate table will be | ||
| 1166 | multiple entries with that request ID: several cookie entries, | ||
| 1167 | several header entries, etc. As you can see in tables | ||
| 1168 | [tblAcc], [tblNotes] and [tblHdr], you have a one-to-many | ||
| 1169 | relationship for request PPIDskBRH30AAGPtAsg: that one access | ||
| 1170 | has two associated notes and four associated headers. You can | ||
| 1171 | extract this data easily using the power of SQL's "select" | ||
| 1172 | statement and table joins. To see the notes associated with a | ||
| 1173 | particular request: | ||
| 1174 | </para> | ||
| 1175 | <programlisting>SELECT a.remote_host, a.request_uri, n.item, n.val | ||
| 1176 | FROM access_log a JOIN notes_log n ON a.id=n.id | ||
| 1177 | WHERE a.id='PPIDskBRH30AAGPtAsg';</programlisting> | ||
| 1178 | <table> | ||
| 1179 | <title>access_log joined to notes_log</title> | ||
| 1180 | <tgroup cols="4"> | ||
| 1181 | <colspec colname="1" /> | ||
| 1182 | <colspec colname="2" /> | ||
| 1183 | <colspec colname="3" /> | ||
| 1184 | <colspec colname="4" colwidth="30" /> | ||
| 1185 | <thead> | ||
| 1186 | <row> | ||
| 1187 | <entry colname="1">remote_host</entry> | ||
| 1188 | <entry colname="2">request_uri</entry> | ||
| 1189 | <entry colname="3">item</entry> | ||
| 1190 | <entry colname="4">val</entry> | ||
| 1191 | </row> | ||
| 1192 | </thead> | ||
| 1193 | <tbody> | ||
| 1194 | <row> | ||
| 1195 | <entry colname="1">zerberus.aiacs.net</entry> | ||
| 1196 | <entry colname="2">/mod_log_sql/index.html</entry> | ||
| 1197 | <entry colname="3">mod_gzip_result</entry> | ||
| 1198 | <entry colname="4">OK</entry> | ||
| 1199 | </row> | ||
| 1200 | <row> | ||
| 1201 | <entry colname="1">zerberus.aiacs.net</entry> | ||
| 1202 | <entry colname="2">/mod_log_sql/index.html</entry> | ||
| 1203 | <entry colname="3">mod_gzip_compression_ratio</entry> | ||
| 1204 | <entry colname="4">69</entry> | ||
| 1205 | </row> | ||
| 1206 | </tbody> | ||
| 1207 | </tgroup> | ||
| 1208 | </table> | ||
| 1209 | <para> | ||
| 1210 | Naturally you can craft similar statements for the outboud | ||
| 1211 | headers, inbound headers and cookies, all of which can live in | ||
| 1212 | separate tables. Your statements are limited in power only by | ||
| 1213 | your skill with SQL. | ||
| 1214 | </para> | ||
| 1215 | <para> | ||
| 1216 | In order to use this capability of mod_log_sql, you must do | ||
| 1217 | several things. | ||
| 1218 | </para> | ||
| 1219 | <itemizedlist> | ||
| 1220 | <listitem> | ||
| 1221 | <para> | ||
| 1222 | Compile mod_unique_id into Apache (statically or as a | ||
| 1223 | DSO). mod_log_sql employs the unique request ID that | ||
| 1224 | mod_unique_id provides in order to key between the | ||
| 1225 | separate tables. You can still log the data without | ||
| 1226 | mod_unqiue_id, but it will be completely uncorrelated and | ||
| 1227 | you will have no way to discern any meaning. | ||
| 1228 | </para> | ||
| 1229 | </listitem> | ||
| 1230 | <listitem> | ||
| 1231 | <para> | ||
| 1232 | Create the appropriate tables. This will be done for you | ||
| 1233 | if you permit mod_log_sql to create its own tables using | ||
| 1234 | LogSQLCreateTables On, or if you use the enclosed | ||
| 1235 | "create_tables.sql" script. | ||
| 1236 | </para> | ||
| 1237 | </listitem> | ||
| 1238 | <listitem> | ||
| 1239 | <para> | ||
| 1240 | Create a SQL index on the "id" column. Without this index, | ||
| 1241 | table joins will be deathly slow. I recommend you consult | ||
| 1242 | the MySQL documentation on the proper way to create a | ||
| 1243 | column index if you are not familiar with this operation. | ||
| 1244 | </para> | ||
| 1245 | </listitem> | ||
| 1246 | <listitem> | ||
| 1247 | <para> | ||
| 1248 | Within each appropriate VirtualHost stanza, use the | ||
| 1249 | LogSQLWhich* and LogSQL*LogTable directives to tell the | ||
| 1250 | module what and where to log the data. In the following | ||
| 1251 | example, I have overridden the name for the notes table | ||
| 1252 | whereas I have left the other table names at their | ||
| 1253 | defaults. I have then specified the cookies, headers and | ||
| 1254 | notes that interest me. (And as you can see, these | ||
| 1255 | directives do not require me to add any characters to | ||
| 1256 | LogSQLTransferLogTable.) | ||
| 1257 | </para> | ||
| 1258 | <programlisting><VirtualHost 216.231.36.128> | ||
| 1259 | (snip) | ||
| 1260 | LogSQLNotesLogTable notestable | ||
| 1261 | LogSQLWhichCookies bluecookie redcookie greencookie | ||
| 1262 | LogSQLWhichNotes mod_gzip_result mod_gzip_compression_ratio | ||
| 1263 | LogSQLWhichHeadersOut Expires Content-Type Cache-Control | ||
| 1264 | LogSQLWhichHeadersIn User-Agent Accept-Encoding Host | ||
| 1265 | (snip) | ||
| 1266 | </VirtualHost></programlisting> | ||
| 1267 | </listitem> | ||
| 1268 | </itemizedlist> | ||
| 1269 | </section> | ||
| 1270 | <section> | ||
| 1271 | <title>Using the same database for production and test</title> | ||
| 1272 | <para> | ||
| 1273 | Although sub-optimal, it is not uncommon to use the same | ||
| 1274 | back-end database for the "production" webservers as well as | ||
| 1275 | the "test" webservers (budgetary constraints, rack-space | ||
| 1276 | limits, etc.). Furthermore, an administrator in this situation | ||
| 1277 | may be unable to use LogSQLRemhostIgnore to exclude requests | ||
| 1278 | from the test servers -- perhaps the generated entries are | ||
| 1279 | genuinely useful for analytical or QA purposes, but their | ||
| 1280 | value after analysis is minimal. | ||
| 1281 | </para> | ||
| 1282 | <para> | ||
| 1283 | It is wasteful and potentially confusing to permit this | ||
| 1284 | internal test data to clutter the database, and a solution to | ||
| 1285 | the problem is the proper use of the LogSQLMachineID | ||
| 1286 | directive. Assume a scenario where the production webservers | ||
| 1287 | have IDs like "web01," "web02," and so on -- and the test | ||
| 1288 | webservers have IDs like "test01," "test02," etc. Because | ||
| 1289 | entries in the log database are distinguished by their source | ||
| 1290 | machine, an administrator may purge unneeded test data from | ||
| 1291 | the access log as follows: | ||
| 1292 | </para> | ||
| 1293 | <programlisting>DELETE FROM access_log WHERE machine_id like 'test%';</programlisting> | ||
| 1294 | </section> | ||
| 1295 | <section id="Sect.DelayedInsert"> | ||
| 1296 | <title id="Sect.DelayedInsert.title"> | ||
| 1297 | Optimizing for a busy database | ||
| 1298 | </title> | ||
| 1299 | <para> | ||
| 1300 | A busy MySQL database will have SELECT statements running | ||
| 1301 | concurrently with INSERT and UPDATE statements. A long-running | ||
| 1302 | SELECT can in certain circumstances block INSERTs and | ||
| 1303 | therefore block mod_log_sql. A workaround is to enable | ||
| 1304 | mod_log_sql for "delayed inserts," which are described as | ||
| 1305 | follows in the MySQL documentation. | ||
| 1306 | </para> | ||
| 1307 | <para> | ||
| 1308 | The DELAYED option for the INSERT statement is a | ||
| 1309 | MySQL-specific option that is very useful if you have clients | ||
| 1310 | that can't wait for the INSERT to complete. This is a common | ||
| 1311 | problem when you use MySQL for logging and you also | ||
| 1312 | periodically run SELECT and UPDATE statements that take a long | ||
| 1313 | time to complete. DELAYED was introduced in MySQL Version | ||
| 1314 | 3.22.15. It is a MySQL extension to ANSI SQL92. | ||
| 1315 | </para> | ||
| 1316 | <para> | ||
| 1317 | INSERT DELAYED only works with ISAM and MyISAM tables. Note | ||
| 1318 | that as MyISAM tables supports concurrent SELECT and INSERT, | ||
| 1319 | if there is no free blocks in the middle of the data file, you | ||
| 1320 | very seldom need to use INSERT DELAYED with MyISAM. | ||
| 1321 | </para> | ||
| 1322 | <para> | ||
| 1323 | When you use INSERT DELAYED, the client will get an OK at once | ||
| 1324 | and the row will be inserted when the table is not in use by | ||
| 1325 | any other thread. | ||
| 1326 | </para> | ||
| 1327 | <para> | ||
| 1328 | Another major benefit of using INSERT DELAYED is that inserts | ||
| 1329 | from many clients are bundled together and written in one | ||
| 1330 | block. This is much faster than doing many separate inserts. | ||
| 1331 | </para> | ||
| 1332 | <para>The general disadvantages of delayed inserts are</para> | ||
| 1333 | <orderedlist> | ||
| 1334 | <listitem> | ||
| 1335 | <para> | ||
| 1336 | The queued rows are only stored in memory until they are | ||
| 1337 | inserted into the table. If mysqld dies unexpectedly, any | ||
| 1338 | queued rows that were not written to disk are lost. | ||
| 1339 | </para> | ||
| 1340 | </listitem> | ||
| 1341 | <listitem> | ||
| 1342 | <para> | ||
| 1343 | There is additional overhead for the server to handle a | ||
| 1344 | separate thread for each table on which you use INSERT | ||
| 1345 | DELAYED. | ||
| 1346 | </para> | ||
| 1347 | </listitem> | ||
| 1348 | </orderedlist> | ||
| 1349 | <note role="warning"> | ||
| 1350 | <para> | ||
| 1351 | The MySQL documentation concludes, "This means that you | ||
| 1352 | should only use INSERT DELAYED when you are really sure you | ||
| 1353 | need it!" Furthermore, the current state of error return | ||
| 1354 | from a failed INSERT DELAYED seems to be in flux, and may | ||
| 1355 | behave in unpredictable ways between different MySQL | ||
| 1356 | versions. See FAQ entry | ||
| 1357 | <xref linkend="FAQ.DelayedInsert" /> | ||
| 1358 | -- you have been warned. | ||
| 1359 | </para> | ||
| 1360 | </note> | ||
| 1361 | <para> | ||
| 1362 | If you are experiencing issues which could be solved by | ||
| 1363 | delayed inserts, then set LogSqlDelayedInserts On in the | ||
| 1364 | <filename>httpd.conf</filename> | ||
| 1365 | . All regular INSERT statements are now INSERT DELAYED, and | ||
| 1366 | you should see no more blocking of the module. | ||
| 1367 | </para> | ||
| 1368 | </section> | ||
| 1369 | </section> | ||
| 1370 | <section id="Sect.ConfigReference"> | ||
| 1371 | <title id="Sect.ConfigReference.title"> | ||
| 1372 | Configuration Directive Reference | ||
| 1373 | </title> | ||
| 1374 | <para> | ||
| 1375 | It is imperative that you understand which directives are used | ||
| 1376 | only once in the main server config, and which are used inside | ||
| 1377 | VirtualHost stanzas and therefore multiple times within | ||
| 1378 | httpd.conf. The "context" listed with each entry informs you of | ||
| 1379 | this. | ||
| 1380 | </para> | ||
| 1381 | <section tocstyle="fragment"> | ||
| 1382 | <title>DataBase Configuration</title> | ||
| 1383 | <variablelist> | ||
| 1384 | <varlistentry> | ||
| 1385 | <term>LogSQLLoginInfo</term> | ||
| 1386 | <listitem> | ||
| 1387 | <cmdsynopsis> | ||
| 1388 | <command>LogSQLLoginInfo</command> | ||
| 1389 | <arg choice="req"> | ||
| 1390 | <replaceable>connection URI</replaceable> | ||
| 1391 | </arg> | ||
| 1392 | </cmdsynopsis> | ||
| 1393 | <simpara> | ||
| 1394 | Example: LogSQLLoginInfo | ||
| 1395 | mysql://logwriter:passw0rd@foobar.baz.com/Apache_log | ||
| 1396 | </simpara> | ||
| 1397 | <simpara>Context: main server config</simpara> | ||
| 1398 | <para> | ||
| 1399 | Defines the basic connection URI to connect to the | ||
| 1400 | database with. The format of the connection URI is | ||
| 1401 | </para> | ||
| 1402 | <simpara> | ||
| 1403 | driver://username[:password]@hostname[:port]/database | ||
| 1404 | </simpara> | ||
| 1405 | <variablelist> | ||
| 1406 | <varlistentry> | ||
| 1407 | <term>driver</term> | ||
| 1408 | <listitem> | ||
| 1409 | <simpara> | ||
| 1410 | The database driver to use (mysql, pgsql, etc..) | ||
| 1411 | </simpara> | ||
| 1412 | </listitem> | ||
| 1413 | </varlistentry> | ||
| 1414 | <varlistentry> | ||
| 1415 | <term>username</term> | ||
| 1416 | <listitem> | ||
| 1417 | <simpara> | ||
| 1418 | The database username to login with INSERT | ||
| 1419 | privileges on the logging table defined in | ||
| 1420 | LogSQLtransferLogTable. | ||
| 1421 | </simpara> | ||
| 1422 | </listitem> | ||
| 1423 | </varlistentry> | ||
| 1424 | <varlistentry> | ||
| 1425 | <term>password</term> | ||
| 1426 | <listitem> | ||
| 1427 | <simpara> | ||
| 1428 | The password to use for username, and can be | ||
| 1429 | omitted if there is no password. | ||
| 1430 | </simpara> | ||
| 1431 | </listitem> | ||
| 1432 | </varlistentry> | ||
| 1433 | <varlistentry> | ||
| 1434 | <term>hostname</term> | ||
| 1435 | <listitem> | ||
| 1436 | <simpara> | ||
| 1437 | The hostname or Ip address of the Database | ||
| 1438 | machine, ans is simple "localhost" if the database | ||
| 1439 | lives on the same machine as Apache. | ||
| 1440 | </simpara> | ||
| 1441 | </listitem> | ||
| 1442 | </varlistentry> | ||
| 1443 | <varlistentry> | ||
| 1444 | <term>port</term> | ||
| 1445 | <listitem> | ||
| 1446 | <simpara> | ||
| 1447 | Port on hostname to connect to the Database, if | ||
| 1448 | not specified use the default port for the | ||
| 1449 | database. | ||
| 1450 | </simpara> | ||
| 1451 | </listitem> | ||
| 1452 | </varlistentry> | ||
| 1453 | <varlistentry> | ||
| 1454 | <term>database</term> | ||
| 1455 | <listitem> | ||
| 1456 | <simpara> | ||
| 1457 | The database to connect to on the server. | ||
| 1458 | </simpara> | ||
| 1459 | </listitem> | ||
| 1460 | </varlistentry> | ||
| 1461 | </variablelist> | ||
| 1462 | <note> | ||
| 1463 | <para> | ||
| 1464 | This is defined only once in the | ||
| 1465 | <filename>httpd.conf</filename> | ||
| 1466 | file. | ||
| 1467 | </para> | ||
| 1468 | <para> | ||
| 1469 | This directive Must be defined for logging to be | ||
| 1470 | enabled. | ||
| 1471 | </para> | ||
| 1472 | </note> | ||
| 1473 | </listitem> | ||
| 1474 | </varlistentry> | ||
| 1475 | <varlistentry> | ||
| 1476 | <term>LogSQLDBParam</term> | ||
| 1477 | <listitem> | ||
| 1478 | <cmdsynopsis sepchar=" "> | ||
| 1479 | <command>LogSQLDBParam</command> | ||
| 1480 | <arg choice="req"> | ||
| 1481 | <replaceable>parameter-name</replaceable> | ||
| 1482 | </arg> | ||
| 1483 | <arg choice="req"> | ||
| 1484 | <replaceable>value</replaceable> | ||
| 1485 | </arg> | ||
| 1486 | </cmdsynopsis> | ||
| 1487 | <simpara> | ||
| 1488 | Example: LogSQLDBParam socketfile | ||
| 1489 | /var/lib/mysql/mysql.socket | ||
| 1490 | </simpara> | ||
| 1491 | <simpara>Context: main server config</simpara> | ||
| 1492 | <para> | ||
| 1493 | This is the new method of specifying Database connection | ||
| 1494 | credentials and settings. This is used to define | ||
| 1495 | database driver specific options. For a list of options | ||
| 1496 | read the documentation for each specific database | ||
| 1497 | driver. | ||
| 1498 | </para> | ||
| 1499 | <table> | ||
| 1500 | <title>MySQL Driver parameters</title> | ||
| 1501 | <tgroup cols="5"> | ||
| 1502 | <colspec colname="1" colnum="1" /> | ||
| 1503 | <colspec colname="2" colnum="2" /> | ||
| 1504 | <colspec colname="3" colnum="3" /> | ||
| 1505 | <thead> | ||
| 1506 | <row> | ||
| 1507 | <entry colname="1">Parameter</entry> | ||
| 1508 | <entry colname="2">Meaning</entry> | ||
| 1509 | <entry colname="3">Default</entry> | ||
| 1510 | </row> | ||
| 1511 | </thead> | ||
| 1512 | <tbody> | ||
| 1513 | <row> | ||
| 1514 | <entry colname="1">hostname</entry> | ||
| 1515 | <entry colname="2">MySQL Server hostname</entry> | ||
| 1516 | <entry colname="3">none (use LogSQLLoginInfo to set)</entry> | ||
| 1517 | </row> | ||
| 1518 | <row> | ||
| 1519 | <entry colname="1">username</entry> | ||
| 1520 | <entry colname="2">The username to log in with</entry> | ||
| 1521 | <entry colname="3">none (use LogSQLLoginInfo to set)</entry> | ||
| 1522 | </row> | ||
| 1523 | <row> | ||
| 1524 | <entry colname="1">password</entry> | ||
| 1525 | <entry colname="2">The password to use</entry> | ||
| 1526 | <entry colname="3">none (use LogSQLLoginInfo to set)</entry> | ||
| 1527 | </row> | ||
| 1528 | <row> | ||
| 1529 | <entry colname="1">database</entry> | ||
| 1530 | <entry colname="2">Which database to connect to</entry> | ||
| 1531 | <entry colname="3">none (use LogSQLLoginInfo to set)</entry> | ||
| 1532 | </row> | ||
| 1533 | <row> | ||
| 1534 | <entry colname="1">port</entry> | ||
| 1535 | <entry colname="2">The TCP port to connect to the MySQL server over</entry> | ||
| 1536 | <entry colname="3">3306 (use LogSQLLoginInfo to set)</entry> | ||
| 1537 | </row> | ||
| 1538 | <row> | ||
| 1539 | <entry colname="1">socketfile</entry> | ||
| 1540 | <entry colname="2">The MySQL Unix socket file to use</entry> | ||
| 1541 | <entry colname="3">none</entry> | ||
| 1542 | </row> | ||
| 1543 | <row> | ||
| 1544 | <entry colname="1">tabletype</entry> | ||
| 1545 | <entry colname="2">MySQL Table Engine to use</entry> | ||
| 1546 | <entry colname="3">MySQL server default</entry> | ||
| 1547 | </row> | ||
| 1548 | </tbody> | ||
| 1549 | </tgroup> | ||
| 1550 | </table> | ||
| 1551 | <note> | ||
| 1552 | <para> | ||
| 1553 | Each parameter-name may only be defined once. | ||
| 1554 | </para> | ||
| 1555 | </note> | ||
| 1556 | </listitem> | ||
| 1557 | </varlistentry> | ||
| 1558 | <varlistentry> | ||
| 1559 | <term>LogSQLCreateTables</term> | ||
| 1560 | <listitem> | ||
| 1561 | <cmdsynopsis sepchar=" "> | ||
| 1562 | <command>LogSQLCreateTables</command> | ||
| 1563 | <arg choice="req">flag</arg> | ||
| 1564 | </cmdsynopsis> | ||
| 1565 | <simpara>Example: LogSQLCreateTables On</simpara> | ||
| 1566 | <simpara>Default: Off</simpara> | ||
| 1567 | <simpara>Context: main server config</simpara> | ||
| 1568 | <para> | ||
| 1569 | mod_log_sql has the ability to create its tables | ||
| 1570 | on-the-fly. The advantage to this is convenience: you | ||
| 1571 | don't have to execute any SQL by hand to prepare the | ||
| 1572 | table. This is especially helpful for people with lots | ||
| 1573 | of virtual hosts (who should also see the | ||
| 1574 | LogSQLMassVirtualHosting directive). | ||
| 1575 | </para> | ||
| 1576 | <para> | ||
| 1577 | There is a slight disadvantage: if you wish to activate | ||
| 1578 | this feature, then the userid specified in | ||
| 1579 | LogSQLLoginInfo must have CREATE privileges on the | ||
| 1580 | database. In an absolutely paranoid, locked-down | ||
| 1581 | situation you may only want to grant your mod_log_sql | ||
| 1582 | user INSERT privileges on the database; in that | ||
| 1583 | situation you are unable to take advantage of | ||
| 1584 | LogSQLCreateTables. But most people -- even the very | ||
| 1585 | security-conscious -- will find that granting CREATE on | ||
| 1586 | the logging database is reasonable. | ||
| 1587 | </para> | ||
| 1588 | <note> | ||
| 1589 | <para> | ||
| 1590 | This is defined only once in the | ||
| 1591 | <filename>httpd.conf</filename> | ||
| 1592 | file. | ||
| 1593 | </para> | ||
| 1594 | </note> | ||
| 1595 | </listitem> | ||
| 1596 | </varlistentry> | ||
| 1597 | <varlistentry> | ||
| 1598 | <term>LogSQLForcePreserve</term> | ||
| 1599 | <listitem> | ||
| 1600 | <cmdsynopsis sepchar=" "> | ||
| 1601 | <command>LogSQLForcePreserve</command> | ||
| 1602 | <arg choice="req">flag</arg> | ||
| 1603 | </cmdsynopsis> | ||
| 1604 | <simpara>Example: LogForcePreserve On</simpara> | ||
| 1605 | <simpara>Default: Off</simpara> | ||
| 1606 | <simpara>Context: main server config</simpara> | ||
| 1607 | <para> | ||
| 1608 | You may need to perform debugging on your database and | ||
| 1609 | specifically want mod_log_sql to make no attempts to log | ||
| 1610 | to it. This directive instructs the module to send all | ||
| 1611 | its log entries directly to the preserve file and to | ||
| 1612 | make no database INSERT attempts. | ||
| 1613 | </para> | ||
| 1614 | <para> | ||
| 1615 | This is presumably a directive for temporary use only; | ||
| 1616 | it could be dangerous if you set it and forget it, as | ||
| 1617 | all your entries will simply pile up in the preserve | ||
| 1618 | file. | ||
| 1619 | </para> | ||
| 1620 | <note> | ||
| 1621 | <para> | ||
| 1622 | This is defined only once in the | ||
| 1623 | <filename>httpd.conf</filename> | ||
| 1624 | file. | ||
| 1625 | </para> | ||
| 1626 | </note> | ||
| 1627 | </listitem> | ||
| 1628 | </varlistentry> | ||
| 1629 | <varlistentry> | ||
| 1630 | <term>LogSQLDisablePreserve</term> | ||
| 1631 | <listitem> | ||
| 1632 | <cmdsynopsis> | ||
| 1633 | <command>LogSQLDisablePreserve</command> | ||
| 1634 | <arg choice="req">flag</arg> | ||
| 1635 | </cmdsynopsis> | ||
| 1636 | <simpara>Example: LogDisablePreserve On</simpara> | ||
| 1637 | <simpara>Default: Off</simpara> | ||
| 1638 | <simpara>Context; main server config</simpara> | ||
| 1639 | <para> | ||
| 1640 | This option can be enabled to completely disable the | ||
| 1641 | preserve file fail back. This may be useful for servers | ||
| 1642 | where the file-system is read-only. | ||
| 1643 | </para> | ||
| 1644 | <para> | ||
| 1645 | If the database is not available those log entries will | ||
| 1646 | be lost. | ||
| 1647 | </para> | ||
| 1648 | <note> | ||
| 1649 | <para> | ||
| 1650 | This is defined only once in the | ||
| 1651 | <filename>httpd.conf</filename> | ||
| 1652 | file. | ||
| 1653 | </para> | ||
| 1654 | </note> | ||
| 1655 | </listitem> | ||
| 1656 | </varlistentry> | ||
| 1657 | <varlistentry> | ||
| 1658 | <term>LogSQLMachineID</term> | ||
| 1659 | <listitem> | ||
| 1660 | <cmdsynopsis sepchar=" "> | ||
| 1661 | <command>LogSQLMachineID</command> | ||
| 1662 | <arg choice="req">machineID</arg> | ||
| 1663 | </cmdsynopsis> | ||
| 1664 | <simpara>Example: LogSQLMachineID web01</simpara> | ||
| 1665 | <simpara>Context: main server config</simpara> | ||
| 1666 | <para> | ||
| 1667 | If you have a farm of webservers then you may wish to | ||
| 1668 | know which particular machine made each entry; this is | ||
| 1669 | useful for analyzing your load-balancing methodology. | ||
| 1670 | LogSQLMachineID permits you to distinguish each | ||
| 1671 | machine's entries if you assign each machine its own | ||
| 1672 | LogSQLMachineID: for example, the first webserver gets | ||
| 1673 | ``LogSQLMachineID web01,'' the second gets | ||
| 1674 | ``LogSQLMachineID web02,'' etc. | ||
| 1675 | </para> | ||
| 1676 | <note> | ||
| 1677 | <para> | ||
| 1678 | This is defined only once in the | ||
| 1679 | <filename>httpd.conf</filename> | ||
| 1680 | file. | ||
| 1681 | </para> | ||
| 1682 | </note> | ||
| 1683 | </listitem> | ||
| 1684 | </varlistentry> | ||
| 1685 | <varlistentry> | ||
| 1686 | <term>LogSQlPreserveFile</term> | ||
| 1687 | <listitem> | ||
| 1688 | <cmdsynopsis sepchar=" "> | ||
| 1689 | <command>LogSQLPreserveFile</command> | ||
| 1690 | <arg choice="req"> | ||
| 1691 | <replaceable>filename</replaceable> | ||
| 1692 | </arg> | ||
| 1693 | </cmdsynopsis> | ||
| 1694 | <simpara> | ||
| 1695 | Example: LogSQLPreserveFile offline-preserve | ||
| 1696 | </simpara> | ||
| 1697 | <simpara>Default: /tmp/sql-preserve</simpara> | ||
| 1698 | <simpara>Context: virtual host</simpara> | ||
| 1699 | <para> | ||
| 1700 | mod_log_sql writes queries to this local preserve file | ||
| 1701 | in the event that it cannot reach the database, and thus | ||
| 1702 | ensures that your high-availability web frontend does | ||
| 1703 | not lose logs during a temporary database outage. This | ||
| 1704 | could happen for a number of reasons: the database goes | ||
| 1705 | offline, the network breaks, etc. You will not lose | ||
| 1706 | entries since the module has this backup. The file | ||
| 1707 | consists of a series of SQL statements that can be | ||
| 1708 | imported into your database at your convenience; | ||
| 1709 | furthermore, because the SQL queries contain the access | ||
| 1710 | timestamps you do not need to worry about out-of-order | ||
| 1711 | data after the import, which is done in a simple manner: | ||
| 1712 | </para> | ||
| 1713 | <programlisting format="linespecific"># mysql -uadminuser -p mydbname < /tmp/sql-preserve</programlisting> | ||
| 1714 | <para> | ||
| 1715 | If you do not define LogSQLPreserveFile then all virtual | ||
| 1716 | servers will log to the same default preserve file ( | ||
| 1717 | <filename>/tmp/sql-preserve</filename> | ||
| 1718 | ). You can redefine this on a virtual-host basis in | ||
| 1719 | order to segregate your preserve files if you desire. | ||
| 1720 | Note that segregation is not usually necessary, as the | ||
| 1721 | SQL statements that are written to the preserve file | ||
| 1722 | already distinguish between different virtual hosts if | ||
| 1723 | you include the 'v' character in your | ||
| 1724 | LogSQLTransferLogFormat directive. It is only necessary | ||
| 1725 | to segregate preserve-files by virualhost if you also | ||
| 1726 | segregate access logs by virtualhost. | ||
| 1727 | </para> | ||
| 1728 | <para> | ||
| 1729 | The module will log to Apache's ErrorLog when it notices | ||
| 1730 | a database outage, and upon database return. You will | ||
| 1731 | therefore know when the preserve file is being used, | ||
| 1732 | although it is your responsibility to import the file. | ||
| 1733 | </para> | ||
| 1734 | <para> | ||
| 1735 | The file does not need to be created in advance. It is | ||
| 1736 | safe to remove or rename the file without interrupting | ||
| 1737 | Apache, as the module closes the filehandle immediately | ||
| 1738 | after completing the write. The file is created with the | ||
| 1739 | user & group ID of the running Apache process (e.g. | ||
| 1740 | 'nobody' on many Linux distributions). | ||
| 1741 | </para> | ||
| 1742 | </listitem> | ||
| 1743 | </varlistentry> | ||
| 1744 | </variablelist> | ||
| 1745 | </section> | ||
| 1746 | <section> | ||
| 1747 | <title>Table Names</title> | ||
| 1748 | <variablelist> | ||
| 1749 | <varlistentry> | ||
| 1750 | <term>LogSQLTransferLogTable</term> | ||
| 1751 | <listitem> | ||
| 1752 | <cmdsynopsis sepchar=" "> | ||
| 1753 | <command>LogSQLTransferLogTable</command> | ||
| 1754 | <arg choice="req"> | ||
| 1755 | <replaceable>table-name</replaceable> | ||
| 1756 | </arg> | ||
| 1757 | </cmdsynopsis> | ||
| 1758 | <simpara> | ||
| 1759 | Example: LogSQLTransferLogTable access_log_table | ||
| 1760 | </simpara> | ||
| 1761 | <simpara>Context: virtual host</simpara> | ||
| 1762 | <para> | ||
| 1763 | Defines which table is used for logging of Apache's | ||
| 1764 | transfers; this is analogous to Apache's TransferLog | ||
| 1765 | directive. table-name must be a valid table within the | ||
| 1766 | database defined in the LogSQLLoginInfo connection URI. | ||
| 1767 | </para> | ||
| 1768 | <para> | ||
| 1769 | This directive is | ||
| 1770 | <emphasis>not</emphasis> | ||
| 1771 | necessary if you declare LogSQLMassVirtualHosting On, | ||
| 1772 | since that directive activates dynamically-named tables. | ||
| 1773 | If you attempt to use LogSqlTransferlogTable at the same | ||
| 1774 | time a warning will be logged and it will be ignored, | ||
| 1775 | since LogSQLMassVirtualHosting takes priority. | ||
| 1776 | </para> | ||
| 1777 | <note> | ||
| 1778 | <para> | ||
| 1779 | Requires unless LogSQLMassVirtualHosting is set to On | ||
| 1780 | </para> | ||
| 1781 | </note> | ||
| 1782 | </listitem> | ||
| 1783 | </varlistentry> | ||
| 1784 | <varlistentry> | ||
| 1785 | <term>LogSQLCookieLogTable</term> | ||
| 1786 | <listitem> | ||
| 1787 | <cmdsynopsis sepchar=" "> | ||
| 1788 | <command>LogSQLCookieLogTable</command> | ||
| 1789 | <arg choice="req"> | ||
| 1790 | <replaceable></replaceable> | ||
| 1791 | table-name | ||
| 1792 | </arg> | ||
| 1793 | </cmdsynopsis> | ||
| 1794 | <simpara> | ||
| 1795 | Example: LogSQLCookieLogTable cookie_log | ||
| 1796 | </simpara> | ||
| 1797 | <simpara>Default: cookies</simpara> | ||
| 1798 | <simpara>Context: virtual host</simpara> | ||
| 1799 | <para> | ||
| 1800 | Defines which table is used for logging of cookies. | ||
| 1801 | Working in conjunction with LogSQLWhichCookies, you can | ||
| 1802 | log many of each request's associated cookies to a | ||
| 1803 | separate table. For meaningful data retrieval the cookie | ||
| 1804 | table is keyed to the access table by the unique request | ||
| 1805 | ID supplied by the standard Apache module mod_unique_id. | ||
| 1806 | </para> | ||
| 1807 | <note> | ||
| 1808 | <para> | ||
| 1809 | You must create the table (see create-tables.sql, | ||
| 1810 | included in the package), or LogSQLCreateTables must | ||
| 1811 | be set to "on". | ||
| 1812 | </para> | ||
| 1813 | </note> | ||
| 1814 | </listitem> | ||
| 1815 | </varlistentry> | ||
| 1816 | <varlistentry> | ||
| 1817 | <term>LogSQLHeadersInLogTable</term> | ||
| 1818 | <listitem> | ||
| 1819 | <cmdsynopsis sepchar=" "> | ||
| 1820 | <command>LogSQLHeadersInLogTable</command> | ||
| 1821 | <arg choice="req"> | ||
| 1822 | <replaceable>table-name</replaceable> | ||
| 1823 | </arg> | ||
| 1824 | </cmdsynopsis> | ||
| 1825 | <simpara> | ||
| 1826 | Example: LogSQLHeadersInLogTable headers | ||
| 1827 | </simpara> | ||
| 1828 | <simpara>Default: headers_in</simpara> | ||
| 1829 | <simpara>Context: virtual host</simpara> | ||
| 1830 | <para> | ||
| 1831 | Defines which table is used for logging of inbound | ||
| 1832 | headers. Working in conjunction with | ||
| 1833 | LogSQLWhichHeadersIn, you can log many of each request's | ||
| 1834 | associated headers to a separate table. For meaningful | ||
| 1835 | data retrieval the headers table is keyed to the access | ||
| 1836 | table by the unique request ID supplied by the standard | ||
| 1837 | Apache module mod_unique_id. | ||
| 1838 | </para> | ||
| 1839 | <note> | ||
| 1840 | <para> | ||
| 1841 | Note that you must create the table (see | ||
| 1842 | create-tables.sql, included in the package), or | ||
| 1843 | LogSQLCreateTables must be set to "on". | ||
| 1844 | </para> | ||
| 1845 | </note> | ||
| 1846 | </listitem> | ||
| 1847 | </varlistentry> | ||
| 1848 | <varlistentry> | ||
| 1849 | <term>LogSQLHeadersOutLogTable</term> | ||
| 1850 | <listitem> | ||
| 1851 | <cmdsynopsis sepchar=" "> | ||
| 1852 | <command>LogSQLHeadersOutLogTable</command> | ||
| 1853 | <arg choice="req"> | ||
| 1854 | <replaceable>table-name</replaceable> | ||
| 1855 | </arg> | ||
| 1856 | </cmdsynopsis> | ||
| 1857 | <simpara> | ||
| 1858 | Example: LogSQLHeadersOutLogTable headers | ||
| 1859 | </simpara> | ||
| 1860 | <simpara>Default: headers_out</simpara> | ||
| 1861 | <simpara>Context: virtual host</simpara> | ||
| 1862 | <para> | ||
| 1863 | Defines which table is used for logging of outbound | ||
| 1864 | headers. Working in conjunction with | ||
| 1865 | LogSQLWhichHeadersOut, you can log many of each | ||
| 1866 | request's associated headers to a separate table. For | ||
| 1867 | meaningful data retrieval the headers table is keyed to | ||
| 1868 | the access table by the unique request ID supplied by | ||
| 1869 | the standard Apache module mod_unique_id. | ||
| 1870 | </para> | ||
| 1871 | <note> | ||
| 1872 | <para> | ||
| 1873 | Note that you must create the table (see | ||
| 1874 | create-tables.sql, included in the package), or | ||
| 1875 | LogSQLCreateTables must be set to "on". | ||
| 1876 | </para> | ||
| 1877 | </note> | ||
| 1878 | </listitem> | ||
| 1879 | </varlistentry> | ||
| 1880 | <varlistentry> | ||
| 1881 | <term>LogSQLNotesLogTable</term> | ||
| 1882 | <listitem> | ||
| 1883 | <cmdsynopsis sepchar=" "> | ||
| 1884 | <command>LogSQLNotesLogTable</command> | ||
| 1885 | <arg choice="req"> | ||
| 1886 | <replaceable>table-name</replaceable> | ||
| 1887 | </arg> | ||
| 1888 | </cmdsynopsis> | ||
| 1889 | <simpara>Example: LogSQLNotesLogTable notes-log</simpara> | ||
| 1890 | <simpara>Default: notes</simpara> | ||
| 1891 | <simpara>Context: virtual_host</simpara> | ||
| 1892 | <para> | ||
| 1893 | Defines which table is used for logging of notes. | ||
| 1894 | Working in conjunction with LogSQLWhichNotes, you can | ||
| 1895 | log many of each request's associated notes to a | ||
| 1896 | separate table. For meaningful data retrieval the notes | ||
| 1897 | table is keyed to the access table by the unique request | ||
| 1898 | ID supplied by the standard Apache module mod_unique_id. | ||
| 1899 | </para> | ||
| 1900 | <note> | ||
| 1901 | <para> | ||
| 1902 | This table must be created (see create-tables.sql | ||
| 1903 | included in the package), or LogSQLCreateTables must | ||
| 1904 | be set to 'On'. | ||
| 1905 | </para> | ||
| 1906 | </note> | ||
| 1907 | </listitem> | ||
| 1908 | </varlistentry> | ||
| 1909 | <varlistentry> | ||
| 1910 | <term>LogSQLMassVirtualHosting</term> | ||
| 1911 | <listitem> | ||
| 1912 | <cmdsynopsis sepchar=" "> | ||
| 1913 | <command>LogSQLMassVirtualHosting</command> | ||
| 1914 | <arg choice="req">flag</arg> | ||
| 1915 | </cmdsynopsis> | ||
| 1916 | <simpara>Example: LogSQLMassVirtualHosting On</simpara> | ||
| 1917 | <simpara>Default: Off</simpara> | ||
| 1918 | <simpara>Context: main server config</simpara> | ||
| 1919 | <para> | ||
| 1920 | If you administer a site hosting many, many virtual | ||
| 1921 | hosts then this option will appeal to you. If you turn | ||
| 1922 | on LogSQLMassVirtualHosting then several things happen: | ||
| 1923 | </para> | ||
| 1924 | <itemizedlist> | ||
| 1925 | <listitem> | ||
| 1926 | <para> | ||
| 1927 | the on-the-fly table creation feature is activated | ||
| 1928 | automatically | ||
| 1929 | </para> | ||
| 1930 | </listitem> | ||
| 1931 | <listitem> | ||
| 1932 | <para> | ||
| 1933 | the transfer log table name is dynamically set from | ||
| 1934 | the virtual host's name after stripping out | ||
| 1935 | SQL-unfriendly characters (example: a virtual host | ||
| 1936 | www.grubbybaby.com gets logged to table | ||
| 1937 | access_www_grubbybaby_com) | ||
| 1938 | </para> | ||
| 1939 | </listitem> | ||
| 1940 | <listitem> | ||
| 1941 | <para> | ||
| 1942 | which, in turn, means that each virtual host logs to | ||
| 1943 | its own segregated table. Because there is no data | ||
| 1944 | shared between virtual servers you can grant your | ||
| 1945 | users access to the tables they need; they will be | ||
| 1946 | unable to view others' data. | ||
| 1947 | </para> | ||
| 1948 | </listitem> | ||
| 1949 | </itemizedlist> | ||
| 1950 | <para> | ||
| 1951 | This is a huge boost in convenience for sites with many | ||
| 1952 | virtual servers. Activating LogSQLMassVirtualHosting | ||
| 1953 | obviates the need to create every virtual server's table | ||
| 1954 | and provides more granular security possibilities. | ||
| 1955 | </para> | ||
| 1956 | <note> | ||
| 1957 | <para> | ||
| 1958 | This is defined only once in the | ||
| 1959 | <filename>httpd.conf</filename> | ||
| 1960 | file. | ||
| 1961 | </para> | ||
| 1962 | </note> | ||
| 1963 | </listitem> | ||
| 1964 | </varlistentry> | ||
| 1965 | </variablelist> | ||
| 1966 | </section> | ||
| 1967 | <section> | ||
| 1968 | <title>Configuring What Is logged</title> | ||
| 1969 | <variablelist> | ||
| 1970 | <varlistentry id="Conf.LogSQLTransferLogFormat"> | ||
| 1971 | <term>LogSQLTransferLogFormat</term> | ||
| 1972 | <listitem> | ||
| 1973 | <cmdsynopsis sepchar=" "> | ||
| 1974 | <command>LogSQLTransferLogFormat</command> | ||
| 1975 | <arg choice="req"> | ||
| 1976 | <replaceable>format-string</replaceable> | ||
| 1977 | </arg> | ||
| 1978 | </cmdsynopsis> | ||
| 1979 | <simpara>Example: LogSQLTransferLogFormat huSUTv</simpara> | ||
| 1980 | <simpara>Default: AbHhmRSsTUuv</simpara> | ||
| 1981 | <simpara>Context: virtual host</simpara> | ||
| 1982 | <para> | ||
| 1983 | Each character in the format-string defines an attribute | ||
| 1984 | of the request that you wish to log. The default logs | ||
| 1985 | the information required to create Combined Log Format | ||
| 1986 | logs, plus several extras. Here is the full list of | ||
| 1987 | allowable keys, which sometimes resemble their Apache | ||
| 1988 | counterparts, but do not always: | ||
| 1989 | </para> | ||
| 1990 | <table> | ||
| 1991 | <title>Core LogFormat parameters</title> | ||
| 1992 | <tgroup cols="5"> | ||
| 1993 | <colspec colname="1" colnum="1" /> | ||
| 1994 | <colspec colname="2" colnum="2" /> | ||
| 1995 | <colspec colname="3" colnum="3" /> | ||
| 1996 | <colspec colname="4" colnum="4" /> | ||
| 1997 | <colspec colname="5" colnum="5" /> | ||
| 1998 | <thead> | ||
| 1999 | <row> | ||
| 2000 | <entry colname="1">Symbol</entry> | ||
| 2001 | <entry colname="2">Meaning</entry> | ||
| 2002 | <entry colname="3">DB Field</entry> | ||
| 2003 | <entry colname="4">Data Type</entry> | ||
| 2004 | <entry colname="5">Example</entry> | ||
| 2005 | </row> | ||
| 2006 | </thead> | ||
| 2007 | <tbody> | ||
| 2008 | <row> | ||
| 2009 | <entry colname="1">A</entry> | ||
| 2010 | <entry colname="2">User Agent</entry> | ||
| 2011 | <entry colname="3">agent</entry> | ||
| 2012 | <entry colname="4">varchar(255)</entry> | ||
| 2013 | <entry colname="5"> | ||
| 2014 | Mozilla/4.0 (compat; MSIE 6.0; Windows) | ||
| 2015 | </entry> | ||
| 2016 | </row> | ||
| 2017 | <row> | ||
| 2018 | <entry colname="1">a</entry> | ||
| 2019 | <entry colname="2">CGi request arguments</entry> | ||
| 2020 | <entry colname="3">request_args</entry> | ||
| 2021 | <entry colname="4">varchar(255)</entry> | ||
| 2022 | <entry colname="5"> | ||
| 2023 | user=Smith&cart=1231&item=532 | ||
| 2024 | </entry> | ||
| 2025 | </row> | ||
| 2026 | <row> | ||
| 2027 | <entry colname="1">b</entry> | ||
| 2028 | <entry colname="2">Bytes transfered</entry> | ||
| 2029 | <entry colname="3">bytes_sent</entry> | ||
| 2030 | <entry colname="4">int unsigned</entry> | ||
| 2031 | <entry colname="5">32561</entry> | ||
| 2032 | </row> | ||
| 2033 | <row> | ||
| 2034 | <entry colname="1"> | ||
| 2035 | c | ||
| 2036 | <xref linkend="Foot.LogCookie" xrefstyle="footer" /> | ||
| 2037 | </entry> | ||
| 2038 | <entry colname="2">Text of cookie</entry> | ||
| 2039 | <entry colname="3">cookie</entry> | ||
| 2040 | <entry colname="4">varchar(255)</entry> | ||
| 2041 | <entry colname="5"> | ||
| 2042 | Apache=sdyn.fooonline.net 1300102700823 | ||
| 2043 | </entry> | ||
| 2044 | </row> | ||
| 2045 | <row> | ||
| 2046 | <entry>f</entry> | ||
| 2047 | <entry>Local filename requested</entry> | ||
| 2048 | <entry>request_file</entry> | ||
| 2049 | <entry>varchar(255)</entry> | ||
| 2050 | <entry>/var/www/html/books-cycroad.html</entry> | ||
| 2051 | </row> | ||
| 2052 | <row> | ||
| 2053 | <entry>H</entry> | ||
| 2054 | <entry>HTTP request_protocol</entry> | ||
| 2055 | <entry>request_protocol</entry> | ||
| 2056 | <entry>varchar(10)</entry> | ||
| 2057 | <entry>HTTP/1.1</entry> | ||
| 2058 | </row> | ||
| 2059 | <row> | ||
| 2060 | <entry>h</entry> | ||
| 2061 | <entry>Name of remote host</entry> | ||
| 2062 | <entry>remote_host</entry> | ||
| 2063 | <entry>varchar(50)</entry> | ||
| 2064 | <entry>blah.foobar.com</entry> | ||
| 2065 | </row> | ||
| 2066 | <row> | ||
| 2067 | <entry>I</entry> | ||
| 2068 | <entry>Request ID (from modd_unique_id)</entry> | ||
| 2069 | <entry>id</entry> | ||
| 2070 | <entry>char(19)</entry> | ||
| 2071 | <entry>POlFcUBRH30AAALdBG8</entry> | ||
| 2072 | </row> | ||
| 2073 | <row> | ||
| 2074 | <entry>l</entry> | ||
| 2075 | <entry>Ident user info</entry> | ||
| 2076 | <entry>remote_logname</entry> | ||
| 2077 | <entry>varcgar(50)</entry> | ||
| 2078 | <entry>bobby</entry> | ||
| 2079 | </row> | ||
| 2080 | <row> | ||
| 2081 | <entry>M</entry> | ||
| 2082 | <entry> | ||
| 2083 | Machine ID | ||
| 2084 | <xref linkend="Foot.MachineID" xrefstyle="footer" /> | ||
| 2085 | </entry> | ||
| 2086 | <entry>machine_id</entry> | ||
| 2087 | <entry>varchar(25)</entry> | ||
| 2088 | <entry>web01</entry> | ||
| 2089 | </row> | ||
| 2090 | <row> | ||
| 2091 | <entry>m</entry> | ||
| 2092 | <entry>HTTP request method</entry> | ||
| 2093 | <entry>request_method</entry> | ||
| 2094 | <entry>varchar(10)</entry> | ||
| 2095 | <entry>GET</entry> | ||
| 2096 | </row> | ||
| 2097 | <row> | ||
| 2098 | <entry>P</entry> | ||
| 2099 | <entry>httpd cchild PID</entry> | ||
| 2100 | <entry>child_pid</entry> | ||
| 2101 | <entry>smallint unsigned</entry> | ||
| 2102 | <entry>3215</entry> | ||
| 2103 | </row> | ||
| 2104 | <row> | ||
| 2105 | <entry>p</entry> | ||
| 2106 | <entry>http port</entry> | ||
| 2107 | <entry>server_port</entry> | ||
| 2108 | <entry>smallint unsigned</entry> | ||
| 2109 | <entry>80</entry> | ||
| 2110 | </row> | ||
| 2111 | <row> | ||
| 2112 | <entry>R</entry> | ||
| 2113 | <entry>Referer</entry> | ||
| 2114 | <entry>referer</entry> | ||
| 2115 | <entry>varchar(255)</entry> | ||
| 2116 | <entry> | ||
| 2117 | http://www.biglinks4u.com/linkpage.html | ||
| 2118 | </entry> | ||
| 2119 | </row> | ||
| 2120 | <row> | ||
| 2121 | <entry>r</entry> | ||
| 2122 | <entry>Request in full form</entry> | ||
| 2123 | <entry>request_line</entry> | ||
| 2124 | <entry>varchar(255)</entry> | ||
| 2125 | <entry>GET /books-cycroad.html HTTP/1.1</entry> | ||
| 2126 | </row> | ||
| 2127 | <row> | ||
| 2128 | <entry>S</entry> | ||
| 2129 | <entry> | ||
| 2130 | Time of request in UNIX time_t format | ||
| 2131 | </entry> | ||
| 2132 | <entry>time_stamp</entry> | ||
| 2133 | <entry>int unsigned</entry> | ||
| 2134 | <entry>1005598029</entry> | ||
| 2135 | </row> | ||
| 2136 | <row> | ||
| 2137 | <entry>s</entry> | ||
| 2138 | <entry>HTTP Response Code Status</entry> | ||
| 2139 | <entry>status</entry> | ||
| 2140 | <entry>smallint</entry> | ||
| 2141 | <entry>200</entry> | ||
| 2142 | </row> | ||
| 2143 | <row> | ||
| 2144 | <entry>T</entry> | ||
| 2145 | <entry>Seconds to service request</entry> | ||
| 2146 | <entry>request_duration</entry> | ||
| 2147 | <entry>smallint unsigned</entry> | ||
| 2148 | <entry>2</entry> | ||
| 2149 | </row> | ||
| 2150 | <row> | ||
| 2151 | <entry>t</entry> | ||
| 2152 | <entry>Time of request in human format</entry> | ||
| 2153 | <entry>request_time</entry> | ||
| 2154 | <entry>char(28)</entry> | ||
| 2155 | <entry>[02/Dec/2001:15:01:26 -0800]</entry> | ||
| 2156 | </row> | ||
| 2157 | <row> | ||
| 2158 | <entry>U</entry> | ||
| 2159 | <entry>Request in simple form</entry> | ||
| 2160 | <entry>request_uri</entry> | ||
| 2161 | <entry>varchar(255)</entry> | ||
| 2162 | <entry>/books-cycroad.html</entry> | ||
| 2163 | </row> | ||
| 2164 | <row> | ||
| 2165 | <entry>u</entry> | ||
| 2166 | <entry>User info from HTTP auth</entry> | ||
| 2167 | <entry>remote_user</entry> | ||
| 2168 | <entry>varchar(50)</entry> | ||
| 2169 | <entry>bobby</entry> | ||
| 2170 | </row> | ||
| 2171 | <row> | ||
| 2172 | <entry>v</entry> | ||
| 2173 | <entry>Virtual host servicing the request</entry> | ||
| 2174 | <entry>virtual_host</entry> | ||
| 2175 | <entry>varchar(255)</entry> | ||
| 2176 | <entry>www.foobar.com</entry> | ||
| 2177 | </row> | ||
| 2178 | <row> | ||
| 2179 | <entry>V</entry> | ||
| 2180 | <entry> | ||
| 2181 | requested Virtual host name (mass | ||
| 2182 | virtualhosting) | ||
| 2183 | </entry> | ||
| 2184 | <entry>virtual_host</entry> | ||
| 2185 | <entry>varchar(255)</entry> | ||
| 2186 | <entry>www.foobar.org</entry> | ||
| 2187 | </row> | ||
| 2188 | </tbody> | ||
| 2189 | </tgroup> | ||
| 2190 | </table> | ||
| 2191 | <note> | ||
| 2192 | <simpara id="Foot.LogCookie"> | ||
| 2193 | [1] You must also specify LogSQLWhichCookie for this | ||
| 2194 | to take effect. | ||
| 2195 | </simpara> | ||
| 2196 | <simpara id="Foot.MachineID"> | ||
| 2197 | [2] You must also specify LogSQLmachineID for this to | ||
| 2198 | take effect. | ||
| 2199 | </simpara> | ||
| 2200 | </note> | ||
| 2201 | <table> | ||
| 2202 | <title>SSL LogFormat Parameters</title> | ||
| 2203 | <tgroup cols="5"> | ||
| 2204 | <colspec colname="1" colnum="1" /> | ||
| 2205 | <colspec colname="2" colnum="2" /> | ||
| 2206 | <colspec colname="3" colnum="3" /> | ||
| 2207 | <colspec colname="4" colnum="4" /> | ||
| 2208 | <colspec colname="5" colnum="5" /> | ||
| 2209 | <thead> | ||
| 2210 | <row> | ||
| 2211 | <entry colname="1">Symbol</entry> | ||
| 2212 | <entry colname="2">Meaning</entry> | ||
| 2213 | <entry colname="3">DB Field</entry> | ||
| 2214 | <entry colname="4">Data Type</entry> | ||
| 2215 | <entry colname="5">Example</entry> | ||
| 2216 | </row> | ||
| 2217 | </thead> | ||
| 2218 | <tbody> | ||
| 2219 | <row> | ||
| 2220 | <entry colname="1">z</entry> | ||
| 2221 | <entry colname="2">SSL cipher used</entry> | ||
| 2222 | <entry colname="3">ssl_cipher</entry> | ||
| 2223 | <entry colname="4">varchar(25)</entry> | ||
| 2224 | <entry colname="5">RC4-MD5</entry> | ||
| 2225 | </row> | ||
| 2226 | <row> | ||
| 2227 | <entry colname="1">q</entry> | ||
| 2228 | <entry colname="2"> | ||
| 2229 | Keysize of the SSL connection | ||
| 2230 | </entry> | ||
| 2231 | <entry colname="3">ssl_keysize</entry> | ||
| 2232 | <entry colname="4">smallint unsigned</entry> | ||
| 2233 | <entry colname="5">56</entry> | ||
| 2234 | </row> | ||
| 2235 | <row> | ||
| 2236 | <entry colname="1">Q</entry> | ||
| 2237 | <entry colname="2"> | ||
| 2238 | maximum keysize supported | ||
| 2239 | </entry> | ||
| 2240 | <entry colname="3">ssl_maxkeysize</entry> | ||
| 2241 | <entry colname="4">smallint unsigned</entry> | ||
| 2242 | <entry colname="5">128</entry> | ||
| 2243 | </row> | ||
| 2244 | </tbody> | ||
| 2245 | </tgroup> | ||
| 2246 | </table> | ||
| 2247 | <table> | ||
| 2248 | <title>LogIO LogFormat Parameters</title> | ||
| 2249 | <tgroup cols="5"> | ||
| 2250 | <colspec colname="1" colnum="1" /> | ||
| 2251 | <colspec colname="2" colnum="2" /> | ||
| 2252 | <colspec colname="3" colnum="3" /> | ||
| 2253 | <colspec colname="4" colnum="4" /> | ||
| 2254 | <colspec colname="5" colnum="5" /> | ||
| 2255 | <thead> | ||
| 2256 | <row> | ||
| 2257 | <entry colname="1">Symbol</entry> | ||
| 2258 | <entry colname="2">Meaning</entry> | ||
| 2259 | <entry colname="3">DB Field</entry> | ||
| 2260 | <entry colname="4">Data Type</entry> | ||
| 2261 | <entry colname="5">Example</entry> | ||
| 2262 | </row> | ||
| 2263 | </thead> | ||
| 2264 | <tbody> | ||
| 2265 | <row> | ||
| 2266 | <entry colname="1">i</entry> | ||
| 2267 | <entry colname="2">Number of actual Bytes transfered in with the request</entry> | ||
| 2268 | <entry colname="3">bytes_in</entry> | ||
| 2269 | <entry colname="4">int unsigned</entry> | ||
| 2270 | <entry colname="5">505</entry> | ||
| 2271 | </row> | ||
| 2272 | <row> | ||
| 2273 | <entry colname="1">o</entry> | ||
| 2274 | <entry colname="2">Number of actual Bytes transfered out with the request</entry> | ||
| 2275 | <entry colname="3">bytes_out</entry> | ||
| 2276 | <entry colname="4">int unsigned</entry> | ||
| 2277 | <entry colname="5">4168</entry> | ||
| 2278 | </row> | ||
| 2279 | </tbody> | ||
| 2280 | </tgroup> | ||
| 2281 | </table> | ||
| 2282 | </listitem> | ||
| 2283 | </varlistentry> | ||
| 2284 | <varlistentry> | ||
| 2285 | <term>LogSQLRemhostIgnore</term> | ||
| 2286 | <listitem> | ||
| 2287 | <cmdsynopsis sepchar=" "> | ||
| 2288 | <command>LogSQLRemhostIgnore</command> | ||
| 2289 | <arg choice="req" rep="repeat"> | ||
| 2290 | <replaceable>hostname</replaceable> | ||
| 2291 | </arg> | ||
| 2292 | </cmdsynopsis> | ||
| 2293 | <simpara> | ||
| 2294 | Example: LogSQLRemhostIgnore localnet.com | ||
| 2295 | </simpara> | ||
| 2296 | <simpara>Context: virtual host</simpara> | ||
| 2297 | <para> | ||
| 2298 | Lists a series of smortrings that, if present in the | ||
| 2299 | REMOTE_HOST, will cause that request to | ||
| 2300 | <emphasis>not</emphasis> | ||
| 2301 | be logged. This directive is useful for cutting down on | ||
| 2302 | log clutter when you are certain that you want to ignore | ||
| 2303 | requests from certain hosts, such as your own internal | ||
| 2304 | network machines. See section | ||
| 2305 | <xref endterm="Sect.Ignore.title" linkend="Sect.Ignore" /> | ||
| 2306 | for some tips for using this directive. | ||
| 2307 | </para> | ||
| 2308 | <para> | ||
| 2309 | Each string may contain a + or - prefix in a | ||
| 2310 | <VirtualHost> context and will cause those strings | ||
| 2311 | to be added (+) or removed (-) from the global | ||
| 2312 | configuration. Otherwise the global is completely | ||
| 2313 | ignored and overridden if defined in a | ||
| 2314 | <VirtualHost> | ||
| 2315 | </para> | ||
| 2316 | <para> | ||
| 2317 | Each string is separated by a space, and no regular | ||
| 2318 | expressions or globbing are allowed. Each string is | ||
| 2319 | evaluated as a substring of the REMOTE_HOST using | ||
| 2320 | strstr(). The comparison is case sensitive. | ||
| 2321 | </para> | ||
| 2322 | </listitem> | ||
| 2323 | </varlistentry> | ||
| 2324 | <varlistentry> | ||
| 2325 | <term>LogSQLRequestAccept</term> | ||
| 2326 | <listitem> | ||
| 2327 | <cmdsynopsis sepchar=" "> | ||
| 2328 | <command>LogSQLRequestAccept</command> | ||
| 2329 | <arg choice="req" rep="repeat"> | ||
| 2330 | <replaceable>substring</replaceable> | ||
| 2331 | </arg> | ||
| 2332 | </cmdsynopsis> | ||
| 2333 | <simpara> | ||
| 2334 | Example: LogSQLRequestAccept .html .php .jpg | ||
| 2335 | </simpara> | ||
| 2336 | <simpara> | ||
| 2337 | Default: if not specified, all requests are 'accepted' | ||
| 2338 | </simpara> | ||
| 2339 | <simpara>Context: virtual host</simpara> | ||
| 2340 | <para> | ||
| 2341 | Lists a series of strings that, if present in the URI, | ||
| 2342 | will permit that request to be considered for logging | ||
| 2343 | (depending on additional filtering by the "ignore" | ||
| 2344 | directives). Any request that fails to match one of the | ||
| 2345 | LogSQLRequestAccept entries will be discarded. | ||
| 2346 | </para> | ||
| 2347 | <para> | ||
| 2348 | Each string may contain a + or - prefix in a | ||
| 2349 | <VirtualHost> context and will cause those strings | ||
| 2350 | to be added (+) or removed (-) from the global | ||
| 2351 | configuration. Otherwise the global is completely | ||
| 2352 | ignored and overridden if defined in a | ||
| 2353 | <VirtualHost> | ||
| 2354 | </para> | ||
| 2355 | <para> | ||
| 2356 | This directive is useful for cutting down on log clutter | ||
| 2357 | when you are certain that you only want to log certain | ||
| 2358 | kinds of requests, and just blanket-ignore everything | ||
| 2359 | else. See section | ||
| 2360 | <xref endterm="Sect.Ignore.title" linkend="Sect.Ignore" /> | ||
| 2361 | for some tips for using this directive. | ||
| 2362 | </para> | ||
| 2363 | <para> | ||
| 2364 | Each string is separated by a space, and no regular | ||
| 2365 | expressions or globbing are allowed. Each string is | ||
| 2366 | evaluated as a substring of the URI using strstr(). The | ||
| 2367 | comparison is case sensitive. | ||
| 2368 | </para> | ||
| 2369 | <para> | ||
| 2370 | This directive is completely optional. It is more | ||
| 2371 | general than LogSQLRequestIgnore and is evaluated before | ||
| 2372 | LogSQLRequestIgnore . If this directive is not used, | ||
| 2373 | <emphasis>all</emphasis> | ||
| 2374 | requests are accepted and passed on to the other | ||
| 2375 | filtering directives. Therefore, only use this directive | ||
| 2376 | if you have a specific reason to do so. | ||
| 2377 | </para> | ||
| 2378 | </listitem> | ||
| 2379 | </varlistentry> | ||
| 2380 | <varlistentry> | ||
| 2381 | <term>LogSQLRequestIgnore</term> | ||
| 2382 | <listitem> | ||
| 2383 | <cmdsynopsis sepchar=" "> | ||
| 2384 | <command>LogSQLRequestIgnore</command> | ||
| 2385 | <arg choice="req" rep="repeat"> | ||
| 2386 | <replaceable>substring</replaceable> | ||
| 2387 | </arg> | ||
| 2388 | </cmdsynopsis> | ||
| 2389 | <simpara> | ||
| 2390 | Example: LogSQLRequestIgnore root.exe cmd.exe | ||
| 2391 | default.ida favicon.ico | ||
| 2392 | </simpara> | ||
| 2393 | <simpara>Context: virtual host</simpara> | ||
| 2394 | <para> | ||
| 2395 | Lists a series of strings that, if present in the URI, | ||
| 2396 | will cause that request to | ||
| 2397 | <emphasis>NOT</emphasis> | ||
| 2398 | be logged. This directive is useful for cutting down on | ||
| 2399 | log clutter when you are certain that you want to ignore | ||
| 2400 | requests for certain objects. See section | ||
| 2401 | <xref endterm="Sect.Ignore.title" linkend="Sect.Ignore" /> | ||
| 2402 | for some tips for using this directive. | ||
| 2403 | </para> | ||
| 2404 | <para> | ||
| 2405 | Each string may contain a + or - prefix in a | ||
| 2406 | <VirtualHost> context and will cause those strings | ||
| 2407 | to be added (+) or removed (-) from the global | ||
| 2408 | configuration. Otherwise the global is completely | ||
| 2409 | ignored and overridden if defined in a | ||
| 2410 | <VirtualHost> | ||
| 2411 | </para> | ||
| 2412 | <para> | ||
| 2413 | Each string is separated by a space, and no regular | ||
| 2414 | expressions or globbing are allowed. Each string is | ||
| 2415 | evaluated as a substring of the URI using strstr(). The | ||
| 2416 | comparison is case sensitive. | ||
| 2417 | </para> | ||
| 2418 | </listitem> | ||
| 2419 | </varlistentry> | ||
| 2420 | <varlistentry> | ||
| 2421 | <term>LogSQLWhichCookie</term> | ||
| 2422 | <listitem> | ||
| 2423 | <cmdsynopsis sepchar=" "> | ||
| 2424 | <command>LogSQLWhichCookie</command> | ||
| 2425 | <arg choice="req"> | ||
| 2426 | <replaceable>cookiename</replaceable> | ||
| 2427 | </arg> | ||
| 2428 | </cmdsynopsis> | ||
| 2429 | <simpara>Example; LogSQLWhichCookie Clicks</simpara> | ||
| 2430 | <simpara>Context: virtual host</simpara> | ||
| 2431 | <para> | ||
| 2432 | In HTTP, cookies have names to distinguish them from | ||
| 2433 | each other. Using mod_usertrack, for example, you can | ||
| 2434 | give your user-tracking cookies a name with the | ||
| 2435 | CookieName directive. | ||
| 2436 | </para> | ||
| 2437 | <para> | ||
| 2438 | mod_log_sql allows you to log cookie information. | ||
| 2439 | LogSQL_WhichCookie tells mod_log_sql which cookie to | ||
| 2440 | log. This is necessary because you will usually be | ||
| 2441 | setting and receiving more than one cookie from a | ||
| 2442 | client. | ||
| 2443 | </para> | ||
| 2444 | <note> | ||
| 2445 | <para> | ||
| 2446 | You must include a 'c' character in | ||
| 2447 | LogSQLTransferLogFormat for this directive to take | ||
| 2448 | effect. | ||
| 2449 | </para> | ||
| 2450 | <para> | ||
| 2451 | although this was origintally intended for people | ||
| 2452 | using mod_usertrack to create user-tracking cookies, | ||
| 2453 | you are not restricted in any way. You can choose | ||
| 2454 | which cookie you wish to log to the database - any | ||
| 2455 | cookie at all - and it does not necessarily have to | ||
| 2456 | have anything to do with mod_usertrack. | ||
| 2457 | </para> | ||
| 2458 | </note> | ||
| 2459 | </listitem> | ||
| 2460 | </varlistentry> | ||
| 2461 | <varlistentry> | ||
| 2462 | <term>LogSQLWhichCookies</term> | ||
| 2463 | <listitem> | ||
| 2464 | <cmdsynopsis sepchar=" "> | ||
| 2465 | <command>LogSQLWhichCookies</command> | ||
| 2466 | <arg choice="req" rep="repeat"> | ||
| 2467 | <replaceable>cookie-name</replaceable> | ||
| 2468 | </arg> | ||
| 2469 | </cmdsynopsis> | ||
| 2470 | <simpara> | ||
| 2471 | Example: logSQLWhichCookies userlogin cookie1 cookie2 | ||
| 2472 | </simpara> | ||
| 2473 | <simpara>Context: virtual host</simpara> | ||
| 2474 | <para> | ||
| 2475 | Defines the list of cookies you would like logged. This | ||
| 2476 | works in conjunction with LogSQLCookieLogTable. This | ||
| 2477 | directive does | ||
| 2478 | <emphasis>not</emphasis> | ||
| 2479 | require any additional characters to be added to the | ||
| 2480 | LogSQLTransferLogFormat string. The feature is activated | ||
| 2481 | simply by including this directive, upon which you will | ||
| 2482 | begin populating the separate cookie table with data. | ||
| 2483 | </para> | ||
| 2484 | <para> | ||
| 2485 | Each string may contain a + or - prefix in a | ||
| 2486 | <VirtualHost> context and will cause those strings | ||
| 2487 | to be added (+) or removed (-) from the global | ||
| 2488 | configuration. Otherwise the global is completely | ||
| 2489 | ignored and overridden if defined in a | ||
| 2490 | <VirtualHost> | ||
| 2491 | </para> | ||
| 2492 | <note> | ||
| 2493 | <para> | ||
| 2494 | The table must be created (see create-tables.sql, | ||
| 2495 | included in the package), or LogSQLCreateTables must | ||
| 2496 | be set to 'On'. | ||
| 2497 | </para> | ||
| 2498 | </note> | ||
| 2499 | </listitem> | ||
| 2500 | </varlistentry> | ||
| 2501 | <varlistentry> | ||
| 2502 | <term>LogSQLWhichHeadersIn</term> | ||
| 2503 | <listitem> | ||
| 2504 | <cmdsynopsis sepchar=" "> | ||
| 2505 | <command>LogSQLWhichHeadersIn</command> | ||
| 2506 | <arg choice="req" rep="repeat"> | ||
| 2507 | <replaceable>header-name</replaceable> | ||
| 2508 | </arg> | ||
| 2509 | </cmdsynopsis> | ||
| 2510 | <simpara> | ||
| 2511 | Example: LogSQLWhichHeadersIn User-Agent Accept-Encoding | ||
| 2512 | Host | ||
| 2513 | </simpara> | ||
| 2514 | <simpara>Context: virtual host</simpara> | ||
| 2515 | <para> | ||
| 2516 | Defines the list of inbound headers you would like | ||
| 2517 | logged. This works in conjunction with | ||
| 2518 | LogSQLHeadersInLogTable. This directive does not require | ||
| 2519 | any additional characters to be added to the | ||
| 2520 | LogSQLTransferLogFormat string. The feature is activated | ||
| 2521 | simply by including this directive, upon which you will | ||
| 2522 | begin populating the separate inbound-headers table with | ||
| 2523 | data. | ||
| 2524 | </para> | ||
| 2525 | <para> | ||
| 2526 | Each string may contain a + or - prefix in a | ||
| 2527 | <VirtualHost> context and will cause those strings | ||
| 2528 | to be added (+) or removed (-) from the global | ||
| 2529 | configuration. Otherwise the global is completely | ||
| 2530 | ignored and overridden if defined in a | ||
| 2531 | <VirtualHost> | ||
| 2532 | </para> | ||
| 2533 | <note> | ||
| 2534 | <para> | ||
| 2535 | The table must be created (see create-tables.sql, | ||
| 2536 | included in the package), or LogSQLCreateTables must | ||
| 2537 | be set to 'On'. | ||
| 2538 | </para> | ||
| 2539 | </note> | ||
| 2540 | </listitem> | ||
| 2541 | </varlistentry> | ||
| 2542 | <varlistentry> | ||
| 2543 | <term>LogSQLWhichHeadersOut</term> | ||
| 2544 | <listitem> | ||
| 2545 | <cmdsynopsis sepchar=" "> | ||
| 2546 | <command>LogSQLWhichHeadersOut</command> | ||
| 2547 | <arg choice="req" rep="repeat"> | ||
| 2548 | <replaceable>header-name</replaceable> | ||
| 2549 | </arg> | ||
| 2550 | </cmdsynopsis> | ||
| 2551 | <simpara> | ||
| 2552 | Example: LogSQLWhichHeadersOut Expires Content-Type | ||
| 2553 | Cache-Control | ||
| 2554 | </simpara> | ||
| 2555 | <simpara>Context: virtual host</simpara> | ||
| 2556 | <para> | ||
| 2557 | Defines the list of outbound headers you would like | ||
| 2558 | logged. This works in conjunction with | ||
| 2559 | LogSQLHeadersOutLogTable. This directive does not | ||
| 2560 | require any additional characters to be added to the | ||
| 2561 | LogSQLTransferLogFormat string. The feature is activated | ||
| 2562 | simply by including this directive, upon which you will | ||
| 2563 | begin populating the separate outbound-headers table | ||
| 2564 | with data. | ||
| 2565 | </para> | ||
| 2566 | <para> | ||
| 2567 | Each string may contain a + or - prefix in a | ||
| 2568 | <VirtualHost> context and will cause those strings | ||
| 2569 | to be added (+) or removed (-) from the global | ||
| 2570 | configuration. Otherwise the global is completely | ||
| 2571 | ignored and overridden if defined in a | ||
| 2572 | <VirtualHost> | ||
| 2573 | </para> | ||
| 2574 | <note> | ||
| 2575 | <para> | ||
| 2576 | The table must be created (see create-tables.sql, | ||
| 2577 | included in the package), or LogSQLCreateTables must | ||
| 2578 | be set to 'On'. | ||
| 2579 | </para> | ||
| 2580 | </note> | ||
| 2581 | </listitem> | ||
| 2582 | </varlistentry> | ||
| 2583 | <varlistentry> | ||
| 2584 | <term>LogSQLWhichNotes</term> | ||
| 2585 | <listitem> | ||
| 2586 | <cmdsynopsis sepchar=" "> | ||
| 2587 | <command>LogSQLWhichNotes</command> | ||
| 2588 | <arg choice="req" rep="repeat"> | ||
| 2589 | <replaceable>note-name</replaceable> | ||
| 2590 | </arg> | ||
| 2591 | </cmdsynopsis> | ||
| 2592 | <simpara> | ||
| 2593 | Example: LogSQLWhichNotes mod_gzip_result | ||
| 2594 | mod_gzip_ompression_ratio | ||
| 2595 | </simpara> | ||
| 2596 | <simpara>Context: virtual host</simpara> | ||
| 2597 | <para> | ||
| 2598 | Defines the list of notes you would like logged. This | ||
| 2599 | works in conjunction with LogSQLNotesLogTable. This | ||
| 2600 | directive does not require any additional characters to | ||
| 2601 | be added to the LogSQLTransferLogFormat string. The | ||
| 2602 | feature is activated simply by including this directive, | ||
| 2603 | upon which you will begin populating the separate notes | ||
| 2604 | table with data. | ||
| 2605 | </para> | ||
| 2606 | <para> | ||
| 2607 | Each string may contain a + or - prefix in a | ||
| 2608 | <VirtualHost> context and will cause those strings | ||
| 2609 | to be added (+) or removed (-) from the global | ||
| 2610 | configuration. Otherwise the global is completely | ||
| 2611 | ignored and overridden if defined in a | ||
| 2612 | <VirtualHost> | ||
| 2613 | </para> | ||
| 2614 | <note> | ||
| 2615 | <para> | ||
| 2616 | The table must be created (see create-tables.sql, | ||
| 2617 | included in the package), or LogSQLCreateTables must | ||
| 2618 | be set to 'On'. | ||
| 2619 | </para> | ||
| 2620 | </note> | ||
| 2621 | </listitem> | ||
| 2622 | </varlistentry> | ||
| 2623 | </variablelist> | ||
| 2624 | </section> | ||
| 2625 | <section> | ||
| 2626 | <title>Deprecated Commands</title> | ||
| 2627 | <variablelist> | ||
| 2628 | <varlistentry> | ||
| 2629 | <term>LogSQLSocketFile [Deprecated]</term> | ||
| 2630 | <listitem> | ||
| 2631 | <cmdsynopsis sepchar=" "> | ||
| 2632 | <command>LogSQLSocketFile</command> | ||
| 2633 | <arg choice="req"> | ||
| 2634 | <replaceable>filename</replaceable> | ||
| 2635 | </arg> | ||
| 2636 | </cmdsynopsis> | ||
| 2637 | <simpara> | ||
| 2638 | Example: LogSQLSocketFile /tmp/mysql.sock | ||
| 2639 | </simpara> | ||
| 2640 | <simpara>Default: (database specific)</simpara> | ||
| 2641 | <simpara> | ||
| 2642 | Default (MySQL): /var/lib/mysql/mysql.sock | ||
| 2643 | </simpara> | ||
| 2644 | <simpara>Context: main server config</simpara> | ||
| 2645 | <para> | ||
| 2646 | At Apache runtime you can specify the MySQL socket file | ||
| 2647 | to use. Set this once in your main server config to | ||
| 2648 | override the default value. This value is irrelevant if | ||
| 2649 | your database resides on a separate machine. | ||
| 2650 | </para> | ||
| 2651 | <para> | ||
| 2652 | mod_log_sql will automatically employ the socket for db | ||
| 2653 | communications if the database resides on the local | ||
| 2654 | host. If the db resides on a separate host the module | ||
| 2655 | will automatically use TCP/IP. This is a function of the | ||
| 2656 | MySQL API and is not user-configurable. | ||
| 2657 | </para> | ||
| 2658 | <note> | ||
| 2659 | <para> | ||
| 2660 | This directive is deprecated in favor of LogSQLDBParam | ||
| 2661 | socketfile [socketfilename] | ||
| 2662 | </para> | ||
| 2663 | <para> | ||
| 2664 | This is defined only once in the | ||
| 2665 | <filename>httpd.conf</filename> | ||
| 2666 | file. | ||
| 2667 | </para> | ||
| 2668 | </note> | ||
| 2669 | </listitem> | ||
| 2670 | </varlistentry> | ||
| 2671 | <varlistentry> | ||
| 2672 | <term>LogSQLTCPPort [Deprecated]</term> | ||
| 2673 | <listitem> | ||
| 2674 | <cmdsynopsis sepchar=" "> | ||
| 2675 | <command>LogSQLTCPPort</command> | ||
| 2676 | <arg choice="req"> | ||
| 2677 | <replaceable>port-number</replaceable> | ||
| 2678 | </arg> | ||
| 2679 | </cmdsynopsis> | ||
| 2680 | <simpara>Example: LogSQLTCPPort 3309</simpara> | ||
| 2681 | <simpara>Default: (database specific)</simpara> | ||
| 2682 | <simpara>Default (MySQL): 3306</simpara> | ||
| 2683 | <simpara>Context: main server config</simpara> | ||
| 2684 | <para> | ||
| 2685 | Your database may listen on a different port than the | ||
| 2686 | default. If so, use this directive to instruct the | ||
| 2687 | module which port to use. This directive only applies if | ||
| 2688 | the database is on a different machine connected via | ||
| 2689 | TCP/IP. | ||
| 2690 | </para> | ||
| 2691 | <note> | ||
| 2692 | <para> | ||
| 2693 | This directive is deprecated in favor of LogSQLDBParam | ||
| 2694 | tcpport [port-number] | ||
| 2695 | </para> | ||
| 2696 | <para> | ||
| 2697 | This is defined only once in the | ||
| 2698 | <filename>httpd.conf</filename> | ||
| 2699 | file. | ||
| 2700 | </para> | ||
| 2701 | </note> | ||
| 2702 | </listitem> | ||
| 2703 | </varlistentry> | ||
| 2704 | <varlistentry> | ||
| 2705 | <term>LogSQLDatabase [Deprecated]</term> | ||
| 2706 | <listitem> | ||
| 2707 | <cmdsynopsis sepchar=" "> | ||
| 2708 | <command>LogSQLDatabase</command> | ||
| 2709 | <arg choice="req"> | ||
| 2710 | <replaceable>database</replaceable> | ||
| 2711 | </arg> | ||
| 2712 | </cmdsynopsis> | ||
| 2713 | <simpara>Example: LogSQLDatabase loggingdb</simpara> | ||
| 2714 | <simpara>Context: main server config</simpara> | ||
| 2715 | <para> | ||
| 2716 | Defines the database that is used for logging. | ||
| 2717 | "database" must be a valid db on the MySQL host defined | ||
| 2718 | in LogSQLLoginInfo | ||
| 2719 | </para> | ||
| 2720 | <note> | ||
| 2721 | <para> | ||
| 2722 | This directive is deprecated in favor of the URI form | ||
| 2723 | of LogSQLLoginInfo. | ||
| 2724 | </para> | ||
| 2725 | <para> | ||
| 2726 | This is defined only once in the | ||
| 2727 | <filename>httpd.conf</filename> | ||
| 2728 | file. | ||
| 2729 | </para> | ||
| 2730 | </note> | ||
| 2731 | </listitem> | ||
| 2732 | </varlistentry> | ||
| 2733 | </variablelist> | ||
| 2734 | </section> | ||
| 2735 | </section> | ||
| 2736 | </section> | ||
| 2737 | <section id="Sect.FAQ"> | ||
| 2738 | <title>FAQ</title> | ||
| 2739 | <qandaset> | ||
| 2740 | <qandadiv> | ||
| 2741 | <title>General module questions</title> | ||
| 2742 | <qandaentry id="FAQ.WhyLogToSQL"> | ||
| 2743 | <question> | ||
| 2744 | <para>Why log to an SQL database?</para> | ||
| 2745 | </question> | ||
| 2746 | <answer> | ||
| 2747 | <para> | ||
| 2748 | To begin with, let's get it out of the way: logging to a | ||
| 2749 | database is not a panacea. But while there are | ||
| 2750 | complexities with this solution, the benefit can be | ||
| 2751 | substantial for certain classes of administrator or people | ||
| 2752 | with advanced requirements: | ||
| 2753 | </para> | ||
| 2754 | <itemizedlist> | ||
| 2755 | <listitem> | ||
| 2756 | <para> | ||
| 2757 | Chores like log rotation go away, as you can DELETE | ||
| 2758 | records from the SQL database once they are no longer | ||
| 2759 | useful. For example, the excellent and popular | ||
| 2760 | log-analysis tool Webalizer (http://www.webalizer.com) | ||
| 2761 | does not need historic logs after it has processed | ||
| 2762 | them, enabling you to delete older logs. | ||
| 2763 | </para> | ||
| 2764 | </listitem> | ||
| 2765 | <listitem> | ||
| 2766 | <para> | ||
| 2767 | People with clusters of web servers (for high | ||
| 2768 | availability) will benefit the most - all their | ||
| 2769 | webservers can log to a single SQL database. This | ||
| 2770 | obviates the need to collate/interleave the many | ||
| 2771 | separate logfiles, which can be / highly/ problematic. | ||
| 2772 | </para> | ||
| 2773 | </listitem> | ||
| 2774 | <listitem> | ||
| 2775 | <para> | ||
| 2776 | People acquainted with the power of SQL SELECT | ||
| 2777 | statements will know the flexibility of the extraction | ||
| 2778 | possibilities at their fingertips. | ||
| 2779 | </para> | ||
| 2780 | </listitem> | ||
| 2781 | </itemizedlist> | ||
| 2782 | <para> | ||
| 2783 | For example, do you want to see all your 404's? Do this: | ||
| 2784 | </para> | ||
| 2785 | <programlisting>SELECT remote_host, status, request_uri, bytes_sent, from_unixtime(time_stamp) | ||
| 2786 | FROM acc_log_tbl WHERE status=404 ORDER BY time_stamp;</programlisting> | ||
| 2787 | <table> | ||
| 2788 | <title></title> | ||
| 2789 | <tgroup cols="5"> | ||
| 2790 | <colspec colname="1" /> | ||
| 2791 | <colspec colname="2" /> | ||
| 2792 | <colspec colname="3" /> | ||
| 2793 | <colspec colname="4" /> | ||
| 2794 | <colspec colname="5" /> | ||
| 2795 | <thead> | ||
| 2796 | <row> | ||
| 2797 | <entry colname="1">remote_host</entry> | ||
| 2798 | <entry colname="2">status</entry> | ||
| 2799 | <entry colname="3">request_uri</entry> | ||
| 2800 | <entry colname="4">bytes_sent</entry> | ||
| 2801 | <entry colname="5">from_unixtime(time_stamp)</entry> | ||
| 2802 | </row> | ||
| 2803 | </thead> | ||
| 2804 | <tbody> | ||
| 2805 | <row> | ||
| 2806 | <entry colname="1">marge.mmm.co.uk</entry> | ||
| 2807 | <entry colname="2">404</entry> | ||
| 2808 | <entry colname="3">/favicon.ico</entry> | ||
| 2809 | <entry colname="4">321</entry> | ||
| 2810 | <entry colname="5">2001-11-20 02:30:56</entry> | ||
| 2811 | </row> | ||
| 2812 | <row> | ||
| 2813 | <entry colname="1">62.180.239.251</entry> | ||
| 2814 | <entry colname="2">404</entry> | ||
| 2815 | <entry colname="3">/favicon.ico</entry> | ||
| 2816 | <entry colname="4">333</entry> | ||
| 2817 | <entry colname="5">2001-11-20 02:45:25</entry> | ||
| 2818 | </row> | ||
| 2819 | <row> | ||
| 2820 | <entry colname="1">212.234.12.66</entry> | ||
| 2821 | <entry colname="2">404</entry> | ||
| 2822 | <entry colname="3">/favicon.ico</entry> | ||
| 2823 | <entry colname="4">321</entry> | ||
| 2824 | <entry colname="5">2001-11-20 03:01:00</entry> | ||
| 2825 | </row> | ||
| 2826 | <row> | ||
| 2827 | <entry colname="1">212.210.78.254</entry> | ||
| 2828 | <entry colname="2">404</entry> | ||
| 2829 | <entry colname="3">/favicon.ico</entry> | ||
| 2830 | <entry colname="4">333</entry> | ||
| 2831 | <entry colname="5">2001-11-20 03:26:05</entry> | ||
| 2832 | </row> | ||
| 2833 | </tbody> | ||
| 2834 | </tgroup> | ||
| 2835 | </table> | ||
| 2836 | <para> | ||
| 2837 | Or do you want to see how many bytes you've sent within a | ||
| 2838 | certain directory or site? Do this: | ||
| 2839 | </para> | ||
| 2840 | <programlisting>SELECT request_uri,sum(bytes_sent) AS bytes, count(request_uri) AS howmany | ||
| 2841 | FROM acc_log_tbl | ||
| 2842 | WHERE request_uri LIKE '%mod_log_sql%' | ||
| 2843 | GROUP BY request_uri ORDER BY howmany DESC;</programlisting> | ||
| 2844 | <table> | ||
| 2845 | <title></title> | ||
| 2846 | <tgroup cols="3"> | ||
| 2847 | <colspec colname="1" /> | ||
| 2848 | <colspec colname="2" /> | ||
| 2849 | <colspec colname="3" /> | ||
| 2850 | <thead> | ||
| 2851 | <row> | ||
| 2852 | <entry colname="1">request_uri</entry> | ||
| 2853 | <entry colname="2">bytes</entry> | ||
| 2854 | <entry colname="3">howmany</entry> | ||
| 2855 | </row> | ||
| 2856 | </thead> | ||
| 2857 | <tbody> | ||
| 2858 | <row> | ||
| 2859 | <entry colname="1">/mod_log_sql/style_1.css</entry> | ||
| 2860 | <entry colname="2">157396</entry> | ||
| 2861 | <entry colname="3">1288</entry> | ||
| 2862 | </row> | ||
| 2863 | <row> | ||
| 2864 | <entry colname="1">/mod_log_sql/</entry> | ||
| 2865 | <entry colname="2">2514337</entry> | ||
| 2866 | <entry colname="3">801</entry> | ||
| 2867 | </row> | ||
| 2868 | <row> | ||
| 2869 | <entry colname="1"> | ||
| 2870 | /mod_log_sql/mod_log_sql.tar.gz | ||
| 2871 | </entry> | ||
| 2872 | <entry colname="2">9769312</entry> | ||
| 2873 | <entry colname="3">456</entry> | ||
| 2874 | </row> | ||
| 2875 | <row> | ||
| 2876 | <entry colname="1">/mod_log_sql/faq.html</entry> | ||
| 2877 | <entry colname="2">5038728</entry> | ||
| 2878 | <entry colname="3">436</entry> | ||
| 2879 | </row> | ||
| 2880 | </tbody> | ||
| 2881 | </tgroup> | ||
| 2882 | </table> | ||
| 2883 | <para> | ||
| 2884 | Or maybe you want to see who's linking to you? Do this: | ||
| 2885 | </para> | ||
| 2886 | <programlisting>SELECT count(referer) AS num,referer | ||
| 2887 | FROM acc_log_tbl | ||
| 2888 | WHERE request_uri='/mod_log_sql/' | ||
| 2889 | GROUP BY referer ORDER BY num DESC;</programlisting> | ||
| 2890 | <table> | ||
| 2891 | <title></title> | ||
| 2892 | <tgroup cols="2"> | ||
| 2893 | <colspec colname="1" /> | ||
| 2894 | <colspec colname="2" /> | ||
| 2895 | <thead> | ||
| 2896 | <row> | ||
| 2897 | <entry colname="1">num</entry> | ||
| 2898 | <entry colname="2">referer</entry> | ||
| 2899 | </row> | ||
| 2900 | </thead> | ||
| 2901 | <tbody> | ||
| 2902 | <row> | ||
| 2903 | <entry colname="1">271</entry> | ||
| 2904 | <entry colname="2"> | ||
| 2905 | http://freshmeat.net/projects/mod_log_sql/ | ||
| 2906 | </entry> | ||
| 2907 | </row> | ||
| 2908 | <row> | ||
| 2909 | <entry colname="1">96</entry> | ||
| 2910 | <entry colname="2"> | ||
| 2911 | http://modules.apache.org/search?id=339 | ||
| 2912 | </entry> | ||
| 2913 | </row> | ||
| 2914 | <row> | ||
| 2915 | <entry colname="1">48</entry> | ||
| 2916 | <entry colname="2">http://freshmeat.net/</entry> | ||
| 2917 | </row> | ||
| 2918 | <row> | ||
| 2919 | <entry colname="1">8</entry> | ||
| 2920 | <entry colname="2">http://freshmeat.net</entry> | ||
| 2921 | </row> | ||
| 2922 | </tbody> | ||
| 2923 | </tgroup> | ||
| 2924 | </table> | ||
| 2925 | <para> | ||
| 2926 | As you can see, there are myriad possibilities that can be | ||
| 2927 | constructed with the wonderful SQL SELECT statement. | ||
| 2928 | Logging to an SQL database can be really quite useful! | ||
| 2929 | </para> | ||
| 2930 | </answer> | ||
| 2931 | </qandaentry> | ||
| 2932 | <qandaentry> | ||
| 2933 | <question> | ||
| 2934 | <para>Why use MySQL? Are there alternatives?</para> | ||
| 2935 | </question> | ||
| 2936 | <answer> | ||
| 2937 | <para> | ||
| 2938 | MySQL is a robust, free, and very powerful | ||
| 2939 | production-quality database engine. It is well supported | ||
| 2940 | and comes with detailed documentation. Many 3rd-party | ||
| 2941 | software pacakges (e.g. Slashcode, the engine that powers | ||
| 2942 | Slashdot) run exclusively with MySQL. In other words, you | ||
| 2943 | will belong to a very robust and well-supported community | ||
| 2944 | by choosing MySQL. | ||
| 2945 | </para> | ||
| 2946 | <para> | ||
| 2947 | That being said, there are alternatives. PostgreSQL is | ||
| 2948 | probably MySQL's leading "competitor" in the free database | ||
| 2949 | world. There is also an excellent module available for | ||
| 2950 | Apache to permit logging to a PostgreSQL database, called | ||
| 2951 | <ulink url="http://www.digitalstratum.com/pglogd/"> | ||
| 2952 | pgLOGd | ||
| 2953 | </ulink> | ||
| 2954 | </para> | ||
| 2955 | </answer> | ||
| 2956 | <answer> | ||
| 2957 | <note> | ||
| 2958 | <para> | ||
| 2959 | Currently a database abstraction system is in the works | ||
| 2960 | to allow any database to be used with mod_log_sql. | ||
| 2961 | </para> | ||
| 2962 | </note> | ||
| 2963 | </answer> | ||
| 2964 | </qandaentry> | ||
| 2965 | <qandaentry> | ||
| 2966 | <question> | ||
| 2967 | <para>Is this code production-ready?</para> | ||
| 2968 | </question> | ||
| 2969 | <answer> | ||
| 2970 | <para> | ||
| 2971 | By all accounts it is. It is known to work without a | ||
| 2972 | problem on many-thousands-of-hits-per-day webservers. Does | ||
| 2973 | that mean it is 100% bug free? Well, no software is, but | ||
| 2974 | it is well-tested and believed to be fully compatible with | ||
| 2975 | production environments. (The usual disclaimers apply. | ||
| 2976 | This software is provided without warranty of any kind.) | ||
| 2977 | </para> | ||
| 2978 | </answer> | ||
| 2979 | </qandaentry> | ||
| 2980 | <qandaentry> | ||
| 2981 | <question> | ||
| 2982 | <para>Who's using mod_log_sql?</para> | ||
| 2983 | </question> | ||
| 2984 | <answer> | ||
| 2985 | <para> | ||
| 2986 | Good question! It would be great to find out! If you are a | ||
| 2987 | production-level mod_log_sql user, please contact eddie at | ||
| 2988 | &EmailContact; | ||
| 2989 | so that you can be mentioned here. | ||
| 2990 | </para> | ||
| 2991 | </answer> | ||
| 2992 | </qandaentry> | ||
| 2993 | <qandaentry> | ||
| 2994 | <question> | ||
| 2995 | <para> | ||
| 2996 | Why doesn't the module also replace the Apache ErrorLog? | ||
| 2997 | </para> | ||
| 2998 | </question> | ||
| 2999 | <answer> | ||
| 3000 | <para> | ||
| 3001 | There are circumstances when that would be quite unwise -- | ||
| 3002 | for example, if Apache could not reach the MySQL server | ||
| 3003 | for some reason and needed to log that fact. Without a | ||
| 3004 | text-based error log you'd never know anything was wrong, | ||
| 3005 | because Apache would be trying to log a database | ||
| 3006 | connection error to the database... you get the point. | ||
| 3007 | </para> | ||
| 3008 | </answer> | ||
| 3009 | <answer> | ||
| 3010 | <para> | ||
| 3011 | Error logs are usually not very high-traffic and are | ||
| 3012 | really best left as text files on a web server machine. | ||
| 3013 | </para> | ||
| 3014 | </answer> | ||
| 3015 | <answer> | ||
| 3016 | <para> | ||
| 3017 | The Error log is free format text.. (no specified | ||
| 3018 | formatting what, so ever) which is rather difficult to | ||
| 3019 | nicely format for storing in a database. | ||
| 3020 | </para> | ||
| 3021 | </answer> | ||
| 3022 | </qandaentry> | ||
| 3023 | <qandaentry> | ||
| 3024 | <question> | ||
| 3025 | <para>Does mod_log_sql work with Apache 2.x?</para> | ||
| 3026 | </question> | ||
| 3027 | <answer> | ||
| 3028 | <para> | ||
| 3029 | Yes. A port of mod_log_sql is available for Apache 2.x as | ||
| 3030 | of mod_log_sql 1.90 | ||
| 3031 | </para> | ||
| 3032 | </answer> | ||
| 3033 | </qandaentry> | ||
| 3034 | <qandaentry> | ||
| 3035 | <question> | ||
| 3036 | <para> | ||
| 3037 | Does mod_log_sql connect to MySQL via TCP/IP or a socket? | ||
| 3038 | </para> | ||
| 3039 | </question> | ||
| 3040 | <answer> | ||
| 3041 | <para>Quick answer, Yes.</para> | ||
| 3042 | </answer> | ||
| 3043 | <answer> | ||
| 3044 | <para> | ||
| 3045 | It depends! This is not determined by mod_log_sql. | ||
| 3046 | mod_log_sql relies on a connection command that is | ||
| 3047 | supplied in the MySQL API, and that command is somewhat | ||
| 3048 | intelligent. How it works: | ||
| 3049 | </para> | ||
| 3050 | <itemizedlist> | ||
| 3051 | <listitem> | ||
| 3052 | <simpara> | ||
| 3053 | if the specified MySQL database is on the same | ||
| 3054 | machine, the connection command uses a socket to | ||
| 3055 | communicate with MySQL | ||
| 3056 | </simpara> | ||
| 3057 | </listitem> | ||
| 3058 | <listitem> | ||
| 3059 | <simpara> | ||
| 3060 | if the specified MySQL database is on a different | ||
| 3061 | machine, mod_log_sql connects using TCP/IP. | ||
| 3062 | </simpara> | ||
| 3063 | </listitem> | ||
| 3064 | </itemizedlist> | ||
| 3065 | <para> | ||
| 3066 | You don't have any control of which methodology is used. | ||
| 3067 | You can fine-tune some of the configuration, however. The | ||
| 3068 | LogSQLSocketFile runtime configuration directive overrides | ||
| 3069 | the default of "/var/lib/mysql/mysql.sock" for | ||
| 3070 | socket-based connections, whereas the LogSQLTCPPort | ||
| 3071 | command allows to you override the default TCP port of | ||
| 3072 | 3306 for TCP/IP connections. | ||
| 3073 | </para> | ||
| 3074 | </answer> | ||
| 3075 | </qandaentry> | ||
| 3076 | <qandaentry> | ||
| 3077 | <question> | ||
| 3078 | <para>I have discovered a bug. Who can I contact?</para> | ||
| 3079 | </question> | ||
| 3080 | <answer> | ||
| 3081 | <para> | ||
| 3082 | Please contact Edward Rudd at | ||
| 3083 | &EmailContact; | ||
| 3084 | , or post a message to the mod_log_sql | ||
| 3085 | <xref endterm="Sect.MailingLists.title" | ||
| 3086 | linkend="Sect.MailingLists" /> | ||
| 3087 | . Your comments, suggestions, bugfixes, bug catches, and | ||
| 3088 | usage testimonials are always welcome. As free software, | ||
| 3089 | mod_log_sql is intended to be a community effort -- any | ||
| 3090 | code contributions or other ideas will be fully and openly | ||
| 3091 | credited, of course. | ||
| 3092 | </para> | ||
| 3093 | </answer> | ||
| 3094 | </qandaentry> | ||
| 3095 | </qandadiv> | ||
| 3096 | <qandadiv> | ||
| 3097 | <title>Problems</title> | ||
| 3098 | <qandaentry> | ||
| 3099 | <question> | ||
| 3100 | <para> | ||
| 3101 | Apache segfaults or has other problems when using PHP and | ||
| 3102 | mod_log_sql | ||
| 3103 | </para> | ||
| 3104 | </question> | ||
| 3105 | <answer> | ||
| 3106 | <para> | ||
| 3107 | This occurs if you compiled PHP with MySQL database | ||
| 3108 | support. PHP utilizes its internal, bundled MySQL | ||
| 3109 | libraries by default. These conflict with the "real" MySQL | ||
| 3110 | libraries linked by mod_log_sql, causing the segmentation | ||
| 3111 | fault. | ||
| 3112 | </para> | ||
| 3113 | <para> | ||
| 3114 | PHP and mod_log_sql can be configured to happily coexist. | ||
| 3115 | The solution is to configure PHP to link against the real | ||
| 3116 | MySQL libraries: recompile PHP using | ||
| 3117 | --with-mysql=/your/path. Apache will run properly once the | ||
| 3118 | modules are all using the same version of the MySQL | ||
| 3119 | libraries. | ||
| 3120 | </para> | ||
| 3121 | </answer> | ||
| 3122 | </qandaentry> | ||
| 3123 | <qandaentry id="FAQ.NothingLogged"> | ||
| 3124 | <question> | ||
| 3125 | <para> | ||
| 3126 | Apache appears to start up fine, but nothing is getting | ||
| 3127 | logged in the database | ||
| 3128 | </para> | ||
| 3129 | </question> | ||
| 3130 | <answer> | ||
| 3131 | <para> | ||
| 3132 | If you do not see any entries in the access_log, then | ||
| 3133 | something is preventing the inserts from happening. This | ||
| 3134 | could be caused by several things: | ||
| 3135 | </para> | ||
| 3136 | <itemizedlist> | ||
| 3137 | <listitem> | ||
| 3138 | <simpara> | ||
| 3139 | Improper privileges set up in the MySQL database | ||
| 3140 | </simpara> | ||
| 3141 | </listitem> | ||
| 3142 | <listitem> | ||
| 3143 | <simpara> | ||
| 3144 | You are not hitting a VirtualHost that has a | ||
| 3145 | LogSQLTransferLogTable entry | ||
| 3146 | </simpara> | ||
| 3147 | </listitem> | ||
| 3148 | <listitem> | ||
| 3149 | <simpara> | ||
| 3150 | You did not specify the right database host or login | ||
| 3151 | information | ||
| 3152 | </simpara> | ||
| 3153 | </listitem> | ||
| 3154 | <listitem> | ||
| 3155 | <simpara> | ||
| 3156 | Another factor is preventing a connection to the | ||
| 3157 | database | ||
| 3158 | </simpara> | ||
| 3159 | </listitem> | ||
| 3160 | </itemizedlist> | ||
| 3161 | <note> | ||
| 3162 | <para> | ||
| 3163 | It is improper to ask for help before you have followed | ||
| 3164 | these steps. | ||
| 3165 | </para> | ||
| 3166 | </note> | ||
| 3167 | <para> | ||
| 3168 | First examine the MySQL log that you established in step | ||
| 3169 | <xref linkend="Item.EnableLogging" /> | ||
| 3170 | of section | ||
| 3171 | <xref endterm="Sect.Preperation.title" | ||
| 3172 | linkend="Sect.Preperation" /> | ||
| 3173 | . Ensure that the INSERT statements are not being rejected | ||
| 3174 | because of a malformed table name or other typographical | ||
| 3175 | error. By enabling that log, you instructed MySQL to log | ||
| 3176 | every connection and command it receives -- if you see no | ||
| 3177 | INSERT attempts in the log, the module isn't successfully | ||
| 3178 | connecting to the database. If you see nothing at all in | ||
| 3179 | the log -- not even a record of your administrative | ||
| 3180 | connection attempts, then you did not enable the log | ||
| 3181 | correctly. If you do see INSERT attempts but they are | ||
| 3182 | failing, the log should tell you why. | ||
| 3183 | </para> | ||
| 3184 | <para> | ||
| 3185 | Second, confirm that your LogSQL* directives are all | ||
| 3186 | correct. | ||
| 3187 | </para> | ||
| 3188 | <para> | ||
| 3189 | Third, examine the Apache error logs for messages from | ||
| 3190 | mod_log_sql; the module will offer hints as to why it | ||
| 3191 | cannot connect, etc. | ||
| 3192 | </para> | ||
| 3193 | <para> | ||
| 3194 | The next thing to do is to change the LogLevel directive | ||
| 3195 | <emphasis> | ||
| 3196 | in the main server config as well as in each VirtualHost | ||
| 3197 | config: | ||
| 3198 | </emphasis> | ||
| 3199 | </para> | ||
| 3200 | <programlisting>LogLevel debug | ||
| 3201 | ErrorLog /var/log/httpd/server-messages</programlisting> | ||
| 3202 | </answer> | ||
| 3203 | </qandaentry> | ||
| 3204 | <qandaentry> | ||
| 3205 | <question> | ||
| 3206 | <para> | ||
| 3207 | Why do I get the message "insufficient configuration info | ||
| 3208 | to establish database link" in my Apache error log? | ||
| 3209 | </para> | ||
| 3210 | </question> | ||
| 3211 | <answer> | ||
| 3212 | <para> | ||
| 3213 | At a minimum, LogSQLLoginInfo in the URl form and either | ||
| 3214 | LogSQLTableName or LogSQLMassVirtualHosting must be | ||
| 3215 | defined in order for the module to be able to establish a | ||
| 3216 | database link. If these are not defined or are incomplete | ||
| 3217 | you will receive this error message. | ||
| 3218 | </para> | ||
| 3219 | </answer> | ||
| 3220 | </qandaentry> | ||
| 3221 | <qandaentry> | ||
| 3222 | <question> | ||
| 3223 | <para> | ||
| 3224 | My database cannot handle all the open connections from | ||
| 3225 | mod_log_sql, is there anything I can do? | ||
| 3226 | </para> | ||
| 3227 | </question> | ||
| 3228 | <answer> | ||
| 3229 | <para> | ||
| 3230 | The rule of thumb: if you have n webservers each | ||
| 3231 | configured to support y MaxClients, then your database | ||
| 3232 | must be able to handle n times y simultaneous connections | ||
| 3233 | in the worst case. Certainly you must use common sense, | ||
| 3234 | consider reasonable traffic expectations and structure | ||
| 3235 | things accordingly. | ||
| 3236 | </para> | ||
| 3237 | </answer> | ||
| 3238 | <answer> | ||
| 3239 | <para> | ||
| 3240 | Tweaking my.cnf to scale to high connection loads is | ||
| 3241 | imperative. But if hardware limitations prevent your MySQL | ||
| 3242 | server from gracefully handling the number of incoming | ||
| 3243 | connections, it would be beneficial to upgrade the memory | ||
| 3244 | or CPU on that server in order to handle the load. | ||
| 3245 | </para> | ||
| 3246 | </answer> | ||
| 3247 | <answer> | ||
| 3248 | <para> | ||
| 3249 | Jeremy Zawodny, a highly respected MySQL user and | ||
| 3250 | contributor to Linux Magazine, has this very helpful and | ||
| 3251 | highly appropriate article on tuning MySQL: | ||
| 3252 | <ulink | ||
| 3253 | url="http://jeremy.zawodny.com/blog/archives/000173.html"> | ||
| 3254 | MySQL, Linux, and Thread Caching | ||
| 3255 | </ulink> | ||
| 3256 | </para> | ||
| 3257 | </answer> | ||
| 3258 | <answer> | ||
| 3259 | <para> | ||
| 3260 | Please remember that mod_log_sql's overriding principle is | ||
| 3261 | performance -- that is what the target audience demands | ||
| 3262 | and expects. Other database logging solutions do not open | ||
| 3263 | and maintain many database connections, but their | ||
| 3264 | performance suffers drastically. For example, pgLOGd | ||
| 3265 | funnels all log connections through a separate daemon that | ||
| 3266 | connects to the database, but that bottlenecks the entire | ||
| 3267 | process. mod_log_sql achieves performance numbers an order | ||
| 3268 | of magnitude greater than the alternatives because it | ||
| 3269 | dispenses with the overhead associated with rapid | ||
| 3270 | connection cycling, and it does not attempt to shoehorn | ||
| 3271 | all the database traffic through a single extra daemon or | ||
| 3272 | proxy process. | ||
| 3273 | </para> | ||
| 3274 | </answer> | ||
| 3275 | <answer> | ||
| 3276 | <note> | ||
| 3277 | <para> | ||
| 3278 | Currently connection pooling is being implemented as | ||
| 3279 | part of the Database Abstraction layer to allow multiple | ||
| 3280 | httpd processes to share connections. | ||
| 3281 | </para> | ||
| 3282 | </note> | ||
| 3283 | </answer> | ||
| 3284 | </qandaentry> | ||
| 3285 | <qandaentry> | ||
| 3286 | <question> | ||
| 3287 | <para> | ||
| 3288 | Why do I occasionally see a "lost connection to MySQL | ||
| 3289 | server" message in my Apache error log? | ||
| 3290 | </para> | ||
| 3291 | </question> | ||
| 3292 | <answer> | ||
| 3293 | <para> | ||
| 3294 | This message may appear every now and then in your Apache | ||
| 3295 | error log, especially on very lightly loaded servers. This | ||
| 3296 | does not mean that anything is necessarily wrong. Within | ||
| 3297 | each httpd child process, mod_log_sql will open (and keep | ||
| 3298 | open) a connection to the MySQL server. MySQL, however, | ||
| 3299 | will close connections that have not been used in a while; | ||
| 3300 | the default timeout is 8 hours. When this occurs, | ||
| 3301 | mod_log_sql will notice and re-open the connection. That | ||
| 3302 | event is what is being logged, and looks like this: | ||
| 3303 | </para> | ||
| 3304 | <programlisting>[Tue Nov 12 19:04:10 2002] [error] mod_log_sql: first attempt failed, | ||
| 3305 | API said: error 2013, Lost connection to MySQL server during query | ||
| 3306 | [Tue Nov 12 19:04:10 2002] [error] mod_log_sql: reconnect successful | ||
| 3307 | [Tue Nov 12 19:04:10 2002] [error] mod_log_sql: second attempt successful</programlisting> | ||
| 3308 | <para> | ||
| 3309 | Reference: | ||
| 3310 | <ulink | ||
| 3311 | url="http://dev.mysql.com/doc/mysql/en/Gone_away.html"> | ||
| 3312 | MySQL documentation | ||
| 3313 | </ulink> | ||
| 3314 | </para> | ||
| 3315 | </answer> | ||
| 3316 | </qandaentry> | ||
| 3317 | <qandaentry> | ||
| 3318 | <question> | ||
| 3319 | <para> | ||
| 3320 | Sometimes a single VirtualHost gets logged to two | ||
| 3321 | different tables (e.g. access_foo_com, | ||
| 3322 | access_www_foo_com). Or, accesses to an unqualified | ||
| 3323 | hostname (e.g. "http://intranet/index.html") get logged in | ||
| 3324 | separate tables. | ||
| 3325 | </para> | ||
| 3326 | </question> | ||
| 3327 | <answer> | ||
| 3328 | <para> | ||
| 3329 | Proper usage of the Apache runtime ServerName directive | ||
| 3330 | and the directive UseCanonicalName On (or DNS) are | ||
| 3331 | necessary to prevent this problem. "On" is the default for | ||
| 3332 | UseCanonicalName, and specifies that self-referential URLs | ||
| 3333 | are generated from the ServerName part of your | ||
| 3334 | VirtualHost: | ||
| 3335 | </para> | ||
| 3336 | <para> | ||
| 3337 | With UseCanonicalName on (and in all versions prior to | ||
| 3338 | 1.3) Apache will use the ServerName and Port directives to | ||
| 3339 | construct the canonical name for the server. With | ||
| 3340 | UseCanonicalName off Apache will form self-referential | ||
| 3341 | URLs using the hostname and port supplied by the client if | ||
| 3342 | any are supplied (otherwise it will use the canonical | ||
| 3343 | name, as defined above). [From | ||
| 3344 | <ulink | ||
| 3345 | url="http://httpd.apache.org/docs/mod/core.html#usecanonicalname"> | ||
| 3346 | the Apache documentation | ||
| 3347 | </ulink> | ||
| 3348 | ] | ||
| 3349 | </para> | ||
| 3350 | <para> | ||
| 3351 | The module inherits Apache's "knowledge" about the server | ||
| 3352 | name being accessed. As long as those two directives are | ||
| 3353 | properly configured, mod_log_sql will log to only one | ||
| 3354 | table per virtual host while using | ||
| 3355 | LogSQLMassVirtualHosting. | ||
| 3356 | </para> | ||
| 3357 | </answer> | ||
| 3358 | </qandaentry> | ||
| 3359 | </qandadiv> | ||
| 3360 | <qandadiv> | ||
| 3361 | <title>Performance and Tuning</title> | ||
| 3362 | <qandaentry> | ||
| 3363 | <question> | ||
| 3364 | <para>How well does it perform?</para> | ||
| 3365 | </question> | ||
| 3366 | <answer> | ||
| 3367 | <para> | ||
| 3368 | mod_log_sql scales to very high loads. Apache 1.3.22 + | ||
| 3369 | mod_log_sql was benchmarked using the "ab" (Apache Bench) | ||
| 3370 | program that comes with the Apache distribution; here are | ||
| 3371 | the results. | ||
| 3372 | </para> | ||
| 3373 | <itemizedlist> | ||
| 3374 | <title>Overall configuration</title> | ||
| 3375 | <listitem> | ||
| 3376 | <simpara>Machine A: Apache webserver</simpara> | ||
| 3377 | </listitem> | ||
| 3378 | <listitem> | ||
| 3379 | <simpara>Machine B: MySQL server</simpara> | ||
| 3380 | </listitem> | ||
| 3381 | <listitem> | ||
| 3382 | <simpara> | ||
| 3383 | Machines A and B connected with 100Mbps Ethernet | ||
| 3384 | </simpara> | ||
| 3385 | </listitem> | ||
| 3386 | <listitem> | ||
| 3387 | <simpara> | ||
| 3388 | Webserver: Celeron 400, 128MB RAM, IDE storage | ||
| 3389 | </simpara> | ||
| 3390 | </listitem> | ||
| 3391 | </itemizedlist> | ||
| 3392 | <example> | ||
| 3393 | <title>Apache configuration</title> | ||
| 3394 | <programlisting>Timeout 300 | ||
| 3395 | KeepAlive On | ||
| 3396 | MaxKeepAliveRequests 100 | ||
| 3397 | KeepAliveTimeout 15 | ||
| 3398 | MinSpareServers 5 | ||
| 3399 | StartServers 10 | ||
| 3400 | MaxSpareServers 15 | ||
| 3401 | MaxClients 256 | ||
| 3402 | MaxRequestsPerChild 5000 | ||
| 3403 | LogSQLTransferLogFormat AbHhmRSsTUuvc | ||
| 3404 | LogSQLWhichCookie Clicks | ||
| 3405 | CookieTracking on | ||
| 3406 | CookieName Clicks</programlisting> | ||
| 3407 | </example> | ||
| 3408 | <example> | ||
| 3409 | <title>"ab" commandline</title> | ||
| 3410 | <programlisting>./ab -c 10 -t 20 -v 2 -C Clicks=ab_run http://www.hostname.com/target</programlisting> | ||
| 3411 | </example> | ||
| 3412 | <para> | ||
| 3413 | ( 10 concurrent requests; 20 second test; setting a cookie | ||
| 3414 | "Clicks=ab_run"; target = the mod_log_sql homepage. ) | ||
| 3415 | </para> | ||
| 3416 | <para> | ||
| 3417 | Ten total ab runs were conducted: five with MySQL logging | ||
| 3418 | enabled, and five with all MySQL directives commented out | ||
| 3419 | of httpd.conf. Then each five were averaged. The results: | ||
| 3420 | </para> | ||
| 3421 | <itemizedlist> | ||
| 3422 | <listitem> | ||
| 3423 | <simpara> | ||
| 3424 | Average of five runs employing MySQL and standard text | ||
| 3425 | logging: | ||
| 3426 | <emphasis> | ||
| 3427 | 139.01 requests per second, zero errors. | ||
| 3428 | </emphasis> | ||
| 3429 | </simpara> | ||
| 3430 | </listitem> | ||
| 3431 | <listitem> | ||
| 3432 | <simpara> | ||
| 3433 | Average of five runs employing only standard text | ||
| 3434 | logging: | ||
| 3435 | <emphasis> | ||
| 3436 | 139.96 requests per second, zero errors. | ||
| 3437 | </emphasis> | ||
| 3438 | </simpara> | ||
| 3439 | </listitem> | ||
| 3440 | </itemizedlist> | ||
| 3441 | <para> | ||
| 3442 | In other words, any rate-limiting effects on this | ||
| 3443 | particular hardware setup are not caused by MySQL. Note | ||
| 3444 | that although this very simple webserver setup is hardly | ||
| 3445 | cutting-edge -- it is, after all, a fairly small machine | ||
| 3446 | -- 139 requests per second equal over twelve million hits | ||
| 3447 | per day. | ||
| 3448 | </para> | ||
| 3449 | <orderedlist> | ||
| 3450 | <title> | ||
| 3451 | If you run this benchmark yourself, take note of three | ||
| 3452 | things: | ||
| 3453 | </title> | ||
| 3454 | <listitem> | ||
| 3455 | <simpara> | ||
| 3456 | Use a target URL that is on your own webserver :-). | ||
| 3457 | </simpara> | ||
| 3458 | </listitem> | ||
| 3459 | <listitem> | ||
| 3460 | <simpara> | ||
| 3461 | Wait until all your connections are closed out between | ||
| 3462 | runs; after several thousand requests your TCP/IP | ||
| 3463 | stack will be filled with hundreds of connections in | ||
| 3464 | TIME_WAIT that need to close. Do a "netstat -t|wc -l" | ||
| 3465 | on the webserver to see. If you don't wait, you can | ||
| 3466 | expect to see a lot of messages like "ip_conntrack: | ||
| 3467 | table full, dropping packet" in your logs. (This has | ||
| 3468 | nothing to do with mod_log_sql, this is simply the | ||
| 3469 | nature of the TCP/IP stack in the Linux kernel.) | ||
| 3470 | </simpara> | ||
| 3471 | </listitem> | ||
| 3472 | <listitem> | ||
| 3473 | <simpara> | ||
| 3474 | When done with your runs, clean these many thousands | ||
| 3475 | of requests out of your database: | ||
| 3476 | </simpara> | ||
| 3477 | <programlisting>mysql> delete from access_log where agent like 'ApacheBench%'; | ||
| 3478 | mysql> optimize table access_log;</programlisting> | ||
| 3479 | </listitem> | ||
| 3480 | </orderedlist> | ||
| 3481 | </answer> | ||
| 3482 | </qandaentry> | ||
| 3483 | <qandaentry> | ||
| 3484 | <question> | ||
| 3485 | <para> | ||
| 3486 | Do I need to be worried about all the running MySQL | ||
| 3487 | children? Will holding open n Apache-to-MySQL connections | ||
| 3488 | consume a lot of memory? | ||
| 3489 | </para> | ||
| 3490 | </question> | ||
| 3491 | <answer> | ||
| 3492 | <para>Short answer: you shouldn't be worried.</para> | ||
| 3493 | </answer> | ||
| 3494 | <answer> | ||
| 3495 | <para> | ||
| 3496 | Long answer: you might be evaluating at the output of "ps | ||
| 3497 | -aufxw" and becoming alarmed at all the 7MB httpd | ||
| 3498 | processes or 22MB mysqld children that you see. Don't be | ||
| 3499 | alarmed. It's true that mod_log_sql opens and holds open | ||
| 3500 | many MySQL connections: each httpd child maintains one | ||
| 3501 | open database connection (and holds it open for | ||
| 3502 | performance reasons). Four webservers, each running 20 | ||
| 3503 | Apache children, will hold open 80 MySQL connections, | ||
| 3504 | which means that your MySQL server needs to handle 80 | ||
| 3505 | simultaneous connections. In truth, your MySQL server | ||
| 3506 | needs to handle far more than that if traffic to your | ||
| 3507 | website spikes and the Apache webservers spawn off an | ||
| 3508 | additional 30 children each... | ||
| 3509 | </para> | ||
| 3510 | <para> | ||
| 3511 | Fortunately the cost reported by 'ps -aufxw' is deceptive. | ||
| 3512 | This is due to an OS memory-management feature called | ||
| 3513 | "copy-on-write." When you have a number of identical child | ||
| 3514 | processes (e.g. Apache, MySQL), it would appear in "ps" as | ||
| 3515 | though each one occupies a great deal of RAM -- as much as | ||
| 3516 | 7MB per httpd child! In actuality each additional child | ||
| 3517 | only occupies a small bit of extra memory -- most of the | ||
| 3518 | memory pages are common to each child and therefore shared | ||
| 3519 | in a "read-only" fashion. The OS can get away with this | ||
| 3520 | because the majority of memory pages for one child are | ||
| 3521 | identical across all children. Instead of thinking of each | ||
| 3522 | child as a rubber stamp of the others, think of each child | ||
| 3523 | as a basket of links to a common memory area. | ||
| 3524 | </para> | ||
| 3525 | <para> | ||
| 3526 | A memory page is only duplicated when it needs to be | ||
| 3527 | written to, hence "copy-on-write." The result is | ||
| 3528 | efficiency and decreased memory consumption. "ps" may | ||
| 3529 | report 7MB per child, but it might really only "cost" 900K | ||
| 3530 | of extra memory to add one more child. It is not correct | ||
| 3531 | to assume that 20 Apache children with a VSZ of 7MB each | ||
| 3532 | equals (2 x 7MB) of memory consumption -- the real answer | ||
| 3533 | is much, much lower. The same "copy-on-write" rules apply | ||
| 3534 | to all your MySQL children: 40 mysqld children @ 22MB each | ||
| 3535 | do not occupy 880MB of RAM. | ||
| 3536 | </para> | ||
| 3537 | <para> | ||
| 3538 | The bottom line: although there is a cost to spawn extra | ||
| 3539 | httpd or mysqld children, that cost is not as great as | ||
| 3540 | "ps" would lead you to believe. | ||
| 3541 | </para> | ||
| 3542 | </answer> | ||
| 3543 | </qandaentry> | ||
| 3544 | <qandaentry> | ||
| 3545 | <question> | ||
| 3546 | <para> | ||
| 3547 | My webserver cannot handle all the traffic that my site | ||
| 3548 | receives, is there anything I can do? | ||
| 3549 | </para> | ||
| 3550 | </question> | ||
| 3551 | <answer> | ||
| 3552 | <para> | ||
| 3553 | If you have exhausted all the tuning possibilities on your | ||
| 3554 | existing server, it is probably time you evaluated the | ||
| 3555 | benefits of clustering two or more webservers together in | ||
| 3556 | a load-balanced fashion. In fact, users of such a setup | ||
| 3557 | are mod_log_sql's target audience! | ||
| 3558 | </para> | ||
| 3559 | </answer> | ||
| 3560 | </qandaentry> | ||
| 3561 | <qandaentry id="FAQ.DelayedInsert"> | ||
| 3562 | <question> | ||
| 3563 | <para> | ||
| 3564 | What is the issue with activating delayed inserts? | ||
| 3565 | </para> | ||
| 3566 | </question> | ||
| 3567 | <answer> | ||
| 3568 | <para> | ||
| 3569 | INSERT DELAYED is a specific syntax to MySQL and is not | ||
| 3570 | supported by any other database. Ergo, why is it needed, | ||
| 3571 | and what MySQL deficiency is it working around? INSERT | ||
| 3572 | DELAYED is a kluge. | ||
| 3573 | </para> | ||
| 3574 | </answer> | ||
| 3575 | <answer> | ||
| 3576 | <para> | ||
| 3577 | The MySQL documentation is unclear whether INSERT DELAYED | ||
| 3578 | is even necessary for an optimized database. It says, "The | ||
| 3579 | DELAYED option for the INSERT statement is a | ||
| 3580 | MySQL-specific option that is very useful if you have | ||
| 3581 | clients that can't wait for the INSERT to complete." But | ||
| 3582 | then it goes on to say, "Note that as MyISAM tables | ||
| 3583 | supports concurrent SELECT and INSERT, if there is no free | ||
| 3584 | blocks in the middle of the data file, you very seldom | ||
| 3585 | need to use INSERT DELAYED with MyISAM." | ||
| 3586 | </para> | ||
| 3587 | </answer> | ||
| 3588 | <answer> | ||
| 3589 | <para> | ||
| 3590 | Because INSERT DELAYED returns without waiting for the | ||
| 3591 | data to be written, a hard kill of your MySQL database at | ||
| 3592 | the right (wrong?) moment could lose those logfile | ||
| 3593 | entries. | ||
| 3594 | </para> | ||
| 3595 | </answer> | ||
| 3596 | <answer> | ||
| 3597 | <para> | ||
| 3598 | As of MySQL version 3.23.52, the error return functions | ||
| 3599 | disagree after a failed INSERT DELAYED: mysql_errno() | ||
| 3600 | always returns 0, even if mysql_error() returns a textual | ||
| 3601 | error. I have reported this bug to the MySQL folks. | ||
| 3602 | However, we have no way of knowing what solution they will | ||
| 3603 | adopt to fix this, and with the worst case solution | ||
| 3604 | mod_log_sql would not be able to tell if anything went | ||
| 3605 | wrong with a delayed insert. | ||
| 3606 | </para> | ||
| 3607 | </answer> | ||
| 3608 | <answer> | ||
| 3609 | <para> | ||
| 3610 | Instead of delayed inserts, you may wish to utilize InnoDB | ||
| 3611 | tables (instead of the standard MyISAM tables). InnoDB | ||
| 3612 | tables suppot row-level locking and are recommended for | ||
| 3613 | high-volume databases. | ||
| 3614 | </para> | ||
| 3615 | </answer> | ||
| 3616 | <answer> | ||
| 3617 | <para> | ||
| 3618 | If after understanding these problems you still wish to | ||
| 3619 | enable delayed inserts, section | ||
| 3620 | <xref endterm="Sect.DelayedInsert.title" | ||
| 3621 | linkend="Sect.DelayedInsert" /> | ||
| 3622 | discusses how. | ||
| 3623 | </para> | ||
| 3624 | </answer> | ||
| 3625 | </qandaentry> | ||
| 3626 | </qandadiv> | ||
| 3627 | <qandadiv> | ||
| 3628 | <title>"How do I...?" -- accomplishing certain tasks</title> | ||
| 3629 | <qandaentry> | ||
| 3630 | <question> | ||
| 3631 | <para> | ||
| 3632 | How do I extract the data in a format that my analysis | ||
| 3633 | tool can understand? | ||
| 3634 | </para> | ||
| 3635 | </question> | ||
| 3636 | <answer> | ||
| 3637 | <para> | ||
| 3638 | mod_log_sql would be virtually useless if there weren't a | ||
| 3639 | way for you to extract the data from your database in a | ||
| 3640 | somewhat meaningful fashion. To that end there's a Perl | ||
| 3641 | script enclosed with the distribution. That script | ||
| 3642 | (make_combined_log.pl) is designed to extract N-many days | ||
| 3643 | worth of access logs and provide them in a Combined Log | ||
| 3644 | Format output. You can use this very tool right in | ||
| 3645 | /etc/crontab to extract logs on a regular basis so that | ||
| 3646 | your favorite web analysis tool can read them. Or you can | ||
| 3647 | examine the Perl code to construct your own custom tool. | ||
| 3648 | </para> | ||
| 3649 | <para> | ||
| 3650 | For example, let's say that you want your web statistics | ||
| 3651 | updated once per day in the wee hours of the morning. A | ||
| 3652 | good way to accomplish that could be the following entries | ||
| 3653 | in /etc/crontab: | ||
| 3654 | </para> | ||
| 3655 | <programlisting># Generate the temporary apache logs from the MySQL database (for webalizer) | ||
| 3656 | 05 04 * * * root make_combined_log.pl 1 www.grubbybaby.com > /var/log/temp01 | ||
| 3657 | # Run webalizer on httpd log | ||
| 3658 | 30 04 * * * root webalizer -c /etc/webalizer.conf; rm -f /var/log/temp01</programlisting> | ||
| 3659 | <para> | ||
| 3660 | Or if you have a newer system that puts files in | ||
| 3661 | /etc/cron.daily etc., create a file called "webalizer" in | ||
| 3662 | the cron.daily subdirectory. Use the following as the | ||
| 3663 | contents of your file, and make sure to chmod 755 it when | ||
| 3664 | done. | ||
| 3665 | </para> | ||
| 3666 | <programlisting>#!/bin/sh | ||
| 3667 | /usr/local/sbin/make_combined_log.pl 1 www.yourdomain.com > /var/log/httpd/templog | ||
| 3668 | /usr/local/bin/webalizer -q -c /etc/webalizer.conf | ||
| 3669 | rm -f /var/log/httpd/templog</programlisting> | ||
| 3670 | <para>See? Easy.</para> | ||
| 3671 | </answer> | ||
| 3672 | </qandaentry> | ||
| 3673 | <qandaentry id="FAQ.Cookie"> | ||
| 3674 | <question> | ||
| 3675 | <para>How can I log mod_usertrack cookies?</para> | ||
| 3676 | </question> | ||
| 3677 | <answer> | ||
| 3678 | <para> | ||
| 3679 | A number of people like to log mod_usertrack cookies in | ||
| 3680 | their Apache TransferLog to aid in understanding their | ||
| 3681 | visitors' clickstreams. This is accomplished, for example, | ||
| 3682 | with a statement as follows: | ||
| 3683 | </para> | ||
| 3684 | <programlisting>LogFormat "%h %l %u %t \"%r\" %s %b \"%{Referer}i\" \"%{User-Agent}i\"" \"%{cookie}n\""</programlisting> | ||
| 3685 | <para> | ||
| 3686 | Naturally it would be nice for mod_log_sql to permit the | ||
| 3687 | admin to log the cookie data as well, so as of version | ||
| 3688 | 1.10 you can do this. You need to have already compiled | ||
| 3689 | mod_usertrack into httpd -- it's one of the standard | ||
| 3690 | Apache modules. | ||
| 3691 | </para> | ||
| 3692 | <para> | ||
| 3693 | First make sure you have a column called "cookie" in the | ||
| 3694 | MySQL database to hold the cookies, which can be done as | ||
| 3695 | follows if you already have a working database: | ||
| 3696 | </para> | ||
| 3697 | <programlisting>mysql> alter table acc_log_tbl add column cookie varchar(255);</programlisting> | ||
| 3698 | <para> | ||
| 3699 | Next configure your server to set usertracking cookies as | ||
| 3700 | follows, and make sure you include the new 'c' directive | ||
| 3701 | in your LogSQLTransferLogFormat, which activates cookie | ||
| 3702 | logging. Here's an example: | ||
| 3703 | </para> | ||
| 3704 | <programlisting><VirtualHost 1.2.3.4> | ||
| 3705 | CookieTracking on | ||
| 3706 | CookieStyle Cookie | ||
| 3707 | CookieName Foobar | ||
| 3708 | LogSQLTransferLogFormat huSUsbTvRAc | ||
| 3709 | LogSQLWhichCookie Foobar | ||
| 3710 | </VirtualHost></programlisting> | ||
| 3711 | <para> | ||
| 3712 | The first three lines configure mod_usertrack to create a | ||
| 3713 | COOKIE (RFC 2109) format cookie called Foobar. The last | ||
| 3714 | two lines tell mod_log_sql to log cookies named Foobar. | ||
| 3715 | You have to choose which cookie to log because more than | ||
| 3716 | one cookie can/will be sent to the server by the client. | ||
| 3717 | </para> | ||
| 3718 | <para> | ||
| 3719 | Recap: the 'c' character activates cookie logging, and the | ||
| 3720 | LogSQLWhichCookie directive chooses which cookie to log. | ||
| 3721 | </para> | ||
| 3722 | <para> | ||
| 3723 | FYI, you are advised NOT to use CookieStyle Cookie2 -- it | ||
| 3724 | seems that even newer browsers (IE 5.5, etc.) have trouble | ||
| 3725 | with the new COOKIE2 (RFC 2965) format. Just stick with | ||
| 3726 | the standard COOKIE format and you'll be fine. | ||
| 3727 | </para> | ||
| 3728 | <para> | ||
| 3729 | Perform some hits on your server and run a select | ||
| 3730 | </para> | ||
| 3731 | <programlisting>SELECT request_uri,cookie | ||
| 3732 | FROM access_log | ||
| 3733 | WHERE cookie IS NOT NULL;</programlisting> | ||
| 3734 | <table> | ||
| 3735 | <title></title> | ||
| 3736 | <tgroup cols="2"> | ||
| 3737 | <colspec colname="1" /> | ||
| 3738 | <colspec colname="2" /> | ||
| 3739 | <thead> | ||
| 3740 | <row> | ||
| 3741 | <entry colname="1">request_uri</entry> | ||
| 3742 | <entry colname="2">cookie</entry> | ||
| 3743 | </row> | ||
| 3744 | </thead> | ||
| 3745 | <tbody> | ||
| 3746 | <row> | ||
| 3747 | <entry colname="1">/mod_log_sql/</entry> | ||
| 3748 | <entry colname="2"> | ||
| 3749 | ool-18e4.dyn.optonline.net.130051007102700823 | ||
| 3750 | </entry> | ||
| 3751 | </row> | ||
| 3752 | <row> | ||
| 3753 | <entry colname="1">/mod_log_sql/usa.gif</entry> | ||
| 3754 | <entry colname="2"> | ||
| 3755 | ool-18e4.dyn.optonline.net.130051007102700823 | ||
| 3756 | </entry> | ||
| 3757 | </row> | ||
| 3758 | <row> | ||
| 3759 | <entry colname="1">/mod_log_sql/style_1.css</entry> | ||
| 3760 | <entry colname="2"> | ||
| 3761 | ool-18e4.dyn.optonline.net.130051007102700823 | ||
| 3762 | </entry> | ||
| 3763 | </row> | ||
| 3764 | </tbody> | ||
| 3765 | </tgroup> | ||
| 3766 | </table> | ||
| 3767 | </answer> | ||
| 3768 | </qandaentry> | ||
| 3769 | <qandaentry> | ||
| 3770 | <question> | ||
| 3771 | <para> | ||
| 3772 | What if I want to log more than one cookie? What is the | ||
| 3773 | difference between LogSQLWhichCookie and | ||
| 3774 | LogSQLWhichCookies? | ||
| 3775 | </para> | ||
| 3776 | </question> | ||
| 3777 | <answer> | ||
| 3778 | <para> | ||
| 3779 | As of version 1.17, you have a choice in how you want | ||
| 3780 | cookie logging handled. | ||
| 3781 | </para> | ||
| 3782 | <para> | ||
| 3783 | If you are interested in logging only one cookie per | ||
| 3784 | request, follow the instructions in FAQ entry | ||
| 3785 | <xref linkend="FAQ.Cookie" /> | ||
| 3786 | above. That cookie will be logged to a column in the | ||
| 3787 | regular access_log table, and the actual cookie you want | ||
| 3788 | to log is specified with LogSQLWhichCookie. Don't forget | ||
| 3789 | to specify the 'c' character in LogSQLTransferLogFormat. | ||
| 3790 | </para> | ||
| 3791 | <para> | ||
| 3792 | If, however, you need to log multiple cookies per request, | ||
| 3793 | you must employ the LogSQLWhichCookies (note the plural) | ||
| 3794 | directive. The cookies you specify will be logged to a | ||
| 3795 | separate table (as discussed in section | ||
| 3796 | <xref endterm="Sect.MultiTable.title" | ||
| 3797 | linkend="Sect.MultiTable" /> | ||
| 3798 | ), and entries in that table will be linked to the regular | ||
| 3799 | access_log entries via the unique ID that is supplied by | ||
| 3800 | mod_unique_id. Without mod_unique_id the information will | ||
| 3801 | still be logged but you will be unable to correlate which | ||
| 3802 | cookies go with which access-requests. Furthermore, with | ||
| 3803 | LogSQLWhichCookies, you do not need to include the 'c' | ||
| 3804 | character in LogSQLTransferLogFormat. | ||
| 3805 | </para> | ||
| 3806 | <para> | ||
| 3807 | LogSQLWhichCookie and LogSQLWhichCookies can coexist | ||
| 3808 | without conflict because they operate on entireley | ||
| 3809 | different tables, but you're better off choosing the one | ||
| 3810 | you need. | ||
| 3811 | </para> | ||
| 3812 | </answer> | ||
| 3813 | </qandaentry> | ||
| 3814 | <qandaentry> | ||
| 3815 | <question> | ||
| 3816 | <para> | ||
| 3817 | What are the SSL logging features, and how do I activate | ||
| 3818 | them? | ||
| 3819 | </para> | ||
| 3820 | </question> | ||
| 3821 | <answer> | ||
| 3822 | <note> | ||
| 3823 | <para> | ||
| 3824 | You do not need to compile SSL support into mod_log_sql | ||
| 3825 | in order to simply use it with a secure site. You only | ||
| 3826 | need to compile SSL support into mod_log_sql if you want | ||
| 3827 | to log SSL-specific data such as the cipher type used, | ||
| 3828 | or the keysize that was negotiated. If that information | ||
| 3829 | is unimportant to you, you can ignore this FAQ. | ||
| 3830 | </para> | ||
| 3831 | </note> | ||
| 3832 | <para> | ||
| 3833 | By adding certain characters to your | ||
| 3834 | LogSQLTransferLogFormat string you can tell mod_log_sql to | ||
| 3835 | log the SSL cipher, the SSL keysize of the connection, and | ||
| 3836 | the maximum keysize that was available. This would let you | ||
| 3837 | tell, for example, which clients were using only | ||
| 3838 | export-grade security to access your secure software area. | ||
| 3839 | </para> | ||
| 3840 | <para> | ||
| 3841 | You can compile mod_log_sql with SSL logging support if | ||
| 3842 | you have the right packages installed. If you already have | ||
| 3843 | an SSL-enabled Apache then you by definition have the | ||
| 3844 | correct packages already installed: OpenSSL and mod_ssl. | ||
| 3845 | </para> | ||
| 3846 | <para> | ||
| 3847 | You need to ensure that your database is set up to log the | ||
| 3848 | SSL data. Issue the following commands to MySQL if your | ||
| 3849 | access table does not already have them: | ||
| 3850 | </para> | ||
| 3851 | <programlisting>mysql> alter table access_log add column ssl_cipher varchar(25); | ||
| 3852 | mysql> alter table access_log add column ssl_keysize smallint unsigned; | ||
| 3853 | mysql> alter table access_log add column ssl_maxkeysize smallint unsigned;</programlisting> | ||
| 3854 | <para> | ||
| 3855 | Finally configure httpd.conf to activate the SSL fields. | ||
| 3856 | Note that this is only meaningful in a VirtualHost that is | ||
| 3857 | set up for SSL. | ||
| 3858 | </para> | ||
| 3859 | <programlisting><VirtualHost 1.2.3.4:443> | ||
| 3860 | LogSQLTransferLogFormat AbHhmRSsTUuvcQqz | ||
| 3861 | </VirtualHost></programlisting> | ||
| 3862 | <para> | ||
| 3863 | You also need to make sure you have the mod_log_sql_ssl | ||
| 3864 | module loaded as well. | ||
| 3865 | </para> | ||
| 3866 | <para> | ||
| 3867 | The last three characters (Qqz) in the directive are the | ||
| 3868 | SSL ones; see section | ||
| 3869 | <xref linkend="Conf.LogSQLTransferLogFormat" /> | ||
| 3870 | in the directives documentation for details of the | ||
| 3871 | LogSQLTransferLogFormat directive. | ||
| 3872 | </para> | ||
| 3873 | <para> | ||
| 3874 | Restart Apache, then perform some hits on your server. | ||
| 3875 | Then run the following select statement: | ||
| 3876 | </para> | ||
| 3877 | <programlisting>SELECT remote_host,request_uri,ssl_cipher,ssl_keysize,ssl_maxkeysize | ||
| 3878 | FROM access_log | ||
| 3879 | WHERE ssl_cipher IS NOT NULL;</programlisting> | ||
| 3880 | <table> | ||
| 3881 | <title></title> | ||
| 3882 | <tgroup cols="5"> | ||
| 3883 | <colspec colname="1" /> | ||
| 3884 | <colspec colname="2" /> | ||
| 3885 | <colspec colname="3" /> | ||
| 3886 | <colspec colname="4" /> | ||
| 3887 | <colspec colname="5" /> | ||
| 3888 | <thead> | ||
| 3889 | <row> | ||
| 3890 | <entry colname="1">remote_host</entry> | ||
| 3891 | <entry colname="2">request_uri</entry> | ||
| 3892 | <entry colname="3">ssl_cipher</entry> | ||
| 3893 | <entry colname="4">ssl_keysize</entry> | ||
| 3894 | <entry colname="5">ssl_maxkeysize</entry> | ||
| 3895 | </row> | ||
| 3896 | </thead> | ||
| 3897 | <tbody> | ||
| 3898 | <row> | ||
| 3899 | <entry colname="1">216.192.52.4</entry> | ||
| 3900 | <entry colname="2">/dir/somefile.html</entry> | ||
| 3901 | <entry colname="3">RC4-MD5</entry> | ||
| 3902 | <entry colname="4">128</entry> | ||
| 3903 | <entry colname="5">128</entry> | ||
| 3904 | </row> | ||
| 3905 | <row> | ||
| 3906 | <entry colname="1">216.192.52.4</entry> | ||
| 3907 | <entry colname="2">/dir/somefile.gif</entry> | ||
| 3908 | <entry colname="3">RC4-MD5</entry> | ||
| 3909 | <entry colname="4">128</entry> | ||
| 3910 | <entry colname="5">128</entry> | ||
| 3911 | </row> | ||
| 3912 | <row> | ||
| 3913 | <entry colname="1">216.192.52.4</entry> | ||
| 3914 | <entry colname="2">/dir/somefile.jpg</entry> | ||
| 3915 | <entry colname="3">RC4-MD5</entry> | ||
| 3916 | <entry colname="4">128</entry> | ||
| 3917 | <entry colname="5">128</entry> | ||
| 3918 | </row> | ||
| 3919 | </tbody> | ||
| 3920 | </tgroup> | ||
| 3921 | </table> | ||
| 3922 | </answer> | ||
| 3923 | </qandaentry> | ||
| 3924 | </qandadiv> | ||
| 3925 | </qandaset> | ||
| 3926 | </section> | ||
| 3927 | </article> | ||
diff --git a/include/apache13.h b/include/apache13.h new file mode 100644 index 0000000..5659420 --- /dev/null +++ b/include/apache13.h | |||
| @@ -0,0 +1,101 @@ | |||
| 1 | /* $Id: apache13.h 175 2007-10-20 13:18:20Z urkle@drip.ws $ */ | ||
| 2 | #ifndef APACHE13_H | ||
| 3 | #define APACHE13_H | ||
| 4 | |||
| 5 | #include "httpd.h" | ||
| 6 | #include "http_config.h" | ||
| 7 | #include "http_log.h" | ||
| 8 | #include "http_core.h" | ||
| 9 | |||
| 10 | /* Defines */ | ||
| 11 | #define AP_MODULE_DECLARE_DATA MODULE_VAR_EXPORT | ||
| 12 | #define APR_OFF_T_FMT "ld" | ||
| 13 | #define APR_PID_T_FMT "d" | ||
| 14 | #define APR_SUCCESS 0 | ||
| 15 | #define APR_OFFSETOF XtOffsetOf | ||
| 16 | |||
| 17 | /** method of declaring a directive with raw argument parsing */ | ||
| 18 | # define AP_INIT_RAW_ARGS(directive, func, mconfig, where, help) \ | ||
| 19 | { directive, func, mconfig, where, RAW_ARGS, help } | ||
| 20 | /** method of declaring a directive which takes 1 argument */ | ||
| 21 | # define AP_INIT_TAKE1(directive, func, mconfig, where, help) \ | ||
| 22 | { directive, func, mconfig, where, TAKE1, help } | ||
| 23 | /** method of declaring a directive which takes 2 argument */ | ||
| 24 | # define AP_INIT_TAKE2(directive, func, mconfig, where, help) \ | ||
| 25 | { directive, func, mconfig, where, TAKE2, help } | ||
| 26 | /** method of declaring a directive which takes multiple arguments */ | ||
| 27 | # define AP_INIT_ITERATE(directive, func, mconfig, where, help) \ | ||
| 28 | { directive, func, mconfig, where, ITERATE, help } | ||
| 29 | /** method of declaring a directive which takes 1 or 3 arguments */ | ||
| 30 | # define AP_INIT_TAKE13(directive, func, mconfig, where, help) \ | ||
| 31 | { directive, func, mconfig, where, TAKE13, help } | ||
| 32 | /** method of declaring a directive which takes 3 arguments */ | ||
| 33 | # define AP_INIT_TAKE3(directive, func, mconfig, where, help) \ | ||
| 34 | { directive, func, mconfig, where, TAKE3, help } | ||
| 35 | /** method of declaring a directive which takes a flag (on/off) as an argument */ | ||
| 36 | # define AP_INIT_FLAG(directive, func, mconfig, where, help) \ | ||
| 37 | { directive, func, mconfig, where, FLAG, help } | ||
| 38 | |||
| 39 | /* Types */ | ||
| 40 | #define apr_pool_t pool | ||
| 41 | #define apr_array_header_t array_header | ||
| 42 | #define apr_table_t table | ||
| 43 | |||
| 44 | #define apr_status_t int | ||
| 45 | #define apr_uri_t uri_components | ||
| 46 | |||
| 47 | /* Functions */ | ||
| 48 | #define ap_get_remote_host(a,b,c,d) ap_get_remote_host(a,b,c) | ||
| 49 | #define ap_set_deprecated NULL | ||
| 50 | |||
| 51 | #define apr_uri_unparse ap_unparse_uri_components | ||
| 52 | #define apr_uri_parse ap_parse_uri_components | ||
| 53 | #define ap_add_version_component(p,s) ap_add_version_component(s) | ||
| 54 | |||
| 55 | #define apr_pool_create(a,b) *(a) = ap_make_sub_pool(b) | ||
| 56 | #define apr_pool_destroy ap_destroy_pool | ||
| 57 | #define apr_pool_cleanup_register ap_register_cleanup | ||
| 58 | #define apr_palloc ap_palloc | ||
| 59 | #define apr_pcalloc ap_pcalloc | ||
| 60 | #define apr_pstrdup ap_pstrdup | ||
| 61 | #define apr_pstrcat ap_pstrcat | ||
| 62 | #define apr_psprintf ap_psprintf | ||
| 63 | #define apr_snprintf ap_snprintf | ||
| 64 | #define ap_strchr strchr | ||
| 65 | #define ap_strchr_c strchr | ||
| 66 | #define ap_strstr strstr | ||
| 67 | #define ap_strstr_c strstr | ||
| 68 | |||
| 69 | #define apr_table_set ap_table_set | ||
| 70 | #define apr_table_get ap_table_get | ||
| 71 | #define apr_table_make ap_make_table | ||
| 72 | |||
| 73 | #define apr_array_push ap_push_array | ||
| 74 | #define apr_array_make ap_make_array | ||
| 75 | #define apr_array_cat ap_array_cat | ||
| 76 | #define apr_is_empty_array(t) (((t) == NULL)||((t)->nelts == 0)) | ||
| 77 | |||
| 78 | #define apr_file_t FILE | ||
| 79 | #define apr_file_printf fprintf | ||
| 80 | |||
| 81 | #define apr_tolower ap_tolower | ||
| 82 | |||
| 83 | void log_error(char *file, int line, int level, apr_status_t status, | ||
| 84 | const server_rec *s, const char *fmt, ...) __attribute__ ((format (printf, 6,7))); | ||
| 85 | |||
| 86 | #ifndef WIN32 | ||
| 87 | inline | ||
| 88 | #endif | ||
| 89 | void log_error(char *file, int line, int level, | ||
| 90 | apr_status_t status, const server_rec *s, const char *fmt, ...) | ||
| 91 | { | ||
| 92 | static char buff[MAX_STRING_LEN]; | ||
| 93 | va_list args; | ||
| 94 | va_start(args, fmt); | ||
| 95 | ap_vsnprintf(buff,MAX_STRING_LEN, fmt,args); | ||
| 96 | ap_log_error(file,line,level | APLOG_NOERRNO ,s,"%s",buff); | ||
| 97 | va_end(args); | ||
| 98 | } | ||
| 99 | |||
| 100 | |||
| 101 | #endif /* APACHE13_H */ | ||
diff --git a/include/apache20.h b/include/apache20.h new file mode 100644 index 0000000..4c755ab --- /dev/null +++ b/include/apache20.h | |||
| @@ -0,0 +1,25 @@ | |||
| 1 | /* $Id: apache20.h 125 2004-04-29 18:05:25Z urkle@drip.ws $ */ | ||
| 2 | #ifndef APACHE20_H | ||
| 3 | #define APACHE20_H | ||
| 4 | |||
| 5 | #include "apr_strings.h" | ||
| 6 | #include "apr_lib.h" | ||
| 7 | #include "apr_hash.h" | ||
| 8 | #include "apr_optional.h" | ||
| 9 | #define APR_WANT_STRFUNC | ||
| 10 | #include "apr_want.h" | ||
| 11 | #include "apr_tables.h" | ||
| 12 | |||
| 13 | #include "ap_config.h" | ||
| 14 | |||
| 15 | #include "httpd.h" | ||
| 16 | #include "http_config.h" | ||
| 17 | #include "http_core.h" | ||
| 18 | #include "http_log.h" | ||
| 19 | #include "http_protocol.h" | ||
| 20 | |||
| 21 | #include "util_time.h" | ||
| 22 | |||
| 23 | #define log_error ap_log_error | ||
| 24 | |||
| 25 | #endif /* APACHE20_H */ | ||
diff --git a/include/mod_log_sql.h b/include/mod_log_sql.h new file mode 100644 index 0000000..c1d9bff --- /dev/null +++ b/include/mod_log_sql.h | |||
| @@ -0,0 +1,166 @@ | |||
| 1 | /* $Id$ */ | ||
| 2 | |||
| 3 | #ifndef MOD_LOG_SQL_H | ||
| 4 | #define MOD_LOG_SQL_H | ||
| 5 | |||
| 6 | /* Create a set of LOGSQL_DECLARE(type), LOGSQL_DECLARE_NONSTD(type) and | ||
| 7 | * LOGSQL_DECLARE_DATA with appropriate export and import tags for the platform | ||
| 8 | */ | ||
| 9 | #if !defined(WIN32) | ||
| 10 | #define LOGSQL_DECLARE(type) type | ||
| 11 | #define LOGSQL_DECLARE_NONSTD(type) type | ||
| 12 | #define LOGSQL_DECLARE_DATA | ||
| 13 | #elif defined(LOGSQL_DECLARE_STATIC) | ||
| 14 | #define LOGSQL_DECLARE(type) type __stdcall | ||
| 15 | #define LOGSQL_DECLARE_NONSTD(type) type | ||
| 16 | #define LOGSQL_DECLARE_DATA | ||
| 17 | #elif defined(LOGSQL_DECLARE_EXPORT) | ||
| 18 | #define LOGSQL_DECLARE(type) __declspec(dllexport) type __stdcall | ||
| 19 | #define LOGSQL_DECLARE_NONSTD(type) __declspec(dllexport) type | ||
| 20 | #define LOGSQL_DECLARE_DATA __declspec(dllexport) | ||
| 21 | #else | ||
| 22 | #define LOGSQL_DECLARE(type) __declspec(dllimport) type __stdcall | ||
| 23 | #define LOGSQL_DECLARE_NONSTD(type) __declspec(dllimport) type | ||
| 24 | #define LOGSQL_DECLARE_DATA __declspec(dllimport) | ||
| 25 | #endif | ||
| 26 | |||
| 27 | #define LOG_SQL_PLUGIN_VERSION 20080318 | ||
| 28 | |||
| 29 | /* Registration function for extract functions */ | ||
| 30 | |||
| 31 | typedef const char *logsql_item_func(request_rec *r, char *a); | ||
| 32 | |||
| 33 | |||
| 34 | typedef enum { | ||
| 35 | LOGSQL_FUNCTION_REQ_FINAL = 0, | ||
| 36 | LOGSQL_FUNCTION_REQ_ORIG | ||
| 37 | } logsql_function_req; | ||
| 38 | |||
| 39 | LOGSQL_DECLARE(void) log_sql_register_function(apr_pool_t *p, | ||
| 40 | const char *alias, logsql_item_func *func, | ||
| 41 | logsql_function_req want_orig_default); | ||
| 42 | |||
| 43 | LOGSQL_DECLARE(void) log_sql_register_alias(server_rec *s, apr_pool_t *p, | ||
| 44 | char key, const char *alias); | ||
| 45 | |||
| 46 | typedef enum { | ||
| 47 | LOGSQL_DATATYPE_INT = 0, | ||
| 48 | LOGSQL_DATATYPE_SMALLINT, | ||
| 49 | LOGSQL_DATATYPE_VARCHAR, | ||
| 50 | LOGSQL_DATATYPE_CHAR, | ||
| 51 | LOGSQL_DATATYPE_BIGINT | ||
| 52 | } logsql_field_datatype; | ||
| 53 | |||
| 54 | LOGSQL_DECLARE(void) log_sql_register_field(apr_pool_t *p, | ||
| 55 | const char *alias, | ||
| 56 | const char *funcalias, const char *param, | ||
| 57 | const char *sql_field_name, | ||
| 58 | logsql_field_datatype datatype, apr_size_t size); | ||
| 59 | |||
| 60 | LOGSQL_DECLARE(void) log_sql_register_finish(server_rec *s); | ||
| 61 | |||
| 62 | /* DB Connection structure holds connection handle */ | ||
| 63 | typedef struct { | ||
| 64 | int connected; /* Are we connected to the DB */ | ||
| 65 | void *handle; /* DB specific connection pointer */ | ||
| 66 | apr_pool_t *p; /* Pool to allocate handle off of */ | ||
| 67 | apr_table_t *parms; /* DB connection parameters */ | ||
| 68 | } logsql_dbconnection; | ||
| 69 | |||
| 70 | /* open db handle return values*/ | ||
| 71 | typedef enum { | ||
| 72 | LOGSQL_OPENDB_FAIL = 0, | ||
| 73 | LOGSQL_OPENDB_SUCCESS, | ||
| 74 | LOGSQL_OPENDB_ALREADY, | ||
| 75 | LOGSQL_OPENDB_PRESERVE | ||
| 76 | } logsql_opendb_ret; | ||
| 77 | |||
| 78 | typedef enum { | ||
| 79 | LOGSQL_QUERY_SUCCESS = 0, | ||
| 80 | LOGSQL_QUERY_FAIL, | ||
| 81 | LOGSQL_QUERY_NOLINK, | ||
| 82 | LOGSQL_QUERY_NOTABLE, | ||
| 83 | LOGSQL_QUERY_PRESERVED | ||
| 84 | } logsql_query_ret; | ||
| 85 | |||
| 86 | typedef enum { | ||
| 87 | LOGSQL_TABLE_SUCCESS = 0, | ||
| 88 | LOGSQL_TABLE_FAIL | ||
| 89 | } logsql_table_ret; | ||
| 90 | |||
| 91 | /* Table type to create/log to */ | ||
| 92 | typedef enum { | ||
| 93 | LOGSQL_TABLE_ACCESS = 0, | ||
| 94 | LOGSQL_TABLE_NOTES, | ||
| 95 | LOGSQL_TABLE_HEADERSOUT, | ||
| 96 | LOGSQL_TABLE_HEADERSIN, | ||
| 97 | LOGSQL_TABLE_COOKIES | ||
| 98 | } logsql_tabletype; | ||
| 99 | |||
| 100 | /* All Tables */ | ||
| 101 | #define LOGSQL_TABLE_ALL LOGSQL_TABLE_ACCESS | LOGSQL_TABLE_NOTES | \ | ||
| 102 | LOGSQL_TABLE_HEADERSIN | LOGSQL_TABLE_HEADERSOUT | LOGSQL_TABLE_COOKIES | ||
| 103 | |||
| 104 | /* MySQL module calls */ | ||
| 105 | |||
| 106 | /* Registration function for database drivers */ | ||
| 107 | |||
| 108 | typedef struct { | ||
| 109 | /* name of the provider */ | ||
| 110 | const char *providername; | ||
| 111 | /* NULL terminated list of drivers strings */ | ||
| 112 | const char **provided_drivers; | ||
| 113 | /* create a connection to the underlying database layer */ | ||
| 114 | logsql_opendb_ret (*connect)(server_rec *s, logsql_dbconnection *db); | ||
| 115 | /* disconnect from the underlying database layer */ | ||
| 116 | void (*disconnect)(logsql_dbconnection *db); | ||
| 117 | /* escape the SQL statement according to database rules */ | ||
| 118 | const char *(*escape)(request_rec *r,const char *from_str, apr_pool_t *p, | ||
| 119 | logsql_dbconnection *db); | ||
| 120 | /* insert a SQL query statement */ | ||
| 121 | logsql_query_ret (*insert)(request_rec *r,logsql_dbconnection *db, | ||
| 122 | const char *query); | ||
| 123 | /* create a SQL table named table_name of table_type */ | ||
| 124 | logsql_table_ret (*create_table)(request_rec *r, logsql_dbconnection *db, | ||
| 125 | logsql_tabletype table_type, const char *table_name); | ||
| 126 | } logsql_dbdriver; | ||
| 127 | |||
| 128 | LOGSQL_DECLARE(void) log_sql_register_driver(apr_pool_t *p, | ||
| 129 | logsql_dbdriver *driver); | ||
| 130 | |||
| 131 | /* Module initialization Macros */ | ||
| 132 | #define LOGSQL_MODULE(driver) log_sql_##driver##_module | ||
| 133 | #if defined(WITH_APACHE20) | ||
| 134 | # define LOGSQL_MODULE_FORWARD(driver) module AP_MODULE_DECLARE_DATA LOGSQL_MODULE(driver) | ||
| 135 | # define LOGSQL_REGISTER(driver) \ | ||
| 136 | static int post_config(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s); \ | ||
| 137 | static void register_hooks(apr_pool_t *p) { \ | ||
| 138 | ap_hook_post_config(post_config, NULL, NULL, APR_HOOK_REALLY_FIRST); \ | ||
| 139 | } \ | ||
| 140 | \ | ||
| 141 | LOGSQL_MODULE_FORWARD(driver) = { \ | ||
| 142 | STANDARD20_MODULE_STUFF, \ | ||
| 143 | NULL, NULL, NULL, NULL, NULL, register_hooks }; \ | ||
| 144 | static int post_config(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s) | ||
| 145 | #elif defined(WITH_APACHE13) | ||
| 146 | # define LOGSQL_MODULE_FORWARD(driver) module MODULE_VAR_EXPORT LOGSQL_MODULE(driver) | ||
| 147 | # define LOGSQL_REGISTER(driver) \ | ||
| 148 | static void module_init(server_rec *s, apr_pool_t *p); \ | ||
| 149 | LOGSQL_MODULE_FORWARD(driver) = { \ | ||
| 150 | STANDARD_MODULE_STUFF, module_init }; \ | ||
| 151 | static void module_init(server_rec *s, apr_pool_t *p) | ||
| 152 | #endif | ||
| 153 | |||
| 154 | #if defined(WITH_APACHE20) | ||
| 155 | # define LOGSQL_SHUTDOWN \ | ||
| 156 | static | ||
| 157 | #endif | ||
| 158 | |||
| 159 | |||
| 160 | #if defined(WITH_APACHE20) | ||
| 161 | #define LOGSQL_REGISTER_RETURN log_sql_register_finish(s); return OK; | ||
| 162 | #elif defined(WITH_APACHE13) | ||
| 163 | #define LOGSQL_REGISTER_RETURN log_sql_register_finish(s); | ||
| 164 | #endif | ||
| 165 | |||
| 166 | #endif /* MOD_LOG_SQL_H */ | ||
diff --git a/include/winconfig.h b/include/winconfig.h new file mode 100644 index 0000000..702c35f --- /dev/null +++ b/include/winconfig.h | |||
| @@ -0,0 +1,78 @@ | |||
| 1 | /* config.h. Generated by configure. */ | ||
| 2 | /* config.h.in. Generated from configure.ac by autoheader. */ | ||
| 3 | |||
| 4 | /* Define to 1 if you have the <inttypes.h> header file. */ | ||
| 5 | /* #undef HAVE_INTTYPES_H */ | ||
| 6 | |||
| 7 | /* Define to 1 if you have the `m' library (-lm). */ | ||
| 8 | /* #undef HAVE_LIBM */ | ||
| 9 | |||
| 10 | /* Define to 1 if you have the `mysqlclient' library (-lmysqlclient). */ | ||
| 11 | #define HAVE_LIBMYSQLCLIENT 1 | ||
| 12 | |||
| 13 | /* Define to 1 if you have the `z' library (-lz). */ | ||
| 14 | /* #undef HAVE_LIBZ */ | ||
| 15 | |||
| 16 | /* Define to 1 if you have the <limits.h> header file. */ | ||
| 17 | #define HAVE_LIMITS_H 1 | ||
| 18 | |||
| 19 | /* Define to 1 if you have the <memory.h> header file. */ | ||
| 20 | #define HAVE_MEMORY_H 1 | ||
| 21 | |||
| 22 | /* Define to 1 if you have the <mod_ssl.h> header file. */ | ||
| 23 | /* #undef HAVE_MOD_SSL_H */ | ||
| 24 | |||
| 25 | /* Define to 1 if you have the `mysql_real_escape_string' function. */ | ||
| 26 | #define HAVE_MYSQL_REAL_ESCAPE_STRING 1 | ||
| 27 | |||
| 28 | /* Define to 1 if you have the <stdint.h> header file. */ | ||
| 29 | /* #undef HAVE_STDINT_H */ | ||
| 30 | |||
| 31 | /* Define to 1 if you have the <stdlib.h> header file. */ | ||
| 32 | /* #undef HAVE_STDLIB_H 1 | ||
| 33 | |||
| 34 | /* Define to 1 if you have the <strings.h> header file. */ | ||
| 35 | /* #undef HAVE_STRINGS_H */ | ||
| 36 | |||
| 37 | /* Define to 1 if you have the <string.h> header file. */ | ||
| 38 | /* #undef HAVE_STRING_H */ | ||
| 39 | |||
| 40 | /* Define to 1 if you have the <sys/stat.h> header file. */ | ||
| 41 | /* #undef HAVE_SYS_STAT_H */ | ||
| 42 | |||
| 43 | /* Define to 1 if you have the <sys/types.h> header file. */ | ||
| 44 | /* #undef HAVE_SYS_TYPES_H */ | ||
| 45 | |||
| 46 | /* Define to 1 if you have the <unistd.h> header file. */ | ||
| 47 | /* #undef HAVE_UNISTD_H */ | ||
| 48 | |||
| 49 | /* Define to the address where bug reports for this package should be sent. */ | ||
| 50 | #define PACKAGE_BUGREPORT "" | ||
| 51 | |||
| 52 | /* Define to the full name of this package. */ | ||
| 53 | #define PACKAGE_NAME "mod_log_sql" | ||
| 54 | |||
| 55 | /* Define to the full name and version of this package. */ | ||
| 56 | #define PACKAGE_STRING "mod_log_sql 1.100" | ||
| 57 | |||
| 58 | /* Define to the one symbol short name of this package. */ | ||
| 59 | #define PACKAGE_TARNAME "mod-log-sql" | ||
| 60 | |||
| 61 | /* Define to the version of this package. */ | ||
| 62 | #define PACKAGE_VERSION "1.100" | ||
| 63 | |||
| 64 | /* Define to 1 if you have the ANSI C header files. */ | ||
| 65 | /* #undef STDC_HEADERS */ | ||
| 66 | |||
| 67 | /* Define if we want to compile in SSL support. */ | ||
| 68 | /* #undef WANT_SSL_LOGGING */ | ||
| 69 | |||
| 70 | /* Define to 1 if we are compiling with Apache 1.3.x */ | ||
| 71 | /* #undef WITH_APACHE13 */ | ||
| 72 | |||
| 73 | /* Define to 1 if we are compiling with Apache 2.0.x */ | ||
| 74 | /* #undef WITH_APACHE20 */ | ||
| 75 | |||
| 76 | /* Define to 1 if we are compiling with mysql */ | ||
| 77 | #define WITH_MYSQL 1 | ||
| 78 | |||
diff --git a/install-sh b/install-sh new file mode 100755 index 0000000..e9de238 --- /dev/null +++ b/install-sh | |||
| @@ -0,0 +1,251 @@ | |||
| 1 | #!/bin/sh | ||
| 2 | # | ||
| 3 | # install - install a program, script, or datafile | ||
| 4 | # This comes from X11R5 (mit/util/scripts/install.sh). | ||
| 5 | # | ||
| 6 | # Copyright 1991 by the Massachusetts Institute of Technology | ||
| 7 | # | ||
| 8 | # Permission to use, copy, modify, distribute, and sell this software and its | ||
| 9 | # documentation for any purpose is hereby granted without fee, provided that | ||
| 10 | # the above copyright notice appear in all copies and that both that | ||
| 11 | # copyright notice and this permission notice appear in supporting | ||
| 12 | # documentation, and that the name of M.I.T. not be used in advertising or | ||
| 13 | # publicity pertaining to distribution of the software without specific, | ||
| 14 | # written prior permission. M.I.T. makes no representations about the | ||
| 15 | # suitability of this software for any purpose. It is provided "as is" | ||
| 16 | # without express or implied warranty. | ||
| 17 | # | ||
| 18 | # Calling this script install-sh is preferred over install.sh, to prevent | ||
| 19 | # `make' implicit rules from creating a file called install from it | ||
| 20 | # when there is no Makefile. | ||
| 21 | # | ||
| 22 | # This script is compatible with the BSD install script, but was written | ||
| 23 | # from scratch. It can only install one file at a time, a restriction | ||
| 24 | # shared with many OS's install programs. | ||
| 25 | |||
| 26 | |||
| 27 | # set DOITPROG to echo to test this script | ||
| 28 | |||
| 29 | # Don't use :- since 4.3BSD and earlier shells don't like it. | ||
| 30 | doit="${DOITPROG-}" | ||
| 31 | |||
| 32 | |||
| 33 | # put in absolute paths if you don't have them in your path; or use env. vars. | ||
| 34 | |||
| 35 | mvprog="${MVPROG-mv}" | ||
| 36 | cpprog="${CPPROG-cp}" | ||
| 37 | chmodprog="${CHMODPROG-chmod}" | ||
| 38 | chownprog="${CHOWNPROG-chown}" | ||
| 39 | chgrpprog="${CHGRPPROG-chgrp}" | ||
| 40 | stripprog="${STRIPPROG-strip}" | ||
| 41 | rmprog="${RMPROG-rm}" | ||
| 42 | mkdirprog="${MKDIRPROG-mkdir}" | ||
| 43 | |||
| 44 | transformbasename="" | ||
| 45 | transform_arg="" | ||
| 46 | instcmd="$mvprog" | ||
| 47 | chmodcmd="$chmodprog 0755" | ||
| 48 | chowncmd="" | ||
| 49 | chgrpcmd="" | ||
| 50 | stripcmd="" | ||
| 51 | rmcmd="$rmprog -f" | ||
| 52 | mvcmd="$mvprog" | ||
| 53 | src="" | ||
| 54 | dst="" | ||
| 55 | dir_arg="" | ||
| 56 | |||
| 57 | while [ x"$1" != x ]; do | ||
| 58 | case $1 in | ||
| 59 | -c) instcmd="$cpprog" | ||
| 60 | shift | ||
| 61 | continue;; | ||
| 62 | |||
| 63 | -d) dir_arg=true | ||
| 64 | shift | ||
| 65 | continue;; | ||
| 66 | |||
| 67 | -m) chmodcmd="$chmodprog $2" | ||
| 68 | shift | ||
| 69 | shift | ||
| 70 | continue;; | ||
| 71 | |||
| 72 | -o) chowncmd="$chownprog $2" | ||
| 73 | shift | ||
| 74 | shift | ||
| 75 | continue;; | ||
| 76 | |||
| 77 | -g) chgrpcmd="$chgrpprog $2" | ||
| 78 | shift | ||
| 79 | shift | ||
| 80 | continue;; | ||
| 81 | |||
| 82 | -s) stripcmd="$stripprog" | ||
| 83 | shift | ||
| 84 | continue;; | ||
| 85 | |||
| 86 | -t=*) transformarg=`echo $1 | sed 's/-t=//'` | ||
| 87 | shift | ||
| 88 | continue;; | ||
| 89 | |||
| 90 | -b=*) transformbasename=`echo $1 | sed 's/-b=//'` | ||
| 91 | shift | ||
| 92 | continue;; | ||
| 93 | |||
| 94 | *) if [ x"$src" = x ] | ||
| 95 | then | ||
| 96 | src=$1 | ||
| 97 | else | ||
| 98 | # this colon is to work around a 386BSD /bin/sh bug | ||
| 99 | : | ||
| 100 | dst=$1 | ||
| 101 | fi | ||
| 102 | shift | ||
| 103 | continue;; | ||
| 104 | esac | ||
| 105 | done | ||
| 106 | |||
| 107 | if [ x"$src" = x ] | ||
| 108 | then | ||
| 109 | echo "install: no input file specified" | ||
| 110 | exit 1 | ||
| 111 | else | ||
| 112 | true | ||
| 113 | fi | ||
| 114 | |||
| 115 | if [ x"$dir_arg" != x ]; then | ||
| 116 | dst=$src | ||
| 117 | src="" | ||
| 118 | |||
| 119 | if [ -d $dst ]; then | ||
| 120 | instcmd=: | ||
| 121 | chmodcmd="" | ||
| 122 | else | ||
| 123 | instcmd=mkdir | ||
| 124 | fi | ||
| 125 | else | ||
| 126 | |||
| 127 | # Waiting for this to be detected by the "$instcmd $src $dsttmp" command | ||
| 128 | # might cause directories to be created, which would be especially bad | ||
| 129 | # if $src (and thus $dsttmp) contains '*'. | ||
| 130 | |||
| 131 | if [ -f $src -o -d $src ] | ||
| 132 | then | ||
| 133 | true | ||
| 134 | else | ||
| 135 | echo "install: $src does not exist" | ||
| 136 | exit 1 | ||
| 137 | fi | ||
| 138 | |||
| 139 | if [ x"$dst" = x ] | ||
| 140 | then | ||
| 141 | echo "install: no destination specified" | ||
| 142 | exit 1 | ||
| 143 | else | ||
| 144 | true | ||
| 145 | fi | ||
| 146 | |||
| 147 | # If destination is a directory, append the input filename; if your system | ||
| 148 | # does not like double slashes in filenames, you may need to add some logic | ||
| 149 | |||
| 150 | if [ -d $dst ] | ||
| 151 | then | ||
| 152 | dst="$dst"/`basename $src` | ||
| 153 | else | ||
| 154 | true | ||
| 155 | fi | ||
| 156 | fi | ||
| 157 | |||
| 158 | ## this sed command emulates the dirname command | ||
| 159 | dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'` | ||
| 160 | |||
| 161 | # Make sure that the destination directory exists. | ||
| 162 | # this part is taken from Noah Friedman's mkinstalldirs script | ||
| 163 | |||
| 164 | # Skip lots of stat calls in the usual case. | ||
| 165 | if [ ! -d "$dstdir" ]; then | ||
| 166 | defaultIFS=' | ||
| 167 | ' | ||
| 168 | IFS="${IFS-${defaultIFS}}" | ||
| 169 | |||
| 170 | oIFS="${IFS}" | ||
| 171 | # Some sh's can't handle IFS=/ for some reason. | ||
| 172 | IFS='%' | ||
| 173 | set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'` | ||
| 174 | IFS="${oIFS}" | ||
| 175 | |||
| 176 | pathcomp='' | ||
| 177 | |||
| 178 | while [ $# -ne 0 ] ; do | ||
| 179 | pathcomp="${pathcomp}${1}" | ||
| 180 | shift | ||
| 181 | |||
| 182 | if [ ! -d "${pathcomp}" ] ; | ||
| 183 | then | ||
| 184 | $mkdirprog "${pathcomp}" | ||
| 185 | else | ||
| 186 | true | ||
| 187 | fi | ||
| 188 | |||
| 189 | pathcomp="${pathcomp}/" | ||
| 190 | done | ||
| 191 | fi | ||
| 192 | |||
| 193 | if [ x"$dir_arg" != x ] | ||
| 194 | then | ||
| 195 | $doit $instcmd $dst && | ||
| 196 | |||
| 197 | if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi && | ||
| 198 | if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi && | ||
| 199 | if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi && | ||
| 200 | if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi | ||
| 201 | else | ||
| 202 | |||
| 203 | # If we're going to rename the final executable, determine the name now. | ||
| 204 | |||
| 205 | if [ x"$transformarg" = x ] | ||
| 206 | then | ||
| 207 | dstfile=`basename $dst` | ||
| 208 | else | ||
| 209 | dstfile=`basename $dst $transformbasename | | ||
| 210 | sed $transformarg`$transformbasename | ||
| 211 | fi | ||
| 212 | |||
| 213 | # don't allow the sed command to completely eliminate the filename | ||
| 214 | |||
| 215 | if [ x"$dstfile" = x ] | ||
| 216 | then | ||
| 217 | dstfile=`basename $dst` | ||
| 218 | else | ||
| 219 | true | ||
| 220 | fi | ||
| 221 | |||
| 222 | # Make a temp file name in the proper directory. | ||
| 223 | |||
| 224 | dsttmp=$dstdir/#inst.$$# | ||
| 225 | |||
| 226 | # Move or copy the file name to the temp name | ||
| 227 | |||
| 228 | $doit $instcmd $src $dsttmp && | ||
| 229 | |||
| 230 | trap "rm -f ${dsttmp}" 0 && | ||
| 231 | |||
| 232 | # and set any options; do chmod last to preserve setuid bits | ||
| 233 | |||
| 234 | # If any of these fail, we abort the whole thing. If we want to | ||
| 235 | # ignore errors from any of these, just make sure not to ignore | ||
| 236 | # errors from the above "$doit $instcmd $src $dsttmp" command. | ||
| 237 | |||
| 238 | if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi && | ||
| 239 | if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi && | ||
| 240 | if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi && | ||
| 241 | if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi && | ||
| 242 | |||
| 243 | # Now rename the file to the real destination. | ||
| 244 | |||
| 245 | $doit $rmcmd -f $dstdir/$dstfile && | ||
| 246 | $doit $mvcmd $dsttmp $dstdir/$dstfile | ||
| 247 | |||
| 248 | fi && | ||
| 249 | |||
| 250 | |||
| 251 | exit 0 | ||
diff --git a/m4 b/m4 new file mode 160000 | |||
| Subproject 946e50ebb8d52a5db376350b5602feef10fa45f | |||
diff --git a/mod_log_sql.c b/mod_log_sql.c deleted file mode 100644 index f02243e..0000000 --- a/mod_log_sql.c +++ /dev/null | |||
| @@ -1,1687 +0,0 @@ | |||
| 1 | |||
| 2 | /* --------* | ||
| 3 | * DEFINES * | ||
| 4 | * --------*/ | ||
| 5 | |||
| 6 | /* The enduser may wish to modify this */ | ||
| 7 | #undef DEBUG | ||
| 8 | |||
| 9 | /* The enduser won't modify these */ | ||
| 10 | #define MYSQL_ERROR(mysql) ((mysql)?(mysql_error(mysql)):"MySQL server has gone away") | ||
| 11 | #define ERRLEVEL APLOG_ERR|APLOG_NOERRNO | ||
| 12 | #define WARNINGLEVEL APLOG_WARNING|APLOG_NOERRNO | ||
| 13 | #define NOTICELEVEL APLOG_NOTICE|APLOG_NOERRNO | ||
| 14 | #define DEBUGLEVEL APLOG_DEBUG|APLOG_NOERRNO | ||
| 15 | |||
| 16 | /* ---------* | ||
| 17 | * INCLUDES * | ||
| 18 | * ---------*/ | ||
| 19 | #include <time.h> | ||
| 20 | #include <stdio.h> | ||
| 21 | #include <stdlib.h> | ||
| 22 | #include <string.h> | ||
| 23 | #include "httpd.h" | ||
| 24 | #include "http_config.h" | ||
| 25 | #include "http_log.h" | ||
| 26 | #include "http_core.h" | ||
| 27 | #include "mysql.h" | ||
| 28 | #include "mysqld_error.h" | ||
| 29 | |||
| 30 | #if MODULE_MAGIC_NUMBER >= 19980324 /* M_M_N is defined in /usr/local/Apache/include/ap_mmn.h, 19990320 as of this writing. */ | ||
| 31 | #include "ap_compat.h" | ||
| 32 | #endif | ||
| 33 | |||
| 34 | #ifdef WANT_SSL_LOGGING | ||
| 35 | #include "mod_ssl.h" | ||
| 36 | #endif | ||
| 37 | |||
| 38 | |||
| 39 | /* -------------* | ||
| 40 | * DECLARATIONS * | ||
| 41 | * -------------*/ | ||
| 42 | |||
| 43 | /* Declare ourselves so the configuration routines can find and know us. */ | ||
| 44 | module sql_log_module; | ||
| 45 | |||
| 46 | /* The contents of these are known 'Apache wide' and are not variable | ||
| 47 | * on a per-virtual-server basis. Every virtual server 'knows' the | ||
| 48 | * same versions of these variables. | ||
| 49 | */ | ||
| 50 | MYSQL logsql_server, *logsql_server_p = NULL; | ||
| 51 | |||
| 52 | int logsql_massvirtual = 0; | ||
| 53 | int logsql_createtables = 0; | ||
| 54 | int logsql_forcepreserve = 0; | ||
| 55 | char *logsql_dbname = NULL; | ||
| 56 | char *logsql_dbhost = NULL; | ||
| 57 | char *logsql_dbuser = NULL; | ||
| 58 | char *logsql_dbpwd = NULL; | ||
| 59 | char *logsql_machid = NULL; | ||
| 60 | char *logsql_socketfile = "/tmp/mysql.sock"; | ||
| 61 | unsigned int logsql_tcpport = 3306; | ||
| 62 | |||
| 63 | #ifdef WANT_DELAYED_MYSQL_INSERT | ||
| 64 | char *logsql_insertclause = "insert delayed into "; | ||
| 65 | #else | ||
| 66 | char *logsql_insertclause = "insert into "; | ||
| 67 | #endif | ||
| 68 | |||
| 69 | typedef const char *(*item_key_func) (request_rec *, char *); | ||
| 70 | |||
| 71 | /* But the contents of this structure will vary by virtual server. | ||
| 72 | * This permits each virtual server to vary its configuration slightly | ||
| 73 | * for per-server customization. | ||
| 74 | * | ||
| 75 | * Each child process has its own segregated copy of this structure. | ||
| 76 | */ | ||
| 77 | typedef struct { | ||
| 78 | array_header *transfer_ignore_list; | ||
| 79 | array_header *transfer_accept_list; | ||
| 80 | array_header *remhost_ignore_list; | ||
| 81 | array_header *notes_list; | ||
| 82 | array_header *hout_list; | ||
| 83 | array_header *hin_list; | ||
| 84 | array_header *cookie_list; | ||
| 85 | char *notes_table_name; | ||
| 86 | char *hout_table_name; | ||
| 87 | char *hin_table_name; | ||
| 88 | char *cookie_table_name; | ||
| 89 | char *transfer_table_name; | ||
| 90 | char *transfer_log_format; | ||
| 91 | char *preserve_file; | ||
| 92 | char *cookie_name; | ||
| 93 | } logsql_state; | ||
| 94 | |||
| 95 | |||
| 96 | /* -----------------* | ||
| 97 | * HELPER FUNCTIONS * | ||
| 98 | * -----------------*/ | ||
| 99 | |||
| 100 | int safe_create_tables(logsql_state *cls, request_rec *r); | ||
| 101 | |||
| 102 | static char *format_integer(pool *p, int i) | ||
| 103 | { | ||
| 104 | char dummy[40]; | ||
| 105 | ap_snprintf(dummy, sizeof(dummy), "%d", i); | ||
| 106 | return pstrdup(p, dummy); | ||
| 107 | } | ||
| 108 | |||
| 109 | static char *pfmt(pool *p, int i) | ||
| 110 | { | ||
| 111 | if (i <= 0) { | ||
| 112 | return "-"; | ||
| 113 | } else { | ||
| 114 | return format_integer(p, i); | ||
| 115 | } | ||
| 116 | } | ||
| 117 | |||
| 118 | /* Begin the individual functions that, given a request r, | ||
| 119 | * extract the needed information from it and return the | ||
| 120 | * value to the calling entity. | ||
| 121 | */ | ||
| 122 | |||
| 123 | static const char *extract_remote_host(request_rec *r, char *a) | ||
| 124 | { | ||
| 125 | return (char *) get_remote_host(r->connection, r->per_dir_config, REMOTE_NAME); | ||
| 126 | } | ||
| 127 | |||
| 128 | static const char *extract_remote_logname(request_rec *r, char *a) | ||
| 129 | { | ||
| 130 | return (char *) get_remote_logname(r); | ||
| 131 | } | ||
| 132 | |||
| 133 | static const char *extract_remote_user(request_rec *r, char *a) | ||
| 134 | { | ||
| 135 | char *rvalue = r->connection->user; | ||
| 136 | |||
| 137 | if (rvalue == NULL) { | ||
| 138 | rvalue = "-"; | ||
| 139 | } else if (strlen(rvalue) == 0) { | ||
| 140 | rvalue = "\"\""; | ||
| 141 | } | ||
| 142 | return rvalue; | ||
| 143 | } | ||
| 144 | |||
| 145 | #ifdef WANT_SSL_LOGGING | ||
| 146 | static const char *extract_ssl_keysize(request_rec *r, char *a) | ||
| 147 | { | ||
| 148 | char *result = NULL; | ||
| 149 | |||
| 150 | if (ap_ctx_get(r->connection->client->ctx, "ssl") != NULL) { | ||
| 151 | result = ssl_var_lookup(r->pool, r->server, r->connection, r, "SSL_CIPHER_USEKEYSIZE"); | ||
| 152 | #ifdef DEBUG | ||
| 153 | ap_log_error(APLOG_MARK,DEBUGLEVEL,r->server,"SSL_KEYSIZE: %s", result); | ||
| 154 | #endif | ||
| 155 | if (result != NULL && result[0] == '\0') | ||
| 156 | result = NULL; | ||
| 157 | return result; | ||
| 158 | } else { | ||
| 159 | return "0"; | ||
| 160 | } | ||
| 161 | } | ||
| 162 | |||
| 163 | static const char *extract_ssl_maxkeysize(request_rec *r, char *a) | ||
| 164 | { | ||
| 165 | char *result = NULL; | ||
| 166 | |||
| 167 | if (ap_ctx_get(r->connection->client->ctx, "ssl") != NULL) { | ||
| 168 | result = ssl_var_lookup(r->pool, r->server, r->connection, r, "SSL_CIPHER_ALGKEYSIZE"); | ||
| 169 | #ifdef DEBUG | ||
| 170 | ap_log_error(APLOG_MARK,DEBUGLEVEL,r->server,"SSL_ALGKEYSIZE: %s", result); | ||
| 171 | #endif | ||
| 172 | if (result != NULL && result[0] == '\0') | ||
| 173 | result = NULL; | ||
| 174 | return result; | ||
| 175 | } else { | ||
| 176 | return "0"; | ||
| 177 | } | ||
| 178 | } | ||
| 179 | |||
| 180 | static const char *extract_ssl_cipher(request_rec *r, char *a) | ||
| 181 | { | ||
| 182 | char *result = NULL; | ||
| 183 | |||
| 184 | if (ap_ctx_get(r->connection->client->ctx, "ssl") != NULL) { | ||
| 185 | result = ssl_var_lookup(r->pool, r->server, r->connection, r, "SSL_CIPHER"); | ||
| 186 | #ifdef DEBUG | ||
| 187 | ap_log_error(APLOG_MARK,DEBUGLEVEL,r->server,"SSL_CIPHER: %s", result); | ||
| 188 | #endif | ||
| 189 | if (result != NULL && result[0] == '\0') | ||
| 190 | result = NULL; | ||
| 191 | return result; | ||
| 192 | } else { | ||
| 193 | return "-"; | ||
| 194 | } | ||
| 195 | } | ||
| 196 | #endif /* WANT_SSL_LOGGING */ | ||
| 197 | |||
| 198 | static const char *extract_request_method(request_rec *r, char *a) | ||
| 199 | { | ||
| 200 | return r->method; | ||
| 201 | } | ||
| 202 | |||
| 203 | static const char *extract_request_protocol(request_rec *r, char *a) | ||
| 204 | { | ||
| 205 | return r->protocol; | ||
| 206 | } | ||
| 207 | |||
| 208 | static const char *extract_request_line(request_rec *r, char *a) | ||
| 209 | { | ||
| 210 | return r->the_request; | ||
| 211 | } | ||
| 212 | |||
| 213 | static const char *extract_request_file(request_rec *r, char *a) | ||
| 214 | { | ||
| 215 | return r->filename; | ||
| 216 | } | ||
| 217 | |||
| 218 | static const char *extract_request_uri(request_rec *r, char *a) | ||
| 219 | { | ||
| 220 | return r->uri; | ||
| 221 | } | ||
| 222 | |||
| 223 | static const char *extract_request_args(request_rec *r, char *a) | ||
| 224 | { | ||
| 225 | return r->args; | ||
| 226 | } | ||
| 227 | |||
| 228 | static const char *extract_status(request_rec *r, char *a) | ||
| 229 | { | ||
| 230 | return pfmt(r->pool, r->status); | ||
| 231 | } | ||
| 232 | |||
| 233 | static const char *extract_bytes_sent(request_rec *r, char *a) | ||
| 234 | { | ||
| 235 | if (!r->sent_bodyct) { | ||
| 236 | return "-"; | ||
| 237 | } else { | ||
| 238 | long int bs; | ||
| 239 | char dummy[40]; | ||
| 240 | bgetopt(r->connection->client, BO_BYTECT, &bs); | ||
| 241 | ap_snprintf(dummy, sizeof(dummy), "%ld", bs); | ||
| 242 | return pstrdup(r->pool, dummy); | ||
| 243 | } | ||
| 244 | } | ||
| 245 | |||
| 246 | /* | ||
| 247 | static const char *extract_header_in(request_rec *r, char *a) | ||
| 248 | { | ||
| 249 | return table_get(r->headers_in, a); | ||
| 250 | } | ||
| 251 | |||
| 252 | static const char *extract_header_out(request_rec *r, char *a) | ||
| 253 | { | ||
| 254 | const char *cp = table_get(r->headers_out, a); | ||
| 255 | if (!strcasecmp(a, "Content-type") && r->content_type) { | ||
| 256 | cp = r->content_type; | ||
| 257 | } | ||
| 258 | if (cp) { | ||
| 259 | return cp; | ||
| 260 | } | ||
| 261 | return table_get(r->err_headers_out, a); | ||
| 262 | } | ||
| 263 | */ | ||
| 264 | |||
| 265 | static const char *extract_request_time(request_rec *r, char *a) | ||
| 266 | { | ||
| 267 | int timz; | ||
| 268 | struct tm *t; | ||
| 269 | char tstr[MAX_STRING_LEN]; | ||
| 270 | |||
| 271 | t = get_gmtoff(&timz); | ||
| 272 | |||
| 273 | if (a && *a) { /* Custom format */ | ||
| 274 | strftime(tstr, MAX_STRING_LEN, a, t); | ||
| 275 | } else { /* CLF format */ | ||
| 276 | char sign = (timz < 0 ? '-' : '+'); | ||
| 277 | |||
| 278 | if (timz < 0) { | ||
| 279 | timz = -timz; | ||
| 280 | } | ||
| 281 | strftime(tstr, MAX_STRING_LEN, "[%d/%b/%Y:%H:%M:%S ", t); | ||
| 282 | ap_snprintf(tstr + strlen(tstr), sizeof(tstr) - strlen(tstr), "%c%.2d%.2d]", sign, timz / 60, timz % 60); | ||
| 283 | } | ||
| 284 | |||
| 285 | return pstrdup(r->pool, tstr); | ||
| 286 | } | ||
| 287 | |||
| 288 | static const char *extract_request_duration(request_rec *r, char *a) | ||
| 289 | { | ||
| 290 | char duration[22]; /* Long enough for 2^64 */ | ||
| 291 | |||
| 292 | ap_snprintf(duration, sizeof(duration), "%ld", time(NULL) - r->request_time); | ||
| 293 | return pstrdup(r->pool, duration); | ||
| 294 | } | ||
| 295 | |||
| 296 | static const char *extract_virtual_host(request_rec *r, char *a) | ||
| 297 | { | ||
| 298 | return ap_get_server_name(r); | ||
| 299 | } | ||
| 300 | |||
| 301 | static const char *extract_machine_id(request_rec *r, char *a) | ||
| 302 | { | ||
| 303 | if (!logsql_machid) | ||
| 304 | return "-"; | ||
| 305 | else | ||
| 306 | return logsql_machid; | ||
| 307 | } | ||
| 308 | |||
| 309 | static const char *extract_server_port(request_rec *r, char *a) | ||
| 310 | { | ||
| 311 | char portnum[22]; | ||
| 312 | |||
| 313 | ap_snprintf(portnum, sizeof(portnum), "%u", r->server->port); | ||
| 314 | return pstrdup(r->pool, portnum); | ||
| 315 | } | ||
| 316 | |||
| 317 | static const char *extract_child_pid(request_rec *r, char *a) | ||
| 318 | { | ||
| 319 | char pidnum[22]; | ||
| 320 | ap_snprintf(pidnum, sizeof(pidnum), "%ld", (long) getpid()); | ||
| 321 | return pstrdup(r->pool, pidnum); | ||
| 322 | } | ||
| 323 | |||
| 324 | static const char *extract_referer(request_rec *r, char *a) | ||
| 325 | { | ||
| 326 | const char *tempref; | ||
| 327 | |||
| 328 | tempref = table_get(r->headers_in, "Referer"); | ||
| 329 | if (!tempref) | ||
| 330 | { | ||
| 331 | return "-"; | ||
| 332 | } else { | ||
| 333 | return tempref; | ||
| 334 | } | ||
| 335 | } | ||
| 336 | |||
| 337 | static const char *extract_agent(request_rec *r, char *a) | ||
| 338 | { | ||
| 339 | const char *tempag; | ||
| 340 | |||
| 341 | tempag = table_get(r->headers_in, "User-Agent"); | ||
| 342 | if (!tempag) | ||
| 343 | { | ||
| 344 | return "-"; | ||
| 345 | } else { | ||
| 346 | return tempag; | ||
| 347 | } | ||
| 348 | } | ||
| 349 | |||
| 350 | static const char *extract_cookie(request_rec *r, char *a) | ||
| 351 | { | ||
| 352 | const char *cookiestr; | ||
| 353 | char *cookieend; | ||
| 354 | char *isvalid; | ||
| 355 | char *cookiebuf; | ||
| 356 | |||
| 357 | logsql_state *cls = get_module_config(r->server->module_config, &sql_log_module); | ||
| 358 | |||
| 359 | if (cls->cookie_name != NULL) { | ||
| 360 | #ifdef DEBUG | ||
| 361 | ap_log_error(APLOG_MARK,DEBUGLEVEL,r->server,"watching for cookie '%s'", cls->cookie_name); | ||
| 362 | #endif | ||
| 363 | |||
| 364 | /* Fetch out the cookie header */ | ||
| 365 | cookiestr = (char *)table_get(r->headers_in, "cookie2"); | ||
| 366 | if (cookiestr != NULL) { | ||
| 367 | #ifdef DEBUG | ||
| 368 | ap_log_error(APLOG_MARK,DEBUGLEVEL,r->server,"Cookie2: [%s]", cookiestr); | ||
| 369 | #endif | ||
| 370 | /* Does the cookie string contain one with our name? */ | ||
| 371 | isvalid = strstr(cookiestr, cls->cookie_name); | ||
| 372 | if (isvalid != NULL) { | ||
| 373 | /* Move past the cookie name and equal sign */ | ||
| 374 | isvalid += strlen(cls->cookie_name) + 1; | ||
| 375 | /* Duplicate it into the pool */ | ||
| 376 | cookiebuf = ap_pstrdup(r->pool, isvalid); | ||
| 377 | /* Segregate just this cookie out of the string | ||
| 378 | * with a terminating nul at the first semicolon */ | ||
| 379 | cookieend = strchr(cookiebuf, ';'); | ||
| 380 | if (cookieend != NULL) | ||
| 381 | *cookieend = '\0'; | ||
| 382 | return cookiebuf; | ||
| 383 | } | ||
| 384 | } | ||
| 385 | |||
| 386 | cookiestr = (char *)table_get(r->headers_in, "cookie"); | ||
| 387 | if (cookiestr != NULL) { | ||
| 388 | #ifdef DEBUG | ||
| 389 | ap_log_error(APLOG_MARK,DEBUGLEVEL,r->server,"Cookie: [%s]", cookiestr); | ||
| 390 | #endif | ||
| 391 | isvalid = strstr(cookiestr, cls->cookie_name); | ||
| 392 | if (isvalid != NULL) { | ||
| 393 | isvalid += strlen(cls->cookie_name) + 1; | ||
| 394 | cookiebuf = ap_pstrdup(r->pool, isvalid); | ||
| 395 | cookieend = strchr(cookiebuf, ';'); | ||
| 396 | if (cookieend != NULL) | ||
| 397 | *cookieend = '\0'; | ||
| 398 | return cookiebuf; | ||
| 399 | } | ||
| 400 | } | ||
| 401 | |||
| 402 | cookiestr = table_get(r->headers_out, "set-cookie"); | ||
| 403 | if (cookiestr != NULL) { | ||
| 404 | #ifdef DEBUG | ||
| 405 | ap_log_error(APLOG_MARK,DEBUGLEVEL,r->server,"Set-Cookie: [%s]", cookiestr); | ||
| 406 | #endif | ||
| 407 | isvalid = strstr(cookiestr, cls->cookie_name); | ||
| 408 | if (isvalid != NULL) { | ||
| 409 | isvalid += strlen(cls->cookie_name) + 1; | ||
| 410 | cookiebuf = ap_pstrdup(r->pool, isvalid); | ||
| 411 | cookieend = strchr(cookiebuf, ';'); | ||
| 412 | if (cookieend != NULL) | ||
| 413 | *cookieend = '\0'; | ||
| 414 | return cookiebuf; | ||
| 415 | } | ||
| 416 | } | ||
| 417 | } | ||
| 418 | |||
| 419 | return "-"; | ||
| 420 | } | ||
| 421 | |||
| 422 | static const char *extract_specific_cookie(request_rec *r, char *a) | ||
| 423 | { | ||
| 424 | const char *cookiestr; | ||
| 425 | char *cookieend; | ||
| 426 | char *isvalid; | ||
| 427 | char *cookiebuf; | ||
| 428 | |||
| 429 | if (a != NULL) { | ||
| 430 | #ifdef DEBUG | ||
| 431 | ap_log_error(APLOG_MARK,DEBUGLEVEL,r->server,"watching for cookie '%s'", a); | ||
| 432 | #endif | ||
| 433 | |||
| 434 | /* Fetch out the cookie header */ | ||
| 435 | cookiestr = (char *)table_get(r->headers_in, "cookie2"); | ||
| 436 | if (cookiestr != NULL) { | ||
| 437 | #ifdef DEBUG | ||
| 438 | ap_log_error(APLOG_MARK,DEBUGLEVEL,r->server,"Cookie2: [%s]", cookiestr); | ||
| 439 | #endif | ||
| 440 | /* Does the cookie string contain one with our name? */ | ||
| 441 | isvalid = strstr(cookiestr, a); | ||
| 442 | if (isvalid != NULL) { | ||
| 443 | /* Move past the cookie name and equal sign */ | ||
| 444 | isvalid += strlen(a) + 1; | ||
| 445 | /* Duplicate it into the pool */ | ||
| 446 | cookiebuf = ap_pstrdup(r->pool, isvalid); | ||
| 447 | /* Segregate just this cookie out of the string | ||
| 448 | * with a terminating nul at the first semicolon */ | ||
| 449 | cookieend = strchr(cookiebuf, ';'); | ||
| 450 | if (cookieend != NULL) | ||
| 451 | *cookieend = '\0'; | ||
| 452 | return cookiebuf; | ||
| 453 | } | ||
| 454 | } | ||
| 455 | |||
| 456 | cookiestr = (char *)table_get(r->headers_in, "cookie"); | ||
| 457 | if (cookiestr != NULL) { | ||
| 458 | #ifdef DEBUG | ||
| 459 | ap_log_error(APLOG_MARK,DEBUGLEVEL,r->server,"Cookie: [%s]", cookiestr); | ||
| 460 | #endif | ||
| 461 | isvalid = strstr(cookiestr, a); | ||
| 462 | if (isvalid != NULL) { | ||
| 463 | isvalid += strlen(a) + 1; | ||
| 464 | cookiebuf = ap_pstrdup(r->pool, isvalid); | ||
| 465 | cookieend = strchr(cookiebuf, ';'); | ||
| 466 | if (cookieend != NULL) | ||
| 467 | *cookieend = '\0'; | ||
| 468 | return cookiebuf; | ||
| 469 | } | ||
| 470 | } | ||
| 471 | |||
| 472 | cookiestr = table_get(r->headers_out, "set-cookie"); | ||
| 473 | if (cookiestr != NULL) { | ||
| 474 | #ifdef DEBUG | ||
| 475 | ap_log_error(APLOG_MARK,DEBUGLEVEL,r->server,"Set-Cookie: [%s]", cookiestr); | ||
| 476 | #endif | ||
| 477 | isvalid = strstr(cookiestr, a); | ||
| 478 | if (isvalid != NULL) { | ||
| 479 | isvalid += strlen(a) + 1; | ||
| 480 | cookiebuf = ap_pstrdup(r->pool, isvalid); | ||
| 481 | cookieend = strchr(cookiebuf, ';'); | ||
| 482 | if (cookieend != NULL) | ||
| 483 | *cookieend = '\0'; | ||
| 484 | return cookiebuf; | ||
| 485 | } | ||
| 486 | } | ||
| 487 | } | ||
| 488 | |||
| 489 | return "-"; | ||
| 490 | } | ||
| 491 | |||
| 492 | |||
| 493 | static const char *extract_request_timestamp(request_rec *r, char *a) | ||
| 494 | { | ||
| 495 | char tstr[32]; | ||
| 496 | |||
| 497 | ap_snprintf(tstr, 32, "%ld", time(NULL)); | ||
| 498 | return pstrdup(r->pool, tstr); | ||
| 499 | } | ||
| 500 | |||
| 501 | /* | ||
| 502 | static const char *extract_note(request_rec *r, char *a) | ||
| 503 | { | ||
| 504 | return ap_table_get(r->notes, a); | ||
| 505 | |||
| 506 | } | ||
| 507 | */ | ||
| 508 | |||
| 509 | static const char *extract_env_var(request_rec *r, char *a) | ||
| 510 | { | ||
| 511 | return ap_table_get(r->subprocess_env, a); | ||
| 512 | } | ||
| 513 | |||
| 514 | static const char *extract_unique_id(request_rec *r, char *a) | ||
| 515 | { | ||
| 516 | const char *tempid; | ||
| 517 | |||
| 518 | tempid = ap_table_get(r->subprocess_env, "UNIQUE_ID"); | ||
| 519 | if (!tempid) | ||
| 520 | return "-"; | ||
| 521 | else | ||
| 522 | return tempid; | ||
| 523 | } | ||
| 524 | |||
| 525 | /* End declarations of various extract_ functions */ | ||
| 526 | |||
| 527 | |||
| 528 | |||
| 529 | struct log_sql_item_list { | ||
| 530 | char ch; /* its letter code */ | ||
| 531 | item_key_func func; /* its extraction function */ | ||
| 532 | const char *sql_field_name; /* its column in SQL */ | ||
| 533 | int want_orig_default; /* if it requires the original request prior to internal redirection */ | ||
| 534 | int string_contents; /* if it returns a string */ | ||
| 535 | } log_sql_item_keys[] = { | ||
| 536 | |||
| 537 | { 'A', extract_agent, "agent", 1, 1 }, | ||
| 538 | { 'a', extract_request_args, "request_args", 1, 1 }, | ||
| 539 | { 'b', extract_bytes_sent, "bytes_sent", 0, 0 }, | ||
| 540 | { 'c', extract_cookie, "cookie", 0, 1 }, | ||
| 541 | { 'e', extract_env_var, "env_var", 0, 1 }, | ||
| 542 | { 'f', extract_request_file, "request_file", 0, 1 }, | ||
| 543 | { 'H', extract_request_protocol, "request_protocol", 0, 1 }, | ||
| 544 | { 'h', extract_remote_host, "remote_host", 0, 1 }, | ||
| 545 | { 'I', extract_unique_id, "id", 0, 1 }, | ||
| 546 | { 'l', extract_remote_logname, "remote_logname", 0, 1 }, | ||
| 547 | { 'm', extract_request_method, "request_method", 0, 1 }, | ||
| 548 | { 'M', extract_machine_id, "machine_id", 0, 1 }, | ||
| 549 | { 'P', extract_child_pid, "child_pid", 0, 0 }, | ||
| 550 | { 'p', extract_server_port, "server_port", 0, 0 }, | ||
| 551 | { 'R', extract_referer, "referer", 1, 1 }, | ||
| 552 | { 'r', extract_request_line, "request_line", 1, 1 }, | ||
| 553 | { 'S', extract_request_timestamp, "time_stamp", 0, 0 }, | ||
| 554 | { 's', extract_status, "status", 1, 0 }, | ||
| 555 | { 'T', extract_request_duration, "request_duration", 1, 0 }, | ||
| 556 | { 't', extract_request_time, "request_time", 0, 1 }, | ||
| 557 | { 'u', extract_remote_user, "remote_user", 0, 1 }, | ||
| 558 | { 'U', extract_request_uri, "request_uri", 1, 1 }, | ||
| 559 | { 'v', extract_virtual_host, "virtual_host", 0, 1 }, | ||
| 560 | #ifdef WANT_SSL_LOGGING | ||
| 561 | { 'q', extract_ssl_keysize, "ssl_keysize", 0, 1 }, | ||
| 562 | { 'Q', extract_ssl_maxkeysize, "ssl_maxkeysize", 0, 1 }, | ||
| 563 | { 'z', extract_ssl_cipher, "ssl_cipher", 0, 1 }, | ||
| 564 | #endif | ||
| 565 | {'\0'} | ||
| 566 | }; | ||
| 567 | |||
| 568 | |||
| 569 | /* Routine to escape the 'dangerous' characters that would otherwise | ||
| 570 | * corrupt the INSERT string: ', \, and " | ||
| 571 | */ | ||
| 572 | const char *escape_query(const char *from_str, pool *p) | ||
| 573 | { | ||
| 574 | if (!from_str) | ||
| 575 | return NULL; | ||
| 576 | else { | ||
| 577 | char *to_str; | ||
| 578 | unsigned long length = strlen(from_str); | ||
| 579 | unsigned long retval; | ||
| 580 | |||
| 581 | /* Pre-allocate a new string that could hold twice the original, which would only | ||
| 582 | * happen if the whole original string was 'dangerous' characters. | ||
| 583 | */ | ||
| 584 | to_str = (char *) ap_palloc(p, length * 2 + 1); | ||
| 585 | if (!to_str) { | ||
| 586 | return from_str; | ||
| 587 | } | ||
| 588 | |||
| 589 | if (!logsql_server_p) { | ||
| 590 | /* Well, I would have liked to use the current database charset. mysql is | ||
| 591 | * unavailable, however, so I fall back to the slightly less respectful | ||
| 592 | * mysql_escape_string() function that uses the default charset. | ||
| 593 | */ | ||
| 594 | retval = mysql_escape_string(to_str, from_str, length); | ||
| 595 | } else { | ||
| 596 | /* MySQL is available, so I'll go ahead and respect the current charset when | ||
| 597 | * I perform the escape. | ||
| 598 | */ | ||
| 599 | retval = mysql_real_escape_string(logsql_server_p, to_str, from_str, length); | ||
| 600 | } | ||
| 601 | |||
| 602 | if (retval) | ||
| 603 | return to_str; | ||
| 604 | else | ||
| 605 | return from_str; | ||
| 606 | } | ||
| 607 | } | ||
| 608 | |||
| 609 | int open_logdb_link(server_rec* s) | ||
| 610 | { | ||
| 611 | /* Returns: | ||
| 612 | 3 if preserve forced | ||
| 613 | 2 if already connected | ||
| 614 | 1 if successful | ||
| 615 | 0 if unsuccessful | ||
| 616 | */ | ||
| 617 | |||
| 618 | if (logsql_forcepreserve) | ||
| 619 | return 3; | ||
| 620 | |||
| 621 | if (logsql_server_p) | ||
| 622 | return 2; | ||
| 623 | |||
| 624 | if ((logsql_dbname) && (logsql_dbhost) && (logsql_dbuser) && (logsql_dbpwd)) { | ||
| 625 | mysql_init(&logsql_server); | ||
| 626 | logsql_server_p = mysql_real_connect(&logsql_server, logsql_dbhost, logsql_dbuser, logsql_dbpwd, logsql_dbname, logsql_tcpport, logsql_socketfile, 0); | ||
| 627 | |||
| 628 | if (logsql_server_p) { | ||
| 629 | #ifdef DEBUG | ||
| 630 | ap_log_error(APLOG_MARK,DEBUGLEVEL,s,"HOST: '%s' PORT: '%d' DB: '%s' USER: '%s' SOCKET: '%s'", | ||
| 631 | logsql_dbhost, logsql_tcpport, logsql_dbname, logsql_dbuser, logsql_socketfile); | ||
| 632 | #endif | ||
| 633 | return 1; | ||
| 634 | } else { | ||
| 635 | #ifdef DEBUG | ||
| 636 | ap_log_error(APLOG_MARK,DEBUGLEVEL,s,"mod_log_sql: database connection error: %s",MYSQL_ERROR(&logsql_server)); | ||
| 637 | ap_log_error(APLOG_MARK,DEBUGLEVEL,s,"HOST: '%s' PORT: '%d' DB: '%s' USER: '%s' SOCKET: '%s'", | ||
| 638 | logsql_dbhost, logsql_tcpport, logsql_dbname, logsql_dbuser, logsql_socketfile); | ||
| 639 | #endif | ||
| 640 | return 0; | ||
| 641 | } | ||
| 642 | } else { | ||
| 643 | ap_log_error(APLOG_MARK,ERRLEVEL,s,"mod_log_sql: insufficient configuration info to establish database link"); | ||
| 644 | return 0; | ||
| 645 | } | ||
| 646 | } | ||
| 647 | |||
| 648 | const char *extract_table(void *data, const char *key, const char *val) | ||
| 649 | { | ||
| 650 | request_rec *r = (request_rec *)data; | ||
| 651 | |||
| 652 | return ap_pstrcat(r->pool, key, " = ", val, " ", NULL); | ||
| 653 | } | ||
| 654 | |||
| 655 | void preserve_entry(request_rec *r, const char *query) | ||
| 656 | { | ||
| 657 | FILE *fp; | ||
| 658 | logsql_state *cls = get_module_config(r->server->module_config, &sql_log_module); | ||
| 659 | |||
| 660 | fp = pfopen(r->pool, cls->preserve_file, "a"); | ||
| 661 | if (fp == NULL) | ||
| 662 | ap_log_error(APLOG_MARK,ERRLEVEL,r->server,"mod_log_sql: attempted append of local preserve file but failed."); | ||
| 663 | else { | ||
| 664 | fprintf(fp,"%s;\n", query); | ||
| 665 | pfclose(r->pool, fp); | ||
| 666 | #ifdef DEBUG | ||
| 667 | ap_log_error(APLOG_MARK,DEBUGLEVEL,r->server,"mod_log_sql: entry preserved in %s", cls->preserve_file); | ||
| 668 | #endif | ||
| 669 | } | ||
| 670 | } | ||
| 671 | |||
| 672 | |||
| 673 | /*-----------------------------------------------------*/ | ||
| 674 | /* safe_sql_query: perform a database query with */ | ||
| 675 | /* a degree of safety and error checking. */ | ||
| 676 | /* */ | ||
| 677 | /* Parms: request record, SQL insert statement */ | ||
| 678 | /* Returns: 0 (OK) on success */ | ||
| 679 | /* 1 if have no log handle */ | ||
| 680 | /* 2 if insert delayed failed (kluge) */ | ||
| 681 | /* the actual MySQL return code on error */ | ||
| 682 | /*-----------------------------------------------------*/ | ||
| 683 | unsigned int safe_sql_query(request_rec *r, const char *query) | ||
| 684 | { | ||
| 685 | int retval; | ||
| 686 | struct timespec delay, remainder; | ||
| 687 | int ret; | ||
| 688 | void (*handler) (int); | ||
| 689 | logsql_state *cls; | ||
| 690 | unsigned int real_error; | ||
| 691 | #ifdef WANT_DELAYED_MYSQL_INSERT | ||
| 692 | char *real_error_str; | ||
| 693 | #endif | ||
| 694 | |||
| 695 | /* A failed mysql_query() may send a SIGPIPE, so we ignore that signal momentarily. */ | ||
| 696 | handler = signal(SIGPIPE, SIG_IGN); | ||
| 697 | |||
| 698 | /* First attempt for the query */ | ||
| 699 | if (!logsql_server_p) { | ||
| 700 | signal(SIGPIPE, handler); | ||
| 701 | return 1; | ||
| 702 | } else if (!(retval = mysql_query(logsql_server_p, query))) { | ||
| 703 | signal(SIGPIPE, handler); | ||
| 704 | return 0; | ||
| 705 | } | ||
| 706 | |||
| 707 | /* If we ran the query and it returned an error, try to be robust. | ||
| 708 | * (After all, the module thought it had a valid mysql_log connection but the query | ||
| 709 | * could have failed for a number of reasons, so we have to be extra-safe and check.) */ | ||
| 710 | #ifdef WANT_DELAYED_MYSQL_INSERT | ||
| 711 | real_error_str = MYSQL_ERROR(logsql_server_p); | ||
| 712 | #else | ||
| 713 | real_error = mysql_errno(logsql_server_p); | ||
| 714 | #endif | ||
| 715 | |||
| 716 | /* Check to see if the error is "nonexistent table" */ | ||
| 717 | #ifdef WANT_DELAYED_MYSQL_INSERT | ||
| 718 | if ((strstr(real_error_str, "Table")) && (strstr(real_error_str,"doesn't exist"))) { | ||
| 719 | #else | ||
| 720 | if (real_error == ER_NO_SUCH_TABLE) { | ||
| 721 | #endif | ||
| 722 | if (logsql_createtables) { | ||
| 723 | ap_log_error(APLOG_MARK,ERRLEVEL,r->server,"mod_log_sql: table doesn't exist...creating now"); | ||
| 724 | cls = get_module_config(r->server->module_config, &sql_log_module); | ||
| 725 | if (safe_create_tables(cls, r)) { | ||
| 726 | ap_log_error(APLOG_MARK,ERRLEVEL,r->server,"mod_log_sql: child attempted but failed to create one or more tables for %s, preserving query", ap_get_server_name(r)); | ||
| 727 | preserve_entry(r, query); | ||
| 728 | retval = mysql_errno(logsql_server_p); | ||
| 729 | } else { | ||
| 730 | ap_log_error(APLOG_MARK,ERRLEVEL,r->server,"mod_log_sql: tables successfully created - retrying query"); | ||
| 731 | if (mysql_query(logsql_server_p, query)) { | ||
| 732 | ap_log_error(APLOG_MARK,ERRLEVEL,r->server,"mod_log_sql: giving up, preserving query"); | ||
| 733 | preserve_entry(r, query); | ||
| 734 | retval = mysql_errno(logsql_server_p); | ||
| 735 | } else | ||
| 736 | ap_log_error(APLOG_MARK,ERRLEVEL,r->server,"mod_log_sql: query successful after table creation"); | ||
| 737 | retval = 0; | ||
| 738 | } | ||
| 739 | } else { | ||
| 740 | ap_log_error(APLOG_MARK,ERRLEVEL,r->server,"mod_log_sql, table doesn't exist, creation denied by configuration, preserving query"); | ||
| 741 | preserve_entry(r, query); | ||
| 742 | retval = ER_NO_SUCH_TABLE; | ||
| 743 | } | ||
| 744 | /* Restore SIGPIPE to its original handler function */ | ||
| 745 | signal(SIGPIPE, handler); | ||
| 746 | return retval; | ||
| 747 | } | ||
| 748 | |||
| 749 | /* Handle all other types of errors */ | ||
| 750 | |||
| 751 | cls = get_module_config(r->server->module_config, &sql_log_module); | ||
| 752 | |||
| 753 | /* Something went wrong, so start by trying to restart the db link. */ | ||
| 754 | #ifdef WANT_DELAYED_MYSQL_INSERT | ||
| 755 | real_error = 2; | ||
| 756 | #else | ||
| 757 | real_error = mysql_errno(logsql_server_p); | ||
| 758 | #endif | ||
| 759 | ap_log_error(APLOG_MARK,ERRLEVEL,r->server,"mod_log_sql: first attempt failed, API said: error %d, \"%s\"", real_error, MYSQL_ERROR(logsql_server_p)); | ||
| 760 | mysql_close(logsql_server_p); | ||
| 761 | logsql_server_p = NULL; | ||
| 762 | open_logdb_link(r->server); | ||
| 763 | |||
| 764 | if (logsql_server_p == NULL) { /* still unable to link */ | ||
| 765 | signal(SIGPIPE, handler); | ||
| 766 | ap_log_error(APLOG_MARK,ERRLEVEL,r->server,"mod_log_sql: reconnect failed, unable to reach database. SQL logging stopped until child regains a db connection."); | ||
| 767 | ap_log_error(APLOG_MARK,ERRLEVEL,r->server,"mod_log_sql: log entries are being preserved in %s", cls->preserve_file); | ||
| 768 | return 1; | ||
| 769 | } else | ||
| 770 | ap_log_error(APLOG_MARK,ERRLEVEL,r->server,"mod_log_sql: db reconnect successful"); | ||
| 771 | |||
| 772 | /* First sleep for a tiny amount of time. */ | ||
| 773 | delay.tv_sec = 0; | ||
| 774 | delay.tv_nsec = 250000000; /* max is 999999999 (nine nines) */ | ||
| 775 | ret = nanosleep(&delay, &remainder); | ||
| 776 | if (ret && errno != EINTR) | ||
| 777 | ap_log_error(APLOG_MARK,ERRLEVEL,r->server,"mod_log_sql: nanosleep unsuccessful"); | ||
| 778 | |||
| 779 | /* Then make our second attempt */ | ||
| 780 | retval = mysql_query(logsql_server_p,query); | ||
| 781 | |||
| 782 | /* If this one also failed, log that and append to our local offline file */ | ||
| 783 | if (retval) { | ||
| 784 | #ifdef WANT_DELAYED_MYSQL_INSERT | ||
| 785 | real_error = 2; | ||
| 786 | #else | ||
| 787 | real_error = mysql_errno(logsql_server_p); | ||
| 788 | #endif | ||
| 789 | ap_log_error(APLOG_MARK,ERRLEVEL,r->server,"mod_log_sql: second attempt failed, API said: error %d, \"%s\" -- preserving", real_error, MYSQL_ERROR(logsql_server_p)); | ||
| 790 | preserve_entry(r, query); | ||
| 791 | retval = real_error; | ||
| 792 | } else | ||
| 793 | ap_log_error(APLOG_MARK,ERRLEVEL,r->server,"mod_log_sql: second attempt successful"); | ||
| 794 | |||
| 795 | /* Restore SIGPIPE to its original handler function */ | ||
| 796 | signal(SIGPIPE, handler); | ||
| 797 | return retval; | ||
| 798 | } | ||
| 799 | |||
| 800 | /*-----------------------------------------------------*/ | ||
| 801 | /* safe_create_tables: create SQL table set for the */ | ||
| 802 | /* virtual server represented by cls. */ | ||
| 803 | /* */ | ||
| 804 | /* Parms: virtserver structure, request record */ | ||
| 805 | /* Returns: 0 on no errors */ | ||
| 806 | /* mysql error code on failure */ | ||
| 807 | /*-----------------------------------------------------*/ | ||
| 808 | int safe_create_tables(logsql_state *cls, request_rec *r) | ||
| 809 | { | ||
| 810 | int retval; | ||
| 811 | unsigned int create_results; | ||
| 812 | char *create_access = NULL; | ||
| 813 | char *create_notes = NULL; | ||
| 814 | char *create_hout = NULL; | ||
| 815 | char *create_hin = NULL; | ||
| 816 | char *create_cookies = NULL; | ||
| 817 | |||
| 818 | char *createprefix = "create table if not exists `"; | ||
| 819 | char *access_suffix = | ||
| 820 | "` (id char(19),\ | ||
| 821 | agent varchar(255),\ | ||
| 822 | bytes_sent int unsigned,\ | ||
| 823 | child_pid smallint unsigned,\ | ||
| 824 | cookie varchar(255),\ | ||
| 825 | machine_id varchar(25),\ | ||
| 826 | request_file varchar(255),\ | ||
| 827 | referer varchar(255),\ | ||
| 828 | remote_host varchar(50),\ | ||
| 829 | remote_logname varchar(50),\ | ||
| 830 | remote_user varchar(50),\ | ||
| 831 | request_duration smallint unsigned,\ | ||
| 832 | request_line varchar(255),\ | ||
| 833 | request_method varchar(10),\ | ||
| 834 | request_protocol varchar(10),\ | ||
| 835 | request_time char(28),\ | ||
| 836 | request_uri varchar(50),\ | ||
| 837 | request_args varchar(255),\ | ||
| 838 | server_port smallint unsigned,\ | ||
| 839 | ssl_cipher varchar(25),\ | ||
| 840 | ssl_keysize smallint unsigned,\ | ||
| 841 | ssl_maxkeysize smallint unsigned,\ | ||
| 842 | status smallint unsigned,\ | ||
| 843 | time_stamp int unsigned,\ | ||
| 844 | virtual_host varchar(50))"; | ||
| 845 | |||
| 846 | char *notes_suffix = | ||
| 847 | "` (id char(19),\ | ||
| 848 | item varchar(80),\ | ||
| 849 | val varchar(80))"; | ||
| 850 | |||
| 851 | char *headers_suffix = | ||
| 852 | "` (id char(19),\ | ||
| 853 | item varchar(80),\ | ||
| 854 | val varchar(80))"; | ||
| 855 | |||
| 856 | char *cookies_suffix = | ||
| 857 | "` (id char(19),\ | ||
| 858 | item varchar(80),\ | ||
| 859 | val varchar(80))"; | ||
| 860 | |||
| 861 | /* Find memory long enough to hold the whole CREATE string + \0 */ | ||
| 862 | create_access = ap_pstrcat(r->pool, createprefix, cls->transfer_table_name, access_suffix, NULL); | ||
| 863 | create_notes = ap_pstrcat(r->pool, createprefix, cls->notes_table_name, notes_suffix, NULL); | ||
| 864 | create_hout = ap_pstrcat(r->pool, createprefix, cls->hout_table_name, headers_suffix, NULL); | ||
| 865 | create_hin = ap_pstrcat(r->pool, createprefix, cls->hin_table_name, headers_suffix, NULL); | ||
| 866 | create_cookies= ap_pstrcat(r->pool, createprefix, cls->cookie_table_name, cookies_suffix, NULL); | ||
| 867 | |||
| 868 | #ifdef DEBUG | ||
| 869 | ap_log_error(APLOG_MARK,DEBUGLEVEL,r->server,"mod_log_sql: create string: %s", create_access); | ||
| 870 | ap_log_error(APLOG_MARK,DEBUGLEVEL,r->server,"mod_log_sql: create string: %s", create_notes); | ||
| 871 | ap_log_error(APLOG_MARK,DEBUGLEVEL,r->server,"mod_log_sql: create string: %s", create_hout); | ||
| 872 | ap_log_error(APLOG_MARK,DEBUGLEVEL,r->server,"mod_log_sql: create string: %s", create_hin); | ||
| 873 | ap_log_error(APLOG_MARK,DEBUGLEVEL,r->server,"mod_log_sql: create string: %s", create_cookies); | ||
| 874 | #endif | ||
| 875 | |||
| 876 | /* Assume that things worked unless told otherwise */ | ||
| 877 | retval = 0; | ||
| 878 | |||
| 879 | if ((create_results = safe_sql_query(r, create_access))) { | ||
| 880 | ap_log_error(APLOG_MARK,ERRLEVEL,r->server,"mod_log_sql: failed to create access table"); | ||
| 881 | retval = create_results; | ||
| 882 | } | ||
| 883 | |||
| 884 | if ((create_results = safe_sql_query(r, create_notes))) { | ||
| 885 | ap_log_error(APLOG_MARK,ERRLEVEL,r->server,"mod_log_sql: failed to create notes table"); | ||
| 886 | retval = create_results; | ||
| 887 | } | ||
| 888 | |||
| 889 | if ((create_results = safe_sql_query(r, create_hin))) { | ||
| 890 | ap_log_error(APLOG_MARK,ERRLEVEL,r->server,"mod_log_sql: failed to create header_out table"); | ||
| 891 | retval = create_results; | ||
| 892 | } | ||
| 893 | |||
| 894 | if ((create_results = safe_sql_query(r, create_hout))) { | ||
| 895 | ap_log_error(APLOG_MARK,ERRLEVEL,r->server,"mod_log_sql: failed to create header_in table"); | ||
| 896 | retval = create_results; | ||
| 897 | } | ||
| 898 | |||
| 899 | if ((create_results = safe_sql_query(r, create_cookies))) { | ||
| 900 | ap_log_error(APLOG_MARK,ERRLEVEL,r->server,"mod_log_sql: failed to create cookies table"); | ||
| 901 | retval = create_results; | ||
| 902 | } | ||
| 903 | |||
| 904 | return retval; | ||
| 905 | } | ||
| 906 | |||
| 907 | /* ------------------------------------------------* | ||
| 908 | * Command handlers that are called according * | ||
| 909 | * to the directives found at Apache runtime. * | ||
| 910 | * ------------------------------------------------*/ | ||
| 911 | |||
| 912 | const char *set_log_sql_massvirtual(cmd_parms *parms, void *dummy, int flag) | ||
| 913 | { | ||
| 914 | logsql_massvirtual = ( flag ? 1 : 0); | ||
| 915 | return NULL; | ||
| 916 | } | ||
| 917 | |||
| 918 | const char *set_log_sql_force_preserve(cmd_parms *parms, void *dummy, int flag) | ||
| 919 | { | ||
| 920 | logsql_forcepreserve = ( flag ? 1 : 0); | ||
| 921 | return NULL; | ||
| 922 | } | ||
| 923 | |||
| 924 | const char *set_log_sql_machine_id(cmd_parms *parms, void *dummy, char *arg) | ||
| 925 | { | ||
| 926 | logsql_machid = arg; | ||
| 927 | return NULL; | ||
| 928 | } | ||
| 929 | |||
| 930 | const char *set_log_sql_create(cmd_parms *parms, void *dummy, int flag) | ||
| 931 | { | ||
| 932 | if (logsql_massvirtual) | ||
| 933 | ap_log_error(APLOG_MARK,WARNINGLEVEL,parms->server,"mod_log_sql: do not set LogSQLCreateTables when LogSQLMassVirtualHosting is On. Ignoring."); | ||
| 934 | else | ||
| 935 | logsql_createtables = ( flag ? 1 : 0); | ||
| 936 | return NULL; | ||
| 937 | } | ||
| 938 | |||
| 939 | const char *set_log_sql_db(cmd_parms *parms, void *dummy, char *arg) | ||
| 940 | { | ||
| 941 | logsql_dbname = arg; | ||
| 942 | return NULL; | ||
| 943 | } | ||
| 944 | |||
| 945 | const char *set_log_sql_cookie(cmd_parms *parms, void *dummy, char *arg) | ||
| 946 | { | ||
| 947 | logsql_state *cls = get_module_config(parms->server->module_config, &sql_log_module); | ||
| 948 | |||
| 949 | cls->cookie_name = arg; | ||
| 950 | return NULL; | ||
| 951 | } | ||
| 952 | |||
| 953 | const char *set_log_sql_preserve_file(cmd_parms *parms, void *dummy, char *arg) | ||
| 954 | { | ||
| 955 | /* char *pfile; */ | ||
| 956 | logsql_state *cls = get_module_config(parms->server->module_config, &sql_log_module); | ||
| 957 | |||
| 958 | /* pfile = ap_pstrcat(parms->pool, "/tmp/", arg, NULL); */ | ||
| 959 | cls->preserve_file = arg; | ||
| 960 | return NULL; | ||
| 961 | } | ||
| 962 | |||
| 963 | const char *set_log_sql_info(cmd_parms *parms, void *dummy, char *host, char *user, char *pwd) | ||
| 964 | { | ||
| 965 | if (*host != '.') { | ||
| 966 | logsql_dbhost = host; | ||
| 967 | } | ||
| 968 | if (*user != '.') { | ||
| 969 | logsql_dbuser = user; | ||
| 970 | } | ||
| 971 | if (*pwd != '.') { | ||
| 972 | logsql_dbpwd = pwd; | ||
| 973 | } | ||
| 974 | return NULL; | ||
| 975 | } | ||
| 976 | |||
| 977 | const char *set_log_sql_transfer_table(cmd_parms *parms, void *dummy, char *arg) | ||
| 978 | { | ||
| 979 | logsql_state *cls = get_module_config(parms->server->module_config, &sql_log_module); | ||
| 980 | |||
| 981 | if (logsql_massvirtual != 0) | ||
| 982 | ap_log_error(APLOG_MARK,WARNINGLEVEL,parms->server,"mod_log_sql: do not set LogSQLTransferLogTable when LogSQLMassVirtualHosting is On. Ignoring."); | ||
| 983 | else | ||
| 984 | cls->transfer_table_name = arg; | ||
| 985 | return NULL; | ||
| 986 | } | ||
| 987 | |||
| 988 | const char *set_log_sql_cookie_table(cmd_parms *parms, void *dummy, char *arg) | ||
| 989 | { | ||
| 990 | logsql_state *cls = get_module_config(parms->server->module_config, &sql_log_module); | ||
| 991 | |||
| 992 | if (logsql_massvirtual != 0) | ||
| 993 | ap_log_error(APLOG_MARK,WARNINGLEVEL,parms->server,"mod_log_sql: do not set LogSQLCookieLogTable when LogSQLMassVirtualHosting is On. Ignoring."); | ||
| 994 | else | ||
| 995 | cls->cookie_table_name = arg; | ||
| 996 | return NULL; | ||
| 997 | } | ||
| 998 | |||
| 999 | const char *set_log_sql_notes_table(cmd_parms *parms, void *dummy, char *arg) | ||
| 1000 | { | ||
| 1001 | logsql_state *cls = get_module_config(parms->server->module_config, &sql_log_module); | ||
| 1002 | |||
| 1003 | if (logsql_massvirtual != 0) | ||
| 1004 | ap_log_error(APLOG_MARK,WARNINGLEVEL,parms->server,"mod_log_sql: do not set LogSQLNotesLogTable when LogSQLMassVirtualHosting is On. Ignoring."); | ||
| 1005 | else | ||
| 1006 | cls->notes_table_name = arg; | ||
| 1007 | return NULL; | ||
| 1008 | } | ||
| 1009 | |||
| 1010 | const char *set_log_sql_hin_table(cmd_parms *parms, void *dummy, char *arg) | ||
| 1011 | { | ||
| 1012 | logsql_state *cls = get_module_config(parms->server->module_config, &sql_log_module); | ||
| 1013 | |||
| 1014 | if (logsql_massvirtual != 0) | ||
| 1015 | ap_log_error(APLOG_MARK,WARNINGLEVEL,parms->server,"mod_log_sql: do not set LogSQLHeadersInLogTable when LogSQLMassVirtualHosting is On. Ignoring."); | ||
| 1016 | else | ||
| 1017 | cls->hin_table_name = arg; | ||
| 1018 | return NULL; | ||
| 1019 | } | ||
| 1020 | |||
| 1021 | const char *set_log_sql_hout_table(cmd_parms *parms, void *dummy, char *arg) | ||
| 1022 | { | ||
| 1023 | logsql_state *cls = get_module_config(parms->server->module_config, &sql_log_module); | ||
| 1024 | |||
| 1025 | if (logsql_massvirtual != 0) | ||
| 1026 | ap_log_error(APLOG_MARK,WARNINGLEVEL,parms->server,"mod_log_sql: do not set LogSQLHeadersOutLogTable when LogSQLMassVirtualHosting is On. Ignoring."); | ||
| 1027 | else | ||
| 1028 | cls->hout_table_name = arg; | ||
| 1029 | return NULL; | ||
| 1030 | } | ||
| 1031 | |||
| 1032 | const char *set_log_sql_transfer_log_format(cmd_parms *parms, void *dummy, char *arg) | ||
| 1033 | { | ||
| 1034 | logsql_state *cls = get_module_config(parms->server->module_config, &sql_log_module); | ||
| 1035 | |||
| 1036 | cls->transfer_log_format = arg; | ||
| 1037 | return NULL; | ||
| 1038 | } | ||
| 1039 | |||
| 1040 | const char *set_log_sql_socket_file(cmd_parms *parms, void *dummy, char *arg) | ||
| 1041 | { | ||
| 1042 | logsql_socketfile = arg; | ||
| 1043 | |||
| 1044 | return NULL; | ||
| 1045 | } | ||
| 1046 | |||
| 1047 | const char *set_log_sql_tcp_port(cmd_parms *parms, void *dummy, char *arg) | ||
| 1048 | { | ||
| 1049 | logsql_tcpport = (unsigned int)atoi(arg); | ||
| 1050 | |||
| 1051 | return NULL; | ||
| 1052 | } | ||
| 1053 | |||
| 1054 | const char *add_log_sql_transfer_accept(cmd_parms *parms, void *dummy, char *arg) | ||
| 1055 | { | ||
| 1056 | char **addme; | ||
| 1057 | logsql_state *cls = get_module_config(parms->server->module_config, &sql_log_module); | ||
| 1058 | |||
| 1059 | addme = push_array(cls->transfer_accept_list); | ||
| 1060 | *addme = pstrdup(cls->transfer_accept_list->pool, arg); | ||
| 1061 | return NULL; | ||
| 1062 | } | ||
| 1063 | |||
| 1064 | const char *add_log_sql_transfer_ignore(cmd_parms *parms, void *dummy, char *arg) | ||
| 1065 | { | ||
| 1066 | char **addme; | ||
| 1067 | logsql_state *cls = get_module_config(parms->server->module_config, &sql_log_module); | ||
| 1068 | |||
| 1069 | addme = push_array(cls->transfer_ignore_list); | ||
| 1070 | *addme = pstrdup(cls->transfer_ignore_list->pool, arg); | ||
| 1071 | return NULL; | ||
| 1072 | } | ||
| 1073 | |||
| 1074 | const char *add_log_sql_remhost_ignore(cmd_parms *parms, void *dummy, char *arg) | ||
| 1075 | { | ||
| 1076 | char **addme; | ||
| 1077 | logsql_state *cls = get_module_config(parms->server->module_config, &sql_log_module); | ||
| 1078 | |||
| 1079 | addme = push_array(cls->remhost_ignore_list); | ||
| 1080 | *addme = pstrdup(cls->remhost_ignore_list->pool, arg); | ||
| 1081 | return NULL; | ||
| 1082 | } | ||
| 1083 | |||
| 1084 | const char *add_log_sql_note(cmd_parms *parms, void *dummy, char *arg) | ||
| 1085 | { | ||
| 1086 | char **addme; | ||
| 1087 | logsql_state *cls = get_module_config(parms->server->module_config, &sql_log_module); | ||
| 1088 | |||
| 1089 | addme = push_array(cls->notes_list); | ||
| 1090 | *addme = pstrdup(cls->notes_list->pool, arg); | ||
| 1091 | return NULL; | ||
| 1092 | } | ||
| 1093 | |||
| 1094 | const char *add_log_sql_hout(cmd_parms *parms, void *dummy, char *arg) | ||
| 1095 | { | ||
| 1096 | char **addme; | ||
| 1097 | logsql_state *cls = get_module_config(parms->server->module_config, &sql_log_module); | ||
| 1098 | |||
| 1099 | addme = push_array(cls->hout_list); | ||
| 1100 | *addme = pstrdup(cls->hout_list->pool, arg); | ||
| 1101 | return NULL; | ||
| 1102 | } | ||
| 1103 | |||
| 1104 | const char *add_log_sql_hin(cmd_parms *parms, void *dummy, char *arg) | ||
| 1105 | { | ||
| 1106 | char **addme; | ||
| 1107 | logsql_state *cls = get_module_config(parms->server->module_config, &sql_log_module); | ||
| 1108 | |||
| 1109 | addme = push_array(cls->hin_list); | ||
| 1110 | *addme = pstrdup(cls->hin_list->pool, arg); | ||
| 1111 | return NULL; | ||
| 1112 | } | ||
| 1113 | |||
| 1114 | const char *add_log_sql_cookie(cmd_parms *parms, void *dummy, char *arg) | ||
| 1115 | { | ||
| 1116 | char **addme; | ||
| 1117 | logsql_state *cls = get_module_config(parms->server->module_config, &sql_log_module); | ||
| 1118 | |||
| 1119 | addme = push_array(cls->cookie_list); | ||
| 1120 | *addme = pstrdup(cls->cookie_list->pool, arg); | ||
| 1121 | return NULL; | ||
| 1122 | } | ||
| 1123 | |||
| 1124 | |||
| 1125 | |||
| 1126 | /*------------------------------------------------------------* | ||
| 1127 | * Apache-specific hooks into the module code * | ||
| 1128 | * that are defined in the array 'mysql_lgog_module' (at EOF) * | ||
| 1129 | *------------------------------------------------------------*/ | ||
| 1130 | |||
| 1131 | |||
| 1132 | /* | ||
| 1133 | * This function is called during server initialisation when an heavy-weight | ||
| 1134 | * process (such as a child) is being initialised. As with the | ||
| 1135 | * module-initialisation function, any information that needs to be recorded | ||
| 1136 | * must be in static cells, since there's no configuration record. | ||
| 1137 | * | ||
| 1138 | * There is no return value. | ||
| 1139 | */ | ||
| 1140 | static void log_sql_child_init(server_rec *s, pool *p) | ||
| 1141 | { | ||
| 1142 | int retval; | ||
| 1143 | |||
| 1144 | /* Open a link to the database */ | ||
| 1145 | retval = open_logdb_link(s); | ||
| 1146 | if (!retval) | ||
| 1147 | ap_log_error(APLOG_MARK,ERRLEVEL,s,"mod_log_sql: child spawned but unable to open database link"); | ||
| 1148 | |||
| 1149 | #ifdef DEBUG | ||
| 1150 | if ( (retval == 1) || (retval == 2) ) | ||
| 1151 | ap_log_error(APLOG_MARK,DEBUGLEVEL,s,"mod_log_sql: open_logdb_link successful"); | ||
| 1152 | if (retval == 3) | ||
| 1153 | ap_log_error(APLOG_MARK,DEBUGLEVEL,s,"mod_log_sql: open_logdb_link said that preservation is forced"); | ||
| 1154 | #endif | ||
| 1155 | } | ||
| 1156 | |||
| 1157 | /* | ||
| 1158 | * This function is called when an heavy-weight process (such as a child) is | ||
| 1159 | * being run down or destroyed. As with the child-initialisation function, | ||
| 1160 | * any information that needs to be recorded must be in static cells, since | ||
| 1161 | * there's no configuration record. | ||
| 1162 | * | ||
| 1163 | * There is no return value. | ||
| 1164 | */ | ||
| 1165 | static void log_sql_child_exit(server_rec *s, pool *p) | ||
| 1166 | { | ||
| 1167 | mysql_close(logsql_server_p); | ||
| 1168 | } | ||
| 1169 | |||
| 1170 | |||
| 1171 | /* | ||
| 1172 | void *log_sql_initializer(server_rec *main_server, pool *p) | ||
| 1173 | { | ||
| 1174 | server_rec *s; | ||
| 1175 | |||
| 1176 | logsql_state main_conf = ap_get_module_config(main_server->module_config, &sql_log_module); | ||
| 1177 | |||
| 1178 | for (server_rec *s = main_server; s; s = s->next) { | ||
| 1179 | conf = ap_get_module_config(s->module_config, &sql_log_module); | ||
| 1180 | if (conf->transfer_log_format == NULL && s != main_server) { | ||
| 1181 | *conf = *main_conf; | ||
| 1182 | } | ||
| 1183 | |||
| 1184 | } | ||
| 1185 | */ | ||
| 1186 | |||
| 1187 | /* | ||
| 1188 | * This function gets called to create a per-server configuration | ||
| 1189 | * record. It will always be called for the main server and | ||
| 1190 | * for each virtual server that is established. Each server maintains | ||
| 1191 | * its own state that is separate from the others' states. | ||
| 1192 | * | ||
| 1193 | * The return value is a pointer to the created module-specific | ||
| 1194 | * structure. | ||
| 1195 | */ | ||
| 1196 | void *log_sql_make_state(pool *p, server_rec *s) | ||
| 1197 | { | ||
| 1198 | |||
| 1199 | logsql_state *cls = (logsql_state *) ap_palloc(p, sizeof(logsql_state)); | ||
| 1200 | |||
| 1201 | /* These defaults are overridable in the httpd.conf file. */ | ||
| 1202 | cls->transfer_table_name = NULL; /* No default b/c we want its absence to disable logging */ | ||
| 1203 | cls->transfer_log_format = "AbHhmRSsTUuv"; | ||
| 1204 | cls->notes_table_name = "notes"; | ||
| 1205 | cls->hin_table_name = "headers_in"; | ||
| 1206 | cls->hout_table_name = "headers_out"; | ||
| 1207 | cls->cookie_table_name = "cookies"; | ||
| 1208 | cls->preserve_file = "/tmp/sql-preserve"; | ||
| 1209 | |||
| 1210 | cls->transfer_ignore_list = make_array(p, 1, sizeof(char *)); | ||
| 1211 | cls->transfer_accept_list = make_array(p, 1, sizeof(char *)); | ||
| 1212 | cls->remhost_ignore_list = make_array(p, 1, sizeof(char *)); | ||
| 1213 | cls->notes_list = make_array(p, 1, sizeof(char *)); | ||
| 1214 | cls->hin_list = make_array(p, 1, sizeof(char *)); | ||
| 1215 | cls->hout_list = make_array(p, 1, sizeof(char *)); | ||
| 1216 | cls->cookie_list = make_array(p, 1, sizeof(char *)); | ||
| 1217 | cls->cookie_name = NULL; | ||
| 1218 | |||
| 1219 | return (void *) cls; | ||
| 1220 | } | ||
| 1221 | |||
| 1222 | |||
| 1223 | /* Setup of the available httpd.conf configuration commands. | ||
| 1224 | * Structure: command, function called, NULL, where available, how many arguments, verbose description | ||
| 1225 | */ | ||
| 1226 | command_rec log_sql_cmds[] = { | ||
| 1227 | {"LogSQLTransferLogTable", set_log_sql_transfer_table, NULL, RSRC_CONF, TAKE1, | ||
| 1228 | "The database table that holds the transfer log"} | ||
| 1229 | , | ||
| 1230 | {"LogSQLNotesLogTable", set_log_sql_notes_table, NULL, RSRC_CONF, TAKE1, | ||
| 1231 | "The database table that holds the notes"} | ||
| 1232 | , | ||
| 1233 | {"LogSQLHeadersOutLogTable", set_log_sql_hout_table, NULL, RSRC_CONF, TAKE1, | ||
| 1234 | "The database table that holds the outbound headers"} | ||
| 1235 | , | ||
| 1236 | {"LogSQLHeadersInLogTable", set_log_sql_hin_table, NULL, RSRC_CONF, TAKE1, | ||
| 1237 | "The database table that holds the inbound headers"} | ||
| 1238 | , | ||
| 1239 | {"LogSQLCookieLogTable", set_log_sql_cookie_table, NULL, RSRC_CONF, TAKE1, | ||
| 1240 | "The database table that holds the cookie info"} | ||
| 1241 | , | ||
| 1242 | {"LogSQLTransferLogFormat", set_log_sql_transfer_log_format, NULL, RSRC_CONF, TAKE1, | ||
| 1243 | "Instruct the module what information to log to the database transfer log"} | ||
| 1244 | , | ||
| 1245 | {"LogSQLMachineID", set_log_sql_machine_id, NULL, RSRC_CONF, TAKE1, | ||
| 1246 | "Machine ID that the module will log, useful in web clusters to differentiate machines"} | ||
| 1247 | , | ||
| 1248 | {"LogSQLRequestAccept", add_log_sql_transfer_accept, NULL, RSRC_CONF, ITERATE, | ||
| 1249 | "List of URIs to accept for logging. Accesses that don't match will not be logged"} | ||
| 1250 | , | ||
| 1251 | {"LogSQLRequestIgnore", add_log_sql_transfer_ignore, NULL, RSRC_CONF, ITERATE, | ||
| 1252 | "List of URIs to ignore. Accesses that match will not be logged to database"} | ||
| 1253 | , | ||
| 1254 | {"LogSQLRemhostIgnore", add_log_sql_remhost_ignore, NULL, RSRC_CONF, ITERATE, | ||
| 1255 | "List of remote hosts to ignore. Accesses that match will not be logged to database"} | ||
| 1256 | , | ||
| 1257 | {"LogSQLDatabase", set_log_sql_db, NULL, RSRC_CONF, TAKE1, | ||
| 1258 | "The name of the database database for logging"} | ||
| 1259 | , | ||
| 1260 | {"LogSQLWhichCookie", set_log_sql_cookie, NULL, RSRC_CONF, TAKE1, | ||
| 1261 | "The single cookie that you want logged in the access_log when using the 'c' config directive"} | ||
| 1262 | , | ||
| 1263 | {"LogSQLLoginInfo", set_log_sql_info, NULL, RSRC_CONF, TAKE3, | ||
| 1264 | "The database host, user-id and password for logging"} | ||
| 1265 | , | ||
| 1266 | {"LogSQLCreateTables", set_log_sql_create, NULL, RSRC_CONF, FLAG, | ||
| 1267 | "Turn on module's capability to create its SQL tables on the fly"} | ||
| 1268 | , | ||
| 1269 | {"LogSQLMassVirtualHosting", set_log_sql_massvirtual, NULL, RSRC_CONF, FLAG, | ||
| 1270 | "Activates option(s) useful for ISPs performing mass virutal hosting"} | ||
| 1271 | , | ||
| 1272 | {"LogSQLForcePreserve", set_log_sql_force_preserve, NULL, RSRC_CONF, FLAG, | ||
| 1273 | "Forces logging to preserve file and bypasses database"} | ||
| 1274 | , | ||
| 1275 | {"LogSQLPreserveFile", set_log_sql_preserve_file, NULL, RSRC_CONF, TAKE1, | ||
| 1276 | "Name of the file to use for data preservation during database downtime"} | ||
| 1277 | , | ||
| 1278 | {"LogSQLSocketFile", set_log_sql_socket_file, NULL, RSRC_CONF, TAKE1, | ||
| 1279 | "Name of the file to employ for socket connections to database"} | ||
| 1280 | , | ||
| 1281 | {"LogSQLTCPPort", set_log_sql_tcp_port, NULL, RSRC_CONF, TAKE1, | ||
| 1282 | "Port number to use for TCP connections to database, defaults to 3306 if not set"} | ||
| 1283 | , | ||
| 1284 | {"LogSQLWhichNotes", add_log_sql_note, NULL, RSRC_CONF, ITERATE, | ||
| 1285 | "Notes that you would like to log in a separate table"} | ||
| 1286 | , | ||
| 1287 | {"LogSQLWhichHeadersOut", add_log_sql_hout, NULL, RSRC_CONF, ITERATE, | ||
| 1288 | "Outbound headers that you would like to log in a separate table"} | ||
| 1289 | , | ||
| 1290 | {"LogSQLWhichHeadersIn", add_log_sql_hin, NULL, RSRC_CONF, ITERATE, | ||
| 1291 | "Inbound headers that you would like to log in a separate table"} | ||
| 1292 | , | ||
| 1293 | {"LogSQLWhichCookies", add_log_sql_cookie, NULL, RSRC_CONF, ITERATE, | ||
| 1294 | "The cookie(s) that you would like to log in a separate table"} | ||
| 1295 | , | ||
| 1296 | {NULL} | ||
| 1297 | }; | ||
| 1298 | |||
| 1299 | |||
| 1300 | |||
| 1301 | /* Routine to perform the actual construction and execution of the relevant | ||
| 1302 | * INSERT statements. | ||
| 1303 | */ | ||
| 1304 | int log_sql_transaction(request_rec *orig) | ||
| 1305 | { | ||
| 1306 | char **ptrptr, **ptrptr2; | ||
| 1307 | logsql_state *cls = get_module_config(orig->server->module_config, &sql_log_module); | ||
| 1308 | const char *access_query; | ||
| 1309 | request_rec *r; | ||
| 1310 | |||
| 1311 | /* We handle mass virtual hosting differently. Dynamically determine the name | ||
| 1312 | * of the table from the virtual server's name, and flag it for creation. | ||
| 1313 | */ | ||
| 1314 | if (logsql_massvirtual) { | ||
| 1315 | char *access_base = "access_"; | ||
| 1316 | char *notes_base = "notes_"; | ||
| 1317 | char *hout_base = "headout_"; | ||
| 1318 | char *hin_base = "headin_"; | ||
| 1319 | char *cookie_base = "cookies_"; | ||
| 1320 | char *a_tablename; | ||
| 1321 | char *n_tablename; | ||
| 1322 | char *i_tablename; | ||
| 1323 | char *o_tablename; | ||
| 1324 | char *c_tablename; | ||
| 1325 | unsigned int i; | ||
| 1326 | |||
| 1327 | /* Find memory long enough to hold the table name + \0. */ | ||
| 1328 | a_tablename = ap_pstrcat(orig->pool, access_base, ap_get_server_name(orig), NULL); | ||
| 1329 | n_tablename = ap_pstrcat(orig->pool, notes_base, ap_get_server_name(orig), NULL); | ||
| 1330 | i_tablename = ap_pstrcat(orig->pool, hin_base, ap_get_server_name(orig), NULL); | ||
| 1331 | o_tablename = ap_pstrcat(orig->pool, hout_base, ap_get_server_name(orig), NULL); | ||
| 1332 | c_tablename = ap_pstrcat(orig->pool, cookie_base, ap_get_server_name(orig), NULL); | ||
| 1333 | |||
| 1334 | /* Transform any dots to underscores */ | ||
| 1335 | for (i = 0; i < strlen(a_tablename); i++) { | ||
| 1336 | if (a_tablename[i] == '.') | ||
| 1337 | a_tablename[i] = '_'; | ||
| 1338 | } | ||
| 1339 | for (i = 0; i < strlen(n_tablename); i++) { | ||
| 1340 | if (n_tablename[i] == '.') | ||
| 1341 | n_tablename[i] = '_'; | ||
| 1342 | } | ||
| 1343 | for (i = 0; i < strlen(i_tablename); i++) { | ||
| 1344 | if (i_tablename[i] == '.') | ||
| 1345 | i_tablename[i] = '_'; | ||
| 1346 | } | ||
| 1347 | for (i = 0; i < strlen(o_tablename); i++) { | ||
| 1348 | if (o_tablename[i] == '.') | ||
| 1349 | o_tablename[i] = '_'; | ||
| 1350 | } | ||
| 1351 | for (i = 0; i < strlen(c_tablename); i++) { | ||
| 1352 | if (c_tablename[i] == '.') | ||
| 1353 | c_tablename[i] = '_'; | ||
| 1354 | } | ||
| 1355 | |||
| 1356 | /* Tell this virtual server its transfer table name, and | ||
| 1357 | * turn on create_tables, which is implied by massvirtual. | ||
| 1358 | */ | ||
| 1359 | cls->transfer_table_name = a_tablename; | ||
| 1360 | cls->notes_table_name = n_tablename; | ||
| 1361 | cls->hout_table_name = o_tablename; | ||
| 1362 | cls->hin_table_name = i_tablename; | ||
| 1363 | cls->cookie_table_name = c_tablename; | ||
| 1364 | logsql_createtables = 1; | ||
| 1365 | } | ||
| 1366 | |||
| 1367 | /* Do we have enough info to log? */ | ||
| 1368 | if (!cls->transfer_table_name) { | ||
| 1369 | return DECLINED; | ||
| 1370 | } else { | ||
| 1371 | const char *thehost; | ||
| 1372 | const char *theitem; | ||
| 1373 | char *fields = "", *values = ""; | ||
| 1374 | char *itemsets = ""; | ||
| 1375 | char *note_query = NULL; | ||
| 1376 | char *hin_query = NULL; | ||
| 1377 | char *hout_query = NULL; | ||
| 1378 | char *cookie_query = NULL; | ||
| 1379 | const char *unique_id; | ||
| 1380 | const char *formatted_item; | ||
| 1381 | int i, j, length; | ||
| 1382 | int proceed; | ||
| 1383 | |||
| 1384 | for (r = orig; r->next; r = r->next) { | ||
| 1385 | continue; | ||
| 1386 | } | ||
| 1387 | |||
| 1388 | /* The following is a stolen upsetting mess of pointers, I'm sorry. | ||
| 1389 | * Anyone with the motiviation and/or the time should feel free | ||
| 1390 | * to make this cleaner. :) */ | ||
| 1391 | ptrptr2 = (char **) (cls->transfer_accept_list->elts + (cls->transfer_accept_list->nelts * cls->transfer_accept_list->elt_size)); | ||
| 1392 | |||
| 1393 | /* Go through each element of the accept list and compare it to the | ||
| 1394 | * request_uri. If we don't get a match, return without logging */ | ||
| 1395 | if ((r->uri) && (cls->transfer_accept_list->nelts)) { | ||
| 1396 | proceed = 0; | ||
| 1397 | for (ptrptr = (char **) cls->transfer_accept_list->elts; ptrptr < ptrptr2; ptrptr = (char **) ((char *) ptrptr + cls->transfer_accept_list->elt_size)) | ||
| 1398 | if (strstr(r->uri, *ptrptr)) { | ||
| 1399 | proceed = 1; | ||
| 1400 | break; | ||
| 1401 | } | ||
| 1402 | if (!proceed) | ||
| 1403 | return OK; | ||
| 1404 | } | ||
| 1405 | |||
| 1406 | /* Go through each element of the ignore list and compare it to the | ||
| 1407 | * request_uri. If we get a match, return without logging */ | ||
| 1408 | ptrptr2 = (char **) (cls->transfer_ignore_list->elts + (cls->transfer_ignore_list->nelts * cls->transfer_ignore_list->elt_size)); | ||
| 1409 | if (r->uri) { | ||
| 1410 | for (ptrptr = (char **) cls->transfer_ignore_list->elts; ptrptr < ptrptr2; ptrptr = (char **) ((char *) ptrptr + cls->transfer_ignore_list->elt_size)) | ||
| 1411 | if (strstr(r->uri, *ptrptr)) { | ||
| 1412 | return OK; | ||
| 1413 | } | ||
| 1414 | } | ||
| 1415 | |||
| 1416 | /* Go through each element of the ignore list and compare it to the | ||
| 1417 | * remote host. If we get a match, return without logging */ | ||
| 1418 | ptrptr2 = (char **) (cls->remhost_ignore_list->elts + (cls->remhost_ignore_list->nelts * cls->remhost_ignore_list->elt_size)); | ||
| 1419 | thehost = get_remote_host(r->connection, r->per_dir_config, REMOTE_NAME); | ||
| 1420 | if (thehost) { | ||
| 1421 | for (ptrptr = (char **) cls->remhost_ignore_list->elts; ptrptr < ptrptr2; ptrptr = (char **) ((char *) ptrptr + cls->remhost_ignore_list->elt_size)) | ||
| 1422 | if (strstr(thehost, *ptrptr)) { | ||
| 1423 | return OK; | ||
| 1424 | } | ||
| 1425 | } | ||
| 1426 | |||
| 1427 | length = strlen(cls->transfer_log_format); | ||
| 1428 | |||
| 1429 | /* Iterate through the format characters and set up the INSERT string according to | ||
| 1430 | * what the user has configured. */ | ||
| 1431 | for (i = 0; i < length; i++) { | ||
| 1432 | j = 0; | ||
| 1433 | |||
| 1434 | while (log_sql_item_keys[j].ch) { | ||
| 1435 | |||
| 1436 | if (log_sql_item_keys[j].ch == cls->transfer_log_format[i]) { | ||
| 1437 | /* Yes, this key is one of the configured keys. | ||
| 1438 | * Call the key's function and put the returned value into 'formatted_item' */ | ||
| 1439 | formatted_item = log_sql_item_keys[j].func(log_sql_item_keys[j].want_orig_default ? orig : r, ""); | ||
| 1440 | |||
| 1441 | /* Massage 'formatted_item' for proper SQL eligibility... */ | ||
| 1442 | if (!formatted_item) { | ||
| 1443 | formatted_item = ""; | ||
| 1444 | } else if (formatted_item[0] == '-' && formatted_item[1] == '\0' && !log_sql_item_keys[j].string_contents) { | ||
| 1445 | /* If apache tried to log a '-' character for a numeric field, convert that to a zero | ||
| 1446 | * because the database expects a numeral and will reject the '-' character. */ | ||
| 1447 | formatted_item = "0"; | ||
| 1448 | } | ||
| 1449 | |||
| 1450 | /* Append the fieldname and value-to-insert to the appropriate strings, quoting stringvals with ' as appropriate */ | ||
| 1451 | fields = pstrcat(r->pool, fields, (i > 0 ? "," : ""), | ||
| 1452 | log_sql_item_keys[j].sql_field_name, NULL); | ||
| 1453 | |||
| 1454 | values = pstrcat(r->pool, values, (i > 0 ? "," : ""), | ||
| 1455 | (log_sql_item_keys[j].string_contents ? "'" : ""), | ||
| 1456 | escape_query(formatted_item, r->pool), | ||
| 1457 | (log_sql_item_keys[j].string_contents ? "'" : ""), NULL); | ||
| 1458 | break; | ||
| 1459 | } | ||
| 1460 | j++; | ||
| 1461 | |||
| 1462 | } | ||
| 1463 | } | ||
| 1464 | |||
| 1465 | /* Work through the list of notes defined by LogSQLWhichNotes */ | ||
| 1466 | i = 0; | ||
| 1467 | unique_id = extract_unique_id(r, ""); | ||
| 1468 | |||
| 1469 | ptrptr2 = (char **) (cls->notes_list->elts + (cls->notes_list->nelts * cls->notes_list->elt_size)); | ||
| 1470 | for (ptrptr = (char **) cls->notes_list->elts; ptrptr < ptrptr2; ptrptr = (char **) ((char *) ptrptr + cls->notes_list->elt_size)) { | ||
| 1471 | /* If the specified note (*ptrptr) exists for the current request... */ | ||
| 1472 | if ((theitem = ap_table_get(r->notes, *ptrptr))) { | ||
| 1473 | itemsets = ap_pstrcat(r->pool, itemsets, | ||
| 1474 | (i > 0 ? "," : ""), | ||
| 1475 | "('", | ||
| 1476 | unique_id, | ||
| 1477 | "','", | ||
| 1478 | escape_query(*ptrptr, r->pool), | ||
| 1479 | "','", | ||
| 1480 | escape_query(theitem, r->pool), | ||
| 1481 | "')", | ||
| 1482 | NULL); | ||
| 1483 | i++; | ||
| 1484 | } | ||
| 1485 | } | ||
| 1486 | if ( itemsets != "" ) { | ||
| 1487 | note_query = ap_pstrcat(r->pool, logsql_insertclause, "`", cls->notes_table_name, "` (id, item, val) values ", itemsets, NULL); | ||
| 1488 | #ifdef DEBUG | ||
| 1489 | ap_log_error(APLOG_MARK,DEBUGLEVEL,orig->server,"mod_log_sql: note string: %s", note_query); | ||
| 1490 | #endif | ||
| 1491 | } | ||
| 1492 | |||
| 1493 | /* Work through the list of headers-out defined by LogSQLWhichHeadersOut*/ | ||
| 1494 | i = 0; | ||
| 1495 | itemsets = ""; | ||
| 1496 | |||
| 1497 | ptrptr2 = (char **) (cls->hout_list->elts + (cls->hout_list->nelts * cls->hout_list->elt_size)); | ||
| 1498 | for (ptrptr = (char **) cls->hout_list->elts; ptrptr < ptrptr2; ptrptr = (char **) ((char *) ptrptr + cls->hout_list->elt_size)) { | ||
| 1499 | /* If the specified header (*ptrptr) exists for the current request... */ | ||
| 1500 | if ((theitem = ap_table_get(r->headers_out, *ptrptr))) { | ||
| 1501 | itemsets = ap_pstrcat(r->pool, itemsets, | ||
| 1502 | (i > 0 ? "," : ""), | ||
| 1503 | "('", | ||
| 1504 | unique_id, | ||
| 1505 | "','", | ||
| 1506 | escape_query(*ptrptr, r->pool), | ||
| 1507 | "','", | ||
| 1508 | escape_query(theitem, r->pool), | ||
| 1509 | "')", | ||
| 1510 | NULL); | ||
| 1511 | i++; | ||
| 1512 | } | ||
| 1513 | } | ||
| 1514 | if ( itemsets != "" ) { | ||
| 1515 | hout_query = ap_pstrcat(r->pool, logsql_insertclause, "`", cls->hout_table_name, "` (id, item, val) values ", itemsets, NULL); | ||
| 1516 | #ifdef DEBUG | ||
| 1517 | ap_log_error(APLOG_MARK,DEBUGLEVEL,orig->server,"mod_log_sql: header_out string: %s", hout_query); | ||
| 1518 | #endif | ||
| 1519 | } | ||
| 1520 | |||
| 1521 | |||
| 1522 | /* Work through the list of headers-in defined by LogSQLWhichHeadersIn */ | ||
| 1523 | i = 0; | ||
| 1524 | itemsets = ""; | ||
| 1525 | |||
| 1526 | ptrptr2 = (char **) (cls->hin_list->elts + (cls->hin_list->nelts * cls->hin_list->elt_size)); | ||
| 1527 | for (ptrptr = (char **) cls->hin_list->elts; ptrptr < ptrptr2; ptrptr = (char **) ((char *) ptrptr + cls->hin_list->elt_size)) { | ||
| 1528 | /* If the specified header (*ptrptr) exists for the current request... */ | ||
| 1529 | if ((theitem = ap_table_get(r->headers_in, *ptrptr))) { | ||
| 1530 | itemsets = ap_pstrcat(r->pool, itemsets, | ||
| 1531 | (i > 0 ? "," : ""), | ||
| 1532 | "('", | ||
| 1533 | unique_id, | ||
| 1534 | "','", | ||
| 1535 | escape_query(*ptrptr, r->pool), | ||
| 1536 | "','", | ||
| 1537 | escape_query(theitem, r->pool), | ||
| 1538 | "')", | ||
| 1539 | NULL); | ||
| 1540 | i++; | ||
| 1541 | } | ||
| 1542 | } | ||
| 1543 | if ( itemsets != "" ) { | ||
| 1544 | hin_query = ap_pstrcat(r->pool, logsql_insertclause, "`", cls->hin_table_name, "` (id, item, val) values ", itemsets, NULL); | ||
| 1545 | #ifdef DEBUG | ||
| 1546 | ap_log_error(APLOG_MARK,DEBUGLEVEL,orig->server,"mod_log_sql: header_in string: %s", hin_query); | ||
| 1547 | #endif | ||
| 1548 | } | ||
| 1549 | |||
| 1550 | |||
| 1551 | /* Work through the list of cookies defined by LogSQLWhichCookies */ | ||
| 1552 | i = 0; | ||
| 1553 | itemsets = ""; | ||
| 1554 | |||
| 1555 | ptrptr2 = (char **) (cls->cookie_list->elts + (cls->cookie_list->nelts * cls->cookie_list->elt_size)); | ||
| 1556 | for (ptrptr = (char **) cls->cookie_list->elts; ptrptr < ptrptr2; ptrptr = (char **) ((char *) ptrptr + cls->cookie_list->elt_size)) { | ||
| 1557 | /* If the specified cookie (*ptrptr) exists for the current request... */ | ||
| 1558 | if ( strncmp((theitem = extract_specific_cookie(r, *ptrptr)), "-", 1) ) { | ||
| 1559 | itemsets = ap_pstrcat(r->pool, itemsets, | ||
| 1560 | (i > 0 ? "," : ""), | ||
| 1561 | "('", | ||
| 1562 | unique_id, | ||
| 1563 | "','", | ||
| 1564 | escape_query(*ptrptr, r->pool), | ||
| 1565 | "','", | ||
| 1566 | escape_query(theitem, r->pool), | ||
| 1567 | "')", | ||
| 1568 | NULL); | ||
| 1569 | i++; | ||
| 1570 | } | ||
| 1571 | |||
| 1572 | } | ||
| 1573 | if ( itemsets != "" ) { | ||
| 1574 | cookie_query = ap_pstrcat(r->pool, logsql_insertclause, "`", cls->cookie_table_name, "` (id, item, val) values ", itemsets, NULL); | ||
| 1575 | #ifdef DEBUG | ||
| 1576 | ap_log_error(APLOG_MARK,DEBUGLEVEL,orig->server,"mod_log_sql: cookie string: %s", cookie_query); | ||
| 1577 | #endif | ||
| 1578 | } | ||
| 1579 | |||
| 1580 | |||
| 1581 | /* Set up the actual INSERT statement */ | ||
| 1582 | access_query = ap_pstrcat(r->pool, logsql_insertclause, "`", cls->transfer_table_name, "` (", fields, ") values (", values, ")", NULL); | ||
| 1583 | |||
| 1584 | #ifdef DEBUG | ||
| 1585 | ap_log_error(APLOG_MARK,DEBUGLEVEL,r->server,"mod_log_sql: access string: %s", access_query); | ||
| 1586 | #endif | ||
| 1587 | |||
| 1588 | /* If the person activated force-preserve, go ahead and push all the entries | ||
| 1589 | * into the preserve file, then return. | ||
| 1590 | */ | ||
| 1591 | if (logsql_forcepreserve) { | ||
| 1592 | #ifdef DEBUG | ||
| 1593 | ap_log_error(APLOG_MARK,DEBUGLEVEL,orig->server,"mod_log_sql: preservation forced"); | ||
| 1594 | #endif | ||
| 1595 | preserve_entry(orig, access_query); | ||
| 1596 | if ( note_query != NULL ) | ||
| 1597 | preserve_entry(orig, note_query); | ||
| 1598 | if ( hin_query != NULL ) | ||
| 1599 | preserve_entry(orig, hin_query); | ||
| 1600 | if ( hout_query != NULL ) | ||
| 1601 | preserve_entry(orig, hout_query); | ||
| 1602 | if ( cookie_query != NULL ) | ||
| 1603 | preserve_entry(orig, cookie_query); | ||
| 1604 | return OK; | ||
| 1605 | } | ||
| 1606 | |||
| 1607 | /* How's our mysql link integrity? */ | ||
| 1608 | if (logsql_server_p == NULL) { | ||
| 1609 | |||
| 1610 | /* Make a try to establish the link */ | ||
| 1611 | open_logdb_link(r->server); | ||
| 1612 | |||
| 1613 | if (logsql_server_p == NULL) { | ||
| 1614 | /* Unable to re-establish a DB link, so assume that it's really | ||
| 1615 | * gone and send the entry to the preserve file instead. | ||
| 1616 | * This short-circuits safe_sql_query() during a db outage and therefore | ||
| 1617 | * we don't keep logging the db error over and over. | ||
| 1618 | */ | ||
| 1619 | preserve_entry(orig, access_query); | ||
| 1620 | if ( note_query != NULL ) | ||
| 1621 | preserve_entry(orig, note_query); | ||
| 1622 | if ( hin_query != NULL ) | ||
| 1623 | preserve_entry(orig, hin_query); | ||
| 1624 | if ( hout_query != NULL ) | ||
| 1625 | preserve_entry(orig, hout_query); | ||
| 1626 | if ( cookie_query != NULL ) | ||
| 1627 | preserve_entry(orig, cookie_query); | ||
| 1628 | |||
| 1629 | return OK; | ||
| 1630 | } else { | ||
| 1631 | /* Whew, we got the DB link back */ | ||
| 1632 | ap_log_error(APLOG_MARK,NOTICELEVEL,orig->server,"mod_log_sql: child established database connection"); | ||
| 1633 | } | ||
| 1634 | } | ||
| 1635 | |||
| 1636 | |||
| 1637 | /* ---> So as of here we have a non-null value of mysql_log. <--- */ | ||
| 1638 | /* ---> i.e. we have a good MySQL connection. <--- */ | ||
| 1639 | |||
| 1640 | /* Make the access-table insert */ | ||
| 1641 | safe_sql_query(orig, access_query); | ||
| 1642 | |||
| 1643 | /* Log the optional notes, headers, etc. */ | ||
| 1644 | if (note_query) | ||
| 1645 | safe_sql_query(orig, note_query); | ||
| 1646 | |||
| 1647 | if (hout_query) | ||
| 1648 | safe_sql_query(orig, hout_query); | ||
| 1649 | |||
| 1650 | if (hin_query) | ||
| 1651 | safe_sql_query(orig, hin_query); | ||
| 1652 | |||
| 1653 | if (cookie_query) | ||
| 1654 | safe_sql_query(orig, cookie_query); | ||
| 1655 | |||
| 1656 | return OK; | ||
| 1657 | } | ||
| 1658 | } | ||
| 1659 | |||
| 1660 | |||
| 1661 | |||
| 1662 | |||
| 1663 | /* The configuration array that sets up the hooks into the module. */ | ||
| 1664 | module sql_log_module = { | ||
| 1665 | STANDARD_MODULE_STUFF, | ||
| 1666 | NULL, /* module initializer */ | ||
| 1667 | NULL, /* create per-dir config */ | ||
| 1668 | NULL, /* merge per-dir config */ | ||
| 1669 | log_sql_make_state, /* create server config */ | ||
| 1670 | NULL, /* merge server config */ | ||
| 1671 | log_sql_cmds, /* config directive table */ | ||
| 1672 | NULL, /* [9] content handlers */ | ||
| 1673 | NULL, /* [2] URI-to-filename translation */ | ||
| 1674 | NULL, /* [5] check/validate user_id */ | ||
| 1675 | NULL, /* [6] check authorization */ | ||
| 1676 | NULL, /* [4] check access by host */ | ||
| 1677 | NULL, /* [7] MIME type checker/setter */ | ||
| 1678 | NULL, /* [8] fixups */ | ||
| 1679 | log_sql_transaction, /* [10] logger */ | ||
| 1680 | NULL /* [3] header parser */ | ||
| 1681 | #if MODULE_MAGIC_NUMBER >= 19970728 /* 1.3-dev or later support these additionals... */ | ||
| 1682 | ,log_sql_child_init, /* child process initializer */ | ||
| 1683 | log_sql_child_exit, /* process exit/cleanup */ | ||
| 1684 | NULL /* [1] post read-request */ | ||
| 1685 | #endif | ||
| 1686 | |||
| 1687 | }; | ||
diff --git a/src/Makefile.in b/src/Makefile.in new file mode 100644 index 0000000..5a5dd6a --- /dev/null +++ b/src/Makefile.in | |||
| @@ -0,0 +1,168 @@ | |||
| 1 | # @configure_input@ | ||
| 2 | |||
| 3 | top_srcdir = @top_srcdir@ | ||
| 4 | srcdir = @abs_srcdir@ | ||
| 5 | builddir = @abs_builddir@ | ||
| 6 | |||
| 7 | HEADERS = ../include/mod_log_sql.h \ | ||
| 8 | functions.h \ | ||
| 9 | functions13.h \ | ||
| 10 | functions20.h \ | ||
| 11 | ../include/apache13.h \ | ||
| 12 | ../include/apache20.h \ | ||
| 13 | ../include/winconfig.h | ||
| 14 | |||
| 15 | CFLAGS = -Wc,-Wall -Wc,-fno-strict-aliasing -I$(top_srcdir)/include | ||
| 16 | |||
| 17 | ifeq (@OOO_MAINTAIN@,1) | ||
| 18 | CFLAGS += -Wc,-Werror | ||
| 19 | endif | ||
| 20 | |||
| 21 | coreSOURCES = @PACKAGE_NAME@.c | ||
| 22 | coreTARGET = @PACKAGE_NAME@@APXS_EXTENSION@ | ||
| 23 | coreLDADD = @RT_LIBS@ | ||
| 24 | coreCFLAGS = | ||
| 25 | coreNAME = log_sql | ||
| 26 | TARGETS = $(coreTARGET) | ||
| 27 | |||
| 28 | sslSOURCES = @PACKAGE_NAME@_ssl.c | ||
| 29 | sslTARGET = @PACKAGE_NAME@_ssl@APXS_EXTENSION@ | ||
| 30 | sslLDADD = | ||
| 31 | sslCFLAGS = @MOD_SSL_CFLAGS@ | ||
| 32 | sslNAME = log_sql_ssl | ||
| 33 | |||
| 34 | ifeq (@WANT_SSL_MOD@,1) | ||
| 35 | TARGETS += $(sslTARGET) | ||
| 36 | endif | ||
| 37 | |||
| 38 | logioSOURCES = @PACKAGE_NAME@_logio.c | ||
| 39 | logioTARGET = @PACKAGE_NAME@_logio@APXS_EXTENSION@ | ||
| 40 | logioLDADD = | ||
| 41 | logioCFLAGS = | ||
| 42 | logioNAME = log_sql_logio | ||
| 43 | |||
| 44 | ifeq (@WANT_LOGIO_MOD@,1) | ||
| 45 | TARGETS += $(logioTARGET) | ||
| 46 | endif | ||
| 47 | |||
| 48 | mysqlSOURCES = @PACKAGE_NAME@_mysql.c | ||
| 49 | mysqlTARGET = @PACKAGE_NAME@_mysql@APXS_EXTENSION@ | ||
| 50 | mysqlLDADD = @MYSQL_LDFLAGS@ @MYSQL_LIBS@ | ||
| 51 | mysqlCFLAGS = @MYSQL_CFLAGS@ | ||
| 52 | mysqlNAME = log_sql_mysql | ||
| 53 | |||
| 54 | ifeq (@WANT_MYSQL_MOD@,1) | ||
| 55 | TARGETS += $(mysqlTARGET) | ||
| 56 | endif | ||
| 57 | |||
| 58 | pgsqlSOURCES = @PACKAGE_NAME@_pgsql.c | ||
| 59 | pgsqlTARGET = @PACKAGE_NAME@_pgsql@APXS_EXTENSION@ | ||
| 60 | pgsqlLDADD = @PGSQL_LDFLAGS@ @PGSQL_LIBS@ | ||
| 61 | pgsqlCFLAGS = @PGSQL_CFLAGS@ | ||
| 62 | pgsqlNAME = log_sql_pgsql | ||
| 63 | |||
| 64 | ifeq (@WANT_PGSQL_MOD@,1) | ||
| 65 | TARGETS += $(pgsqlTARGET) | ||
| 66 | endif | ||
| 67 | |||
| 68 | dbiSOURCES = @PACKAGE_NAME@_dbi.c | ||
| 69 | dbiTARGET = @PACKAGE_NAME@_dbi@APXS_EXTENSION@ | ||
| 70 | dbiLDADD = @DBI_LDFLAGS@ @DBI_LIBS@ | ||
| 71 | dbiCFLAGS = @DBI_CFLAGS@ | ||
| 72 | dbiNAME = log_sql_dbi | ||
| 73 | |||
| 74 | ifeq (@WANT_DBI_MOD@,1) | ||
| 75 | TARGETS += $(dbiTARGET) | ||
| 76 | endif | ||
| 77 | |||
| 78 | dbdSOURCES = @PACKAGE_NAME@_dbd.c | ||
| 79 | dbdTARGET = @PACKAGE_NAME@_dbd@APXS_EXTENSION@ | ||
| 80 | dbdLDADD = | ||
| 81 | dbdCFLAGS = | ||
| 82 | dbdNAME = log_sql_dbd | ||
| 83 | |||
| 84 | ifeq (@WANT_DBD_MOD@,1) | ||
| 85 | TARGETS += $(dbdTARGET) | ||
| 86 | endif | ||
| 87 | |||
| 88 | OBJ = $(coreSOURCES:.c=.o) $(logioSOURCES:.c=.o) $(sslSOURCES:.c=.o) $(mysqlSOURCES:.c=.o) \ | ||
| 89 | $(dbiSOURCES:.c=.o) $(pgsqlSOURCES:.c=.o) $(dbdSOURCES:.c=.o) | ||
| 90 | |||
| 91 | LO = $(coreSOURCES:.c=.lo) $(logioSOURCES:.c=.lo) $(sslSOURCES:.c=.lo) $(mysqlSOURCES:.c=.lo) \ | ||
| 92 | $(dbiSOURCES:.c=.lo) $(pgsqlSOURCES:.c=.lo) $(dbdSOURCES:.c=.lo) | ||
| 93 | |||
| 94 | SLO = $(coreSOURCES:.c=.slo) $(logioSOURCES:.c=.slo) $(sslSOURCES:.c=.slo) $(mysqlSOURCES:.c=.slo) \ | ||
| 95 | $(dbiSOURCES:.c=.slo) $(pgsqlSOURCES:.c=.slo) $(dbdSOURCES:.c=.slo) | ||
| 96 | |||
| 97 | STD_DIST = Makefile.in | ||
| 98 | |||
| 99 | DISTFILES = $(STD_DIST) $(EXTRA_DIST) $(coreSOURCES) $(HEADERS) \ | ||
| 100 | $(sslSOURCES) $(logioSOURCES) $(mysqlSOURCES) $(pgsqlSOURCES) $(dbiSOURCES) $(dbdSOURCES) | ||
| 101 | |||
| 102 | all: $(TARGETS) | ||
| 103 | |||
| 104 | $(coreTARGET): $(coreSOURCES) $(HEADERS) | ||
| 105 | @@APXS_BIN@ -c -o $(coreTARGET) $(coreCFLAGS) $(CFLAGS) \ | ||
| 106 | @DEFS@ @AP_DEFS@ $(coreLDADD) $(coreSOURCES) | ||
| 107 | |||
| 108 | $(logioTARGET): $(logioSOURCES) $(HEADERS) | ||
| 109 | @@APXS_BIN@ -c -o $(logioTARGET) $(logioCFLAGS) $(CFLAGS) \ | ||
| 110 | @DEFS@ @AP_DEFS@ $(logioLDADD) $(logioSOURCES) | ||
| 111 | |||
| 112 | $(sslTARGET): $(sslSOURCES) $(HEADERS) | ||
| 113 | @@APXS_BIN@ -c -o $(sslTARGET) $(sslCFLAGS) $(CFLAGS) \ | ||
| 114 | @DEFS@ @AP_DEFS@ $(sslLDADD) $(sslSOURCES) | ||
| 115 | |||
| 116 | $(mysqlTARGET): $(mysqlSOURCES) $(HEADERS) | ||
| 117 | @@APXS_BIN@ -c -o $(mysqlTARGET) $(mysqlCFLAGS) $(CFLAGS) \ | ||
| 118 | @DEFS@ @AP_DEFS@ $(mysqlLDADD) $(mysqlSOURCES) | ||
| 119 | |||
| 120 | $(pgsqlTARGET): $(pgsqlSOURCES) $(HEADERS) | ||
| 121 | @@APXS_BIN@ -c -o $(pgsqlTARGET) $(pgsqlCFLAGS) $(CFLAGS) \ | ||
| 122 | @DEFS@ @AP_DEFS@ $(pgsqlLDADD) $(pgsqlSOURCES) | ||
| 123 | |||
| 124 | $(dbiTARGET): $(dbiSOURCES) $(HEADERS) | ||
| 125 | @@APXS_BIN@ -c -o $(dbiTARGET) $(dbiCFLAGS) $(CFLAGS) \ | ||
| 126 | @DEFS@ @AP_DEFS@ $(dbiLDADD) $(dbiSOURCES) | ||
| 127 | |||
| 128 | $(dbdTARGET): $(dbdSOURCES) $(HEADERS) | ||
| 129 | @@APXS_BIN@ -c -o $(dbdTARGET) $(dbdCFLAGS) $(CFLAGS) \ | ||
| 130 | @DEFS@ @AP_DEFS@ $(dbdLDADD) $(dbdSOURCES) | ||
| 131 | |||
| 132 | install: $(TARGETS) | ||
| 133 | @@APXS_BIN@ -n $(coreNAME) -i $(coreTARGET); \ | ||
| 134 | if test @WANT_MYSQL_MOD@ -eq 1; then \ | ||
| 135 | @APXS_BIN@ -n $(mysqlNAME) -i $(mysqlTARGET); \ | ||
| 136 | fi; \ | ||
| 137 | if test @WANT_PGSQL_MOD@ -eq 1; then \ | ||
| 138 | @APXS_BIN@ -n $(pgsqlNAME) -i $(pgsqlTARGET); \ | ||
| 139 | fi; \ | ||
| 140 | if test @WANT_DBI_MOD@ -eq 1; then \ | ||
| 141 | @APXS_BIN@ -n $(dbiNAME) -i $(dbiTARGET); \ | ||
| 142 | fi; \ | ||
| 143 | if test @WANT_DBD_MOD@ -eq 1; then \ | ||
| 144 | @APXS_BIN@ -n $(dbdNAME) -i $(dbdTARGET); \ | ||
| 145 | fi; \ | ||
| 146 | if test @WANT_SSL_MOD@ -eq 1; then \ | ||
| 147 | @APXS_BIN@ -n $(sslNAME) -i $(sslTARGET); \ | ||
| 148 | fi; \ | ||
| 149 | if test @WANT_LOGIO_MOD@ -eq 1; then \ | ||
| 150 | @APXS_BIN@ -n $(logioNAME) -i $(logioTARGET); \ | ||
| 151 | fi; | ||
| 152 | |||
| 153 | activate: | ||
| 154 | @@APXS_BIN@ -n $(coreNAME) -i -a $(coreTARGET); \ | ||
| 155 | if test @WANT_SSL_MOD@ -eq 1; then \ | ||
| 156 | @APXS_BIN@ -n $(sslNAME) -i -a $(sslTARGET); \ | ||
| 157 | fi | ||
| 158 | |||
| 159 | clean: | ||
| 160 | $(RM) $(OBJ) $(SLO) $(LO) $(TARGETS) | ||
| 161 | $(RM) -r .libs | ||
| 162 | |||
| 163 | local-dist: $(DISTFILES) | ||
| 164 | mkdir -p $(DESTDIR) | ||
| 165 | cp -dp --parents $(DISTFILES) $(DESTDIR) | ||
| 166 | |||
| 167 | Makefile: Makefile.in ../config.status | ||
| 168 | cd .. && ./config.status | ||
diff --git a/src/functions.h b/src/functions.h new file mode 100644 index 0000000..07711d2 --- /dev/null +++ b/src/functions.h | |||
| @@ -0,0 +1,261 @@ | |||
| 1 | /* $Id$ */ | ||
| 2 | |||
| 3 | /* Begin the individual functions that, given a request r, | ||
| 4 | * extract the needed information from it and return the | ||
| 5 | * value to the calling entity. | ||
| 6 | */ | ||
| 7 | |||
| 8 | static const char *extract_remote_host(request_rec *r, char *a) | ||
| 9 | { | ||
| 10 | return (char *) ap_get_remote_host(r->connection, r->per_dir_config, REMOTE_NAME, NULL); | ||
| 11 | } | ||
| 12 | |||
| 13 | static const char *extract_remote_address(request_rec *r, char *a) __attribute__((unused)); | ||
| 14 | |||
| 15 | static const char *extract_remote_address(request_rec *r, char *a) | ||
| 16 | { | ||
| 17 | return r->connection->remote_ip; | ||
| 18 | } | ||
| 19 | |||
| 20 | static const char *extract_local_address(request_rec *r, char *a) __attribute__((unused)); | ||
| 21 | |||
| 22 | static const char *extract_local_address(request_rec *r, char *a) | ||
| 23 | { | ||
| 24 | return r->connection->local_ip; | ||
| 25 | } | ||
| 26 | |||
| 27 | static const char *extract_remote_logname(request_rec *r, char *a) | ||
| 28 | { | ||
| 29 | return (char *) ap_get_remote_logname(r); | ||
| 30 | } | ||
| 31 | |||
| 32 | static const char *extract_remote_user(request_rec *r, char *a) | ||
| 33 | { | ||
| 34 | #ifdef WITH_APACHE13 | ||
| 35 | char *rvalue = r->connection->user; | ||
| 36 | #else | ||
| 37 | char *rvalue = r->user; | ||
| 38 | #endif | ||
| 39 | if (rvalue == NULL) { | ||
| 40 | rvalue = "-"; | ||
| 41 | } else if (strlen(rvalue) == 0) { | ||
| 42 | rvalue = "\"\""; | ||
| 43 | } | ||
| 44 | return rvalue; | ||
| 45 | } | ||
| 46 | |||
| 47 | static const char *extract_request_line(request_rec *r, char *a) | ||
| 48 | { | ||
| 49 | /* Upddated to mod_log_config logic */ | ||
| 50 | /* NOTE: If the original request contained a password, we | ||
| 51 | * re-write the request line here to contain XXXXXX instead: | ||
| 52 | * (note the truncation before the protocol string for HTTP/0.9 requests) | ||
| 53 | * (note also that r->the_request contains the unmodified request) | ||
| 54 | */ | ||
| 55 | return (r->parsed_uri.password) | ||
| 56 | ? apr_pstrcat(r->pool, r->method, " ", | ||
| 57 | apr_uri_unparse(r->pool, | ||
| 58 | &r->parsed_uri, 0), | ||
| 59 | r->assbackwards ? NULL : " ", | ||
| 60 | r->protocol, NULL) | ||
| 61 | : r->the_request; | ||
| 62 | } | ||
| 63 | |||
| 64 | static const char *extract_request_file(request_rec *r, char *a) | ||
| 65 | { | ||
| 66 | return r->filename; | ||
| 67 | } | ||
| 68 | |||
| 69 | static const char *extract_request_uri(request_rec *r, char *a) | ||
| 70 | { | ||
| 71 | return r->uri; | ||
| 72 | } | ||
| 73 | |||
| 74 | static const char *extract_request_method(request_rec *r, char *a) | ||
| 75 | { | ||
| 76 | return r->method; | ||
| 77 | } | ||
| 78 | |||
| 79 | static const char *extract_request_protocol(request_rec *r, char *a) | ||
| 80 | { | ||
| 81 | return r->protocol; | ||
| 82 | } | ||
| 83 | |||
| 84 | static const char *extract_request_query(request_rec *r, char *a) | ||
| 85 | { | ||
| 86 | return (r->args) ? apr_pstrcat(r->pool, "?", | ||
| 87 | r->args, NULL) | ||
| 88 | : ""; | ||
| 89 | } | ||
| 90 | |||
| 91 | static const char *extract_status(request_rec *r, char *a) | ||
| 92 | { | ||
| 93 | if (r->status <= 0) { | ||
| 94 | return "-"; | ||
| 95 | } else { | ||
| 96 | return apr_psprintf(r->pool, "%d", r->status); | ||
| 97 | } | ||
| 98 | } | ||
| 99 | |||
| 100 | static const char *extract_virtual_host(request_rec *r, char *a) | ||
| 101 | { | ||
| 102 | return r->server->server_hostname; | ||
| 103 | } | ||
| 104 | |||
| 105 | static const char *extract_server_name(request_rec *r, char *a) | ||
| 106 | { | ||
| 107 | return ap_get_server_name(r); | ||
| 108 | } | ||
| 109 | |||
| 110 | static const char *extract_machine_id(request_rec *r, char *a) | ||
| 111 | { | ||
| 112 | if (!global_config.machid) | ||
| 113 | return "-"; | ||
| 114 | else | ||
| 115 | return global_config.machid; | ||
| 116 | } | ||
| 117 | |||
| 118 | static const char *extract_server_port(request_rec *r, char *a) | ||
| 119 | { | ||
| 120 | return apr_psprintf(r->pool, "%u", | ||
| 121 | r->server->port ? r->server->port : ap_default_port(r)); | ||
| 122 | } | ||
| 123 | |||
| 124 | /* This respects the setting of UseCanonicalName so that | ||
| 125 | * the dynamic mass virtual hosting trick works better. | ||
| 126 | */ | ||
| 127 | static const char *log_server_name(request_rec *r, char *a) __attribute__((unused)); | ||
| 128 | static const char *log_server_name(request_rec *r, char *a) | ||
| 129 | { | ||
| 130 | return ap_get_server_name(r); | ||
| 131 | } | ||
| 132 | |||
| 133 | static const char *extract_child_pid(request_rec *r, char *a) | ||
| 134 | { | ||
| 135 | if (*a == '\0' || !strcmp(a, "pid")) { | ||
| 136 | return apr_psprintf(r->pool, "%" APR_PID_T_FMT, getpid()); | ||
| 137 | } | ||
| 138 | else if (!strcmp(a, "tid")) { | ||
| 139 | #if APR_HAS_THREADS | ||
| 140 | apr_os_thread_t tid = apr_os_thread_current(); | ||
| 141 | #else | ||
| 142 | int tid = 0; /* APR will format "0" anyway but an arg is needed */ | ||
| 143 | #endif | ||
| 144 | return apr_psprintf(r->pool, "%pT", &tid); | ||
| 145 | } | ||
| 146 | /* bogus format */ | ||
| 147 | return a; | ||
| 148 | } | ||
| 149 | |||
| 150 | static const char *extract_referer(request_rec *r, char *a) | ||
| 151 | { | ||
| 152 | const char *tempref; | ||
| 153 | |||
| 154 | tempref = apr_table_get(r->headers_in, "Referer"); | ||
| 155 | if (!tempref) | ||
| 156 | { | ||
| 157 | return "-"; | ||
| 158 | } else { | ||
| 159 | return tempref; | ||
| 160 | } | ||
| 161 | } | ||
| 162 | |||
| 163 | static const char *extract_agent(request_rec *r, char *a) | ||
| 164 | { | ||
| 165 | const char *tempag; | ||
| 166 | |||
| 167 | tempag = apr_table_get(r->headers_in, "User-Agent"); | ||
| 168 | if (!tempag) | ||
| 169 | { | ||
| 170 | return "-"; | ||
| 171 | } else { | ||
| 172 | return tempag; | ||
| 173 | } | ||
| 174 | } | ||
| 175 | |||
| 176 | static const char *extract_specific_cookie(request_rec *r, char *a) | ||
| 177 | { | ||
| 178 | const char *cookiestr; | ||
| 179 | char *cookieend; | ||
| 180 | char *isvalid; | ||
| 181 | char *cookiebuf; | ||
| 182 | |||
| 183 | if (a != NULL) { | ||
| 184 | log_error(APLOG_MARK,APLOG_DEBUG, 0, r->server, | ||
| 185 | "watching for cookie '%s'", a); | ||
| 186 | |||
| 187 | /* Fetch out the cookie header */ | ||
| 188 | cookiestr = (char *)apr_table_get(r->headers_in, "cookie2"); | ||
| 189 | if (cookiestr != NULL) { | ||
| 190 | log_error(APLOG_MARK,APLOG_DEBUG, 0, r->server, | ||
| 191 | "Cookie2: [%s]", cookiestr); | ||
| 192 | /* Does the cookie string contain one with our name? */ | ||
| 193 | isvalid = ap_strstr_c(cookiestr, a); | ||
| 194 | if (isvalid != NULL) { | ||
| 195 | /* Move past the cookie name and equal sign */ | ||
| 196 | isvalid += strlen(a) + 1; | ||
| 197 | /* Duplicate it into the pool */ | ||
| 198 | cookiebuf = apr_pstrdup(r->pool, isvalid); | ||
| 199 | /* Segregate just this cookie out of the string | ||
| 200 | * with a terminating nul at the first semicolon */ | ||
| 201 | cookieend = ap_strchr(cookiebuf, ';'); | ||
| 202 | if (cookieend != NULL) | ||
| 203 | *cookieend = '\0'; | ||
| 204 | return cookiebuf; | ||
| 205 | } | ||
| 206 | } | ||
| 207 | |||
| 208 | cookiestr = (char *)apr_table_get(r->headers_in, "cookie"); | ||
| 209 | if (cookiestr != NULL) { | ||
| 210 | log_error(APLOG_MARK,APLOG_DEBUG, 0, r->server, | ||
| 211 | "Cookie: [%s]", cookiestr); | ||
| 212 | isvalid = ap_strstr_c(cookiestr, a); | ||
| 213 | if (isvalid != NULL) { | ||
| 214 | isvalid += strlen(a) + 1; | ||
| 215 | cookiebuf = apr_pstrdup(r->pool, isvalid); | ||
| 216 | cookieend = ap_strchr(cookiebuf, ';'); | ||
| 217 | if (cookieend != NULL) | ||
| 218 | *cookieend = '\0'; | ||
| 219 | return cookiebuf; | ||
| 220 | } | ||
| 221 | } | ||
| 222 | |||
| 223 | cookiestr = apr_table_get(r->headers_out, "set-cookie"); | ||
| 224 | if (cookiestr != NULL) { | ||
| 225 | log_error(APLOG_MARK,APLOG_DEBUG, 0, r->server, | ||
| 226 | "Set-Cookie: [%s]", cookiestr); | ||
| 227 | isvalid = ap_strstr_c(cookiestr, a); | ||
| 228 | if (isvalid != NULL) { | ||
| 229 | isvalid += strlen(a) + 1; | ||
| 230 | cookiebuf = apr_pstrdup(r->pool, isvalid); | ||
| 231 | cookieend = ap_strchr(cookiebuf, ';'); | ||
| 232 | if (cookieend != NULL) | ||
| 233 | *cookieend = '\0'; | ||
| 234 | return cookiebuf; | ||
| 235 | } | ||
| 236 | } | ||
| 237 | } | ||
| 238 | |||
| 239 | return "-"; | ||
| 240 | } | ||
| 241 | |||
| 242 | /*static const char *extract_cookie(request_rec *r, char *a) | ||
| 243 | { | ||
| 244 | logsql_state *cls = ap_get_module_config(r->server->module_config, | ||
| 245 | &log_sql_module); | ||
| 246 | |||
| 247 | return extract_specific_cookie(r, (char *)cls->cookie_name); | ||
| 248 | }*/ | ||
| 249 | |||
| 250 | static const char *extract_unique_id(request_rec *r, char *a) | ||
| 251 | { | ||
| 252 | const char *tempid; | ||
| 253 | |||
| 254 | tempid = apr_table_get(r->subprocess_env, "UNIQUE_ID"); | ||
| 255 | if (!tempid) | ||
| 256 | return "-"; | ||
| 257 | else | ||
| 258 | return tempid; | ||
| 259 | } | ||
| 260 | |||
| 261 | /* End declarations of various extract_ functions */ | ||
diff --git a/src/functions13.h b/src/functions13.h new file mode 100644 index 0000000..54e3138 --- /dev/null +++ b/src/functions13.h | |||
| @@ -0,0 +1,58 @@ | |||
| 1 | /* $Id$ */ | ||
| 2 | |||
| 3 | static const char *extract_bytes_sent(request_rec *r, char *a) | ||
| 4 | { | ||
| 5 | if (!r->sent_bodyct) { | ||
| 6 | return "-"; | ||
| 7 | } | ||
| 8 | else { | ||
| 9 | long int bs; | ||
| 10 | ap_bgetopt(r->connection->client, BO_BYTECT, &bs); | ||
| 11 | return ap_psprintf(r->pool, "%ld", bs); | ||
| 12 | } | ||
| 13 | } | ||
| 14 | |||
| 15 | static const char *extract_request_time(request_rec *r, char *a) | ||
| 16 | { | ||
| 17 | int timz; | ||
| 18 | struct tm *t; | ||
| 19 | char tstr[MAX_STRING_LEN]; | ||
| 20 | |||
| 21 | t = ap_get_gmtoff(&timz); | ||
| 22 | |||
| 23 | if (a && *a) { /* Custom format */ | ||
| 24 | strftime(tstr, MAX_STRING_LEN, a, t); | ||
| 25 | } else { /* CLF format */ | ||
| 26 | char sign = (timz < 0 ? '-' : '+'); | ||
| 27 | |||
| 28 | if (timz < 0) { | ||
| 29 | timz = -timz; | ||
| 30 | } | ||
| 31 | strftime(tstr, MAX_STRING_LEN, "[%d/%b/%Y:%H:%M:%S ", t); | ||
| 32 | ap_snprintf(tstr + strlen(tstr), sizeof(tstr) - strlen(tstr), "%c%.2d%.2d]", sign, timz / 60, timz % 60); | ||
| 33 | } | ||
| 34 | |||
| 35 | return ap_pstrdup(r->pool, tstr); | ||
| 36 | } | ||
| 37 | |||
| 38 | static const char *extract_request_duration(request_rec *r, char *a) | ||
| 39 | { | ||
| 40 | return ap_psprintf(r->pool, "%ld", time(NULL) - r->request_time); | ||
| 41 | } | ||
| 42 | |||
| 43 | static const char *extract_request_timestamp(request_rec *r, char *a) | ||
| 44 | { | ||
| 45 | return ap_psprintf(r->pool, "%ld", (long) time(NULL)); | ||
| 46 | } | ||
| 47 | static const char *extract_connection_status(request_rec *r, char *a) __attribute__((unused)); | ||
| 48 | static const char *extract_connection_status(request_rec *r, char *a) | ||
| 49 | { | ||
| 50 | if (r->connection->aborted) | ||
| 51 | return "X"; | ||
| 52 | |||
| 53 | if ((r->connection->keepalive) && | ||
| 54 | ((r->server->keep_alive_max - r->connection->keepalives) > 0)) { | ||
| 55 | return "+"; | ||
| 56 | } | ||
| 57 | return "-"; | ||
| 58 | } | ||
diff --git a/src/functions20.h b/src/functions20.h new file mode 100644 index 0000000..5220b7c --- /dev/null +++ b/src/functions20.h | |||
| @@ -0,0 +1,116 @@ | |||
| 1 | /* $Id$ */ | ||
| 2 | |||
| 3 | static const char *extract_bytes_sent(request_rec *r, char *a) | ||
| 4 | { | ||
| 5 | if (!r->sent_bodyct || !r->bytes_sent) { | ||
| 6 | return "-"; | ||
| 7 | } else { | ||
| 8 | return apr_psprintf(r->pool, "%" APR_OFF_T_FMT, r->bytes_sent); | ||
| 9 | } | ||
| 10 | } | ||
| 11 | |||
| 12 | static const char *extract_request_time_custom(request_rec *r, char *a, | ||
| 13 | apr_time_exp_t *xt) | ||
| 14 | { | ||
| 15 | apr_size_t retcode; | ||
| 16 | char tstr[MAX_STRING_LEN]; | ||
| 17 | apr_strftime(tstr, &retcode, sizeof(tstr), a, xt); | ||
| 18 | return apr_pstrdup(r->pool, tstr); | ||
| 19 | } | ||
| 20 | |||
| 21 | #define DEFAULT_REQUEST_TIME_SIZE 32 | ||
| 22 | typedef struct { | ||
| 23 | unsigned t; | ||
| 24 | char timestr[DEFAULT_REQUEST_TIME_SIZE]; | ||
| 25 | unsigned t_validate; | ||
| 26 | } cached_request_time; | ||
| 27 | |||
| 28 | #define TIME_CACHE_SIZE 4 | ||
| 29 | #define TIME_CACHE_MASK 3 | ||
| 30 | static cached_request_time request_time_cache[TIME_CACHE_SIZE]; | ||
| 31 | |||
| 32 | static const char *extract_request_time(request_rec *r, char *a) | ||
| 33 | { | ||
| 34 | apr_time_exp_t xt; | ||
| 35 | |||
| 36 | /* Please read comments in mod_log_config.h for more info about | ||
| 37 | * the I_INSIST....COMPLIANCE define | ||
| 38 | */ | ||
| 39 | if (a && *a) { /* Custom format */ | ||
| 40 | #ifdef I_INSIST_ON_EXTRA_CYCLES_FOR_CLF_COMPLIANCE | ||
| 41 | ap_explode_recent_localtime(&xt, apr_time_now()); | ||
| 42 | #else | ||
| 43 | ap_explode_recent_localtime(&xt, r->request_time); | ||
| 44 | #endif | ||
| 45 | return extract_request_time_custom(r, a, &xt); | ||
| 46 | } else { /* CLF format */ | ||
| 47 | /* This code uses the same technique as ap_explode_recent_localtime(): | ||
| 48 | * optimistic caching with logic to detect and correct race conditions. | ||
| 49 | * See the comments in server/util_time.c for more information. | ||
| 50 | */ | ||
| 51 | cached_request_time* cached_time = apr_palloc(r->pool, | ||
| 52 | sizeof(*cached_time)); | ||
| 53 | #ifdef I_INSIST_ON_EXTRA_CYCLES_FOR_CLF_COMPLIANCE | ||
| 54 | apr_time_t request_time = apr_time_now(); | ||
| 55 | #else | ||
| 56 | apr_time_t request_time = r->request_time; | ||
| 57 | #endif | ||
| 58 | unsigned t_seconds = (unsigned)apr_time_sec(request_time); | ||
| 59 | unsigned i = t_seconds & TIME_CACHE_MASK; | ||
| 60 | memcpy(cached_time, &(request_time_cache[i]), sizeof(*cached_time)); | ||
| 61 | if ((t_seconds != cached_time->t) || | ||
| 62 | (t_seconds != cached_time->t_validate)) { | ||
| 63 | |||
| 64 | /* Invalid or old snapshot, so compute the proper time string | ||
| 65 | * and store it in the cache | ||
| 66 | */ | ||
| 67 | char sign; | ||
| 68 | int timz; | ||
| 69 | |||
| 70 | ap_explode_recent_localtime(&xt, r->request_time); | ||
| 71 | timz = xt.tm_gmtoff; | ||
| 72 | if (timz < 0) { | ||
| 73 | timz = -timz; | ||
| 74 | sign = '-'; | ||
| 75 | } | ||
| 76 | else { | ||
| 77 | sign = '+'; | ||
| 78 | } | ||
| 79 | cached_time->t = t_seconds; | ||
| 80 | apr_snprintf(cached_time->timestr, DEFAULT_REQUEST_TIME_SIZE, | ||
| 81 | "[%02d/%s/%d:%02d:%02d:%02d %c%.2d%.2d]", | ||
| 82 | xt.tm_mday, apr_month_snames[xt.tm_mon], | ||
| 83 | xt.tm_year+1900, xt.tm_hour, xt.tm_min, xt.tm_sec, | ||
| 84 | sign, timz / (60*60), timz % (60*60)); | ||
| 85 | cached_time->t_validate = t_seconds; | ||
| 86 | memcpy(&(request_time_cache[i]), cached_time, | ||
| 87 | sizeof(*cached_time)); | ||
| 88 | } | ||
| 89 | return cached_time->timestr; | ||
| 90 | } | ||
| 91 | } | ||
| 92 | |||
| 93 | static const char *extract_request_duration(request_rec *r, char *a) | ||
| 94 | { | ||
| 95 | apr_time_t duration = apr_time_now() - r->request_time; | ||
| 96 | return apr_psprintf(r->pool, "%" APR_TIME_T_FMT, apr_time_sec(duration)); | ||
| 97 | } | ||
| 98 | |||
| 99 | static const char *extract_request_timestamp(request_rec *r, char *a) | ||
| 100 | { | ||
| 101 | return apr_psprintf(r->pool, "%"APR_TIME_T_FMT, apr_time_sec(apr_time_now())); | ||
| 102 | } | ||
| 103 | |||
| 104 | static const char *extract_connection_status(request_rec *r, char *a) __attribute__((unused)); | ||
| 105 | static const char *extract_connection_status(request_rec *r, char *a) | ||
| 106 | { | ||
| 107 | if (r->connection->aborted) | ||
| 108 | return "X"; | ||
| 109 | |||
| 110 | if (r->connection->keepalive == AP_CONN_KEEPALIVE && | ||
| 111 | (!r->server->keep_alive_max || | ||
| 112 | (r->server->keep_alive_max - r->connection->keepalives) > 0)) { | ||
| 113 | return "+"; | ||
| 114 | } | ||
| 115 | return "-"; | ||
| 116 | } | ||
diff --git a/src/mod_log_sql.c b/src/mod_log_sql.c new file mode 100644 index 0000000..be4e1f9 --- /dev/null +++ b/src/mod_log_sql.c | |||
| @@ -0,0 +1,1578 @@ | |||
| 1 | /* $Id:mod_log_sql.c 180 2008-09-21 15:54:12Z urkle@drip.ws $ */ | ||
| 2 | |||
| 3 | #if defined(WITH_APACHE20) | ||
| 4 | # include "apache20.h" | ||
| 5 | #elif defined(WITH_APACHE13) | ||
| 6 | # include "apache13.h" | ||
| 7 | #else | ||
| 8 | # error Unsupported Apache version | ||
| 9 | #endif | ||
| 10 | |||
| 11 | #ifdef HAVE_CONFIG_H | ||
| 12 | /* Undefine these to prevent conflicts between Apache ap_config_auto.h and | ||
| 13 | * my config.h. Only really needed for Apache < 2.0.48, but it can't hurt. | ||
| 14 | */ | ||
| 15 | #undef PACKAGE_BUGREPORT | ||
| 16 | #undef PACKAGE_NAME | ||
| 17 | #undef PACKAGE_STRING | ||
| 18 | #undef PACKAGE_TARNAME | ||
| 19 | #undef PACKAGE_VERSION | ||
| 20 | |||
| 21 | #include "autoconfig.h" | ||
| 22 | #endif | ||
| 23 | |||
| 24 | #if APR_HAVE_UNISTD_H | ||
| 25 | #include <unistd.h> | ||
| 26 | #endif | ||
| 27 | #ifdef HAVE_LIMITS_H | ||
| 28 | #include <limits.h> | ||
| 29 | #endif | ||
| 30 | |||
| 31 | #include "mod_log_sql.h" | ||
| 32 | |||
| 33 | /* Configuratino Defaults */ | ||
| 34 | #define DEFAULT_TRANSFER_LOG_FMT "AbHhmRSsTUuv" | ||
| 35 | #define DEFAULT_NOTES_TABLE_NAME "notes" | ||
| 36 | #define DEFAULT_HIN_TABLE_NAME "headers_in" | ||
| 37 | #define DEFAULT_HOUT_TABLE_NAME "headers_out" | ||
| 38 | #define DEFAULT_COOKIE_TABLE_NAME "cookies" | ||
| 39 | #define DEFAULT_PRESERVE_FILE "logs/mod_log_sql-preserve" | ||
| 40 | |||
| 41 | /* -------------* | ||
| 42 | * DECLARATIONS * | ||
| 43 | * -------------*/ | ||
| 44 | |||
| 45 | /* Declare ourselves so the configuration routines can find and know us. */ | ||
| 46 | module AP_MODULE_DECLARE_DATA log_sql_module; | ||
| 47 | |||
| 48 | /* The contents of these are known 'Apache wide' and are not variable | ||
| 49 | * on a per-virtual-server basis. Every virtual server 'knows' the | ||
| 50 | * same versions of these variables. | ||
| 51 | */ | ||
| 52 | |||
| 53 | typedef struct { | ||
| 54 | int massvirtual; | ||
| 55 | int createtables; | ||
| 56 | int forcepreserve; | ||
| 57 | int disablepreserve; | ||
| 58 | char *machid; | ||
| 59 | int announce; | ||
| 60 | logsql_dbconnection db; | ||
| 61 | logsql_dbdriver *driver; | ||
| 62 | /** Show config support */ | ||
| 63 | char *showconfig; | ||
| 64 | apr_file_t *showconfig_fp; | ||
| 65 | } global_config_t; | ||
| 66 | |||
| 67 | static global_config_t global_config; | ||
| 68 | |||
| 69 | /* structure to hold helper function info */ | ||
| 70 | typedef struct { | ||
| 71 | const char *alias; /* The function alias */ | ||
| 72 | logsql_item_func *func; /* The extraction function pointer */ | ||
| 73 | int want_orig_req; /* if it requires the original request prior to internal redirection */ | ||
| 74 | } logsql_function; | ||
| 75 | |||
| 76 | /* list of logsql_functions's for log types */ | ||
| 77 | static apr_array_header_t *logsql_function_list; | ||
| 78 | |||
| 79 | /* structure to hold sqlfield mappings */ | ||
| 80 | typedef struct { | ||
| 81 | const char *alias; /* long name for item */ | ||
| 82 | const char *funcalias; /* The function alias */ | ||
| 83 | logsql_function *func; /* its extraction function */ | ||
| 84 | char *param; /* Parameter for function */ | ||
| 85 | const char *sql_field_name; /* its column in SQL */ | ||
| 86 | char string_contents; /* Whether this is a string field or not */ | ||
| 87 | logsql_field_datatype datatype; /* the field data type */ | ||
| 88 | apr_size_t size; /* The size of the data type */ | ||
| 89 | } logsql_field; | ||
| 90 | |||
| 91 | /* list of logsql_item's for log types */ | ||
| 92 | static apr_array_header_t *logsql_field_list; | ||
| 93 | |||
| 94 | /* But the contents of this structure will vary by virtual server. | ||
| 95 | * This permits each virtual server to vary its configuration slightly | ||
| 96 | * for per-server customization. | ||
| 97 | * | ||
| 98 | * Each child process has its own segregated copy of this structure. | ||
| 99 | */ | ||
| 100 | typedef struct { | ||
| 101 | apr_array_header_t *transfer_ignore_list; | ||
| 102 | apr_array_header_t *transfer_accept_list; | ||
| 103 | apr_array_header_t *remhost_ignore_list; | ||
| 104 | apr_array_header_t *notes_list; | ||
| 105 | apr_array_header_t *hout_list; | ||
| 106 | apr_array_header_t *hin_list; | ||
| 107 | apr_array_header_t *cookie_list; | ||
| 108 | const char *notes_table_name; | ||
| 109 | const char *hout_table_name; | ||
| 110 | const char *hin_table_name; | ||
| 111 | const char *cookie_table_name; | ||
| 112 | const char *transfer_table_name; | ||
| 113 | apr_array_header_t *transfer_log_format; | ||
| 114 | apr_pool_t *parsed_pool; | ||
| 115 | logsql_field **parsed_log_format; | ||
| 116 | const char *preserve_file; | ||
| 117 | const char *cookie_name; | ||
| 118 | } logsql_state; | ||
| 119 | |||
| 120 | /** Registration function for extract functions | ||
| 121 | * | ||
| 122 | * This functions registers an alias for a function | ||
| 123 | * | ||
| 124 | * @note This is exported from the module | ||
| 125 | */ | ||
| 126 | LOGSQL_DECLARE(void) log_sql_register_function(apr_pool_t *p, | ||
| 127 | const char *alias, logsql_item_func *func, | ||
| 128 | logsql_function_req want_orig_req) | ||
| 129 | { | ||
| 130 | logsql_function *item; | ||
| 131 | if (!logsql_function_list) | ||
| 132 | logsql_function_list = apr_array_make(p,10, sizeof(logsql_function)); | ||
| 133 | |||
| 134 | item = apr_array_push(logsql_function_list); | ||
| 135 | item->alias = alias; | ||
| 136 | item->func = func; | ||
| 137 | item->want_orig_req = want_orig_req; | ||
| 138 | if (global_config.showconfig_fp) { | ||
| 139 | apr_file_printf(global_config.showconfig_fp," Function : %s\n",alias); | ||
| 140 | } | ||
| 141 | } | ||
| 142 | /** Register a old style sql mapping to the new style | ||
| 143 | * | ||
| 144 | * @note This is exported from the module | ||
| 145 | */ | ||
| 146 | LOGSQL_DECLARE(void) log_sql_register_alias(server_rec *s, apr_pool_t *p, | ||
| 147 | char key, const char *alias) | ||
| 148 | { | ||
| 149 | server_rec *ts; | ||
| 150 | for (ts = s; ts; ts = ts->next) { | ||
| 151 | logsql_state *cfg = ap_get_module_config(ts->module_config, | ||
| 152 | &log_sql_module); | ||
| 153 | int itr; | ||
| 154 | for (itr = 0; itr < cfg->transfer_log_format->nelts; itr++) { | ||
| 155 | const char *logformat = ((const char **)cfg->transfer_log_format->elts)[itr]; | ||
| 156 | //log_error(APLOG_MARK, APLOG_DEBUG, 0, s, "Testing Logformat %s against %c for %s",logformat,key,alias); | ||
| 157 | // Check if it is only one character AND it is our key | ||
| 158 | if (logformat[1]=='\0' && logformat[0]==key) { | ||
| 159 | ((const char **)cfg->transfer_log_format->elts)[itr] = alias; | ||
| 160 | } | ||
| 161 | } | ||
| 162 | } | ||
| 163 | } | ||
| 164 | |||
| 165 | |||
| 166 | /** Registration sqlfield aliases to functions | ||
| 167 | * | ||
| 168 | * And update parse cache for transfer_log_format | ||
| 169 | * | ||
| 170 | * @note This is exported from the module | ||
| 171 | */ | ||
| 172 | LOGSQL_DECLARE(void) log_sql_register_field(apr_pool_t *p, | ||
| 173 | const char *alias, | ||
| 174 | const char *funcalias, const char *param, | ||
| 175 | const char *sql_field_name, | ||
| 176 | logsql_field_datatype datatype, apr_size_t size) | ||
| 177 | { | ||
| 178 | logsql_field *item; | ||
| 179 | |||
| 180 | if (!logsql_field_list) | ||
| 181 | logsql_field_list = apr_array_make(p,10, sizeof(logsql_field)); | ||
| 182 | |||
| 183 | item = apr_array_push(logsql_field_list); | ||
| 184 | item->func = NULL; | ||
| 185 | item->alias = apr_pstrdup(p, alias); | ||
| 186 | item->funcalias = apr_pstrdup(p, funcalias); | ||
| 187 | item->param = apr_pstrdup(p, param); | ||
| 188 | item->sql_field_name = apr_pstrdup(p,sql_field_name); | ||
| 189 | item->datatype = datatype; | ||
| 190 | item->string_contents = 0; | ||
| 191 | if (datatype == LOGSQL_DATATYPE_CHAR || datatype == LOGSQL_DATATYPE_VARCHAR) { | ||
| 192 | item->string_contents = 1; | ||
| 193 | } | ||
| 194 | item->size = size; | ||
| 195 | } | ||
| 196 | |||
| 197 | /** | ||
| 198 | * Links sql field items with their functions | ||
| 199 | */ | ||
| 200 | LOGSQL_DECLARE(void) log_sql_register_finish(server_rec *s) | ||
| 201 | { | ||
| 202 | server_rec *ts; | ||
| 203 | int itr, f; | ||
| 204 | logsql_field *item; | ||
| 205 | logsql_function *func; | ||
| 206 | for (itr = 0; itr < logsql_field_list->nelts; itr++) { | ||
| 207 | item = &((logsql_field *)logsql_field_list->elts)[itr]; | ||
| 208 | if (item->func) continue; | ||
| 209 | /* Find function alias in function list */ | ||
| 210 | for (f = 0; f < logsql_function_list->nelts; f++) { | ||
| 211 | func = &((logsql_function *)logsql_function_list->elts)[f]; | ||
| 212 | if (strcmp(func->alias,item->funcalias)==0) { | ||
| 213 | item->func = func; | ||
| 214 | if (global_config.showconfig_fp) { | ||
| 215 | apr_file_printf(global_config.showconfig_fp," Item : %s using function %s(%s)\n" | ||
| 216 | "\tStoring in field %s of type %s(%"APR_SIZE_T_FMT")\n", | ||
| 217 | item->alias, item->funcalias, item->param, | ||
| 218 | item->sql_field_name, item->string_contents ? "TEXT":"NUMERIC", item->size); | ||
| 219 | } | ||
| 220 | break; | ||
| 221 | } | ||
| 222 | } | ||
| 223 | if (!item->func) { | ||
| 224 | log_error(APLOG_MARK, APLOG_DEBUG, 0, s, | ||
| 225 | "Could not find function %s for item %s",item->funcalias, item->alias); | ||
| 226 | } | ||
| 227 | } | ||
| 228 | /* some voodoo here to post parse logitems in all servers * | ||
| 229 | * so a "cached" list is used in the main logging loop for speed */ | ||
| 230 | for (ts = s; ts; ts = ts->next) { | ||
| 231 | logsql_state *cfg = ap_get_module_config(ts->module_config, | ||
| 232 | &log_sql_module); | ||
| 233 | |||
| 234 | if (!cfg->parsed_log_format) { | ||
| 235 | cfg->parsed_log_format = apr_pcalloc(cfg->parsed_pool, | ||
| 236 | cfg->transfer_log_format->nelts * sizeof(logsql_field *)); | ||
| 237 | } | ||
| 238 | |||
| 239 | for (itr = 0; itr < cfg->transfer_log_format->nelts; itr++) { | ||
| 240 | const char *logformat = ((char **)cfg->transfer_log_format->elts)[itr]; | ||
| 241 | for (f = 0; f < logsql_field_list->nelts; f++) { | ||
| 242 | item = &((logsql_field *)logsql_field_list->elts)[f]; | ||
| 243 | if (item->func && strcmp(logformat,item->alias)==0) { | ||
| 244 | cfg->parsed_log_format[itr] = item; | ||
| 245 | break; | ||
| 246 | } | ||
| 247 | } | ||
| 248 | } | ||
| 249 | } | ||
| 250 | } | ||
| 251 | |||
| 252 | /* Registration function for database drivers */ | ||
| 253 | LOGSQL_DECLARE(void) log_sql_register_driver(apr_pool_t *p, | ||
| 254 | logsql_dbdriver *driver) | ||
| 255 | { | ||
| 256 | global_config.driver = driver; | ||
| 257 | } | ||
| 258 | |||
| 259 | /* Include all the core extract functions */ | ||
| 260 | #include "functions.h" | ||
| 261 | #if defined(WITH_APACHE13) | ||
| 262 | # include "functions13.h" | ||
| 263 | #elif defined(WITH_APACHE20) | ||
| 264 | # include "functions20.h" | ||
| 265 | #endif | ||
| 266 | |||
| 267 | static logsql_opendb_ret log_sql_opendb_link(server_rec* s) | ||
| 268 | { | ||
| 269 | logsql_opendb_ret result; | ||
| 270 | if (global_config.driver == NULL) { | ||
| 271 | return LOGSQL_OPENDB_FAIL; | ||
| 272 | } | ||
| 273 | if (global_config.forcepreserve) { | ||
| 274 | /*global_config.db.connected = 1;*/ | ||
| 275 | return LOGSQL_OPENDB_PRESERVE; | ||
| 276 | } | ||
| 277 | if (global_config.db.connected) { | ||
| 278 | return LOGSQL_OPENDB_ALREADY; | ||
| 279 | } | ||
| 280 | /* database | ||
| 281 | host | ||
| 282 | user | ||
| 283 | passwd | ||
| 284 | */ | ||
| 285 | if (global_config.db.parms) { | ||
| 286 | result = global_config.driver->connect(s, &global_config.db); | ||
| 287 | if (result==LOGSQL_OPENDB_FAIL) { | ||
| 288 | global_config.db.connected = 0; | ||
| 289 | } else { | ||
| 290 | global_config.db.connected = 1; | ||
| 291 | } | ||
| 292 | return result; | ||
| 293 | } else { | ||
| 294 | log_error(APLOG_MARK, APLOG_ERR, 0, s, | ||
| 295 | "mod_log_sql: insufficient configuration info to establish database link"); | ||
| 296 | return LOGSQL_OPENDB_FAIL; | ||
| 297 | } | ||
| 298 | } | ||
| 299 | |||
| 300 | static void preserve_entry(request_rec *r, const char *query) | ||
| 301 | { | ||
| 302 | logsql_state *cls = ap_get_module_config(r->server->module_config, | ||
| 303 | &log_sql_module); | ||
| 304 | apr_status_t result; | ||
| 305 | apr_file_t *fp; | ||
| 306 | |||
| 307 | /* If preserve file is disabled bail out */ | ||
| 308 | if (global_config.disablepreserve) | ||
| 309 | return; | ||
| 310 | #if defined(WITH_APACHE20) | ||
| 311 | result = apr_file_open(&fp, cls->preserve_file,APR_APPEND | APR_WRITE | APR_CREATE, APR_OS_DEFAULT, r->pool); | ||
| 312 | #elif defined(WITH_APACHE13) | ||
| 313 | fp = ap_pfopen(r->pool, cls->preserve_file, "a"); | ||
| 314 | result = (fp)?0:errno; | ||
| 315 | #endif | ||
| 316 | if (result != APR_SUCCESS) { | ||
| 317 | log_error(APLOG_MARK, APLOG_ERR, result, r->server, | ||
| 318 | "attempted append of local preserve file '%s' but failed.",cls->preserve_file); | ||
| 319 | } else { | ||
| 320 | apr_file_printf(fp,"%s;\n", query); | ||
| 321 | #if defined(WITH_APACHE20) | ||
| 322 | apr_file_close(fp); | ||
| 323 | #elif defined(WITH_APACHE13) | ||
| 324 | ap_pfclose(r->pool, fp); | ||
| 325 | #endif | ||
| 326 | log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, | ||
| 327 | "mod_log_sql: entry preserved in %s", cls->preserve_file); | ||
| 328 | } | ||
| 329 | } | ||
| 330 | |||
| 331 | |||
| 332 | /* ------------------------------------------------* | ||
| 333 | * Command handlers that are called according * | ||
| 334 | * to the directives found at Apache runtime. * | ||
| 335 | * ------------------------------------------------*/ | ||
| 336 | |||
| 337 | |||
| 338 | static const char *set_global_flag_slot(cmd_parms *cmd, | ||
| 339 | void *struct_ptr, | ||
| 340 | int flag) | ||
| 341 | { | ||
| 342 | void *ptr = &global_config; | ||
| 343 | int offset = (int)(long)cmd->info; | ||
| 344 | |||
| 345 | *(int *)((char *)ptr + offset) = flag ? 1 : 0; | ||
| 346 | |||
| 347 | return NULL; | ||
| 348 | } | ||
| 349 | |||
| 350 | static const char *set_global_nmv_flag_slot(cmd_parms *cmd, | ||
| 351 | void *struct_ptr, | ||
| 352 | int flag) | ||
| 353 | { | ||
| 354 | if (global_config.massvirtual) { | ||
| 355 | return apr_psprintf(cmd->pool, | ||
| 356 | "mod_log_sql: do not set %s when LogSQLMassVirtualHosting(%d) is On.%d:%d", | ||
| 357 | cmd->cmd->name, global_config.massvirtual, | ||
| 358 | (int)(long)&global_config, (int)(long)struct_ptr); | ||
| 359 | } else { | ||
| 360 | return set_global_flag_slot(cmd,struct_ptr,flag); | ||
| 361 | } | ||
| 362 | } | ||
| 363 | |||
| 364 | static const char *set_global_string_slot(cmd_parms *cmd, | ||
| 365 | void *struct_ptr, | ||
| 366 | const char *arg) | ||
| 367 | { | ||
| 368 | void *ptr = &global_config; | ||
| 369 | int offset = (int)(long)cmd->info; | ||
| 370 | |||
| 371 | *(const char **)((char *)ptr + offset) = apr_pstrdup(cmd->pool,arg); | ||
| 372 | return NULL; | ||
| 373 | } | ||
| 374 | |||
| 375 | static const char *set_server_string_slot(cmd_parms *cmd, | ||
| 376 | void *struct_ptr, | ||
| 377 | const char *arg) | ||
| 378 | { | ||
| 379 | void *ptr = ap_get_module_config(cmd->server->module_config, | ||
| 380 | &log_sql_module); | ||
| 381 | int offset = (int)(long)cmd->info; | ||
| 382 | |||
| 383 | *(const char **)((char *)ptr + offset) = arg; | ||
| 384 | |||
| 385 | return NULL; | ||
| 386 | } | ||
| 387 | |||
| 388 | static const char *set_server_file_slot(cmd_parms *cmd, | ||
| 389 | void *struct_ptr, | ||
| 390 | const char *arg) | ||
| 391 | { | ||
| 392 | void *ptr = ap_get_module_config(cmd->server->module_config, | ||
| 393 | &log_sql_module); | ||
| 394 | int offset = (int)(long)cmd->info; | ||
| 395 | const char *path; | ||
| 396 | |||
| 397 | path = ap_server_root_relative(cmd->pool, (char *)arg); | ||
| 398 | |||
| 399 | if (!path) { | ||
| 400 | return apr_pstrcat(cmd->pool, "Invalid file path ", | ||
| 401 | arg, NULL); | ||
| 402 | } | ||
| 403 | |||
| 404 | *(const char **)((char*)ptr + offset) = path; | ||
| 405 | |||
| 406 | return NULL; | ||
| 407 | } | ||
| 408 | |||
| 409 | static apr_array_header_t *create_logformat_default(apr_pool_t *p) | ||
| 410 | { | ||
| 411 | apr_array_header_t *logformat; | ||
| 412 | char **addme; | ||
| 413 | |||
| 414 | logformat = apr_array_make(p, 12, sizeof(char *)); | ||
| 415 | addme = apr_array_push(logformat); *addme = "useragent"; | ||
| 416 | addme = apr_array_push(logformat); *addme = "bytes_sent"; | ||
| 417 | addme = apr_array_push(logformat); *addme = "request_protocol"; | ||
| 418 | addme = apr_array_push(logformat); *addme = "remote_host"; | ||
| 419 | addme = apr_array_push(logformat); *addme = "request_method"; | ||
| 420 | addme = apr_array_push(logformat); *addme = "referer"; | ||
| 421 | addme = apr_array_push(logformat); *addme = "timestamp"; | ||
| 422 | addme = apr_array_push(logformat); *addme = "status"; | ||
| 423 | addme = apr_array_push(logformat); *addme = "request_duration"; | ||
| 424 | addme = apr_array_push(logformat); *addme = "request_uri"; | ||
| 425 | addme = apr_array_push(logformat); *addme = "remote_user"; | ||
| 426 | addme = apr_array_push(logformat); *addme = "virtual_host"; | ||
| 427 | return logformat; | ||
| 428 | } | ||
| 429 | |||
| 430 | static const char *set_server_nmv_string_slot(cmd_parms *parms, | ||
| 431 | void *struct_ptr, | ||
| 432 | const char *arg) | ||
| 433 | { | ||
| 434 | if (global_config.massvirtual) | ||
| 435 | return apr_psprintf(parms->pool, | ||
| 436 | "mod_log_sql: do not set %s when LogSQLMassVirtualHosting is On.", | ||
| 437 | parms->cmd->name); | ||
| 438 | else | ||
| 439 | return set_server_string_slot(parms,struct_ptr,arg); | ||
| 440 | } | ||
| 441 | |||
| 442 | /* Set a DB connection parameter */ | ||
| 443 | static const char *set_dbparam(cmd_parms *cmd, | ||
| 444 | void *struct_ptr, | ||
| 445 | const char *key, | ||
| 446 | const char *val) | ||
| 447 | { | ||
| 448 | if (!global_config.db.parms) { | ||
| 449 | global_config.db.parms = apr_table_make(cmd->pool,5); | ||
| 450 | } | ||
| 451 | apr_table_set(global_config.db.parms,key,val); | ||
| 452 | return NULL; | ||
| 453 | } | ||
| 454 | |||
| 455 | static const char *set_dbparam_slot(cmd_parms *cmd, | ||
| 456 | void *struct_ptr, | ||
| 457 | const char *arg) | ||
| 458 | { | ||
| 459 | const char *param = (char *)cmd->info; | ||
| 460 | set_dbparam(cmd,NULL,param,arg); | ||
| 461 | return NULL; | ||
| 462 | } | ||
| 463 | |||
| 464 | /* Sets basic connection info */ | ||
| 465 | static const char *set_log_sql_info(cmd_parms *cmd, void *dummy, | ||
| 466 | const char *host, const char *user, const char *pwd) | ||
| 467 | { | ||
| 468 | if (!user) { /* user is null, so only one arg passed */ | ||
| 469 | /* TODO: to more error checking/force all params to be set */ | ||
| 470 | apr_uri_t uri; | ||
| 471 | apr_uri_parse(cmd->pool, host, &uri); | ||
| 472 | if (uri.scheme) { | ||
| 473 | set_dbparam(cmd, NULL, "driver", uri.scheme); | ||
| 474 | } | ||
| 475 | if (uri.hostname) { | ||
| 476 | set_dbparam(cmd, NULL, "hostname", uri.hostname); | ||
| 477 | } | ||
| 478 | if (uri.user) { | ||
| 479 | set_dbparam(cmd, NULL, "username", uri.user); | ||
| 480 | } | ||
| 481 | if (uri.password) { | ||
| 482 | set_dbparam(cmd, NULL, "password", uri.password); | ||
| 483 | } | ||
| 484 | if (uri.port_str) { | ||
| 485 | set_dbparam(cmd, NULL, "port", uri.port_str); | ||
| 486 | } | ||
| 487 | if (uri.path) { | ||
| 488 | /* extract Database name */ | ||
| 489 | char *off = ap_strchr(++uri.path,'/'); | ||
| 490 | if (off) | ||
| 491 | *off='\0'; | ||
| 492 | set_dbparam(cmd, NULL, "database", uri.path); | ||
| 493 | |||
| 494 | } | ||
| 495 | } else { | ||
| 496 | if (*host != '.') { | ||
| 497 | set_dbparam(cmd, NULL, "hostname", host); | ||
| 498 | } | ||
| 499 | if (*user != '.') { | ||
| 500 | set_dbparam(cmd, NULL, "username", user); | ||
| 501 | } | ||
| 502 | if (*pwd != '.') { | ||
| 503 | set_dbparam(cmd, NULL, "password", pwd); | ||
| 504 | } | ||
| 505 | } | ||
| 506 | return NULL; | ||
| 507 | } | ||
| 508 | |||
| 509 | static const char *add_server_string_slot(cmd_parms *cmd, | ||
| 510 | void *struct_ptr, | ||
| 511 | const char *arg) | ||
| 512 | { | ||
| 513 | char **addme; | ||
| 514 | void *ptr = ap_get_module_config(cmd->server->module_config, | ||
| 515 | &log_sql_module); | ||
| 516 | int offset = (int)(long)cmd->info; | ||
| 517 | apr_array_header_t *ary = *(apr_array_header_t **)((char *)ptr + offset); | ||
| 518 | addme = apr_array_push(ary); | ||
| 519 | *addme = apr_pstrdup(ary->pool, arg); | ||
| 520 | |||
| 521 | return NULL; | ||
| 522 | } | ||
| 523 | |||
| 524 | static const char *set_logformat_slot(cmd_parms *cmd, | ||
| 525 | void *struct_ptr, | ||
| 526 | const char *arg) | ||
| 527 | { | ||
| 528 | const char *t; | ||
| 529 | char t2[2] = {'\0','\0'}; | ||
| 530 | for (t = arg; *t != '\0'; t++) { | ||
| 531 | t2[0] = *t; | ||
| 532 | add_server_string_slot(cmd, NULL, t2); | ||
| 533 | } | ||
| 534 | return NULL; | ||
| 535 | } | ||
| 536 | |||
| 537 | static const char *set_register_field(cmd_parms *cmd, | ||
| 538 | void *struct_ptr, | ||
| 539 | const char *arg) | ||
| 540 | { | ||
| 541 | char *alias, *funcalias, *param, *field, *datatype_s, *size_s; | ||
| 542 | logsql_field_datatype datatype; | ||
| 543 | apr_size_t size; | ||
| 544 | |||
| 545 | alias = ap_getword_white(cmd->pool, &arg); | ||
| 546 | funcalias = ap_getword_white(cmd->pool, &arg); | ||
| 547 | param = ap_getword_conf(cmd->pool, &arg); | ||
| 548 | field = ap_getword_white(cmd->pool, &arg); | ||
| 549 | datatype_s = ap_getword_white(cmd->pool, &arg); | ||
| 550 | size_s = ap_getword_white(cmd->pool, &arg); | ||
| 551 | |||
| 552 | if (strcasecmp("VARCHAR",datatype_s)==0) { | ||
| 553 | datatype = LOGSQL_DATATYPE_VARCHAR; | ||
| 554 | } else if (strcasecmp("INT",datatype_s)==0) { | ||
| 555 | datatype = LOGSQL_DATATYPE_INT; | ||
| 556 | } else if (strcasecmp("CHAR",datatype_s)==0) { | ||
| 557 | datatype = LOGSQL_DATATYPE_CHAR; | ||
| 558 | } else if (strcasecmp("SMALLINT",datatype_s)==0) { | ||
| 559 | datatype = LOGSQL_DATATYPE_SMALLINT; | ||
| 560 | } else if (strcasecmp("BIGINT",datatype_s)==0) { | ||
| 561 | datatype = LOGSQL_DATATYPE_BIGINT; | ||
| 562 | } else { | ||
| 563 | return apr_psprintf(cmd->pool, "Unknown data type %s",datatype_s); | ||
| 564 | } | ||
| 565 | |||
| 566 | size = atoi(size_s); | ||
| 567 | |||
| 568 | log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server, | ||
| 569 | "%s, %s, %s, %s, %s(%d), %s(%"APR_SIZE_T_FMT")", | ||
| 570 | alias, funcalias, param, field, datatype_s, datatype, size_s, size); | ||
| 571 | |||
| 572 | log_sql_register_field(cmd->pool, alias, funcalias, param, | ||
| 573 | field, datatype, size); | ||
| 574 | |||
| 575 | return NULL; | ||
| 576 | } | ||
| 577 | |||
| 578 | /*------------------------------------------------------------* | ||
| 579 | * Apache-specific hooks into the module code * | ||
| 580 | * that are defined in the array 'mysql_lgog_module' (at EOF) * | ||
| 581 | *------------------------------------------------------------*/ | ||
| 582 | /* Closing mysql link: child_exit(1.3), pool registration(2.0) */ | ||
| 583 | #if defined(WITH_APACHE20) | ||
| 584 | static apr_status_t log_sql_close_link(void *data) | ||
| 585 | { | ||
| 586 | if (global_config.driver) | ||
| 587 | global_config.driver->disconnect(&global_config.db); | ||
| 588 | return APR_SUCCESS; | ||
| 589 | } | ||
| 590 | #elif defined(WITH_APACHE13) | ||
| 591 | static void log_sql_child_exit(server_rec *s, apr_pool_t *p) | ||
| 592 | { | ||
| 593 | if (global_config.driver) | ||
| 594 | global_config.driver->disconnect(&global_config.db); | ||
| 595 | } | ||
| 596 | #endif | ||
| 597 | |||
| 598 | /* Child Init */ | ||
| 599 | #if defined(WITH_APACHE20) | ||
| 600 | static void log_sql_child_init(apr_pool_t *p, server_rec *s) | ||
| 601 | #elif defined(WITH_APACHE13) | ||
| 602 | static void log_sql_child_init(server_rec *s, apr_pool_t *p) | ||
| 603 | #endif | ||
| 604 | { | ||
| 605 | logsql_opendb_ret retval; | ||
| 606 | # if defined(WITH_APACHE20) | ||
| 607 | /* Register cleanup hook to close DDB connection (apache 2 doesn't have child_exit) */ | ||
| 608 | apr_pool_cleanup_register(p, NULL, log_sql_close_link, log_sql_close_link); | ||
| 609 | # endif | ||
| 610 | /* Open a link to the database */ | ||
| 611 | retval = log_sql_opendb_link(s); | ||
| 612 | switch (retval) { | ||
| 613 | case LOGSQL_OPENDB_FAIL: | ||
| 614 | if (global_config.driver==NULL) { | ||
| 615 | log_error(APLOG_MARK, APLOG_ERR, 0, s, | ||
| 616 | "mod_log_sql: Driver module not loaded"); | ||
| 617 | } else { | ||
| 618 | log_error(APLOG_MARK, APLOG_ERR, 0, s, | ||
| 619 | "mod_log_sql: child spawned but unable to open database link"); | ||
| 620 | } | ||
| 621 | break; | ||
| 622 | case LOGSQL_OPENDB_SUCCESS: | ||
| 623 | case LOGSQL_OPENDB_ALREADY: | ||
| 624 | log_error(APLOG_MARK,APLOG_DEBUG,0, s, | ||
| 625 | "mod_log_sql: open_logdb_link successful"); | ||
| 626 | break; | ||
| 627 | case LOGSQL_OPENDB_PRESERVE: | ||
| 628 | log_error(APLOG_MARK,APLOG_DEBUG, 0, s, | ||
| 629 | "mod_log_sql: open_logdb_link said that preservation is forced"); | ||
| 630 | break; | ||
| 631 | } | ||
| 632 | } | ||
| 633 | |||
| 634 | static apr_array_header_t *do_merge_array(apr_array_header_t *parent, apr_array_header_t *child, apr_pool_t *p); | ||
| 635 | |||
| 636 | /* post_config / module_init */ | ||
| 637 | #if defined(WITH_APACHE20) | ||
| 638 | static int log_sql_post_config(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s) | ||
| 639 | #elif defined(WITH_APACHE13) | ||
| 640 | static void log_sql_module_init(server_rec *s, apr_pool_t *p) | ||
| 641 | #endif | ||
| 642 | { | ||
| 643 | server_rec *cur_s; | ||
| 644 | const char *default_p = ap_server_root_relative(p, DEFAULT_PRESERVE_FILE); | ||
| 645 | apr_array_header_t *parent = NULL; | ||
| 646 | |||
| 647 | if (global_config.showconfig != NULL) { | ||
| 648 | const char *tempfile = ap_server_root_relative(p, global_config.showconfig); | ||
| 649 | apr_status_t result; | ||
| 650 | #if defined(WITH_APACHE20) | ||
| 651 | result = apr_file_open(&global_config.showconfig_fp, tempfile,APR_TRUNCATE | APR_WRITE | APR_CREATE, APR_OS_DEFAULT, p); | ||
| 652 | #elif defined(WITH_APACHE13) | ||
| 653 | global_config.showconfig_fp = ap_pfopen(p, tempfile, "w"); | ||
| 654 | result = (fp)?0:errno; | ||
| 655 | #endif | ||
| 656 | if (result != APR_SUCCESS) { | ||
| 657 | log_error(APLOG_MARK, APLOG_ERR, result, s, | ||
| 658 | "attempted open of showconfig file '%s' failed.",tempfile); | ||
| 659 | global_config.showconfig_fp = NULL; | ||
| 660 | } else { | ||
| 661 | #if defined(WITH_APACHE20) | ||
| 662 | char temp_time[APR_RFC822_DATE_LEN]; | ||
| 663 | apr_rfc822_date(temp_time,apr_time_now()); | ||
| 664 | #elif defined(WITH_APACHE13) | ||
| 665 | char *temp_time = ap_get_time()); | ||
| 666 | #endif | ||
| 667 | apr_file_printf(global_config.showconfig_fp,"Mod_log_sql Config dump created on %s\n", temp_time); | ||
| 668 | } | ||
| 669 | } | ||
| 670 | |||
| 671 | for (cur_s = s; cur_s != NULL; cur_s= cur_s->next) { | ||
| 672 | logsql_state *cls = ap_get_module_config(cur_s->module_config, | ||
| 673 | &log_sql_module); | ||
| 674 | /* ap_server_root_relative any default preserve file locations */ | ||
| 675 | if (cls->preserve_file == DEFAULT_PRESERVE_FILE) | ||
| 676 | cls->preserve_file = default_p; | ||
| 677 | |||
| 678 | /* Post-process logformats */ | ||
| 679 | if (!cur_s->is_virtual) { | ||
| 680 | parent = create_logformat_default(p); | ||
| 681 | cls->transfer_log_format = do_merge_array(parent, cls->transfer_log_format, p); | ||
| 682 | parent = cls->transfer_log_format; | ||
| 683 | } else { | ||
| 684 | cls->transfer_log_format = do_merge_array(parent, cls->transfer_log_format, p); | ||
| 685 | } | ||
| 686 | } | ||
| 687 | |||
| 688 | /* TODO: Add local_address, remote_address, connection_status */ | ||
| 689 | /** Register functions */ | ||
| 690 | /** register_function(p, funcname, func_ptr, which request_rec); */ | ||
| 691 | log_sql_register_function(p, "useragent", extract_agent, LOGSQL_FUNCTION_REQ_ORIG); | ||
| 692 | log_sql_register_function(p, "request_args", extract_request_query, LOGSQL_FUNCTION_REQ_ORIG); | ||
| 693 | log_sql_register_function(p, "bytes_sent", extract_bytes_sent, LOGSQL_FUNCTION_REQ_FINAL); | ||
| 694 | log_sql_register_function(p, "cookie", extract_specific_cookie,LOGSQL_FUNCTION_REQ_FINAL); | ||
| 695 | log_sql_register_function(p, "request_file", extract_request_file, LOGSQL_FUNCTION_REQ_FINAL); | ||
| 696 | log_sql_register_function(p, "request_protocol",extract_request_protocol,LOGSQL_FUNCTION_REQ_FINAL); | ||
| 697 | log_sql_register_function(p, "remote_host", extract_remote_host, LOGSQL_FUNCTION_REQ_FINAL); | ||
| 698 | log_sql_register_function(p, "unique_id", extract_unique_id, LOGSQL_FUNCTION_REQ_FINAL); | ||
| 699 | log_sql_register_function(p, "remote_logname", extract_remote_logname, LOGSQL_FUNCTION_REQ_FINAL); | ||
| 700 | log_sql_register_function(p, "request_method", extract_request_method, LOGSQL_FUNCTION_REQ_FINAL); | ||
| 701 | log_sql_register_function(p, "machine_id", extract_machine_id, LOGSQL_FUNCTION_REQ_FINAL); | ||
| 702 | log_sql_register_function(p, "child_pid", extract_child_pid, LOGSQL_FUNCTION_REQ_FINAL); | ||
| 703 | log_sql_register_function(p, "server_port", extract_server_port, LOGSQL_FUNCTION_REQ_FINAL); | ||
| 704 | log_sql_register_function(p, "referrer", extract_referer, LOGSQL_FUNCTION_REQ_ORIG); | ||
| 705 | log_sql_register_function(p, "request_line", extract_request_line, LOGSQL_FUNCTION_REQ_ORIG); | ||
| 706 | log_sql_register_function(p, "timestamp", extract_request_timestamp,LOGSQL_FUNCTION_REQ_FINAL); | ||
| 707 | log_sql_register_function(p, "status", extract_status, LOGSQL_FUNCTION_REQ_ORIG); | ||
| 708 | log_sql_register_function(p, "request_duration",extract_request_duration,LOGSQL_FUNCTION_REQ_ORIG); | ||
| 709 | log_sql_register_function(p, "request_time", extract_request_time, LOGSQL_FUNCTION_REQ_FINAL); | ||
| 710 | log_sql_register_function(p, "remote_user", extract_remote_user, LOGSQL_FUNCTION_REQ_FINAL); | ||
| 711 | log_sql_register_function(p, "request_uri", extract_request_uri, LOGSQL_FUNCTION_REQ_ORIG); | ||
| 712 | log_sql_register_function(p, "virtual_host", extract_virtual_host, LOGSQL_FUNCTION_REQ_FINAL); | ||
| 713 | log_sql_register_function(p, "server_name", extract_server_name, LOGSQL_FUNCTION_REQ_FINAL); | ||
| 714 | |||
| 715 | /** Old style aliases */ | ||
| 716 | /** register_alias(s, shortname, longname) */ | ||
| 717 | log_sql_register_alias(s,p,'A',"useragent"); | ||
| 718 | log_sql_register_alias(s,p,'a',"request_args"); | ||
| 719 | log_sql_register_alias(s,p,'b',"bytes_sent"); | ||
| 720 | log_sql_register_alias(s,p,'c',"cookie"); | ||
| 721 | log_sql_register_alias(s,p,'f',"request_file"); | ||
| 722 | log_sql_register_alias(s,p,'H',"request_protocol"); | ||
| 723 | log_sql_register_alias(s,p,'h',"remote_host"); | ||
| 724 | log_sql_register_alias(s,p,'I',"unique_id"); | ||
| 725 | log_sql_register_alias(s,p,'l',"remote_logname"); | ||
| 726 | log_sql_register_alias(s,p,'m',"request_method"); | ||
| 727 | log_sql_register_alias(s,p,'M',"machine_id"); | ||
| 728 | log_sql_register_alias(s,p,'P',"child_pid"); | ||
| 729 | log_sql_register_alias(s,p,'p',"server_port"); | ||
| 730 | log_sql_register_alias(s,p,'R',"referrer"); | ||
| 731 | log_sql_register_alias(s,p,'r',"request_line"); | ||
| 732 | log_sql_register_alias(s,p,'S',"timestamp"); | ||
| 733 | log_sql_register_alias(s,p,'s',"status"); | ||
| 734 | log_sql_register_alias(s,p,'T',"request_duration"); | ||
| 735 | log_sql_register_alias(s,p,'t',"request_time"); | ||
| 736 | log_sql_register_alias(s,p,'u',"remote_user"); | ||
| 737 | log_sql_register_alias(s,p,'U',"request_uri"); | ||
| 738 | log_sql_register_alias(s,p,'v',"virtual_host"); | ||
| 739 | log_sql_register_alias(s,p,'V',"server_name"); | ||
| 740 | |||
| 741 | /* Register handlers */ | ||
| 742 | /** register_field(s,p, longname, funcalias, arg, | ||
| 743 | * sqlfieldname, DATATYPE, DATA LENGTH); */ | ||
| 744 | log_sql_register_field(p,"useragent", "useragent",NULL, | ||
| 745 | "agent", LOGSQL_DATATYPE_VARCHAR, 0); | ||
| 746 | log_sql_register_field(p,"request_args", "request_args",NULL, | ||
| 747 | "request_args",LOGSQL_DATATYPE_VARCHAR,0); | ||
| 748 | log_sql_register_field(p,"bytes_sent", "bytes_sent",NULL, | ||
| 749 | "bytes_sent",LOGSQL_DATATYPE_INT,0); | ||
| 750 | log_sql_register_field(p,"cookie", "cookie","Apache", | ||
| 751 | "cookie",LOGSQL_DATATYPE_VARCHAR,0); | ||
| 752 | log_sql_register_field(p,"request_file", "request_file",NULL, | ||
| 753 | "request_file",LOGSQL_DATATYPE_VARCHAR,0); | ||
| 754 | log_sql_register_field(p,"request_protocol", "request_protocol",NULL, | ||
| 755 | "request_protocol",LOGSQL_DATATYPE_VARCHAR,0); | ||
| 756 | log_sql_register_field(p,"remote_host", "remote_host",NULL, | ||
| 757 | "remote_host",LOGSQL_DATATYPE_VARCHAR,0); | ||
| 758 | log_sql_register_field(p,"unique_id", "unique_id",NULL, | ||
| 759 | "id",LOGSQL_DATATYPE_VARCHAR,0); | ||
| 760 | log_sql_register_field(p,"remote_logname", "remote_logname",NULL, | ||
| 761 | "remote_logname",LOGSQL_DATATYPE_VARCHAR,0); | ||
| 762 | log_sql_register_field(p,"request_method", "request_method",NULL, | ||
| 763 | "request_method",LOGSQL_DATATYPE_VARCHAR,0); | ||
| 764 | log_sql_register_field(p,"machine_id", "machine_id",NULL, | ||
| 765 | "machine_id",LOGSQL_DATATYPE_VARCHAR,0); | ||
| 766 | log_sql_register_field(p,"child_pid", "child_pid",NULL, | ||
| 767 | "child_pid",LOGSQL_DATATYPE_INT,0); | ||
| 768 | log_sql_register_field(p,"server_port", "server_port",NULL, | ||
| 769 | "server_port",LOGSQL_DATATYPE_INT,0); | ||
| 770 | log_sql_register_field(p,"referer", "referrer",NULL, | ||
| 771 | "referer",LOGSQL_DATATYPE_VARCHAR,0); | ||
| 772 | log_sql_register_field(p,"referrer", "referrer",NULL, | ||
| 773 | "referer",LOGSQL_DATATYPE_VARCHAR,0); | ||
| 774 | log_sql_register_field(p,"request_line", "request_line",NULL, | ||
| 775 | "request_line",LOGSQL_DATATYPE_VARCHAR,0); | ||
| 776 | log_sql_register_field(p,"timestamp", "timestamp",NULL, | ||
| 777 | "time_stamp",LOGSQL_DATATYPE_INT,0); | ||
| 778 | log_sql_register_field(p,"status", "status",NULL, | ||
| 779 | "status",LOGSQL_DATATYPE_INT,0); | ||
| 780 | log_sql_register_field(p,"request_duration", "request_duration",NULL, | ||
| 781 | "request_duration",LOGSQL_DATATYPE_INT,0); | ||
| 782 | log_sql_register_field(p,"request_time", "request_time",NULL, | ||
| 783 | "request_time",LOGSQL_DATATYPE_VARCHAR,0); | ||
| 784 | log_sql_register_field(p,"remote_user", "remote_user",NULL, | ||
| 785 | "remote_user",LOGSQL_DATATYPE_VARCHAR,0); | ||
| 786 | log_sql_register_field(p,"request_uri", "request_uri",NULL, | ||
| 787 | "request_uri",LOGSQL_DATATYPE_VARCHAR,0); | ||
| 788 | log_sql_register_field(p,"virtual_host", "virtual_host",NULL, | ||
| 789 | "virtual_host",LOGSQL_DATATYPE_VARCHAR,0); | ||
| 790 | log_sql_register_field(p,"server_name", "server_name",NULL, | ||
| 791 | "virtual_host",LOGSQL_DATATYPE_VARCHAR,0); | ||
| 792 | |||
| 793 | log_sql_register_finish(s); | ||
| 794 | |||
| 795 | if (global_config.announce) { | ||
| 796 | ap_add_version_component(p, PACKAGE_NAME"/"PACKAGE_VERSION); | ||
| 797 | } | ||
| 798 | global_config.db.p = p; | ||
| 799 | |||
| 800 | #if defined(WITH_APACHE20) | ||
| 801 | return OK; | ||
| 802 | #endif | ||
| 803 | } | ||
| 804 | |||
| 805 | /* This function handles calling the DB module, handling errors | ||
| 806 | * of missing tables and lost DB connections, and falling back to | ||
| 807 | * preserving the DB query. | ||
| 808 | * | ||
| 809 | * Parms: request record, table type, table name, and the full SQL command | ||
| 810 | */ | ||
| 811 | |||
| 812 | static logsql_query_ret safe_sql_insert(request_rec *r, logsql_tabletype table_type, | ||
| 813 | const char *table_name, const char *query) { | ||
| 814 | |||
| 815 | logsql_query_ret result; | ||
| 816 | logsql_state *cls = ap_get_module_config(r->server->module_config, | ||
| 817 | &log_sql_module); | ||
| 818 | |||
| 819 | if (!global_config.db.connected || global_config.driver == NULL) { | ||
| 820 | /* preserve query */ | ||
| 821 | return LOGSQL_QUERY_NOLINK; | ||
| 822 | } | ||
| 823 | |||
| 824 | result = global_config.driver->insert(r,&global_config.db,query); | ||
| 825 | |||
| 826 | /* If we ran the query and it returned an error, try to be robust. | ||
| 827 | * (After all, the module thought it had a valid mysql_log connection but the query | ||
| 828 | * could have failed for a number of reasons, so we have to be extra-safe and check.) */ | ||
| 829 | switch (result) { | ||
| 830 | case LOGSQL_QUERY_SUCCESS: | ||
| 831 | return LOGSQL_QUERY_SUCCESS; | ||
| 832 | case LOGSQL_QUERY_NOLINK: | ||
| 833 | return LOGSQL_QUERY_FAIL; | ||
| 834 | /* TODO: What do we do here */ | ||
| 835 | case LOGSQL_QUERY_FAIL: | ||
| 836 | global_config.driver->disconnect(&global_config.db); | ||
| 837 | global_config.db.connected = 0; | ||
| 838 | /* re-open the connection and try again */ | ||
| 839 | if (log_sql_opendb_link(r->server) != LOGSQL_OPENDB_FAIL) { | ||
| 840 | log_error(APLOG_MARK,APLOG_NOTICE,0, r->server,"db reconnect successful"); | ||
| 841 | # if defined(WITH_APACHE20) | ||
| 842 | apr_sleep(apr_time_from_sec(0.25)); /* pause for a quarter second */ | ||
| 843 | # elif defined(WITH_APACHE13) | ||
| 844 | # if defined(WIN32) | ||
| 845 | Sleep((DWORD)0.25); | ||
| 846 | # else | ||
| 847 | { | ||
| 848 | struct timespec delay, remainder; | ||
| 849 | int nanoret; | ||
| 850 | delay.tv_sec = 0; | ||
| 851 | delay.tv_nsec = 250000000; /* pause for a quarter second */ | ||
| 852 | nanoret = nanosleep(&delay, &remainder); | ||
| 853 | if (nanoret && errno != EINTR) { | ||
| 854 | log_error(APLOG_MARK,APLOG_ERR, errno, r->server,"nanosleep unsuccessful"); | ||
| 855 | } | ||
| 856 | } | ||
| 857 | # endif /* win32 */ | ||
| 858 | # endif | ||
| 859 | result = global_config.driver->insert(r,&global_config.db,query); | ||
| 860 | if (result == LOGSQL_QUERY_SUCCESS) { | ||
| 861 | return LOGSQL_QUERY_SUCCESS; | ||
| 862 | } else { | ||
| 863 | log_error(APLOG_MARK,APLOG_ERR,0,r->server,"second attempt failed"); | ||
| 864 | preserve_entry(r, query); | ||
| 865 | return LOGSQL_QUERY_PRESERVED; | ||
| 866 | } | ||
| 867 | } else { | ||
| 868 | log_error(APLOG_MARK,APLOG_ERR,0,r->server, | ||
| 869 | "reconnect failed, unable to reach database. SQL logging stopped until child regains a db connection."); | ||
| 870 | log_error(APLOG_MARK,APLOG_ERR,0,r->server, | ||
| 871 | "log entries are being preserved in %s",cls->preserve_file); | ||
| 872 | preserve_entry(r, query); | ||
| 873 | return LOGSQL_QUERY_PRESERVED; | ||
| 874 | } | ||
| 875 | break; | ||
| 876 | case LOGSQL_QUERY_NOTABLE: | ||
| 877 | if (global_config.createtables) { | ||
| 878 | log_error(APLOG_MARK,APLOG_ERR,0,r->server, | ||
| 879 | "table doesn't exist...creating now"); | ||
| 880 | if ((result = global_config.driver->create_table(r, &global_config.db, table_type, | ||
| 881 | table_name))!=LOGSQL_TABLE_SUCCESS) { | ||
| 882 | log_error(APLOG_MARK,APLOG_ERR,result,r->server, | ||
| 883 | "child attempted but failed to create one or more tables for %s, preserving query", ap_get_server_name(r)); | ||
| 884 | preserve_entry(r, query); | ||
| 885 | return LOGSQL_QUERY_PRESERVED; | ||
| 886 | } else { | ||
| 887 | log_error(APLOG_MARK,APLOG_ERR,result, r->server, | ||
| 888 | "tables successfully created - retrying query"); | ||
| 889 | if ((result = global_config.driver->insert(r,&global_config.db,query))!=LOGSQL_QUERY_SUCCESS) { | ||
| 890 | log_error(APLOG_MARK,APLOG_ERR,result, r->server, | ||
| 891 | "giving up, preserving query"); | ||
| 892 | preserve_entry(r, query); | ||
| 893 | return LOGSQL_QUERY_PRESERVED; | ||
| 894 | } else { | ||
| 895 | log_error(APLOG_MARK,APLOG_NOTICE,0, r->server, | ||
| 896 | "query successful after table creation"); | ||
| 897 | return LOGSQL_QUERY_SUCCESS; | ||
| 898 | } | ||
| 899 | } | ||
| 900 | } else { | ||
| 901 | log_error(APLOG_MARK,APLOG_ERR,0,r->server, | ||
| 902 | "table doesn't exist, creation denied by configuration, preserving query"); | ||
| 903 | preserve_entry(r, query); | ||
| 904 | return LOGSQL_QUERY_PRESERVED; | ||
| 905 | } | ||
| 906 | break; | ||
| 907 | default: | ||
| 908 | log_error(APLOG_MARK,APLOG_ERR,0, r->server, | ||
| 909 | "Invalid return code from mog_log_query"); | ||
| 910 | return LOGSQL_QUERY_FAIL; | ||
| 911 | break; | ||
| 912 | } | ||
| 913 | return LOGSQL_QUERY_FAIL; | ||
| 914 | } | ||
| 915 | |||
| 916 | /* This function gets called to create a per-server configuration | ||
| 917 | * record. It will always be called for the main server and | ||
| 918 | * for each virtual server that is established. Each server maintains | ||
| 919 | * its own state that is separate from the others' states. | ||
| 920 | * | ||
| 921 | * The return value is a pointer to the created module-specific | ||
| 922 | * structure. | ||
| 923 | */ | ||
| 924 | static void *log_sql_make_state(apr_pool_t *p, server_rec *s) | ||
| 925 | { | ||
| 926 | logsql_state *cls = (logsql_state *) apr_pcalloc(p, sizeof(logsql_state)); | ||
| 927 | |||
| 928 | /* These defaults are overridable in the httpd.conf file. */ | ||
| 929 | cls->transfer_log_format = apr_array_make(p, 1, sizeof(char *)); | ||
| 930 | cls->parsed_pool = p; | ||
| 931 | |||
| 932 | cls->notes_table_name = DEFAULT_NOTES_TABLE_NAME; | ||
| 933 | cls->hin_table_name = DEFAULT_HIN_TABLE_NAME; | ||
| 934 | cls->hout_table_name = DEFAULT_HOUT_TABLE_NAME; | ||
| 935 | cls->cookie_table_name = DEFAULT_COOKIE_TABLE_NAME; | ||
| 936 | cls->preserve_file = DEFAULT_PRESERVE_FILE; | ||
| 937 | |||
| 938 | cls->transfer_ignore_list = apr_array_make(p, 1, sizeof(char *)); | ||
| 939 | cls->transfer_accept_list = apr_array_make(p, 1, sizeof(char *)); | ||
| 940 | cls->remhost_ignore_list = apr_array_make(p, 1, sizeof(char *)); | ||
| 941 | cls->notes_list = apr_array_make(p, 1, sizeof(char *)); | ||
| 942 | cls->hin_list = apr_array_make(p, 1, sizeof(char *)); | ||
| 943 | cls->hout_list = apr_array_make(p, 1, sizeof(char *)); | ||
| 944 | cls->cookie_list = apr_array_make(p, 1, sizeof(char *)); | ||
| 945 | return (void *) cls; | ||
| 946 | } | ||
| 947 | |||
| 948 | |||
| 949 | /* Iterates through an array of char* and searches for a matching element | ||
| 950 | * Returns 0 if not found, 1 if found */ | ||
| 951 | static int in_array(apr_array_header_t *ary, const char *elem) | ||
| 952 | { | ||
| 953 | int itr; | ||
| 954 | for (itr = 0; itr < ary->nelts; itr++) { | ||
| 955 | if (!strcmp(elem,((char **)ary->elts)[itr])) { | ||
| 956 | return 1; | ||
| 957 | } | ||
| 958 | } | ||
| 959 | return 0; | ||
| 960 | } | ||
| 961 | |||
| 962 | |||
| 963 | /* Parse through lists and merge based on +/- prefixes */ | ||
| 964 | static apr_array_header_t *do_merge_array(apr_array_header_t *parent, apr_array_header_t *child, apr_pool_t *p) | ||
| 965 | { | ||
| 966 | apr_array_header_t *ret; | ||
| 967 | ret = apr_array_make(p, 1, sizeof(char *)); | ||
| 968 | if (apr_is_empty_array(child)) { | ||
| 969 | apr_array_cat(ret, parent); | ||
| 970 | } else { | ||
| 971 | apr_array_header_t *addlist, *dellist; | ||
| 972 | apr_pool_t *subp; | ||
| 973 | char **elem, **ptr = (char **)(child->elts); | ||
| 974 | int itr, overwrite = 0; | ||
| 975 | |||
| 976 | apr_pool_create(&subp,p); | ||
| 977 | |||
| 978 | addlist = apr_array_make(subp,5,sizeof(char *)); | ||
| 979 | dellist = apr_array_make(subp,5,sizeof(char *)); | ||
| 980 | |||
| 981 | for (itr=0; itr<child->nelts; itr++) { | ||
| 982 | if (*ptr[itr] == '+') { | ||
| 983 | elem = (char **)apr_array_push(addlist); | ||
| 984 | *elem = (ptr[itr]+1); | ||
| 985 | } else if (*ptr[itr] == '-') { | ||
| 986 | elem = (char **)apr_array_push(dellist); | ||
| 987 | *elem = (ptr[itr]+1); | ||
| 988 | } else { | ||
| 989 | overwrite = 1; | ||
| 990 | elem = (char **)apr_array_push(addlist); | ||
| 991 | *elem = ptr[itr]; | ||
| 992 | } | ||
| 993 | } | ||
| 994 | child = apr_array_make(p,1,sizeof(char *)); | ||
| 995 | ptr = (char **)(parent->elts); | ||
| 996 | if (overwrite==0) { | ||
| 997 | /* if we are not overwriting the existing then prepare for merge */ | ||
| 998 | for (itr=0; itr<parent->nelts; itr++) { | ||
| 999 | if (!in_array(addlist, ptr[itr]) && !in_array(dellist,ptr[itr])) { | ||
| 1000 | elem = apr_array_push(ret); | ||
| 1001 | *elem = apr_pstrdup(p, ptr[itr]); | ||
| 1002 | } | ||
| 1003 | } | ||
| 1004 | } | ||
| 1005 | apr_array_cat(ret, addlist); | ||
| 1006 | apr_pool_destroy(subp); | ||
| 1007 | } | ||
| 1008 | return ret; | ||
| 1009 | } | ||
| 1010 | |||
| 1011 | static void *log_sql_merge_state(apr_pool_t *p, void *basev, void *addv) | ||
| 1012 | { | ||
| 1013 | /* Fetch the two states to merge */ | ||
| 1014 | logsql_state *parent = (logsql_state *) basev; | ||
| 1015 | logsql_state *child = (logsql_state *) addv; | ||
| 1016 | |||
| 1017 | /* Child can override these, otherwise they default to parent's choice. | ||
| 1018 | * If the parent didn't set them, create reasonable defaults for the | ||
| 1019 | * ones that should have such default settings. Leave the others null. */ | ||
| 1020 | |||
| 1021 | /* No default for transfer_table_name because we want its absence | ||
| 1022 | * to disable logging. */ | ||
| 1023 | if (!child->transfer_table_name) { | ||
| 1024 | child->transfer_table_name = parent->transfer_table_name; | ||
| 1025 | } | ||
| 1026 | |||
| 1027 | if (child->preserve_file == DEFAULT_PRESERVE_FILE) | ||
| 1028 | child->preserve_file = parent->preserve_file; | ||
| 1029 | /* server_root_relative the preserve file location */ | ||
| 1030 | if (child->preserve_file == DEFAULT_PRESERVE_FILE) | ||
| 1031 | child->preserve_file = ap_server_root_relative(p, DEFAULT_PRESERVE_FILE); | ||
| 1032 | |||
| 1033 | if (child->notes_table_name == DEFAULT_NOTES_TABLE_NAME) | ||
| 1034 | child->notes_table_name = parent->notes_table_name; | ||
| 1035 | |||
| 1036 | if (child->hin_table_name == DEFAULT_HIN_TABLE_NAME) | ||
| 1037 | child->hin_table_name = parent->hin_table_name; | ||
| 1038 | |||
| 1039 | if (child->hout_table_name == DEFAULT_HOUT_TABLE_NAME) | ||
| 1040 | child->hout_table_name = parent->hout_table_name; | ||
| 1041 | |||
| 1042 | if (child->cookie_table_name == DEFAULT_COOKIE_TABLE_NAME) | ||
| 1043 | child->cookie_table_name = parent->cookie_table_name; | ||
| 1044 | |||
| 1045 | child->transfer_ignore_list = do_merge_array(parent->transfer_ignore_list, child->transfer_ignore_list, p); | ||
| 1046 | child->transfer_accept_list = do_merge_array(parent->transfer_accept_list, child->transfer_accept_list, p); | ||
| 1047 | child->remhost_ignore_list = do_merge_array(parent->remhost_ignore_list, child->remhost_ignore_list, p); | ||
| 1048 | child->notes_list = do_merge_array(parent->notes_list, child->notes_list, p); | ||
| 1049 | child->hin_list = do_merge_array(parent->hin_list, child->hin_list, p); | ||
| 1050 | child->hout_list = do_merge_array(parent->hout_list, child->hout_list, p); | ||
| 1051 | child->cookie_list = do_merge_array(parent->cookie_list,child->cookie_list, p); | ||
| 1052 | |||
| 1053 | if (!child->cookie_name) | ||
| 1054 | child->cookie_name = parent->cookie_name; | ||
| 1055 | |||
| 1056 | |||
| 1057 | return (void*) child; | ||
| 1058 | } | ||
| 1059 | |||
| 1060 | /* Routine to perform the actual construction and execution of the relevant | ||
| 1061 | * INSERT statements. | ||
| 1062 | */ | ||
| 1063 | static int log_sql_transaction(request_rec *orig) | ||
| 1064 | { | ||
| 1065 | char **ptrptr, **ptrptr2; | ||
| 1066 | logsql_state *cls = ap_get_module_config(orig->server->module_config, &log_sql_module); | ||
| 1067 | const char *access_query; | ||
| 1068 | request_rec *r; | ||
| 1069 | const char *transfer_tablename = cls->transfer_table_name; | ||
| 1070 | const char *notes_tablename = cls->notes_table_name; | ||
| 1071 | const char *hout_tablename = cls->hout_table_name; | ||
| 1072 | const char *hin_tablename = cls->hin_table_name; | ||
| 1073 | const char *cookie_tablename = cls->cookie_table_name; | ||
| 1074 | if (global_config.driver == NULL) { | ||
| 1075 | return OK; | ||
| 1076 | } | ||
| 1077 | /* We handle mass virtual hosting differently. Dynamically determine the name | ||
| 1078 | * of the table from the virtual server's name, and flag it for creation. | ||
| 1079 | */ | ||
| 1080 | if (global_config.massvirtual) { | ||
| 1081 | /* TODO: Make these configurable? */ | ||
| 1082 | char *access_base = "access_"; | ||
| 1083 | char *notes_base = "notes_"; | ||
| 1084 | char *hout_base = "headout_"; | ||
| 1085 | char *hin_base = "headin_"; | ||
| 1086 | char *cookie_base = "cookies_"; | ||
| 1087 | |||
| 1088 | |||
| 1089 | /* Determine the hostname and convert it to all lower-case; */ | ||
| 1090 | char *servername = apr_pstrdup(orig->pool,(char *)ap_get_server_name(orig)); | ||
| 1091 | |||
| 1092 | char *p=servername; | ||
| 1093 | while (*p) { | ||
| 1094 | *p = apr_tolower(*p); | ||
| 1095 | if (*p == '.') *p = '_'; | ||
| 1096 | if (*p == '-') *p = '_'; | ||
| 1097 | ++p; | ||
| 1098 | } | ||
| 1099 | |||
| 1100 | /* Find memory long enough to hold the table name + \0. */ | ||
| 1101 | transfer_tablename = apr_pstrcat(orig->pool, access_base, servername, NULL); | ||
| 1102 | notes_tablename = apr_pstrcat(orig->pool, notes_base, servername, NULL); | ||
| 1103 | hin_tablename = apr_pstrcat(orig->pool, hin_base, servername, NULL); | ||
| 1104 | hout_tablename = apr_pstrcat(orig->pool, hout_base, servername, NULL); | ||
| 1105 | cookie_tablename = apr_pstrcat(orig->pool, cookie_base, servername, NULL); | ||
| 1106 | |||
| 1107 | /* Tell this virtual server its transfer table name, and | ||
| 1108 | * turn on create_tables, which is implied by massvirtual. | ||
| 1109 | */ | ||
| 1110 | |||
| 1111 | global_config.createtables = 1; | ||
| 1112 | } | ||
| 1113 | |||
| 1114 | /* Do we have enough info to log? */ | ||
| 1115 | if (!transfer_tablename) { | ||
| 1116 | return DECLINED; | ||
| 1117 | } else { | ||
| 1118 | const char *thehost; | ||
| 1119 | const char *theitem; | ||
| 1120 | char *fields = "", *values = ""; | ||
| 1121 | char *itemsets = ""; | ||
| 1122 | char *note_query = NULL; | ||
| 1123 | char *hin_query = NULL; | ||
| 1124 | char *hout_query = NULL; | ||
| 1125 | char *cookie_query = NULL; | ||
| 1126 | const char *unique_id; | ||
| 1127 | const char *formatted_item; | ||
| 1128 | int i, showcomma; | ||
| 1129 | int proceed; | ||
| 1130 | |||
| 1131 | for (r = orig; r->next; r = r->next) { | ||
| 1132 | continue; | ||
| 1133 | } | ||
| 1134 | |||
| 1135 | /* The following is a stolen upsetting mess of pointers, I'm sorry. | ||
| 1136 | * Anyone with the motiviation and/or the time should feel free | ||
| 1137 | * to make this cleaner. :) */ | ||
| 1138 | ptrptr2 = (char **) (cls->transfer_accept_list->elts + (cls->transfer_accept_list->nelts * cls->transfer_accept_list->elt_size)); | ||
| 1139 | |||
| 1140 | /* Go through each element of the accept list and compare it to the | ||
| 1141 | * request_uri. If we don't get a match, return without logging */ | ||
| 1142 | if ((r->uri) && (cls->transfer_accept_list->nelts)) { | ||
| 1143 | proceed = 0; | ||
| 1144 | for (ptrptr = (char **) cls->transfer_accept_list->elts; ptrptr < ptrptr2; ptrptr = (char **) ((char *) ptrptr + cls->transfer_accept_list->elt_size)) | ||
| 1145 | if (ap_strstr(r->uri, *ptrptr)) { | ||
| 1146 | proceed = 1; | ||
| 1147 | break; | ||
| 1148 | } | ||
| 1149 | if (!proceed) | ||
| 1150 | return OK; | ||
| 1151 | } | ||
| 1152 | |||
| 1153 | /* Go through each element of the ignore list and compare it to the | ||
| 1154 | * request_uri. If we get a match, return without logging */ | ||
| 1155 | ptrptr2 = (char **) (cls->transfer_ignore_list->elts + (cls->transfer_ignore_list->nelts * cls->transfer_ignore_list->elt_size)); | ||
| 1156 | if (r->uri) { | ||
| 1157 | for (ptrptr = (char **) cls->transfer_ignore_list->elts; ptrptr < ptrptr2; ptrptr = (char **) ((char *) ptrptr + cls->transfer_ignore_list->elt_size)) | ||
| 1158 | if (ap_strstr(r->uri, *ptrptr)) { | ||
| 1159 | return OK; | ||
| 1160 | } | ||
| 1161 | } | ||
| 1162 | |||
| 1163 | /* Go through each element of the ignore list and compare it to the | ||
| 1164 | * remote host. If we get a match, return without logging */ | ||
| 1165 | ptrptr2 = (char **) (cls->remhost_ignore_list->elts + (cls->remhost_ignore_list->nelts * cls->remhost_ignore_list->elt_size)); | ||
| 1166 | thehost = ap_get_remote_host(r->connection, r->per_dir_config, REMOTE_NAME, NULL); | ||
| 1167 | if (thehost) { | ||
| 1168 | for (ptrptr = (char **) cls->remhost_ignore_list->elts; ptrptr < ptrptr2; ptrptr = (char **) ((char *) ptrptr + cls->remhost_ignore_list->elt_size)) | ||
| 1169 | if (ap_strstr_c(thehost, *ptrptr)) { | ||
| 1170 | return OK; | ||
| 1171 | } | ||
| 1172 | } | ||
| 1173 | |||
| 1174 | |||
| 1175 | /* Iterate through the format characters and set up the INSERT string according to | ||
| 1176 | * what the user has configured. */ | ||
| 1177 | showcomma = 0; | ||
| 1178 | for (i = 0; i<cls->transfer_log_format->nelts; i++) { | ||
| 1179 | logsql_field *item = cls->parsed_log_format[i]; | ||
| 1180 | if (item==NULL || item->func==NULL) { | ||
| 1181 | log_error(APLOG_MARK, APLOG_ERR, 0, orig->server, | ||
| 1182 | "Log Format '%s' unknown or incomplete",((char **)cls->transfer_log_format->elts)[i]); | ||
| 1183 | continue; | ||
| 1184 | } | ||
| 1185 | |||
| 1186 | /* Yes, this key is one of the configured keys. | ||
| 1187 | * Call the key's function and put the returned value into 'formatted_item' */ | ||
| 1188 | formatted_item = item->func->func(item->func->want_orig_req ? orig : r, | ||
| 1189 | item->param ? item->param : ""); | ||
| 1190 | |||
| 1191 | /* Massage 'formatted_item' for proper SQL eligibility... */ | ||
| 1192 | if (!formatted_item) { | ||
| 1193 | formatted_item = ""; | ||
| 1194 | } else if (formatted_item[0] == '-' && formatted_item[1] == '\0' && !item->string_contents) { | ||
| 1195 | /* If apache tried to log a '-' character for a numeric field, convert that to a zero | ||
| 1196 | * because the database expects a numeral and will reject the '-' character. */ | ||
| 1197 | formatted_item = "0"; | ||
| 1198 | } | ||
| 1199 | |||
| 1200 | /* Append the fieldname and value-to-insert to the appropriate strings, quoting stringvals with ' as appropriate */ | ||
| 1201 | fields = apr_pstrcat(r->pool, fields, (showcomma ? "," : ""), | ||
| 1202 | item->sql_field_name, NULL); | ||
| 1203 | values = apr_pstrcat(r->pool, values, (showcomma ? "," : ""), | ||
| 1204 | global_config.driver->escape(r, formatted_item, r->pool,&global_config.db), NULL); | ||
| 1205 | showcomma = 1; | ||
| 1206 | } | ||
| 1207 | |||
| 1208 | /* Work through the list of notes defined by LogSQLWhichNotes */ | ||
| 1209 | i = 0; | ||
| 1210 | unique_id = extract_unique_id(r, ""); | ||
| 1211 | |||
| 1212 | ptrptr2 = (char **) (cls->notes_list->elts + (cls->notes_list->nelts * cls->notes_list->elt_size)); | ||
| 1213 | for (ptrptr = (char **) cls->notes_list->elts; ptrptr < ptrptr2; ptrptr = (char **) ((char *) ptrptr + cls->notes_list->elt_size)) { | ||
| 1214 | /* If the specified note (*ptrptr) exists for the current request... */ | ||
| 1215 | if ((theitem = apr_table_get(r->notes, *ptrptr))) { | ||
| 1216 | itemsets = apr_pstrcat(r->pool, itemsets, | ||
| 1217 | (i > 0 ? "," : ""), | ||
| 1218 | "(", | ||
| 1219 | global_config.driver->escape(r, unique_id, r->pool, &global_config.db), | ||
| 1220 | ",", | ||
| 1221 | global_config.driver->escape(r, *ptrptr, r->pool,&global_config.db), | ||
| 1222 | ",", | ||
| 1223 | global_config.driver->escape(r, theitem, r->pool,&global_config.db), | ||
| 1224 | ")", | ||
| 1225 | NULL); | ||
| 1226 | i++; | ||
| 1227 | } | ||
| 1228 | } | ||
| 1229 | if ( *itemsets != '\0' ) { | ||
| 1230 | note_query = apr_psprintf(r->pool, "insert %s into %s (id, item, val) values %s", | ||
| 1231 | /*global_config.insertdelayed?"delayed":*/"", notes_tablename, itemsets); | ||
| 1232 | |||
| 1233 | log_error(APLOG_MARK,APLOG_DEBUG,0, orig->server,"mod_log_sql: note string: %s", note_query); | ||
| 1234 | } | ||
| 1235 | |||
| 1236 | /* Work through the list of headers-out defined by LogSQLWhichHeadersOut*/ | ||
| 1237 | i = 0; | ||
| 1238 | itemsets = ""; | ||
| 1239 | |||
| 1240 | ptrptr2 = (char **) (cls->hout_list->elts + (cls->hout_list->nelts * cls->hout_list->elt_size)); | ||
| 1241 | for (ptrptr = (char **) cls->hout_list->elts; ptrptr < ptrptr2; ptrptr = (char **) ((char *) ptrptr + cls->hout_list->elt_size)) { | ||
| 1242 | /* If the specified header (*ptrptr) exists for the current request... */ | ||
| 1243 | if ((theitem = apr_table_get(r->headers_out, *ptrptr))) { | ||
| 1244 | itemsets = apr_pstrcat(r->pool, itemsets, | ||
| 1245 | (i > 0 ? "," : ""), | ||
| 1246 | "(", | ||
| 1247 | global_config.driver->escape(r,unique_id, r->pool, &global_config.db), | ||
| 1248 | ",", | ||
| 1249 | global_config.driver->escape(r,*ptrptr, r->pool,&global_config.db), | ||
| 1250 | ",", | ||
| 1251 | global_config.driver->escape(r,theitem, r->pool,&global_config.db), | ||
| 1252 | ")", | ||
| 1253 | NULL); | ||
| 1254 | i++; | ||
| 1255 | } | ||
| 1256 | } | ||
| 1257 | if ( *itemsets != '\0' ) { | ||
| 1258 | hout_query = apr_psprintf(r->pool, "insert %s into %s (id, item, val) values %s", | ||
| 1259 | /*global_config.insertdelayed?"delayed":*/"", hout_tablename, itemsets); | ||
| 1260 | |||
| 1261 | log_error(APLOG_MARK,APLOG_DEBUG,0, orig->server,"mod_log_sql: header_out string: %s", hout_query); | ||
| 1262 | } | ||
| 1263 | |||
| 1264 | |||
| 1265 | /* Work through the list of headers-in defined by LogSQLWhichHeadersIn */ | ||
| 1266 | i = 0; | ||
| 1267 | itemsets = ""; | ||
| 1268 | |||
| 1269 | ptrptr2 = (char **) (cls->hin_list->elts + (cls->hin_list->nelts * cls->hin_list->elt_size)); | ||
| 1270 | for (ptrptr = (char **) cls->hin_list->elts; ptrptr < ptrptr2; ptrptr = (char **) ((char *) ptrptr + cls->hin_list->elt_size)) { | ||
| 1271 | /* If the specified header (*ptrptr) exists for the current request... */ | ||
| 1272 | if ((theitem = apr_table_get(r->headers_in, *ptrptr))) { | ||
| 1273 | itemsets = apr_pstrcat(r->pool, itemsets, | ||
| 1274 | (i > 0 ? "," : ""), | ||
| 1275 | "(", | ||
| 1276 | global_config.driver->escape(r,unique_id, r->pool, &global_config.db), | ||
| 1277 | ",", | ||
| 1278 | global_config.driver->escape(r,*ptrptr, r->pool,&global_config.db), | ||
| 1279 | ",", | ||
| 1280 | global_config.driver->escape(r,theitem, r->pool,&global_config.db), | ||
| 1281 | ")", | ||
| 1282 | NULL); | ||
| 1283 | i++; | ||
| 1284 | } | ||
| 1285 | } | ||
| 1286 | if ( *itemsets != '\0' ) { | ||
| 1287 | hin_query = apr_psprintf(r->pool, "insert %s into %s (id, item, val) values %s", | ||
| 1288 | /*global_config.insertdelayed?"delayed":*/"", hin_tablename, itemsets); | ||
| 1289 | |||
| 1290 | log_error(APLOG_MARK,APLOG_DEBUG,0, orig->server,"mod_log_sql: header_in string: %s", hin_query); | ||
| 1291 | } | ||
| 1292 | |||
| 1293 | |||
| 1294 | /* Work through the list of cookies defined by LogSQLWhichCookies */ | ||
| 1295 | i = 0; | ||
| 1296 | itemsets = ""; | ||
| 1297 | |||
| 1298 | ptrptr2 = (char **) (cls->cookie_list->elts + (cls->cookie_list->nelts * cls->cookie_list->elt_size)); | ||
| 1299 | for (ptrptr = (char **) cls->cookie_list->elts; ptrptr < ptrptr2; ptrptr = (char **) ((char *) ptrptr + cls->cookie_list->elt_size)) { | ||
| 1300 | /* If the specified cookie (*ptrptr) exists for the current request... */ | ||
| 1301 | if ( strncmp((theitem = extract_specific_cookie(r, *ptrptr)), "-", 1) ) { | ||
| 1302 | itemsets = apr_pstrcat(r->pool, itemsets, | ||
| 1303 | (i > 0 ? "," : ""), | ||
| 1304 | "(", | ||
| 1305 | global_config.driver->escape(r,unique_id, r->pool, &global_config.db), | ||
| 1306 | ",", | ||
| 1307 | global_config.driver->escape(r,*ptrptr, r->pool,&global_config.db), | ||
| 1308 | ",", | ||
| 1309 | global_config.driver->escape(r,theitem, r->pool,&global_config.db), | ||
| 1310 | ")", | ||
| 1311 | NULL); | ||
| 1312 | i++; | ||
| 1313 | } | ||
| 1314 | |||
| 1315 | } | ||
| 1316 | if ( *itemsets != '\0' ) { | ||
| 1317 | cookie_query = apr_psprintf(r->pool, "insert %s into %s (id, item, val) values %s", | ||
| 1318 | /*global_config.insertdelayed?"delayed":*/"", cookie_tablename, itemsets); | ||
| 1319 | |||
| 1320 | log_error(APLOG_MARK,APLOG_DEBUG,0, orig->server,"mod_log_sql: cookie string: %s", cookie_query); | ||
| 1321 | } | ||
| 1322 | |||
| 1323 | |||
| 1324 | /* Set up the actual INSERT statement */ | ||
| 1325 | access_query = apr_psprintf(r->pool, "insert %s into %s (%s) values (%s)", | ||
| 1326 | /*global_config.insertdelayed?"delayed":*/"", transfer_tablename, fields, values); | ||
| 1327 | |||
| 1328 | log_error(APLOG_MARK,APLOG_DEBUG,0, r->server,"mod_log_sql: access string: %s", access_query); | ||
| 1329 | |||
| 1330 | /* If the person activated force-preserve, go ahead and push all the entries | ||
| 1331 | * into the preserve file, then return. | ||
| 1332 | */ | ||
| 1333 | if (global_config.forcepreserve) { | ||
| 1334 | log_error(APLOG_MARK,APLOG_DEBUG,0, orig->server,"mod_log_sql: preservation forced"); | ||
| 1335 | preserve_entry(orig, access_query); | ||
| 1336 | if ( note_query != NULL ) | ||
| 1337 | preserve_entry(orig, note_query); | ||
| 1338 | if ( hin_query != NULL ) | ||
| 1339 | preserve_entry(orig, hin_query); | ||
| 1340 | if ( hout_query != NULL ) | ||
| 1341 | preserve_entry(orig, hout_query); | ||
| 1342 | if ( cookie_query != NULL ) | ||
| 1343 | preserve_entry(orig, cookie_query); | ||
| 1344 | return OK; | ||
| 1345 | } | ||
| 1346 | |||
| 1347 | /* How's our mysql link integrity? */ | ||
| 1348 | if (!global_config.db.connected) { | ||
| 1349 | if (!global_config.forcepreserve) { | ||
| 1350 | /* Make a try to establish the link */ | ||
| 1351 | log_sql_opendb_link(r->server); | ||
| 1352 | } | ||
| 1353 | if (!global_config.db.connected) { | ||
| 1354 | /* Unable to re-establish a DB link, so assume that it's really | ||
| 1355 | * gone and send the entry to the preserve file instead. | ||
| 1356 | * This short-circuits safe_sql_query() during a db outage and therefore | ||
| 1357 | * we don't keep logging the db error over and over. | ||
| 1358 | */ | ||
| 1359 | preserve_entry(orig, access_query); | ||
| 1360 | if ( note_query != NULL ) | ||
| 1361 | preserve_entry(orig, note_query); | ||
| 1362 | if ( hin_query != NULL ) | ||
| 1363 | preserve_entry(orig, hin_query); | ||
| 1364 | if ( hout_query != NULL ) | ||
| 1365 | preserve_entry(orig, hout_query); | ||
| 1366 | if ( cookie_query != NULL ) | ||
| 1367 | preserve_entry(orig, cookie_query); | ||
| 1368 | |||
| 1369 | return OK; | ||
| 1370 | } else { | ||
| 1371 | /* Whew, we got the DB link back */ | ||
| 1372 | log_error(APLOG_MARK,APLOG_NOTICE,0, orig->server,"mod_log_sql: child established database connection"); | ||
| 1373 | } | ||
| 1374 | } | ||
| 1375 | |||
| 1376 | |||
| 1377 | /* ---> So as of here we have a non-null value of mysql_log. <--- */ | ||
| 1378 | /* ---> i.e. we have a good MySQL connection. <--- */ | ||
| 1379 | |||
| 1380 | /* Make the access-table insert */ | ||
| 1381 | safe_sql_insert(orig,LOGSQL_TABLE_ACCESS,transfer_tablename,access_query); | ||
| 1382 | |||
| 1383 | /* Log the optional notes, headers, etc. */ | ||
| 1384 | if (note_query) | ||
| 1385 | safe_sql_insert(orig, LOGSQL_TABLE_NOTES,notes_tablename,note_query); | ||
| 1386 | |||
| 1387 | if (hout_query) | ||
| 1388 | safe_sql_insert(orig, LOGSQL_TABLE_HEADERSOUT,hout_tablename,hout_query); | ||
| 1389 | |||
| 1390 | if (hin_query) | ||
| 1391 | safe_sql_insert(orig, LOGSQL_TABLE_HEADERSIN,hin_tablename,hin_query); | ||
| 1392 | |||
| 1393 | if (cookie_query) | ||
| 1394 | safe_sql_insert(orig, LOGSQL_TABLE_COOKIES,cookie_tablename,cookie_query); | ||
| 1395 | |||
| 1396 | return OK; | ||
| 1397 | } | ||
| 1398 | } | ||
| 1399 | |||
| 1400 | |||
| 1401 | /* Setup of the available httpd.conf configuration commands. | ||
| 1402 | * Structure: command, function called, NULL, where available, how many arguments, verbose description | ||
| 1403 | */ | ||
| 1404 | static const command_rec log_sql_cmds[] = { | ||
| 1405 | AP_INIT_FLAG("LogSQLAnnounce", set_global_flag_slot, | ||
| 1406 | (void *)APR_OFFSETOF(global_config_t, announce), RSRC_CONF, | ||
| 1407 | "Whether to announce that mod_log_sql is loaded in the server header") | ||
| 1408 | , | ||
| 1409 | /* DB connection parameters */ | ||
| 1410 | AP_INIT_TAKE13("LogSQLLoginInfo", set_log_sql_info, NULL, RSRC_CONF, | ||
| 1411 | "The database connection URI in the form "driver://user:password@hostname:port/database"") | ||
| 1412 | , | ||
| 1413 | AP_INIT_TAKE2("LogSQLDBParam", set_dbparam, NULL, RSRC_CONF, | ||
| 1414 | "First argument is the DB parameter, second is the value to assign") | ||
| 1415 | , | ||
| 1416 | AP_INIT_FLAG("LogSQLForcePreserve", set_global_flag_slot, | ||
| 1417 | (void *)APR_OFFSETOF(global_config_t, forcepreserve), RSRC_CONF, | ||
| 1418 | "Forces logging to preserve file and bypasses database") | ||
| 1419 | , | ||
| 1420 | AP_INIT_FLAG("LogSQLDisablePreserve", set_global_flag_slot, | ||
| 1421 | (void *)APR_OFFSETOF(global_config_t, disablepreserve), RSRC_CONF, | ||
| 1422 | "Completely disables use of the preserve file") | ||
| 1423 | , | ||
| 1424 | AP_INIT_TAKE1("LogSQLPreserveFile", set_server_file_slot, | ||
| 1425 | (void *)APR_OFFSETOF(logsql_state,preserve_file), RSRC_CONF, | ||
| 1426 | "Name of the file to use for data preservation during database downtime") | ||
| 1427 | , | ||
| 1428 | AP_INIT_FLAG("LogSQLCreateTables", set_global_nmv_flag_slot, | ||
| 1429 | (void *)APR_OFFSETOF(global_config_t, createtables), RSRC_CONF, | ||
| 1430 | "Turn on module's capability to create its SQL tables on the fly") | ||
| 1431 | , | ||
| 1432 | /* Table names */ | ||
| 1433 | AP_INIT_FLAG("LogSQLMassVirtualHosting", set_global_flag_slot, | ||
| 1434 | (void *)APR_OFFSETOF(global_config_t, massvirtual), RSRC_CONF, | ||
| 1435 | "Activates option(s) useful for ISPs performing mass virutal hosting") | ||
| 1436 | , | ||
| 1437 | AP_INIT_TAKE1("LogSQLTransferLogTable", set_server_nmv_string_slot, | ||
| 1438 | (void *)APR_OFFSETOF(logsql_state, transfer_table_name), RSRC_CONF, | ||
| 1439 | "The database table that holds the transfer log") | ||
| 1440 | , | ||
| 1441 | AP_INIT_TAKE1("LogSQLNotesLogTable", set_server_nmv_string_slot, | ||
| 1442 | (void *)APR_OFFSETOF(logsql_state, notes_table_name), RSRC_CONF, | ||
| 1443 | "The database table that holds the notes") | ||
| 1444 | , | ||
| 1445 | AP_INIT_TAKE1("LogSQLHeadersOutLogTable", set_server_nmv_string_slot, | ||
| 1446 | (void *)APR_OFFSETOF(logsql_state, hout_table_name), RSRC_CONF, | ||
| 1447 | "The database table that holds the outbound headers") | ||
| 1448 | , | ||
| 1449 | AP_INIT_TAKE1("LogSQLHeadersInLogTable", set_server_nmv_string_slot, | ||
| 1450 | (void *)APR_OFFSETOF(logsql_state, hin_table_name), RSRC_CONF, | ||
| 1451 | "The database table that holds the inbound headers") | ||
| 1452 | , | ||
| 1453 | AP_INIT_TAKE1("LogSQLCookieLogTable", set_server_nmv_string_slot, | ||
| 1454 | (void *)APR_OFFSETOF(logsql_state, cookie_table_name), RSRC_CONF, | ||
| 1455 | "The database table that holds the cookie info") | ||
| 1456 | , | ||
| 1457 | /* New Log Format */ | ||
| 1458 | AP_INIT_ITERATE("LogSQLTransferLogItems", add_server_string_slot, | ||
| 1459 | (void *)APR_OFFSETOF(logsql_state, transfer_log_format), RSRC_CONF, | ||
| 1460 | "What fields to log to the database transfer log") | ||
| 1461 | , | ||
| 1462 | AP_INIT_RAW_ARGS("LogSQLRegisterItem", set_register_field, | ||
| 1463 | NULL, RSRC_CONF, | ||
| 1464 | "Register a new Item for logging, Arguments: ItemName function argument sqlfield datatype datalen<br>" | ||
| 1465 | "datatypes are INT, SMALLINT, VARCHAR, CHAR<br>") | ||
| 1466 | , | ||
| 1467 | AP_INIT_TAKE1("LogSQLShowConfig", set_global_string_slot, | ||
| 1468 | (void *)APR_OFFSETOF(global_config_t, showconfig), RSRC_CONF, | ||
| 1469 | "Add this to export the entire running function and dfield configuration to the named file") | ||
| 1470 | , | ||
| 1471 | /* Machine ID */ | ||
| 1472 | AP_INIT_TAKE1("LogSQLMachineID", set_global_string_slot, | ||
| 1473 | (void *)APR_OFFSETOF(global_config_t, machid), RSRC_CONF, | ||
| 1474 | "Machine ID that the module will log, useful in web clusters to differentiate machines") | ||
| 1475 | , | ||
| 1476 | /* Limits on logging */ | ||
| 1477 | AP_INIT_ITERATE("LogSQLRequestAccept", add_server_string_slot, | ||
| 1478 | (void *)APR_OFFSETOF(logsql_state, transfer_accept_list), RSRC_CONF, | ||
| 1479 | "List of URIs to accept for logging. Accesses that don't match will not be logged") | ||
| 1480 | , | ||
| 1481 | AP_INIT_ITERATE("LogSQLRequestIgnore", add_server_string_slot, | ||
| 1482 | (void *)APR_OFFSETOF(logsql_state, transfer_ignore_list), RSRC_CONF, | ||
| 1483 | "List of URIs to ignore. Accesses that match will not be logged to database") | ||
| 1484 | , | ||
| 1485 | AP_INIT_ITERATE("LogSQLRemhostIgnore", add_server_string_slot, | ||
| 1486 | (void *)APR_OFFSETOF(logsql_state, remhost_ignore_list), RSRC_CONF, | ||
| 1487 | "List of remote hosts to ignore. Accesses that match will not be logged to database") | ||
| 1488 | , | ||
| 1489 | /* Special logging table configuration */ | ||
| 1490 | AP_INIT_TAKE1("LogSQLWhichCookie", set_server_string_slot, | ||
| 1491 | (void *)APR_OFFSETOF(logsql_state, cookie_name), RSRC_CONF, | ||
| 1492 | "The single cookie that you want logged in the access_log when using the 'c' config directive") | ||
| 1493 | , | ||
| 1494 | AP_INIT_ITERATE("LogSQLWhichNotes", add_server_string_slot, | ||
| 1495 | (void *)APR_OFFSETOF(logsql_state, notes_list), RSRC_CONF, | ||
| 1496 | "Notes that you would like to log in a separate table") | ||
| 1497 | , | ||
| 1498 | AP_INIT_ITERATE("LogSQLWhichHeadersOut", add_server_string_slot, | ||
| 1499 | (void *)APR_OFFSETOF(logsql_state, hout_list), RSRC_CONF, | ||
| 1500 | "Outbound headers that you would like to log in a separate table") | ||
| 1501 | , | ||
| 1502 | AP_INIT_ITERATE("LogSQLWhichHeadersIn", add_server_string_slot, | ||
| 1503 | (void *)APR_OFFSETOF(logsql_state, hin_list), RSRC_CONF, | ||
| 1504 | "Inbound headers that you would like to log in a separate table") | ||
| 1505 | , | ||
| 1506 | AP_INIT_ITERATE("LogSQLWhichCookies", add_server_string_slot, | ||
| 1507 | (void *)APR_OFFSETOF(logsql_state, cookie_list), RSRC_CONF, | ||
| 1508 | "The cookie(s) that you would like to log in a separate table") | ||
| 1509 | , | ||
| 1510 | AP_INIT_RAW_ARGS("LogSQLDeprecated", ap_set_deprecated, NULL, RSRC_CONF, | ||
| 1511 | "<br><b>Deprecated</b><br>The following Commands are deprecated and should not be used.. <br>Read the documentation for more information<br><b>Deprecated</b>") | ||
| 1512 | , | ||
| 1513 | /* Deprecated commands */ | ||
| 1514 | AP_INIT_TAKE1("LogSQLTransferLogFormat", set_logformat_slot, | ||
| 1515 | (void *)APR_OFFSETOF(logsql_state, transfer_log_format), RSRC_CONF, | ||
| 1516 | "<b>(Deprecated) Use LogSQLTransferLogItem to specify symbolic log items instead") | ||
| 1517 | , | ||
| 1518 | AP_INIT_TAKE1("LogSQLDatabase", set_dbparam_slot, | ||
| 1519 | (void *)"database", RSRC_CONF, | ||
| 1520 | "<b>(Deprecated) Use LogSQLDBParam database dbname.</b> The name of the database database for logging") | ||
| 1521 | , | ||
| 1522 | AP_INIT_TAKE1("LogSQLTableType", set_dbparam_slot, | ||
| 1523 | (void *)"tabletype", RSRC_CONF, | ||
| 1524 | "<b>(Deprecated) Use LogSQLDBParam tabletype type.</b> What kind of table to create (MyISAM, InnoDB,...) when creating tables") | ||
| 1525 | , | ||
| 1526 | AP_INIT_TAKE1("LogSQLSocketFile", set_dbparam_slot, | ||
| 1527 | (void *)"socketfile", RSRC_CONF, | ||
| 1528 | "<b>(Deprecated) Use LogSQLDBParam socketfile socket.</b> Name of the file to employ for socket connections to database") | ||
| 1529 | , | ||
| 1530 | AP_INIT_TAKE1("LogSQLTCPPort", set_dbparam_slot, | ||
| 1531 | (void *)"port", RSRC_CONF, | ||
| 1532 | "<b>(Deprecated) Use LogSQLDBParam port port.</b> Port number to use for TCP connections to database, defaults to 3306 if not set") | ||
| 1533 | , | ||
| 1534 | {NULL} | ||
| 1535 | }; | ||
| 1536 | /* The configuration array that sets up the hooks into the module. */ | ||
| 1537 | #if defined(WITH_APACHE20) | ||
| 1538 | static void register_hooks(apr_pool_t *p) { | ||
| 1539 | ap_hook_post_config(log_sql_post_config, NULL, NULL, APR_HOOK_REALLY_FIRST); | ||
| 1540 | ap_hook_child_init(log_sql_child_init, NULL, NULL, APR_HOOK_MIDDLE); | ||
| 1541 | ap_hook_log_transaction(log_sql_transaction, NULL, NULL, APR_HOOK_MIDDLE); | ||
| 1542 | } | ||
| 1543 | |||
| 1544 | module AP_MODULE_DECLARE_DATA log_sql_module = { | ||
| 1545 | STANDARD20_MODULE_STUFF, | ||
| 1546 | NULL, /* create per-directory config structures */ | ||
| 1547 | NULL, /* merge per-directory config structures */ | ||
| 1548 | log_sql_make_state, /* create per-server config structures */ | ||
| 1549 | log_sql_merge_state, /* merge per-server config structures */ | ||
| 1550 | log_sql_cmds, /* command handlers */ | ||
| 1551 | register_hooks /* register hooks */ | ||
| 1552 | }; | ||
| 1553 | #elif defined(WITH_APACHE13) | ||
| 1554 | module MODULE_VAR_EXPORT log_sql_module = { | ||
| 1555 | STANDARD_MODULE_STUFF, | ||
| 1556 | log_sql_module_init, /* module initializer */ | ||
| 1557 | NULL, /* create per-dir config */ | ||
| 1558 | NULL, /* merge per-dir config */ | ||
| 1559 | log_sql_make_state, /* create server config */ | ||
| 1560 | log_sql_merge_state, /* merge server config */ | ||
| 1561 | log_sql_cmds, /* config directive table */ | ||
| 1562 | NULL, /* [9] content handlers */ | ||
| 1563 | NULL, /* [2] URI-to-filename translation */ | ||
| 1564 | NULL, /* [5] check/validate user_id */ | ||
| 1565 | NULL, /* [6] check authorization */ | ||
| 1566 | NULL, /* [4] check access by host */ | ||
| 1567 | NULL, /* [7] MIME type checker/setter */ | ||
| 1568 | NULL, /* [8] fixups */ | ||
| 1569 | log_sql_transaction, /* [10] logger */ | ||
| 1570 | NULL /* [3] header parser */ | ||
| 1571 | #if MODULE_MAGIC_NUMBER >= 19970728 /* 1.3-dev or later support these additionals... */ | ||
| 1572 | ,log_sql_child_init, /* child process initializer */ | ||
| 1573 | log_sql_child_exit, /* process exit/cleanup */ | ||
| 1574 | NULL /* [1] post read-request */ | ||
| 1575 | #endif | ||
| 1576 | |||
| 1577 | }; | ||
| 1578 | #endif | ||
diff --git a/src/mod_log_sql_dbd.c b/src/mod_log_sql_dbd.c new file mode 100644 index 0000000..e641c35 --- /dev/null +++ b/src/mod_log_sql_dbd.c | |||
| @@ -0,0 +1,132 @@ | |||
| 1 | /* $Id: mod_log_sql_dbi.c 120 2004-04-17 15:14:12Z urkle@drip.ws $ */ | ||
| 2 | |||
| 3 | #if defined(WITH_APACHE20) | ||
| 4 | # include "apache20.h" | ||
| 5 | #else | ||
| 6 | # error Unsupported Apache version | ||
| 7 | #endif | ||
| 8 | |||
| 9 | |||
| 10 | #ifdef HAVE_CONFIG_H | ||
| 11 | /* Undefine these to prevent conflicts between Apache ap_config_auto.h and | ||
| 12 | * my config.h. Only really needed for Apache < 2.0.48, but it can't hurt. | ||
| 13 | */ | ||
| 14 | #undef PACKAGE_BUGREPORT | ||
| 15 | #undef PACKAGE_NAME | ||
| 16 | #undef PACKAGE_STRING | ||
| 17 | #undef PACKAGE_TARNAME | ||
| 18 | #undef PACKAGE_VERSION | ||
| 19 | |||
| 20 | #include "autoconfig.h" | ||
| 21 | #endif | ||
| 22 | |||
| 23 | #include "mod_log_sql.h" | ||
| 24 | |||
| 25 | #include "apr_dbd.h" | ||
| 26 | #include "mod_dbd.h" | ||
| 27 | |||
| 28 | typedef struct { | ||
| 29 | ap_dbd_t *dbd; | ||
| 30 | } request_config_t; | ||
| 31 | |||
| 32 | LOGSQL_MODULE_FORWARD(dbd); | ||
| 33 | |||
| 34 | static ap_dbd_t *(*dbd_acquire_fn)(request_rec*) = NULL; | ||
| 35 | |||
| 36 | static ap_dbd_t *log_sql_dbd_getconnection(request_rec *r) | ||
| 37 | { | ||
| 38 | request_config_t *rconf = ap_get_module_config(r->request_config, &LOGSQL_MODULE(dbd)); | ||
| 39 | if (!rconf) { | ||
| 40 | rconf = apr_pcalloc(r->pool, sizeof(request_config_t)); | ||
| 41 | ap_set_module_config(r->request_config, &LOGSQL_MODULE(dbd), (void *)rconf); | ||
| 42 | rconf->dbd = dbd_acquire_fn(r); | ||
| 43 | } | ||
| 44 | return rconf->dbd; | ||
| 45 | } | ||
| 46 | |||
| 47 | /* Connect to the database */ | ||
| 48 | static logsql_opendb_ret log_sql_dbd_connect(server_rec *s, logsql_dbconnection *db) | ||
| 49 | { | ||
| 50 | // We are using mod_dbd so we don't do anything here | ||
| 51 | if (!dbd_acquire_fn) { | ||
| 52 | // no mod_dbd return failure | ||
| 53 | log_error(APLOG_MARK,APLOG_ERR,0, s,"mod_log_sql_dbd: mod_dbd is not loaded or available"); | ||
| 54 | return LOGSQL_OPENDB_FAIL; | ||
| 55 | } else { | ||
| 56 | return LOGSQL_OPENDB_SUCCESS; | ||
| 57 | } | ||
| 58 | } | ||
| 59 | |||
| 60 | /* Close the DB link */ | ||
| 61 | static void log_sql_dbd_close(logsql_dbconnection *db) | ||
| 62 | { | ||
| 63 | // mod_dbd handles this, so do nothing | ||
| 64 | } | ||
| 65 | |||
| 66 | /* Routine to escape the 'dangerous' characters that would otherwise | ||
| 67 | * corrupt the INSERT string: ', \, and " | ||
| 68 | */ | ||
| 69 | static const char *log_sql_dbd_escape(request_rec *r, const char *from_str, apr_pool_t *p, | ||
| 70 | logsql_dbconnection *db) | ||
| 71 | { | ||
| 72 | // Acquire a DBD connection from mod_dbd | ||
| 73 | ap_dbd_t *dbd = log_sql_dbd_getconnection(r); | ||
| 74 | if (!dbd) return NULL; | ||
| 75 | |||
| 76 | if (!from_str) | ||
| 77 | return NULL; | ||
| 78 | |||
| 79 | return apr_pstrcat(p, "'",apr_dbd_escape(dbd->driver, p, from_str, dbd->handle),"'",NULL); | ||
| 80 | } | ||
| 81 | |||
| 82 | /* Run an insert query and return a categorized error or success */ | ||
| 83 | static logsql_query_ret log_sql_dbd_query(request_rec *r,logsql_dbconnection *db, | ||
| 84 | const char *query) | ||
| 85 | { | ||
| 86 | int ret; | ||
| 87 | const char *err; | ||
| 88 | int affected; | ||
| 89 | // Acquire a DBD connection from mod_dbd | ||
| 90 | ap_dbd_t *dbd = log_sql_dbd_getconnection(r); | ||
| 91 | if (!dbd) return LOGSQL_QUERY_NOLINK; | ||
| 92 | |||
| 93 | // Run the query | ||
| 94 | ret = apr_dbd_query(dbd->driver, dbd->handle, &affected, query); | ||
| 95 | if (ret == 0) { | ||
| 96 | return LOGSQL_QUERY_SUCCESS; | ||
| 97 | } else { | ||
| 98 | // attempt to detect error message | ||
| 99 | err = apr_dbd_error(dbd->driver, dbd->handle, ret); | ||
| 100 | log_error(APLOG_MARK, APLOG_ERR, 0, r->server, "DB Returned error: (%d) %s", ret, err); | ||
| 101 | // Unable to check if "NO SUCH TABLE" due to apr_dbd not mapping error codes to a standard set. | ||
| 102 | return LOGSQL_QUERY_FAIL; | ||
| 103 | } | ||
| 104 | } | ||
| 105 | |||
| 106 | /* Create table table_name of type table_type. */ | ||
| 107 | static logsql_table_ret log_sql_dbd_create(request_rec *r, logsql_dbconnection *db, | ||
| 108 | logsql_tabletype table_type, const char *table_name) | ||
| 109 | { | ||
| 110 | return LOGSQL_TABLE_FAIL; | ||
| 111 | } | ||
| 112 | |||
| 113 | static const char *supported_drivers[] = {"dbd",NULL}; | ||
| 114 | static logsql_dbdriver log_sql_dbd_driver = { | ||
| 115 | "dbd", | ||
| 116 | supported_drivers, | ||
| 117 | log_sql_dbd_connect,/* open DB connection */ | ||
| 118 | log_sql_dbd_close, /* close DB connection */ | ||
| 119 | log_sql_dbd_escape, /* escape query */ | ||
| 120 | log_sql_dbd_query, /* insert query */ | ||
| 121 | log_sql_dbd_create /* create table */ | ||
| 122 | }; | ||
| 123 | |||
| 124 | LOGSQL_REGISTER(dbd) { | ||
| 125 | dbd_acquire_fn = APR_RETRIEVE_OPTIONAL_FN(ap_dbd_acquire); | ||
| 126 | if (dbd_acquire_fn == NULL) { | ||
| 127 | log_error(APLOG_MARK,APLOG_ERR,0,s,"You must load mod_dbd to enable AuthDBD functions"); | ||
| 128 | } | ||
| 129 | |||
| 130 | log_sql_register_driver(p,&log_sql_dbd_driver); | ||
| 131 | LOGSQL_REGISTER_RETURN; | ||
| 132 | } | ||
diff --git a/src/mod_log_sql_dbi.c b/src/mod_log_sql_dbi.c new file mode 100644 index 0000000..5b83836 --- /dev/null +++ b/src/mod_log_sql_dbi.c | |||
| @@ -0,0 +1,263 @@ | |||
| 1 | /* $Id: mod_log_sql_dbi.c 120 2004-04-17 15:14:12Z urkle@drip.ws $ */ | ||
| 2 | |||
| 3 | #if defined(WITH_APACHE20) | ||
| 4 | # include "apache20.h" | ||
| 5 | #elif defined(WITH_APACHE13) | ||
| 6 | # include "apache13.h" | ||
| 7 | #else | ||
| 8 | # error Unsupported Apache version | ||
| 9 | #endif | ||
| 10 | |||
| 11 | |||
| 12 | #ifdef HAVE_CONFIG_H | ||
| 13 | /* Undefine these to prevent conflicts between Apache ap_config_auto.h and | ||
| 14 | * my config.h. Only really needed for Apache < 2.0.48, but it can't hurt. | ||
| 15 | */ | ||
| 16 | #undef PACKAGE_BUGREPORT | ||
| 17 | #undef PACKAGE_NAME | ||
| 18 | #undef PACKAGE_STRING | ||
| 19 | #undef PACKAGE_TARNAME | ||
| 20 | #undef PACKAGE_VERSION | ||
| 21 | |||
| 22 | #include "autoconfig.h" | ||
| 23 | #endif | ||
| 24 | |||
| 25 | #include "mod_log_sql.h" | ||
| 26 | |||
| 27 | #include "dbi/dbi.h" | ||
| 28 | |||
| 29 | typedef struct { | ||
| 30 | dbi_conn conn; | ||
| 31 | } dbi_conn_rec; | ||
| 32 | |||
| 33 | /* Connect to the MYSQL database */ | ||
| 34 | static logsql_opendb_ret log_sql_dbi_connect(server_rec *s, logsql_dbconnection *db) | ||
| 35 | { | ||
| 36 | const char *driver = apr_table_get(db->parms,"driver"); | ||
| 37 | const char *host = apr_table_get(db->parms,"hostname"); | ||
| 38 | const char *user = apr_table_get(db->parms,"username"); | ||
| 39 | const char *passwd = apr_table_get(db->parms,"password"); | ||
| 40 | const char *database = apr_table_get(db->parms,"database"); | ||
| 41 | const char *s_tcpport = apr_table_get(db->parms,"port"); | ||
| 42 | unsigned int tcpport = (s_tcpport)?atoi(s_tcpport):0; | ||
| 43 | const char *socketfile = apr_table_get(db->parms,"socketfile"); | ||
| 44 | //dbi_result result; | ||
| 45 | dbi_conn_rec *dblink = db->handle; | ||
| 46 | if (!dblink) { | ||
| 47 | dblink = apr_pcalloc(db->p, sizeof(*dblink)); | ||
| 48 | db->handle = (void *)dblink; | ||
| 49 | } | ||
| 50 | |||
| 51 | dblink->conn = dbi_conn_new(driver); | ||
| 52 | |||
| 53 | dbi_conn_set_option(dblink->conn, "host", host); | ||
| 54 | dbi_conn_set_option(dblink->conn, "username", user); | ||
| 55 | dbi_conn_set_option(dblink->conn, "password", passwd); | ||
| 56 | dbi_conn_set_option(dblink->conn, "dbname", database); | ||
| 57 | if (tcpport) { | ||
| 58 | dbi_conn_set_option_numeric(dblink->conn, "port", tcpport); | ||
| 59 | } | ||
| 60 | |||
| 61 | if (socketfile && !strcmp(driver,"mysql")) { | ||
| 62 | dbi_conn_set_option(dblink->conn, "mysql_unix_socket", socketfile); | ||
| 63 | } | ||
| 64 | |||
| 65 | if (!dbi_conn_connect(dblink->conn)) { | ||
| 66 | log_error(APLOG_MARK, APLOG_DEBUG, 0, s, | ||
| 67 | "HOST: '%s' PORT: '%d' DB: '%s' USER: '%s' SOCKET: '%s'", | ||
| 68 | host, tcpport, database, user, socketfile); | ||
| 69 | return LOGSQL_OPENDB_SUCCESS; | ||
| 70 | } else { | ||
| 71 | const char *error; | ||
| 72 | dbi_conn_error(dblink->conn, &error); | ||
| 73 | log_error(APLOG_MARK, APLOG_ERR, 0, s, | ||
| 74 | "DBI Error: %s", error); | ||
| 75 | log_error(APLOG_MARK, APLOG_DEBUG, 0, s, | ||
| 76 | "HOST: '%s' PORT: '%d' DB: '%s' USER: '%s' SOCKET: '%s'", | ||
| 77 | host, tcpport, database, user, socketfile); | ||
| 78 | return LOGSQL_OPENDB_FAIL; | ||
| 79 | } | ||
| 80 | } | ||
| 81 | |||
| 82 | /* Close the DB link */ | ||
| 83 | static void log_sql_dbi_close(logsql_dbconnection *db) | ||
| 84 | { | ||
| 85 | dbi_conn_rec *dblink = db->handle; | ||
| 86 | dbi_conn_close(dblink->conn); | ||
| 87 | dblink->conn = NULL; | ||
| 88 | } | ||
| 89 | |||
| 90 | /* Routine to escape the 'dangerous' characters that would otherwise | ||
| 91 | * corrupt the INSERT string: ', \, and " | ||
| 92 | */ | ||
| 93 | static const char *log_sql_dbi_escape(request_rec *r,const char *from_str, apr_pool_t *p, | ||
| 94 | logsql_dbconnection *db) | ||
| 95 | { | ||
| 96 | dbi_conn_rec *dblink = db->handle; | ||
| 97 | |||
| 98 | if (!from_str) | ||
| 99 | return NULL; | ||
| 100 | else { | ||
| 101 | char *to_str = strdup(from_str); | ||
| 102 | char *retval; | ||
| 103 | dbi_driver_quote_string(dbi_conn_get_driver(dblink->conn), &to_str); | ||
| 104 | retval = apr_pstrdup(p, to_str); | ||
| 105 | free(to_str); | ||
| 106 | return retval; | ||
| 107 | } | ||
| 108 | } | ||
| 109 | |||
| 110 | /* Run a mysql insert query and return a categorized error or success */ | ||
| 111 | static logsql_query_ret log_sql_dbi_query(request_rec *r,logsql_dbconnection *db, | ||
| 112 | const char *query) | ||
| 113 | { | ||
| 114 | const char *error; | ||
| 115 | dbi_result result; | ||
| 116 | dbi_conn_rec *dblink = db->handle; | ||
| 117 | |||
| 118 | if (!dblink->conn) { | ||
| 119 | return LOGSQL_QUERY_NOLINK; | ||
| 120 | } | ||
| 121 | |||
| 122 | /* Run the query */ | ||
| 123 | if ((result = dbi_conn_query(dblink->conn, query))) { | ||
| 124 | return LOGSQL_QUERY_SUCCESS; | ||
| 125 | } | ||
| 126 | /* Check to see if the error is "nonexistent table" */ | ||
| 127 | dbi_conn_error(dblink->conn, &error); | ||
| 128 | log_error(APLOG_MARK, APLOG_ERR, 0, r->server, | ||
| 129 | "DBI Error: %s", error); | ||
| 130 | /* if (real_error == ER_NO_SUCH_TABLE) { | ||
| 131 | log_error(APLOG_MARK,APLOG_ERR,0, r->server,"table does not exist, preserving query"); | ||
| 132 | return LOGSQL_QUERY_NOTABLE; | ||
| 133 | }*/ | ||
| 134 | return LOGSQL_QUERY_FAIL; | ||
| 135 | } | ||
| 136 | |||
| 137 | /* Create table table_name of type table_type. */ | ||
| 138 | static logsql_table_ret log_sql_dbi_create(request_rec *r, logsql_dbconnection *db, | ||
| 139 | logsql_tabletype table_type, const char *table_name) | ||
| 140 | { | ||
| 141 | dbi_result result; | ||
| 142 | const char *driver = apr_table_get(db->parms,"driver"); | ||
| 143 | const char *tabletype = apr_table_get(db->parms,"tabletype"); | ||
| 144 | char *type_suffix = NULL; | ||
| 145 | |||
| 146 | char *create_prefix = "create table if not exists `"; | ||
| 147 | char *create_suffix = NULL; | ||
| 148 | char *create_sql; | ||
| 149 | |||
| 150 | dbi_conn_rec *dblink = db->handle; | ||
| 151 | |||
| 152 | /* if (!global_config.createtables) { | ||
| 153 | return APR_SUCCESS; | ||
| 154 | }*/ | ||
| 155 | |||
| 156 | switch (table_type) { | ||
| 157 | case LOGSQL_TABLE_ACCESS: | ||
| 158 | create_suffix = | ||
| 159 | "` (id char(19),\ | ||
| 160 | agent varchar(255),\ | ||
| 161 | bytes_sent int unsigned,\ | ||
| 162 | child_pid smallint unsigned,\ | ||
| 163 | cookie varchar(255),\ | ||
| 164 | machine_id varchar(25),\ | ||
| 165 | request_file varchar(255),\ | ||
| 166 | referer varchar(255),\ | ||
| 167 | remote_host varchar(50),\ | ||
| 168 | remote_logname varchar(50),\ | ||
| 169 | remote_user varchar(50),\ | ||
| 170 | request_duration smallint unsigned,\ | ||
| 171 | request_line varchar(255),\ | ||
| 172 | request_method varchar(10),\ | ||
| 173 | request_protocol varchar(10),\ | ||
| 174 | request_time char(28),\ | ||
| 175 | request_uri varchar(255),\ | ||
| 176 | request_args varchar(255),\ | ||
| 177 | server_port smallint unsigned,\ | ||
| 178 | ssl_cipher varchar(25),\ | ||
| 179 | ssl_keysize smallint unsigned,\ | ||
| 180 | ssl_maxkeysize smallint unsigned,\ | ||
| 181 | status smallint unsigned,\ | ||
| 182 | time_stamp int unsigned,\ | ||
| 183 | virtual_host varchar(255))"; | ||
| 184 | break; | ||
| 185 | case LOGSQL_TABLE_COOKIES: | ||
| 186 | case LOGSQL_TABLE_HEADERSIN: | ||
| 187 | case LOGSQL_TABLE_HEADERSOUT: | ||
| 188 | case LOGSQL_TABLE_NOTES: | ||
| 189 | create_suffix = | ||
| 190 | "` (id char(19),\ | ||
| 191 | item varchar(80),\ | ||
| 192 | val varchar(80))"; | ||
| 193 | break; | ||
| 194 | } | ||
| 195 | |||
| 196 | if (tabletype && !strcmp(driver,"mysql")) { | ||
| 197 | type_suffix = apr_pstrcat(r->pool, " TYPE=", | ||
| 198 | tabletype, NULL); | ||
| 199 | } | ||
| 200 | /* Find memory long enough to hold the whole CREATE string + \0 */ | ||
| 201 | create_sql = apr_pstrcat(r->pool, create_prefix, table_name, create_suffix, | ||
| 202 | type_suffix, NULL); | ||
| 203 | |||
| 204 | log_error(APLOG_MARK,APLOG_DEBUG,0, r->server,"create string: %s", create_sql); | ||
| 205 | |||
| 206 | if (!dblink) { | ||
| 207 | return LOGSQL_QUERY_NOLINK; | ||
| 208 | } | ||
| 209 | |||
| 210 | /* Run the create query */ | ||
| 211 | if (!(result = dbi_conn_query(dblink, create_sql))) { | ||
| 212 | const char *error; | ||
| 213 | dbi_conn_error(dblink->conn, &error); | ||
| 214 | log_error(APLOG_MARK, APLOG_ERR, 0, r->server, | ||
| 215 | "DBI Error: %s", error); | ||
| 216 | return LOGSQL_TABLE_FAIL; | ||
| 217 | } | ||
| 218 | |||
| 219 | return LOGSQL_TABLE_SUCCESS; | ||
| 220 | } | ||
| 221 | |||
| 222 | static logsql_dbdriver log_sql_dbi_driver = { | ||
| 223 | "dbi", | ||
| 224 | NULL, | ||
| 225 | log_sql_dbi_connect, /* open DB connection */ | ||
| 226 | log_sql_dbi_close, /* close DB connection */ | ||
| 227 | log_sql_dbi_escape, /* escape query */ | ||
| 228 | log_sql_dbi_query, /* insert query */ | ||
| 229 | log_sql_dbi_create /* create table */ | ||
| 230 | }; | ||
| 231 | |||
| 232 | static apr_status_t log_sql_dbi_cleanup(void *data) | ||
| 233 | { | ||
| 234 | dbi_shutdown(); | ||
| 235 | #if defined(WITH_APACHE20) | ||
| 236 | return APR_SUCCESS; | ||
| 237 | #endif | ||
| 238 | } | ||
| 239 | |||
| 240 | LOGSQL_REGISTER(dbi) { | ||
| 241 | dbi_driver driver; | ||
| 242 | const char **driver_list; | ||
| 243 | int count = 1; | ||
| 244 | |||
| 245 | dbi_initialize(NULL); | ||
| 246 | |||
| 247 | for (driver = dbi_driver_list(NULL); | ||
| 248 | driver; | ||
| 249 | driver = dbi_driver_list(driver)) { | ||
| 250 | count++; | ||
| 251 | } | ||
| 252 | driver_list = apr_pcalloc(p, sizeof(char *) * (count)); | ||
| 253 | count = 0; | ||
| 254 | for (driver = dbi_driver_list(NULL); | ||
| 255 | driver; | ||
| 256 | driver = dbi_driver_list(driver)) { | ||
| 257 | driver_list[count++] = dbi_driver_get_name(driver); | ||
| 258 | } | ||
| 259 | log_sql_dbi_driver.provided_drivers = driver_list; | ||
| 260 | log_sql_register_driver(p,&log_sql_dbi_driver); | ||
| 261 | apr_pool_cleanup_register(p, NULL, log_sql_dbi_cleanup, NULL); | ||
| 262 | LOGSQL_REGISTER_RETURN; | ||
| 263 | } | ||
diff --git a/src/mod_log_sql_logio.c b/src/mod_log_sql_logio.c new file mode 100644 index 0000000..ebdeddd --- /dev/null +++ b/src/mod_log_sql_logio.c | |||
| @@ -0,0 +1,146 @@ | |||
| 1 | /* $Id: mod_log_sql_ssl.c 140 2004-05-14 03:50:47Z urkle@drip.ws $ */ | ||
| 2 | |||
| 3 | #if defined(WITH_APACHE20) | ||
| 4 | # include "apache20.h" | ||
| 5 | #else | ||
| 6 | # error Unsupported Apache version | ||
| 7 | #endif | ||
| 8 | |||
| 9 | #ifdef HAVE_CONFIG_H | ||
| 10 | /* Undefine these to prevent conflicts between Apache ap_config_auto.h and | ||
| 11 | * my config.h. Only really needed for Apache < 2.0.48, but it can't hurt. | ||
| 12 | */ | ||
| 13 | #undef PACKAGE_BUGREPORT | ||
| 14 | #undef PACKAGE_NAME | ||
| 15 | #undef PACKAGE_STRING | ||
| 16 | #undef PACKAGE_TARNAME | ||
| 17 | #undef PACKAGE_VERSION | ||
| 18 | |||
| 19 | #include "autoconfig.h" | ||
| 20 | #endif | ||
| 21 | |||
| 22 | #include "mod_log_sql.h" | ||
| 23 | |||
| 24 | #include "http_connection.h" | ||
| 25 | |||
| 26 | module AP_MODULE_DECLARE_DATA log_sql_logio_module; | ||
| 27 | |||
| 28 | // From apachge 2.2's mod_logio.c to provide logging ACTUAL incoming and outgoing bytes | ||
| 29 | static const char logio_filter_name[] = "LOG_SQL_INPUT_OUTPUT"; | ||
| 30 | |||
| 31 | typedef struct { | ||
| 32 | apr_off_t bytes_in; | ||
| 33 | apr_off_t bytes_out; | ||
| 34 | } logio_config_t; | ||
| 35 | |||
| 36 | static void ap_logio_add_bytes_out(conn_rec *c, apr_off_t bytes){ | ||
| 37 | logio_config_t *cf = ap_get_module_config(c->conn_config, &log_sql_logio_module); | ||
| 38 | |||
| 39 | cf->bytes_out += bytes; | ||
| 40 | } | ||
| 41 | |||
| 42 | static const char *log_bytes_in(request_rec *r, char *a) | ||
| 43 | { | ||
| 44 | logio_config_t *cf = ap_get_module_config(r->connection->conn_config, | ||
| 45 | &log_sql_logio_module); | ||
| 46 | |||
| 47 | return apr_off_t_toa(r->pool, cf->bytes_in); | ||
| 48 | } | ||
| 49 | |||
| 50 | static const char *log_bytes_out(request_rec *r, char *a) | ||
| 51 | { | ||
| 52 | logio_config_t *cf = ap_get_module_config(r->connection->conn_config, | ||
| 53 | &log_sql_logio_module); | ||
| 54 | |||
| 55 | return apr_off_t_toa(r->pool, cf->bytes_out); | ||
| 56 | } | ||
| 57 | |||
| 58 | static int logio_transaction(request_rec *r) | ||
| 59 | { | ||
| 60 | logio_config_t *cf = ap_get_module_config(r->connection->conn_config, | ||
| 61 | &log_sql_logio_module); | ||
| 62 | |||
| 63 | cf->bytes_in = cf->bytes_out = 0; | ||
| 64 | |||
| 65 | return OK; | ||
| 66 | } | ||
| 67 | |||
| 68 | static apr_status_t logio_in_filter(ap_filter_t *f, | ||
| 69 | apr_bucket_brigade *bb, | ||
| 70 | ap_input_mode_t mode, | ||
| 71 | apr_read_type_e block, | ||
| 72 | apr_off_t readbytes) { | ||
| 73 | apr_off_t length; | ||
| 74 | apr_status_t status; | ||
| 75 | logio_config_t *cf = ap_get_module_config(f->c->conn_config, &log_sql_logio_module); | ||
| 76 | |||
| 77 | status = ap_get_brigade(f->next, bb, mode, block, readbytes); | ||
| 78 | |||
| 79 | apr_brigade_length (bb, 0, &length); | ||
| 80 | |||
| 81 | if (length > 0) | ||
| 82 | cf->bytes_in += length; | ||
| 83 | |||
| 84 | return status; | ||
| 85 | } | ||
| 86 | |||
| 87 | static apr_status_t logio_out_filter(ap_filter_t *f, | ||
| 88 | apr_bucket_brigade *bb) { | ||
| 89 | apr_bucket *b = APR_BRIGADE_LAST(bb); | ||
| 90 | |||
| 91 | /* End of data, make sure we flush */ | ||
| 92 | if (APR_BUCKET_IS_EOS(b)) { | ||
| 93 | APR_BUCKET_INSERT_BEFORE(b, | ||
| 94 | apr_bucket_flush_create(f->c->bucket_alloc)); | ||
| 95 | } | ||
| 96 | |||
| 97 | return ap_pass_brigade(f->next, bb); | ||
| 98 | } | ||
| 99 | |||
| 100 | static int logio_pre_conn(conn_rec *c, void *csd) { | ||
| 101 | logio_config_t *cf = apr_pcalloc(c->pool, sizeof(logio_config_t)); | ||
| 102 | |||
| 103 | ap_set_module_config(c->conn_config, &log_sql_logio_module, cf); | ||
| 104 | |||
| 105 | ap_add_input_filter(logio_filter_name, NULL, NULL, c); | ||
| 106 | ap_add_output_filter(logio_filter_name, NULL, NULL, c); | ||
| 107 | |||
| 108 | return OK; | ||
| 109 | } | ||
| 110 | |||
| 111 | static int post_config(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s) | ||
| 112 | { | ||
| 113 | log_sql_register_function(p, "bytes_in", log_bytes_in, LOGSQL_FUNCTION_REQ_FINAL); | ||
| 114 | log_sql_register_function(p, "bytes_out", log_bytes_out, LOGSQL_FUNCTION_REQ_FINAL); | ||
| 115 | |||
| 116 | log_sql_register_alias(s,p,'i', "bytes_in"); | ||
| 117 | log_sql_register_alias(s,p,'o', "bytes_out"); | ||
| 118 | |||
| 119 | log_sql_register_field(p, "bytes_in", "bytes_in", NULL, | ||
| 120 | "bytes_in", LOGSQL_DATATYPE_INT, 0); | ||
| 121 | log_sql_register_field(p, "bytes_out", "bytes_out", NULL, | ||
| 122 | "bytes_out", LOGSQL_DATATYPE_INT, 0); | ||
| 123 | |||
| 124 | log_sql_register_finish(s); | ||
| 125 | return OK; | ||
| 126 | } | ||
| 127 | |||
| 128 | static void register_hooks(apr_pool_t *p) { | ||
| 129 | static const char *pre[] = { "mod_log_sql.c", NULL }; | ||
| 130 | |||
| 131 | ap_hook_pre_connection(logio_pre_conn, NULL, NULL, APR_HOOK_MIDDLE); | ||
| 132 | ap_hook_log_transaction(logio_transaction, pre, NULL, APR_HOOK_MIDDLE); | ||
| 133 | ap_hook_post_config(post_config, NULL, NULL, APR_HOOK_REALLY_FIRST); | ||
| 134 | |||
| 135 | ap_register_input_filter(logio_filter_name, logio_in_filter, NULL, | ||
| 136 | AP_FTYPE_NETWORK - 1); | ||
| 137 | ap_register_output_filter(logio_filter_name, logio_out_filter, NULL, | ||
| 138 | AP_FTYPE_NETWORK - 1); | ||
| 139 | |||
| 140 | APR_REGISTER_OPTIONAL_FN(ap_logio_add_bytes_out); | ||
| 141 | } | ||
| 142 | |||
| 143 | module AP_MODULE_DECLARE_DATA log_sql_logio_module = { | ||
| 144 | STANDARD20_MODULE_STUFF, | ||
| 145 | NULL, NULL, NULL, NULL, NULL, register_hooks | ||
| 146 | }; | ||
diff --git a/src/mod_log_sql_mysql.c b/src/mod_log_sql_mysql.c new file mode 100644 index 0000000..902cadf --- /dev/null +++ b/src/mod_log_sql_mysql.c | |||
| @@ -0,0 +1,269 @@ | |||
| 1 | /* $Id:mod_log_sql_mysql.c 180 2008-09-21 15:54:12Z urkle@drip.ws $ */ | ||
| 2 | |||
| 3 | #if defined(WITH_APACHE20) | ||
| 4 | # include "apache20.h" | ||
| 5 | #elif defined(WITH_APACHE13) | ||
| 6 | # include "apache13.h" | ||
| 7 | #else | ||
| 8 | # error Unsupported Apache version | ||
| 9 | #endif | ||
| 10 | |||
| 11 | #ifdef HAVE_CONFIG_H | ||
| 12 | /* Undefine these to prevent conflicts between Apache ap_config_auto.h and | ||
| 13 | * my config.h. Only really needed for Apache < 2.0.48, but it can't hurt. | ||
| 14 | */ | ||
| 15 | #undef PACKAGE_BUGREPORT | ||
| 16 | #undef PACKAGE_NAME | ||
| 17 | #undef PACKAGE_STRING | ||
| 18 | #undef PACKAGE_TARNAME | ||
| 19 | #undef PACKAGE_VERSION | ||
| 20 | |||
| 21 | #include "autoconfig.h" | ||
| 22 | #endif | ||
| 23 | |||
| 24 | #include "mod_log_sql.h" | ||
| 25 | |||
| 26 | #include "mysql.h" | ||
| 27 | #include "mysqld_error.h" | ||
| 28 | |||
| 29 | /* The enduser won't modify these */ | ||
| 30 | #define MYSQL_ERROR(mysql) ((mysql)?(mysql_error(mysql)):"MySQL server has gone away") | ||
| 31 | |||
| 32 | /* Connect to the MYSQL database */ | ||
| 33 | static logsql_opendb_ret log_sql_mysql_connect(server_rec *s, logsql_dbconnection *db) | ||
| 34 | { | ||
| 35 | const char *host = apr_table_get(db->parms,"hostname"); | ||
| 36 | const char *user = apr_table_get(db->parms,"username"); | ||
| 37 | const char *passwd = apr_table_get(db->parms,"password"); | ||
| 38 | const char *database = apr_table_get(db->parms,"database"); | ||
| 39 | const char *s_tcpport = apr_table_get(db->parms,"port"); | ||
| 40 | unsigned int tcpport = (s_tcpport)?atoi(s_tcpport):3306; | ||
| 41 | const char *socketfile = apr_table_get(db->parms,"socketfile"); | ||
| 42 | MYSQL *dblink = db->handle; | ||
| 43 | |||
| 44 | dblink = mysql_init(dblink); | ||
| 45 | db->handle = (void *)dblink; | ||
| 46 | |||
| 47 | |||
| 48 | if (!socketfile) { | ||
| 49 | socketfile = "/var/lib/mysql/mysql.sock"; | ||
| 50 | } | ||
| 51 | |||
| 52 | if (mysql_real_connect(dblink, host, user, passwd, database, tcpport, | ||
| 53 | socketfile, 0)) { | ||
| 54 | log_error(APLOG_MARK,APLOG_DEBUG,0, s,"HOST: '%s' PORT: '%d' DB: '%s' USER: '%s' SOCKET: '%s'", | ||
| 55 | host, tcpport, database, user, socketfile); | ||
| 56 | return LOGSQL_OPENDB_SUCCESS; | ||
| 57 | } else { | ||
| 58 | log_error(APLOG_MARK,APLOG_ERR,0, s,"mod_log_sql_mysql: database connection error: mysql error: %s", | ||
| 59 | MYSQL_ERROR(dblink)); | ||
| 60 | log_error(APLOG_MARK,APLOG_DEBUG, 0, s,"HOST: '%s' PORT: '%d' DB: '%s' USER: '%s' SOCKET: '%s'", | ||
| 61 | host, tcpport, database, user, socketfile); | ||
| 62 | return LOGSQL_OPENDB_FAIL; | ||
| 63 | } | ||
| 64 | } | ||
| 65 | |||
| 66 | /* Close the DB link */ | ||
| 67 | static void log_sql_mysql_close(logsql_dbconnection *db) | ||
| 68 | { | ||
| 69 | mysql_close((MYSQL *)db->handle); | ||
| 70 | /* mysql_close frees this data so NULL it out incase we reconnect later */ | ||
| 71 | db->handle=NULL; | ||
| 72 | } | ||
| 73 | |||
| 74 | /* Routine to escape the 'dangerous' characters that would otherwise | ||
| 75 | * corrupt the INSERT string: ', \, and " | ||
| 76 | */ | ||
| 77 | static const char *log_sql_mysql_escape(request_rec *r, const char *from_str, apr_pool_t *p, | ||
| 78 | logsql_dbconnection *db) | ||
| 79 | { | ||
| 80 | /* Return "NULL" for empty strings */ | ||
| 81 | if (!from_str || strlen(from_str) == 0) | ||
| 82 | return "NULL"; | ||
| 83 | else { | ||
| 84 | char *to_str; | ||
| 85 | unsigned long length = strlen(from_str); | ||
| 86 | unsigned long retval; | ||
| 87 | |||
| 88 | /* Pre-allocate a new string that could hold twice the original, which would only | ||
| 89 | * happen if the whole original string was 'dangerous' characters. | ||
| 90 | */ | ||
| 91 | to_str = (char *) apr_palloc(p, length * 2 + 3); | ||
| 92 | if (!to_str) { | ||
| 93 | return from_str; | ||
| 94 | } | ||
| 95 | strcpy(to_str, "'"); | ||
| 96 | if (!db->connected) { | ||
| 97 | /* Well, I would have liked to use the current database charset. mysql is | ||
| 98 | * unavailable, however, so I fall back to the slightly less respectful | ||
| 99 | * mysql_escape_string() function that uses the default charset. | ||
| 100 | */ | ||
| 101 | retval = mysql_escape_string(to_str+1, from_str, length); | ||
| 102 | } else { | ||
| 103 | /* MySQL is available, so I'll go ahead and respect the current charset when | ||
| 104 | * I perform the escape. | ||
| 105 | */ | ||
| 106 | retval = mysql_real_escape_string((MYSQL *)db->handle, to_str+1, from_str, length); | ||
| 107 | } | ||
| 108 | strcat(to_str,"'"); | ||
| 109 | |||
| 110 | if (retval) | ||
| 111 | return to_str; | ||
| 112 | else | ||
| 113 | return from_str; | ||
| 114 | } | ||
| 115 | } | ||
| 116 | |||
| 117 | #if defined(WIN32) | ||
| 118 | #define SIGNAL_GRAB | ||
| 119 | #define SIGNAL_RELEASE | ||
| 120 | #define SIGNAL_VAR | ||
| 121 | #else | ||
| 122 | #define SIGNAL_VAR void (*handler) (int); | ||
| 123 | #define SIGNAL_GRAB handler = signal(SIGPIPE, SIG_IGN); | ||
| 124 | #define SIGNAL_RELEASE signal(SIGPIPE, handler); | ||
| 125 | #endif | ||
| 126 | /* Run a mysql insert query and return a categorized error or success */ | ||
| 127 | static logsql_query_ret log_sql_mysql_query(request_rec *r,logsql_dbconnection *db, | ||
| 128 | const char *query) | ||
| 129 | { | ||
| 130 | int retval; | ||
| 131 | SIGNAL_VAR | ||
| 132 | |||
| 133 | unsigned int real_error = 0; | ||
| 134 | /*const char *real_error_str = NULL;*/ | ||
| 135 | |||
| 136 | MYSQL *dblink = (MYSQL *)db->handle; | ||
| 137 | |||
| 138 | if (!dblink) { | ||
| 139 | return LOGSQL_QUERY_NOLINK; | ||
| 140 | } | ||
| 141 | |||
| 142 | /* A failed mysql_query() may send a SIGPIPE, so we ignore that signal momentarily. */ | ||
| 143 | SIGNAL_GRAB | ||
| 144 | |||
| 145 | /* Run the query */ | ||
| 146 | if (!(retval = mysql_query(dblink, query))) { | ||
| 147 | SIGNAL_RELEASE | ||
| 148 | return LOGSQL_QUERY_SUCCESS; | ||
| 149 | } | ||
| 150 | real_error = mysql_errno(dblink); | ||
| 151 | log_error(APLOG_MARK, APLOG_ERR, 0, r->server, | ||
| 152 | "mysql_query returned (%d) \"%s\"", real_error, MYSQL_ERROR(dblink)); | ||
| 153 | /* Check to see if the error is "nonexistent table" */ | ||
| 154 | |||
| 155 | if (real_error == ER_NO_SUCH_TABLE) { | ||
| 156 | log_error(APLOG_MARK,APLOG_ERR,0, r->server,"table does not exist, preserving query"); | ||
| 157 | /* Restore SIGPIPE to its original handler function */ | ||
| 158 | SIGNAL_RELEASE | ||
| 159 | return LOGSQL_QUERY_NOTABLE; | ||
| 160 | } | ||
| 161 | |||
| 162 | /* Restore SIGPIPE to its original handler function */ | ||
| 163 | SIGNAL_RELEASE | ||
| 164 | return LOGSQL_QUERY_FAIL; | ||
| 165 | } | ||
| 166 | |||
| 167 | /* Create table table_name of type table_type. */ | ||
| 168 | static logsql_table_ret log_sql_mysql_create(request_rec *r, logsql_dbconnection *db, | ||
| 169 | logsql_tabletype table_type, const char *table_name) | ||
| 170 | { | ||
| 171 | int retval; | ||
| 172 | const char *tabletype = apr_table_get(db->parms,"tabletype"); | ||
| 173 | SIGNAL_VAR | ||
| 174 | char *type_suffix = NULL; | ||
| 175 | |||
| 176 | char *create_prefix = "create table if not exists `"; | ||
| 177 | char *create_suffix = NULL; | ||
| 178 | char *create_sql; | ||
| 179 | |||
| 180 | MYSQL *dblink = (MYSQL *)db->handle; | ||
| 181 | |||
| 182 | /* if (!global_config.createtables) { | ||
| 183 | return APR_SUCCESS; | ||
| 184 | }*/ | ||
| 185 | |||
| 186 | switch (table_type) { | ||
| 187 | case LOGSQL_TABLE_ACCESS: | ||
| 188 | create_suffix = | ||
| 189 | "` (id char(19),\ | ||
| 190 | agent varchar(255),\ | ||
| 191 | bytes_sent int unsigned,\ | ||
| 192 | child_pid smallint unsigned,\ | ||
| 193 | cookie varchar(255),\ | ||
| 194 | machine_id varchar(25),\ | ||
| 195 | request_file varchar(255),\ | ||
| 196 | referer varchar(255),\ | ||
| 197 | remote_host varchar(50),\ | ||
| 198 | remote_logname varchar(50),\ | ||
| 199 | remote_user varchar(50),\ | ||
| 200 | request_duration smallint unsigned,\ | ||
| 201 | request_line varchar(255),\ | ||
| 202 | request_method varchar(10),\ | ||
| 203 | request_protocol varchar(10),\ | ||
| 204 | request_time char(28),\ | ||
| 205 | request_uri varchar(255),\ | ||
| 206 | request_args varchar(255),\ | ||
| 207 | server_port smallint unsigned,\ | ||
| 208 | ssl_cipher varchar(25),\ | ||
| 209 | ssl_keysize smallint unsigned,\ | ||
| 210 | ssl_maxkeysize smallint unsigned,\ | ||
| 211 | status smallint unsigned,\ | ||
| 212 | time_stamp int unsigned,\ | ||
| 213 | virtual_host varchar(255),\ | ||
| 214 | bytes_in int unsigned,\ | ||
| 215 | bytes_out int unsigned)"; | ||
| 216 | break; | ||
| 217 | case LOGSQL_TABLE_COOKIES: | ||
| 218 | case LOGSQL_TABLE_HEADERSIN: | ||
| 219 | case LOGSQL_TABLE_HEADERSOUT: | ||
| 220 | case LOGSQL_TABLE_NOTES: | ||
| 221 | create_suffix = | ||
| 222 | "` (id char(19),\ | ||
| 223 | item varchar(80),\ | ||
| 224 | val varchar(80))"; | ||
| 225 | break; | ||
| 226 | } | ||
| 227 | |||
| 228 | if (tabletype) { | ||
| 229 | type_suffix = apr_pstrcat(r->pool, " TYPE=", | ||
| 230 | tabletype, NULL); | ||
| 231 | } | ||
| 232 | /* Find memory long enough to hold the whole CREATE string + \0 */ | ||
| 233 | create_sql = apr_pstrcat(r->pool, create_prefix, table_name, create_suffix, | ||
| 234 | type_suffix, NULL); | ||
| 235 | |||
| 236 | log_error(APLOG_MARK,APLOG_DEBUG,0, r->server,"create string: %s", create_sql); | ||
| 237 | |||
| 238 | if (!dblink) { | ||
| 239 | return LOGSQL_QUERY_NOLINK; | ||
| 240 | } | ||
| 241 | /* A failed mysql_query() may send a SIGPIPE, so we ignore that signal momentarily. */ | ||
| 242 | SIGNAL_GRAB | ||
| 243 | |||
| 244 | /* Run the create query */ | ||
| 245 | if ((retval = mysql_query(dblink, create_sql))) { | ||
| 246 | log_error(APLOG_MARK,APLOG_ERR,0, r->server,"failed to create table: %s", | ||
| 247 | table_name); | ||
| 248 | SIGNAL_RELEASE | ||
| 249 | return LOGSQL_TABLE_FAIL; | ||
| 250 | } | ||
| 251 | SIGNAL_RELEASE | ||
| 252 | return LOGSQL_TABLE_SUCCESS; | ||
| 253 | } | ||
| 254 | |||
| 255 | static const char *supported_drivers[] = {"mysql",NULL}; | ||
| 256 | static logsql_dbdriver mysql_driver = { | ||
| 257 | "mysql", | ||
| 258 | supported_drivers, | ||
| 259 | log_sql_mysql_connect, /* open DB connection */ | ||
| 260 | log_sql_mysql_close, /* close DB connection */ | ||
| 261 | log_sql_mysql_escape, /* escape query */ | ||
| 262 | log_sql_mysql_query, /* insert query */ | ||
| 263 | log_sql_mysql_create /* create table */ | ||
| 264 | }; | ||
| 265 | |||
| 266 | LOGSQL_REGISTER(mysql) { | ||
| 267 | log_sql_register_driver(p,&mysql_driver); | ||
| 268 | LOGSQL_REGISTER_RETURN; | ||
| 269 | } | ||
diff --git a/src/mod_log_sql_pgsql.c b/src/mod_log_sql_pgsql.c new file mode 100644 index 0000000..dcfbd73 --- /dev/null +++ b/src/mod_log_sql_pgsql.c | |||
| @@ -0,0 +1,247 @@ | |||
| 1 | /* $Id:mod_log_sql_pgsql.c 180 2008-09-21 15:54:12Z urkle@drip.ws $ */ | ||
| 2 | |||
| 3 | #if defined(WITH_APACHE20) | ||
| 4 | # include "apache20.h" | ||
| 5 | #elif defined(WITH_APACHE13) | ||
| 6 | # include "apache13.h" | ||
| 7 | #else | ||
| 8 | # error Unsupported Apache version | ||
| 9 | #endif | ||
| 10 | |||
| 11 | |||
| 12 | #ifdef HAVE_CONFIG_H | ||
| 13 | /* Undefine these to prevent conflicts between Apache ap_config_auto.h and | ||
| 14 | * my config.h. Only really needed for Apache < 2.0.48, but it can't hurt. | ||
| 15 | */ | ||
| 16 | #undef PACKAGE_BUGREPORT | ||
| 17 | #undef PACKAGE_NAME | ||
| 18 | #undef PACKAGE_STRING | ||
| 19 | #undef PACKAGE_TARNAME | ||
| 20 | #undef PACKAGE_VERSION | ||
| 21 | |||
| 22 | #include "autoconfig.h" | ||
| 23 | #endif | ||
| 24 | |||
| 25 | #include "mod_log_sql.h" | ||
| 26 | |||
| 27 | #include "libpq-fe.h" | ||
| 28 | |||
| 29 | /* Connect to the PGSQL database */ | ||
| 30 | static logsql_opendb_ret log_sql_pgsql_connect(server_rec *s, logsql_dbconnection *db) | ||
| 31 | { | ||
| 32 | const char *host = apr_table_get(db->parms,"hostname"); | ||
| 33 | const char *user = apr_table_get(db->parms,"username"); | ||
| 34 | const char *passwd = apr_table_get(db->parms,"password"); | ||
| 35 | const char *database = apr_table_get(db->parms,"database"); | ||
| 36 | const char *s_tcpport = apr_table_get(db->parms,"port"); | ||
| 37 | |||
| 38 | db->handle = PQsetdbLogin(host, s_tcpport, NULL, NULL, database, user, passwd); | ||
| 39 | |||
| 40 | if (PQstatus(db->handle) == CONNECTION_OK) { | ||
| 41 | log_error(APLOG_MARK,APLOG_DEBUG,0, s,"HOST: '%s' PORT: '%s' DB: '%s' USER: '%s'", | ||
| 42 | host, s_tcpport, database, user); | ||
| 43 | return LOGSQL_OPENDB_SUCCESS; | ||
| 44 | } else { | ||
| 45 | log_error(APLOG_MARK,APLOG_DEBUG,0, s,"mod_log_sql: database connection error: %s", | ||
| 46 | PQerrorMessage(db->handle)); | ||
| 47 | log_error(APLOG_MARK,APLOG_DEBUG, 0, s,"HOST: '%s' PORT: '%s' DB: '%s' USER: '%s'", | ||
| 48 | host, s_tcpport, database, user); | ||
| 49 | return LOGSQL_OPENDB_FAIL; | ||
| 50 | } | ||
| 51 | } | ||
| 52 | |||
| 53 | /* Close the DB link */ | ||
| 54 | static void log_sql_pgsql_close(logsql_dbconnection *db) | ||
| 55 | { | ||
| 56 | PQfinish((PGconn*)(db->handle)); | ||
| 57 | } | ||
| 58 | |||
| 59 | /* Routine to escape the 'dangerous' characters that would otherwise | ||
| 60 | * corrupt the INSERT string: ', \, and " | ||
| 61 | * Also PQescapeString does not place the ' around the string. So we have | ||
| 62 | * to do this manually | ||
| 63 | */ | ||
| 64 | static const char *log_sql_pgsql_escape(const char *from_str, apr_pool_t *p, | ||
| 65 | logsql_dbconnection *db) | ||
| 66 | { | ||
| 67 | char *temp; | ||
| 68 | if (!from_str) | ||
| 69 | return NULL; | ||
| 70 | else { | ||
| 71 | char *to_str; | ||
| 72 | unsigned long length = strlen(from_str); | ||
| 73 | unsigned long retval; | ||
| 74 | |||
| 75 | /* Pre-allocate a new string that could hold twice the original, which would only | ||
| 76 | * happen if the whole original string was 'dangerous' characters. | ||
| 77 | * And forsee the space for the 2 ' | ||
| 78 | */ | ||
| 79 | temp = to_str = (char *) apr_palloc(p, length * 2 + 3); | ||
| 80 | if (!to_str) { | ||
| 81 | return from_str; | ||
| 82 | } | ||
| 83 | |||
| 84 | *temp = '\''; | ||
| 85 | temp++; | ||
| 86 | |||
| 87 | retval = PQescapeString(temp, from_str, length); | ||
| 88 | |||
| 89 | /* avoid the string to be tolong for the sql database*/ | ||
| 90 | if (retval > 250) retval = 250; | ||
| 91 | |||
| 92 | *(temp+retval) = '\''; | ||
| 93 | *(temp+retval+1) = '\0'; | ||
| 94 | |||
| 95 | /* We must always return the to_str, because we always need the ' added */ | ||
| 96 | // if (retval) | ||
| 97 | return to_str; | ||
| 98 | // else | ||
| 99 | // return from_str; | ||
| 100 | } | ||
| 101 | } | ||
| 102 | |||
| 103 | /* Run a sql insert query and return a categorized error or success */ | ||
| 104 | static logsql_query_ret log_sql_pgsql_query(request_rec *r,logsql_dbconnection *db, | ||
| 105 | const char *query) | ||
| 106 | { | ||
| 107 | PGresult *result; | ||
| 108 | void (*handler) (int); | ||
| 109 | unsigned int real_error = 0; | ||
| 110 | /*const char *real_error_str = NULL;*/ | ||
| 111 | |||
| 112 | PGconn *conn = db->handle; | ||
| 113 | |||
| 114 | if (PQstatus(conn) != CONNECTION_OK) { | ||
| 115 | return LOGSQL_QUERY_NOLINK; | ||
| 116 | } | ||
| 117 | /* A failed mysql_query() may send a SIGPIPE, so we ignore that signal momentarily. */ | ||
| 118 | /* Does postgresql do this also ??? */ | ||
| 119 | handler = signal(SIGPIPE, SIG_IGN); | ||
| 120 | |||
| 121 | result = PQexec(conn, query); | ||
| 122 | /* Run the query */ | ||
| 123 | if (PQresultStatus(result) == PGRES_COMMAND_OK) { | ||
| 124 | signal(SIGPIPE, handler); | ||
| 125 | PQclear(result); | ||
| 126 | return LOGSQL_QUERY_SUCCESS; | ||
| 127 | } | ||
| 128 | /* Check to see if the error is "nonexistent table" */ | ||
| 129 | /* removed ... don't know how ! (sorry) | ||
| 130 | real_error = mysql_errno(dblink); | ||
| 131 | |||
| 132 | if (real_error == ER_NO_SUCH_TABLE) { | ||
| 133 | log_error(APLOG_MARK,APLOG_ERR,0, r->server,"table does not exist, preserving query"); | ||
| 134 | signal(SIGPIPE, handler); | ||
| 135 | PQclear(result); | ||
| 136 | return LOGSQL_QUERY_NOTABLE; | ||
| 137 | }*/ | ||
| 138 | |||
| 139 | /* Restore SIGPIPE to its original handler function */ | ||
| 140 | signal(SIGPIPE, handler); | ||
| 141 | PQclear(result); | ||
| 142 | return LOGSQL_QUERY_FAIL; | ||
| 143 | } | ||
| 144 | |||
| 145 | /* Create table table_name of type table_type. */ | ||
| 146 | static logsql_table_ret log_sql_pgsql_create(request_rec *r, logsql_dbconnection *db, | ||
| 147 | logsql_tabletype table_type, const char *table_name) | ||
| 148 | { | ||
| 149 | PGresult *result; | ||
| 150 | const char *tabletype = apr_table_get(db->parms,"tabletype"); | ||
| 151 | void (*handler) (int); | ||
| 152 | char *type_suffix = NULL; | ||
| 153 | |||
| 154 | char *create_prefix = "create table if not exists `"; | ||
| 155 | char *create_suffix = NULL; | ||
| 156 | char *create_sql; | ||
| 157 | |||
| 158 | PGconn *conn = db->handle; | ||
| 159 | |||
| 160 | /* if (!global_config.createtables) { | ||
| 161 | return APR_SUCCESS; | ||
| 162 | }*/ | ||
| 163 | |||
| 164 | switch (table_type) { | ||
| 165 | case LOGSQL_TABLE_ACCESS: | ||
| 166 | create_suffix = | ||
| 167 | "` (id char(19),\ | ||
| 168 | agent varchar(255),\ | ||
| 169 | bytes_sent int unsigned,\ | ||
| 170 | child_pid smallint unsigned,\ | ||
| 171 | cookie varchar(255),\ | ||
| 172 | machine_id varchar(25),\ | ||
| 173 | request_file varchar(255),\ | ||
| 174 | referer varchar(255),\ | ||
| 175 | remote_host varchar(50),\ | ||
| 176 | remote_logname varchar(50),\ | ||
| 177 | remote_user varchar(50),\ | ||
| 178 | request_duration smallint unsigned,\ | ||
| 179 | request_line varchar(255),\ | ||
| 180 | request_method varchar(10),\ | ||
| 181 | request_protocol varchar(10),\ | ||
| 182 | request_time char(28),\ | ||
| 183 | request_uri varchar(255),\ | ||
| 184 | request_args varchar(255),\ | ||
| 185 | server_port smallint unsigned,\ | ||
| 186 | ssl_cipher varchar(25),\ | ||
| 187 | ssl_keysize smallint unsigned,\ | ||
| 188 | ssl_maxkeysize smallint unsigned,\ | ||
| 189 | status smallint unsigned,\ | ||
| 190 | time_stamp int unsigned,\ | ||
| 191 | virtual_host varchar(255))"; | ||
| 192 | break; | ||
| 193 | case LOGSQL_TABLE_COOKIES: | ||
| 194 | case LOGSQL_TABLE_HEADERSIN: | ||
| 195 | case LOGSQL_TABLE_HEADERSOUT: | ||
| 196 | case LOGSQL_TABLE_NOTES: | ||
| 197 | create_suffix = | ||
| 198 | "` (id char(19),\ | ||
| 199 | item varchar(80),\ | ||
| 200 | val varchar(80))"; | ||
| 201 | break; | ||
| 202 | } | ||
| 203 | |||
| 204 | if (tabletype) { | ||
| 205 | type_suffix = apr_pstrcat(r->pool, " TYPE=", | ||
| 206 | tabletype, NULL); | ||
| 207 | } | ||
| 208 | /* Find memory long enough to hold the whole CREATE string + \0 */ | ||
| 209 | create_sql = apr_pstrcat(r->pool, create_prefix, table_name, create_suffix, | ||
| 210 | type_suffix, NULL); | ||
| 211 | |||
| 212 | log_error(APLOG_MARK,APLOG_DEBUG,0, r->server,"create string: %s", create_sql); | ||
| 213 | |||
| 214 | if (PQstatus(conn) != CONNECTION_OK) { | ||
| 215 | return LOGSQL_QUERY_NOLINK; | ||
| 216 | } | ||
| 217 | /* A failed mysql_query() may send a SIGPIPE, so we ignore that signal momentarily. */ | ||
| 218 | handler = signal(SIGPIPE, SIG_IGN); | ||
| 219 | |||
| 220 | /* Run the create query */ | ||
| 221 | result = PQexec(conn, create_sql); | ||
| 222 | if (PQresultStatus(result) != PGRES_COMMAND_OK) { | ||
| 223 | log_error(APLOG_MARK,APLOG_ERR,0, r->server,"failed to create table: %s", | ||
| 224 | table_name); | ||
| 225 | signal(SIGPIPE, handler); | ||
| 226 | PQclear(result); | ||
| 227 | return LOGSQL_TABLE_FAIL; | ||
| 228 | } | ||
| 229 | signal(SIGPIPE, handler); | ||
| 230 | PQclear(result); | ||
| 231 | return LOGSQL_TABLE_SUCCESS; | ||
| 232 | } | ||
| 233 | |||
| 234 | static char *supported_drivers[] = {"pgsql",NULL}; | ||
| 235 | static logsql_dbdriver pgsql_driver = { | ||
| 236 | supported_drivers, | ||
| 237 | log_sql_pgsql_connect, /* open DB connection */ | ||
| 238 | log_sql_pgsql_close, /* close DB connection */ | ||
| 239 | log_sql_pgsql_escape, /* escape query */ | ||
| 240 | log_sql_pgsql_query, /* insert query */ | ||
| 241 | log_sql_pgsql_create /* create table */ | ||
| 242 | }; | ||
| 243 | |||
| 244 | LOGSQL_REGISTER(pgsql) { | ||
| 245 | log_sql_register_driver(p,&pgsql_driver); | ||
| 246 | LOGSQL_REGISTER_RETURN; | ||
| 247 | } | ||
diff --git a/src/mod_log_sql_ssl.c b/src/mod_log_sql_ssl.c new file mode 100644 index 0000000..b3a4929 --- /dev/null +++ b/src/mod_log_sql_ssl.c | |||
| @@ -0,0 +1,106 @@ | |||
| 1 | /* $Id:mod_log_sql_ssl.c 180 2008-09-21 15:54:12Z urkle@drip.ws $ */ | ||
| 2 | |||
| 3 | #if defined(WITH_APACHE20) | ||
| 4 | # include "apache20.h" | ||
| 5 | #elif defined(WITH_APACHE13) | ||
| 6 | # include "apache13.h" | ||
| 7 | #else | ||
| 8 | # error Unsupported Apache version | ||
| 9 | #endif | ||
| 10 | |||
| 11 | #ifdef HAVE_CONFIG_H | ||
| 12 | /* Undefine these to prevent conflicts between Apache ap_config_auto.h and | ||
| 13 | * my config.h. Only really needed for Apache < 2.0.48, but it can't hurt. | ||
| 14 | */ | ||
| 15 | #undef PACKAGE_BUGREPORT | ||
| 16 | #undef PACKAGE_NAME | ||
| 17 | #undef PACKAGE_STRING | ||
| 18 | #undef PACKAGE_TARNAME | ||
| 19 | #undef PACKAGE_VERSION | ||
| 20 | |||
| 21 | #include "autoconfig.h" | ||
| 22 | #endif | ||
| 23 | |||
| 24 | #include "mod_log_sql.h" | ||
| 25 | |||
| 26 | #include "mod_ssl.h" | ||
| 27 | |||
| 28 | #if defined(WITH_APACHE20) | ||
| 29 | static APR_OPTIONAL_FN_TYPE(ssl_var_lookup) *header_ssl_lookup = NULL; | ||
| 30 | # define TEST_SSL(r) header_ssl_lookup | ||
| 31 | #elif defined(WITH_APACHE13) | ||
| 32 | # define TEST_SSL(r) ap_ctx_get(r->connection->client->ctx, "ssl") | ||
| 33 | # define header_ssl_lookup ssl_var_lookup | ||
| 34 | #endif | ||
| 35 | |||
| 36 | static const char *extract_ssl_keysize(request_rec *r, char *a) | ||
| 37 | { | ||
| 38 | char *result = NULL; | ||
| 39 | if (TEST_SSL(r) != NULL) | ||
| 40 | { | ||
| 41 | result = header_ssl_lookup(r->pool, r->server, r->connection, r, "SSL_CIPHER_USEKEYSIZE"); | ||
| 42 | log_error(APLOG_MARK,APLOG_DEBUG,0, r->server,"SSL_KEYSIZE: %s", result); | ||
| 43 | if (result && result[0]) | ||
| 44 | return result; | ||
| 45 | else | ||
| 46 | return "0"; | ||
| 47 | } else { | ||
| 48 | return "0"; | ||
| 49 | } | ||
| 50 | } | ||
| 51 | |||
| 52 | static const char *extract_ssl_maxkeysize(request_rec *r, char *a) | ||
| 53 | { | ||
| 54 | char *result = NULL; | ||
| 55 | if (TEST_SSL(r) != NULL) | ||
| 56 | { | ||
| 57 | result = header_ssl_lookup(r->pool, r->server, r->connection, r, "SSL_CIPHER_ALGKEYSIZE"); | ||
| 58 | log_error(APLOG_MARK,APLOG_DEBUG,0, r->server,"SSL_ALGKEYSIZE: %s", result); | ||
| 59 | if (result && result[0]) | ||
| 60 | return result; | ||
| 61 | else | ||
| 62 | return "0"; | ||
| 63 | } else { | ||
| 64 | return "0"; | ||
| 65 | } | ||
| 66 | } | ||
| 67 | |||
| 68 | static const char *extract_ssl_cipher(request_rec *r, char *a) | ||
| 69 | { | ||
| 70 | char *result = NULL; | ||
| 71 | if (TEST_SSL(r) != NULL) | ||
| 72 | { | ||
| 73 | result = header_ssl_lookup(r->pool, r->server, r->connection, r, "SSL_CIPHER"); | ||
| 74 | log_error(APLOG_MARK,APLOG_DEBUG,0, r->server,"SSL_CIPHER: %s", result); | ||
| 75 | if (result && result[0]) | ||
| 76 | return result; | ||
| 77 | else | ||
| 78 | return "0"; | ||
| 79 | } else { | ||
| 80 | return "-"; | ||
| 81 | } | ||
| 82 | } | ||
| 83 | |||
| 84 | |||
| 85 | LOGSQL_REGISTER(ssl) | ||
| 86 | { | ||
| 87 | log_sql_register_function(p, "ssl_keysize", extract_ssl_keysize, LOGSQL_FUNCTION_REQ_FINAL); | ||
| 88 | log_sql_register_function(p, "ssl_maxkeysize", extract_ssl_maxkeysize, LOGSQL_FUNCTION_REQ_FINAL); | ||
| 89 | log_sql_register_function(p, "ssl_cipher", extract_ssl_cipher, LOGSQL_FUNCTION_REQ_FINAL); | ||
| 90 | |||
| 91 | log_sql_register_alias(s,p,'q', "ssl_keysize"); | ||
| 92 | log_sql_register_alias(s,p,'Q', "ssl_maxkeysize"); | ||
| 93 | log_sql_register_alias(s,p,'z', "ssl_cipher"); | ||
| 94 | |||
| 95 | log_sql_register_field(p, "ssl_keysize", "ssl_keysize", NULL, | ||
| 96 | "ssl_keysize", LOGSQL_DATATYPE_VARCHAR, 0); | ||
| 97 | log_sql_register_field(p, "ssl_maxkeysize", "ssl_maxkeysize", NULL, | ||
| 98 | "ssl_maxkeysize", LOGSQL_DATATYPE_VARCHAR, 0); | ||
| 99 | log_sql_register_field(p, "ssl_cipher", "ssl_cipher", NULL, | ||
| 100 | "ssl_cipher", LOGSQL_DATATYPE_VARCHAR, 0); | ||
| 101 | |||
| 102 | #if defined(WITH_APACHE20) | ||
| 103 | header_ssl_lookup = APR_RETRIEVE_OPTIONAL_FN(ssl_var_lookup); | ||
| 104 | #endif | ||
| 105 | LOGSQL_REGISTER_RETURN; | ||
| 106 | } | ||
diff --git a/utility/Makefile.in b/utility/Makefile.in new file mode 100644 index 0000000..fdc3846 --- /dev/null +++ b/utility/Makefile.in | |||
| @@ -0,0 +1,49 @@ | |||
| 1 | # @configure_input@ | ||
| 2 | |||
| 3 | top_srcdir = @top_srcdir@ | ||
| 4 | srcdir = @abs_srcdir@ | ||
| 5 | top_builddir = @top_builddir@ | ||
| 6 | builddir = @abs_builddir@ | ||
| 7 | |||
| 8 | LIBTOOL=@LIBTOOL@ | ||
| 9 | CFLAGS = -g3 -Wall -fno-strict-aliasing \ | ||
| 10 | @APR_INCLUDES@ @APU_INCLUDES@ @PCRE_CFLAGS@ \ | ||
| 11 | -I$(top_srcdir)/include | ||
| 12 | CPPFLAGS = @APR_CPPFLAGS@ | ||
| 13 | LDFLAGS = @APR_LIBTOOL@ @APU_LIBTOOL@ @PCRE_LIBS@ | ||
| 14 | |||
| 15 | ifeq (@OOO_MAINTAIN@,1) | ||
| 16 | CFLAGS += -Werror | ||
| 17 | endif | ||
| 18 | |||
| 19 | STD_DIST = Makefile.in | ||
| 20 | |||
| 21 | DISTFILES = $(STD_DIST) $(EXTRA_DIST) $(SOURCES) $(HEADERS) | ||
| 22 | |||
| 23 | SOURCES = shell.c config.c logparse.c ap_pcre.c util.c database.c | ||
| 24 | HEADERS = shell.h config.h logparse.h ap_pcre.h util.h database.h | ||
| 25 | OBJECTS = $(SOURCES:.c=.o) | ||
| 26 | DEPS = $(SOURCES:.c=.d) | ||
| 27 | TARGETS = mod_log_sql | ||
| 28 | |||
| 29 | all: $(TARGETS) | ||
| 30 | |||
| 31 | mod_log_sql: $(OBJECTS) $(HEADERS) | ||
| 32 | $(LIBTOOL) --tag=CC --mode=link $(CC) -o $@ $(OBJECTS) $(LDFLAGS) | ||
| 33 | |||
| 34 | %.o: %.c %.d | ||
| 35 | gcc -c $(CFLAGS) $(CPPFLAGS) $< -o $@ | ||
| 36 | %.d: %.c | ||
| 37 | gcc -MM $(CFLAGS) $(CPPFLAGS) $< -o $@ | ||
| 38 | |||
| 39 | -include $(DEPS) | ||
| 40 | |||
| 41 | clean: | ||
| 42 | $(RM) $(OBJECTS) $(TARGETS) $(DEPS) | ||
| 43 | |||
| 44 | local-dist: $(DISTFILES) | ||
| 45 | mkdir -p $(DESTDIR) | ||
| 46 | cp -dp --parents $(DISTFILES) $(DESTDIR) | ||
| 47 | |||
| 48 | Makefile: Makefile.in ../config.status | ||
| 49 | cd .. && ./config.status | ||
diff --git a/utility/ap_pcre.c b/utility/ap_pcre.c new file mode 100644 index 0000000..b2b9767 --- /dev/null +++ b/utility/ap_pcre.c | |||
| @@ -0,0 +1,344 @@ | |||
| 1 | /************************************************* | ||
| 2 | * Perl-Compatible Regular Expressions * | ||
| 3 | *************************************************/ | ||
| 4 | |||
| 5 | /* | ||
| 6 | This is a library of functions to support regular expressions whose syntax | ||
| 7 | and semantics are as close as possible to those of the Perl 5 language. See | ||
| 8 | the file Tech.Notes for some information on the internals. | ||
| 9 | |||
| 10 | This module is a wrapper that provides a POSIX API to the underlying PCRE | ||
| 11 | functions. | ||
| 12 | |||
| 13 | Written by: Philip Hazel <ph10@cam.ac.uk> | ||
| 14 | |||
| 15 | Copyright (c) 1997-2004 University of Cambridge | ||
| 16 | |||
| 17 | ----------------------------------------------------------------------------- | ||
| 18 | Redistribution and use in source and binary forms, with or without | ||
| 19 | modification, are permitted provided that the following conditions are met: | ||
| 20 | |||
| 21 | * Redistributions of source code must retain the above copyright notice, | ||
| 22 | this list of conditions and the following disclaimer. | ||
| 23 | |||
| 24 | * Redistributions in binary form must reproduce the above copyright | ||
| 25 | notice, this list of conditions and the following disclaimer in the | ||
| 26 | documentation and/or other materials provided with the distribution. | ||
| 27 | |||
| 28 | * Neither the name of the University of Cambridge nor the names of its | ||
| 29 | contributors may be used to endorse or promote products derived from | ||
| 30 | this software without specific prior written permission. | ||
| 31 | |||
| 32 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | ||
| 33 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||
| 34 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | ||
| 35 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE | ||
| 36 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | ||
| 37 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | ||
| 38 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | ||
| 39 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | ||
| 40 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | ||
| 41 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | ||
| 42 | POSSIBILITY OF SUCH DAMAGE. | ||
| 43 | ----------------------------------------------------------------------------- | ||
| 44 | */ | ||
| 45 | |||
| 46 | #include "apr_lib.h" | ||
| 47 | #include "apr_strings.h" | ||
| 48 | #include "ap_pcre.h" | ||
| 49 | #include "pcre.h" | ||
| 50 | |||
| 51 | #define APR_WANT_STRFUNC | ||
| 52 | #include "apr_want.h" | ||
| 53 | |||
| 54 | #ifndef POSIX_MALLOC_THRESHOLD | ||
| 55 | #define POSIX_MALLOC_THRESHOLD (10) | ||
| 56 | #endif | ||
| 57 | |||
| 58 | /* Table of error strings corresponding to POSIX error codes; must be | ||
| 59 | * kept in synch with include/ap_regex.h's AP_REG_E* definitions. */ | ||
| 60 | |||
| 61 | static const char *const pstring[] = { | ||
| 62 | "", /* Dummy for value 0 */ | ||
| 63 | "internal error", /* AP_REG_ASSERT */ | ||
| 64 | "failed to get memory", /* AP_REG_ESPACE */ | ||
| 65 | "bad argument", /* AP_REG_INVARG */ | ||
| 66 | "match failed" /* AP_REG_NOMATCH */ | ||
| 67 | }; | ||
| 68 | |||
| 69 | apr_size_t ap_regerror(int errcode, const ap_regex_t *preg, char *errbuf, | ||
| 70 | apr_size_t errbuf_size) | ||
| 71 | { | ||
| 72 | const char *message, *addmessage; | ||
| 73 | apr_size_t length, addlength; | ||
| 74 | |||
| 75 | message | ||
| 76 | = (errcode >= (int)(sizeof(pstring)/sizeof(char *))) ? "unknown error code" | ||
| 77 | : pstring[errcode]; | ||
| 78 | length = strlen(message) + 1; | ||
| 79 | |||
| 80 | addmessage = " at offset "; | ||
| 81 | addlength | ||
| 82 | = (preg != NULL && (int)preg->re_erroffset != -1) ? strlen(addmessage) | ||
| 83 | + 6 | ||
| 84 | : 0; | ||
| 85 | |||
| 86 | if (errbuf_size > 0) { | ||
| 87 | if (addlength > 0 && errbuf_size >= length + addlength) | ||
| 88 | apr_snprintf(errbuf, sizeof errbuf, "%s%s%-6d", message, | ||
| 89 | addmessage, (int)preg->re_erroffset); | ||
| 90 | else { | ||
| 91 | strncpy(errbuf, message, errbuf_size - 1); | ||
| 92 | errbuf[errbuf_size-1] = 0; | ||
| 93 | } | ||
| 94 | } | ||
| 95 | |||
| 96 | return length + addlength; | ||
| 97 | } | ||
| 98 | |||
| 99 | /************************************************* | ||
| 100 | * Free store held by a regex * | ||
| 101 | *************************************************/ | ||
| 102 | |||
| 103 | void ap_regfree(ap_regex_t *preg) | ||
| 104 | { | ||
| 105 | (pcre_free)(preg->re_pcre); | ||
| 106 | } | ||
| 107 | |||
| 108 | /************************************************* | ||
| 109 | * Compile a regular expression * | ||
| 110 | *************************************************/ | ||
| 111 | |||
| 112 | /* | ||
| 113 | Arguments: | ||
| 114 | preg points to a structure for recording the compiled expression | ||
| 115 | pattern the pattern to compile | ||
| 116 | cflags compilation flags | ||
| 117 | |||
| 118 | Returns: 0 on success | ||
| 119 | various non-zero codes on failure | ||
| 120 | */ | ||
| 121 | |||
| 122 | int ap_regcomp(ap_regex_t *preg, const char *pattern, int cflags) | ||
| 123 | { | ||
| 124 | const char *errorptr; | ||
| 125 | int erroffset; | ||
| 126 | int options = 0; | ||
| 127 | |||
| 128 | if ((cflags & AP_REG_ICASE) != 0) | ||
| 129 | options |= PCRE_CASELESS; | ||
| 130 | if ((cflags & AP_REG_NEWLINE) != 0) | ||
| 131 | options |= PCRE_MULTILINE; | ||
| 132 | |||
| 133 | preg->re_pcre = pcre_compile(pattern, options, &errorptr, &erroffset, NULL); | ||
| 134 | preg->re_erroffset = erroffset; | ||
| 135 | |||
| 136 | if (preg->re_pcre == NULL) | ||
| 137 | return AP_REG_INVARG; | ||
| 138 | |||
| 139 | preg->re_nsub = pcre_info((const pcre *)preg->re_pcre, NULL, NULL); | ||
| 140 | return 0; | ||
| 141 | } | ||
| 142 | |||
| 143 | /************************************************* | ||
| 144 | * Match a regular expression * | ||
| 145 | *************************************************/ | ||
| 146 | |||
| 147 | /* Unfortunately, PCRE requires 3 ints of working space for each captured | ||
| 148 | substring, so we have to get and release working store instead of just using | ||
| 149 | the POSIX structures as was done in earlier releases when PCRE needed only 2 | ||
| 150 | ints. However, if the number of possible capturing brackets is small, use a | ||
| 151 | block of store on the stack, to reduce the use of malloc/free. The threshold is | ||
| 152 | in a macro that can be changed at configure time. */ | ||
| 153 | |||
| 154 | int ap_regexec(const ap_regex_t *preg, const char *string, apr_size_t nmatch, | ||
| 155 | ap_regmatch_t pmatch[], int eflags) | ||
| 156 | { | ||
| 157 | int rc; | ||
| 158 | int options = 0; | ||
| 159 | int *ovector= NULL; | ||
| 160 | int small_ovector[POSIX_MALLOC_THRESHOLD * 3]; | ||
| 161 | int allocated_ovector = 0; | ||
| 162 | |||
| 163 | if ((eflags & AP_REG_NOTBOL) != 0) | ||
| 164 | options |= PCRE_NOTBOL; | ||
| 165 | if ((eflags & AP_REG_NOTEOL) != 0) | ||
| 166 | options |= PCRE_NOTEOL; | ||
| 167 | |||
| 168 | ((ap_regex_t *)preg)->re_erroffset = (apr_size_t)(-1); /* Only has meaning after compile */ | ||
| 169 | |||
| 170 | if (nmatch > 0) { | ||
| 171 | if (nmatch <= POSIX_MALLOC_THRESHOLD) { | ||
| 172 | ovector = &(small_ovector[0]); | ||
| 173 | } else { | ||
| 174 | ovector = (int *)malloc(sizeof(int) * nmatch * 3); | ||
| 175 | if (ovector == NULL) | ||
| 176 | return AP_REG_ESPACE; | ||
| 177 | allocated_ovector = 1; | ||
| 178 | } | ||
| 179 | } | ||
| 180 | |||
| 181 | rc = pcre_exec((const pcre *)preg->re_pcre, NULL, string, | ||
| 182 | (int)strlen(string), 0, options, ovector, nmatch * 3); | ||
| 183 | |||
| 184 | if (rc == 0) | ||
| 185 | rc = nmatch; /* All captured slots were filled in */ | ||
| 186 | |||
| 187 | if (rc >= 0) { | ||
| 188 | apr_size_t i; | ||
| 189 | for (i = 0; i < (apr_size_t)rc; i++) { | ||
| 190 | pmatch[i].rm_so = ovector[i*2]; | ||
| 191 | pmatch[i].rm_eo = ovector[i*2+1]; | ||
| 192 | } | ||
| 193 | if (allocated_ovector) | ||
| 194 | free(ovector); | ||
| 195 | for (; i < nmatch; i++) | ||
| 196 | pmatch[i].rm_so = pmatch[i].rm_eo = -1; | ||
| 197 | return 0; | ||
| 198 | } | ||
| 199 | |||
| 200 | else { | ||
| 201 | if (allocated_ovector) | ||
| 202 | free(ovector); | ||
| 203 | switch (rc) { | ||
| 204 | case PCRE_ERROR_NOMATCH: | ||
| 205 | return AP_REG_NOMATCH; | ||
| 206 | case PCRE_ERROR_NULL: | ||
| 207 | return AP_REG_INVARG; | ||
| 208 | case PCRE_ERROR_BADOPTION: | ||
| 209 | return AP_REG_INVARG; | ||
| 210 | case PCRE_ERROR_BADMAGIC: | ||
| 211 | return AP_REG_INVARG; | ||
| 212 | case PCRE_ERROR_UNKNOWN_NODE: | ||
| 213 | return AP_REG_ASSERT; | ||
| 214 | case PCRE_ERROR_NOMEMORY: | ||
| 215 | return AP_REG_ESPACE; | ||
| 216 | #ifdef PCRE_ERROR_MATCHLIMIT | ||
| 217 | case PCRE_ERROR_MATCHLIMIT: return AP_REG_ESPACE; | ||
| 218 | #endif | ||
| 219 | #ifdef PCRE_ERROR_BADUTF8 | ||
| 220 | case PCRE_ERROR_BADUTF8: return AP_REG_INVARG; | ||
| 221 | #endif | ||
| 222 | #ifdef PCRE_ERROR_BADUTF8_OFFSET | ||
| 223 | case PCRE_ERROR_BADUTF8_OFFSET: return AP_REG_INVARG; | ||
| 224 | #endif | ||
| 225 | default: | ||
| 226 | return AP_REG_ASSERT; | ||
| 227 | } | ||
| 228 | } | ||
| 229 | } | ||
| 230 | |||
| 231 | /* | ||
| 232 | * Here's a pool-based interface to the POSIX-esque ap_regcomp(). | ||
| 233 | * Note that we return ap_regex_t instead of being passed one. | ||
| 234 | * The reason is that if you use an already-used ap_regex_t structure, | ||
| 235 | * the memory that you've already allocated gets forgotten, and | ||
| 236 | * regfree() doesn't clear it. So we don't allow it. | ||
| 237 | */ | ||
| 238 | |||
| 239 | static apr_status_t regex_cleanup(void *preg) | ||
| 240 | { | ||
| 241 | ap_regfree((ap_regex_t *) preg); | ||
| 242 | return APR_SUCCESS; | ||
| 243 | } | ||
| 244 | |||
| 245 | ap_regex_t *ap_pregcomp(apr_pool_t *p, const char *pattern, int cflags) | ||
| 246 | { | ||
| 247 | ap_regex_t *preg = apr_palloc(p, sizeof *preg); | ||
| 248 | |||
| 249 | if (ap_regcomp(preg, pattern, cflags)) { | ||
| 250 | return NULL; | ||
| 251 | } | ||
| 252 | |||
| 253 | apr_pool_cleanup_register(p, (void *) preg, regex_cleanup, | ||
| 254 | apr_pool_cleanup_null); | ||
| 255 | |||
| 256 | return preg; | ||
| 257 | } | ||
| 258 | |||
| 259 | void ap_pregfree(apr_pool_t *p, ap_regex_t *reg) | ||
| 260 | { | ||
| 261 | ap_regfree(reg); | ||
| 262 | apr_pool_cleanup_kill(p, (void *) reg, regex_cleanup); | ||
| 263 | } | ||
| 264 | |||
| 265 | /* This function substitutes for $0-$9, filling in regular expression | ||
| 266 | * submatches. Pass it the same nmatch and pmatch arguments that you | ||
| 267 | * passed ap_regexec(). pmatch should not be greater than the maximum number | ||
| 268 | * of subexpressions - i.e. one more than the re_nsub member of ap_regex_t. | ||
| 269 | * | ||
| 270 | * input should be the string with the $-expressions, source should be the | ||
| 271 | * string that was matched against. | ||
| 272 | * | ||
| 273 | * It returns the substituted string, or NULL on error. | ||
| 274 | * | ||
| 275 | * Parts of this code are based on Henry Spencer's regsub(), from his | ||
| 276 | * AT&T V8 regexp package. | ||
| 277 | */ | ||
| 278 | |||
| 279 | char * ap_pregsub(apr_pool_t *p, const char *input, const char *source, | ||
| 280 | size_t nmatch, ap_regmatch_t pmatch[]) | ||
| 281 | { | ||
| 282 | const char *src = input; | ||
| 283 | char *dest, *dst; | ||
| 284 | char c; | ||
| 285 | size_t no; | ||
| 286 | int len; | ||
| 287 | |||
| 288 | if (!source) | ||
| 289 | return NULL; | ||
| 290 | if (!nmatch) | ||
| 291 | return apr_pstrdup(p, src); | ||
| 292 | |||
| 293 | /* First pass, find the size */ | ||
| 294 | |||
| 295 | len = 0; | ||
| 296 | |||
| 297 | while ((c = *src++) != '\0') { | ||
| 298 | if (c == '&') | ||
| 299 | no = 0; | ||
| 300 | else if (c == '$' && apr_isdigit(*src)) | ||
| 301 | no = *src++ - '0'; | ||
| 302 | else | ||
| 303 | no = 10; | ||
| 304 | |||
| 305 | if (no> 9) { /* Ordinary character. */ | ||
| 306 | if (c == '\\' && (*src == '$' || *src == '&')) | ||
| 307 | c = *src++; | ||
| 308 | len++; | ||
| 309 | } else if (no < nmatch && pmatch[no].rm_so < pmatch[no].rm_eo) { | ||
| 310 | len += pmatch[no].rm_eo - pmatch[no].rm_so; | ||
| 311 | } | ||
| 312 | |||
| 313 | } | ||
| 314 | |||
| 315 | dest = dst = apr_pcalloc(p, len + 1); | ||
| 316 | |||
| 317 | /* Now actually fill in the string */ | ||
| 318 | |||
| 319 | src = input; | ||
| 320 | |||
| 321 | while ((c = *src++) != '\0') { | ||
| 322 | if (c == '&') | ||
| 323 | no = 0; | ||
| 324 | else if (c == '$' && apr_isdigit(*src)) | ||
| 325 | no = *src++ - '0'; | ||
| 326 | else | ||
| 327 | no = 10; | ||
| 328 | |||
| 329 | if (no> 9) { /* Ordinary character. */ | ||
| 330 | if (c == '\\' && (*src == '$' || *src == '&')) | ||
| 331 | c = *src++; | ||
| 332 | *dst++ = c; | ||
| 333 | } else if (no < nmatch && pmatch[no].rm_so < pmatch[no].rm_eo) { | ||
| 334 | len = pmatch[no].rm_eo - pmatch[no].rm_so; | ||
| 335 | memcpy(dst, source + pmatch[no].rm_so, len); | ||
| 336 | dst += len; | ||
| 337 | } | ||
| 338 | |||
| 339 | } | ||
| 340 | *dst = '\0'; | ||
| 341 | |||
| 342 | return dest; | ||
| 343 | } | ||
| 344 | /* End of pcreposix.c */ | ||
diff --git a/utility/ap_pcre.h b/utility/ap_pcre.h new file mode 100644 index 0000000..e817dc2 --- /dev/null +++ b/utility/ap_pcre.h | |||
| @@ -0,0 +1,178 @@ | |||
| 1 | /* Licensed to the Apache Software Foundation (ASF) under one or more | ||
| 2 | * contributor license agreements. See the NOTICE file distributed with | ||
| 3 | * this work for additional information regarding copyright ownership. | ||
| 4 | * The ASF licenses this file to You under the Apache License, Version 2.0 | ||
| 5 | * (the "License"); you may not use this file except in compliance with | ||
| 6 | * the License. You may obtain a copy of the License at | ||
| 7 | * | ||
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 | ||
| 9 | * | ||
| 10 | * Unless required by applicable law or agreed to in writing, software | ||
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, | ||
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 13 | * See the License for the specific language governing permissions and | ||
| 14 | * limitations under the License. | ||
| 15 | */ | ||
| 16 | |||
| 17 | /* Derived from PCRE's pcreposix.h. | ||
| 18 | |||
| 19 | Copyright (c) 1997-2004 University of Cambridge | ||
| 20 | |||
| 21 | ----------------------------------------------------------------------------- | ||
| 22 | Redistribution and use in source and binary forms, with or without | ||
| 23 | modification, are permitted provided that the following conditions are met: | ||
| 24 | |||
| 25 | * Redistributions of source code must retain the above copyright notice, | ||
| 26 | this list of conditions and the following disclaimer. | ||
| 27 | |||
| 28 | * Redistributions in binary form must reproduce the above copyright | ||
| 29 | notice, this list of conditions and the following disclaimer in the | ||
| 30 | documentation and/or other materials provided with the distribution. | ||
| 31 | |||
| 32 | * Neither the name of the University of Cambridge nor the names of its | ||
| 33 | contributors may be used to endorse or promote products derived from | ||
| 34 | this software without specific prior written permission. | ||
| 35 | |||
| 36 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | ||
| 37 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||
| 38 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | ||
| 39 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE | ||
| 40 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | ||
| 41 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | ||
| 42 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | ||
| 43 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | ||
| 44 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | ||
| 45 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | ||
| 46 | POSSIBILITY OF SUCH DAMAGE. | ||
| 47 | ----------------------------------------------------------------------------- | ||
| 48 | */ | ||
| 49 | |||
| 50 | /** | ||
| 51 | * @file ap_regex.h | ||
| 52 | * @brief Apache Regex defines | ||
| 53 | */ | ||
| 54 | |||
| 55 | #ifndef AP_REGEX_H | ||
| 56 | #define AP_REGEX_H | ||
| 57 | |||
| 58 | #include "apr.h" | ||
| 59 | |||
| 60 | /* Allow for C++ users */ | ||
| 61 | |||
| 62 | #ifdef __cplusplus | ||
| 63 | extern "C" { | ||
| 64 | #endif | ||
| 65 | |||
| 66 | /* Options for ap_regexec: */ | ||
| 67 | |||
| 68 | #define AP_REG_ICASE 0x01 /** use a case-insensitive match */ | ||
| 69 | #define AP_REG_NEWLINE 0x02 /** don't match newlines against '.' etc */ | ||
| 70 | #define AP_REG_NOTBOL 0x04 /** ^ will not match against start-of-string */ | ||
| 71 | #define AP_REG_NOTEOL 0x08 /** $ will not match against end-of-string */ | ||
| 72 | |||
| 73 | #define AP_REG_EXTENDED (0) /** unused */ | ||
| 74 | #define AP_REG_NOSUB (0) /** unused */ | ||
| 75 | |||
| 76 | #define AP_MAX_REG_MATCH 10 | ||
| 77 | |||
| 78 | /* Error values: */ | ||
| 79 | enum { | ||
| 80 | AP_REG_ASSERT = 1, /** internal error ? */ | ||
| 81 | AP_REG_ESPACE, /** failed to get memory */ | ||
| 82 | AP_REG_INVARG, /** invalid argument */ | ||
| 83 | AP_REG_NOMATCH /** match failed */ | ||
| 84 | }; | ||
| 85 | |||
| 86 | /* The structure representing a compiled regular expression. */ | ||
| 87 | typedef struct { | ||
| 88 | void *re_pcre; | ||
| 89 | apr_size_t re_nsub; | ||
| 90 | apr_size_t re_erroffset; | ||
| 91 | } ap_regex_t; | ||
| 92 | |||
| 93 | /* The structure in which a captured offset is returned. */ | ||
| 94 | typedef struct { | ||
| 95 | int rm_so; | ||
| 96 | int rm_eo; | ||
| 97 | } ap_regmatch_t; | ||
| 98 | |||
| 99 | /* The functions */ | ||
| 100 | |||
| 101 | /** | ||
| 102 | * Compile a regular expression. | ||
| 103 | * @param preg Returned compiled regex | ||
| 104 | * @param regex The regular expression string | ||
| 105 | * @param cflags Must be zero (currently). | ||
| 106 | * @return Zero on success or non-zero on error | ||
| 107 | */ | ||
| 108 | int ap_regcomp(ap_regex_t *preg, const char *regex, int cflags); | ||
| 109 | |||
| 110 | /** | ||
| 111 | * Match a NUL-terminated string against a pre-compiled regex. | ||
| 112 | * @param preg The pre-compiled regex | ||
| 113 | * @param string The string to match | ||
| 114 | * @param nmatch Provide information regarding the location of any matches | ||
| 115 | * @param pmatch Provide information regarding the location of any matches | ||
| 116 | * @param eflags Bitwise OR of any of AP_REG_* flags | ||
| 117 | * @return 0 for successful match, #REG_NOMATCH otherwise | ||
| 118 | */ | ||
| 119 | int ap_regexec(const ap_regex_t *preg, const char *string, | ||
| 120 | apr_size_t nmatch, ap_regmatch_t *pmatch, int eflags); | ||
| 121 | |||
| 122 | /** | ||
| 123 | * Return the error code returned by regcomp or regexec into error messages | ||
| 124 | * @param errcode the error code returned by regexec or regcomp | ||
| 125 | * @param preg The precompiled regex | ||
| 126 | * @param errbuf A buffer to store the error in | ||
| 127 | * @param errbuf_size The size of the buffer | ||
| 128 | */ | ||
| 129 | apr_size_t ap_regerror(int errcode, const ap_regex_t *preg, | ||
| 130 | char *errbuf, apr_size_t errbuf_size); | ||
| 131 | |||
| 132 | /** Destroy a pre-compiled regex. | ||
| 133 | * @param preg The pre-compiled regex to free. | ||
| 134 | */ | ||
| 135 | void ap_regfree(ap_regex_t *preg); | ||
| 136 | |||
| 137 | /** | ||
| 138 | * Compile a regular expression to be used later | ||
| 139 | * @param p The pool to allocate from | ||
| 140 | * @param pattern the regular expression to compile | ||
| 141 | * @param cflags The bitwise or of one or more of the following: | ||
| 142 | * @li REG_EXTENDED - Use POSIX extended Regular Expressions | ||
| 143 | * @li REG_ICASE - Ignore case | ||
| 144 | * @li REG_NOSUB - Support for substring addressing of matches | ||
| 145 | * not required | ||
| 146 | * @li REG_NEWLINE - Match-any-character operators don't match new-line | ||
| 147 | * @return The compiled regular expression | ||
| 148 | */ | ||
| 149 | ap_regex_t * ap_pregcomp(apr_pool_t *p, const char *pattern, | ||
| 150 | int cflags); | ||
| 151 | |||
| 152 | /** | ||
| 153 | * Free the memory associated with a compiled regular expression | ||
| 154 | * @param p The pool the regex was allocated from | ||
| 155 | * @param reg The regular expression to free | ||
| 156 | */ | ||
| 157 | void ap_pregfree(apr_pool_t *p, ap_regex_t *reg); | ||
| 158 | |||
| 159 | /** | ||
| 160 | * After performing a successful regex match, you may use this function to | ||
| 161 | * perform a series of string substitutions based on subexpressions that were | ||
| 162 | * matched during the call to ap_regexec | ||
| 163 | * @param p The pool to allocate from | ||
| 164 | * @param input An arbitrary string containing $1 through $9. These are | ||
| 165 | * replaced with the corresponding matched sub-expressions | ||
| 166 | * @param source The string that was originally matched to the regex | ||
| 167 | * @param nmatch the nmatch returned from ap_pregex | ||
| 168 | * @param pmatch the pmatch array returned from ap_pregex | ||
| 169 | */ | ||
| 170 | char * ap_pregsub(apr_pool_t *p, const char *input, const char *source, | ||
| 171 | size_t nmatch, ap_regmatch_t pmatch[]); | ||
| 172 | |||
| 173 | #ifdef __cplusplus | ||
| 174 | } /* extern "C" */ | ||
| 175 | #endif | ||
| 176 | |||
| 177 | #endif /* AP_REGEX_T */ | ||
| 178 | |||
diff --git a/utility/config.c b/utility/config.c new file mode 100644 index 0000000..6867476 --- /dev/null +++ b/utility/config.c | |||
| @@ -0,0 +1,534 @@ | |||
| 1 | #include "apr.h" | ||
| 2 | #include "apr_file_info.h" | ||
| 3 | #include "apr_file_io.h" | ||
| 4 | #include "apr_strings.h" | ||
| 5 | #include "apr_hash.h" | ||
| 6 | #include "apr_uri.h" | ||
| 7 | |||
| 8 | #include "shell.h" | ||
| 9 | #include "config.h" | ||
| 10 | #include "util.h" | ||
| 11 | #include "logparse.h" | ||
| 12 | #include "autoconfig.h" | ||
| 13 | |||
| 14 | apr_hash_t *g_config_opts; | ||
| 15 | |||
| 16 | static apr_status_t config_set_string(config_t *cfg, config_opt_t *opt, | ||
| 17 | int argc, const char **argv) | ||
| 18 | { | ||
| 19 | int offset = (int)(long)opt->data; | ||
| 20 | char **data = (char **)((void *)cfg + offset); | ||
| 21 | if (argc != 2) | ||
| 22 | return APR_EINVAL; | ||
| 23 | *data = apr_pstrdup(cfg->pool, argv[1]); | ||
| 24 | return APR_SUCCESS; | ||
| 25 | } | ||
| 26 | |||
| 27 | static apr_status_t config_set_file(config_t *cfg, config_opt_t *opt, | ||
| 28 | int argc, const char **argv) | ||
| 29 | { | ||
| 30 | int offset = (int)(long)opt->data; | ||
| 31 | char **data = (char **)((void *)cfg + offset); | ||
| 32 | if (argc != 2) | ||
| 33 | return APR_EINVAL; | ||
| 34 | apr_filepath_merge(data, NULL, argv[1], | ||
| 35 | APR_FILEPATH_TRUENAME, cfg->pool); | ||
| 36 | return APR_SUCCESS; | ||
| 37 | } | ||
| 38 | |||
| 39 | static apr_status_t config_set_int(config_t *cfg, config_opt_t *opt, int argc, | ||
| 40 | const char **argv) | ||
| 41 | { | ||
| 42 | int offset = (int)(long)opt->data; | ||
| 43 | int *data = (int *)((void *)cfg + offset); | ||
| 44 | if (argc != 2) | ||
| 45 | return APR_EINVAL; | ||
| 46 | *data = apr_atoi64(argv[1]); | ||
| 47 | return APR_SUCCESS; | ||
| 48 | } | ||
| 49 | |||
| 50 | static apr_status_t config_set_flag(config_t *cfg, config_opt_t *opt, int argc, | ||
| 51 | const char **argv) | ||
| 52 | { | ||
| 53 | int offset = (int)(long)opt->data; | ||
| 54 | int *data = (int *)((void *)cfg + offset); | ||
| 55 | if (argc != 2) | ||
| 56 | return APR_EINVAL; | ||
| 57 | *data = CHECK_YESNO(argv[1]); | ||
| 58 | return APR_SUCCESS; | ||
| 59 | } | ||
| 60 | |||
| 61 | static apr_status_t config_set_loglevel(config_t *cfg, config_opt_t *opt, | ||
| 62 | int argc, const char **argv) | ||
| 63 | { | ||
| 64 | if (argc != 2) | ||
| 65 | return APR_EINVAL; | ||
| 66 | if (!strcasecmp(argv[1], "error")) { | ||
| 67 | cfg->loglevel = LOGLEVEL_ERROR; | ||
| 68 | } else if (!strcasecmp(argv[1], "notice")) { | ||
| 69 | cfg->loglevel = LOGLEVEL_NOTICE; | ||
| 70 | } else if (!strcasecmp(argv[1], "debug")) { | ||
| 71 | cfg->loglevel = LOGLEVEL_DEBUG; | ||
| 72 | } else { | ||
| 73 | cfg->loglevel = LOGLEVEL_ERROR; | ||
| 74 | } | ||
| 75 | return APR_SUCCESS; | ||
| 76 | } | ||
| 77 | |||
| 78 | static apr_status_t config_set_inputfile(config_t *cfg, config_opt_t *opt, | ||
| 79 | int argc, const char **argv) | ||
| 80 | { | ||
| 81 | config_filestat_t *newp; | ||
| 82 | if (argc != 2) | ||
| 83 | return APR_EINVAL; | ||
| 84 | newp = (config_filestat_t *)apr_array_push(cfg->input_files); | ||
| 85 | char *temp; | ||
| 86 | apr_filepath_merge(&temp, NULL, argv[1], | ||
| 87 | APR_FILEPATH_TRUENAME, cfg->pool); | ||
| 88 | newp->fname = temp; | ||
| 89 | newp->result = "Not Parsed"; | ||
| 90 | return APR_SUCCESS; | ||
| 91 | } | ||
| 92 | |||
| 93 | static apr_status_t config_set_dummy(config_t *cfg, config_opt_t *opt, | ||
| 94 | int argc, const char **argv) | ||
| 95 | { | ||
| 96 | return APR_SUCCESS; | ||
| 97 | } | ||
| 98 | |||
| 99 | static apr_status_t config_set_logformat(config_t *cfg, config_opt_t *opt, | ||
| 100 | int argc, const char **argv) | ||
| 101 | { | ||
| 102 | config_logformat_t *format; | ||
| 103 | config_logformat_field_t *field; | ||
| 104 | |||
| 105 | if (argc != 4) | ||
| 106 | return APR_EINVAL; | ||
| 107 | |||
| 108 | format = apr_hash_get(cfg->log_formats, argv[1], APR_HASH_KEY_STRING); | ||
| 109 | if (!format) { | ||
| 110 | format = apr_palloc(cfg->pool, sizeof(config_logformat_t)); | ||
| 111 | format->name = apr_pstrdup(cfg->pool, argv[1]); | ||
| 112 | format->fields = apr_array_make(cfg->pool, 5, | ||
| 113 | sizeof(config_logformat_field_t)); | ||
| 114 | apr_hash_set(cfg->log_formats, apr_pstrdup(cfg->pool,argv[1]), APR_HASH_KEY_STRING, format); | ||
| 115 | } | ||
| 116 | field = (config_logformat_field_t *)apr_array_push(format->fields); | ||
| 117 | field->name = apr_pstrdup(cfg->pool, argv[2]); | ||
| 118 | field->datatype = apr_pstrdup(cfg->pool, argv[3]); | ||
| 119 | return APR_SUCCESS; | ||
| 120 | } | ||
| 121 | |||
| 122 | static apr_status_t config_set_output_field(config_t *cfg, config_opt_t *opt, | ||
| 123 | int argc, const char **argv) | ||
| 124 | { | ||
| 125 | config_output_field_t *field; | ||
| 126 | char *type, *size, *temp; | ||
| 127 | |||
| 128 | if (argc < 5) | ||
| 129 | return APR_EINVAL; | ||
| 130 | field = (config_output_field_t *)apr_array_push(cfg->output_fields); | ||
| 131 | field->field = apr_pstrdup(cfg->pool, argv[1]); | ||
| 132 | field->source = apr_pstrdup(cfg->pool, argv[4]); | ||
| 133 | field->def = apr_pstrdup(cfg->pool, argv[3]); | ||
| 134 | type = size = apr_pstrdup(cfg->pool, argv[2]); | ||
| 135 | while (*size!='\0' && *size!='(') | ||
| 136 | size++; | ||
| 137 | if (*size == '(') { | ||
| 138 | *size = '\0'; | ||
| 139 | size++; | ||
| 140 | temp = size; | ||
| 141 | while (*temp != '\0' && *temp != ')') | ||
| 142 | temp++; | ||
| 143 | *temp = '\0'; | ||
| 144 | field->size = apr_atoi64(size); | ||
| 145 | } | ||
| 146 | if (strcasecmp("VARCHAR", type)==0) { | ||
| 147 | field->datatype = LOGSQL_DATATYPE_VARCHAR; | ||
| 148 | } else if (strcasecmp("INT", type)==0) { | ||
| 149 | field->datatype = LOGSQL_DATATYPE_INT; | ||
| 150 | } else if (strcasecmp("CHAR", type)==0) { | ||
| 151 | field->datatype = LOGSQL_DATATYPE_CHAR; | ||
| 152 | } else if (strcasecmp("SMALLINT", type)==0) { | ||
| 153 | field->datatype = LOGSQL_DATATYPE_SMALLINT; | ||
| 154 | } else if (strcasecmp("BIGINT", type)==0) { | ||
| 155 | field->datatype = LOGSQL_DATATYPE_BIGINT; | ||
| 156 | } else { | ||
| 157 | return APR_EINVAL; | ||
| 158 | } | ||
| 159 | |||
| 160 | // Has a function | ||
| 161 | if (argc > 5) { | ||
| 162 | int i; | ||
| 163 | field->fname = apr_pstrdup(cfg->pool, argv[5]); | ||
| 164 | field->func = parser_get_func(field->fname); | ||
| 165 | field->args = apr_pcalloc(cfg->pool, sizeof(char *) * (argc-5+1)); | ||
| 166 | for (i=6; i<=argc; i++) { | ||
| 167 | field->args[i-6] = apr_pstrdup(cfg->pool, argv[i]); | ||
| 168 | } | ||
| 169 | } | ||
| 170 | |||
| 171 | return APR_SUCCESS; | ||
| 172 | } | ||
| 173 | |||
| 174 | static apr_status_t config_set_filter(config_t *cfg, config_opt_t *opt, | ||
| 175 | int argc, const char **argv) | ||
| 176 | { | ||
| 177 | int argn = 1; | ||
| 178 | config_filter_t *filter; | ||
| 179 | switch (opt->name[1]) { | ||
| 180 | case 'i': //line | ||
| 181 | filter = apr_array_push(cfg->linefilters); | ||
| 182 | break; | ||
| 183 | case 'r': //pre | ||
| 184 | filter = apr_array_push(cfg->prefilters); | ||
| 185 | break; | ||
| 186 | case 'o': //post | ||
| 187 | filter = apr_array_push(cfg->postfilters); | ||
| 188 | break; | ||
| 189 | } | ||
| 190 | |||
| 191 | if (opt->name[0]=='P') { // Pre or post 2-3 args | ||
| 192 | if (argc == 1) | ||
| 193 | return APR_EINVAL; | ||
| 194 | filter->field = apr_pstrdup(cfg->pool, argv[1]); | ||
| 195 | argn++; | ||
| 196 | } // Otherwise Line based only 1-2 args (no field) | ||
| 197 | if (argc <= argn) | ||
| 198 | return APR_EINVAL; | ||
| 199 | if (*argv[argn] == '+') | ||
| 200 | argn++; | ||
| 201 | if (*argv[argn] == '-') { | ||
| 202 | filter->negative = 1; | ||
| 203 | argn++; | ||
| 204 | } | ||
| 205 | if (filter->negative && argc == argn) { | ||
| 206 | // if no filter for negative.. that's ok.. Assume ALL | ||
| 207 | return APR_SUCCESS; | ||
| 208 | } | ||
| 209 | if (argc <= argn) | ||
| 210 | return APR_EINVAL; | ||
| 211 | filter->filter = apr_pstrdup(cfg->pool, argv[argn]); | ||
| 212 | filter->regex = ap_pregcomp(cfg->pool, filter->filter, AP_REG_EXTENDED|AP_REG_ICASE); | ||
| 213 | return APR_SUCCESS; | ||
| 214 | } | ||
| 215 | |||
| 216 | void config_dump(config_t *cfg) | ||
| 217 | { | ||
| 218 | apr_hash_index_t *hi; | ||
| 219 | int i; | ||
| 220 | config_output_field_t *fields; | ||
| 221 | config_filter_t *filters; | ||
| 222 | |||
| 223 | printf("ErrorLog: %s\n", cfg->errorlog); | ||
| 224 | printf("LogLevel: %d\n", cfg->loglevel); | ||
| 225 | |||
| 226 | printf("BadLineFile: %s\n", cfg->badlinefile); | ||
| 227 | printf("BadLineMax: %d\n", cfg->badlinemax); | ||
| 228 | |||
| 229 | printf("InputDir: %s\n", cfg->input_dir); | ||
| 230 | |||
| 231 | printf("Split input files: %d\n", cfg->split_enabled); | ||
| 232 | printf("Split output directory: %s\n", cfg->split_dir); | ||
| 233 | printf("Split file count: %d\n", cfg->split_count); | ||
| 234 | printf("Split min lines: %'d\n", cfg->split_minimum); | ||
| 235 | printf("Split max lines: %'d\n", cfg->split_maximum); | ||
| 236 | |||
| 237 | printf("DB Driver: %s\n", cfg->dbdriver); | ||
| 238 | printf("DB Params: %s\n", cfg->dbparams); | ||
| 239 | |||
| 240 | printf("Table: %s\n", cfg->table); | ||
| 241 | printf("Transactions: %d\n", cfg->transactions); | ||
| 242 | printf("MachineID: %s\n", cfg->machineid); | ||
| 243 | |||
| 244 | printf("Log formats:\n"); | ||
| 245 | for (hi = apr_hash_first(cfg->pool, cfg->log_formats); hi; hi | ||
| 246 | = apr_hash_next(hi)) { | ||
| 247 | config_logformat_t *format; | ||
| 248 | config_logformat_field_t *fields; | ||
| 249 | int i; | ||
| 250 | |||
| 251 | apr_hash_this(hi, NULL, NULL, (void **)&format); | ||
| 252 | printf(">> '%s'\n", format->name); | ||
| 253 | fields = (config_logformat_field_t *)format->fields->elts; | ||
| 254 | for (i=0; i<format->fields->nelts; i++) { | ||
| 255 | printf(">>>> %s:%s\n", fields[i].name, fields[i].datatype); | ||
| 256 | } | ||
| 257 | } | ||
| 258 | printf("Log Format: '%s'\n", cfg->logformat); | ||
| 259 | |||
| 260 | printf("Output Fields:\n"); | ||
| 261 | fields = (config_output_field_t *)cfg->output_fields->elts; | ||
| 262 | for (i=0; i<cfg->output_fields->nelts; i++) { | ||
| 263 | printf(">> %s %s(%d) DEFAULT '%s': %s", fields[i].field, | ||
| 264 | logsql_field_datatyeName(fields[i].datatype), | ||
| 265 | fields[i].size, fields[i].def, fields[i].source); | ||
| 266 | if (fields[i].func) { | ||
| 267 | printf(" :: %s(", fields[i].fname); | ||
| 268 | if (fields[i].args) { | ||
| 269 | int a = 0; | ||
| 270 | while (fields[i].args[a]) { | ||
| 271 | printf("%s,", fields[i].args[a]); | ||
| 272 | a++; | ||
| 273 | } | ||
| 274 | } | ||
| 275 | printf(")"); | ||
| 276 | } | ||
| 277 | printf("\n"); | ||
| 278 | } | ||
| 279 | printf("Filters:\n>> Line:\n"); | ||
| 280 | filters = (config_filter_t *)cfg->linefilters->elts; | ||
| 281 | for (i=0; i<cfg->linefilters->nelts; i++) { | ||
| 282 | printf(">>>> %c \"%s\" (%pp)\n",filters[i].negative ? '-':'+', | ||
| 283 | filters[i].filter, filters[i].regex); | ||
| 284 | } | ||
| 285 | printf(">> Pre:\n"); | ||
| 286 | filters = (config_filter_t *)cfg->prefilters->elts; | ||
| 287 | for (i=0; i<cfg->prefilters->nelts; i++) { | ||
| 288 | printf(">>>> %s %c \"%s\" (%pp)\n", | ||
| 289 | filters[i].field, filters[i].negative ? '-':'+', | ||
| 290 | filters[i].filter, filters[i].regex); | ||
| 291 | } | ||
| 292 | printf(">> Post:\n"); | ||
| 293 | filters = (config_filter_t *)cfg->postfilters->elts; | ||
| 294 | for (i=0; i<cfg->postfilters->nelts; i++) { | ||
| 295 | printf(">>>> %s %c \"%s\" (%pp)\n", | ||
| 296 | filters[i].field, filters[i].negative ? '-':'+', | ||
| 297 | filters[i].filter, filters[i].regex); | ||
| 298 | } | ||
| 299 | |||
| 300 | printf("DryRun: %d\n", cfg->dryrun); | ||
| 301 | printf("Summary: %d\n", cfg->summary); | ||
| 302 | } | ||
| 303 | |||
| 304 | #define config_get_option(name) apr_hash_get(g_config_opts, name, APR_HASH_KEY_STRING) | ||
| 305 | |||
| 306 | static void config_add_option(apr_pool_t *p, const char *const name, | ||
| 307 | const char *const help, config_func_t func, void *data) | ||
| 308 | { | ||
| 309 | config_opt_t *opt; | ||
| 310 | if (!g_config_opts) { | ||
| 311 | g_config_opts = apr_hash_make(p); | ||
| 312 | } | ||
| 313 | opt = apr_palloc(p, sizeof(config_opt_t)); | ||
| 314 | opt->name = name; | ||
| 315 | opt->help = help; | ||
| 316 | opt->func = func; | ||
| 317 | opt->data = data; | ||
| 318 | apr_hash_set(g_config_opts, lowerstr(p, name), APR_HASH_KEY_STRING, opt); | ||
| 319 | } | ||
| 320 | |||
| 321 | void config_init(apr_pool_t *p) | ||
| 322 | { | ||
| 323 | config_add_option(p, "ErrorLog", "File to log errors", config_set_file, | ||
| 324 | (void *)APR_OFFSETOF(config_t, errorlog)); | ||
| 325 | config_add_option(p, "LogLevel", "Set Log Level (error, warn, debug, quiet)", | ||
| 326 | config_set_loglevel, NULL); | ||
| 327 | |||
| 328 | config_add_option(p, "BadLineFile", "File to log bad log lines", config_set_file, | ||
| 329 | (void *)APR_OFFSETOF(config_t, badlinefile)); | ||
| 330 | config_add_option(p, "BadLineMax", "Max number of bad lines before aborting", | ||
| 331 | config_set_int, (void *)APR_OFFSETOF(config_t, badlinemax)); | ||
| 332 | |||
| 333 | |||
| 334 | config_add_option(p, "InputDirectory", "Directory to scan for log files", | ||
| 335 | config_set_file, (void *)APR_OFFSETOF(config_t, input_dir)); | ||
| 336 | config_add_option(p, "InputFile", "Parse only this file", | ||
| 337 | config_set_inputfile, NULL); | ||
| 338 | |||
| 339 | config_add_option(p, "SplitInput", | ||
| 340 | "Split the file into pieces, then process", | ||
| 341 | config_set_flag, (void *)APR_OFFSETOF(config_t, split_enabled)); | ||
| 342 | config_add_option(p, "SplitCount", | ||
| 343 | "Split the file into N number of pieces", | ||
| 344 | config_set_int, (void *)APR_OFFSETOF(config_t, split_count)); | ||
| 345 | config_add_option(p, "SplitMinLines", | ||
| 346 | "Each split piece will have a minumum of N lines", | ||
| 347 | config_set_int, (void *)APR_OFFSETOF(config_t, split_minimum)); | ||
| 348 | config_add_option(p, "SplitMaxLines", | ||
| 349 | "Each split piece will have a maximum of N lines", | ||
| 350 | config_set_int, (void *)APR_OFFSETOF(config_t, split_maximum)); | ||
| 351 | config_add_option(p, "SplitDirectory", | ||
| 352 | "Output directory to put intermediate split files", | ||
| 353 | config_set_file, (void *)APR_OFFSETOF(config_t, split_dir)); | ||
| 354 | |||
| 355 | config_add_option(p, "ThreadCount", | ||
| 356 | "Numer of threads to use for processing the input files", | ||
| 357 | config_set_int, (void *)APR_OFFSETOF(config_t, thread_count)); | ||
| 358 | |||
| 359 | config_add_option(p, "DBDDriver", "DBD Driver to use", | ||
| 360 | config_set_string, (void *)APR_OFFSETOF(config_t, dbdriver)); | ||
| 361 | config_add_option(p, "DBDParams", "DBD Connection Parameters", | ||
| 362 | config_set_string, (void *)APR_OFFSETOF(config_t, dbparams)); | ||
| 363 | config_add_option(p, "Table", "Table to import the log to", | ||
| 364 | config_set_string, (void *)APR_OFFSETOF(config_t, table)); | ||
| 365 | config_add_option(p, "UseTransactions", "Enable Transactions?", | ||
| 366 | config_set_flag, (void *)APR_OFFSETOF(config_t, transactions)); | ||
| 367 | config_add_option(p, "MachineID", "Machine ID to set", | ||
| 368 | config_set_string, (void *)APR_OFFSETOF(config_t, machineid)); | ||
| 369 | |||
| 370 | config_add_option(p, "LogFormatConfig", "Define input log formats", | ||
| 371 | config_set_logformat, NULL); | ||
| 372 | config_add_option(p, "LogFormat", "Use this logformat when parsing files", | ||
| 373 | config_set_string, (void *)APR_OFFSETOF(config_t, logformat)); | ||
| 374 | |||
| 375 | config_add_option(p, "LineFilter", | ||
| 376 | "A regular expression to apply to the input line", | ||
| 377 | config_set_filter, (void *)APR_OFFSETOF(config_t, linefilters)); | ||
| 378 | config_add_option(p, "PreFilter", | ||
| 379 | "A regular expression to apply to a specific input field", | ||
| 380 | config_set_filter, (void *)APR_OFFSETOF(config_t, prefilters)); | ||
| 381 | config_add_option(p, "PostFilter", | ||
| 382 | "A regular expression to apply to a specific SQL output field", | ||
| 383 | config_set_filter, (void *)APR_OFFSETOF(config_t, postfilters)); | ||
| 384 | |||
| 385 | config_add_option(p, "OutputField", | ||
| 386 | "Define output fields: field datatype source optfunc optarg...", | ||
| 387 | config_set_output_field, NULL); | ||
| 388 | |||
| 389 | config_add_option(p, "DryRun", "Don't perform any actual database changes", | ||
| 390 | config_set_flag, (void *)APR_OFFSETOF(config_t, dryrun)); | ||
| 391 | config_add_option(p, "Dump", "Dump Configuration and quit", | ||
| 392 | config_set_flag, (void *)APR_OFFSETOF(config_t, dump)); | ||
| 393 | config_add_option(p, "Config", "Dummy to handle config directive", | ||
| 394 | config_set_dummy, NULL); | ||
| 395 | config_add_option(p, "Summary", "Show the summary before exit?", | ||
| 396 | config_set_flag, (void *)APR_OFFSETOF(config_t, summary)); | ||
| 397 | } | ||
| 398 | |||
| 399 | config_t *config_create(apr_pool_t *p) | ||
| 400 | { | ||
| 401 | config_t *cfg; | ||
| 402 | apr_pool_t *sp; | ||
| 403 | apr_pool_create(&sp, p); | ||
| 404 | cfg = apr_pcalloc(sp, sizeof(config_t)); | ||
| 405 | cfg->pool = sp; | ||
| 406 | cfg->loglevel = LOGLEVEL_ERROR; | ||
| 407 | cfg->summary = 1; | ||
| 408 | cfg->transactions = 1; | ||
| 409 | cfg->thread_count = 0; // default zero thread (aka non-threaded) | ||
| 410 | cfg->split_count = 4; | ||
| 411 | cfg->split_minimum = 10000; | ||
| 412 | cfg->split_maximum = 50000; | ||
| 413 | cfg->input_files = apr_array_make(cfg->pool, 2, sizeof(config_filestat_t)); | ||
| 414 | cfg->log_formats = apr_hash_make(cfg->pool); | ||
| 415 | cfg->output_fields = apr_array_make(cfg->pool, 10, | ||
| 416 | sizeof(config_output_field_t)); | ||
| 417 | cfg->linefilters = apr_array_make(cfg->pool, 2, sizeof(config_filter_t)); | ||
| 418 | cfg->prefilters = apr_array_make(cfg->pool, 2, sizeof(config_filter_t)); | ||
| 419 | cfg->postfilters = apr_array_make(cfg->pool, 2, sizeof(config_filter_t)); | ||
| 420 | return cfg; | ||
| 421 | } | ||
| 422 | |||
| 423 | apr_status_t config_check(config_t *cfg) | ||
| 424 | { | ||
| 425 | apr_status_t ret = APR_SUCCESS; | ||
| 426 | if (!cfg->dbdriver || !cfg->dbparams) { | ||
| 427 | logging_log(cfg, LOGLEVEL_NOISE, "CONFIG: Database configuration is missing"); | ||
| 428 | ret = APR_EINVAL; | ||
| 429 | } | ||
| 430 | if (!cfg->table) { | ||
| 431 | logging_log(cfg, LOGLEVEL_NOISE, "CONFIG: No Log Table defined"); | ||
| 432 | ret = APR_EINVAL; | ||
| 433 | } | ||
| 434 | if (apr_is_empty_array(cfg->output_fields)) { | ||
| 435 | logging_log(cfg, LOGLEVEL_NOISE, "CONFIG: No Output Fields Defined"); | ||
| 436 | ret = APR_EINVAL; | ||
| 437 | } | ||
| 438 | if (apr_hash_count(cfg->log_formats)==0) { | ||
| 439 | logging_log(cfg, LOGLEVEL_NOISE, "CONFIG: No Input Log Formats Defined"); | ||
| 440 | ret = APR_EINVAL; | ||
| 441 | } | ||
| 442 | #if !defined(HAVE_APR_DBD_TRANSACTION_MODE_GET) | ||
| 443 | if (cfg->transactions) { | ||
| 444 | logging_log(cfg, LOGLEVEL_NOISE, "CONFIG: Disabling Transaction Support. Requires apr-util 1.3.0 or higher"); | ||
| 445 | cfg->transactions = 0; | ||
| 446 | } | ||
| 447 | #endif | ||
| 448 | return ret; | ||
| 449 | } | ||
| 450 | |||
| 451 | static int config_merge(void *rec, const char *key, const char *value) | ||
| 452 | { | ||
| 453 | config_t *cfg = (config_t *)rec; | ||
| 454 | |||
| 455 | config_opt_t *opt= config_get_option(key); | ||
| 456 | if (opt) { | ||
| 457 | const char *args[] = { | ||
| 458 | key, | ||
| 459 | value }; | ||
| 460 | opt->func(cfg, opt, 2, args); | ||
| 461 | } else { | ||
| 462 | logging_log(cfg, LOGLEVEL_NOISE, "Unhandled: %s\n", key); | ||
| 463 | } | ||
| 464 | return 1; | ||
| 465 | } | ||
| 466 | |||
| 467 | apr_status_t config_read(config_t *cfg, const char *filename, | ||
| 468 | apr_table_t *merge) | ||
| 469 | { | ||
| 470 | apr_finfo_t finfo; | ||
| 471 | apr_file_t *file; | ||
| 472 | apr_status_t rv, ret= APR_SUCCESS; | ||
| 473 | apr_pool_t *tp, *targp; | ||
| 474 | config_opt_t *opt; | ||
| 475 | char buff[1024]; | ||
| 476 | char *ptr; | ||
| 477 | char **targv; | ||
| 478 | int targc; | ||
| 479 | int line; | ||
| 480 | |||
| 481 | apr_pool_create(&tp, cfg->pool); | ||
| 482 | apr_pool_create(&targp, tp); | ||
| 483 | |||
| 484 | if (apr_stat(&finfo, filename, APR_FINFO_MIN, tp) != APR_SUCCESS) { | ||
| 485 | return APR_ENOENT; | ||
| 486 | } | ||
| 487 | rv = apr_file_open(&file, filename, APR_FOPEN_READ | APR_BUFFERED, | ||
| 488 | APR_OS_DEFAULT, tp); | ||
| 489 | if (rv != APR_SUCCESS) | ||
| 490 | return rv; | ||
| 491 | |||
| 492 | line = 0; | ||
| 493 | do { | ||
| 494 | rv = apr_file_gets(buff, 1024, file); | ||
| 495 | if (rv == APR_SUCCESS) { // we read data | ||
| 496 | line++; | ||
| 497 | |||
| 498 | // skip leading white space | ||
| 499 | for (ptr = buff; *ptr == ' ' || *ptr == '\t'; ptr++) | ||
| 500 | ; | ||
| 501 | line_chomp(ptr); | ||
| 502 | |||
| 503 | // skip comments | ||
| 504 | if (*ptr == '#') | ||
| 505 | continue; | ||
| 506 | if (*ptr == '\0') | ||
| 507 | continue; | ||
| 508 | apr_pool_clear(targp); | ||
| 509 | parser_tokenize_line(ptr, &targv, targp); | ||
| 510 | targc = 0; | ||
| 511 | while (targv[targc]) | ||
| 512 | targc++; | ||
| 513 | opt = config_get_option(lowerstr(targp,targv[0])); | ||
| 514 | if (opt) { | ||
| 515 | rv = opt->func(cfg, opt, targc, (const char **)targv); | ||
| 516 | if (APR_STATUS_IS_EINVAL(rv)) { | ||
| 517 | logging_log(cfg, LOGLEVEL_NOISE, | ||
| 518 | "Config Error: Invalid Arguments for %s\n\t%s\n", | ||
| 519 | opt->name, opt->help); | ||
| 520 | ret = rv; | ||
| 521 | } | ||
| 522 | } else { | ||
| 523 | logging_log(cfg, LOGLEVEL_NOISE, "Unhandled: %s\n", targv[0]); | ||
| 524 | } | ||
| 525 | } | ||
| 526 | } while (rv == APR_SUCCESS); | ||
| 527 | |||
| 528 | // Apply merges | ||
| 529 | apr_table_do(config_merge, (void *)cfg, merge, NULL); | ||
| 530 | |||
| 531 | apr_file_close(file); | ||
| 532 | apr_pool_destroy(tp); | ||
| 533 | return ret; | ||
| 534 | } | ||
diff --git a/utility/config.h b/utility/config.h new file mode 100644 index 0000000..a9bd1b5 --- /dev/null +++ b/utility/config.h | |||
| @@ -0,0 +1,190 @@ | |||
| 1 | #ifndef CONFIG_H_ | ||
| 2 | #define CONFIG_H_ | ||
| 3 | |||
| 4 | #include "apr_tables.h" | ||
| 5 | #include "apr_hash.h" | ||
| 6 | #include "apr_file_io.h" | ||
| 7 | #include "ap_pcre.h" | ||
| 8 | |||
| 9 | typedef enum { | ||
| 10 | LOGLEVEL_NOISE = 0, | ||
| 11 | LOGLEVEL_NONE, | ||
| 12 | LOGLEVEL_ERROR, | ||
| 13 | LOGLEVEL_NOTICE, | ||
| 14 | LOGLEVEL_DEBUG, | ||
| 15 | } loglevel_e; | ||
| 16 | |||
| 17 | typedef struct config_dbd_t config_dbd_t; | ||
| 18 | typedef struct config_t config_t; | ||
| 19 | struct config_t { | ||
| 20 | /** the structures pool (to ease function arguments) */ | ||
| 21 | apr_pool_t *pool; | ||
| 22 | |||
| 23 | /** error log file */ | ||
| 24 | const char *errorlog; | ||
| 25 | /** log level */ | ||
| 26 | loglevel_e loglevel; | ||
| 27 | /** error_log */ | ||
| 28 | apr_file_t *errorlog_fp; | ||
| 29 | apr_file_t *errorlog_fperr; | ||
| 30 | apr_pool_t *errorlog_p; | ||
| 31 | |||
| 32 | const char *badlinefile; | ||
| 33 | const char *badlastfile; | ||
| 34 | apr_file_t *badline_fp; | ||
| 35 | int badline_count; | ||
| 36 | int badlinemax; | ||
| 37 | |||
| 38 | |||
| 39 | /** input directory of log files */ | ||
| 40 | const char *input_dir; | ||
| 41 | /** list of files to scan */ | ||
| 42 | apr_array_header_t *input_files; | ||
| 43 | |||
| 44 | /** split the input file before processing */ | ||
| 45 | int split_enabled; | ||
| 46 | /** the number of files to split each input file into */ | ||
| 47 | int split_count; | ||
| 48 | /** the minimum number of lines for each piece */ | ||
| 49 | int split_minimum; | ||
| 50 | /** the maximum number of lines for each piece */ | ||
| 51 | int split_maximum; | ||
| 52 | /** directory to put ouput split files */ | ||
| 53 | const char *split_dir; | ||
| 54 | |||
| 55 | /** the number of threads to run the import in */ | ||
| 56 | int thread_count; | ||
| 57 | |||
| 58 | /** db connection configuration */ | ||
| 59 | const char *dbdriver; | ||
| 60 | const char *dbparams; | ||
| 61 | |||
| 62 | /** Logging table */ | ||
| 63 | const char *table; | ||
| 64 | /** Use transactons */ | ||
| 65 | int transactions; | ||
| 66 | /** Machine ID */ | ||
| 67 | const char *machineid; | ||
| 68 | |||
| 69 | /** Log file formats */ | ||
| 70 | apr_hash_t *log_formats; | ||
| 71 | /** format to use to parse files */ | ||
| 72 | const char *logformat; | ||
| 73 | |||
| 74 | /** output fields */ | ||
| 75 | apr_array_header_t *output_fields; | ||
| 76 | |||
| 77 | /** filter configuration */ | ||
| 78 | apr_array_header_t *linefilters; | ||
| 79 | apr_array_header_t *prefilters; | ||
| 80 | apr_array_header_t *postfilters; | ||
| 81 | |||
| 82 | /** Dry Run */ | ||
| 83 | int dryrun; | ||
| 84 | /** dump configuration only */ | ||
| 85 | int dump; | ||
| 86 | |||
| 87 | /* Show the summary */ | ||
| 88 | int summary; | ||
| 89 | }; | ||
| 90 | |||
| 91 | typedef struct config_filestat_t config_filestat_t; | ||
| 92 | struct config_filestat_t { | ||
| 93 | const char *fname; | ||
| 94 | apr_size_t linesparsed; | ||
| 95 | apr_size_t lineskipped; | ||
| 96 | apr_size_t linesbad; | ||
| 97 | const char *result; | ||
| 98 | apr_time_t start; | ||
| 99 | apr_time_t stop; | ||
| 100 | }; | ||
| 101 | |||
| 102 | typedef struct config_logformat_t config_logformat_t; | ||
| 103 | struct config_logformat_t { | ||
| 104 | const char *name; | ||
| 105 | apr_array_header_t *fields; | ||
| 106 | }; | ||
| 107 | |||
| 108 | typedef struct config_logformat_field_t config_logformat_field_t; | ||
| 109 | struct config_logformat_field_t { | ||
| 110 | const char *name; | ||
| 111 | const char *datatype; | ||
| 112 | }; | ||
| 113 | |||
| 114 | typedef struct config_opt_t config_opt_t; | ||
| 115 | typedef apr_status_t (*config_func_t)(config_t *cfg, config_opt_t *opt, | ||
| 116 | int argc, const char **argv); | ||
| 117 | struct config_opt_t { | ||
| 118 | const char *name; | ||
| 119 | const char *help; | ||
| 120 | config_func_t func; | ||
| 121 | void *data; | ||
| 122 | }; | ||
| 123 | |||
| 124 | typedef struct config_filter_t config_filter_t; | ||
| 125 | struct config_filter_t { | ||
| 126 | const char *field; | ||
| 127 | const char *filter; | ||
| 128 | int negative; | ||
| 129 | ap_regex_t *regex; | ||
| 130 | }; | ||
| 131 | |||
| 132 | typedef enum { | ||
| 133 | LOGSQL_DATATYPE_INT = 0, | ||
| 134 | LOGSQL_DATATYPE_SMALLINT, | ||
| 135 | LOGSQL_DATATYPE_VARCHAR, | ||
| 136 | LOGSQL_DATATYPE_CHAR, | ||
| 137 | LOGSQL_DATATYPE_BIGINT | ||
| 138 | } logsql_field_datatype; | ||
| 139 | #define logsql_field_datatyeName(x) \ | ||
| 140 | (x == LOGSQL_DATATYPE_INT ? "INT" \ | ||
| 141 | : (x == LOGSQL_DATATYPE_SMALLINT ? "SMALLINT" \ | ||
| 142 | : (x == LOGSQL_DATATYPE_VARCHAR ? "VARCHAR" \ | ||
| 143 | : (x == LOGSQL_DATATYPE_CHAR ? "CHAR" \ | ||
| 144 | : (x == LOGSQL_DATATYPE_BIGINT ? "BIGINT" : "ERR"))))) | ||
| 145 | |||
| 146 | typedef struct config_output_field_t config_output_field_t; | ||
| 147 | |||
| 148 | typedef struct parser_func_t parser_func_t; | ||
| 149 | |||
| 150 | struct config_output_field_t { | ||
| 151 | const char *field; | ||
| 152 | logsql_field_datatype datatype; | ||
| 153 | apr_size_t size; | ||
| 154 | const char *def; | ||
| 155 | const char *source; | ||
| 156 | const char *fname; | ||
| 157 | parser_func_t *func; | ||
| 158 | const char **args; | ||
| 159 | void *data; | ||
| 160 | }; | ||
| 161 | |||
| 162 | #define CHECK_YESNO(c) ((!strcasecmp(c,"on") || !strcasecmp(c,"yes")) ? 1 : 0) | ||
| 163 | |||
| 164 | /** | ||
| 165 | * Initialize the config parser | ||
| 166 | */ | ||
| 167 | void config_init(apr_pool_t *p); | ||
| 168 | |||
| 169 | /** | ||
| 170 | * Dump the configuration to stdout | ||
| 171 | */ | ||
| 172 | void config_dump(config_t *cfg); | ||
| 173 | |||
| 174 | /** | ||
| 175 | * Checks the configuration to ensure all the required settings are set | ||
| 176 | */ | ||
| 177 | apr_status_t config_check(config_t *cfg); | ||
| 178 | |||
| 179 | /** | ||
| 180 | * Creates the default configuration | ||
| 181 | */ | ||
| 182 | config_t *config_create(apr_pool_t *p); | ||
| 183 | |||
| 184 | /** | ||
| 185 | * Read in a configuration file | ||
| 186 | */ | ||
| 187 | apr_status_t config_read(config_t *cfg, const char *filename, | ||
| 188 | apr_table_t *merge); | ||
| 189 | |||
| 190 | #endif /*CONFIG_H_*/ | ||
diff --git a/utility/database.c b/utility/database.c new file mode 100644 index 0000000..ff81caa --- /dev/null +++ b/utility/database.c | |||
| @@ -0,0 +1,212 @@ | |||
| 1 | #include "database.h" | ||
| 2 | #include "apu.h" | ||
| 3 | #include "apr_dbd.h" | ||
| 4 | #include "apr_strings.h" | ||
| 5 | |||
| 6 | #include "util.h" | ||
| 7 | #include "autoconfig.h" | ||
| 8 | |||
| 9 | struct config_dbd_t { | ||
| 10 | const apr_dbd_driver_t *driver; | ||
| 11 | apr_dbd_t *dbd; | ||
| 12 | apr_dbd_prepared_t *stmt; | ||
| 13 | apr_dbd_transaction_t *txn; | ||
| 14 | const char **args; | ||
| 15 | }; | ||
| 16 | |||
| 17 | void database_init(apr_pool_t *p) | ||
| 18 | { | ||
| 19 | apr_dbd_init(p); | ||
| 20 | } | ||
| 21 | |||
| 22 | /** @todo split this into load and connect */ | ||
| 23 | apr_status_t database_connect(config_t *cfg, config_dbd_t **dbconn) | ||
| 24 | { | ||
| 25 | apr_status_t rv; | ||
| 26 | if (!cfg->dbdriver || !cfg->dbparams) | ||
| 27 | return APR_EINVAL; | ||
| 28 | if (!*dbconn) { | ||
| 29 | *dbconn = apr_pcalloc(cfg->pool, sizeof(config_dbd_t)); | ||
| 30 | } | ||
| 31 | rv = apr_dbd_get_driver(cfg->pool, cfg->dbdriver, &((*dbconn)->driver)); | ||
| 32 | if (rv) { | ||
| 33 | |||
| 34 | logging_log(cfg, LOGLEVEL_ERROR, | ||
| 35 | "DB: Could not load database driver %s. Error %s", | ||
| 36 | cfg->dbdriver, logging_strerror(rv)); | ||
| 37 | return rv; | ||
| 38 | } | ||
| 39 | |||
| 40 | rv = apr_dbd_open((*dbconn)->driver, cfg->pool, cfg->dbparams, | ||
| 41 | &((*dbconn)->dbd)); | ||
| 42 | if (rv) { | ||
| 43 | logging_log(cfg, LOGLEVEL_ERROR, | ||
| 44 | "DB: Could not connect to database. Error (%d)%s", rv, | ||
| 45 | logging_strerror(rv)); | ||
| 46 | return rv; | ||
| 47 | } | ||
| 48 | |||
| 49 | return APR_SUCCESS; | ||
| 50 | } | ||
| 51 | |||
| 52 | apr_status_t database_disconnect(config_dbd_t *dbconn) | ||
| 53 | { | ||
| 54 | return apr_dbd_close(dbconn->driver, dbconn->dbd); | ||
| 55 | } | ||
| 56 | |||
| 57 | static apr_dbd_prepared_t *database_prepare_insert(config_t *cfg, | ||
| 58 | config_dbd_t *dbconn, apr_pool_t *p) | ||
| 59 | { | ||
| 60 | apr_status_t rv; | ||
| 61 | char *sql; | ||
| 62 | int i, f; | ||
| 63 | struct iovec *vec; | ||
| 64 | apr_dbd_prepared_t *stmt= NULL; | ||
| 65 | int nfs = cfg->output_fields->nelts; | ||
| 66 | config_output_field_t *ofields; | ||
| 67 | |||
| 68 | ofields = (config_output_field_t *)cfg->output_fields->elts; | ||
| 69 | |||
| 70 | vec = apr_palloc(p, (nfs*2 + 5) * sizeof(struct iovec)); | ||
| 71 | sql = apr_palloc(p, (nfs*3)); | ||
| 72 | |||
| 73 | vec[0].iov_base = "INSERT INTO "; | ||
| 74 | vec[0].iov_len = 12; | ||
| 75 | vec[1].iov_base = (void *)cfg->table; | ||
| 76 | vec[1].iov_len = strlen(cfg->table); | ||
| 77 | vec[2].iov_base = " ("; | ||
| 78 | vec[2].iov_len = 2; | ||
| 79 | for (i=3, f=0; f<nfs; f++, i+=2) { | ||
| 80 | vec[i].iov_base = (void *)ofields[f].field; | ||
| 81 | vec[i].iov_len = strlen(vec[i].iov_base); | ||
| 82 | vec[i+1].iov_base = ","; | ||
| 83 | vec[i+1].iov_len = 1; | ||
| 84 | memcpy(&sql[f*3], "%s,", 3); | ||
| 85 | } | ||
| 86 | sql[nfs*3-1] = '\0'; | ||
| 87 | vec[i-1].iov_base = ") VALUES ("; | ||
| 88 | vec[i-1].iov_len = 10; | ||
| 89 | vec[i].iov_base = sql; | ||
| 90 | vec[i].iov_len = nfs*3-1; | ||
| 91 | vec[i+1].iov_base = ")"; | ||
| 92 | vec[i+1].iov_len = 1; | ||
| 93 | |||
| 94 | sql = apr_pstrcatv(p, vec, i+2, NULL); | ||
| 95 | |||
| 96 | logging_log(cfg, LOGLEVEL_DEBUG, "DB: Generated SQL: %s", sql); | ||
| 97 | |||
| 98 | rv = apr_dbd_prepare(dbconn->driver, cfg->pool, dbconn->dbd, sql, | ||
| 99 | "INSERT", &stmt); | ||
| 100 | |||
| 101 | if (rv) { | ||
| 102 | logging_log(cfg, LOGLEVEL_NOISE, | ||
| 103 | "DB: Unable to Prepare SQL insert: %s", apr_dbd_error( | ||
| 104 | dbconn->driver, dbconn->dbd, rv)); | ||
| 105 | return NULL; | ||
| 106 | } | ||
| 107 | return stmt; | ||
| 108 | } | ||
| 109 | |||
| 110 | apr_status_t database_insert(config_t *cfg, config_dbd_t *dbconn, | ||
| 111 | apr_pool_t *p, apr_table_t *data) | ||
| 112 | { | ||
| 113 | apr_status_t rv; | ||
| 114 | int f, nfs; | ||
| 115 | config_output_field_t *ofields; | ||
| 116 | ofields = (config_output_field_t *)cfg->output_fields->elts; | ||
| 117 | nfs = cfg->output_fields->nelts; | ||
| 118 | // Prepare statement | ||
| 119 | if (!dbconn->stmt) { | ||
| 120 | dbconn->stmt = database_prepare_insert(cfg, dbconn, p); | ||
| 121 | if (!dbconn->stmt) { | ||
| 122 | return APR_EINVAL; | ||
| 123 | } | ||
| 124 | dbconn->args = apr_palloc(cfg->pool, nfs * sizeof(char *)); | ||
| 125 | } | ||
| 126 | for (f=0; f<nfs; f++) { | ||
| 127 | dbconn->args[f] = apr_table_get(data, ofields[f].field); | ||
| 128 | } | ||
| 129 | rv = apr_dbd_pquery(dbconn->driver, p, dbconn->dbd, &f, | ||
| 130 | dbconn->stmt, nfs, dbconn->args); | ||
| 131 | if (rv) { | ||
| 132 | logging_log(cfg, LOGLEVEL_ERROR, "DB: Unable to Insert SQL: %s", | ||
| 133 | apr_dbd_error(dbconn->driver, dbconn->dbd, rv)); | ||
| 134 | return rv; | ||
| 135 | } | ||
| 136 | return APR_SUCCESS; | ||
| 137 | } | ||
| 138 | |||
| 139 | apr_status_t database_trans_start(config_t *cfg, config_dbd_t *dbconn, | ||
| 140 | apr_pool_t *p) | ||
| 141 | { | ||
| 142 | #if HAVE_APR_DBD_TRANSACTION_MODE_GET | ||
| 143 | apr_status_t rv; | ||
| 144 | if (!cfg->transactions) | ||
| 145 | return APR_SUCCESS; | ||
| 146 | if (dbconn->txn) { | ||
| 147 | logging_log(cfg, LOGLEVEL_NOISE, | ||
| 148 | "Transaction Already Started. Something is BROKE"); | ||
| 149 | return APR_EINVAL; | ||
| 150 | } | ||
| 151 | logging_log(cfg, LOGLEVEL_DEBUG, "DB: Starting Transaction"); | ||
| 152 | rv = apr_dbd_transaction_start(dbconn->driver, p, dbconn->dbd, | ||
| 153 | &dbconn->txn); | ||
| 154 | if (rv) | ||
| 155 | logging_log(cfg, LOGLEVEL_NOISE, | ||
| 156 | "DB: Error Starting Transaction: (%d)%s", rv, apr_dbd_error( | ||
| 157 | dbconn->driver, dbconn->dbd, rv)); | ||
| 158 | return rv; | ||
| 159 | #else | ||
| 160 | return APR_SUCCESS; | ||
| 161 | #endif | ||
| 162 | } | ||
| 163 | |||
| 164 | apr_status_t database_trans_stop(config_t *cfg, config_dbd_t *dbconn, | ||
| 165 | apr_pool_t *p) | ||
| 166 | { | ||
| 167 | #if HAVE_APR_DBD_TRANSACTION_MODE_GET | ||
| 168 | apr_status_t rv; | ||
| 169 | if (!cfg->transactions) | ||
| 170 | return APR_SUCCESS; | ||
| 171 | if (!dbconn->txn) { | ||
| 172 | logging_log(cfg, LOGLEVEL_NOISE, | ||
| 173 | "No Transaction Started. Something is BROKE"); | ||
| 174 | return APR_EINVAL; | ||
| 175 | } | ||
| 176 | logging_log(cfg, LOGLEVEL_DEBUG, "DB: Stopping Transaction"); | ||
| 177 | rv = apr_dbd_transaction_end(dbconn->driver, p, dbconn->txn); | ||
| 178 | if (rv) | ||
| 179 | logging_log(cfg, LOGLEVEL_NOISE, | ||
| 180 | "DB: Error Stopping Transaction: (%d)%s", rv, apr_dbd_error( | ||
| 181 | dbconn->driver, dbconn->dbd, rv)); | ||
| 182 | |||
| 183 | dbconn->txn = NULL; | ||
| 184 | return rv; | ||
| 185 | #else | ||
| 186 | return APR_SUCCESS; | ||
| 187 | #endif | ||
| 188 | } | ||
| 189 | |||
| 190 | apr_status_t database_trans_abort(config_t *cfg, config_dbd_t *dbconn) | ||
| 191 | { | ||
| 192 | #if HAVE_APR_DBD_TRANSACTION_MODE_GET | ||
| 193 | apr_status_t rv; | ||
| 194 | if (!cfg->transactions) | ||
| 195 | return APR_SUCCESS; | ||
| 196 | if (!dbconn->txn) { | ||
| 197 | logging_log(cfg, LOGLEVEL_NOISE, | ||
| 198 | "No Transaction Started. Something is BROKE"); | ||
| 199 | return APR_EINVAL; | ||
| 200 | } | ||
| 201 | logging_log(cfg, LOGLEVEL_NOTICE, "DB: Aborting Transaction"); | ||
| 202 | rv = apr_dbd_transaction_mode_set(dbconn->driver, dbconn->txn, | ||
| 203 | APR_DBD_TRANSACTION_ROLLBACK); | ||
| 204 | if (rv) | ||
| 205 | logging_log(cfg, LOGLEVEL_NOISE, | ||
| 206 | "DB: Error Aborting Transaction: (%d)%s", rv, apr_dbd_error( | ||
| 207 | dbconn->driver, dbconn->dbd, rv)); | ||
| 208 | return rv; | ||
| 209 | #else | ||
| 210 | return APR_SUCCESS; | ||
| 211 | #endif | ||
| 212 | } | ||
diff --git a/utility/database.h b/utility/database.h new file mode 100644 index 0000000..9797d51 --- /dev/null +++ b/utility/database.h | |||
| @@ -0,0 +1,25 @@ | |||
| 1 | #ifndef DATABASE_H_ | ||
| 2 | #define DATABASE_H_ | ||
| 3 | |||
| 4 | #include "apr_pools.h" | ||
| 5 | |||
| 6 | #include "config.h" | ||
| 7 | |||
| 8 | void database_init(apr_pool_t *p); | ||
| 9 | |||
| 10 | apr_status_t database_connect(config_t *cfg, config_dbd_t **dbconn); | ||
| 11 | |||
| 12 | apr_status_t database_disconnect(config_dbd_t *dbconn); | ||
| 13 | |||
| 14 | apr_status_t database_insert(config_t *cfg, config_dbd_t *dbconn, | ||
| 15 | apr_pool_t *p, apr_table_t *data); | ||
| 16 | |||
| 17 | apr_status_t database_trans_start(config_t *cfg, config_dbd_t *dbconn, | ||
| 18 | apr_pool_t *p); | ||
| 19 | |||
| 20 | apr_status_t database_trans_stop(config_t *cfg, config_dbd_t *dbconn, | ||
| 21 | apr_pool_t *p); | ||
| 22 | |||
| 23 | apr_status_t database_trans_abort(config_t *cfg, config_dbd_t *dbconn); | ||
| 24 | |||
| 25 | #endif /*DATABASE_H_*/ | ||
diff --git a/utility/logparse.c b/utility/logparse.c new file mode 100644 index 0000000..1b3cc97 --- /dev/null +++ b/utility/logparse.c | |||
| @@ -0,0 +1,768 @@ | |||
| 1 | #include "logparse.h" | ||
| 2 | #include "apr_file_info.h" | ||
| 3 | #include "apr_file_io.h" | ||
| 4 | #include "apr_strings.h" | ||
| 5 | #include "apr_time.h" | ||
| 6 | |||
| 7 | #include "time.h" | ||
| 8 | #include "stdlib.h" | ||
| 9 | |||
| 10 | #include "util.h" | ||
| 11 | #include "ap_pcre.h" | ||
| 12 | #include "database.h" | ||
| 13 | |||
| 14 | apr_hash_t *g_parser_funcs; | ||
| 15 | void **g_parser_linedata; | ||
| 16 | |||
| 17 | static apr_status_t parser_func_wrap(apr_pool_t *p, config_t *cfg, | ||
| 18 | config_output_field_t *field, const char *value, const char **ret) | ||
| 19 | { | ||
| 20 | if (field->args[0] && field->args[1]) { | ||
| 21 | struct iovec vec[3]; | ||
| 22 | apr_size_t len; | ||
| 23 | |||
| 24 | vec[0].iov_base = (void *)field->args[0]; | ||
| 25 | vec[0].iov_len = strlen(field->args[0]); | ||
| 26 | vec[1].iov_base = (void *)value; | ||
| 27 | vec[1].iov_len = strlen(value); | ||
| 28 | vec[2].iov_base = (void *)field->args[1]; | ||
| 29 | vec[2].iov_len = strlen(field->args[1]); | ||
| 30 | |||
| 31 | *ret = apr_pstrcatv(p, vec, 3, &len); | ||
| 32 | } else { | ||
| 33 | logging_log(cfg, LOGLEVEL_NOISE, "wrap requires before and after strings"); | ||
| 34 | return APR_EINVAL; | ||
| 35 | } | ||
| 36 | return APR_SUCCESS; | ||
| 37 | } | ||
| 38 | |||
| 39 | static apr_status_t parser_func_regexmatch(apr_pool_t *p, config_t *cfg, | ||
| 40 | config_output_field_t *field, const char *value, const char **ret) | ||
| 41 | { | ||
| 42 | struct { | ||
| 43 | ap_regex_t *rx; | ||
| 44 | const char *substr; | ||
| 45 | }*_data; | ||
| 46 | ap_regmatch_t regm[AP_MAX_REG_MATCH]; | ||
| 47 | // Check if a regular expression configured | ||
| 48 | if (!field->args[0]) | ||
| 49 | return APR_EINVAL; | ||
| 50 | if (!field->data) { | ||
| 51 | // pre compile the regex | ||
| 52 | _data = apr_palloc(cfg->pool, sizeof(ap_regex_t)+sizeof(const char *)); | ||
| 53 | _data->rx = ap_pregcomp(cfg->pool, field->args[0], | ||
| 54 | AP_REG_EXTENDED|AP_REG_ICASE); | ||
| 55 | if (field->args[1]) { | ||
| 56 | _data->substr = field->args[1]; | ||
| 57 | } else { | ||
| 58 | _data->substr = "$1"; | ||
| 59 | } | ||
| 60 | if (!_data->rx) { | ||
| 61 | logging_log(cfg, LOGLEVEL_NOISE, "Failed to compile regular expression"); | ||
| 62 | return APR_EINVAL; | ||
| 63 | } | ||
| 64 | field->data = _data; | ||
| 65 | } else | ||
| 66 | _data = field->data; | ||
| 67 | |||
| 68 | if (!ap_regexec(_data->rx, value, AP_MAX_REG_MATCH, regm, 0)) { | ||
| 69 | *ret = ap_pregsub(p, _data->substr, value, AP_MAX_REG_MATCH, regm); | ||
| 70 | } | ||
| 71 | logging_log(cfg, LOGLEVEL_DEBUG, "REGEX: matched %s against %s to %s", value, | ||
| 72 | field->args[0], *ret); | ||
| 73 | return APR_SUCCESS; | ||
| 74 | } | ||
| 75 | |||
| 76 | static apr_status_t parser_func_totimestamp(apr_pool_t *p, config_t *cfg, | ||
| 77 | config_output_field_t *field, const char *value, const char **ret) | ||
| 78 | { | ||
| 79 | time_t time; | ||
| 80 | struct tm ts; | ||
| 81 | |||
| 82 | //memset(&ts,0,sizeof(struct tm)); | ||
| 83 | |||
| 84 | strptime(value, "%d/%b/%Y:%H:%M:%S %z", &ts); | ||
| 85 | time = mktime(&ts); | ||
| 86 | |||
| 87 | *ret = apr_itoa(p, time); | ||
| 88 | return APR_SUCCESS; | ||
| 89 | } | ||
| 90 | |||
| 91 | static apr_status_t parser_func_machineid(apr_pool_t *p, config_t *cfg, | ||
| 92 | config_output_field_t *field, const char *value, const char **ret) | ||
| 93 | { | ||
| 94 | if (cfg->machineid) { | ||
| 95 | *ret = apr_pstrdup(p, cfg->machineid); | ||
| 96 | } | ||
| 97 | return APR_SUCCESS; | ||
| 98 | } | ||
| 99 | |||
| 100 | static apr_status_t parser_func_queryarg(apr_pool_t *p, config_t *cfg, | ||
| 101 | config_output_field_t *field, const char *value, const char **ret) | ||
| 102 | { | ||
| 103 | apr_table_t *query = parser_get_linedata(field->func); | ||
| 104 | |||
| 105 | if (!field->args[0]) { | ||
| 106 | logging_log(cfg, LOGLEVEL_NOISE, "queryarg requires name of query arg"); | ||
| 107 | return APR_EINVAL; | ||
| 108 | } | ||
| 109 | |||
| 110 | if (!query) { | ||
| 111 | char *query_beg; | ||
| 112 | |||
| 113 | query = apr_table_make(p,3); | ||
| 114 | |||
| 115 | query_beg = strchr(value, '?'); | ||
| 116 | // if we have a query string, rip it apart | ||
| 117 | if (query_beg) { | ||
| 118 | char *key; | ||
| 119 | char *value; | ||
| 120 | const char *delim = "&"; | ||
| 121 | char *query_string; | ||
| 122 | char *strtok_state; | ||
| 123 | char *query_end = strrchr(++query_beg,' '); | ||
| 124 | |||
| 125 | query_string = apr_pstrndup(p, query_beg, query_end-query_beg); | ||
| 126 | logging_log(cfg, LOGLEVEL_DEBUG, "QUERY: Found String %pp, %pp, %s", | ||
| 127 | query_beg, query_end, query_string); | ||
| 128 | if (field->args[1]) { | ||
| 129 | delim = field->args[1]; | ||
| 130 | } | ||
| 131 | key = apr_strtok(query_string, delim, &strtok_state); | ||
| 132 | while (key) { | ||
| 133 | value = strchr(key, '='); | ||
| 134 | if (value) { | ||
| 135 | *value = '\0'; /* Split the string in two */ | ||
| 136 | value++; /* Skip past the = */ | ||
| 137 | } | ||
| 138 | else { | ||
| 139 | value = "1"; | ||
| 140 | } | ||
| 141 | ap_unescape_url(key); | ||
| 142 | ap_unescape_url(value); | ||
| 143 | apr_table_set(query, key, value); | ||
| 144 | |||
| 145 | logging_log(cfg, LOGLEVEL_DEBUG, | ||
| 146 | "QUERY: Found arg: %s = %s", key, value); | ||
| 147 | |||
| 148 | key = apr_strtok(NULL, delim, &strtok_state); | ||
| 149 | } | ||
| 150 | } | ||
| 151 | parser_set_linedata(field->func,query); | ||
| 152 | } | ||
| 153 | *ret = apr_table_get(query, field->args[0]); | ||
| 154 | return APR_SUCCESS; | ||
| 155 | } | ||
| 156 | |||
| 157 | parser_func_t *parser_get_func(const char *name) | ||
| 158 | { | ||
| 159 | return apr_hash_get(g_parser_funcs, name, APR_HASH_KEY_STRING); | ||
| 160 | } | ||
| 161 | |||
| 162 | static void parser_add_func(apr_pool_t *p, const char *const name, | ||
| 163 | parser_func_f func, int id) | ||
| 164 | { | ||
| 165 | parser_func_t *s; | ||
| 166 | if (!g_parser_funcs) { | ||
| 167 | g_parser_funcs = apr_hash_make(p); | ||
| 168 | } | ||
| 169 | s = apr_palloc(p, sizeof(parser_func_t)); | ||
| 170 | s->func = func; | ||
| 171 | s->pos = id; | ||
| 172 | s->data = NULL; | ||
| 173 | s->linedata = &g_parser_linedata; | ||
| 174 | apr_hash_set(g_parser_funcs, lowerstr(p, name), APR_HASH_KEY_STRING, s); | ||
| 175 | } | ||
| 176 | |||
| 177 | void parser_init(apr_pool_t *p) | ||
| 178 | { | ||
| 179 | int i = 0; | ||
| 180 | parser_add_func(p, "regexmatch", parser_func_regexmatch, ++i); | ||
| 181 | parser_add_func(p, "totimestamp", parser_func_totimestamp, ++i); | ||
| 182 | parser_add_func(p, "machineid", parser_func_machineid, ++i); | ||
| 183 | parser_add_func(p, "queryarg", parser_func_queryarg, ++i); | ||
| 184 | parser_add_func(p, "wrap", parser_func_wrap, ++i); | ||
| 185 | g_parser_linedata = apr_pcalloc(p, sizeof(void *) * (i+1)); | ||
| 186 | g_parser_linedata[0] = (void *)i; | ||
| 187 | } | ||
| 188 | |||
| 189 | void parser_find_logs(config_t *cfg) | ||
| 190 | { | ||
| 191 | apr_pool_t *tp; | ||
| 192 | apr_dir_t *dir; | ||
| 193 | apr_finfo_t finfo; | ||
| 194 | config_filestat_t *newp; | ||
| 195 | |||
| 196 | logging_log(cfg, LOGLEVEL_NOTICE, "Find Log files"); | ||
| 197 | if (!cfg->input_dir) | ||
| 198 | return; | ||
| 199 | apr_pool_create(&tp, cfg->pool); | ||
| 200 | if (apr_dir_open(&dir, cfg->input_dir, tp)==APR_SUCCESS) { | ||
| 201 | while (apr_dir_read(&finfo, APR_FINFO_NAME | APR_FINFO_TYPE, dir) | ||
| 202 | == APR_SUCCESS) { | ||
| 203 | char *temp; | ||
| 204 | if (finfo.filetype == APR_DIR) | ||
| 205 | continue; | ||
| 206 | newp = (config_filestat_t *)apr_array_push(cfg->input_files); | ||
| 207 | newp->result = "Not Parsed"; | ||
| 208 | apr_filepath_merge(&temp, cfg->input_dir, finfo.name, | ||
| 209 | APR_FILEPATH_TRUENAME, cfg->pool); | ||
| 210 | newp->fname = temp; | ||
| 211 | } | ||
| 212 | apr_dir_close(dir); | ||
| 213 | } | ||
| 214 | apr_pool_destroy(tp); | ||
| 215 | } | ||
| 216 | |||
| 217 | #define BUFFER_SIZE (16 * 1024) | ||
| 218 | |||
| 219 | void parser_split_logs(config_t *cfg) | ||
| 220 | { | ||
| 221 | apr_pool_t *tp, *tfp; | ||
| 222 | apr_array_header_t *foundfiles; | ||
| 223 | config_filestat_t *filelist; | ||
| 224 | config_filestat_t *newfile; | ||
| 225 | apr_file_t *infile; | ||
| 226 | int f, l; | ||
| 227 | apr_status_t rv; | ||
| 228 | apr_finfo_t finfo; | ||
| 229 | char buff[BUFFER_SIZE]; | ||
| 230 | int linecount; | ||
| 231 | int piecesize; | ||
| 232 | |||
| 233 | if (!cfg->split_enabled) return; | ||
| 234 | if (!cfg->split_dir) { | ||
| 235 | logging_log(cfg, LOGLEVEL_NOISE, "SPLITTER: Missing Split Output directory"); | ||
| 236 | return; | ||
| 237 | } | ||
| 238 | apr_pool_create(&tp, cfg->pool); | ||
| 239 | apr_pool_create(&tfp, tp); | ||
| 240 | |||
| 241 | if (APR_SUCCESS != apr_stat(&finfo, cfg->split_dir, APR_FINFO_MIN, tp)) { | ||
| 242 | logging_log(cfg, LOGLEVEL_NOISE, "SPLITTER: Directory %s does not exist", cfg->split_dir); | ||
| 243 | return; | ||
| 244 | } | ||
| 245 | foundfiles = apr_array_copy(tp, cfg->input_files); | ||
| 246 | apr_array_clear(cfg->input_files); | ||
| 247 | |||
| 248 | filelist = (config_filestat_t *)foundfiles->elts; | ||
| 249 | for (f=0, l=foundfiles->nelts; f < l; f++) { | ||
| 250 | apr_pool_clear(tfp); | ||
| 251 | logging_log(cfg, LOGLEVEL_NOTICE, "SPLITTER: Begin Splitting Log File '%s'", filelist[f].fname); | ||
| 252 | rv = apr_file_open(&infile, filelist[f].fname, APR_FOPEN_READ, APR_OS_DEFAULT, tfp); | ||
| 253 | |||
| 254 | if (rv != APR_SUCCESS) { | ||
| 255 | logging_log(cfg, LOGLEVEL_NOISE, "SPLITTER: Could not open %s", filelist[f].fname); | ||
| 256 | return; | ||
| 257 | } | ||
| 258 | linecount = 0; | ||
| 259 | while (apr_file_eof(infile) == APR_SUCCESS) { | ||
| 260 | apr_size_t read = BUFFER_SIZE; | ||
| 261 | char *p; | ||
| 262 | apr_file_read(infile, buff, &read); | ||
| 263 | p = buff; | ||
| 264 | while ((p = memchr(p, '\n', (buff + read) - p))) { | ||
| 265 | ++p; | ||
| 266 | ++linecount; | ||
| 267 | } | ||
| 268 | } | ||
| 269 | // now we know how long it is. Lets split up the file | ||
| 270 | piecesize = linecount / cfg->split_count; | ||
| 271 | if (piecesize < cfg->split_minimum) | ||
| 272 | piecesize = cfg->split_minimum; | ||
| 273 | if (piecesize > cfg->split_maximum && cfg->split_maximum > 0) | ||
| 274 | piecesize = cfg->split_maximum; | ||
| 275 | if (piecesize > linecount) { | ||
| 276 | // File is smaller than piece size just add it back in as is | ||
| 277 | newfile = (config_filestat_t *)apr_array_push(cfg->input_files); | ||
| 278 | newfile->result = "Not Parsed"; | ||
| 279 | newfile->fname = filelist[f].fname; | ||
| 280 | } else { | ||
| 281 | //split apart the files | ||
| 282 | int cur_line = 0; | ||
| 283 | int file_count = 1; | ||
| 284 | int out_lines = 0; | ||
| 285 | const char *basefile, *file; | ||
| 286 | apr_file_t *outfile; | ||
| 287 | char trail[2048]; | ||
| 288 | apr_size_t trail_size = 0; | ||
| 289 | apr_size_t write; | ||
| 290 | apr_off_t off = 0; | ||
| 291 | |||
| 292 | apr_file_seek(infile, APR_SET, &off); | ||
| 293 | |||
| 294 | basefile = apr_pstrdup(tfp, basename(apr_pstrdup(tfp, filelist[f].fname))); | ||
| 295 | |||
| 296 | file = apr_psprintf(tfp, "%s/%s-%d", cfg->split_dir, basefile, file_count++); | ||
| 297 | logging_log(cfg, LOGLEVEL_NOTICE, "SPLITTER: Creating output file %s", file); | ||
| 298 | rv = apr_file_open(&outfile, file, APR_FOPEN_WRITE | APR_FOPEN_CREATE | APR_FOPEN_TRUNCATE, APR_OS_DEFAULT, tfp); | ||
| 299 | if (rv != APR_SUCCESS) { | ||
| 300 | logging_log(cfg, LOGLEVEL_NOISE, "SPLITTER: Could not open %s (%d)", file, rv); | ||
| 301 | return; | ||
| 302 | } | ||
| 303 | newfile = (config_filestat_t *)apr_array_push(cfg->input_files); | ||
| 304 | newfile->result = "Not Parsed"; | ||
| 305 | newfile->fname = apr_pstrdup(cfg->pool, file); | ||
| 306 | |||
| 307 | while (apr_file_eof(infile) == APR_SUCCESS) { | ||
| 308 | apr_size_t read = BUFFER_SIZE; | ||
| 309 | char *p, *pp, *buff_start; | ||
| 310 | apr_file_read(infile, buff, &read); | ||
| 311 | buff_start = p = pp = buff; | ||
| 312 | if (trail_size) { | ||
| 313 | p = memchr(p, '\n', (buff + read) - p); | ||
| 314 | if (p) { | ||
| 315 | //printf("Trail Line: %p, %p, %d\n", pp, p, (p - pp) + trail_size); | ||
| 316 | ++p; | ||
| 317 | pp = p; | ||
| 318 | ++cur_line; | ||
| 319 | ++out_lines; | ||
| 320 | // write out to file | ||
| 321 | apr_file_write(outfile, trail, &trail_size); | ||
| 322 | trail_size = 0; | ||
| 323 | } else { | ||
| 324 | if ((read + trail_size) > 2048) { | ||
| 325 | logging_log(cfg, LOGLEVEL_NOISE, "SPLITTER: Excessively long line %d in file %s", cur_line, filelist[f].fname); | ||
| 326 | exit(1); | ||
| 327 | } else { | ||
| 328 | memcpy(trail+trail_size, buff, read); | ||
| 329 | trail_size += read; | ||
| 330 | } | ||
| 331 | } | ||
| 332 | } | ||
| 333 | while ((p = memchr(p, '\n', (buff + read) - p))) { | ||
| 334 | //printf("Line: %p, %p, %d\n", pp, p, (p - pp)); | ||
| 335 | if (out_lines == piecesize) { | ||
| 336 | // Write out to file | ||
| 337 | write = pp - buff_start; | ||
| 338 | apr_file_write(outfile, buff_start, &write); | ||
| 339 | buff_start = pp; | ||
| 340 | out_lines = 0; | ||
| 341 | // Open new file | ||
| 342 | file = apr_psprintf(tfp, "%s/%s-%d", cfg->split_dir, basefile, file_count++); | ||
| 343 | logging_log(cfg, LOGLEVEL_NOTICE, "SPLITTER: Creating output file %s", file); | ||
| 344 | rv = apr_file_open(&outfile, file, APR_FOPEN_WRITE | APR_FOPEN_CREATE | APR_FOPEN_TRUNCATE, APR_OS_DEFAULT, tfp); | ||
| 345 | if (rv != APR_SUCCESS) { | ||
| 346 | logging_log(cfg, LOGLEVEL_NOISE, "SPLITTER: Could not open %s (%d)", file, rv); | ||
| 347 | return; | ||
| 348 | } | ||
| 349 | newfile = (config_filestat_t *)apr_array_push(cfg->input_files); | ||
| 350 | newfile->result = "Not Parsed"; | ||
| 351 | newfile->fname = apr_pstrdup(cfg->pool, file); | ||
| 352 | } | ||
| 353 | ++p; | ||
| 354 | pp = p; | ||
| 355 | ++cur_line; | ||
| 356 | ++out_lines; | ||
| 357 | } | ||
| 358 | // Write out to file | ||
| 359 | write = pp - buff_start; | ||
| 360 | apr_file_write(outfile, buff_start, &write); | ||
| 361 | |||
| 362 | trail_size = (buff+read) - pp; | ||
| 363 | if (trail_size) { | ||
| 364 | memcpy(trail, pp, trail_size); | ||
| 365 | } | ||
| 366 | } | ||
| 367 | } | ||
| 368 | } | ||
| 369 | apr_pool_destroy(tfp); | ||
| 370 | apr_pool_destroy(tp); | ||
| 371 | } | ||
| 372 | |||
| 373 | apr_status_t parser_logbadline(config_t *cfg, const char *filename, | ||
| 374 | const char *badline) | ||
| 375 | { | ||
| 376 | apr_status_t rv = APR_SUCCESS; | ||
| 377 | apr_size_t len; | ||
| 378 | struct iovec vec[5]; | ||
| 379 | |||
| 380 | if (cfg->badlinefile) { | ||
| 381 | if (!cfg->badline_fp) { | ||
| 382 | rv = apr_file_open(&cfg->badline_fp, cfg->badlinefile, | ||
| 383 | APR_FOPEN_CREATE | APR_FOPEN_WRITE | APR_FOPEN_APPEND, | ||
| 384 | APR_OS_DEFAULT, cfg->pool); | ||
| 385 | if (rv) { | ||
| 386 | logging_log(cfg, LOGLEVEL_NOISE, | ||
| 387 | "Error opening badline file %s\n", cfg->badlinefile); | ||
| 388 | cfg->badlinefile = NULL; | ||
| 389 | } | ||
| 390 | } | ||
| 391 | if (!rv) { | ||
| 392 | if (filename != cfg->badlastfile){ | ||
| 393 | char date[APR_RFC822_DATE_LEN]; | ||
| 394 | vec[0].iov_base = "Starting BadLines for \""; | ||
| 395 | vec[0].iov_len = sizeof("Starting BadLines for \"")-1; | ||
| 396 | vec[1].iov_base = (void *)filename; | ||
| 397 | vec[1].iov_len = strlen(filename); | ||
| 398 | vec[2].iov_base = "\" on "; | ||
| 399 | vec[2].iov_len = sizeof("\" on ")-1; | ||
| 400 | apr_rfc822_date(date, apr_time_now()); | ||
| 401 | vec[3].iov_base = date; | ||
| 402 | vec[3].iov_len = APR_RFC822_DATE_LEN-1; | ||
| 403 | vec[4].iov_base = "\n"; | ||
| 404 | vec[4].iov_len = 1; | ||
| 405 | apr_file_writev(cfg->badline_fp, vec,5, &len); | ||
| 406 | cfg->badlastfile = filename; | ||
| 407 | } | ||
| 408 | |||
| 409 | if ((++cfg->badline_count) > cfg->badlinemax) { | ||
| 410 | logging_log(cfg, LOGLEVEL_NOISE, | ||
| 411 | "Found more than %d bad lines (found %d)", | ||
| 412 | cfg->badlinemax, cfg->badline_count); | ||
| 413 | rv = APR_EINVAL; | ||
| 414 | } else { | ||
| 415 | vec[0].iov_base = (void *)badline; | ||
| 416 | vec[0].iov_len = strlen(badline); | ||
| 417 | vec[1].iov_base = "\n"; | ||
| 418 | vec[1].iov_len = 1; | ||
| 419 | apr_file_writev(cfg->badline_fp, vec,2, &len); | ||
| 420 | } | ||
| 421 | } | ||
| 422 | } | ||
| 423 | return rv; | ||
| 424 | } | ||
| 425 | |||
| 426 | /* | ||
| 427 | * Modified version of apr_tokenize_to_argv to add [] as quoting characters | ||
| 428 | * | ||
| 429 | * token_context: Context from which pool allocations will occur. | ||
| 430 | * arg_str: Input string for conversion to argv[]. | ||
| 431 | * argv_out: Output location. This is a pointer to an array | ||
| 432 | * of pointers to strings (ie. &(char *argv[]). | ||
| 433 | * This value will be allocated from the contexts | ||
| 434 | * pool and filled in with copies of the tokens | ||
| 435 | * found during parsing of the arg_str. | ||
| 436 | */ | ||
| 437 | apr_status_t parser_tokenize_line(const char *arg_str, char ***argv_out, | ||
| 438 | apr_pool_t *token_context) | ||
| 439 | { | ||
| 440 | const char *cp; | ||
| 441 | const char *ct; | ||
| 442 | char *cleaned, *dirty; | ||
| 443 | int escaped; | ||
| 444 | int isquoted, numargs = 0, argnum; | ||
| 445 | |||
| 446 | #define SKIP_WHITESPACE(cp) \ | ||
| 447 | for ( ; *cp == ' ' || *cp == '\t'; ) { \ | ||
| 448 | cp++; \ | ||
| 449 | }; | ||
| 450 | |||
| 451 | #define CHECK_QUOTATION(cp,isquoted) \ | ||
| 452 | isquoted = 0; \ | ||
| 453 | if (*cp == '"') { \ | ||
| 454 | isquoted = 1; \ | ||
| 455 | cp++; \ | ||
| 456 | } \ | ||
| 457 | else if (*cp == '\'') { \ | ||
| 458 | isquoted = 2; \ | ||
| 459 | cp++; \ | ||
| 460 | } \ | ||
| 461 | else if (*cp == '[') { \ | ||
| 462 | isquoted = 3; \ | ||
| 463 | cp++; \ | ||
| 464 | } | ||
| 465 | |||
| 466 | /* DETERMINE_NEXTSTRING: | ||
| 467 | * At exit, cp will point to one of the following: NULL, SPACE, TAB or QUOTE. | ||
| 468 | * NULL implies the argument string has been fully traversed. | ||
| 469 | */ | ||
| 470 | #define DETERMINE_NEXTSTRING(cp,isquoted) \ | ||
| 471 | for ( ; *cp != '\0'; cp++) { \ | ||
| 472 | if ( (*cp == '\\' && (*(cp+1) == ' ' || *(cp+1) == '\t' || \ | ||
| 473 | *(cp+1) == '"' || *(cp+1) == '\'' || \ | ||
| 474 | *(cp+1) == '[' || *(cp+1) == ']'))) { \ | ||
| 475 | cp++; \ | ||
| 476 | continue; \ | ||
| 477 | } \ | ||
| 478 | if ( (!isquoted && (*cp == ' ' || *cp == '\t')) \ | ||
| 479 | || (isquoted == 1 && *cp == '"') \ | ||
| 480 | || (isquoted == 2 && *cp == '\'') \ | ||
| 481 | || (isquoted == 3 && *cp == ']') \ | ||
| 482 | ) { \ | ||
| 483 | break; \ | ||
| 484 | } \ | ||
| 485 | } | ||
| 486 | |||
| 487 | /* REMOVE_ESCAPE_CHARS: | ||
| 488 | * Compresses the arg string to remove all of the '\' escape chars. | ||
| 489 | * The final argv strings should not have any extra escape chars in it. | ||
| 490 | */ | ||
| 491 | #define REMOVE_ESCAPE_CHARS(cleaned, dirty, escaped) \ | ||
| 492 | escaped = 0; \ | ||
| 493 | while(*dirty) { \ | ||
| 494 | if (!escaped && *dirty == '\\') { \ | ||
| 495 | escaped = 1; \ | ||
| 496 | } \ | ||
| 497 | else { \ | ||
| 498 | escaped = 0; \ | ||
| 499 | *cleaned++ = *dirty; \ | ||
| 500 | } \ | ||
| 501 | ++dirty; \ | ||
| 502 | } \ | ||
| 503 | *cleaned = 0; /* last line of macro... */ | ||
| 504 | |||
| 505 | cp = arg_str; | ||
| 506 | SKIP_WHITESPACE(cp); | ||
| 507 | ct = cp; | ||
| 508 | |||
| 509 | /* This is ugly and expensive, but if anyone wants to figure a | ||
| 510 | * way to support any number of args without counting and | ||
| 511 | * allocating, please go ahead and change the code. | ||
| 512 | * | ||
| 513 | * Must account for the trailing NULL arg. | ||
| 514 | */ | ||
| 515 | numargs = 1; | ||
| 516 | while (*ct != '\0') { | ||
| 517 | CHECK_QUOTATION(ct, isquoted) | ||
| 518 | ; | ||
| 519 | DETERMINE_NEXTSTRING(ct, isquoted); | ||
| 520 | if (*ct != '\0') { | ||
| 521 | ct++; | ||
| 522 | } | ||
| 523 | numargs++; | ||
| 524 | SKIP_WHITESPACE(ct); | ||
| 525 | } | ||
| 526 | *argv_out = apr_palloc(token_context, numargs * sizeof(char*)); | ||
| 527 | |||
| 528 | /* determine first argument */ | ||
| 529 | for (argnum = 0; argnum < (numargs-1); argnum++) { | ||
| 530 | SKIP_WHITESPACE(cp); | ||
| 531 | CHECK_QUOTATION(cp, isquoted) | ||
| 532 | ; | ||
| 533 | ct = cp; | ||
| 534 | DETERMINE_NEXTSTRING(cp, isquoted); | ||
| 535 | cp++; | ||
| 536 | (*argv_out)[argnum] = apr_palloc(token_context, cp - ct); | ||
| 537 | apr_cpystrn((*argv_out)[argnum], ct, cp - ct); | ||
| 538 | cleaned = dirty = (*argv_out)[argnum]; | ||
| 539 | REMOVE_ESCAPE_CHARS(cleaned, dirty, escaped) | ||
| 540 | ; | ||
| 541 | } | ||
| 542 | (*argv_out)[argnum] = NULL; | ||
| 543 | |||
| 544 | return APR_SUCCESS; | ||
| 545 | } | ||
| 546 | |||
| 547 | apr_status_t parser_parsefile(config_t *cfg, config_dbd_t *dbconn, | ||
| 548 | config_filestat_t *fstat) | ||
| 549 | { | ||
| 550 | apr_pool_t *tp, *targp; | ||
| 551 | apr_file_t *file; | ||
| 552 | apr_status_t rv; | ||
| 553 | char buff[2048]; | ||
| 554 | char readbuff[BUFFER_SIZE]; | ||
| 555 | char **targv; | ||
| 556 | int targc; | ||
| 557 | |||
| 558 | apr_pool_create(&tp, cfg->pool); | ||
| 559 | apr_pool_create(&targp, tp); | ||
| 560 | |||
| 561 | logging_log(cfg, LOGLEVEL_NOTICE, "PARSER: Begin Parsing Log File '%s'", fstat->fname); | ||
| 562 | |||
| 563 | rv = apr_file_open(&file, fstat->fname, APR_FOPEN_READ, APR_OS_DEFAULT, tp); | ||
| 564 | apr_file_buffer_set(file, readbuff, BUFFER_SIZE); | ||
| 565 | if (rv != APR_SUCCESS) { | ||
| 566 | logging_log(cfg, LOGLEVEL_NOISE, "PARSER: Could not open %s", fstat->fname); | ||
| 567 | return rv; | ||
| 568 | } | ||
| 569 | |||
| 570 | fstat->linesparsed = 0; | ||
| 571 | // Start Transaction | ||
| 572 | fstat->start = apr_time_now(); | ||
| 573 | if (!cfg->dryrun && database_trans_start(cfg, dbconn, tp)) { | ||
| 574 | fstat->result = "Database Transaction Error"; | ||
| 575 | fstat->stop = apr_time_now(); | ||
| 576 | return rv; | ||
| 577 | } | ||
| 578 | |||
| 579 | do { | ||
| 580 | rv = apr_file_gets(buff, 2048, file); | ||
| 581 | if (rv == APR_SUCCESS) { | ||
| 582 | int i,m, cont = 0; | ||
| 583 | config_filter_t *filters; | ||
| 584 | |||
| 585 | fstat->linesparsed++; | ||
| 586 | // chomp off newline | ||
| 587 | line_chomp(buff); | ||
| 588 | // Run line filters | ||
| 589 | for (i=0, m=cfg->linefilters->nelts, | ||
| 590 | filters = (config_filter_t *)cfg->linefilters->elts; | ||
| 591 | i<m; i++) { | ||
| 592 | if (!filters[i].regex || ap_regexec(filters[i].regex, buff, 0, NULL,0)==0) { | ||
| 593 | if (filters[i].negative) { | ||
| 594 | logging_log(cfg, LOGLEVEL_DEBUG, | ||
| 595 | "PARSER: LINEFILTER: Skipping Line %d due to Filter (%d)%s", | ||
| 596 | fstat->linesparsed, i, filters[i].filter); | ||
| 597 | fstat->lineskipped++; | ||
| 598 | cont = 1; | ||
| 599 | } else { | ||
| 600 | logging_log(cfg, LOGLEVEL_DEBUG, | ||
| 601 | "PARSER: LINEFILTER: Force Parsing Line %d due to Filter (%d)%s", | ||
| 602 | fstat->linesparsed, i, filters[i].filter); | ||
| 603 | } | ||
| 604 | break; | ||
| 605 | } | ||
| 606 | } | ||
| 607 | if (cont) continue; | ||
| 608 | |||
| 609 | apr_pool_clear(targp); | ||
| 610 | parser_tokenize_line(buff, &targv, targp); | ||
| 611 | targc = 0; | ||
| 612 | while (targv[targc]) | ||
| 613 | targc++; | ||
| 614 | rv = parser_processline(targp, cfg, dbconn, fstat, targv, targc); | ||
| 615 | if (rv != APR_SUCCESS) { | ||
| 616 | int i; | ||
| 617 | |||
| 618 | fstat->linesbad++; | ||
| 619 | rv = parser_logbadline(cfg, fstat->fname, buff); | ||
| 620 | if (rv) { | ||
| 621 | if (!cfg->dryrun) database_trans_abort(cfg, dbconn); | ||
| 622 | logging_log(cfg, LOGLEVEL_ERROR, "Line %d(%d): %s", fstat->linesparsed, | ||
| 623 | targc, buff); | ||
| 624 | for (i = 0; targv[i]; i++) { | ||
| 625 | logging_log(cfg, LOGLEVEL_ERROR, "Arg (%d): '%s'", i, | ||
| 626 | targv[i]); | ||
| 627 | } | ||
| 628 | } | ||
| 629 | } | ||
| 630 | } else { | ||
| 631 | rv = APR_SUCCESS; | ||
| 632 | break; | ||
| 633 | } | ||
| 634 | } while (rv == APR_SUCCESS); | ||
| 635 | apr_file_close(file); | ||
| 636 | // Finish Transaction | ||
| 637 | if (!cfg->dryrun && database_trans_stop(cfg, dbconn, tp)) { | ||
| 638 | fstat->result = apr_psprintf(cfg->pool, | ||
| 639 | "Input line %d, Database Transaction Error", | ||
| 640 | fstat->linesparsed); | ||
| 641 | } | ||
| 642 | |||
| 643 | apr_pool_destroy(tp); | ||
| 644 | logging_log(cfg, LOGLEVEL_NOTICE, | ||
| 645 | "PARSER: Finish Parsing Log File '%s'. Lines: (%d/%d)", | ||
| 646 | fstat->fname, fstat->linesparsed - fstat->lineskipped, fstat->linesparsed); | ||
| 647 | if (!rv) { | ||
| 648 | fstat->result = "File Parsed Succesfully"; | ||
| 649 | } | ||
| 650 | fstat->stop = apr_time_now(); | ||
| 651 | return rv; | ||
| 652 | } | ||
| 653 | |||
| 654 | apr_status_t parser_processline(apr_pool_t *ptemp, config_t *cfg, | ||
| 655 | config_dbd_t *dbconn, config_filestat_t *fstat, char **argv, int argc) | ||
| 656 | { | ||
| 657 | config_logformat_t *fmt; | ||
| 658 | config_logformat_field_t *ifields; | ||
| 659 | config_output_field_t *ofields; | ||
| 660 | config_filter_t *filters; | ||
| 661 | apr_table_t *datain; | ||
| 662 | apr_table_t *dataout; | ||
| 663 | apr_status_t rv= APR_SUCCESS; | ||
| 664 | int i,m; | ||
| 665 | |||
| 666 | fmt = apr_hash_get(cfg->log_formats, cfg->logformat, APR_HASH_KEY_STRING); | ||
| 667 | if (!fmt) { | ||
| 668 | logging_log(cfg, LOGLEVEL_NOISE, "PARSER: No Input Log format"); | ||
| 669 | return APR_EINVAL; | ||
| 670 | } | ||
| 671 | if (fmt->fields->nelts != argc) { | ||
| 672 | logging_log(cfg, LOGLEVEL_NOISE, | ||
| 673 | "PARSER: Input line field number differs from expected. Expected %d got %d.", | ||
| 674 | fmt->fields->nelts, argc); | ||
| 675 | fstat->result = apr_psprintf(cfg->pool, | ||
| 676 | "Input line %d is badly formatted (wrong number of fields)", | ||
| 677 | fstat->linesparsed); | ||
| 678 | return APR_EINVAL; | ||
| 679 | } | ||
| 680 | |||
| 681 | datain = apr_table_make(ptemp, fmt->fields->nelts); | ||
| 682 | dataout = apr_table_make(ptemp, cfg->output_fields->nelts); | ||
| 683 | |||
| 684 | ifields = (config_logformat_field_t *)fmt->fields->elts; | ||
| 685 | for (i=0; i<fmt->fields->nelts; i++) { | ||
| 686 | apr_table_setn(datain, ifields[i].name, argv[i]); | ||
| 687 | } | ||
| 688 | // Run Pre Filters | ||
| 689 | for (i=0, m=cfg->prefilters->nelts, | ||
| 690 | filters = (config_filter_t *)cfg->prefilters->elts; | ||
| 691 | i<m; i++) { | ||
| 692 | const char *temp = apr_table_get(datain, filters[i].field); | ||
| 693 | if (temp && (!filters[i].regex || ap_regexec(filters[i].regex, temp, 0, NULL,0)==0)) { | ||
| 694 | if (filters[i].negative) { | ||
| 695 | logging_log(cfg, LOGLEVEL_DEBUG, | ||
| 696 | "PARSER: PREFILTER: Skipping Line %d due to Filter (%d)%s", | ||
| 697 | fstat->linesparsed, i, filters[i].filter); | ||
| 698 | fstat->lineskipped++; | ||
| 699 | return APR_SUCCESS; | ||
| 700 | } else { | ||
| 701 | logging_log(cfg, LOGLEVEL_DEBUG, | ||
| 702 | "PARSER: PREFILTER: Force Parsing Line %d due to Filter (%d)%s", | ||
| 703 | fstat->linesparsed, i, filters[i].filter); | ||
| 704 | } | ||
| 705 | break; | ||
| 706 | } | ||
| 707 | } | ||
| 708 | |||
| 709 | ofields = (config_output_field_t *)cfg->output_fields->elts; | ||
| 710 | // clear out ofield function per-line data | ||
| 711 | memset(&g_parser_linedata[1],0,sizeof(void *)*(int)g_parser_linedata[0]); | ||
| 712 | // Convert input fields to output fields | ||
| 713 | for (i=0,m=cfg->output_fields->nelts; i<m; i++) { | ||
| 714 | const char *val; | ||
| 715 | val = apr_table_get(datain, ofields[i].source); | ||
| 716 | // If we can't find the source field just continue | ||
| 717 | if (!val && !(ofields[i].source[0]=='\0' && ofields[i].func)) { | ||
| 718 | apr_table_setn(dataout, ofields[i].field, ofields[i].def); | ||
| 719 | continue; | ||
| 720 | } | ||
| 721 | if (!ofields[i].func) { | ||
| 722 | apr_table_setn(dataout, ofields[i].field, val); | ||
| 723 | } else { | ||
| 724 | const char *ret= NULL; | ||
| 725 | rv = ((parser_func_t *)ofields[i].func)->func(ptemp, cfg, | ||
| 726 | &ofields[i], val, &ret); | ||
| 727 | if (rv) { | ||
| 728 | fstat->result = apr_psprintf(cfg->pool, | ||
| 729 | "Input line %d, Parser function %s returned error (%d)%s", | ||
| 730 | fstat->linesparsed, ofields[i].fname, rv, logging_strerror(rv)); | ||
| 731 | return rv; | ||
| 732 | } | ||
| 733 | apr_table_setn(dataout, ofields[i].field, ret ? ret : ofields[i].def); | ||
| 734 | } | ||
| 735 | } | ||
| 736 | |||
| 737 | // Run Post filters | ||
| 738 | for (i=0, m=cfg->postfilters->nelts, | ||
| 739 | filters = (config_filter_t *)cfg->postfilters->elts; | ||
| 740 | i<m; i++) { | ||
| 741 | const char *temp = apr_table_get(dataout, filters[i].field); | ||
| 742 | if (temp && (!filters[i].regex || ap_regexec(filters[i].regex, temp, 0, NULL,0)==0)) { | ||
| 743 | if (filters[i].negative) { | ||
| 744 | logging_log(cfg, LOGLEVEL_DEBUG, | ||
| 745 | "PARSER: POSTFILTER: Skipping Line %d due to Filter (%d)%s", | ||
| 746 | fstat->linesparsed, i, filters[i].filter); | ||
| 747 | fstat->lineskipped++; | ||
| 748 | return APR_SUCCESS; | ||
| 749 | } else { | ||
| 750 | logging_log(cfg, LOGLEVEL_DEBUG, | ||
| 751 | "PARSER: POSTFILTER: Force Parsing Line %d due to Filter (%d)%s", | ||
| 752 | fstat->linesparsed, i, filters[i].filter); | ||
| 753 | } | ||
| 754 | break; | ||
| 755 | } | ||
| 756 | } | ||
| 757 | |||
| 758 | // Process DB Query | ||
| 759 | if (!cfg->dryrun) { | ||
| 760 | rv = database_insert(cfg, dbconn, ptemp, dataout); | ||
| 761 | if (rv) { | ||
| 762 | fstat->result = apr_psprintf(cfg->pool, | ||
| 763 | "Input line %d, Database Error", | ||
| 764 | fstat->linesparsed); | ||
| 765 | } | ||
| 766 | } | ||
| 767 | return rv; | ||
| 768 | } | ||
diff --git a/utility/logparse.h b/utility/logparse.h new file mode 100644 index 0000000..7ca0958 --- /dev/null +++ b/utility/logparse.h | |||
| @@ -0,0 +1,37 @@ | |||
| 1 | #ifndef LOGPARSE_H_ | ||
| 2 | #define LOGPARSE_H_ | ||
| 3 | |||
| 4 | #include "config.h" | ||
| 5 | |||
| 6 | typedef apr_status_t (*parser_func_f)(apr_pool_t *p, config_t *cfg, | ||
| 7 | config_output_field_t *field, const char *value, const char **ret); | ||
| 8 | |||
| 9 | struct parser_func_t { | ||
| 10 | parser_func_f func; | ||
| 11 | int pos; | ||
| 12 | void *data; | ||
| 13 | void ***linedata; | ||
| 14 | }; | ||
| 15 | |||
| 16 | #define parser_get_linedata(f) (*f->linedata)[f->pos] | ||
| 17 | |||
| 18 | #define parser_set_linedata(f, v) (*f->linedata)[f->pos] = v | ||
| 19 | |||
| 20 | parser_func_t *parser_get_func(const char *name); | ||
| 21 | |||
| 22 | void parser_init(apr_pool_t *p); | ||
| 23 | |||
| 24 | void parser_find_logs(config_t *cfg); | ||
| 25 | |||
| 26 | void parser_split_logs(config_t *cfg); | ||
| 27 | |||
| 28 | apr_status_t parser_tokenize_line(const char *arg_str, char ***argv_out, | ||
| 29 | apr_pool_t *token_context); | ||
| 30 | |||
| 31 | apr_status_t parser_parsefile(config_t *cfg, config_dbd_t *dbconn, | ||
| 32 | config_filestat_t *fstat); | ||
| 33 | |||
| 34 | apr_status_t parser_processline(apr_pool_t *ptemp, config_t *cfg, | ||
| 35 | config_dbd_t *dbconn, config_filestat_t *line, char **argv, int argc); | ||
| 36 | |||
| 37 | #endif /*LOGPARSE_H_*/ | ||
diff --git a/utility/mod_log_sql.conf b/utility/mod_log_sql.conf new file mode 100644 index 0000000..911fc18 --- /dev/null +++ b/utility/mod_log_sql.conf | |||
| @@ -0,0 +1,70 @@ | |||
| 1 | InputDirectory ./logs | ||
| 2 | ErrorLog ./error_log | ||
| 3 | |||
| 4 | DBDDriver mysql | ||
| 5 | DBDParams "host=localhost;user=root;dbname=apache_log" | ||
| 6 | Table access_log | ||
| 7 | MachineID 7of9 | ||
| 8 | UseTransactions on | ||
| 9 | LogLevel notice | ||
| 10 | DryRun off | ||
| 11 | Summary on | ||
| 12 | |||
| 13 | SplitInput on | ||
| 14 | #SplitCount 4 | ||
| 15 | SplitMinLines 0 | ||
| 16 | SplitMaxLines 50000 | ||
| 17 | SplitDirectory ./split_temp | ||
| 18 | |||
| 19 | #ThreadCount 0 | ||
| 20 | |||
| 21 | BadLineFile ./badlines.log | ||
| 22 | BadLineMax 10 | ||
| 23 | |||
| 24 | LogFormatConfig CLF remhost String | ||
| 25 | LogFormatConfig CLF ident String | ||
| 26 | LogFormatConfig CLF user String | ||
| 27 | LogFormatConfig CLF date Date | ||
| 28 | LogFormatConfig CLF request String | ||
| 29 | LogFormatConfig CLF status Number | ||
| 30 | LogFormatConfig CLF bytes_sent Number | ||
| 31 | |||
| 32 | LogFormatConfig Combined remhost String | ||
| 33 | LogFormatConfig Combined ident String | ||
| 34 | LogFormatConfig Combined user String | ||
| 35 | LogFormatConfig Combined date Date | ||
| 36 | LogFormatConfig Combined request String | ||
| 37 | LogFormatConfig Combined status Number | ||
| 38 | LogFormatConfig Combined bytes_sent Number | ||
| 39 | LogFormatConfig Combined referer String | ||
| 40 | LogFormatConfig Combined agent String | ||
| 41 | |||
| 42 | LogFormat Combined | ||
| 43 | |||
| 44 | #Linefilter - "BAD" | ||
| 45 | #LineFilter "GOOD" | ||
| 46 | #LineFilter + "BETTER" | ||
| 47 | # the next filter ignores ALL lines | ||
| 48 | #LineFilter - | ||
| 49 | #PreFilter request - "Rebuild" | ||
| 50 | #PostFilter request_method "GET" | ||
| 51 | |||
| 52 | # Usage field datatype(size) default source [function [param]...] | ||
| 53 | OutputField bytes_sent int 0 bytes_sent | ||
| 54 | OutputField request_protocol varchar(10) "" request regexmatch "(HTTP/[\\d\\.]+)$" | ||
| 55 | OutputField remote_host varchar(50) "" remhost | ||
| 56 | OutputField request_method varchar(25) "" request regexmatch "^(\\w+)" | ||
| 57 | OutputField time_stamp int 0 date totimestamp | ||
| 58 | OutputField status smallint 0 status | ||
| 59 | OutputField request_line varchar(255) "" request | ||
| 60 | #OutputField request_uri varchar(255) "" request regexmatch "^\\w+ (.+) \\w+/[\\d\\.]+$" | ||
| 61 | OutputField remote_user varchar(50) "" user | ||
| 62 | OutputField remote_logname varchar(50) "" ident | ||
| 63 | OutputField request_time char(28) "" date wrap "[" "]" | ||
| 64 | #Only used for Combined log input, if standard CLF input, they are ignored | ||
| 65 | OutputField agent varchar(255) "" agent | ||
| 66 | OutputField referer varchar(255) "" referer | ||
| 67 | OutputField machine_id varchar(25) "" "" machineid | ||
| 68 | #VIZU Fields | ||
| 69 | OutputField poll_id int 0 request queryarg n "&;" | ||
| 70 | |||
diff --git a/utility/shell.c b/utility/shell.c new file mode 100644 index 0000000..1b9e890 --- /dev/null +++ b/utility/shell.c | |||
| @@ -0,0 +1,342 @@ | |||
| 1 | #include "apr.h" | ||
| 2 | #include "apr_getopt.h" | ||
| 3 | #include "apr_tables.h" | ||
| 4 | |||
| 5 | #define APR_WANT_STDIO | ||
| 6 | #include "apr_want.h" | ||
| 7 | #include "stdlib.h" | ||
| 8 | |||
| 9 | #include "shell.h" | ||
| 10 | #include "config.h" | ||
| 11 | #include "logparse.h" | ||
| 12 | #include "database.h" | ||
| 13 | #include "util.h" | ||
| 14 | |||
| 15 | #if APR_HAS_THREADS | ||
| 16 | #include "apr_queue.h" | ||
| 17 | #include "apr_thread_pool.h" | ||
| 18 | |||
| 19 | static apr_queue_t *queue; | ||
| 20 | |||
| 21 | void run_multithreaded(config_t *cfg); | ||
| 22 | #endif | ||
| 23 | |||
| 24 | void run_singlethreaded(config_t *cfg); | ||
| 25 | |||
| 26 | const apr_getopt_option_t _opt_config[] = { | ||
| 27 | {"machineid", 'm', 1, "Machine ID for the log file"}, | ||
| 28 | {"transaction", 't', 1, "Use a Transaction (yes,no)"}, | ||
| 29 | {"logformat", 'r', 1, "Use this logformat to parse files"}, | ||
| 30 | {"file", 'f', 1, "Parse this single log file (input dir is NOT scanned)"}, | ||
| 31 | {"inputdir", 'i', 1, "Input Directory to look for log files"}, | ||
| 32 | {"config", 'c', 1, "Configuration file to use (default mod_log_sql.conf)"}, | ||
| 33 | {"dryrun", 'n', 0, "Perform a dry run (do not actually alter the databse)"}, | ||
| 34 | {"dump", 'd', 0, "Dump the configuration after parsing and quit"}, | ||
| 35 | {"loglevel", 'l', 1, "Log Level (deubg, notice, error)"}, | ||
| 36 | {"summary", 's', 1, "Summary (yes,no)"}, | ||
| 37 | {"threadcount", 'p', 1, "Set thread count (a number greater than 0)"}, | ||
| 38 | {"help", 'h', 0, "Show Help"}, | ||
| 39 | {NULL} | ||
| 40 | }; | ||
| 41 | |||
| 42 | void show_help(const char *prog, const apr_getopt_option_t *opts, FILE *output) | ||
| 43 | { | ||
| 44 | int ptr = 0; | ||
| 45 | fprintf(output, "Usage: %s [OPTIONS] [files...]\n\n", prog); | ||
| 46 | while (opts[ptr].optch != 0) { | ||
| 47 | if (opts[ptr].optch > 255) { | ||
| 48 | if (opts[ptr].name) { | ||
| 49 | fprintf(output, " --%-10s", opts[ptr].name); | ||
| 50 | } else { | ||
| 51 | fprintf(output, " "); | ||
| 52 | } | ||
| 53 | } else { | ||
| 54 | if (opts[ptr].name) { | ||
| 55 | fprintf(output, " -%c --%-10s", opts[ptr].optch, opts[ptr].name); | ||
| 56 | } else { | ||
| 57 | fprintf(output, " -%c ", opts[ptr].optch); | ||
| 58 | } | ||
| 59 | } | ||
| 60 | if (opts[ptr].has_arg) { | ||
| 61 | fprintf(output, " (arg)"); | ||
| 62 | } else { | ||
| 63 | fprintf(output, " "); | ||
| 64 | } | ||
| 65 | fprintf(output, " %s\n", opts[ptr].description); | ||
| 66 | ptr++; | ||
| 67 | } | ||
| 68 | } | ||
| 69 | |||
| 70 | void print_summary(config_t *cfg) { | ||
| 71 | config_filestat_t *fstat; | ||
| 72 | int i,m; | ||
| 73 | apr_time_t totaltime = 0; | ||
| 74 | apr_size_t totalparsed = 0, totalskipped = 0, totalbad = 0; | ||
| 75 | |||
| 76 | fstat = (config_filestat_t *)cfg->input_files->elts; | ||
| 77 | |||
| 78 | printf("Execution Summary\nParsed %d files\n", cfg->input_files->nelts); | ||
| 79 | for (i=0, m=cfg->input_files->nelts; i<m; i++) { | ||
| 80 | totaltime += fstat[i].stop - fstat[i].start; | ||
| 81 | totalparsed += fstat[i].linesparsed; | ||
| 82 | totalskipped += fstat[i].lineskipped; | ||
| 83 | totalbad += fstat[i].linesbad; | ||
| 84 | printf(" File: %s\n" | ||
| 85 | " Lines Added %'d out of %'d (Skipped %'d, Bad %'d)\n" | ||
| 86 | " Status: %s\n" | ||
| 87 | " Duration: %02"APR_TIME_T_FMT":%02"APR_TIME_T_FMT".%"APR_TIME_T_FMT" (minutes, seconds, and miliseconds)\n" | ||
| 88 | "\n", | ||
| 89 | fstat[i].fname, | ||
| 90 | fstat[i].linesparsed - fstat[i].lineskipped - fstat[i].linesbad, | ||
| 91 | fstat[i].linesparsed, | ||
| 92 | fstat[i].lineskipped, | ||
| 93 | fstat[i].linesbad, | ||
| 94 | fstat[i].result, | ||
| 95 | apr_time_sec(fstat[i].stop - fstat[i].start)/60, | ||
| 96 | apr_time_sec(fstat[i].stop - fstat[i].start) % 60, | ||
| 97 | apr_time_msec(fstat[i].stop - fstat[i].start) | ||
| 98 | ); | ||
| 99 | } | ||
| 100 | printf("Totals\n" | ||
| 101 | " Lines Added %'d out of %'d (Skipped %'d, Bad %'d)\n" | ||
| 102 | " Duration: %02"APR_TIME_T_FMT":%02"APR_TIME_T_FMT".%"APR_TIME_T_FMT" (minutes, seconds, and miliseconds)\n" | ||
| 103 | "\n", | ||
| 104 | totalparsed - totalskipped - totalbad, | ||
| 105 | totalparsed, | ||
| 106 | totalskipped, | ||
| 107 | totalbad, | ||
| 108 | apr_time_sec(totaltime)/60, | ||
| 109 | apr_time_sec(totaltime) % 60, | ||
| 110 | apr_time_msec(totaltime) | ||
| 111 | ); | ||
| 112 | } | ||
| 113 | |||
| 114 | int main(int argc, const char *const argv[]) | ||
| 115 | { | ||
| 116 | apr_pool_t *pool, *ptemp; | ||
| 117 | apr_getopt_t *opts; | ||
| 118 | int opt; | ||
| 119 | const char *opt_arg; | ||
| 120 | apr_status_t rv; | ||
| 121 | apr_table_t *args; | ||
| 122 | config_t *cfg; | ||
| 123 | |||
| 124 | apr_app_initialize(&argc, &argv, NULL); | ||
| 125 | atexit(apr_terminate); | ||
| 126 | |||
| 127 | if (apr_pool_create(&pool, NULL) != APR_SUCCESS) { | ||
| 128 | fprintf(stderr, "Failed to create memory pool!\n"); | ||
| 129 | exit(1); | ||
| 130 | } | ||
| 131 | apr_pool_create(&ptemp, NULL); | ||
| 132 | |||
| 133 | /** Iterate over command line arguments | ||
| 134 | * shoving args in a apr_table for processing later*/ | ||
| 135 | args = apr_table_make(ptemp, 5); | ||
| 136 | apr_table_setn(args, "config", "mod_log_sql.conf"); | ||
| 137 | apr_getopt_init(&opts, ptemp, argc, argv); | ||
| 138 | while ((rv = apr_getopt_long(opts, _opt_config, &opt, &opt_arg)) == APR_SUCCESS) { | ||
| 139 | switch (opt) { | ||
| 140 | case 'c': | ||
| 141 | apr_table_setn(args,"config",opt_arg); | ||
| 142 | break; | ||
| 143 | case 'd': | ||
| 144 | apr_table_setn(args,"dump","yes"); | ||
| 145 | break; | ||
| 146 | case 'f': | ||
| 147 | apr_table_setn(args,"inputfile",opt_arg); | ||
| 148 | break; | ||
| 149 | case 'h': | ||
| 150 | show_help(argv[0], _opt_config, stdout); | ||
| 151 | exit(1); | ||
| 152 | break; | ||
| 153 | case 'i': | ||
| 154 | apr_table_setn(args,"inputdirectory",opt_arg); | ||
| 155 | break; | ||
| 156 | case 'l': | ||
| 157 | apr_table_setn(args,"loglevel",opt_arg); | ||
| 158 | break; | ||
| 159 | case 'm': | ||
| 160 | apr_table_setn(args,"machineid",opt_arg); | ||
| 161 | break; | ||
| 162 | case 'n': | ||
| 163 | apr_table_setn(args,"dryrun","yes"); | ||
| 164 | break; | ||
| 165 | case 'p': | ||
| 166 | apr_table_setn(args,"threadcount",opt_arg); | ||
| 167 | break; | ||
| 168 | case 'r': | ||
| 169 | apr_table_setn(args,"logformat",opt_arg); | ||
| 170 | break; | ||
| 171 | case 's': | ||
| 172 | apr_table_setn(args,"summary",opt_arg); | ||
| 173 | break; | ||
| 174 | case 't': | ||
| 175 | apr_table_setn(args,"usetransactions",opt_arg); | ||
| 176 | break; | ||
| 177 | } | ||
| 178 | } | ||
| 179 | if (rv != APR_EOF) { | ||
| 180 | show_help(argv[0], _opt_config, stderr); | ||
| 181 | exit(1); | ||
| 182 | } | ||
| 183 | // Check if no extra args were passed | ||
| 184 | if (opts->ind != opts->argc) { | ||
| 185 | show_help(argv[0], _opt_config, stderr); | ||
| 186 | fprintf(stderr, "\n%s: Extra unknown arguments passed\n\n",argv[0]); | ||
| 187 | exit(1); | ||
| 188 | } | ||
| 189 | |||
| 190 | // Initialize sub systems | ||
| 191 | parser_init(pool); | ||
| 192 | config_init(pool); | ||
| 193 | database_init(pool); | ||
| 194 | // Process configuration file | ||
| 195 | cfg = config_create(pool); | ||
| 196 | // initialize STD out error log | ||
| 197 | logging_preinit(cfg); | ||
| 198 | rv = config_read(cfg, apr_table_get(args,"Config"), args); | ||
| 199 | apr_pool_destroy(ptemp); | ||
| 200 | |||
| 201 | // Initialize Log system AFTER we parse the configuration | ||
| 202 | logging_init(cfg); | ||
| 203 | |||
| 204 | if (APR_STATUS_IS_ENOENT(rv)) { | ||
| 205 | logging_log(cfg,LOGLEVEL_NOISE,"Could not load configuration file: %s",apr_table_get(args,"config")); | ||
| 206 | } else if (rv) { | ||
| 207 | exit(1); | ||
| 208 | } | ||
| 209 | if (cfg->dump) { | ||
| 210 | config_dump(cfg); | ||
| 211 | exit(0); | ||
| 212 | } | ||
| 213 | |||
| 214 | if (config_check(cfg)) { | ||
| 215 | logging_log(cfg,LOGLEVEL_NOISE, "Please correct the configuration"); | ||
| 216 | exit(1); | ||
| 217 | } | ||
| 218 | |||
| 219 | // Only Find files IF no filename was passed via the command line | ||
| 220 | if (apr_is_empty_array(cfg->input_files)) { | ||
| 221 | parser_find_logs(cfg); | ||
| 222 | } | ||
| 223 | if (!apr_is_empty_array(cfg->input_files)) { | ||
| 224 | parser_split_logs(cfg); | ||
| 225 | #if APR_HAS_THREADS | ||
| 226 | if (cfg->thread_count > 0) { | ||
| 227 | run_multithreaded(cfg); | ||
| 228 | } else { | ||
| 229 | #endif | ||
| 230 | run_singlethreaded(cfg); | ||
| 231 | #if APR_HAS_THREADS | ||
| 232 | } | ||
| 233 | #endif | ||
| 234 | } else { | ||
| 235 | logging_log(cfg,LOGLEVEL_NOISE,"No log files found to parse"); | ||
| 236 | } | ||
| 237 | |||
| 238 | if (cfg->summary) { | ||
| 239 | print_summary(cfg); | ||
| 240 | } | ||
| 241 | return 0; | ||
| 242 | } | ||
| 243 | |||
| 244 | void run_singlethreaded(config_t *cfg) | ||
| 245 | { | ||
| 246 | config_filestat_t *filelist; | ||
| 247 | config_dbd_t *dbconn = NULL; | ||
| 248 | int f, l; | ||
| 249 | apr_status_t rv; | ||
| 250 | |||
| 251 | if (!cfg->dryrun) { | ||
| 252 | if ((rv = database_connect(cfg, &dbconn))) { | ||
| 253 | logging_log(cfg,LOGLEVEL_NOISE, "Error Connecting to Database"); | ||
| 254 | exit(1); | ||
| 255 | } | ||
| 256 | } | ||
| 257 | |||
| 258 | filelist = (config_filestat_t *)cfg->input_files->elts; | ||
| 259 | for (f=0, l=cfg->input_files->nelts; f < l; f++) { | ||
| 260 | rv = parser_parsefile(cfg, dbconn, &filelist[f]); | ||
| 261 | if (rv) { | ||
| 262 | logging_log(cfg, LOGLEVEL_NOISE, | ||
| 263 | "Error occured parsing log files. Aborting"); | ||
| 264 | break; | ||
| 265 | } | ||
| 266 | } | ||
| 267 | |||
| 268 | if (!cfg->dryrun) { | ||
| 269 | database_disconnect(dbconn); | ||
| 270 | } | ||
| 271 | } | ||
| 272 | |||
| 273 | #if APR_HAS_THREADS | ||
| 274 | void * APR_THREAD_FUNC run_filethread(apr_thread_t *thd, void *data) | ||
| 275 | { | ||
| 276 | config_t *cfg = data; | ||
| 277 | config_dbd_t *dbconn = NULL; | ||
| 278 | config_filestat_t *fileentry; | ||
| 279 | apr_status_t rv; | ||
| 280 | |||
| 281 | if (!cfg->dryrun) { | ||
| 282 | if ((rv = database_connect(cfg, &dbconn))) { | ||
| 283 | logging_log(cfg,LOGLEVEL_NOISE, "Error Connecting to Database"); | ||
| 284 | return NULL; | ||
| 285 | } | ||
| 286 | } | ||
| 287 | |||
| 288 | while (1) { | ||
| 289 | rv = apr_queue_pop(queue, (void **)&fileentry); | ||
| 290 | if (rv == APR_EINTR) | ||
| 291 | continue; | ||
| 292 | if (rv == APR_EOF) | ||
| 293 | break; | ||
| 294 | rv = parser_parsefile(cfg, dbconn, fileentry); | ||
| 295 | if (rv) { | ||
| 296 | logging_log(cfg, LOGLEVEL_NOISE, | ||
| 297 | "Error occured parsing log file %s", fileentry->fname); | ||
| 298 | } | ||
| 299 | } | ||
| 300 | |||
| 301 | if (!cfg->dryrun) { | ||
| 302 | database_disconnect(dbconn); | ||
| 303 | } | ||
| 304 | return NULL; | ||
| 305 | } | ||
| 306 | |||
| 307 | void run_multithreaded(config_t *cfg) | ||
| 308 | { | ||
| 309 | logging_log(cfg, LOGLEVEL_NOISE, "Running Multithreaded"); | ||
| 310 | |||
| 311 | config_filestat_t *filelist; | ||
| 312 | int f, l; | ||
| 313 | apr_status_t rv; | ||
| 314 | apr_pool_t *tp; | ||
| 315 | apr_thread_pool_t *thrp; | ||
| 316 | unsigned int count; | ||
| 317 | |||
| 318 | apr_pool_create(&tp, cfg->pool); | ||
| 319 | rv = apr_queue_create(&queue, cfg->input_files->nelts, tp); | ||
| 320 | |||
| 321 | rv = apr_thread_pool_create(&thrp, 0, cfg->thread_count, tp); | ||
| 322 | |||
| 323 | //populate queue | ||
| 324 | filelist = (config_filestat_t *)cfg->input_files->elts; | ||
| 325 | for (f=0, l=cfg->input_files->nelts; f < l; f++) { | ||
| 326 | rv = apr_queue_push(queue, &filelist[f]); | ||
| 327 | } | ||
| 328 | // populate the worker threads | ||
| 329 | for (f=0; f<cfg->thread_count; f++) { | ||
| 330 | rv = apr_thread_pool_push(thrp, run_filethread, cfg, 0, NULL); | ||
| 331 | } | ||
| 332 | |||
| 333 | do { | ||
| 334 | apr_sleep(apr_time_from_sec(1)); | ||
| 335 | count = apr_queue_size(queue); | ||
| 336 | } while (count > 0); | ||
| 337 | |||
| 338 | rv = apr_queue_term(queue); | ||
| 339 | |||
| 340 | rv = apr_thread_pool_destroy(thrp); | ||
| 341 | } | ||
| 342 | #endif | ||
diff --git a/utility/shell.h b/utility/shell.h new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/utility/shell.h | |||
diff --git a/utility/util.c b/utility/util.c new file mode 100644 index 0000000..dcfb028 --- /dev/null +++ b/utility/util.c | |||
| @@ -0,0 +1,189 @@ | |||
| 1 | #include "util.h" | ||
| 2 | #include "apr_strings.h" | ||
| 3 | #include "apr_lib.h" | ||
| 4 | #include "apr_file_io.h" | ||
| 5 | #include "apr_time.h" | ||
| 6 | |||
| 7 | #include "config.h" | ||
| 8 | |||
| 9 | #include <stdarg.h> | ||
| 10 | |||
| 11 | char *lowerstr(apr_pool_t *pool, const char *input) | ||
| 12 | { | ||
| 13 | char *temp; | ||
| 14 | char *itr; | ||
| 15 | temp = apr_pstrdup(pool, input); | ||
| 16 | for (itr=temp; *itr!='\0'; itr++) { | ||
| 17 | *itr = apr_tolower(*itr); | ||
| 18 | } | ||
| 19 | return temp; | ||
| 20 | } | ||
| 21 | |||
| 22 | void line_chomp(char *str) | ||
| 23 | { | ||
| 24 | int len; | ||
| 25 | // chomp off newline | ||
| 26 | len = strlen(str); | ||
| 27 | if (len) { | ||
| 28 | while (str[len-1] == '\r' || str[len-1] == '\n') { | ||
| 29 | str[len-1] = '\0'; | ||
| 30 | len--; | ||
| 31 | } | ||
| 32 | } | ||
| 33 | } | ||
| 34 | |||
| 35 | /* | ||
| 36 | * *** Ripped from HTTPD util.c (why are so many PORTABLE things not in APR UTIL?) | ||
| 37 | */ | ||
| 38 | static char x2c(const char *what) | ||
| 39 | { | ||
| 40 | register char digit; | ||
| 41 | |||
| 42 | digit = ((what[0] >= 'A') ? ((what[0] & 0xdf) - 'A') + 10 | ||
| 43 | : (what[0] - '0')); | ||
| 44 | digit *= 16; | ||
| 45 | digit += (what[1] >= 'A' ? ((what[1] & 0xdf) - 'A') + 10 | ||
| 46 | : (what[1] - '0')); | ||
| 47 | return (digit); | ||
| 48 | } | ||
| 49 | |||
| 50 | /* | ||
| 51 | * *** Ripped from HTTPD util.c (why are so many PORTABLE things not in APR UTIL?) | ||
| 52 | * | ||
| 53 | * Unescapes a URL, leaving reserved characters intact. | ||
| 54 | * Returns 0 on success, non-zero on error | ||
| 55 | * Failure is due to | ||
| 56 | * bad % escape returns HTTP_BAD_REQUEST | ||
| 57 | * | ||
| 58 | * decoding %00 or a forbidden character returns HTTP_NOT_FOUND | ||
| 59 | */ | ||
| 60 | static int unescape_url(char *url, const char *forbid, const char *reserved) | ||
| 61 | { | ||
| 62 | register int badesc, badpath; | ||
| 63 | char *x, *y; | ||
| 64 | |||
| 65 | badesc = 0; | ||
| 66 | badpath = 0; | ||
| 67 | /* Initial scan for first '%'. Don't bother writing values before | ||
| 68 | * seeing a '%' */ | ||
| 69 | y = strchr(url, '%'); | ||
| 70 | if (y == NULL) { | ||
| 71 | return APR_SUCCESS; | ||
| 72 | } | ||
| 73 | for (x = y; *y; ++x, ++y) { | ||
| 74 | if (*y != '%') { | ||
| 75 | *x = *y; | ||
| 76 | } | ||
| 77 | else { | ||
| 78 | if (!apr_isxdigit(*(y + 1)) || !apr_isxdigit(*(y + 2))) { | ||
| 79 | badesc = 1; | ||
| 80 | *x = '%'; | ||
| 81 | } | ||
| 82 | else { | ||
| 83 | char decoded; | ||
| 84 | decoded = x2c(y + 1); | ||
| 85 | if ((decoded == '\0') | ||
| 86 | || (forbid && strchr(forbid, decoded))) { | ||
| 87 | badpath = 1; | ||
| 88 | *x = decoded; | ||
| 89 | y += 2; | ||
| 90 | } | ||
| 91 | else if (reserved && strchr(reserved, decoded)) { | ||
| 92 | *x++ = *y++; | ||
| 93 | *x++ = *y++; | ||
| 94 | *x = *y; | ||
| 95 | } | ||
| 96 | else { | ||
| 97 | *x = decoded; | ||
| 98 | y += 2; | ||
| 99 | } | ||
| 100 | } | ||
| 101 | } | ||
| 102 | } | ||
| 103 | *x = '\0'; | ||
| 104 | if (badesc) { | ||
| 105 | return APR_EINVAL; | ||
| 106 | } | ||
| 107 | else if (badpath) { | ||
| 108 | return APR_EINVAL; | ||
| 109 | } | ||
| 110 | else { | ||
| 111 | return APR_SUCCESS; | ||
| 112 | } | ||
| 113 | } | ||
| 114 | |||
| 115 | /* | ||
| 116 | * *** Ripped from HTTPD util.c (why are so many PORTABLE things not in APR UTIL?) | ||
| 117 | */ | ||
| 118 | int ap_unescape_url(char *url) | ||
| 119 | { | ||
| 120 | /* Traditional */ | ||
| 121 | #ifdef CASE_BLIND_FILESYSTEM | ||
| 122 | return unescape_url(url, "/\\", NULL); | ||
| 123 | #else | ||
| 124 | return unescape_url(url, "/", NULL); | ||
| 125 | #endif | ||
| 126 | } | ||
| 127 | |||
| 128 | void logging_preinit(config_t *cfg) | ||
| 129 | { | ||
| 130 | apr_pool_create(&cfg->errorlog_p, cfg->pool); | ||
| 131 | apr_file_open_stderr(&cfg->errorlog_fperr, cfg->pool); | ||
| 132 | } | ||
| 133 | |||
| 134 | void logging_init(config_t *cfg) | ||
| 135 | { | ||
| 136 | apr_status_t rv; | ||
| 137 | if (cfg->errorlog) { | ||
| 138 | rv = apr_file_open(&cfg->errorlog_fp, cfg->errorlog, | ||
| 139 | APR_FOPEN_CREATE | APR_FOPEN_WRITE | APR_FOPEN_APPEND, | ||
| 140 | APR_OS_DEFAULT, | ||
| 141 | cfg->pool); | ||
| 142 | if (rv) { | ||
| 143 | printf("Error opening %s\n",cfg->errorlog); | ||
| 144 | cfg->loglevel = LOGLEVEL_NONE; | ||
| 145 | } | ||
| 146 | logging_log(cfg, LOGLEVEL_ERROR, "Log file Opened"); | ||
| 147 | } else { | ||
| 148 | cfg->loglevel = LOGLEVEL_NONE; | ||
| 149 | logging_log(cfg, LOGLEVEL_NOISE, "No Log file specified, disabled logging"); | ||
| 150 | } | ||
| 151 | } | ||
| 152 | |||
| 153 | const char *logging_strerror(apr_status_t rv) | ||
| 154 | { | ||
| 155 | char buff[256]; | ||
| 156 | return apr_strerror(rv, buff, 256); | ||
| 157 | } | ||
| 158 | |||
| 159 | void logging_log(config_t *cfg, loglevel_e level, const char *fmt, ...) | ||
| 160 | { | ||
| 161 | va_list ap; | ||
| 162 | char date[APR_RFC822_DATE_LEN]; | ||
| 163 | struct iovec vec[4]; | ||
| 164 | apr_size_t blen; | ||
| 165 | |||
| 166 | if (cfg->loglevel < level) return; | ||
| 167 | |||
| 168 | va_start(ap, fmt); | ||
| 169 | apr_pool_clear(cfg->errorlog_p); | ||
| 170 | |||
| 171 | apr_rfc822_date(date, apr_time_now()); | ||
| 172 | vec[0].iov_base = date; | ||
| 173 | vec[0].iov_len = APR_RFC822_DATE_LEN-1; | ||
| 174 | vec[1].iov_base = " "; | ||
| 175 | vec[1].iov_len = 2; | ||
| 176 | vec[2].iov_base = apr_pvsprintf(cfg->errorlog_p, fmt, ap); | ||
| 177 | vec[2].iov_len = strlen(vec[2].iov_base); | ||
| 178 | vec[3].iov_base = "\n"; | ||
| 179 | vec[3].iov_len = 1; | ||
| 180 | |||
| 181 | if (level == LOGLEVEL_NOISE) { | ||
| 182 | apr_file_writev(cfg->errorlog_fperr,&vec[2],2,&blen); | ||
| 183 | } | ||
| 184 | if (cfg->loglevel > LOGLEVEL_NONE && cfg->errorlog_fp) { | ||
| 185 | apr_file_writev(cfg->errorlog_fp,vec,4,&blen); | ||
| 186 | } | ||
| 187 | |||
| 188 | va_end(ap); | ||
| 189 | } | ||
diff --git a/utility/util.h b/utility/util.h new file mode 100644 index 0000000..a5a7f7e --- /dev/null +++ b/utility/util.h | |||
| @@ -0,0 +1,26 @@ | |||
| 1 | #ifndef UTIL_H_ | ||
| 2 | #define UTIL_H_ | ||
| 3 | |||
| 4 | #include "apr_pools.h" | ||
| 5 | |||
| 6 | #include "config.h" | ||
| 7 | |||
| 8 | char *lowerstr(apr_pool_t *pool, const char *input); | ||
| 9 | |||
| 10 | /** | ||
| 11 | * Chomp new line characters off the end of the line | ||
| 12 | */ | ||
| 13 | void line_chomp(char *str); | ||
| 14 | |||
| 15 | int ap_unescape_url(char *url); | ||
| 16 | |||
| 17 | void logging_preinit(config_t *cfg); | ||
| 18 | |||
| 19 | void logging_init(config_t *cfg); | ||
| 20 | |||
| 21 | void logging_log(config_t *cfg, loglevel_e level, const char *fmt, ...) | ||
| 22 | __attribute__((format(printf, 3, 4))); | ||
| 23 | |||
| 24 | const char *logging_strerror(apr_status_t rv); | ||
| 25 | |||
| 26 | #endif /*UTIL_H_*/ | ||
