summaryrefslogtreecommitdiffstatsabout
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.in168
-rw-r--r--src/functions.h261
-rw-r--r--src/functions13.h58
-rw-r--r--src/functions20.h116
-rw-r--r--src/mod_log_sql.c1578
-rw-r--r--src/mod_log_sql_dbd.c132
-rw-r--r--src/mod_log_sql_dbi.c263
-rw-r--r--src/mod_log_sql_logio.c146
-rw-r--r--src/mod_log_sql_mysql.c269
-rw-r--r--src/mod_log_sql_pgsql.c247
-rw-r--r--src/mod_log_sql_ssl.c106
11 files changed, 3344 insertions, 0 deletions
diff --git a/src/Makefile.in b/src/Makefile.in
new file mode 100644
index 0000000..5a5dd6a
--- /dev/null
+++ b/src/Makefile.in
@@ -0,0 +1,168 @@
1# @configure_input@
2
3top_srcdir = @top_srcdir@
4srcdir = @abs_srcdir@
5builddir = @abs_builddir@
6
7HEADERS = ../include/mod_log_sql.h \
8 functions.h \
9 functions13.h \
10 functions20.h \
11 ../include/apache13.h \
12 ../include/apache20.h \
13 ../include/winconfig.h
14
15CFLAGS = -Wc,-Wall -Wc,-fno-strict-aliasing -I$(top_srcdir)/include
16
17ifeq (@OOO_MAINTAIN@,1)
18CFLAGS += -Wc,-Werror
19endif
20
21coreSOURCES = @PACKAGE_NAME@.c
22coreTARGET = @PACKAGE_NAME@@APXS_EXTENSION@
23coreLDADD = @RT_LIBS@
24coreCFLAGS =
25coreNAME = log_sql
26TARGETS = $(coreTARGET)
27
28sslSOURCES = @PACKAGE_NAME@_ssl.c
29sslTARGET = @PACKAGE_NAME@_ssl@APXS_EXTENSION@
30sslLDADD =
31sslCFLAGS = @MOD_SSL_CFLAGS@
32sslNAME = log_sql_ssl
33
34ifeq (@WANT_SSL_MOD@,1)
35TARGETS += $(sslTARGET)
36endif
37
38logioSOURCES = @PACKAGE_NAME@_logio.c
39logioTARGET = @PACKAGE_NAME@_logio@APXS_EXTENSION@
40logioLDADD =
41logioCFLAGS =
42logioNAME = log_sql_logio
43
44ifeq (@WANT_LOGIO_MOD@,1)
45TARGETS += $(logioTARGET)
46endif
47
48mysqlSOURCES = @PACKAGE_NAME@_mysql.c
49mysqlTARGET = @PACKAGE_NAME@_mysql@APXS_EXTENSION@
50mysqlLDADD = @MYSQL_LDFLAGS@ @MYSQL_LIBS@
51mysqlCFLAGS = @MYSQL_CFLAGS@
52mysqlNAME = log_sql_mysql
53
54ifeq (@WANT_MYSQL_MOD@,1)
55TARGETS += $(mysqlTARGET)
56endif
57
58pgsqlSOURCES = @PACKAGE_NAME@_pgsql.c
59pgsqlTARGET = @PACKAGE_NAME@_pgsql@APXS_EXTENSION@
60pgsqlLDADD = @PGSQL_LDFLAGS@ @PGSQL_LIBS@
61pgsqlCFLAGS = @PGSQL_CFLAGS@
62pgsqlNAME = log_sql_pgsql
63
64ifeq (@WANT_PGSQL_MOD@,1)
65TARGETS += $(pgsqlTARGET)
66endif
67
68dbiSOURCES = @PACKAGE_NAME@_dbi.c
69dbiTARGET = @PACKAGE_NAME@_dbi@APXS_EXTENSION@
70dbiLDADD = @DBI_LDFLAGS@ @DBI_LIBS@
71dbiCFLAGS = @DBI_CFLAGS@
72dbiNAME = log_sql_dbi
73
74ifeq (@WANT_DBI_MOD@,1)
75TARGETS += $(dbiTARGET)
76endif
77
78dbdSOURCES = @PACKAGE_NAME@_dbd.c
79dbdTARGET = @PACKAGE_NAME@_dbd@APXS_EXTENSION@
80dbdLDADD =
81dbdCFLAGS =
82dbdNAME = log_sql_dbd
83
84ifeq (@WANT_DBD_MOD@,1)
85TARGETS += $(dbdTARGET)
86endif
87
88OBJ = $(coreSOURCES:.c=.o) $(logioSOURCES:.c=.o) $(sslSOURCES:.c=.o) $(mysqlSOURCES:.c=.o) \
89 $(dbiSOURCES:.c=.o) $(pgsqlSOURCES:.c=.o) $(dbdSOURCES:.c=.o)
90
91LO = $(coreSOURCES:.c=.lo) $(logioSOURCES:.c=.lo) $(sslSOURCES:.c=.lo) $(mysqlSOURCES:.c=.lo) \
92 $(dbiSOURCES:.c=.lo) $(pgsqlSOURCES:.c=.lo) $(dbdSOURCES:.c=.lo)
93
94SLO = $(coreSOURCES:.c=.slo) $(logioSOURCES:.c=.slo) $(sslSOURCES:.c=.slo) $(mysqlSOURCES:.c=.slo) \
95 $(dbiSOURCES:.c=.slo) $(pgsqlSOURCES:.c=.slo) $(dbdSOURCES:.c=.slo)
96
97STD_DIST = Makefile.in
98
99DISTFILES = $(STD_DIST) $(EXTRA_DIST) $(coreSOURCES) $(HEADERS) \
100 $(sslSOURCES) $(logioSOURCES) $(mysqlSOURCES) $(pgsqlSOURCES) $(dbiSOURCES) $(dbdSOURCES)
101
102all: $(TARGETS)
103
104$(coreTARGET): $(coreSOURCES) $(HEADERS)
105 @@APXS_BIN@ -c -o $(coreTARGET) $(coreCFLAGS) $(CFLAGS) \
106 @DEFS@ @AP_DEFS@ $(coreLDADD) $(coreSOURCES)
107
108$(logioTARGET): $(logioSOURCES) $(HEADERS)
109 @@APXS_BIN@ -c -o $(logioTARGET) $(logioCFLAGS) $(CFLAGS) \
110 @DEFS@ @AP_DEFS@ $(logioLDADD) $(logioSOURCES)
111
112$(sslTARGET): $(sslSOURCES) $(HEADERS)
113 @@APXS_BIN@ -c -o $(sslTARGET) $(sslCFLAGS) $(CFLAGS) \
114 @DEFS@ @AP_DEFS@ $(sslLDADD) $(sslSOURCES)
115
116$(mysqlTARGET): $(mysqlSOURCES) $(HEADERS)
117 @@APXS_BIN@ -c -o $(mysqlTARGET) $(mysqlCFLAGS) $(CFLAGS) \
118 @DEFS@ @AP_DEFS@ $(mysqlLDADD) $(mysqlSOURCES)
119
120$(pgsqlTARGET): $(pgsqlSOURCES) $(HEADERS)
121 @@APXS_BIN@ -c -o $(pgsqlTARGET) $(pgsqlCFLAGS) $(CFLAGS) \
122 @DEFS@ @AP_DEFS@ $(pgsqlLDADD) $(pgsqlSOURCES)
123
124$(dbiTARGET): $(dbiSOURCES) $(HEADERS)
125 @@APXS_BIN@ -c -o $(dbiTARGET) $(dbiCFLAGS) $(CFLAGS) \
126 @DEFS@ @AP_DEFS@ $(dbiLDADD) $(dbiSOURCES)
127
128$(dbdTARGET): $(dbdSOURCES) $(HEADERS)
129 @@APXS_BIN@ -c -o $(dbdTARGET) $(dbdCFLAGS) $(CFLAGS) \
130 @DEFS@ @AP_DEFS@ $(dbdLDADD) $(dbdSOURCES)
131
132install: $(TARGETS)
133 @@APXS_BIN@ -n $(coreNAME) -i $(coreTARGET); \
134 if test @WANT_MYSQL_MOD@ -eq 1; then \
135 @APXS_BIN@ -n $(mysqlNAME) -i $(mysqlTARGET); \
136 fi; \
137 if test @WANT_PGSQL_MOD@ -eq 1; then \
138 @APXS_BIN@ -n $(pgsqlNAME) -i $(pgsqlTARGET); \
139 fi; \
140 if test @WANT_DBI_MOD@ -eq 1; then \
141 @APXS_BIN@ -n $(dbiNAME) -i $(dbiTARGET); \
142 fi; \
143 if test @WANT_DBD_MOD@ -eq 1; then \
144 @APXS_BIN@ -n $(dbdNAME) -i $(dbdTARGET); \
145 fi; \
146 if test @WANT_SSL_MOD@ -eq 1; then \
147 @APXS_BIN@ -n $(sslNAME) -i $(sslTARGET); \
148 fi; \
149 if test @WANT_LOGIO_MOD@ -eq 1; then \
150 @APXS_BIN@ -n $(logioNAME) -i $(logioTARGET); \
151 fi;
152
153activate:
154 @@APXS_BIN@ -n $(coreNAME) -i -a $(coreTARGET); \
155 if test @WANT_SSL_MOD@ -eq 1; then \
156 @APXS_BIN@ -n $(sslNAME) -i -a $(sslTARGET); \
157 fi
158
159clean:
160 $(RM) $(OBJ) $(SLO) $(LO) $(TARGETS)
161 $(RM) -r .libs
162
163local-dist: $(DISTFILES)
164 mkdir -p $(DESTDIR)
165 cp -dp --parents $(DISTFILES) $(DESTDIR)
166
167Makefile: Makefile.in ../config.status
168 cd .. && ./config.status
diff --git a/src/functions.h b/src/functions.h
new file mode 100644
index 0000000..07711d2
--- /dev/null
+++ b/src/functions.h
@@ -0,0 +1,261 @@
1/* $Id$ */
2
3/* Begin the individual functions that, given a request r,
4 * extract the needed information from it and return the
5 * value to the calling entity.
6 */
7
8static const char *extract_remote_host(request_rec *r, char *a)
9{
10 return (char *) ap_get_remote_host(r->connection, r->per_dir_config, REMOTE_NAME, NULL);
11}
12
13static const char *extract_remote_address(request_rec *r, char *a) __attribute__((unused));
14
15static const char *extract_remote_address(request_rec *r, char *a)
16{
17 return r->connection->remote_ip;
18}
19
20static const char *extract_local_address(request_rec *r, char *a) __attribute__((unused));
21
22static const char *extract_local_address(request_rec *r, char *a)
23{
24 return r->connection->local_ip;
25}
26
27static const char *extract_remote_logname(request_rec *r, char *a)
28{
29 return (char *) ap_get_remote_logname(r);
30}
31
32static const char *extract_remote_user(request_rec *r, char *a)
33{
34 #ifdef WITH_APACHE13
35 char *rvalue = r->connection->user;
36 #else
37 char *rvalue = r->user;
38 #endif
39 if (rvalue == NULL) {
40 rvalue = "-";
41 } else if (strlen(rvalue) == 0) {
42 rvalue = "\"\"";
43 }
44 return rvalue;
45}
46
47static const char *extract_request_line(request_rec *r, char *a)
48{
49 /* Upddated to mod_log_config logic */
50 /* NOTE: If the original request contained a password, we
51 * re-write the request line here to contain XXXXXX instead:
52 * (note the truncation before the protocol string for HTTP/0.9 requests)
53 * (note also that r->the_request contains the unmodified request)
54 */
55 return (r->parsed_uri.password)
56 ? apr_pstrcat(r->pool, r->method, " ",
57 apr_uri_unparse(r->pool,
58 &r->parsed_uri, 0),
59 r->assbackwards ? NULL : " ",
60 r->protocol, NULL)
61 : r->the_request;
62}
63
64static const char *extract_request_file(request_rec *r, char *a)
65{
66 return r->filename;
67}
68
69static const char *extract_request_uri(request_rec *r, char *a)
70{
71 return r->uri;
72}
73
74static const char *extract_request_method(request_rec *r, char *a)
75{
76 return r->method;
77}
78
79static const char *extract_request_protocol(request_rec *r, char *a)
80{
81 return r->protocol;
82}
83
84static const char *extract_request_query(request_rec *r, char *a)
85{
86 return (r->args) ? apr_pstrcat(r->pool, "?",
87 r->args, NULL)
88 : "";
89}
90
91static const char *extract_status(request_rec *r, char *a)
92{
93 if (r->status <= 0) {
94 return "-";
95 } else {
96 return apr_psprintf(r->pool, "%d", r->status);
97 }
98}
99
100static const char *extract_virtual_host(request_rec *r, char *a)
101{
102 return r->server->server_hostname;
103}
104
105static const char *extract_server_name(request_rec *r, char *a)
106{
107 return ap_get_server_name(r);
108}
109
110static const char *extract_machine_id(request_rec *r, char *a)
111{
112 if (!global_config.machid)
113 return "-";
114 else
115 return global_config.machid;
116}
117
118static const char *extract_server_port(request_rec *r, char *a)
119{
120 return apr_psprintf(r->pool, "%u",
121 r->server->port ? r->server->port : ap_default_port(r));
122}
123
124/* This respects the setting of UseCanonicalName so that
125 * the dynamic mass virtual hosting trick works better.
126 */
127static const char *log_server_name(request_rec *r, char *a) __attribute__((unused));
128static const char *log_server_name(request_rec *r, char *a)
129{
130 return ap_get_server_name(r);
131}
132
133static const char *extract_child_pid(request_rec *r, char *a)
134{
135 if (*a == '\0' || !strcmp(a, "pid")) {
136 return apr_psprintf(r->pool, "%" APR_PID_T_FMT, getpid());
137 }
138 else if (!strcmp(a, "tid")) {
139#if APR_HAS_THREADS
140 apr_os_thread_t tid = apr_os_thread_current();
141#else
142 int tid = 0; /* APR will format "0" anyway but an arg is needed */
143#endif
144 return apr_psprintf(r->pool, "%pT", &tid);
145 }
146 /* bogus format */
147 return a;
148}
149
150static const char *extract_referer(request_rec *r, char *a)
151{
152 const char *tempref;
153
154 tempref = apr_table_get(r->headers_in, "Referer");
155 if (!tempref)
156 {
157 return "-";
158 } else {
159 return tempref;
160 }
161}
162
163static const char *extract_agent(request_rec *r, char *a)
164{
165 const char *tempag;
166
167 tempag = apr_table_get(r->headers_in, "User-Agent");
168 if (!tempag)
169 {
170 return "-";
171 } else {
172 return tempag;
173 }
174}
175
176static const char *extract_specific_cookie(request_rec *r, char *a)
177{
178 const char *cookiestr;
179 char *cookieend;
180 char *isvalid;
181 char *cookiebuf;
182
183 if (a != NULL) {
184 log_error(APLOG_MARK,APLOG_DEBUG, 0, r->server,
185 "watching for cookie '%s'", a);
186
187 /* Fetch out the cookie header */
188 cookiestr = (char *)apr_table_get(r->headers_in, "cookie2");
189 if (cookiestr != NULL) {
190 log_error(APLOG_MARK,APLOG_DEBUG, 0, r->server,
191 "Cookie2: [%s]", cookiestr);
192 /* Does the cookie string contain one with our name? */
193 isvalid = ap_strstr_c(cookiestr, a);
194 if (isvalid != NULL) {
195 /* Move past the cookie name and equal sign */
196 isvalid += strlen(a) + 1;
197 /* Duplicate it into the pool */
198 cookiebuf = apr_pstrdup(r->pool, isvalid);
199 /* Segregate just this cookie out of the string
200 * with a terminating nul at the first semicolon */
201 cookieend = ap_strchr(cookiebuf, ';');
202 if (cookieend != NULL)
203 *cookieend = '\0';
204 return cookiebuf;
205 }
206 }
207
208 cookiestr = (char *)apr_table_get(r->headers_in, "cookie");
209 if (cookiestr != NULL) {
210 log_error(APLOG_MARK,APLOG_DEBUG, 0, r->server,
211 "Cookie: [%s]", cookiestr);
212 isvalid = ap_strstr_c(cookiestr, a);
213 if (isvalid != NULL) {
214 isvalid += strlen(a) + 1;
215 cookiebuf = apr_pstrdup(r->pool, isvalid);
216 cookieend = ap_strchr(cookiebuf, ';');
217 if (cookieend != NULL)
218 *cookieend = '\0';
219 return cookiebuf;
220 }
221 }
222
223 cookiestr = apr_table_get(r->headers_out, "set-cookie");
224 if (cookiestr != NULL) {
225 log_error(APLOG_MARK,APLOG_DEBUG, 0, r->server,
226 "Set-Cookie: [%s]", cookiestr);
227 isvalid = ap_strstr_c(cookiestr, a);
228 if (isvalid != NULL) {
229 isvalid += strlen(a) + 1;
230 cookiebuf = apr_pstrdup(r->pool, isvalid);
231 cookieend = ap_strchr(cookiebuf, ';');
232 if (cookieend != NULL)
233 *cookieend = '\0';
234 return cookiebuf;
235 }
236 }
237 }
238
239 return "-";
240}
241
242/*static const char *extract_cookie(request_rec *r, char *a)
243{
244 logsql_state *cls = ap_get_module_config(r->server->module_config,
245 &log_sql_module);
246
247 return extract_specific_cookie(r, (char *)cls->cookie_name);
248}*/
249
250static const char *extract_unique_id(request_rec *r, char *a)
251{
252 const char *tempid;
253
254 tempid = apr_table_get(r->subprocess_env, "UNIQUE_ID");
255 if (!tempid)
256 return "-";
257 else
258 return tempid;
259}
260
261/* End declarations of various extract_ functions */
diff --git a/src/functions13.h b/src/functions13.h
new file mode 100644
index 0000000..54e3138
--- /dev/null
+++ b/src/functions13.h
@@ -0,0 +1,58 @@
1/* $Id$ */
2
3static const char *extract_bytes_sent(request_rec *r, char *a)
4{
5 if (!r->sent_bodyct) {
6 return "-";
7 }
8 else {
9 long int bs;
10 ap_bgetopt(r->connection->client, BO_BYTECT, &bs);
11 return ap_psprintf(r->pool, "%ld", bs);
12 }
13}
14
15static const char *extract_request_time(request_rec *r, char *a)
16{
17 int timz;
18 struct tm *t;
19 char tstr[MAX_STRING_LEN];
20
21 t = ap_get_gmtoff(&timz);
22
23 if (a && *a) { /* Custom format */
24 strftime(tstr, MAX_STRING_LEN, a, t);
25 } else { /* CLF format */
26 char sign = (timz < 0 ? '-' : '+');
27
28 if (timz < 0) {
29 timz = -timz;
30 }
31 strftime(tstr, MAX_STRING_LEN, "[%d/%b/%Y:%H:%M:%S ", t);
32 ap_snprintf(tstr + strlen(tstr), sizeof(tstr) - strlen(tstr), "%c%.2d%.2d]", sign, timz / 60, timz % 60);
33 }
34
35 return ap_pstrdup(r->pool, tstr);
36}
37
38static const char *extract_request_duration(request_rec *r, char *a)
39{
40 return ap_psprintf(r->pool, "%ld", time(NULL) - r->request_time);
41}
42
43static const char *extract_request_timestamp(request_rec *r, char *a)
44{
45 return ap_psprintf(r->pool, "%ld", (long) time(NULL));
46}
47static const char *extract_connection_status(request_rec *r, char *a) __attribute__((unused));
48static const char *extract_connection_status(request_rec *r, char *a)
49{
50 if (r->connection->aborted)
51 return "X";
52
53 if ((r->connection->keepalive) &&
54 ((r->server->keep_alive_max - r->connection->keepalives) > 0)) {
55 return "+";
56 }
57 return "-";
58}
diff --git a/src/functions20.h b/src/functions20.h
new file mode 100644
index 0000000..5220b7c
--- /dev/null
+++ b/src/functions20.h
@@ -0,0 +1,116 @@
1/* $Id$ */
2
3static const char *extract_bytes_sent(request_rec *r, char *a)
4{
5 if (!r->sent_bodyct || !r->bytes_sent) {
6 return "-";
7 } else {
8 return apr_psprintf(r->pool, "%" APR_OFF_T_FMT, r->bytes_sent);
9 }
10}
11
12static const char *extract_request_time_custom(request_rec *r, char *a,
13 apr_time_exp_t *xt)
14{
15 apr_size_t retcode;
16 char tstr[MAX_STRING_LEN];
17 apr_strftime(tstr, &retcode, sizeof(tstr), a, xt);
18 return apr_pstrdup(r->pool, tstr);
19}
20
21#define DEFAULT_REQUEST_TIME_SIZE 32
22typedef struct {
23 unsigned t;
24 char timestr[DEFAULT_REQUEST_TIME_SIZE];
25 unsigned t_validate;
26} cached_request_time;
27
28#define TIME_CACHE_SIZE 4
29#define TIME_CACHE_MASK 3
30static cached_request_time request_time_cache[TIME_CACHE_SIZE];
31
32static const char *extract_request_time(request_rec *r, char *a)
33{
34 apr_time_exp_t xt;
35
36 /* Please read comments in mod_log_config.h for more info about
37 * the I_INSIST....COMPLIANCE define
38 */
39 if (a && *a) { /* Custom format */
40#ifdef I_INSIST_ON_EXTRA_CYCLES_FOR_CLF_COMPLIANCE
41 ap_explode_recent_localtime(&xt, apr_time_now());
42#else
43 ap_explode_recent_localtime(&xt, r->request_time);
44#endif
45 return extract_request_time_custom(r, a, &xt);
46 } else { /* CLF format */
47 /* This code uses the same technique as ap_explode_recent_localtime():
48 * optimistic caching with logic to detect and correct race conditions.
49 * See the comments in server/util_time.c for more information.
50 */
51 cached_request_time* cached_time = apr_palloc(r->pool,
52 sizeof(*cached_time));
53#ifdef I_INSIST_ON_EXTRA_CYCLES_FOR_CLF_COMPLIANCE
54 apr_time_t request_time = apr_time_now();
55#else
56 apr_time_t request_time = r->request_time;
57#endif
58 unsigned t_seconds = (unsigned)apr_time_sec(request_time);
59 unsigned i = t_seconds & TIME_CACHE_MASK;
60 memcpy(cached_time, &(request_time_cache[i]), sizeof(*cached_time));
61 if ((t_seconds != cached_time->t) ||
62 (t_seconds != cached_time->t_validate)) {
63
64 /* Invalid or old snapshot, so compute the proper time string
65 * and store it in the cache
66 */
67 char sign;
68 int timz;
69
70 ap_explode_recent_localtime(&xt, r->request_time);
71 timz = xt.tm_gmtoff;
72 if (timz < 0) {
73 timz = -timz;
74 sign = '-';
75 }
76 else {
77 sign = '+';
78 }
79 cached_time->t = t_seconds;
80 apr_snprintf(cached_time->timestr, DEFAULT_REQUEST_TIME_SIZE,
81 "[%02d/%s/%d:%02d:%02d:%02d %c%.2d%.2d]",
82 xt.tm_mday, apr_month_snames[xt.tm_mon],
83 xt.tm_year+1900, xt.tm_hour, xt.tm_min, xt.tm_sec,
84 sign, timz / (60*60), timz % (60*60));
85 cached_time->t_validate = t_seconds;
86 memcpy(&(request_time_cache[i]), cached_time,
87 sizeof(*cached_time));
88 }
89 return cached_time->timestr;
90 }
91}
92
93static const char *extract_request_duration(request_rec *r, char *a)
94{
95 apr_time_t duration = apr_time_now() - r->request_time;
96 return apr_psprintf(r->pool, "%" APR_TIME_T_FMT, apr_time_sec(duration));
97}
98
99static const char *extract_request_timestamp(request_rec *r, char *a)
100{
101 return apr_psprintf(r->pool, "%"APR_TIME_T_FMT, apr_time_sec(apr_time_now()));
102}
103
104static const char *extract_connection_status(request_rec *r, char *a) __attribute__((unused));
105static const char *extract_connection_status(request_rec *r, char *a)
106{
107 if (r->connection->aborted)
108 return "X";
109
110 if (r->connection->keepalive == AP_CONN_KEEPALIVE &&
111 (!r->server->keep_alive_max ||
112 (r->server->keep_alive_max - r->connection->keepalives) > 0)) {
113 return "+";
114 }
115 return "-";
116}
diff --git a/src/mod_log_sql.c b/src/mod_log_sql.c
new file mode 100644
index 0000000..be4e1f9
--- /dev/null
+++ b/src/mod_log_sql.c
@@ -0,0 +1,1578 @@
1/* $Id:mod_log_sql.c 180 2008-09-21 15:54:12Z urkle@drip.ws $ */
2
3#if defined(WITH_APACHE20)
4# include "apache20.h"
5#elif defined(WITH_APACHE13)
6# include "apache13.h"
7#else
8# error Unsupported Apache version
9#endif
10
11#ifdef HAVE_CONFIG_H
12/* Undefine these to prevent conflicts between Apache ap_config_auto.h and
13 * my config.h. Only really needed for Apache < 2.0.48, but it can't hurt.
14 */
15#undef PACKAGE_BUGREPORT
16#undef PACKAGE_NAME
17#undef PACKAGE_STRING
18#undef PACKAGE_TARNAME
19#undef PACKAGE_VERSION
20
21#include "autoconfig.h"
22#endif
23
24#if APR_HAVE_UNISTD_H
25#include <unistd.h>
26#endif
27#ifdef HAVE_LIMITS_H
28#include <limits.h>
29#endif
30
31#include "mod_log_sql.h"
32
33/* Configuratino Defaults */
34#define DEFAULT_TRANSFER_LOG_FMT "AbHhmRSsTUuv"
35#define DEFAULT_NOTES_TABLE_NAME "notes"
36#define DEFAULT_HIN_TABLE_NAME "headers_in"
37#define DEFAULT_HOUT_TABLE_NAME "headers_out"
38#define DEFAULT_COOKIE_TABLE_NAME "cookies"
39#define DEFAULT_PRESERVE_FILE "logs/mod_log_sql-preserve"
40
41/* -------------*
42 * DECLARATIONS *
43 * -------------*/
44
45/* Declare ourselves so the configuration routines can find and know us. */
46module AP_MODULE_DECLARE_DATA log_sql_module;
47
48/* The contents of these are known 'Apache wide' and are not variable
49 * on a per-virtual-server basis. Every virtual server 'knows' the
50 * same versions of these variables.
51 */
52
53typedef struct {
54 int massvirtual;
55 int createtables;
56 int forcepreserve;
57 int disablepreserve;
58 char *machid;
59 int announce;
60 logsql_dbconnection db;
61 logsql_dbdriver *driver;
62 /** Show config support */
63 char *showconfig;
64 apr_file_t *showconfig_fp;
65} global_config_t;
66
67static global_config_t global_config;
68
69/* structure to hold helper function info */
70typedef struct {
71 const char *alias; /* The function alias */
72 logsql_item_func *func; /* The extraction function pointer */
73 int want_orig_req; /* if it requires the original request prior to internal redirection */
74} logsql_function;
75
76/* list of logsql_functions's for log types */
77static apr_array_header_t *logsql_function_list;
78
79/* structure to hold sqlfield mappings */
80typedef struct {
81 const char *alias; /* long name for item */
82 const char *funcalias; /* The function alias */
83 logsql_function *func; /* its extraction function */
84 char *param; /* Parameter for function */
85 const char *sql_field_name; /* its column in SQL */
86 char string_contents; /* Whether this is a string field or not */
87 logsql_field_datatype datatype; /* the field data type */
88 apr_size_t size; /* The size of the data type */
89} logsql_field;
90
91/* list of logsql_item's for log types */
92static apr_array_header_t *logsql_field_list;
93
94/* But the contents of this structure will vary by virtual server.
95 * This permits each virtual server to vary its configuration slightly
96 * for per-server customization.
97 *
98 * Each child process has its own segregated copy of this structure.
99 */
100typedef struct {
101 apr_array_header_t *transfer_ignore_list;
102 apr_array_header_t *transfer_accept_list;
103 apr_array_header_t *remhost_ignore_list;
104 apr_array_header_t *notes_list;
105 apr_array_header_t *hout_list;
106 apr_array_header_t *hin_list;
107 apr_array_header_t *cookie_list;
108 const char *notes_table_name;
109 const char *hout_table_name;
110 const char *hin_table_name;
111 const char *cookie_table_name;
112 const char *transfer_table_name;
113 apr_array_header_t *transfer_log_format;
114 apr_pool_t *parsed_pool;
115 logsql_field **parsed_log_format;
116 const char *preserve_file;
117 const char *cookie_name;
118} logsql_state;
119
120/** Registration function for extract functions
121 *
122 * This functions registers an alias for a function
123 *
124 * @note This is exported from the module
125 */
126LOGSQL_DECLARE(void) log_sql_register_function(apr_pool_t *p,
127 const char *alias, logsql_item_func *func,
128 logsql_function_req want_orig_req)
129{
130 logsql_function *item;
131 if (!logsql_function_list)
132 logsql_function_list = apr_array_make(p,10, sizeof(logsql_function));
133
134 item = apr_array_push(logsql_function_list);
135 item->alias = alias;
136 item->func = func;
137 item->want_orig_req = want_orig_req;
138 if (global_config.showconfig_fp) {
139 apr_file_printf(global_config.showconfig_fp," Function : %s\n",alias);
140 }
141}
142/** Register a old style sql mapping to the new style
143 *
144 * @note This is exported from the module
145 */
146LOGSQL_DECLARE(void) log_sql_register_alias(server_rec *s, apr_pool_t *p,
147 char key, const char *alias)
148{
149 server_rec *ts;
150 for (ts = s; ts; ts = ts->next) {
151 logsql_state *cfg = ap_get_module_config(ts->module_config,
152 &log_sql_module);
153 int itr;
154 for (itr = 0; itr < cfg->transfer_log_format->nelts; itr++) {
155 const char *logformat = ((const char **)cfg->transfer_log_format->elts)[itr];
156 //log_error(APLOG_MARK, APLOG_DEBUG, 0, s, "Testing Logformat %s against %c for %s",logformat,key,alias);
157 // Check if it is only one character AND it is our key
158 if (logformat[1]=='\0' && logformat[0]==key) {
159 ((const char **)cfg->transfer_log_format->elts)[itr] = alias;
160 }
161 }
162 }
163}
164
165
166/** Registration sqlfield aliases to functions
167 *
168 * And update parse cache for transfer_log_format
169 *
170 * @note This is exported from the module
171 */
172LOGSQL_DECLARE(void) log_sql_register_field(apr_pool_t *p,
173 const char *alias,
174 const char *funcalias, const char *param,
175 const char *sql_field_name,
176 logsql_field_datatype datatype, apr_size_t size)
177{
178 logsql_field *item;
179
180 if (!logsql_field_list)
181 logsql_field_list = apr_array_make(p,10, sizeof(logsql_field));
182
183 item = apr_array_push(logsql_field_list);
184 item->func = NULL;
185 item->alias = apr_pstrdup(p, alias);
186 item->funcalias = apr_pstrdup(p, funcalias);
187 item->param = apr_pstrdup(p, param);
188 item->sql_field_name = apr_pstrdup(p,sql_field_name);
189 item->datatype = datatype;
190 item->string_contents = 0;
191 if (datatype == LOGSQL_DATATYPE_CHAR || datatype == LOGSQL_DATATYPE_VARCHAR) {
192 item->string_contents = 1;
193 }
194 item->size = size;
195}
196
197/**
198 * Links sql field items with their functions
199 */
200LOGSQL_DECLARE(void) log_sql_register_finish(server_rec *s)
201{
202 server_rec *ts;
203 int itr, f;
204 logsql_field *item;
205 logsql_function *func;
206 for (itr = 0; itr < logsql_field_list->nelts; itr++) {
207 item = &((logsql_field *)logsql_field_list->elts)[itr];
208 if (item->func) continue;
209 /* Find function alias in function list */
210 for (f = 0; f < logsql_function_list->nelts; f++) {
211 func = &((logsql_function *)logsql_function_list->elts)[f];
212 if (strcmp(func->alias,item->funcalias)==0) {
213 item->func = func;
214 if (global_config.showconfig_fp) {
215 apr_file_printf(global_config.showconfig_fp," Item : %s using function %s(%s)\n"
216 "\tStoring in field %s of type %s(%"APR_SIZE_T_FMT")\n",
217 item->alias, item->funcalias, item->param,
218 item->sql_field_name, item->string_contents ? "TEXT":"NUMERIC", item->size);
219 }
220 break;
221 }
222 }
223 if (!item->func) {
224 log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
225 "Could not find function %s for item %s",item->funcalias, item->alias);
226 }
227 }
228 /* some voodoo here to post parse logitems in all servers *
229 * so a "cached" list is used in the main logging loop for speed */
230 for (ts = s; ts; ts = ts->next) {
231 logsql_state *cfg = ap_get_module_config(ts->module_config,
232 &log_sql_module);
233
234 if (!cfg->parsed_log_format) {
235 cfg->parsed_log_format = apr_pcalloc(cfg->parsed_pool,
236 cfg->transfer_log_format->nelts * sizeof(logsql_field *));
237 }
238
239 for (itr = 0; itr < cfg->transfer_log_format->nelts; itr++) {
240 const char *logformat = ((char **)cfg->transfer_log_format->elts)[itr];
241 for (f = 0; f < logsql_field_list->nelts; f++) {
242 item = &((logsql_field *)logsql_field_list->elts)[f];
243 if (item->func && strcmp(logformat,item->alias)==0) {
244 cfg->parsed_log_format[itr] = item;
245 break;
246 }
247 }
248 }
249 }
250}
251
252/* Registration function for database drivers */
253LOGSQL_DECLARE(void) log_sql_register_driver(apr_pool_t *p,
254 logsql_dbdriver *driver)
255{
256 global_config.driver = driver;
257}
258
259/* Include all the core extract functions */
260#include "functions.h"
261#if defined(WITH_APACHE13)
262# include "functions13.h"
263#elif defined(WITH_APACHE20)
264# include "functions20.h"
265#endif
266
267static logsql_opendb_ret log_sql_opendb_link(server_rec* s)
268{
269 logsql_opendb_ret result;
270 if (global_config.driver == NULL) {
271 return LOGSQL_OPENDB_FAIL;
272 }
273 if (global_config.forcepreserve) {
274 /*global_config.db.connected = 1;*/
275 return LOGSQL_OPENDB_PRESERVE;
276 }
277 if (global_config.db.connected) {
278 return LOGSQL_OPENDB_ALREADY;
279 }
280 /* database
281 host
282 user
283 passwd
284 */
285 if (global_config.db.parms) {
286 result = global_config.driver->connect(s, &global_config.db);
287 if (result==LOGSQL_OPENDB_FAIL) {
288 global_config.db.connected = 0;
289 } else {
290 global_config.db.connected = 1;
291 }
292 return result;
293 } else {
294 log_error(APLOG_MARK, APLOG_ERR, 0, s,
295 "mod_log_sql: insufficient configuration info to establish database link");
296 return LOGSQL_OPENDB_FAIL;
297 }
298}
299
300static void preserve_entry(request_rec *r, const char *query)
301{
302 logsql_state *cls = ap_get_module_config(r->server->module_config,
303 &log_sql_module);
304 apr_status_t result;
305 apr_file_t *fp;
306
307 /* If preserve file is disabled bail out */
308 if (global_config.disablepreserve)
309 return;
310 #if defined(WITH_APACHE20)
311 result = apr_file_open(&fp, cls->preserve_file,APR_APPEND | APR_WRITE | APR_CREATE, APR_OS_DEFAULT, r->pool);
312 #elif defined(WITH_APACHE13)
313 fp = ap_pfopen(r->pool, cls->preserve_file, "a");
314 result = (fp)?0:errno;
315 #endif
316 if (result != APR_SUCCESS) {
317 log_error(APLOG_MARK, APLOG_ERR, result, r->server,
318 "attempted append of local preserve file '%s' but failed.",cls->preserve_file);
319 } else {
320 apr_file_printf(fp,"%s;\n", query);
321 #if defined(WITH_APACHE20)
322 apr_file_close(fp);
323 #elif defined(WITH_APACHE13)
324 ap_pfclose(r->pool, fp);
325 #endif
326 log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
327 "mod_log_sql: entry preserved in %s", cls->preserve_file);
328 }
329}
330
331
332/* ------------------------------------------------*
333 * Command handlers that are called according *
334 * to the directives found at Apache runtime. *
335 * ------------------------------------------------*/
336
337
338static const char *set_global_flag_slot(cmd_parms *cmd,
339 void *struct_ptr,
340 int flag)
341{
342 void *ptr = &global_config;
343 int offset = (int)(long)cmd->info;
344
345 *(int *)((char *)ptr + offset) = flag ? 1 : 0;
346
347 return NULL;
348}
349
350static const char *set_global_nmv_flag_slot(cmd_parms *cmd,
351 void *struct_ptr,
352 int flag)
353{
354 if (global_config.massvirtual) {
355 return apr_psprintf(cmd->pool,
356 "mod_log_sql: do not set %s when LogSQLMassVirtualHosting(%d) is On.%d:%d",
357 cmd->cmd->name, global_config.massvirtual,
358 (int)(long)&global_config, (int)(long)struct_ptr);
359 } else {
360 return set_global_flag_slot(cmd,struct_ptr,flag);
361 }
362}
363
364static const char *set_global_string_slot(cmd_parms *cmd,
365 void *struct_ptr,
366 const char *arg)
367{
368 void *ptr = &global_config;
369 int offset = (int)(long)cmd->info;
370
371 *(const char **)((char *)ptr + offset) = apr_pstrdup(cmd->pool,arg);
372 return NULL;
373}
374
375static const char *set_server_string_slot(cmd_parms *cmd,
376 void *struct_ptr,
377 const char *arg)
378{
379 void *ptr = ap_get_module_config(cmd->server->module_config,
380 &log_sql_module);
381 int offset = (int)(long)cmd->info;
382
383 *(const char **)((char *)ptr + offset) = arg;
384
385 return NULL;
386}
387
388static const char *set_server_file_slot(cmd_parms *cmd,
389 void *struct_ptr,
390 const char *arg)
391{
392 void *ptr = ap_get_module_config(cmd->server->module_config,
393 &log_sql_module);
394 int offset = (int)(long)cmd->info;
395 const char *path;
396
397 path = ap_server_root_relative(cmd->pool, (char *)arg);
398
399 if (!path) {
400 return apr_pstrcat(cmd->pool, "Invalid file path ",
401 arg, NULL);
402 }
403
404 *(const char **)((char*)ptr + offset) = path;
405
406 return NULL;
407}
408
409static apr_array_header_t *create_logformat_default(apr_pool_t *p)
410{
411 apr_array_header_t *logformat;
412 char **addme;
413
414 logformat = apr_array_make(p, 12, sizeof(char *));
415 addme = apr_array_push(logformat); *addme = "useragent";
416 addme = apr_array_push(logformat); *addme = "bytes_sent";
417 addme = apr_array_push(logformat); *addme = "request_protocol";
418 addme = apr_array_push(logformat); *addme = "remote_host";
419 addme = apr_array_push(logformat); *addme = "request_method";
420 addme = apr_array_push(logformat); *addme = "referer";
421 addme = apr_array_push(logformat); *addme = "timestamp";
422 addme = apr_array_push(logformat); *addme = "status";
423 addme = apr_array_push(logformat); *addme = "request_duration";
424 addme = apr_array_push(logformat); *addme = "request_uri";
425 addme = apr_array_push(logformat); *addme = "remote_user";
426 addme = apr_array_push(logformat); *addme = "virtual_host";
427 return logformat;
428}
429
430static const char *set_server_nmv_string_slot(cmd_parms *parms,
431 void *struct_ptr,
432 const char *arg)
433{
434 if (global_config.massvirtual)
435 return apr_psprintf(parms->pool,
436 "mod_log_sql: do not set %s when LogSQLMassVirtualHosting is On.",
437 parms->cmd->name);
438 else
439 return set_server_string_slot(parms,struct_ptr,arg);
440}
441
442/* Set a DB connection parameter */
443static const char *set_dbparam(cmd_parms *cmd,
444 void *struct_ptr,
445 const char *key,
446 const char *val)
447{
448 if (!global_config.db.parms) {
449 global_config.db.parms = apr_table_make(cmd->pool,5);
450 }
451 apr_table_set(global_config.db.parms,key,val);
452 return NULL;
453}
454
455static const char *set_dbparam_slot(cmd_parms *cmd,
456 void *struct_ptr,
457 const char *arg)
458{
459 const char *param = (char *)cmd->info;
460 set_dbparam(cmd,NULL,param,arg);
461 return NULL;
462}
463
464/* Sets basic connection info */
465static const char *set_log_sql_info(cmd_parms *cmd, void *dummy,
466 const char *host, const char *user, const char *pwd)
467{
468 if (!user) { /* user is null, so only one arg passed */
469 /* TODO: to more error checking/force all params to be set */
470 apr_uri_t uri;
471 apr_uri_parse(cmd->pool, host, &uri);
472 if (uri.scheme) {
473 set_dbparam(cmd, NULL, "driver", uri.scheme);
474 }
475 if (uri.hostname) {
476 set_dbparam(cmd, NULL, "hostname", uri.hostname);
477 }
478 if (uri.user) {
479 set_dbparam(cmd, NULL, "username", uri.user);
480 }
481 if (uri.password) {
482 set_dbparam(cmd, NULL, "password", uri.password);
483 }
484 if (uri.port_str) {
485 set_dbparam(cmd, NULL, "port", uri.port_str);
486 }
487 if (uri.path) {
488 /* extract Database name */
489 char *off = ap_strchr(++uri.path,'/');
490 if (off)
491 *off='\0';
492 set_dbparam(cmd, NULL, "database", uri.path);
493
494 }
495 } else {
496 if (*host != '.') {
497 set_dbparam(cmd, NULL, "hostname", host);
498 }
499 if (*user != '.') {
500 set_dbparam(cmd, NULL, "username", user);
501 }
502 if (*pwd != '.') {
503 set_dbparam(cmd, NULL, "password", pwd);
504 }
505 }
506 return NULL;
507}
508
509static const char *add_server_string_slot(cmd_parms *cmd,
510 void *struct_ptr,
511 const char *arg)
512{
513 char **addme;
514 void *ptr = ap_get_module_config(cmd->server->module_config,
515 &log_sql_module);
516 int offset = (int)(long)cmd->info;
517 apr_array_header_t *ary = *(apr_array_header_t **)((char *)ptr + offset);
518 addme = apr_array_push(ary);
519 *addme = apr_pstrdup(ary->pool, arg);
520
521 return NULL;
522}
523
524static const char *set_logformat_slot(cmd_parms *cmd,
525 void *struct_ptr,
526 const char *arg)
527{
528 const char *t;
529 char t2[2] = {'\0','\0'};
530 for (t = arg; *t != '\0'; t++) {
531 t2[0] = *t;
532 add_server_string_slot(cmd, NULL, t2);
533 }
534 return NULL;
535}
536
537static const char *set_register_field(cmd_parms *cmd,
538 void *struct_ptr,
539 const char *arg)
540{
541 char *alias, *funcalias, *param, *field, *datatype_s, *size_s;
542 logsql_field_datatype datatype;
543 apr_size_t size;
544
545 alias = ap_getword_white(cmd->pool, &arg);
546 funcalias = ap_getword_white(cmd->pool, &arg);
547 param = ap_getword_conf(cmd->pool, &arg);
548 field = ap_getword_white(cmd->pool, &arg);
549 datatype_s = ap_getword_white(cmd->pool, &arg);
550 size_s = ap_getword_white(cmd->pool, &arg);
551
552 if (strcasecmp("VARCHAR",datatype_s)==0) {
553 datatype = LOGSQL_DATATYPE_VARCHAR;
554 } else if (strcasecmp("INT",datatype_s)==0) {
555 datatype = LOGSQL_DATATYPE_INT;
556 } else if (strcasecmp("CHAR",datatype_s)==0) {
557 datatype = LOGSQL_DATATYPE_CHAR;
558 } else if (strcasecmp("SMALLINT",datatype_s)==0) {
559 datatype = LOGSQL_DATATYPE_SMALLINT;
560 } else if (strcasecmp("BIGINT",datatype_s)==0) {
561 datatype = LOGSQL_DATATYPE_BIGINT;
562 } else {
563 return apr_psprintf(cmd->pool, "Unknown data type %s",datatype_s);
564 }
565
566 size = atoi(size_s);
567
568 log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server,
569 "%s, %s, %s, %s, %s(%d), %s(%"APR_SIZE_T_FMT")",
570 alias, funcalias, param, field, datatype_s, datatype, size_s, size);
571
572 log_sql_register_field(cmd->pool, alias, funcalias, param,
573 field, datatype, size);
574
575 return NULL;
576}
577
578/*------------------------------------------------------------*
579 * Apache-specific hooks into the module code *
580 * that are defined in the array 'mysql_lgog_module' (at EOF) *
581 *------------------------------------------------------------*/
582/* Closing mysql link: child_exit(1.3), pool registration(2.0) */
583#if defined(WITH_APACHE20)
584static apr_status_t log_sql_close_link(void *data)
585{
586 if (global_config.driver)
587 global_config.driver->disconnect(&global_config.db);
588 return APR_SUCCESS;
589}
590#elif defined(WITH_APACHE13)
591static void log_sql_child_exit(server_rec *s, apr_pool_t *p)
592{
593 if (global_config.driver)
594 global_config.driver->disconnect(&global_config.db);
595}
596#endif
597
598/* Child Init */
599#if defined(WITH_APACHE20)
600static void log_sql_child_init(apr_pool_t *p, server_rec *s)
601#elif defined(WITH_APACHE13)
602static void log_sql_child_init(server_rec *s, apr_pool_t *p)
603#endif
604{
605 logsql_opendb_ret retval;
606# if defined(WITH_APACHE20)
607 /* Register cleanup hook to close DDB connection (apache 2 doesn't have child_exit) */
608 apr_pool_cleanup_register(p, NULL, log_sql_close_link, log_sql_close_link);
609# endif
610 /* Open a link to the database */
611 retval = log_sql_opendb_link(s);
612 switch (retval) {
613 case LOGSQL_OPENDB_FAIL:
614 if (global_config.driver==NULL) {
615 log_error(APLOG_MARK, APLOG_ERR, 0, s,
616 "mod_log_sql: Driver module not loaded");
617 } else {
618 log_error(APLOG_MARK, APLOG_ERR, 0, s,
619 "mod_log_sql: child spawned but unable to open database link");
620 }
621 break;
622 case LOGSQL_OPENDB_SUCCESS:
623 case LOGSQL_OPENDB_ALREADY:
624 log_error(APLOG_MARK,APLOG_DEBUG,0, s,
625 "mod_log_sql: open_logdb_link successful");
626 break;
627 case LOGSQL_OPENDB_PRESERVE:
628 log_error(APLOG_MARK,APLOG_DEBUG, 0, s,
629 "mod_log_sql: open_logdb_link said that preservation is forced");
630 break;
631 }
632}
633
634static apr_array_header_t *do_merge_array(apr_array_header_t *parent, apr_array_header_t *child, apr_pool_t *p);
635
636/* post_config / module_init */
637#if defined(WITH_APACHE20)
638static int log_sql_post_config(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s)
639#elif defined(WITH_APACHE13)
640static void log_sql_module_init(server_rec *s, apr_pool_t *p)
641#endif
642{
643 server_rec *cur_s;
644 const char *default_p = ap_server_root_relative(p, DEFAULT_PRESERVE_FILE);
645 apr_array_header_t *parent = NULL;
646
647 if (global_config.showconfig != NULL) {
648 const char *tempfile = ap_server_root_relative(p, global_config.showconfig);
649 apr_status_t result;
650 #if defined(WITH_APACHE20)
651 result = apr_file_open(&global_config.showconfig_fp, tempfile,APR_TRUNCATE | APR_WRITE | APR_CREATE, APR_OS_DEFAULT, p);
652 #elif defined(WITH_APACHE13)
653 global_config.showconfig_fp = ap_pfopen(p, tempfile, "w");
654 result = (fp)?0:errno;
655 #endif
656 if (result != APR_SUCCESS) {
657 log_error(APLOG_MARK, APLOG_ERR, result, s,
658 "attempted open of showconfig file '%s' failed.",tempfile);
659 global_config.showconfig_fp = NULL;
660 } else {
661 #if defined(WITH_APACHE20)
662 char temp_time[APR_RFC822_DATE_LEN];
663 apr_rfc822_date(temp_time,apr_time_now());
664 #elif defined(WITH_APACHE13)
665 char *temp_time = ap_get_time());
666 #endif
667 apr_file_printf(global_config.showconfig_fp,"Mod_log_sql Config dump created on %s\n", temp_time);
668 }
669 }
670
671 for (cur_s = s; cur_s != NULL; cur_s= cur_s->next) {
672 logsql_state *cls = ap_get_module_config(cur_s->module_config,
673 &log_sql_module);
674 /* ap_server_root_relative any default preserve file locations */
675 if (cls->preserve_file == DEFAULT_PRESERVE_FILE)
676 cls->preserve_file = default_p;
677
678 /* Post-process logformats */
679 if (!cur_s->is_virtual) {
680 parent = create_logformat_default(p);
681 cls->transfer_log_format = do_merge_array(parent, cls->transfer_log_format, p);
682 parent = cls->transfer_log_format;
683 } else {
684 cls->transfer_log_format = do_merge_array(parent, cls->transfer_log_format, p);
685 }
686 }
687
688 /* TODO: Add local_address, remote_address, connection_status */
689 /** Register functions */
690 /** register_function(p, funcname, func_ptr, which request_rec); */
691 log_sql_register_function(p, "useragent", extract_agent, LOGSQL_FUNCTION_REQ_ORIG);
692 log_sql_register_function(p, "request_args", extract_request_query, LOGSQL_FUNCTION_REQ_ORIG);
693 log_sql_register_function(p, "bytes_sent", extract_bytes_sent, LOGSQL_FUNCTION_REQ_FINAL);
694 log_sql_register_function(p, "cookie", extract_specific_cookie,LOGSQL_FUNCTION_REQ_FINAL);
695 log_sql_register_function(p, "request_file", extract_request_file, LOGSQL_FUNCTION_REQ_FINAL);
696 log_sql_register_function(p, "request_protocol",extract_request_protocol,LOGSQL_FUNCTION_REQ_FINAL);
697 log_sql_register_function(p, "remote_host", extract_remote_host, LOGSQL_FUNCTION_REQ_FINAL);
698 log_sql_register_function(p, "unique_id", extract_unique_id, LOGSQL_FUNCTION_REQ_FINAL);
699 log_sql_register_function(p, "remote_logname", extract_remote_logname, LOGSQL_FUNCTION_REQ_FINAL);
700 log_sql_register_function(p, "request_method", extract_request_method, LOGSQL_FUNCTION_REQ_FINAL);
701 log_sql_register_function(p, "machine_id", extract_machine_id, LOGSQL_FUNCTION_REQ_FINAL);
702 log_sql_register_function(p, "child_pid", extract_child_pid, LOGSQL_FUNCTION_REQ_FINAL);
703 log_sql_register_function(p, "server_port", extract_server_port, LOGSQL_FUNCTION_REQ_FINAL);
704 log_sql_register_function(p, "referrer", extract_referer, LOGSQL_FUNCTION_REQ_ORIG);
705 log_sql_register_function(p, "request_line", extract_request_line, LOGSQL_FUNCTION_REQ_ORIG);
706 log_sql_register_function(p, "timestamp", extract_request_timestamp,LOGSQL_FUNCTION_REQ_FINAL);
707 log_sql_register_function(p, "status", extract_status, LOGSQL_FUNCTION_REQ_ORIG);
708 log_sql_register_function(p, "request_duration",extract_request_duration,LOGSQL_FUNCTION_REQ_ORIG);
709 log_sql_register_function(p, "request_time", extract_request_time, LOGSQL_FUNCTION_REQ_FINAL);
710 log_sql_register_function(p, "remote_user", extract_remote_user, LOGSQL_FUNCTION_REQ_FINAL);
711 log_sql_register_function(p, "request_uri", extract_request_uri, LOGSQL_FUNCTION_REQ_ORIG);
712 log_sql_register_function(p, "virtual_host", extract_virtual_host, LOGSQL_FUNCTION_REQ_FINAL);
713 log_sql_register_function(p, "server_name", extract_server_name, LOGSQL_FUNCTION_REQ_FINAL);
714
715 /** Old style aliases */
716 /** register_alias(s, shortname, longname) */
717 log_sql_register_alias(s,p,'A',"useragent");
718 log_sql_register_alias(s,p,'a',"request_args");
719 log_sql_register_alias(s,p,'b',"bytes_sent");
720 log_sql_register_alias(s,p,'c',"cookie");
721 log_sql_register_alias(s,p,'f',"request_file");
722 log_sql_register_alias(s,p,'H',"request_protocol");
723 log_sql_register_alias(s,p,'h',"remote_host");
724 log_sql_register_alias(s,p,'I',"unique_id");
725 log_sql_register_alias(s,p,'l',"remote_logname");
726 log_sql_register_alias(s,p,'m',"request_method");
727 log_sql_register_alias(s,p,'M',"machine_id");
728 log_sql_register_alias(s,p,'P',"child_pid");
729 log_sql_register_alias(s,p,'p',"server_port");
730 log_sql_register_alias(s,p,'R',"referrer");
731 log_sql_register_alias(s,p,'r',"request_line");
732 log_sql_register_alias(s,p,'S',"timestamp");
733 log_sql_register_alias(s,p,'s',"status");
734 log_sql_register_alias(s,p,'T',"request_duration");
735 log_sql_register_alias(s,p,'t',"request_time");
736 log_sql_register_alias(s,p,'u',"remote_user");
737 log_sql_register_alias(s,p,'U',"request_uri");
738 log_sql_register_alias(s,p,'v',"virtual_host");
739 log_sql_register_alias(s,p,'V',"server_name");
740
741 /* Register handlers */
742 /** register_field(s,p, longname, funcalias, arg,
743 * sqlfieldname, DATATYPE, DATA LENGTH); */
744 log_sql_register_field(p,"useragent", "useragent",NULL,
745 "agent", LOGSQL_DATATYPE_VARCHAR, 0);
746 log_sql_register_field(p,"request_args", "request_args",NULL,
747 "request_args",LOGSQL_DATATYPE_VARCHAR,0);
748 log_sql_register_field(p,"bytes_sent", "bytes_sent",NULL,
749 "bytes_sent",LOGSQL_DATATYPE_INT,0);
750 log_sql_register_field(p,"cookie", "cookie","Apache",
751 "cookie",LOGSQL_DATATYPE_VARCHAR,0);
752 log_sql_register_field(p,"request_file", "request_file",NULL,
753 "request_file",LOGSQL_DATATYPE_VARCHAR,0);
754 log_sql_register_field(p,"request_protocol", "request_protocol",NULL,
755 "request_protocol",LOGSQL_DATATYPE_VARCHAR,0);
756 log_sql_register_field(p,"remote_host", "remote_host",NULL,
757 "remote_host",LOGSQL_DATATYPE_VARCHAR,0);
758 log_sql_register_field(p,"unique_id", "unique_id",NULL,
759 "id",LOGSQL_DATATYPE_VARCHAR,0);
760 log_sql_register_field(p,"remote_logname", "remote_logname",NULL,
761 "remote_logname",LOGSQL_DATATYPE_VARCHAR,0);
762 log_sql_register_field(p,"request_method", "request_method",NULL,
763 "request_method",LOGSQL_DATATYPE_VARCHAR,0);
764 log_sql_register_field(p,"machine_id", "machine_id",NULL,
765 "machine_id",LOGSQL_DATATYPE_VARCHAR,0);
766 log_sql_register_field(p,"child_pid", "child_pid",NULL,
767 "child_pid",LOGSQL_DATATYPE_INT,0);
768 log_sql_register_field(p,"server_port", "server_port",NULL,
769 "server_port",LOGSQL_DATATYPE_INT,0);
770 log_sql_register_field(p,"referer", "referrer",NULL,
771 "referer",LOGSQL_DATATYPE_VARCHAR,0);
772 log_sql_register_field(p,"referrer", "referrer",NULL,
773 "referer",LOGSQL_DATATYPE_VARCHAR,0);
774 log_sql_register_field(p,"request_line", "request_line",NULL,
775 "request_line",LOGSQL_DATATYPE_VARCHAR,0);
776 log_sql_register_field(p,"timestamp", "timestamp",NULL,
777 "time_stamp",LOGSQL_DATATYPE_INT,0);
778 log_sql_register_field(p,"status", "status",NULL,
779 "status",LOGSQL_DATATYPE_INT,0);
780 log_sql_register_field(p,"request_duration", "request_duration",NULL,
781 "request_duration",LOGSQL_DATATYPE_INT,0);
782 log_sql_register_field(p,"request_time", "request_time",NULL,
783 "request_time",LOGSQL_DATATYPE_VARCHAR,0);
784 log_sql_register_field(p,"remote_user", "remote_user",NULL,
785 "remote_user",LOGSQL_DATATYPE_VARCHAR,0);
786 log_sql_register_field(p,"request_uri", "request_uri",NULL,
787 "request_uri",LOGSQL_DATATYPE_VARCHAR,0);
788 log_sql_register_field(p,"virtual_host", "virtual_host",NULL,
789 "virtual_host",LOGSQL_DATATYPE_VARCHAR,0);
790 log_sql_register_field(p,"server_name", "server_name",NULL,
791 "virtual_host",LOGSQL_DATATYPE_VARCHAR,0);
792
793 log_sql_register_finish(s);
794
795 if (global_config.announce) {
796 ap_add_version_component(p, PACKAGE_NAME"/"PACKAGE_VERSION);
797 }
798 global_config.db.p = p;
799
800#if defined(WITH_APACHE20)
801 return OK;
802#endif
803}
804
805/* This function handles calling the DB module, handling errors
806 * of missing tables and lost DB connections, and falling back to
807 * preserving the DB query.
808 *
809 * Parms: request record, table type, table name, and the full SQL command
810 */
811
812static logsql_query_ret safe_sql_insert(request_rec *r, logsql_tabletype table_type,
813 const char *table_name, const char *query) {
814
815 logsql_query_ret result;
816 logsql_state *cls = ap_get_module_config(r->server->module_config,
817 &log_sql_module);
818
819 if (!global_config.db.connected || global_config.driver == NULL) {
820 /* preserve query */
821 return LOGSQL_QUERY_NOLINK;
822 }
823
824 result = global_config.driver->insert(r,&global_config.db,query);
825
826 /* If we ran the query and it returned an error, try to be robust.
827 * (After all, the module thought it had a valid mysql_log connection but the query
828 * could have failed for a number of reasons, so we have to be extra-safe and check.) */
829 switch (result) {
830 case LOGSQL_QUERY_SUCCESS:
831 return LOGSQL_QUERY_SUCCESS;
832 case LOGSQL_QUERY_NOLINK:
833 return LOGSQL_QUERY_FAIL;
834 /* TODO: What do we do here */
835 case LOGSQL_QUERY_FAIL:
836 global_config.driver->disconnect(&global_config.db);
837 global_config.db.connected = 0;
838 /* re-open the connection and try again */
839 if (log_sql_opendb_link(r->server) != LOGSQL_OPENDB_FAIL) {
840 log_error(APLOG_MARK,APLOG_NOTICE,0, r->server,"db reconnect successful");
841# if defined(WITH_APACHE20)
842 apr_sleep(apr_time_from_sec(0.25)); /* pause for a quarter second */
843# elif defined(WITH_APACHE13)
844# if defined(WIN32)
845 Sleep((DWORD)0.25);
846# else
847 {
848 struct timespec delay, remainder;
849 int nanoret;
850 delay.tv_sec = 0;
851 delay.tv_nsec = 250000000; /* pause for a quarter second */
852 nanoret = nanosleep(&delay, &remainder);
853 if (nanoret && errno != EINTR) {
854 log_error(APLOG_MARK,APLOG_ERR, errno, r->server,"nanosleep unsuccessful");
855 }
856 }
857# endif /* win32 */
858# endif
859 result = global_config.driver->insert(r,&global_config.db,query);
860 if (result == LOGSQL_QUERY_SUCCESS) {
861 return LOGSQL_QUERY_SUCCESS;
862 } else {
863 log_error(APLOG_MARK,APLOG_ERR,0,r->server,"second attempt failed");
864 preserve_entry(r, query);
865 return LOGSQL_QUERY_PRESERVED;
866 }
867 } else {
868 log_error(APLOG_MARK,APLOG_ERR,0,r->server,
869 "reconnect failed, unable to reach database. SQL logging stopped until child regains a db connection.");
870 log_error(APLOG_MARK,APLOG_ERR,0,r->server,
871 "log entries are being preserved in %s",cls->preserve_file);
872 preserve_entry(r, query);
873 return LOGSQL_QUERY_PRESERVED;
874 }
875 break;
876 case LOGSQL_QUERY_NOTABLE:
877 if (global_config.createtables) {
878 log_error(APLOG_MARK,APLOG_ERR,0,r->server,
879 "table doesn't exist...creating now");
880 if ((result = global_config.driver->create_table(r, &global_config.db, table_type,
881 table_name))!=LOGSQL_TABLE_SUCCESS) {
882 log_error(APLOG_MARK,APLOG_ERR,result,r->server,
883 "child attempted but failed to create one or more tables for %s, preserving query", ap_get_server_name(r));
884 preserve_entry(r, query);
885 return LOGSQL_QUERY_PRESERVED;
886 } else {
887 log_error(APLOG_MARK,APLOG_ERR,result, r->server,
888 "tables successfully created - retrying query");
889 if ((result = global_config.driver->insert(r,&global_config.db,query))!=LOGSQL_QUERY_SUCCESS) {
890 log_error(APLOG_MARK,APLOG_ERR,result, r->server,
891 "giving up, preserving query");
892 preserve_entry(r, query);
893 return LOGSQL_QUERY_PRESERVED;
894 } else {
895 log_error(APLOG_MARK,APLOG_NOTICE,0, r->server,
896 "query successful after table creation");
897 return LOGSQL_QUERY_SUCCESS;
898 }
899 }
900 } else {
901 log_error(APLOG_MARK,APLOG_ERR,0,r->server,
902 "table doesn't exist, creation denied by configuration, preserving query");
903 preserve_entry(r, query);
904 return LOGSQL_QUERY_PRESERVED;
905 }
906 break;
907 default:
908 log_error(APLOG_MARK,APLOG_ERR,0, r->server,
909 "Invalid return code from mog_log_query");
910 return LOGSQL_QUERY_FAIL;
911 break;
912 }
913 return LOGSQL_QUERY_FAIL;
914}
915
916/* This function gets called to create a per-server configuration
917 * record. It will always be called for the main server and
918 * for each virtual server that is established. Each server maintains
919 * its own state that is separate from the others' states.
920 *
921 * The return value is a pointer to the created module-specific
922 * structure.
923 */
924static void *log_sql_make_state(apr_pool_t *p, server_rec *s)
925{
926 logsql_state *cls = (logsql_state *) apr_pcalloc(p, sizeof(logsql_state));
927
928 /* These defaults are overridable in the httpd.conf file. */
929 cls->transfer_log_format = apr_array_make(p, 1, sizeof(char *));
930 cls->parsed_pool = p;
931
932 cls->notes_table_name = DEFAULT_NOTES_TABLE_NAME;
933 cls->hin_table_name = DEFAULT_HIN_TABLE_NAME;
934 cls->hout_table_name = DEFAULT_HOUT_TABLE_NAME;
935 cls->cookie_table_name = DEFAULT_COOKIE_TABLE_NAME;
936 cls->preserve_file = DEFAULT_PRESERVE_FILE;
937
938 cls->transfer_ignore_list = apr_array_make(p, 1, sizeof(char *));
939 cls->transfer_accept_list = apr_array_make(p, 1, sizeof(char *));
940 cls->remhost_ignore_list = apr_array_make(p, 1, sizeof(char *));
941 cls->notes_list = apr_array_make(p, 1, sizeof(char *));
942 cls->hin_list = apr_array_make(p, 1, sizeof(char *));
943 cls->hout_list = apr_array_make(p, 1, sizeof(char *));
944 cls->cookie_list = apr_array_make(p, 1, sizeof(char *));
945 return (void *) cls;
946}
947
948
949/* Iterates through an array of char* and searches for a matching element
950 * Returns 0 if not found, 1 if found */
951static int in_array(apr_array_header_t *ary, const char *elem)
952{
953 int itr;
954 for (itr = 0; itr < ary->nelts; itr++) {
955 if (!strcmp(elem,((char **)ary->elts)[itr])) {
956 return 1;
957 }
958 }
959 return 0;
960}
961
962
963/* Parse through lists and merge based on +/- prefixes */
964static apr_array_header_t *do_merge_array(apr_array_header_t *parent, apr_array_header_t *child, apr_pool_t *p)
965{
966 apr_array_header_t *ret;
967 ret = apr_array_make(p, 1, sizeof(char *));
968 if (apr_is_empty_array(child)) {
969 apr_array_cat(ret, parent);
970 } else {
971 apr_array_header_t *addlist, *dellist;
972 apr_pool_t *subp;
973 char **elem, **ptr = (char **)(child->elts);
974 int itr, overwrite = 0;
975
976 apr_pool_create(&subp,p);
977
978 addlist = apr_array_make(subp,5,sizeof(char *));
979 dellist = apr_array_make(subp,5,sizeof(char *));
980
981 for (itr=0; itr<child->nelts; itr++) {
982 if (*ptr[itr] == '+') {
983 elem = (char **)apr_array_push(addlist);
984 *elem = (ptr[itr]+1);
985 } else if (*ptr[itr] == '-') {
986 elem = (char **)apr_array_push(dellist);
987 *elem = (ptr[itr]+1);
988 } else {
989 overwrite = 1;
990 elem = (char **)apr_array_push(addlist);
991 *elem = ptr[itr];
992 }
993 }
994 child = apr_array_make(p,1,sizeof(char *));
995 ptr = (char **)(parent->elts);
996 if (overwrite==0) {
997 /* if we are not overwriting the existing then prepare for merge */
998 for (itr=0; itr<parent->nelts; itr++) {
999 if (!in_array(addlist, ptr[itr]) && !in_array(dellist,ptr[itr])) {
1000 elem = apr_array_push(ret);
1001 *elem = apr_pstrdup(p, ptr[itr]);
1002 }
1003 }
1004 }
1005 apr_array_cat(ret, addlist);
1006 apr_pool_destroy(subp);
1007 }
1008 return ret;
1009}
1010
1011static void *log_sql_merge_state(apr_pool_t *p, void *basev, void *addv)
1012{
1013 /* Fetch the two states to merge */
1014 logsql_state *parent = (logsql_state *) basev;
1015 logsql_state *child = (logsql_state *) addv;
1016
1017 /* Child can override these, otherwise they default to parent's choice.
1018 * If the parent didn't set them, create reasonable defaults for the
1019 * ones that should have such default settings. Leave the others null. */
1020
1021 /* No default for transfer_table_name because we want its absence
1022 * to disable logging. */
1023 if (!child->transfer_table_name) {
1024 child->transfer_table_name = parent->transfer_table_name;
1025 }
1026
1027 if (child->preserve_file == DEFAULT_PRESERVE_FILE)
1028 child->preserve_file = parent->preserve_file;
1029 /* server_root_relative the preserve file location */
1030 if (child->preserve_file == DEFAULT_PRESERVE_FILE)
1031 child->preserve_file = ap_server_root_relative(p, DEFAULT_PRESERVE_FILE);
1032
1033 if (child->notes_table_name == DEFAULT_NOTES_TABLE_NAME)
1034 child->notes_table_name = parent->notes_table_name;
1035
1036 if (child->hin_table_name == DEFAULT_HIN_TABLE_NAME)
1037 child->hin_table_name = parent->hin_table_name;
1038
1039 if (child->hout_table_name == DEFAULT_HOUT_TABLE_NAME)
1040 child->hout_table_name = parent->hout_table_name;
1041
1042 if (child->cookie_table_name == DEFAULT_COOKIE_TABLE_NAME)
1043 child->cookie_table_name = parent->cookie_table_name;
1044
1045 child->transfer_ignore_list = do_merge_array(parent->transfer_ignore_list, child->transfer_ignore_list, p);
1046 child->transfer_accept_list = do_merge_array(parent->transfer_accept_list, child->transfer_accept_list, p);
1047 child->remhost_ignore_list = do_merge_array(parent->remhost_ignore_list, child->remhost_ignore_list, p);
1048 child->notes_list = do_merge_array(parent->notes_list, child->notes_list, p);
1049 child->hin_list = do_merge_array(parent->hin_list, child->hin_list, p);
1050 child->hout_list = do_merge_array(parent->hout_list, child->hout_list, p);
1051 child->cookie_list = do_merge_array(parent->cookie_list,child->cookie_list, p);
1052
1053 if (!child->cookie_name)
1054 child->cookie_name = parent->cookie_name;
1055
1056
1057 return (void*) child;
1058}
1059
1060/* Routine to perform the actual construction and execution of the relevant
1061 * INSERT statements.
1062 */
1063static int log_sql_transaction(request_rec *orig)
1064{
1065 char **ptrptr, **ptrptr2;
1066 logsql_state *cls = ap_get_module_config(orig->server->module_config, &log_sql_module);
1067 const char *access_query;
1068 request_rec *r;
1069 const char *transfer_tablename = cls->transfer_table_name;
1070 const char *notes_tablename = cls->notes_table_name;
1071 const char *hout_tablename = cls->hout_table_name;
1072 const char *hin_tablename = cls->hin_table_name;
1073 const char *cookie_tablename = cls->cookie_table_name;
1074 if (global_config.driver == NULL) {
1075 return OK;
1076 }
1077 /* We handle mass virtual hosting differently. Dynamically determine the name
1078 * of the table from the virtual server's name, and flag it for creation.
1079 */
1080 if (global_config.massvirtual) {
1081 /* TODO: Make these configurable? */
1082 char *access_base = "access_";
1083 char *notes_base = "notes_";
1084 char *hout_base = "headout_";
1085 char *hin_base = "headin_";
1086 char *cookie_base = "cookies_";
1087
1088
1089 /* Determine the hostname and convert it to all lower-case; */
1090 char *servername = apr_pstrdup(orig->pool,(char *)ap_get_server_name(orig));
1091
1092 char *p=servername;
1093 while (*p) {
1094 *p = apr_tolower(*p);
1095 if (*p == '.') *p = '_';
1096 if (*p == '-') *p = '_';
1097 ++p;
1098 }
1099
1100 /* Find memory long enough to hold the table name + \0. */
1101 transfer_tablename = apr_pstrcat(orig->pool, access_base, servername, NULL);
1102 notes_tablename = apr_pstrcat(orig->pool, notes_base, servername, NULL);
1103 hin_tablename = apr_pstrcat(orig->pool, hin_base, servername, NULL);
1104 hout_tablename = apr_pstrcat(orig->pool, hout_base, servername, NULL);
1105 cookie_tablename = apr_pstrcat(orig->pool, cookie_base, servername, NULL);
1106
1107 /* Tell this virtual server its transfer table name, and
1108 * turn on create_tables, which is implied by massvirtual.
1109 */
1110
1111 global_config.createtables = 1;
1112 }
1113
1114 /* Do we have enough info to log? */
1115 if (!transfer_tablename) {
1116 return DECLINED;
1117 } else {
1118 const char *thehost;
1119 const char *theitem;
1120 char *fields = "", *values = "";
1121 char *itemsets = "";
1122 char *note_query = NULL;
1123 char *hin_query = NULL;
1124 char *hout_query = NULL;
1125 char *cookie_query = NULL;
1126 const char *unique_id;
1127 const char *formatted_item;
1128 int i, showcomma;
1129 int proceed;
1130
1131 for (r = orig; r->next; r = r->next) {
1132 continue;
1133 }
1134
1135 /* The following is a stolen upsetting mess of pointers, I'm sorry.
1136 * Anyone with the motiviation and/or the time should feel free
1137 * to make this cleaner. :) */
1138 ptrptr2 = (char **) (cls->transfer_accept_list->elts + (cls->transfer_accept_list->nelts * cls->transfer_accept_list->elt_size));
1139
1140 /* Go through each element of the accept list and compare it to the
1141 * request_uri. If we don't get a match, return without logging */
1142 if ((r->uri) && (cls->transfer_accept_list->nelts)) {
1143 proceed = 0;
1144 for (ptrptr = (char **) cls->transfer_accept_list->elts; ptrptr < ptrptr2; ptrptr = (char **) ((char *) ptrptr + cls->transfer_accept_list->elt_size))
1145 if (ap_strstr(r->uri, *ptrptr)) {
1146 proceed = 1;
1147 break;
1148 }
1149 if (!proceed)
1150 return OK;
1151 }
1152
1153 /* Go through each element of the ignore list and compare it to the
1154 * request_uri. If we get a match, return without logging */
1155 ptrptr2 = (char **) (cls->transfer_ignore_list->elts + (cls->transfer_ignore_list->nelts * cls->transfer_ignore_list->elt_size));
1156 if (r->uri) {
1157 for (ptrptr = (char **) cls->transfer_ignore_list->elts; ptrptr < ptrptr2; ptrptr = (char **) ((char *) ptrptr + cls->transfer_ignore_list->elt_size))
1158 if (ap_strstr(r->uri, *ptrptr)) {
1159 return OK;
1160 }
1161 }
1162
1163 /* Go through each element of the ignore list and compare it to the
1164 * remote host. If we get a match, return without logging */
1165 ptrptr2 = (char **) (cls->remhost_ignore_list->elts + (cls->remhost_ignore_list->nelts * cls->remhost_ignore_list->elt_size));
1166 thehost = ap_get_remote_host(r->connection, r->per_dir_config, REMOTE_NAME, NULL);
1167 if (thehost) {
1168 for (ptrptr = (char **) cls->remhost_ignore_list->elts; ptrptr < ptrptr2; ptrptr = (char **) ((char *) ptrptr + cls->remhost_ignore_list->elt_size))
1169 if (ap_strstr_c(thehost, *ptrptr)) {
1170 return OK;
1171 }
1172 }
1173
1174
1175 /* Iterate through the format characters and set up the INSERT string according to
1176 * what the user has configured. */
1177 showcomma = 0;
1178 for (i = 0; i<cls->transfer_log_format->nelts; i++) {
1179 logsql_field *item = cls->parsed_log_format[i];
1180 if (item==NULL || item->func==NULL) {
1181 log_error(APLOG_MARK, APLOG_ERR, 0, orig->server,
1182 "Log Format '%s' unknown or incomplete",((char **)cls->transfer_log_format->elts)[i]);
1183 continue;
1184 }
1185
1186 /* Yes, this key is one of the configured keys.
1187 * Call the key's function and put the returned value into 'formatted_item' */
1188 formatted_item = item->func->func(item->func->want_orig_req ? orig : r,
1189 item->param ? item->param : "");
1190
1191 /* Massage 'formatted_item' for proper SQL eligibility... */
1192 if (!formatted_item) {
1193 formatted_item = "";
1194 } else if (formatted_item[0] == '-' && formatted_item[1] == '\0' && !item->string_contents) {
1195 /* If apache tried to log a '-' character for a numeric field, convert that to a zero
1196 * because the database expects a numeral and will reject the '-' character. */
1197 formatted_item = "0";
1198 }
1199
1200 /* Append the fieldname and value-to-insert to the appropriate strings, quoting stringvals with ' as appropriate */
1201 fields = apr_pstrcat(r->pool, fields, (showcomma ? "," : ""),
1202 item->sql_field_name, NULL);
1203 values = apr_pstrcat(r->pool, values, (showcomma ? "," : ""),
1204 global_config.driver->escape(r, formatted_item, r->pool,&global_config.db), NULL);
1205 showcomma = 1;
1206 }
1207
1208 /* Work through the list of notes defined by LogSQLWhichNotes */
1209 i = 0;
1210 unique_id = extract_unique_id(r, "");
1211
1212 ptrptr2 = (char **) (cls->notes_list->elts + (cls->notes_list->nelts * cls->notes_list->elt_size));
1213 for (ptrptr = (char **) cls->notes_list->elts; ptrptr < ptrptr2; ptrptr = (char **) ((char *) ptrptr + cls->notes_list->elt_size)) {
1214 /* If the specified note (*ptrptr) exists for the current request... */
1215 if ((theitem = apr_table_get(r->notes, *ptrptr))) {
1216 itemsets = apr_pstrcat(r->pool, itemsets,
1217 (i > 0 ? "," : ""),
1218 "(",
1219 global_config.driver->escape(r, unique_id, r->pool, &global_config.db),
1220 ",",
1221 global_config.driver->escape(r, *ptrptr, r->pool,&global_config.db),
1222 ",",
1223 global_config.driver->escape(r, theitem, r->pool,&global_config.db),
1224 ")",
1225 NULL);
1226 i++;
1227 }
1228 }
1229 if ( *itemsets != '\0' ) {
1230 note_query = apr_psprintf(r->pool, "insert %s into %s (id, item, val) values %s",
1231 /*global_config.insertdelayed?"delayed":*/"", notes_tablename, itemsets);
1232
1233 log_error(APLOG_MARK,APLOG_DEBUG,0, orig->server,"mod_log_sql: note string: %s", note_query);
1234 }
1235
1236 /* Work through the list of headers-out defined by LogSQLWhichHeadersOut*/
1237 i = 0;
1238 itemsets = "";
1239
1240 ptrptr2 = (char **) (cls->hout_list->elts + (cls->hout_list->nelts * cls->hout_list->elt_size));
1241 for (ptrptr = (char **) cls->hout_list->elts; ptrptr < ptrptr2; ptrptr = (char **) ((char *) ptrptr + cls->hout_list->elt_size)) {
1242 /* If the specified header (*ptrptr) exists for the current request... */
1243 if ((theitem = apr_table_get(r->headers_out, *ptrptr))) {
1244 itemsets = apr_pstrcat(r->pool, itemsets,
1245 (i > 0 ? "," : ""),
1246 "(",
1247 global_config.driver->escape(r,unique_id, r->pool, &global_config.db),
1248 ",",
1249 global_config.driver->escape(r,*ptrptr, r->pool,&global_config.db),
1250 ",",
1251 global_config.driver->escape(r,theitem, r->pool,&global_config.db),
1252 ")",
1253 NULL);
1254 i++;
1255 }
1256 }
1257 if ( *itemsets != '\0' ) {
1258 hout_query = apr_psprintf(r->pool, "insert %s into %s (id, item, val) values %s",
1259 /*global_config.insertdelayed?"delayed":*/"", hout_tablename, itemsets);
1260
1261 log_error(APLOG_MARK,APLOG_DEBUG,0, orig->server,"mod_log_sql: header_out string: %s", hout_query);
1262 }
1263
1264
1265 /* Work through the list of headers-in defined by LogSQLWhichHeadersIn */
1266 i = 0;
1267 itemsets = "";
1268
1269 ptrptr2 = (char **) (cls->hin_list->elts + (cls->hin_list->nelts * cls->hin_list->elt_size));
1270 for (ptrptr = (char **) cls->hin_list->elts; ptrptr < ptrptr2; ptrptr = (char **) ((char *) ptrptr + cls->hin_list->elt_size)) {
1271 /* If the specified header (*ptrptr) exists for the current request... */
1272 if ((theitem = apr_table_get(r->headers_in, *ptrptr))) {
1273 itemsets = apr_pstrcat(r->pool, itemsets,
1274 (i > 0 ? "," : ""),
1275 "(",
1276 global_config.driver->escape(r,unique_id, r->pool, &global_config.db),
1277 ",",
1278 global_config.driver->escape(r,*ptrptr, r->pool,&global_config.db),
1279 ",",
1280 global_config.driver->escape(r,theitem, r->pool,&global_config.db),
1281 ")",
1282 NULL);
1283 i++;
1284 }
1285 }
1286 if ( *itemsets != '\0' ) {
1287 hin_query = apr_psprintf(r->pool, "insert %s into %s (id, item, val) values %s",
1288 /*global_config.insertdelayed?"delayed":*/"", hin_tablename, itemsets);
1289
1290 log_error(APLOG_MARK,APLOG_DEBUG,0, orig->server,"mod_log_sql: header_in string: %s", hin_query);
1291 }
1292
1293
1294 /* Work through the list of cookies defined by LogSQLWhichCookies */
1295 i = 0;
1296 itemsets = "";
1297
1298 ptrptr2 = (char **) (cls->cookie_list->elts + (cls->cookie_list->nelts * cls->cookie_list->elt_size));
1299 for (ptrptr = (char **) cls->cookie_list->elts; ptrptr < ptrptr2; ptrptr = (char **) ((char *) ptrptr + cls->cookie_list->elt_size)) {
1300 /* If the specified cookie (*ptrptr) exists for the current request... */
1301 if ( strncmp((theitem = extract_specific_cookie(r, *ptrptr)), "-", 1) ) {
1302 itemsets = apr_pstrcat(r->pool, itemsets,
1303 (i > 0 ? "," : ""),
1304 "(",
1305 global_config.driver->escape(r,unique_id, r->pool, &global_config.db),
1306 ",",
1307 global_config.driver->escape(r,*ptrptr, r->pool,&global_config.db),
1308 ",",
1309 global_config.driver->escape(r,theitem, r->pool,&global_config.db),
1310 ")",
1311 NULL);
1312 i++;
1313 }
1314
1315 }
1316 if ( *itemsets != '\0' ) {
1317 cookie_query = apr_psprintf(r->pool, "insert %s into %s (id, item, val) values %s",
1318 /*global_config.insertdelayed?"delayed":*/"", cookie_tablename, itemsets);
1319
1320 log_error(APLOG_MARK,APLOG_DEBUG,0, orig->server,"mod_log_sql: cookie string: %s", cookie_query);
1321 }
1322
1323
1324 /* Set up the actual INSERT statement */
1325 access_query = apr_psprintf(r->pool, "insert %s into %s (%s) values (%s)",
1326 /*global_config.insertdelayed?"delayed":*/"", transfer_tablename, fields, values);
1327
1328 log_error(APLOG_MARK,APLOG_DEBUG,0, r->server,"mod_log_sql: access string: %s", access_query);
1329
1330 /* If the person activated force-preserve, go ahead and push all the entries
1331 * into the preserve file, then return.
1332 */
1333 if (global_config.forcepreserve) {
1334 log_error(APLOG_MARK,APLOG_DEBUG,0, orig->server,"mod_log_sql: preservation forced");
1335 preserve_entry(orig, access_query);
1336 if ( note_query != NULL )
1337 preserve_entry(orig, note_query);
1338 if ( hin_query != NULL )
1339 preserve_entry(orig, hin_query);
1340 if ( hout_query != NULL )
1341 preserve_entry(orig, hout_query);
1342 if ( cookie_query != NULL )
1343 preserve_entry(orig, cookie_query);
1344 return OK;
1345 }
1346
1347 /* How's our mysql link integrity? */
1348 if (!global_config.db.connected) {
1349 if (!global_config.forcepreserve) {
1350 /* Make a try to establish the link */
1351 log_sql_opendb_link(r->server);
1352 }
1353 if (!global_config.db.connected) {
1354 /* Unable to re-establish a DB link, so assume that it's really
1355 * gone and send the entry to the preserve file instead.
1356 * This short-circuits safe_sql_query() during a db outage and therefore
1357 * we don't keep logging the db error over and over.
1358 */
1359 preserve_entry(orig, access_query);
1360 if ( note_query != NULL )
1361 preserve_entry(orig, note_query);
1362 if ( hin_query != NULL )
1363 preserve_entry(orig, hin_query);
1364 if ( hout_query != NULL )
1365 preserve_entry(orig, hout_query);
1366 if ( cookie_query != NULL )
1367 preserve_entry(orig, cookie_query);
1368
1369 return OK;
1370 } else {
1371 /* Whew, we got the DB link back */
1372 log_error(APLOG_MARK,APLOG_NOTICE,0, orig->server,"mod_log_sql: child established database connection");
1373 }
1374 }
1375
1376
1377 /* ---> So as of here we have a non-null value of mysql_log. <--- */
1378 /* ---> i.e. we have a good MySQL connection. <--- */
1379
1380 /* Make the access-table insert */
1381 safe_sql_insert(orig,LOGSQL_TABLE_ACCESS,transfer_tablename,access_query);
1382
1383 /* Log the optional notes, headers, etc. */
1384 if (note_query)
1385 safe_sql_insert(orig, LOGSQL_TABLE_NOTES,notes_tablename,note_query);
1386
1387 if (hout_query)
1388 safe_sql_insert(orig, LOGSQL_TABLE_HEADERSOUT,hout_tablename,hout_query);
1389
1390 if (hin_query)
1391 safe_sql_insert(orig, LOGSQL_TABLE_HEADERSIN,hin_tablename,hin_query);
1392
1393 if (cookie_query)
1394 safe_sql_insert(orig, LOGSQL_TABLE_COOKIES,cookie_tablename,cookie_query);
1395
1396 return OK;
1397 }
1398}
1399
1400
1401/* Setup of the available httpd.conf configuration commands.
1402 * Structure: command, function called, NULL, where available, how many arguments, verbose description
1403 */
1404static const command_rec log_sql_cmds[] = {
1405 AP_INIT_FLAG("LogSQLAnnounce", set_global_flag_slot,
1406 (void *)APR_OFFSETOF(global_config_t, announce), RSRC_CONF,
1407 "Whether to announce that mod_log_sql is loaded in the server header")
1408 ,
1409 /* DB connection parameters */
1410 AP_INIT_TAKE13("LogSQLLoginInfo", set_log_sql_info, NULL, RSRC_CONF,
1411 "The database connection URI in the form &quot;driver://user:password@hostname:port/database&quot;")
1412 ,
1413 AP_INIT_TAKE2("LogSQLDBParam", set_dbparam, NULL, RSRC_CONF,
1414 "First argument is the DB parameter, second is the value to assign")
1415 ,
1416 AP_INIT_FLAG("LogSQLForcePreserve", set_global_flag_slot,
1417 (void *)APR_OFFSETOF(global_config_t, forcepreserve), RSRC_CONF,
1418 "Forces logging to preserve file and bypasses database")
1419 ,
1420 AP_INIT_FLAG("LogSQLDisablePreserve", set_global_flag_slot,
1421 (void *)APR_OFFSETOF(global_config_t, disablepreserve), RSRC_CONF,
1422 "Completely disables use of the preserve file")
1423 ,
1424 AP_INIT_TAKE1("LogSQLPreserveFile", set_server_file_slot,
1425 (void *)APR_OFFSETOF(logsql_state,preserve_file), RSRC_CONF,
1426 "Name of the file to use for data preservation during database downtime")
1427 ,
1428 AP_INIT_FLAG("LogSQLCreateTables", set_global_nmv_flag_slot,
1429 (void *)APR_OFFSETOF(global_config_t, createtables), RSRC_CONF,
1430 "Turn on module's capability to create its SQL tables on the fly")
1431 ,
1432 /* Table names */
1433 AP_INIT_FLAG("LogSQLMassVirtualHosting", set_global_flag_slot,
1434 (void *)APR_OFFSETOF(global_config_t, massvirtual), RSRC_CONF,
1435 "Activates option(s) useful for ISPs performing mass virutal hosting")
1436 ,
1437 AP_INIT_TAKE1("LogSQLTransferLogTable", set_server_nmv_string_slot,
1438 (void *)APR_OFFSETOF(logsql_state, transfer_table_name), RSRC_CONF,
1439 "The database table that holds the transfer log")
1440 ,
1441 AP_INIT_TAKE1("LogSQLNotesLogTable", set_server_nmv_string_slot,
1442 (void *)APR_OFFSETOF(logsql_state, notes_table_name), RSRC_CONF,
1443 "The database table that holds the notes")
1444 ,
1445 AP_INIT_TAKE1("LogSQLHeadersOutLogTable", set_server_nmv_string_slot,
1446 (void *)APR_OFFSETOF(logsql_state, hout_table_name), RSRC_CONF,
1447 "The database table that holds the outbound headers")
1448 ,
1449 AP_INIT_TAKE1("LogSQLHeadersInLogTable", set_server_nmv_string_slot,
1450 (void *)APR_OFFSETOF(logsql_state, hin_table_name), RSRC_CONF,
1451 "The database table that holds the inbound headers")
1452 ,
1453 AP_INIT_TAKE1("LogSQLCookieLogTable", set_server_nmv_string_slot,
1454 (void *)APR_OFFSETOF(logsql_state, cookie_table_name), RSRC_CONF,
1455 "The database table that holds the cookie info")
1456 ,
1457 /* New Log Format */
1458 AP_INIT_ITERATE("LogSQLTransferLogItems", add_server_string_slot,
1459 (void *)APR_OFFSETOF(logsql_state, transfer_log_format), RSRC_CONF,
1460 "What fields to log to the database transfer log")
1461 ,
1462 AP_INIT_RAW_ARGS("LogSQLRegisterItem", set_register_field,
1463 NULL, RSRC_CONF,
1464 "Register a new Item for logging, Arguments: ItemName function argument sqlfield datatype datalen<br>"
1465 "datatypes are INT, SMALLINT, VARCHAR, CHAR<br>")
1466 ,
1467 AP_INIT_TAKE1("LogSQLShowConfig", set_global_string_slot,
1468 (void *)APR_OFFSETOF(global_config_t, showconfig), RSRC_CONF,
1469 "Add this to export the entire running function and dfield configuration to the named file")
1470 ,
1471 /* Machine ID */
1472 AP_INIT_TAKE1("LogSQLMachineID", set_global_string_slot,
1473 (void *)APR_OFFSETOF(global_config_t, machid), RSRC_CONF,
1474 "Machine ID that the module will log, useful in web clusters to differentiate machines")
1475 ,
1476 /* Limits on logging */
1477 AP_INIT_ITERATE("LogSQLRequestAccept", add_server_string_slot,
1478 (void *)APR_OFFSETOF(logsql_state, transfer_accept_list), RSRC_CONF,
1479 "List of URIs to accept for logging. Accesses that don't match will not be logged")
1480 ,
1481 AP_INIT_ITERATE("LogSQLRequestIgnore", add_server_string_slot,
1482 (void *)APR_OFFSETOF(logsql_state, transfer_ignore_list), RSRC_CONF,
1483 "List of URIs to ignore. Accesses that match will not be logged to database")
1484 ,
1485 AP_INIT_ITERATE("LogSQLRemhostIgnore", add_server_string_slot,
1486 (void *)APR_OFFSETOF(logsql_state, remhost_ignore_list), RSRC_CONF,
1487 "List of remote hosts to ignore. Accesses that match will not be logged to database")
1488 ,
1489 /* Special logging table configuration */
1490 AP_INIT_TAKE1("LogSQLWhichCookie", set_server_string_slot,
1491 (void *)APR_OFFSETOF(logsql_state, cookie_name), RSRC_CONF,
1492 "The single cookie that you want logged in the access_log when using the 'c' config directive")
1493 ,
1494 AP_INIT_ITERATE("LogSQLWhichNotes", add_server_string_slot,
1495 (void *)APR_OFFSETOF(logsql_state, notes_list), RSRC_CONF,
1496 "Notes that you would like to log in a separate table")
1497 ,
1498 AP_INIT_ITERATE("LogSQLWhichHeadersOut", add_server_string_slot,
1499 (void *)APR_OFFSETOF(logsql_state, hout_list), RSRC_CONF,
1500 "Outbound headers that you would like to log in a separate table")
1501 ,
1502 AP_INIT_ITERATE("LogSQLWhichHeadersIn", add_server_string_slot,
1503 (void *)APR_OFFSETOF(logsql_state, hin_list), RSRC_CONF,
1504 "Inbound headers that you would like to log in a separate table")
1505 ,
1506 AP_INIT_ITERATE("LogSQLWhichCookies", add_server_string_slot,
1507 (void *)APR_OFFSETOF(logsql_state, cookie_list), RSRC_CONF,
1508 "The cookie(s) that you would like to log in a separate table")
1509 ,
1510 AP_INIT_RAW_ARGS("LogSQLDeprecated", ap_set_deprecated, NULL, RSRC_CONF,
1511 "<br><b>Deprecated</b><br>The following Commands are deprecated and should not be used.. <br>Read the documentation for more information<br><b>Deprecated</b>")
1512 ,
1513 /* Deprecated commands */
1514 AP_INIT_TAKE1("LogSQLTransferLogFormat", set_logformat_slot,
1515 (void *)APR_OFFSETOF(logsql_state, transfer_log_format), RSRC_CONF,
1516 "<b>(Deprecated) Use LogSQLTransferLogItem to specify symbolic log items instead")
1517 ,
1518 AP_INIT_TAKE1("LogSQLDatabase", set_dbparam_slot,
1519 (void *)"database", RSRC_CONF,
1520 "<b>(Deprecated) Use LogSQLDBParam database dbname.</b> The name of the database database for logging")
1521 ,
1522 AP_INIT_TAKE1("LogSQLTableType", set_dbparam_slot,
1523 (void *)"tabletype", RSRC_CONF,
1524 "<b>(Deprecated) Use LogSQLDBParam tabletype type.</b> What kind of table to create (MyISAM, InnoDB,...) when creating tables")
1525 ,
1526 AP_INIT_TAKE1("LogSQLSocketFile", set_dbparam_slot,
1527 (void *)"socketfile", RSRC_CONF,
1528 "<b>(Deprecated) Use LogSQLDBParam socketfile socket.</b> Name of the file to employ for socket connections to database")
1529 ,
1530 AP_INIT_TAKE1("LogSQLTCPPort", set_dbparam_slot,
1531 (void *)"port", RSRC_CONF,
1532 "<b>(Deprecated) Use LogSQLDBParam port port.</b> Port number to use for TCP connections to database, defaults to 3306 if not set")
1533 ,
1534 {NULL}
1535};
1536/* The configuration array that sets up the hooks into the module. */
1537#if defined(WITH_APACHE20)
1538static void register_hooks(apr_pool_t *p) {
1539 ap_hook_post_config(log_sql_post_config, NULL, NULL, APR_HOOK_REALLY_FIRST);
1540 ap_hook_child_init(log_sql_child_init, NULL, NULL, APR_HOOK_MIDDLE);
1541 ap_hook_log_transaction(log_sql_transaction, NULL, NULL, APR_HOOK_MIDDLE);
1542}
1543
1544module AP_MODULE_DECLARE_DATA log_sql_module = {
1545 STANDARD20_MODULE_STUFF,
1546 NULL, /* create per-directory config structures */
1547 NULL, /* merge per-directory config structures */
1548 log_sql_make_state, /* create per-server config structures */
1549 log_sql_merge_state, /* merge per-server config structures */
1550 log_sql_cmds, /* command handlers */
1551 register_hooks /* register hooks */
1552};
1553#elif defined(WITH_APACHE13)
1554module MODULE_VAR_EXPORT log_sql_module = {
1555 STANDARD_MODULE_STUFF,
1556 log_sql_module_init, /* module initializer */
1557 NULL, /* create per-dir config */
1558 NULL, /* merge per-dir config */
1559 log_sql_make_state, /* create server config */
1560 log_sql_merge_state, /* merge server config */
1561 log_sql_cmds, /* config directive table */
1562 NULL, /* [9] content handlers */
1563 NULL, /* [2] URI-to-filename translation */
1564 NULL, /* [5] check/validate user_id */
1565 NULL, /* [6] check authorization */
1566 NULL, /* [4] check access by host */
1567 NULL, /* [7] MIME type checker/setter */
1568 NULL, /* [8] fixups */
1569 log_sql_transaction, /* [10] logger */
1570 NULL /* [3] header parser */
1571#if MODULE_MAGIC_NUMBER >= 19970728 /* 1.3-dev or later support these additionals... */
1572 ,log_sql_child_init, /* child process initializer */
1573 log_sql_child_exit, /* process exit/cleanup */
1574 NULL /* [1] post read-request */
1575#endif
1576
1577};
1578#endif
diff --git a/src/mod_log_sql_dbd.c b/src/mod_log_sql_dbd.c
new file mode 100644
index 0000000..e641c35
--- /dev/null
+++ b/src/mod_log_sql_dbd.c
@@ -0,0 +1,132 @@
1/* $Id: mod_log_sql_dbi.c 120 2004-04-17 15:14:12Z urkle@drip.ws $ */
2
3#if defined(WITH_APACHE20)
4# include "apache20.h"
5#else
6# error Unsupported Apache version
7#endif
8
9
10#ifdef HAVE_CONFIG_H
11/* Undefine these to prevent conflicts between Apache ap_config_auto.h and
12 * my config.h. Only really needed for Apache < 2.0.48, but it can't hurt.
13 */
14#undef PACKAGE_BUGREPORT
15#undef PACKAGE_NAME
16#undef PACKAGE_STRING
17#undef PACKAGE_TARNAME
18#undef PACKAGE_VERSION
19
20#include "autoconfig.h"
21#endif
22
23#include "mod_log_sql.h"
24
25#include "apr_dbd.h"
26#include "mod_dbd.h"
27
28typedef struct {
29 ap_dbd_t *dbd;
30} request_config_t;
31
32LOGSQL_MODULE_FORWARD(dbd);
33
34static ap_dbd_t *(*dbd_acquire_fn)(request_rec*) = NULL;
35
36static ap_dbd_t *log_sql_dbd_getconnection(request_rec *r)
37{
38 request_config_t *rconf = ap_get_module_config(r->request_config, &LOGSQL_MODULE(dbd));
39 if (!rconf) {
40 rconf = apr_pcalloc(r->pool, sizeof(request_config_t));
41 ap_set_module_config(r->request_config, &LOGSQL_MODULE(dbd), (void *)rconf);
42 rconf->dbd = dbd_acquire_fn(r);
43 }
44 return rconf->dbd;
45}
46
47/* Connect to the database */
48static logsql_opendb_ret log_sql_dbd_connect(server_rec *s, logsql_dbconnection *db)
49{
50 // We are using mod_dbd so we don't do anything here
51 if (!dbd_acquire_fn) {
52 // no mod_dbd return failure
53 log_error(APLOG_MARK,APLOG_ERR,0, s,"mod_log_sql_dbd: mod_dbd is not loaded or available");
54 return LOGSQL_OPENDB_FAIL;
55 } else {
56 return LOGSQL_OPENDB_SUCCESS;
57 }
58}
59
60/* Close the DB link */
61static void log_sql_dbd_close(logsql_dbconnection *db)
62{
63 // mod_dbd handles this, so do nothing
64}
65
66/* Routine to escape the 'dangerous' characters that would otherwise
67 * corrupt the INSERT string: ', \, and "
68 */
69static const char *log_sql_dbd_escape(request_rec *r, const char *from_str, apr_pool_t *p,
70 logsql_dbconnection *db)
71{
72 // Acquire a DBD connection from mod_dbd
73 ap_dbd_t *dbd = log_sql_dbd_getconnection(r);
74 if (!dbd) return NULL;
75
76 if (!from_str)
77 return NULL;
78
79 return apr_pstrcat(p, "'",apr_dbd_escape(dbd->driver, p, from_str, dbd->handle),"'",NULL);
80}
81
82/* Run an insert query and return a categorized error or success */
83static logsql_query_ret log_sql_dbd_query(request_rec *r,logsql_dbconnection *db,
84 const char *query)
85{
86 int ret;
87 const char *err;
88 int affected;
89 // Acquire a DBD connection from mod_dbd
90 ap_dbd_t *dbd = log_sql_dbd_getconnection(r);
91 if (!dbd) return LOGSQL_QUERY_NOLINK;
92
93 // Run the query
94 ret = apr_dbd_query(dbd->driver, dbd->handle, &affected, query);
95 if (ret == 0) {
96 return LOGSQL_QUERY_SUCCESS;
97 } else {
98 // attempt to detect error message
99 err = apr_dbd_error(dbd->driver, dbd->handle, ret);
100 log_error(APLOG_MARK, APLOG_ERR, 0, r->server, "DB Returned error: (%d) %s", ret, err);
101 // Unable to check if "NO SUCH TABLE" due to apr_dbd not mapping error codes to a standard set.
102 return LOGSQL_QUERY_FAIL;
103 }
104}
105
106/* Create table table_name of type table_type. */
107static logsql_table_ret log_sql_dbd_create(request_rec *r, logsql_dbconnection *db,
108 logsql_tabletype table_type, const char *table_name)
109{
110 return LOGSQL_TABLE_FAIL;
111}
112
113static const char *supported_drivers[] = {"dbd",NULL};
114static logsql_dbdriver log_sql_dbd_driver = {
115 "dbd",
116 supported_drivers,
117 log_sql_dbd_connect,/* open DB connection */
118 log_sql_dbd_close, /* close DB connection */
119 log_sql_dbd_escape, /* escape query */
120 log_sql_dbd_query, /* insert query */
121 log_sql_dbd_create /* create table */
122};
123
124LOGSQL_REGISTER(dbd) {
125 dbd_acquire_fn = APR_RETRIEVE_OPTIONAL_FN(ap_dbd_acquire);
126 if (dbd_acquire_fn == NULL) {
127 log_error(APLOG_MARK,APLOG_ERR,0,s,"You must load mod_dbd to enable AuthDBD functions");
128 }
129
130 log_sql_register_driver(p,&log_sql_dbd_driver);
131 LOGSQL_REGISTER_RETURN;
132}
diff --git a/src/mod_log_sql_dbi.c b/src/mod_log_sql_dbi.c
new file mode 100644
index 0000000..5b83836
--- /dev/null
+++ b/src/mod_log_sql_dbi.c
@@ -0,0 +1,263 @@
1/* $Id: mod_log_sql_dbi.c 120 2004-04-17 15:14:12Z urkle@drip.ws $ */
2
3#if defined(WITH_APACHE20)
4# include "apache20.h"
5#elif defined(WITH_APACHE13)
6# include "apache13.h"
7#else
8# error Unsupported Apache version
9#endif
10
11
12#ifdef HAVE_CONFIG_H
13/* Undefine these to prevent conflicts between Apache ap_config_auto.h and
14 * my config.h. Only really needed for Apache < 2.0.48, but it can't hurt.
15 */
16#undef PACKAGE_BUGREPORT
17#undef PACKAGE_NAME
18#undef PACKAGE_STRING
19#undef PACKAGE_TARNAME
20#undef PACKAGE_VERSION
21
22#include "autoconfig.h"
23#endif
24
25#include "mod_log_sql.h"
26
27#include "dbi/dbi.h"
28
29typedef struct {
30 dbi_conn conn;
31} dbi_conn_rec;
32
33/* Connect to the MYSQL database */
34static logsql_opendb_ret log_sql_dbi_connect(server_rec *s, logsql_dbconnection *db)
35{
36 const char *driver = apr_table_get(db->parms,"driver");
37 const char *host = apr_table_get(db->parms,"hostname");
38 const char *user = apr_table_get(db->parms,"username");
39 const char *passwd = apr_table_get(db->parms,"password");
40 const char *database = apr_table_get(db->parms,"database");
41 const char *s_tcpport = apr_table_get(db->parms,"port");
42 unsigned int tcpport = (s_tcpport)?atoi(s_tcpport):0;
43 const char *socketfile = apr_table_get(db->parms,"socketfile");
44 //dbi_result result;
45 dbi_conn_rec *dblink = db->handle;
46 if (!dblink) {
47 dblink = apr_pcalloc(db->p, sizeof(*dblink));
48 db->handle = (void *)dblink;
49 }
50
51 dblink->conn = dbi_conn_new(driver);
52
53 dbi_conn_set_option(dblink->conn, "host", host);
54 dbi_conn_set_option(dblink->conn, "username", user);
55 dbi_conn_set_option(dblink->conn, "password", passwd);
56 dbi_conn_set_option(dblink->conn, "dbname", database);
57 if (tcpport) {
58 dbi_conn_set_option_numeric(dblink->conn, "port", tcpport);
59 }
60
61 if (socketfile && !strcmp(driver,"mysql")) {
62 dbi_conn_set_option(dblink->conn, "mysql_unix_socket", socketfile);
63 }
64
65 if (!dbi_conn_connect(dblink->conn)) {
66 log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
67 "HOST: '%s' PORT: '%d' DB: '%s' USER: '%s' SOCKET: '%s'",
68 host, tcpport, database, user, socketfile);
69 return LOGSQL_OPENDB_SUCCESS;
70 } else {
71 const char *error;
72 dbi_conn_error(dblink->conn, &error);
73 log_error(APLOG_MARK, APLOG_ERR, 0, s,
74 "DBI Error: %s", error);
75 log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
76 "HOST: '%s' PORT: '%d' DB: '%s' USER: '%s' SOCKET: '%s'",
77 host, tcpport, database, user, socketfile);
78 return LOGSQL_OPENDB_FAIL;
79 }
80}
81
82/* Close the DB link */
83static void log_sql_dbi_close(logsql_dbconnection *db)
84{
85 dbi_conn_rec *dblink = db->handle;
86 dbi_conn_close(dblink->conn);
87 dblink->conn = NULL;
88}
89
90/* Routine to escape the 'dangerous' characters that would otherwise
91 * corrupt the INSERT string: ', \, and "
92 */
93static const char *log_sql_dbi_escape(request_rec *r,const char *from_str, apr_pool_t *p,
94 logsql_dbconnection *db)
95{
96 dbi_conn_rec *dblink = db->handle;
97
98 if (!from_str)
99 return NULL;
100 else {
101 char *to_str = strdup(from_str);
102 char *retval;
103 dbi_driver_quote_string(dbi_conn_get_driver(dblink->conn), &to_str);
104 retval = apr_pstrdup(p, to_str);
105 free(to_str);
106 return retval;
107 }
108}
109
110/* Run a mysql insert query and return a categorized error or success */
111static logsql_query_ret log_sql_dbi_query(request_rec *r,logsql_dbconnection *db,
112 const char *query)
113{
114 const char *error;
115 dbi_result result;
116 dbi_conn_rec *dblink = db->handle;
117
118 if (!dblink->conn) {
119 return LOGSQL_QUERY_NOLINK;
120 }
121
122 /* Run the query */
123 if ((result = dbi_conn_query(dblink->conn, query))) {
124 return LOGSQL_QUERY_SUCCESS;
125 }
126 /* Check to see if the error is "nonexistent table" */
127 dbi_conn_error(dblink->conn, &error);
128 log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
129 "DBI Error: %s", error);
130/* if (real_error == ER_NO_SUCH_TABLE) {
131 log_error(APLOG_MARK,APLOG_ERR,0, r->server,"table does not exist, preserving query");
132 return LOGSQL_QUERY_NOTABLE;
133 }*/
134 return LOGSQL_QUERY_FAIL;
135}
136
137/* Create table table_name of type table_type. */
138static logsql_table_ret log_sql_dbi_create(request_rec *r, logsql_dbconnection *db,
139 logsql_tabletype table_type, const char *table_name)
140{
141 dbi_result result;
142 const char *driver = apr_table_get(db->parms,"driver");
143 const char *tabletype = apr_table_get(db->parms,"tabletype");
144 char *type_suffix = NULL;
145
146 char *create_prefix = "create table if not exists `";
147 char *create_suffix = NULL;
148 char *create_sql;
149
150 dbi_conn_rec *dblink = db->handle;
151
152/* if (!global_config.createtables) {
153 return APR_SUCCESS;
154 }*/
155
156 switch (table_type) {
157 case LOGSQL_TABLE_ACCESS:
158 create_suffix =
159 "` (id char(19),\
160 agent varchar(255),\
161 bytes_sent int unsigned,\
162 child_pid smallint unsigned,\
163 cookie varchar(255),\
164 machine_id varchar(25),\
165 request_file varchar(255),\
166 referer varchar(255),\
167 remote_host varchar(50),\
168 remote_logname varchar(50),\
169 remote_user varchar(50),\
170 request_duration smallint unsigned,\
171 request_line varchar(255),\
172 request_method varchar(10),\
173 request_protocol varchar(10),\
174 request_time char(28),\
175 request_uri varchar(255),\
176 request_args varchar(255),\
177 server_port smallint unsigned,\
178 ssl_cipher varchar(25),\
179 ssl_keysize smallint unsigned,\
180 ssl_maxkeysize smallint unsigned,\
181 status smallint unsigned,\
182 time_stamp int unsigned,\
183 virtual_host varchar(255))";
184 break;
185 case LOGSQL_TABLE_COOKIES:
186 case LOGSQL_TABLE_HEADERSIN:
187 case LOGSQL_TABLE_HEADERSOUT:
188 case LOGSQL_TABLE_NOTES:
189 create_suffix =
190 "` (id char(19),\
191 item varchar(80),\
192 val varchar(80))";
193 break;
194 }
195
196 if (tabletype && !strcmp(driver,"mysql")) {
197 type_suffix = apr_pstrcat(r->pool, " TYPE=",
198 tabletype, NULL);
199 }
200 /* Find memory long enough to hold the whole CREATE string + \0 */
201 create_sql = apr_pstrcat(r->pool, create_prefix, table_name, create_suffix,
202 type_suffix, NULL);
203
204 log_error(APLOG_MARK,APLOG_DEBUG,0, r->server,"create string: %s", create_sql);
205
206 if (!dblink) {
207 return LOGSQL_QUERY_NOLINK;
208 }
209
210 /* Run the create query */
211 if (!(result = dbi_conn_query(dblink, create_sql))) {
212 const char *error;
213 dbi_conn_error(dblink->conn, &error);
214 log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
215 "DBI Error: %s", error);
216 return LOGSQL_TABLE_FAIL;
217 }
218
219 return LOGSQL_TABLE_SUCCESS;
220}
221
222static logsql_dbdriver log_sql_dbi_driver = {
223 "dbi",
224 NULL,
225 log_sql_dbi_connect, /* open DB connection */
226 log_sql_dbi_close, /* close DB connection */
227 log_sql_dbi_escape, /* escape query */
228 log_sql_dbi_query, /* insert query */
229 log_sql_dbi_create /* create table */
230};
231
232static apr_status_t log_sql_dbi_cleanup(void *data)
233{
234 dbi_shutdown();
235#if defined(WITH_APACHE20)
236 return APR_SUCCESS;
237#endif
238}
239
240LOGSQL_REGISTER(dbi) {
241 dbi_driver driver;
242 const char **driver_list;
243 int count = 1;
244
245 dbi_initialize(NULL);
246
247 for (driver = dbi_driver_list(NULL);
248 driver;
249 driver = dbi_driver_list(driver)) {
250 count++;
251 }
252 driver_list = apr_pcalloc(p, sizeof(char *) * (count));
253 count = 0;
254 for (driver = dbi_driver_list(NULL);
255 driver;
256 driver = dbi_driver_list(driver)) {
257 driver_list[count++] = dbi_driver_get_name(driver);
258 }
259 log_sql_dbi_driver.provided_drivers = driver_list;
260 log_sql_register_driver(p,&log_sql_dbi_driver);
261 apr_pool_cleanup_register(p, NULL, log_sql_dbi_cleanup, NULL);
262 LOGSQL_REGISTER_RETURN;
263}
diff --git a/src/mod_log_sql_logio.c b/src/mod_log_sql_logio.c
new file mode 100644
index 0000000..ebdeddd
--- /dev/null
+++ b/src/mod_log_sql_logio.c
@@ -0,0 +1,146 @@
1/* $Id: mod_log_sql_ssl.c 140 2004-05-14 03:50:47Z urkle@drip.ws $ */
2
3#if defined(WITH_APACHE20)
4# include "apache20.h"
5#else
6# error Unsupported Apache version
7#endif
8
9#ifdef HAVE_CONFIG_H
10/* Undefine these to prevent conflicts between Apache ap_config_auto.h and
11 * my config.h. Only really needed for Apache < 2.0.48, but it can't hurt.
12 */
13#undef PACKAGE_BUGREPORT
14#undef PACKAGE_NAME
15#undef PACKAGE_STRING
16#undef PACKAGE_TARNAME
17#undef PACKAGE_VERSION
18
19#include "autoconfig.h"
20#endif
21
22#include "mod_log_sql.h"
23
24#include "http_connection.h"
25
26module AP_MODULE_DECLARE_DATA log_sql_logio_module;
27
28// From apachge 2.2's mod_logio.c to provide logging ACTUAL incoming and outgoing bytes
29static const char logio_filter_name[] = "LOG_SQL_INPUT_OUTPUT";
30
31typedef struct {
32 apr_off_t bytes_in;
33 apr_off_t bytes_out;
34} logio_config_t;
35
36static void ap_logio_add_bytes_out(conn_rec *c, apr_off_t bytes){
37 logio_config_t *cf = ap_get_module_config(c->conn_config, &log_sql_logio_module);
38
39 cf->bytes_out += bytes;
40}
41
42static const char *log_bytes_in(request_rec *r, char *a)
43{
44 logio_config_t *cf = ap_get_module_config(r->connection->conn_config,
45 &log_sql_logio_module);
46
47 return apr_off_t_toa(r->pool, cf->bytes_in);
48}
49
50static const char *log_bytes_out(request_rec *r, char *a)
51{
52 logio_config_t *cf = ap_get_module_config(r->connection->conn_config,
53 &log_sql_logio_module);
54
55 return apr_off_t_toa(r->pool, cf->bytes_out);
56}
57
58static int logio_transaction(request_rec *r)
59{
60 logio_config_t *cf = ap_get_module_config(r->connection->conn_config,
61 &log_sql_logio_module);
62
63 cf->bytes_in = cf->bytes_out = 0;
64
65 return OK;
66}
67
68static apr_status_t logio_in_filter(ap_filter_t *f,
69 apr_bucket_brigade *bb,
70 ap_input_mode_t mode,
71 apr_read_type_e block,
72 apr_off_t readbytes) {
73 apr_off_t length;
74 apr_status_t status;
75 logio_config_t *cf = ap_get_module_config(f->c->conn_config, &log_sql_logio_module);
76
77 status = ap_get_brigade(f->next, bb, mode, block, readbytes);
78
79 apr_brigade_length (bb, 0, &length);
80
81 if (length > 0)
82 cf->bytes_in += length;
83
84 return status;
85}
86
87static apr_status_t logio_out_filter(ap_filter_t *f,
88 apr_bucket_brigade *bb) {
89 apr_bucket *b = APR_BRIGADE_LAST(bb);
90
91 /* End of data, make sure we flush */
92 if (APR_BUCKET_IS_EOS(b)) {
93 APR_BUCKET_INSERT_BEFORE(b,
94 apr_bucket_flush_create(f->c->bucket_alloc));
95 }
96
97 return ap_pass_brigade(f->next, bb);
98}
99
100static int logio_pre_conn(conn_rec *c, void *csd) {
101 logio_config_t *cf = apr_pcalloc(c->pool, sizeof(logio_config_t));
102
103 ap_set_module_config(c->conn_config, &log_sql_logio_module, cf);
104
105 ap_add_input_filter(logio_filter_name, NULL, NULL, c);
106 ap_add_output_filter(logio_filter_name, NULL, NULL, c);
107
108 return OK;
109}
110
111static int post_config(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s)
112{
113 log_sql_register_function(p, "bytes_in", log_bytes_in, LOGSQL_FUNCTION_REQ_FINAL);
114 log_sql_register_function(p, "bytes_out", log_bytes_out, LOGSQL_FUNCTION_REQ_FINAL);
115
116 log_sql_register_alias(s,p,'i', "bytes_in");
117 log_sql_register_alias(s,p,'o', "bytes_out");
118
119 log_sql_register_field(p, "bytes_in", "bytes_in", NULL,
120 "bytes_in", LOGSQL_DATATYPE_INT, 0);
121 log_sql_register_field(p, "bytes_out", "bytes_out", NULL,
122 "bytes_out", LOGSQL_DATATYPE_INT, 0);
123
124 log_sql_register_finish(s);
125 return OK;
126}
127
128static void register_hooks(apr_pool_t *p) {
129 static const char *pre[] = { "mod_log_sql.c", NULL };
130
131 ap_hook_pre_connection(logio_pre_conn, NULL, NULL, APR_HOOK_MIDDLE);
132 ap_hook_log_transaction(logio_transaction, pre, NULL, APR_HOOK_MIDDLE);
133 ap_hook_post_config(post_config, NULL, NULL, APR_HOOK_REALLY_FIRST);
134
135 ap_register_input_filter(logio_filter_name, logio_in_filter, NULL,
136 AP_FTYPE_NETWORK - 1);
137 ap_register_output_filter(logio_filter_name, logio_out_filter, NULL,
138 AP_FTYPE_NETWORK - 1);
139
140 APR_REGISTER_OPTIONAL_FN(ap_logio_add_bytes_out);
141}
142
143module AP_MODULE_DECLARE_DATA log_sql_logio_module = {
144 STANDARD20_MODULE_STUFF,
145 NULL, NULL, NULL, NULL, NULL, register_hooks
146};
diff --git a/src/mod_log_sql_mysql.c b/src/mod_log_sql_mysql.c
new file mode 100644
index 0000000..902cadf
--- /dev/null
+++ b/src/mod_log_sql_mysql.c
@@ -0,0 +1,269 @@
1/* $Id:mod_log_sql_mysql.c 180 2008-09-21 15:54:12Z urkle@drip.ws $ */
2
3#if defined(WITH_APACHE20)
4# include "apache20.h"
5#elif defined(WITH_APACHE13)
6# include "apache13.h"
7#else
8# error Unsupported Apache version
9#endif
10
11#ifdef HAVE_CONFIG_H
12/* Undefine these to prevent conflicts between Apache ap_config_auto.h and
13 * my config.h. Only really needed for Apache < 2.0.48, but it can't hurt.
14 */
15#undef PACKAGE_BUGREPORT
16#undef PACKAGE_NAME
17#undef PACKAGE_STRING
18#undef PACKAGE_TARNAME
19#undef PACKAGE_VERSION
20
21#include "autoconfig.h"
22#endif
23
24#include "mod_log_sql.h"
25
26#include "mysql.h"
27#include "mysqld_error.h"
28
29/* The enduser won't modify these */
30#define MYSQL_ERROR(mysql) ((mysql)?(mysql_error(mysql)):"MySQL server has gone away")
31
32/* Connect to the MYSQL database */
33static logsql_opendb_ret log_sql_mysql_connect(server_rec *s, logsql_dbconnection *db)
34{
35 const char *host = apr_table_get(db->parms,"hostname");
36 const char *user = apr_table_get(db->parms,"username");
37 const char *passwd = apr_table_get(db->parms,"password");
38 const char *database = apr_table_get(db->parms,"database");
39 const char *s_tcpport = apr_table_get(db->parms,"port");
40 unsigned int tcpport = (s_tcpport)?atoi(s_tcpport):3306;
41 const char *socketfile = apr_table_get(db->parms,"socketfile");
42 MYSQL *dblink = db->handle;
43
44 dblink = mysql_init(dblink);
45 db->handle = (void *)dblink;
46
47
48 if (!socketfile) {
49 socketfile = "/var/lib/mysql/mysql.sock";
50 }
51
52 if (mysql_real_connect(dblink, host, user, passwd, database, tcpport,
53 socketfile, 0)) {
54 log_error(APLOG_MARK,APLOG_DEBUG,0, s,"HOST: '%s' PORT: '%d' DB: '%s' USER: '%s' SOCKET: '%s'",
55 host, tcpport, database, user, socketfile);
56 return LOGSQL_OPENDB_SUCCESS;
57 } else {
58 log_error(APLOG_MARK,APLOG_ERR,0, s,"mod_log_sql_mysql: database connection error: mysql error: %s",
59 MYSQL_ERROR(dblink));
60 log_error(APLOG_MARK,APLOG_DEBUG, 0, s,"HOST: '%s' PORT: '%d' DB: '%s' USER: '%s' SOCKET: '%s'",
61 host, tcpport, database, user, socketfile);
62 return LOGSQL_OPENDB_FAIL;
63 }
64}
65
66/* Close the DB link */
67static void log_sql_mysql_close(logsql_dbconnection *db)
68{
69 mysql_close((MYSQL *)db->handle);
70 /* mysql_close frees this data so NULL it out incase we reconnect later */
71 db->handle=NULL;
72}
73
74/* Routine to escape the 'dangerous' characters that would otherwise
75 * corrupt the INSERT string: ', \, and "
76 */
77static const char *log_sql_mysql_escape(request_rec *r, const char *from_str, apr_pool_t *p,
78 logsql_dbconnection *db)
79{
80 /* Return "NULL" for empty strings */
81 if (!from_str || strlen(from_str) == 0)
82 return "NULL";
83 else {
84 char *to_str;
85 unsigned long length = strlen(from_str);
86 unsigned long retval;
87
88 /* Pre-allocate a new string that could hold twice the original, which would only
89 * happen if the whole original string was 'dangerous' characters.
90 */
91 to_str = (char *) apr_palloc(p, length * 2 + 3);
92 if (!to_str) {
93 return from_str;
94 }
95 strcpy(to_str, "'");
96 if (!db->connected) {
97 /* Well, I would have liked to use the current database charset. mysql is
98 * unavailable, however, so I fall back to the slightly less respectful
99 * mysql_escape_string() function that uses the default charset.
100 */
101 retval = mysql_escape_string(to_str+1, from_str, length);
102 } else {
103 /* MySQL is available, so I'll go ahead and respect the current charset when
104 * I perform the escape.
105 */
106 retval = mysql_real_escape_string((MYSQL *)db->handle, to_str+1, from_str, length);
107 }
108 strcat(to_str,"'");
109
110 if (retval)
111 return to_str;
112 else
113 return from_str;
114 }
115}
116
117#if defined(WIN32)
118#define SIGNAL_GRAB
119#define SIGNAL_RELEASE
120#define SIGNAL_VAR
121#else
122#define SIGNAL_VAR void (*handler) (int);
123#define SIGNAL_GRAB handler = signal(SIGPIPE, SIG_IGN);
124#define SIGNAL_RELEASE signal(SIGPIPE, handler);
125#endif
126/* Run a mysql insert query and return a categorized error or success */
127static logsql_query_ret log_sql_mysql_query(request_rec *r,logsql_dbconnection *db,
128 const char *query)
129{
130 int retval;
131 SIGNAL_VAR
132
133 unsigned int real_error = 0;
134 /*const char *real_error_str = NULL;*/
135
136 MYSQL *dblink = (MYSQL *)db->handle;
137
138 if (!dblink) {
139 return LOGSQL_QUERY_NOLINK;
140 }
141
142 /* A failed mysql_query() may send a SIGPIPE, so we ignore that signal momentarily. */
143 SIGNAL_GRAB
144
145 /* Run the query */
146 if (!(retval = mysql_query(dblink, query))) {
147 SIGNAL_RELEASE
148 return LOGSQL_QUERY_SUCCESS;
149 }
150 real_error = mysql_errno(dblink);
151 log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
152 "mysql_query returned (%d) \"%s\"", real_error, MYSQL_ERROR(dblink));
153 /* Check to see if the error is "nonexistent table" */
154
155 if (real_error == ER_NO_SUCH_TABLE) {
156 log_error(APLOG_MARK,APLOG_ERR,0, r->server,"table does not exist, preserving query");
157 /* Restore SIGPIPE to its original handler function */
158 SIGNAL_RELEASE
159 return LOGSQL_QUERY_NOTABLE;
160 }
161
162 /* Restore SIGPIPE to its original handler function */
163 SIGNAL_RELEASE
164 return LOGSQL_QUERY_FAIL;
165}
166
167/* Create table table_name of type table_type. */
168static logsql_table_ret log_sql_mysql_create(request_rec *r, logsql_dbconnection *db,
169 logsql_tabletype table_type, const char *table_name)
170{
171 int retval;
172 const char *tabletype = apr_table_get(db->parms,"tabletype");
173 SIGNAL_VAR
174 char *type_suffix = NULL;
175
176 char *create_prefix = "create table if not exists `";
177 char *create_suffix = NULL;
178 char *create_sql;
179
180 MYSQL *dblink = (MYSQL *)db->handle;
181
182/* if (!global_config.createtables) {
183 return APR_SUCCESS;
184 }*/
185
186 switch (table_type) {
187 case LOGSQL_TABLE_ACCESS:
188 create_suffix =
189 "` (id char(19),\
190 agent varchar(255),\
191 bytes_sent int unsigned,\
192 child_pid smallint unsigned,\
193 cookie varchar(255),\
194 machine_id varchar(25),\
195 request_file varchar(255),\
196 referer varchar(255),\
197 remote_host varchar(50),\
198 remote_logname varchar(50),\
199 remote_user varchar(50),\
200 request_duration smallint unsigned,\
201 request_line varchar(255),\
202 request_method varchar(10),\
203 request_protocol varchar(10),\
204 request_time char(28),\
205 request_uri varchar(255),\
206 request_args varchar(255),\
207 server_port smallint unsigned,\
208 ssl_cipher varchar(25),\
209 ssl_keysize smallint unsigned,\
210 ssl_maxkeysize smallint unsigned,\
211 status smallint unsigned,\
212 time_stamp int unsigned,\
213 virtual_host varchar(255),\
214 bytes_in int unsigned,\
215 bytes_out int unsigned)";
216 break;
217 case LOGSQL_TABLE_COOKIES:
218 case LOGSQL_TABLE_HEADERSIN:
219 case LOGSQL_TABLE_HEADERSOUT:
220 case LOGSQL_TABLE_NOTES:
221 create_suffix =
222 "` (id char(19),\
223 item varchar(80),\
224 val varchar(80))";
225 break;
226 }
227
228 if (tabletype) {
229 type_suffix = apr_pstrcat(r->pool, " TYPE=",
230 tabletype, NULL);
231 }
232 /* Find memory long enough to hold the whole CREATE string + \0 */
233 create_sql = apr_pstrcat(r->pool, create_prefix, table_name, create_suffix,
234 type_suffix, NULL);
235
236 log_error(APLOG_MARK,APLOG_DEBUG,0, r->server,"create string: %s", create_sql);
237
238 if (!dblink) {
239 return LOGSQL_QUERY_NOLINK;
240 }
241 /* A failed mysql_query() may send a SIGPIPE, so we ignore that signal momentarily. */
242 SIGNAL_GRAB
243
244 /* Run the create query */
245 if ((retval = mysql_query(dblink, create_sql))) {
246 log_error(APLOG_MARK,APLOG_ERR,0, r->server,"failed to create table: %s",
247 table_name);
248 SIGNAL_RELEASE
249 return LOGSQL_TABLE_FAIL;
250 }
251 SIGNAL_RELEASE
252 return LOGSQL_TABLE_SUCCESS;
253}
254
255static const char *supported_drivers[] = {"mysql",NULL};
256static logsql_dbdriver mysql_driver = {
257 "mysql",
258 supported_drivers,
259 log_sql_mysql_connect, /* open DB connection */
260 log_sql_mysql_close, /* close DB connection */
261 log_sql_mysql_escape, /* escape query */
262 log_sql_mysql_query, /* insert query */
263 log_sql_mysql_create /* create table */
264};
265
266LOGSQL_REGISTER(mysql) {
267 log_sql_register_driver(p,&mysql_driver);
268 LOGSQL_REGISTER_RETURN;
269}
diff --git a/src/mod_log_sql_pgsql.c b/src/mod_log_sql_pgsql.c
new file mode 100644
index 0000000..dcfbd73
--- /dev/null
+++ b/src/mod_log_sql_pgsql.c
@@ -0,0 +1,247 @@
1/* $Id:mod_log_sql_pgsql.c 180 2008-09-21 15:54:12Z urkle@drip.ws $ */
2
3#if defined(WITH_APACHE20)
4# include "apache20.h"
5#elif defined(WITH_APACHE13)
6# include "apache13.h"
7#else
8# error Unsupported Apache version
9#endif
10
11
12#ifdef HAVE_CONFIG_H
13/* Undefine these to prevent conflicts between Apache ap_config_auto.h and
14 * my config.h. Only really needed for Apache < 2.0.48, but it can't hurt.
15 */
16#undef PACKAGE_BUGREPORT
17#undef PACKAGE_NAME
18#undef PACKAGE_STRING
19#undef PACKAGE_TARNAME
20#undef PACKAGE_VERSION
21
22#include "autoconfig.h"
23#endif
24
25#include "mod_log_sql.h"
26
27#include "libpq-fe.h"
28
29/* Connect to the PGSQL database */
30static logsql_opendb_ret log_sql_pgsql_connect(server_rec *s, logsql_dbconnection *db)
31{
32 const char *host = apr_table_get(db->parms,"hostname");
33 const char *user = apr_table_get(db->parms,"username");
34 const char *passwd = apr_table_get(db->parms,"password");
35 const char *database = apr_table_get(db->parms,"database");
36 const char *s_tcpport = apr_table_get(db->parms,"port");
37
38 db->handle = PQsetdbLogin(host, s_tcpport, NULL, NULL, database, user, passwd);
39
40 if (PQstatus(db->handle) == CONNECTION_OK) {
41 log_error(APLOG_MARK,APLOG_DEBUG,0, s,"HOST: '%s' PORT: '%s' DB: '%s' USER: '%s'",
42 host, s_tcpport, database, user);
43 return LOGSQL_OPENDB_SUCCESS;
44 } else {
45 log_error(APLOG_MARK,APLOG_DEBUG,0, s,"mod_log_sql: database connection error: %s",
46 PQerrorMessage(db->handle));
47 log_error(APLOG_MARK,APLOG_DEBUG, 0, s,"HOST: '%s' PORT: '%s' DB: '%s' USER: '%s'",
48 host, s_tcpport, database, user);
49 return LOGSQL_OPENDB_FAIL;
50 }
51}
52
53/* Close the DB link */
54static void log_sql_pgsql_close(logsql_dbconnection *db)
55{
56 PQfinish((PGconn*)(db->handle));
57}
58
59/* Routine to escape the 'dangerous' characters that would otherwise
60 * corrupt the INSERT string: ', \, and "
61 * Also PQescapeString does not place the ' around the string. So we have
62 * to do this manually
63 */
64static const char *log_sql_pgsql_escape(const char *from_str, apr_pool_t *p,
65 logsql_dbconnection *db)
66{
67 char *temp;
68 if (!from_str)
69 return NULL;
70 else {
71 char *to_str;
72 unsigned long length = strlen(from_str);
73 unsigned long retval;
74
75 /* Pre-allocate a new string that could hold twice the original, which would only
76 * happen if the whole original string was 'dangerous' characters.
77 * And forsee the space for the 2 '
78 */
79 temp = to_str = (char *) apr_palloc(p, length * 2 + 3);
80 if (!to_str) {
81 return from_str;
82 }
83
84 *temp = '\'';
85 temp++;
86
87 retval = PQescapeString(temp, from_str, length);
88
89 /* avoid the string to be tolong for the sql database*/
90 if (retval > 250) retval = 250;
91
92 *(temp+retval) = '\'';
93 *(temp+retval+1) = '\0';
94
95 /* We must always return the to_str, because we always need the ' added */
96// if (retval)
97 return to_str;
98// else
99// return from_str;
100 }
101}
102
103/* Run a sql insert query and return a categorized error or success */
104static logsql_query_ret log_sql_pgsql_query(request_rec *r,logsql_dbconnection *db,
105 const char *query)
106{
107 PGresult *result;
108 void (*handler) (int);
109 unsigned int real_error = 0;
110 /*const char *real_error_str = NULL;*/
111
112 PGconn *conn = db->handle;
113
114 if (PQstatus(conn) != CONNECTION_OK) {
115 return LOGSQL_QUERY_NOLINK;
116 }
117 /* A failed mysql_query() may send a SIGPIPE, so we ignore that signal momentarily. */
118 /* Does postgresql do this also ??? */
119 handler = signal(SIGPIPE, SIG_IGN);
120
121 result = PQexec(conn, query);
122 /* Run the query */
123 if (PQresultStatus(result) == PGRES_COMMAND_OK) {
124 signal(SIGPIPE, handler);
125 PQclear(result);
126 return LOGSQL_QUERY_SUCCESS;
127 }
128 /* Check to see if the error is "nonexistent table" */
129 /* removed ... don't know how ! (sorry)
130 real_error = mysql_errno(dblink);
131
132 if (real_error == ER_NO_SUCH_TABLE) {
133 log_error(APLOG_MARK,APLOG_ERR,0, r->server,"table does not exist, preserving query");
134 signal(SIGPIPE, handler);
135 PQclear(result);
136 return LOGSQL_QUERY_NOTABLE;
137 }*/
138
139 /* Restore SIGPIPE to its original handler function */
140 signal(SIGPIPE, handler);
141 PQclear(result);
142 return LOGSQL_QUERY_FAIL;
143}
144
145/* Create table table_name of type table_type. */
146static logsql_table_ret log_sql_pgsql_create(request_rec *r, logsql_dbconnection *db,
147 logsql_tabletype table_type, const char *table_name)
148{
149 PGresult *result;
150 const char *tabletype = apr_table_get(db->parms,"tabletype");
151 void (*handler) (int);
152 char *type_suffix = NULL;
153
154 char *create_prefix = "create table if not exists `";
155 char *create_suffix = NULL;
156 char *create_sql;
157
158 PGconn *conn = db->handle;
159
160/* if (!global_config.createtables) {
161 return APR_SUCCESS;
162 }*/
163
164 switch (table_type) {
165 case LOGSQL_TABLE_ACCESS:
166 create_suffix =
167 "` (id char(19),\
168 agent varchar(255),\
169 bytes_sent int unsigned,\
170 child_pid smallint unsigned,\
171 cookie varchar(255),\
172 machine_id varchar(25),\
173 request_file varchar(255),\
174 referer varchar(255),\
175 remote_host varchar(50),\
176 remote_logname varchar(50),\
177 remote_user varchar(50),\
178 request_duration smallint unsigned,\
179 request_line varchar(255),\
180 request_method varchar(10),\
181 request_protocol varchar(10),\
182 request_time char(28),\
183 request_uri varchar(255),\
184 request_args varchar(255),\
185 server_port smallint unsigned,\
186 ssl_cipher varchar(25),\
187 ssl_keysize smallint unsigned,\
188 ssl_maxkeysize smallint unsigned,\
189 status smallint unsigned,\
190 time_stamp int unsigned,\
191 virtual_host varchar(255))";
192 break;
193 case LOGSQL_TABLE_COOKIES:
194 case LOGSQL_TABLE_HEADERSIN:
195 case LOGSQL_TABLE_HEADERSOUT:
196 case LOGSQL_TABLE_NOTES:
197 create_suffix =
198 "` (id char(19),\
199 item varchar(80),\
200 val varchar(80))";
201 break;
202 }
203
204 if (tabletype) {
205 type_suffix = apr_pstrcat(r->pool, " TYPE=",
206 tabletype, NULL);
207 }
208 /* Find memory long enough to hold the whole CREATE string + \0 */
209 create_sql = apr_pstrcat(r->pool, create_prefix, table_name, create_suffix,
210 type_suffix, NULL);
211
212 log_error(APLOG_MARK,APLOG_DEBUG,0, r->server,"create string: %s", create_sql);
213
214 if (PQstatus(conn) != CONNECTION_OK) {
215 return LOGSQL_QUERY_NOLINK;
216 }
217 /* A failed mysql_query() may send a SIGPIPE, so we ignore that signal momentarily. */
218 handler = signal(SIGPIPE, SIG_IGN);
219
220 /* Run the create query */
221 result = PQexec(conn, create_sql);
222 if (PQresultStatus(result) != PGRES_COMMAND_OK) {
223 log_error(APLOG_MARK,APLOG_ERR,0, r->server,"failed to create table: %s",
224 table_name);
225 signal(SIGPIPE, handler);
226 PQclear(result);
227 return LOGSQL_TABLE_FAIL;
228 }
229 signal(SIGPIPE, handler);
230 PQclear(result);
231 return LOGSQL_TABLE_SUCCESS;
232}
233
234static char *supported_drivers[] = {"pgsql",NULL};
235static logsql_dbdriver pgsql_driver = {
236 supported_drivers,
237 log_sql_pgsql_connect, /* open DB connection */
238 log_sql_pgsql_close, /* close DB connection */
239 log_sql_pgsql_escape, /* escape query */
240 log_sql_pgsql_query, /* insert query */
241 log_sql_pgsql_create /* create table */
242};
243
244LOGSQL_REGISTER(pgsql) {
245 log_sql_register_driver(p,&pgsql_driver);
246 LOGSQL_REGISTER_RETURN;
247}
diff --git a/src/mod_log_sql_ssl.c b/src/mod_log_sql_ssl.c
new file mode 100644
index 0000000..b3a4929
--- /dev/null
+++ b/src/mod_log_sql_ssl.c
@@ -0,0 +1,106 @@
1/* $Id:mod_log_sql_ssl.c 180 2008-09-21 15:54:12Z urkle@drip.ws $ */
2
3#if defined(WITH_APACHE20)
4# include "apache20.h"
5#elif defined(WITH_APACHE13)
6# include "apache13.h"
7#else
8# error Unsupported Apache version
9#endif
10
11#ifdef HAVE_CONFIG_H
12/* Undefine these to prevent conflicts between Apache ap_config_auto.h and
13 * my config.h. Only really needed for Apache < 2.0.48, but it can't hurt.
14 */
15#undef PACKAGE_BUGREPORT
16#undef PACKAGE_NAME
17#undef PACKAGE_STRING
18#undef PACKAGE_TARNAME
19#undef PACKAGE_VERSION
20
21#include "autoconfig.h"
22#endif
23
24#include "mod_log_sql.h"
25
26#include "mod_ssl.h"
27
28#if defined(WITH_APACHE20)
29static APR_OPTIONAL_FN_TYPE(ssl_var_lookup) *header_ssl_lookup = NULL;
30# define TEST_SSL(r) header_ssl_lookup
31#elif defined(WITH_APACHE13)
32# define TEST_SSL(r) ap_ctx_get(r->connection->client->ctx, "ssl")
33# define header_ssl_lookup ssl_var_lookup
34#endif
35
36static const char *extract_ssl_keysize(request_rec *r, char *a)
37{
38 char *result = NULL;
39 if (TEST_SSL(r) != NULL)
40 {
41 result = header_ssl_lookup(r->pool, r->server, r->connection, r, "SSL_CIPHER_USEKEYSIZE");
42 log_error(APLOG_MARK,APLOG_DEBUG,0, r->server,"SSL_KEYSIZE: %s", result);
43 if (result && result[0])
44 return result;
45 else
46 return "0";
47 } else {
48 return "0";
49 }
50}
51
52static const char *extract_ssl_maxkeysize(request_rec *r, char *a)
53{
54 char *result = NULL;
55 if (TEST_SSL(r) != NULL)
56 {
57 result = header_ssl_lookup(r->pool, r->server, r->connection, r, "SSL_CIPHER_ALGKEYSIZE");
58 log_error(APLOG_MARK,APLOG_DEBUG,0, r->server,"SSL_ALGKEYSIZE: %s", result);
59 if (result && result[0])
60 return result;
61 else
62 return "0";
63 } else {
64 return "0";
65 }
66}
67
68static const char *extract_ssl_cipher(request_rec *r, char *a)
69{
70 char *result = NULL;
71 if (TEST_SSL(r) != NULL)
72 {
73 result = header_ssl_lookup(r->pool, r->server, r->connection, r, "SSL_CIPHER");
74 log_error(APLOG_MARK,APLOG_DEBUG,0, r->server,"SSL_CIPHER: %s", result);
75 if (result && result[0])
76 return result;
77 else
78 return "0";
79 } else {
80 return "-";
81 }
82}
83
84
85LOGSQL_REGISTER(ssl)
86{
87 log_sql_register_function(p, "ssl_keysize", extract_ssl_keysize, LOGSQL_FUNCTION_REQ_FINAL);
88 log_sql_register_function(p, "ssl_maxkeysize", extract_ssl_maxkeysize, LOGSQL_FUNCTION_REQ_FINAL);
89 log_sql_register_function(p, "ssl_cipher", extract_ssl_cipher, LOGSQL_FUNCTION_REQ_FINAL);
90
91 log_sql_register_alias(s,p,'q', "ssl_keysize");
92 log_sql_register_alias(s,p,'Q', "ssl_maxkeysize");
93 log_sql_register_alias(s,p,'z', "ssl_cipher");
94
95 log_sql_register_field(p, "ssl_keysize", "ssl_keysize", NULL,
96 "ssl_keysize", LOGSQL_DATATYPE_VARCHAR, 0);
97 log_sql_register_field(p, "ssl_maxkeysize", "ssl_maxkeysize", NULL,
98 "ssl_maxkeysize", LOGSQL_DATATYPE_VARCHAR, 0);
99 log_sql_register_field(p, "ssl_cipher", "ssl_cipher", NULL,
100 "ssl_cipher", LOGSQL_DATATYPE_VARCHAR, 0);
101
102#if defined(WITH_APACHE20)
103 header_ssl_lookup = APR_RETRIEVE_OPTIONAL_FN(ssl_var_lookup);
104#endif
105 LOGSQL_REGISTER_RETURN;
106}