From 92d85f793b1a41bbbde1811004ae2708a47a44aa Mon Sep 17 00:00:00 2001
From: Christopher Powell
Date: Wed, 28 Nov 2001 05:26:53 +0000
Subject: Initial revision
---
CHANGELOG | 99 +++++++
CONFIGURATION | 8 +
INSTALL | 180 ++++++++++++
LICENSE | 58 ++++
Makefile | 28 ++
README | 204 +++++++++++++
access_log.sql | 13 +
make_combined_log.pl | 133 +++++++++
mod_log_sql.c | 786 +++++++++++++++++++++++++++++++++++++++++++++++++++
9 files changed, 1509 insertions(+)
create mode 100644 CHANGELOG
create mode 100644 CONFIGURATION
create mode 100644 INSTALL
create mode 100644 LICENSE
create mode 100644 Makefile
create mode 100644 README
create mode 100644 access_log.sql
create mode 100755 make_combined_log.pl
create mode 100644 mod_log_sql.c
diff --git a/CHANGELOG b/CHANGELOG
new file mode 100644
index 0000000..674fbec
--- /dev/null
+++ b/CHANGELOG
@@ -0,0 +1,99 @@
+$Id: CHANGELOG,v 1.1 2001/11/28 05:26:54 helios Exp $
+
+
+TODO:
+* Full commenting of the code.
+
+
+CHANGES:
+
+1.09:
+
+* If the MySQL INSERT causes an error, we re-try that INSERT after
+ a short 1/2-second sleep just to make sure it wasn't due to a
+ network glitch or other gremlin.
+* Made the default log format: huSUsbTvRA. This provides everything
+ required to reproduce Combined Log Format data.
+
+
+1.08:
+
+* Now log a single '-' (instead of a zero-length string) when
+ User-Agent is blank. This is similar to what Apache does in its own
+ logs. (Should have caught this when I did the same thing for
+ Referer.)
+* Separated documentation into README, INSTALL, CHANGELOG, etc. as
+ appropriate.
+
+
+1.07:
+
+* Renamed TransferIgnore directive to RequestIgnore, since that's
+ really a more specific and accurate description of what that
+ directive means.
+* Now log a single '-' (instead of a zero-length string) when Referer
+ is blank. This is similar to what Apache does in its own logs.
+
+
+1.06:
+
+* Added 'R' and 'A' options to TransferLogMySQLFormat so that we now
+ can log Referer and Agent respectively.
+* Code cleanup: all compilation warnings are now gone, even with -Wall.
+ (They were mainly "const" issues that needed straightening up.)
+* Added RemhostIgnore configuration directive to permit non-logging of
+ any request coming from a specific host, e.g. a local network
+ machine, etc.
+* Now use the non-obsolete ap_compat.h headerfile instead of
+ compat.h. This simply gets rid of a compilation warning, nothing
+ more.
+* Now include a headerfile (http_log.h) that was missing. Its
+ absence was giving us this warning message: "implicit declaration of
+ function `ap_log_error_old'."
+* For numerics that Apache customarily logs as a "-" we now log a zero
+ instead of a -1. This seems to be more intuitive, e.g. in the
+ "bytes_sent" column.
+* We now have a Makefile and a full "make" process that does all
+ the real work.
+* New maintainer.
+
+
+1.05:
+
+* Removed some redundant code, after being noted by Vivek Khera that
+ this code doesn't even get called with the current apache code. It
+ can be done in apache 1.3, but it works ok without it anyway.
+* Added the necessary include file to make the module compile under
+ Apache 1.3b6. I haven't actually tested that it works, though.
+ indent'd the code.
+
+
+1.04:
+
+* Rearranged some code to allow for a successful apache 1.3beta
+ compilation. Please note that this is *untested*, I only got it to
+ compile, I haven't actually tried to run apache 1.3.
+
+
+1.03:
+
+* Changed the check for 'mysql server has gone away' to be case
+ insensitive, so that it works with MySQL 3.21
+* Changed the behavior so that a link isn't established until it's
+ necessary (e.g., if SQL logging is used for one virtual IP, a link
+ won't be opened until there's an access to that IP).
+
+
+1.02:
+
+* Managed to track down that segmentation fault that occured once, and
+ fixed it. No known bugs now exist.
+
+
+1.01:
+
+* Segmentation fault in case of certain parameters lacking fixed.
+* Worked around the SIGPIPE signal that's sent in certain events from
+* mysql_query(). Minor modifications
+
+
diff --git a/CONFIGURATION b/CONFIGURATION
new file mode 100644
index 0000000..75117c3
--- /dev/null
+++ b/CONFIGURATION
@@ -0,0 +1,8 @@
+$Id: CONFIGURATION,v 1.1 2001/11/28 05:26:54 helios Exp $
+
+
+Run-time configuration directives are fully documented on the
+mod_log_mysql homepage:
+
+http://www.grubbybaby.com/mod_log_mysql/directives.html
+
diff --git a/INSTALL b/INSTALL
new file mode 100644
index 0000000..0229879
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,180 @@
+$Id: INSTALL,v 1.1 2001/11/28 05:26:54 helios Exp $
+
+
+Requirements
+============
+
+0) I run a Red Hat 6.2 system, but these instructions should easily
+ adapt to any modern distro.
+
+1) Apache 1.2.x or higher installed. (I run 1.3.22 and it works fine).
+ You should have already successfully compiled Apache and know what
+ you're doing there. In fact, you should already have any other
+ modules and add-ons like mod_ssl or PHP configured and installed
+ before you start this process.
+
+2) The MySQL development headers. (I run MySQL-devel-3.23.44-1.i386.rpm).
+
+3) MySQL configured, installed and running on either localhost or an
+ accessible networked machine. You should already have a basic
+ understanding of MySQL and how it functions.
+
+4) Again, basic administrative skills with Apache and MySQL. I try to
+ make things as easy as possible in this README, but its purpose is
+ not to be an administrative tutorial.
+
+Installation
+============
+
+0) Perform all the following steps as root so that you have install
+ privs, etc.
+
+1) Unpack the archive into a working directory.
+
+ # tar zxf mod_log_mysql.tar.gz -C /usr/local/src
+ # cd /usr/local/src/mod_log_mysql
+
+2) Edit Makefile and make any adjustments for your system: make sure
+ that APACHEDIR points to the location of your Apache source code,
+ and that CFLAGS points to the location of your Apache installation
+ directory (where your httpd binary lives).
+
+3) # make all
+ (You should receive NO warnings or errors of any kind.
+ If you see messages like this: "mod_log_mysql.c:69: httpd.h: No such
+ file or directory" then you do not have your CFLAGS correctly
+ pointing to the right include directory.)
+
+4) # make install
+
+5) Change to your Apache source dir.
+
+ # cd /usr/local/src/apache-1.3.22/src
+
+6) Re-make your httpd binary as follows.
+
+ 6a) Edit Configuration.apaci as follows...
+
+ * Append the following string to the EXTRA_LIBS= line. (/usr/lib/mysql is where your libmysqlclient.a file lives):
+ -L/usr/lib/mysql -lmysqlclient -lm
+
+ * Add this line at the end of the file:
+ Module mysql_log_module mod_log_mysql.o
+
+ 6b) # cp Configuration.apaci Configuration
+
+ 6c) # ./Configure
+
+ 6d) # make
+
+ 6e) # strip httpd
+
+7) Test your new apache binary:
+
+ # ./httpd -l
+
+ You should see something like:
+
+ Compiled-in modules:
+ http_core.c
+ mod_log_mysql.c <-- That's the line you're looking for.
+ mod_env.c
+ mod_log_config.c
+ mod_mime.c
+ mod_negotiation.c
+ ...etc...
+
+8) Install your httpd binary. Copy it over your old httpd binary,
+ wherever it lives. You can and should rename your old httpd first so
+ that you can easily revert to that working version in case of bugs or
+ whatever.
+
+ # /etc/rc.d/init.d/httpd stop
+ # cp -f ./httpd /usr/local/Apache/bin/
+
+9) Configure your apache daemon to log to your database. Here's a very
+ basic set of config lines to start you off. Full docs on them are
+ included after this section.
+
+ EXAMPLE: Connect to the MySQL database called "apache" running
+ on "dbmachine.foo.com". The module uses username "loguser" and
+ password "l0gger" to authenticate to the database; this user must,
+ of course, exist in the MySQL user table and have the proper
+ permissions -- more on that in step 11. The log entries will be
+ INSERTed into the table called "access_log".
+
+
+ LogMySQLInfo dbmachine.foo.com loguser l0gger
+ LogMySQLDB apache
+
+
+ [snip]
+
+ TransferLogMySQLTable access_log
+ TransferLogMySQLFormat huSUsbTvRA
+
+ [snip]
+
+
+
+10) Create a database and table to hold the new log data. I log the
+ same data as the regular "combined log" plus a little extra information
+ that can be useful.
+
+ The order that the fields appear in the table is irrelevant
+ because you can SELECT them in any order you choose. To create
+ this table I first created a new database called "apache":
+
+ # mysql -uadmin -pmypassword
+ mysql> create database apache;
+
+ Then I created the table called "access_log". You should use the
+ enclosed SQL file to do this for you.
+
+ mysql> source access_log.sql
+
+
+11) Create a specific mysql userid that httpd will use to authenticate
+ and enter data. This userid need not be an actual Unix user. It
+ is a userid specific to mysql with specific privileges. To create a
+ user called "loguser" with the password "l0gger" with only the
+ capability of INSERT to "access_log":
+
+ mysql> grant insert on apache.access_log to loguser@my.apachemachine.com identified by 'l0gger';
+
+
+12) Enable full logging of your MySQL daemon (at least temporarily
+ for debugging purposes) if you don't do this already:
+
+ Edit /etc/my.cnf and add the following line to your [mysqld] section:
+
+ log=/var/log/mysql-messages
+
+ Then restart MySQL.
+
+13) Restart apache.
+
+ # /etc/rc.d/init.d/httpd start
+
+13) Load your web site in a browser to trigger some hits, then confirm that
+ the entries are being successfully logged:
+
+ # mysql -hmysql.host.com -umysqladmin -p -e "select * from access_log" apache;
+ Enter password:
+
+ +---------------------------------------------------+-------------+-------------+------------------+------------------+------------+--------+------------+------------------------------------+
+ | remote_host | remote_user | request_uri | request_duration | virtual_host | time_stamp | status | bytes_sent | referer |
+ +---------------------------------------------------+-------------+-------------+------------------+------------------+------------+--------+------------+------------------------------------+
+ [snipped lines]
+ .
+ .
+ .
+ +---------------------------------------------------+-------------+-------------+------------------+------------------+------------+--------+------------+------------------------------------+
+
+14) You have basic functionality. Don't disable your regular Apache logs until
+ you feel comfortable that the database is behaving as you'd like and that
+ things are going well.
+
+
+
+
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..cd0c286
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,58 @@
+$Id: LICENSE,v 1.1 2001/11/28 05:26:54 helios Exp $
+
+
+In compliance with the requirements set forth by The Apache Group,
+the original license to this code is hereby reproduced:
+
+
+====================================================================
+Copyright (c) 1995-1997 The Apache Group. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+3. All advertising materials mentioning features or use of this
+ software must display the following acknowledgment:
+ "This product includes software developed by the Apache Group
+ for use in the Apache HTTP server project (http://www.apache.org/)."
+
+4. The names "Apache Server" and "Apache Group" must not be used to
+ endorse or promote products derived from this software without
+ prior written permission.
+
+5. Redistributions of any form whatsoever must retain the following
+ acknowledgment:
+ "This product includes software developed by the Apache Group
+ for use in the Apache HTTP server project (http://www.apache.org/)."
+
+THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
+ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+OF THE POSSIBILITY OF SUCH DAMAGE.
+====================================================================
+
+This software consists of voluntary contributions made by many
+individuals on behalf of the Apache Group and was originally based
+on public domain software written at the National Center for
+Supercomputing Applications, University of Illinois, Urbana-Champaign.
+For more information on the Apache Group and the Apache HTTP server
+project, please see .
+
+
\ No newline at end of file
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..cd349d0
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,28 @@
+# $Id: Makefile,v 1.1 2001/11/28 05:26:54 helios Exp $
+
+# Verify that this points to the right place...
+APACHEDIR = /usr/local/src/apache_1.3.22/src
+
+# Verify that this include directory is correct for you...
+CFLAGS = -fpic -O2 -Wall -I/usr/local/Apache/include
+
+# ---------------------------------------------------------
+# You shouldn't have to touch below here!
+
+CC = gcc
+DEFS = -DSHARED_MODULE
+INSTALL = /usr/bin/install -m 664
+
+all: mod_log_mysql.o
+
+mod_log_mysql.o: mod_log_mysql.c
+ $(CC) ${CFLAGS} ${DEFS} -c mod_log_mysql.c
+
+install: all
+ $(INSTALL) mod_log_mysql.o ${APACHEDIR}/mod_log_mysql.o
+
+distro: all
+ cd ..; tar zcf mod_log_mysql.tar.gz mod_log_mysql/; $(INSTALL) mod_log_mysql.tar.gz /usr/local/Apache/html/mod_log_mysql/; rm -f mod_log_mysql.tar.gz
+
+clean:
+ rm -f *.o *~
diff --git a/README b/README
new file mode 100644
index 0000000..23b8a91
--- /dev/null
+++ b/README
@@ -0,0 +1,204 @@
+$Id: README,v 1.1 2001/11/28 05:26:54 helios Exp $
+
+
+Homepage
+--------
+http://www.grubbybaby.com/mod_log_mysql/
+
+
+
+Approach
+--------
+
+In order to save speed and overhead, links are kept alive in between
+queries. This module uses one SQL link per httpd process. Among other
+things, this means that this module supports logging into only one
+MySQL server, and for now, also, only one SQL database (although the
+latter limitation can be relatively easily removed).
+
+Different data can be sent to different tables. i.e., it's possible to
+define one table for TransferLog, one for RefererLog, and a 3rd for
+AgentLog. [ Note: this is now deprecated behavior. Please consider
+logging Agent and Referer to the same table as your transfers. ]
+
+Virtual hosts are supported in the same manner they are in the regular
+logging modules. If you specify a different table for a virtual
+host it will be used, otherwise the 'general' would be used. Note:
+since all 3 types of logs are implemented within the same module, if
+you specify an overriding table for a virtual host for one type of log,
+it'll ignore any previous 'general' defaults (see the example in the
+end).
+
+SQL links are opened on demand (i.e., the first time each httpd needs
+to log something to SQL, the link is opened). In case the SQL server
+is down when trying to connect to it, the module remains silent and
+logs no error (I didn't want thousands of error messages in the
+logfile). In case the SQL link is broken ("mysql server has gone
+away") a proper error message is kept to the error log (textual :), and
+the module tries to reestablish the concact (and reports whether it
+succeeded or not in the error log). If the link cannot be
+reestablished, the module will, again, remain silent. Technical note:
+The SQL link is registered using apache's pool mechanism, so SQL links
+are properly closed on any normal shutdown, kill -HUP or kill -TERM.
+This also means that if you restart the MySQL daemon for any reason you
+should restart Apache.
+
+
+
+Supported directives
+--------------------
+
+Please see the web-based documentation for full explanation of all
+supported run-time directives.
+
+http://www.grubbybaby.com/mod_log_mysql/directives.html
+
+
+
+What gets logged by default?
+----------------------------
+
+All the data that would be contained in the "Combined Log Format"
+is logged by default, plus a little extra. Your best bet is to
+accept this default and employ the enclosed access_log.sql to
+format your table. Customize your logging format after you've
+had a chance to experiment with the default first.
+
+The MySQL table looks like this if you use the enclosed access_log.sql:
+
++------------------+------------------+
+| Field | Type |
++------------------+------------------+
+| remote_host | varchar(50) |
+| remote_user | varchar(50) |
+| request_uri | varchar(50) |
+| request_duration | smallint(6) |
+| virtual_host | varchar(50) |
+| time_stamp | int(10) unsigned |
+| status | smallint(6) |
+| bytes_sent | int(11) |
+| referer | varchar(255) |
+| agent | varchar(255) |
++------------------+------------------+
+
+remote_host: corresponds to the Apache %h directive. Contains the remote
+ hostname or IP of the machine accessing your server.
+ Example: si4002.inktomi.com
+
+remote_user: corresponds to the Apache %u directive. Contains the
+ userid of people who have authenticated to your server, if applicable.
+ Example: freddy
+
+request_uri: corresponds to the Apache %U directive. Contains the
+ URL path requested, excluding any query string. This is different than
+ the %r information you might be used to seeing:
+
+ %r: GET /cgi-bin/neomail.pl?sessionid=freddy-session-0.742143231719&sort=date_rev HTTP/1.1
+ %U: /cgi-bin/neomail.pl
+
+ We log %U because it contains the real meat of the information that is
+ needed for log analysis, and saves the database a LOT of wasted growth
+ on unneeded bytes.
+
+request_duration: corresponds to the Apache %T directive. Contains the
+ time in seconds that it took to serve the request.
+ Example: 2
+
+virtual_host: contains the VirtualHost that is making the log entry. This
+ allows you to log multiple VirtualHosts to a single MySQL database and
+ yet still be able to extract them for separate analysis.
+ Example: www.grubbybaby.com
+
+time_stamp: contains the time that the request was logged. Please see
+ "Notes" below to get a better understanding of this.
+ Example: 1014249231
+
+status: corresponds to the Apache %t directive. Contains the HTTP status
+ of the request.
+ Example: 404
+
+bytes_sent: corresponds to the Apache %b directive. Contains the number
+ of bytes sent to service the request.
+ Example: 23123
+
+referer: corresponds to the Apache "%{Referer}i" directive. Contains the
+ referring HTML page's URL, if applicable.
+ Example: http://www.foobar.com/links.html
+
+agent: corresponds to the Apache "%{User-Agent}" directive. Contains the
+ broswer type (user agent) of the software that made the request.
+ Example: Mozilla/3.0 (Slurp/si; slurp@inktomi.com; http://www.inktomi.com/slurp.html)
+
+
+Notes
+-----
+
+* The 'time_stamp' field is stored in an UNSIGNED INTEGER column, in the
+ standard unix "seconds since 1/1/1970 12:00:00" format. This is
+ superior to storing the access time as a string due to size
+ requirements: an UNSIGNED INT type fits in 4 bytes. The Apache date
+ string (e.g. "18/Nov/2001:13:59:52 -0800") requires 26 bytes --
+ significantly larger, and those extra 22 bytes will add up over the
+ thousands of accesses that a busy server will experience. Besides,
+ an INT type is far more flexible for comparisons, etc.
+
+ In MySQL 3.21 and above you can easily convert this to a human
+ readable format using from_unixtime(), e.g.:
+
+ select remote_host,request_uri,from_unixtime(time_stamp) from access_log;
+
+ The enclosed perl program make_combined_log.pl shows how you can
+ extract your access records in a format that is completely Combined
+ Log Format compliant. You can then feed this to your favorite web
+ log analysis tool.
+
+
+* The table's string values can be CHAR or VARCHAR, at a length of your choice.
+ VARCHAR is superior because it truncates long strings; CHAR types are
+ fixed-length and will be padded with spaces. Just like the
+ time_stamp described above, that kind of space waste will add up over
+ thousands of records.
+
+
+* Most fields should probably be set to NOT NULL. The only ones that
+ shouldn't are extra fields that you don't intend the logging module
+ to update. (You can have other fields in the logging tables if you'd
+ like, but if they're set to NOT NULL then the logging module won't be
+ able to insert rows to these tables.)
+
+
+* Apache normally logs numeric fields with a '-' character to mean "not
+ applicable," e.g. bytes_sent on a request with a 304 response code.
+ Since '-' is an illegal character in an SQL numeric field, such
+ fields are assigned the value 0 instead of '-' which, of course,
+ makes perfect sense anyway.
+
+
+Disclaimer
+----------
+
+It works for me (I've tested it on my '2 hits/busy day' home Linux box,
+and afterwards on our pretty busy tucows mirror (>100K hits a day) and
+it appears to be working fine.
+
+If it doesn't, and causes you damage of any sort, including but not
+limited to losing logs, losing money or your girlfriend leaving you
+(read 'boyfriend' where applicable), I'm not liable to anything. Bug
+reports and constructive flame mail are ok, though (both about the code
+and this quickly-written README file).
+
+
+Author / Maintainer
+-------------------
+
+The actual logging code was taken from the already existing flat file
+text modules, so all that credit goes to the Apache Server group.
+
+The MySQL routines and directives was added in by Zeev Suraski
+
+
+Changes from 1.06 on and the new documentation were added by
+Chris Powell . It seems that the module had fallen
+into the "unmaintained" category -- it hadn't been updated since 1998 --
+so I've adopted it as the new maintainer.
+
diff --git a/access_log.sql b/access_log.sql
new file mode 100644
index 0000000..c38cb50
--- /dev/null
+++ b/access_log.sql
@@ -0,0 +1,13 @@
+create table access_log (
+ remote_host varchar(50) not null,
+ remote_user varchar(50) not null,
+ request_uri varchar(50) not null,
+ request_duration smallint not null,
+ virtual_host varchar(50) not null,
+ time_stamp int unsigned not null,
+ status smallint not null,
+ bytes_sent int not null,
+ referer varchar(255) not null,
+ agent varchar(255) not null
+)
+
diff --git a/make_combined_log.pl b/make_combined_log.pl
new file mode 100755
index 0000000..d375f3a
--- /dev/null
+++ b/make_combined_log.pl
@@ -0,0 +1,133 @@
+#!/usr/bin/perl
+
+# $Id: make_combined_log.pl,v 1.1 2001/11/28 05:26:54 helios Exp $
+#
+# make_combined_log.pl
+#
+# Usage: make_combined_log
+#
+# This perl script extracts the httpd access data from a MySQL database
+# and formats it properly for parsing by 3rd-party log analysis tools.
+#
+# The script is intended to be run out by cron. Its commandline arguments tell
+# it how many days' worth of access records to extract, and which virtual_host
+# you are interested in (because many people log several virthosts to one MySQL
+# db.) This permits you to run it daily, weekly, every 9 days -- whatever you
+# decide.
+#
+# Note: By "days" I mean "chunks of 24 hours prior to the moment this script is
+# run." So if you run it at 4:34 p.m. on the 12th, it will go back through 4:34
+# p.m. on the 11th.
+#
+# Known issues:
+# * Because GET and POST are not discriminated in the MySQL log, we'll just
+# assume that all requests are GETs. This should have negligible effect
+# on any analysis software. This could be remedied IF you stored the full
+# HTTP request in your database instead of just the URI, but that's going to
+# cost you a LOT of space really quickly...
+#
+# * Because this is somewhat of a quick hack it doesn't do the most robust
+# error checking in the world. Run it by hand to confirm your usage before
+# putting it in crontab.
+
+$| = 1;
+
+use DBI;
+
+# Remember, $#ARGV is parameters minus one...
+if ($#ARGV != 1) {
+ die "Incorrect usage, please read the perl source code for correct usage."
+}
+
+$days = $ARGV[0];
+$virthost = $ARGV[1];
+
+#
+# Set up the proper variables to permit database access
+#
+$serverName = "your.dbmachine.com";
+$serverPort = "3306";
+$serverUser = "someuser";
+$serverPass = "somepass";
+$serverTbl = "acc_log_tbl";
+$serverDb = "apache";
+
+#
+# Other constants
+#
+$st_tz = "-0800";
+$dt_tz = "-0700";
+$type = "GET";
+$http = "HTTP/1.1";
+
+$now = time();
+$start = $now - (86400 * $days);
+
+#
+# Connect and fetch the records
+#
+$dbh = DBI->connect("DBI:mysql:database=$serverDb;host=$serverName;port=$serverPort",$serverUser,$serverPass);
+if (not $dbh) {
+ die "Unable to connect to the database. Please check your connection variables. (Bad password? Incorrect perms?)";
+}
+
+$records = $dbh->prepare("select remote_host,remote_user,request_uri,request_duration,time_stamp,status,bytes_sent,referer,agent from $serverTbl where virtual_host='$virthost' and time_stamp >= $start");
+$records->execute;
+if (not $records) {
+ die "No such table or the select returned no records."
+}
+
+#Right
+#ariston.netcraft.com - - [14/Nov/2001:05:13:39 -0800] "GET / HTTP/1.0" 200 502 "-" "Mozilla/4.08 [en] (Win98; I)"
+#ariston.netcraft.com - - [14/Nov/2001:05:13:39 -0800] "GET / HTTP/1.0" 200 502 "-" "Mozilla/4.08 [en] (Win98; I)"
+
+#Bad
+#ariston.netcraft.com - - [2001-11-14 05:13:39 -0800] "GET / HTTP/1.1" 200 502 "-" "Mozilla/4.08 [en] (Win98; I)"
+#ariston.netcraft.com - - [2001-11-14 05:13:39 -0800] "GET / HTTP/1.1" 200 502 "-" "Mozilla/4.08 [en] (Win98; I)"
+
+
+#
+# Pull out the data row by row and format it
+#
+while (@data = $records->fetchrow_array) {
+ ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($data[4]);
+ $year=$year+1900;
+
+ # Create format for leading-zero formatting
+ if ($day < 10) { $day = "0$day"; }
+ if ($month < 10) { $month = "0$month"; }
+ if ($hour < 10) { $hour = "0$hour"; }
+ if ($min < 10) { $min = "0$min"; }
+ if ($sec < 10) { $sec = "0$sec"; }
+
+ # Convert numeric month to string month
+ for ($mon) {
+ if (/00/) { $mon = "Jan";}
+ elsif (/01/) { $mon = "Feb";}
+ elsif (/02/) { $mon = "Mar";}
+ elsif (/03/) { $mon = "Apr";}
+ elsif (/04/) { $mon = "May";}
+ elsif (/05/) { $mon = "Jun";}
+ elsif (/06/) { $mon = "Jul";}
+ elsif (/07/) { $mon = "Aug";}
+ elsif (/08/) { $mon = "Sep";}
+ elsif (/09/) { $mon = "Oct";}
+ elsif (/10/) { $mon = "Nov";}
+ elsif (/11/) { $mon = "Dec";}
+ }
+
+ # Create the output
+ print "$data[0] $data[1] - [$mday/$mon/$year:$hour:$min:$sec ";
+ if ($isdst) {
+ print "$dt_tz\] ";
+ } else {
+ print "$st_tz\] ";
+ }
+ print "\"$type $data[2] $http\" $data[5] $data[6] \"$data[7]\" \"$data[8]\"\n";
+}
+
+#
+# Done
+#
+$records->finish;
+
diff --git a/mod_log_sql.c b/mod_log_sql.c
new file mode 100644
index 0000000..1493e41
--- /dev/null
+++ b/mod_log_sql.c
@@ -0,0 +1,786 @@
+/* $Id: mod_log_sql.c,v 1.1 2001/11/28 05:26:55 helios Exp $
+ *
+ * mod_log_mysql.c
+ *
+ * Hi, I'm the new maintainer of this code. If you have any questions,
+ * comments or suggestions (which are always welcome), please contact Chris
+ * Powell . This code still falls under the rules of
+ * the Apache license, and all credit for the code up to my changes is still
+ * preserved below.
+ *
+ * ====================================================================
+ *
+ * The original preface from version 1.05: This module was patched, wrapped
+ * and coded by Zeev Suraski
+ *
+ * It may be used freely, with the same restrictions as its predecessors
+ * (specified below). This module is based on code from standard apache
+ * modules. Their copyright notice follows.
+ *
+ * ====================================================================
+ * Copyright (c) 1995-1997 The Apache Group. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * 4. The names "Apache Server" and "Apache Group" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission.
+ *
+ * 5. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Group and was originally based
+ * on public domain software written at the National Center for
+ * Supercomputing Applications, University of Illinois, Urbana-Champaign.
+ * For more information on the Apache Group and the Apache HTTP server
+ * project, please see .
+ *
+ */
+
+
+#include "httpd.h"
+#include "http_config.h"
+#include "http_log.h"
+#if MODULE_MAGIC_NUMBER >= 19980324
+#include "ap_compat.h"
+#endif
+#include "http_core.h"
+#include
+#include
+
+module mysql_log_module;
+MYSQL log_sql_server, *mysql_log = NULL;
+char *log_db_name = NULL, *db_host = NULL, *db_user = NULL, *db_pwd = NULL;
+
+#define MYSQL_ERROR(mysql) ((mysql)?(mysql_error(mysql)):"MySQL server has gone away")
+
+typedef const char *(*item_key_func) (request_rec *, char *);
+typedef struct {
+ char *referer_table_name, *agent_table_name, *transfer_table_name;
+ array_header *referer_ignore_list;
+ array_header *transfer_ignore_list;
+ array_header *remhost_ignore_list;
+ char *transfer_log_format;
+} log_mysql_state;
+
+
+#if MODULE_MAGIC_NUMBER < 19970103
+extern const char *log_remote_host(request_rec * r, char *a);
+extern const char *log_remote_logname(request_rec * r, char *a);
+extern const char *log_remote_user(request_rec * r, char *a);
+extern const char *log_request_time(request_rec * r, char *a);
+extern const char *log_request_timestamp(request_rec * r, char *a);
+extern const char *log_request_duration(request_rec * r, char *a);
+extern const char *log_request_line(request_rec * r, char *a);
+extern const char *log_request_file(request_rec * r, char *a);
+extern const char *log_request_uri(request_rec * r, char *a);
+extern const char *log_status(request_rec * r, char *a);
+extern const char *log_bytes_sent(request_rec * r, char *a);
+extern const char *log_header_in(request_rec * r, char *a);
+extern const char *log_header_out(request_rec * r, char *a);
+extern const char *log_note(request_rec * r, char *a);
+extern const char *log_env_var(request_rec * r, char *a);
+extern const char *log_virtual_host(request_rec * r, char *a);
+extern const char *log_server_port(request_rec * r, char *a);
+extern const char *log_child_pid(request_rec * r, char *a);
+#else
+static char *format_integer(pool *p, int i)
+{
+ char dummy[40];
+ ap_snprintf(dummy, sizeof(dummy), "%d", i);
+ return pstrdup(p, dummy);
+}
+
+static char *pfmt(pool *p, int i)
+{
+ if (i <= 0) {
+ return "-";
+ } else {
+ return format_integer(p, i);
+ }
+}
+
+static const char *log_remote_host(request_rec * r, char *a)
+{
+ return (char *) get_remote_host(r->connection, r->per_dir_config, REMOTE_NAME);
+}
+
+static const char *log_remote_logname(request_rec * r, char *a)
+{
+ return (char *) get_remote_logname(r);
+}
+
+static const char *log_remote_user(request_rec * r, char *a)
+{
+ char *rvalue = r->connection->user;
+
+ if (rvalue == NULL) {
+ rvalue = "-";
+ } else if (strlen(rvalue) == 0) {
+ rvalue = "\"\"";
+ }
+ return rvalue;
+}
+
+static const char *log_request_line(request_rec * r, char *a)
+{
+ return r->the_request;
+}
+
+static const char *log_request_file(request_rec * r, char *a)
+{
+ return r->filename;
+}
+
+static const char *log_request_uri(request_rec * r, char *a)
+{
+ return r->uri;
+}
+
+static const char *log_status(request_rec * r, char *a)
+{
+ return pfmt(r->pool, r->status);
+}
+
+static const char *log_bytes_sent(request_rec * r, char *a)
+{
+ if (!r->sent_bodyct) {
+ return "-";
+ } else {
+ long int bs;
+ char dummy[40];
+ bgetopt(r->connection->client, BO_BYTECT, &bs);
+ ap_snprintf(dummy, sizeof(dummy), "%ld", bs);
+ return pstrdup(r->pool, dummy);
+ }
+}
+
+static const char *log_header_in(request_rec * r, char *a)
+{
+ return table_get(r->headers_in, a);
+}
+
+static const char *log_header_out(request_rec * r, char *a)
+{
+ const char *cp = table_get(r->headers_out, a);
+ if (!strcasecmp(a, "Content-type") && r->content_type) {
+ cp = r->content_type;
+ }
+ if (cp) {
+ return cp;
+ }
+ return table_get(r->err_headers_out, a);
+}
+
+static const char *log_request_time(request_rec * r, char *a)
+{
+ int timz;
+ struct tm *t;
+ char tstr[MAX_STRING_LEN];
+
+ t = get_gmtoff(&timz);
+
+ if (a && *a) { /* Custom format */
+ strftime(tstr, MAX_STRING_LEN, a, t);
+ } else { /* CLF format */
+ char sign = (timz < 0 ? '-' : '+');
+
+ if (timz < 0) {
+ timz = -timz;
+ }
+ strftime(tstr, MAX_STRING_LEN, "[%d/%b/%Y:%H:%M:%S ", t);
+ ap_snprintf(tstr + strlen(tstr), sizeof(tstr) - strlen(tstr), "%c%.2d%.2d]", sign, timz / 60, timz % 60);
+ }
+
+ return pstrdup(r->pool, tstr);
+}
+
+static const char *log_request_duration(request_rec * r, char *a)
+{
+ char duration[22]; /* Long enough for 2^64 */
+
+ ap_snprintf(duration, sizeof(duration), "%ld", time(NULL) - r->request_time);
+ return pstrdup(r->pool, duration);
+}
+
+static const char *log_virtual_host(request_rec * r, char *a)
+{
+ return pstrdup(r->pool, r->server->server_hostname);
+}
+
+static const char *log_server_port(request_rec * r, char *a)
+{
+ char portnum[22];
+
+ ap_snprintf(portnum, sizeof(portnum), "%u", r->server->port);
+ return pstrdup(r->pool, portnum);
+}
+
+static const char *log_child_pid(request_rec * r, char *a)
+{
+ char pidnum[22];
+ ap_snprintf(pidnum, sizeof(pidnum), "%ld", (long) getpid());
+ return pstrdup(r->pool, pidnum);
+}
+
+static const char *log_referer(request_rec * r, char *a)
+{
+ const char *tempref;
+
+ tempref = table_get(r->headers_in, "Referer");
+ if (!tempref)
+ {
+ return "-";
+ } else {
+ return tempref;
+ }
+
+}
+
+static const char *log_agent(request_rec * r, char *a)
+{
+ const char *tempag;
+
+ tempag = table_get(r->headers_in, "User-Agent");
+ if (!tempag)
+ {
+ return "-";
+ } else {
+ return tempag;
+ }
+}
+
+const char *log_request_timestamp(request_rec * r, char *a)
+{
+ char tstr[32];
+
+ snprintf(tstr, 32, "%ld", time(NULL));
+ return pstrdup(r->pool, tstr);
+}
+
+static const char *log_note(request_rec * r, char *a)
+{
+ return table_get(r->notes, a);
+}
+
+static const char *log_env_var(request_rec * r, char *a)
+{
+ return table_get(r->subprocess_env, a);
+}
+#endif
+
+struct log_mysql_item_list {
+ char ch;
+ item_key_func func;
+ const char *sql_field_name;
+ int want_orig_default;
+ int string_contents;
+} log_mysql_item_keys[] = {
+
+ { 'h', log_remote_host, "remote_host", 0, 1 },
+ { 'l', log_remote_logname, "remote_logname", 0, 1 },
+ { 'u', log_remote_user, "remote_user", 0, 1 },
+ { 't', log_request_time, "request_time", 0, 1 },
+ { 'S', log_request_timestamp, "time_stamp", 0, 0 },
+ { 'T', log_request_duration, "request_duration", 1, 0 },
+ { 'r', log_request_line, "request_line", 1, 1 },
+ { 'f', log_request_file, "request_file", 0, 1 },
+ { 'U', log_request_uri, "request_uri", 1, 1 },
+ { 's', log_status, "status", 1, 0 },
+ { 'b', log_bytes_sent, "bytes_sent", 0, 0 },
+ { 'i', log_header_in, "header_in", 0, 1 },
+ { 'o', log_header_out, "header_out", 0, 1 },
+ { 'n', log_note, "note", 0, 1 },
+ { 'e', log_env_var, "env_var", 0, 1 },
+ { 'v', log_virtual_host, "virtual_host", 0, 1 },
+ { 'p', log_server_port, "server_port", 0, 0 },
+ { 'P', log_child_pid, "child_pid", 0, 0 },
+ { 'R', log_referer, "referer", 0, 1 },
+ { 'A', log_agent, "agent", 0, 1 },
+ { '\0'}
+};
+
+
+/* Routine to escape 'dangerous' characters that would otherwise
+ * corrupt the INSERT string.
+ */
+const char *mysql_escape_log(const char *str, pool *p)
+{
+ register int i = 0, j = 0;
+ int need_to_escape = 0;
+
+ if (!str) {
+ return NULL;
+ }
+ /* first find out if we need to escape */
+ i = 0;
+ while (str[i]) {
+ if (str[i] != '\'' || str[i] != '\\' || str[i] != '\"') {
+ need_to_escape = 1;
+ break;
+ }
+ i++;
+ }
+
+ if (need_to_escape) {
+ char *tmp_str;
+ int length = strlen(str);
+
+ tmp_str = (char *) palloc(p, length * 2 + 1); /* worst case situation, which wouldn't be a pretty sight :) */
+ if (!tmp_str) {
+ return str;
+ }
+ for (i = 0, j = 0; i < length; i++, j++) {
+ switch (str[i]) {
+ case '\'':
+ case '\"':
+ case '\\':
+ tmp_str[j] = '\\';
+ j++;
+ default:
+ tmp_str[j] = str[i];
+ }
+ }
+ tmp_str[j] = 0;
+ return tmp_str;
+ } else {
+ return str;
+ }
+}
+
+
+void open_log_dblink()
+{
+ if (mysql_log != NULL) { /* virtual database link shared with main server */
+ return;
+ }
+ if (log_db_name) { /* open an SQL link */
+ /* link to the MySQL database and register its cleanup!@$ */
+ mysql_log = mysql_connect(&log_sql_server, db_host, db_user, db_pwd);
+ if (mysql_log) { /* link opened */
+ if (mysql_select_db(mysql_log, log_db_name) != 0) { /* unable to select database */
+ mysql_close(mysql_log);
+ mysql_log = NULL;
+ }
+ }
+ }
+}
+
+
+void *make_log_mysql_state(pool *p, server_rec * s)
+{
+ log_mysql_state *cls = (log_mysql_state *) palloc(p, sizeof(log_mysql_state));
+
+ cls->referer_table_name = cls->agent_table_name = cls->transfer_table_name = "";
+ cls->referer_ignore_list = make_array(p, 1, sizeof(char *));
+ cls->transfer_ignore_list = make_array(p, 1, sizeof(char *));
+ cls->remhost_ignore_list = make_array(p, 1, sizeof(char *));
+ cls->transfer_log_format = "";
+ return (void *) cls;
+}
+
+const char *set_referer_log_mysql_table(cmd_parms * parms, void *dummy, char *arg)
+{
+ log_mysql_state *cls = get_module_config(parms->server->module_config,
+ &mysql_log_module);
+
+ cls->referer_table_name = arg;
+ return NULL;
+}
+
+
+const char *set_agent_log_mysql_table(cmd_parms * parms, void *dummy, char *arg)
+{
+ log_mysql_state *cls = get_module_config(parms->server->module_config,
+ &mysql_log_module);
+
+ cls->agent_table_name = arg;
+ return NULL;
+}
+
+
+const char *set_transfer_log_mysql_table(cmd_parms * parms, void *dummy, char *arg)
+{
+ log_mysql_state *cls = get_module_config(parms->server->module_config,
+ &mysql_log_module);
+
+ cls->transfer_table_name = arg;
+ return NULL;
+}
+
+
+const char *set_transfer_log_format(cmd_parms * parms, void *dummy, char *arg)
+{
+ log_mysql_state *cls = get_module_config(parms->server->module_config,
+ &mysql_log_module);
+
+ cls->transfer_log_format = arg;
+ return NULL;
+}
+
+
+const char *set_log_mysql_db(cmd_parms * parms, void *dummy, char *arg)
+{
+ log_db_name = arg;
+ return NULL;
+}
+
+const char *set_log_mysql_info(cmd_parms * parms, void *dummy, char *host, char *user, char *pwd)
+{
+ if (*host != '.') {
+ db_host = host;
+ }
+ if (*user != '.') {
+ db_user = user;
+ }
+ if (*pwd != '.') {
+ db_pwd = pwd;
+ }
+ return NULL;
+}
+
+
+const char *add_referer_mysql_ignore(cmd_parms * parms, void *dummy, char *arg)
+{
+ char **addme;
+ log_mysql_state *cls = get_module_config(parms->server->module_config,
+ &mysql_log_module);
+
+ addme = push_array(cls->referer_ignore_list);
+ *addme = pstrdup(cls->referer_ignore_list->pool, arg);
+ return NULL;
+}
+
+const char *add_transfer_mysql_ignore(cmd_parms * parms, void *dummy, char *arg)
+{
+ char **addme;
+ log_mysql_state *cls = get_module_config(parms->server->module_config,
+ &mysql_log_module);
+
+ addme = push_array(cls->transfer_ignore_list);
+ *addme = pstrdup(cls->transfer_ignore_list->pool, arg);
+ return NULL;
+}
+
+const char *add_remhost_mysql_ignore(cmd_parms * parms, void *dummy, char *arg)
+{
+ char **addme;
+ log_mysql_state *cls = get_module_config(parms->server->module_config,
+ &mysql_log_module);
+
+ addme = push_array(cls->remhost_ignore_list);
+ *addme = pstrdup(cls->remhost_ignore_list->pool, arg);
+ return NULL;
+}
+
+command_rec log_mysql_cmds[] = {
+ {"RefererLogMySQLTable", set_referer_log_mysql_table, NULL, RSRC_CONF, TAKE1,
+ "the table of the referer log"}
+ ,
+ {"AgentLogMySQLTable", set_agent_log_mysql_table, NULL, RSRC_CONF, TAKE1,
+ "the table of the agent log"}
+ ,
+ {"TransferLogMySQLTable", set_transfer_log_mysql_table, NULL, RSRC_CONF, TAKE1,
+ "the table of the transfer log"}
+ ,
+ {"TransferLogMySQLFormat", set_transfer_log_format, NULL, RSRC_CONF, TAKE1,
+ "specific format for the MySQL transfer log"}
+ ,
+ {"RefererIgnore", add_referer_mysql_ignore, NULL, RSRC_CONF, ITERATE,
+ "referer hostnames to ignore"}
+ ,
+ {"RequestIgnore", add_transfer_mysql_ignore, NULL, RSRC_CONF, ITERATE,
+ "transfer log URIs to ignore"}
+ ,
+ {"RemhostIgnore", add_remhost_mysql_ignore, NULL, RSRC_CONF, ITERATE,
+ "transfer log remote hosts to ignore"}
+ ,
+ {"LogMySQLDB", set_log_mysql_db, NULL, RSRC_CONF, TAKE1,
+ "the database of the referer log"}
+ ,
+ {"LogMySQLInfo", set_log_mysql_info, NULL, RSRC_CONF, TAKE3,
+ "host, user and password for MySQL link"}
+ ,
+ {NULL}
+};
+
+
+void log_mysql_child(void *cmd)
+{
+ /* Child process code for 'RefererLog "|..."';
+ * may want a common framework for this, since I expect it will
+ * be common for other foo-loggers to want this sort of thing...
+ */
+
+ cleanup_for_exec();
+ signal(SIGHUP, SIG_IGN);
+#ifdef __EMX__
+ /* For OS/2 we need to use a '/' */
+ execl(SHELL_PATH, SHELL_PATH, "/c", (char *) cmd, NULL);
+#else
+ execl(SHELL_PATH, SHELL_PATH, "-c", (char *) cmd, NULL);
+#endif
+ perror("execl");
+ fprintf(stderr, "Exec of shell for logging failed!!!\n");
+ exit(1);
+}
+
+
+int safe_mysql_query(request_rec * r, const char *query)
+{
+ int error = 1;
+ struct timespec delay, remainder;
+ int ret;
+ char *str;
+ void (*handler) (int);
+
+ handler = signal(SIGPIPE, SIG_IGN); /* a failed mysql_query() may send a SIGPIPE */
+
+ if ( !mysql_log ||
+ (
+ (error = mysql_query(mysql_log, query)) &&
+ !strcasecmp(mysql_error(mysql_log), "MySQL server has gone away")
+ )
+ )
+
+ { /* We need to restart the server link */
+
+ mysql_log = NULL;
+ log_error("MySQL: connection lost, attempting reconnect", r->server);
+
+ open_log_dblink();
+
+ if (mysql_log == NULL) { /* unable to link */
+ signal(SIGPIPE, handler);
+ log_error("MySQL: reconnect failed.", r->server);
+ return error;
+ }
+
+ log_error("MySQL: reconnect successful.", r->server);
+ error = mysql_query(mysql_log, query);
+ }
+
+ signal(SIGPIPE, handler);
+
+ if (error) {
+ /* Attempt a single re-try... First sleep for a tiny amount of time. */
+
+ delay.tv_sec = 0;
+ delay.tv_nsec = 500000000; /* max is 999999999 (nine nines) */
+ ret = nanosleep(&delay, &remainder);
+ if (ret && errno != EINTR)
+ perror("nanosleep");
+
+ /* Now re-attempt */
+ error = mysql_query(mysql_log,query);
+
+ if (error) {
+ str = pstrcat(r->pool, "MySQL query failed: ", query, NULL);
+ log_error(str, r->server);
+ str = pstrcat(r->pool, "MySQL failure reason: ", MYSQL_ERROR(mysql_log), NULL);
+ log_error(str, r->server);
+ } else {
+ log_error("MySQL: INSERT successful after a delayed retry.", r->server);
+ }
+ }
+ return error;
+}
+
+
+/* Routine to perform the actual construction and execution of the relevant
+ * INSERT statements.
+ */
+int log_mysql_transaction(request_rec * orig)
+{
+ char **ptrptr, **ptrptr2;
+ log_mysql_state *cls = get_module_config(orig->server->module_config,
+ &mysql_log_module);
+ char *str;
+ const char *referer;
+ request_rec *r;
+ int retvalue = DECLINED;
+ int referer_needed, agent_needed, transfer_needed;
+
+ /* Are there configuration directives for these logs? For each found
+ * config directive that is found, mark that type as 'needed'.
+ */
+ referer_needed = ((cls->referer_table_name[0] != '\0') ? 1 : 0);
+ agent_needed = ((cls->agent_table_name[0] != '\0') ? 1 : 0);
+ transfer_needed = ((cls->transfer_table_name[0] != '\0') ? 1 : 0);
+
+ if (!referer_needed && !agent_needed && !transfer_needed) {
+ return OK;
+ }
+ if (mysql_log == NULL) { /* mysql link not up, hopefully we can do something about it */
+ open_log_dblink();
+ if (mysql_log == NULL) {
+ return OK;
+ }
+ }
+ for (r = orig; r->next; r = r->next) {
+ continue;
+ }
+
+ /* Log the 'referer' to its own log if configured to do so. */
+ if (referer_needed) {
+ retvalue = OK;
+ referer = table_get(orig->headers_in, "Referer");
+ if (referer != NULL) {
+
+ /* The following is an upsetting mess of pointers, I'm sorry
+ Anyone with the motiviation and/or the time should feel free
+ to make this cleaner... */
+ ptrptr2 = (char **) (cls->referer_ignore_list->elts + (cls->referer_ignore_list->nelts * cls->referer_ignore_list->elt_size));
+
+ /* Go through each element of the ignore list and compare it to the
+ referer_host. If we get a match, return without logging */
+ for (ptrptr = (char **) cls->referer_ignore_list->elts; ptrptr < ptrptr2; ptrptr = (char **) ((char *) ptrptr + cls->referer_ignore_list->elt_size)) {
+ if (strstr(referer, *ptrptr)) {
+ return OK;
+ }
+ }
+ str = pstrcat(orig->pool, "insert into ", cls->referer_table_name, " (referer,url,time_stamp) values ('", mysql_escape_log(referer, orig->pool), "','", mysql_escape_log(r->uri, orig->pool), "',unix_timestamp(now()) )", NULL);
+ safe_mysql_query(orig, str);
+ }
+ }
+
+ /* Log the 'user agent' to its own log if configured to do so. */
+ if (agent_needed) {
+ const char *agent, *str;
+ retvalue = OK;
+ agent = table_get(orig->headers_in, "User-Agent");
+ if (agent != NULL) {
+ str = pstrcat(orig->pool, "insert into ", cls->agent_table_name, "(agent,time_stamp) values ('", mysql_escape_log(agent, orig->pool), "',unix_timestamp(now()) )", NULL);
+ safe_mysql_query(orig, str);
+ }
+ }
+
+ /* Log the transfer to its own log if configured to do so. */
+ if (transfer_needed) {
+ const char *thehost;
+
+ char *fields = "", *values = "", *query;
+ const char *formatted_item;
+ int i, j, length;
+
+ retvalue = OK;
+
+
+ /* The following is a stolen upsetting mess of pointers, I'm sorry
+ Anyone with the motiviation and/or the time should feel free
+ to make this cleaner, and while at it, clean the same mess at the RefererLog part :) */
+ ptrptr2 = (char **) (cls->transfer_ignore_list->elts + (cls->transfer_ignore_list->nelts * cls->transfer_ignore_list->elt_size));
+
+ /* Go through each element of the ignore list and compare it to the
+ request_uri. If we get a match, return without logging */
+ if (r->uri) {
+ for (ptrptr = (char **) cls->transfer_ignore_list->elts; ptrptr < ptrptr2; ptrptr = (char **) ((char *) ptrptr + cls->transfer_ignore_list->elt_size)) {
+ if (strstr(r->uri, *ptrptr)) {
+ return retvalue;
+ }
+ }
+ }
+
+
+ /* Go through each element of the ignore list and compare it to the
+ remote host. If we get a match, return without logging */
+ ptrptr2 = (char **) (cls->remhost_ignore_list->elts + (cls->remhost_ignore_list->nelts * cls->remhost_ignore_list->elt_size));
+ thehost = get_remote_host(r->connection, r->per_dir_config, REMOTE_NAME);
+ if (thehost) {
+ for (ptrptr = (char **) cls->remhost_ignore_list->elts; ptrptr < ptrptr2; ptrptr = (char **) ((char *) ptrptr + cls->remhost_ignore_list->elt_size)) {
+ if (strstr(thehost, *ptrptr)) {
+ return retvalue;
+ }
+ }
+ }
+
+
+ if (cls->transfer_log_format[0] == '\0') {
+ /* If not specified by the user, use the default format */
+ cls->transfer_log_format = "huSUsbTvRA";
+ }
+ length = strlen(cls->transfer_log_format);
+
+ /* Iterate through the characters and set up the INSERT string according to
+ * what the user has configured. */
+ for (i = 0; i < length; i++) {
+ j = 0;
+ while (log_mysql_item_keys[j].ch) {
+ if (log_mysql_item_keys[j].ch == cls->transfer_log_format[i]) {
+ /* Yes, this key is one of the configured keys */
+ formatted_item = log_mysql_item_keys[j].func(log_mysql_item_keys[j].want_orig_default ? orig : r, "");
+ if (!formatted_item) {
+ formatted_item = "";
+ } else if (formatted_item[0] == '-' && formatted_item[1] == '\0' && !log_mysql_item_keys[j].string_contents) {
+ /* If apache tried to log a '-' character for a numeric field, convert that to a zero
+ * because the database expects an integer. */
+ formatted_item = "0";
+ }
+ fields = pstrcat(orig->pool, fields, (i > 0 ? "," : ""), log_mysql_item_keys[j].sql_field_name, NULL);
+ values = pstrcat(orig->pool, values, (i > 0 ? "," : ""), (log_mysql_item_keys[j].string_contents ? "'" : ""), mysql_escape_log(formatted_item, orig->pool), (log_mysql_item_keys[j].string_contents ? "'" : ""), NULL);
+ break;
+ }
+ j++;
+ }
+ }
+
+ /* Set up the actual INSERT statement and execute it. */
+ query = pstrcat(orig->pool, "insert into ", cls->transfer_table_name, " (", fields, ") values (", values, ")", NULL);
+ safe_mysql_query(orig, query);
+
+ }
+ return retvalue;
+}
+
+
+
+module mysql_log_module = {
+ STANDARD_MODULE_STUFF,
+ NULL, /* initializer */
+ NULL, /* create per-dir config */
+ NULL, /* merge per-dir config */
+ make_log_mysql_state, /* server config */
+ NULL, /* merge server config */
+ log_mysql_cmds, /* command table */
+ NULL, /* handlers */
+ NULL, /* filename translation */
+ NULL, /* check_user_id */
+ NULL, /* check auth */
+ NULL, /* check access */
+ NULL, /* type_checker */
+ NULL, /* fixups */
+ log_mysql_transaction, /* logger */
+ NULL /* header parser */
+};
--
cgit