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