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