diff options
Diffstat (limited to 'mod_log_sql.c')
-rw-r--r-- | mod_log_sql.c | 1282 |
1 files changed, 0 insertions, 1282 deletions
diff --git a/mod_log_sql.c b/mod_log_sql.c deleted file mode 100644 index 1892eec..0000000 --- a/mod_log_sql.c +++ /dev/null | |||
@@ -1,1282 +0,0 @@ | |||
1 | /* $Id$ */ | ||
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 "config.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 | } global_config_t; | ||
63 | |||
64 | static global_config_t global_config; | ||
65 | |||
66 | /* structure to hold helper function info */ | ||
67 | typedef struct { | ||
68 | char key; /* item letter character */ | ||
69 | logsql_item_func *func; /* its extraction function */ | ||
70 | const char *sql_field_name; /* its column in SQL */ | ||
71 | int want_orig_default; /* if it requires the original request prior to internal redirection */ | ||
72 | int string_contents; /* if it returns a string */ | ||
73 | } logsql_item; | ||
74 | |||
75 | /* But the contents of this structure will vary by virtual server. | ||
76 | * This permits each virtual server to vary its configuration slightly | ||
77 | * for per-server customization. | ||
78 | * | ||
79 | * Each child process has its own segregated copy of this structure. | ||
80 | */ | ||
81 | typedef struct { | ||
82 | apr_array_header_t *transfer_ignore_list; | ||
83 | apr_array_header_t *transfer_accept_list; | ||
84 | apr_array_header_t *remhost_ignore_list; | ||
85 | apr_array_header_t *notes_list; | ||
86 | apr_array_header_t *hout_list; | ||
87 | apr_array_header_t *hin_list; | ||
88 | apr_array_header_t *cookie_list; | ||
89 | const char *notes_table_name; | ||
90 | const char *hout_table_name; | ||
91 | const char *hin_table_name; | ||
92 | const char *cookie_table_name; | ||
93 | const char *transfer_table_name; | ||
94 | const char *transfer_log_format; | ||
95 | apr_pool_t *parsed_pool; | ||
96 | logsql_item **parsed_log_format; | ||
97 | const char *preserve_file; | ||
98 | const char *cookie_name; | ||
99 | } logsql_state; | ||
100 | |||
101 | |||
102 | /* list of "handlers" for log types */ | ||
103 | static apr_array_header_t *logsql_item_list; | ||
104 | |||
105 | /* Registration function for extract functions * | ||
106 | * and update parse cache for transfer_log_format * | ||
107 | * this is exported from the module */ | ||
108 | LOGSQL_DECLARE(void) log_sql_register_item(server_rec *s, apr_pool_t *p, | ||
109 | char key, logsql_item_func *func, const char *sql_field_name, | ||
110 | int want_orig_default, int string_contents) | ||
111 | { | ||
112 | server_rec *ts; | ||
113 | logsql_item *item; | ||
114 | if (!logsql_item_list) | ||
115 | logsql_item_list = apr_array_make(p,10, sizeof(logsql_item)); | ||
116 | |||
117 | item= apr_array_push(logsql_item_list); | ||
118 | item->key = key; | ||
119 | item->func = func; | ||
120 | item->sql_field_name = sql_field_name; | ||
121 | item->want_orig_default = want_orig_default; | ||
122 | item->string_contents = string_contents; | ||
123 | /* some voodoo here to post parse logitems in all servers * | ||
124 | * so a "cached" list is used in the main logging loop for speed */ | ||
125 | for (ts = s; ts; ts = ts->next) { | ||
126 | logsql_state *cfg = ap_get_module_config(ts->module_config, | ||
127 | &log_sql_module); | ||
128 | char *pos; | ||
129 | |||
130 | if (cfg->transfer_log_format) { | ||
131 | if ( (pos = ap_strchr_c(cfg->transfer_log_format,key))!=NULL) { | ||
132 | cfg->parsed_log_format[pos - cfg->transfer_log_format] = item; | ||
133 | } | ||
134 | } | ||
135 | } | ||
136 | } | ||
137 | |||
138 | /* Registration function for database drivers */ | ||
139 | LOGSQL_DECLARE(void) log_sql_register_driver(apr_pool_t *p, | ||
140 | logsql_dbdriver *driver) | ||
141 | { | ||
142 | global_config.driver = driver; | ||
143 | } | ||
144 | |||
145 | /* Include all the core extract functions */ | ||
146 | #include "functions.h" | ||
147 | #if defined(WITH_APACHE13) | ||
148 | # include "functions13.h" | ||
149 | #elif defined(WITH_APACHE20) | ||
150 | # include "functions20.h" | ||
151 | #endif | ||
152 | |||
153 | static logsql_opendb_ret log_sql_opendb_link(server_rec* s) | ||
154 | { | ||
155 | logsql_opendb_ret result; | ||
156 | if (global_config.driver == NULL) { | ||
157 | return LOGSQL_OPENDB_FAIL; | ||
158 | } | ||
159 | if (global_config.forcepreserve) { | ||
160 | //global_config.db.connected = 1; | ||
161 | return LOGSQL_OPENDB_PRESERVE; | ||
162 | } | ||
163 | if (global_config.db.connected) { | ||
164 | return LOGSQL_OPENDB_ALREADY; | ||
165 | } | ||
166 | /* database | ||
167 | host | ||
168 | user | ||
169 | passwd | ||
170 | */ | ||
171 | if (global_config.db.parms) { | ||
172 | result = global_config.driver->connect(s, &global_config.db); | ||
173 | if (result==LOGSQL_OPENDB_FAIL) { | ||
174 | global_config.db.connected = 0; | ||
175 | } else { | ||
176 | global_config.db.connected = 1; | ||
177 | } | ||
178 | return result; | ||
179 | } else { | ||
180 | log_error(APLOG_MARK, APLOG_ERR, 0, s, | ||
181 | "mod_log_sql: insufficient configuration info to establish database link"); | ||
182 | return LOGSQL_OPENDB_FAIL; | ||
183 | } | ||
184 | } | ||
185 | |||
186 | static void preserve_entry(request_rec *r, const char *query) | ||
187 | { | ||
188 | logsql_state *cls = ap_get_module_config(r->server->module_config, | ||
189 | &log_sql_module); | ||
190 | #if defined(WITH_APACHE20) | ||
191 | apr_file_t *fp; | ||
192 | apr_status_t result; | ||
193 | #elif defined(WITH_APACHE13) | ||
194 | FILE *fp; | ||
195 | int result; | ||
196 | #endif | ||
197 | /* If preserve file is disabled bail out */ | ||
198 | if (global_config.disablepreserve) | ||
199 | return; | ||
200 | #if defined(WITH_APACHE20) | ||
201 | result = apr_file_open(&fp, cls->preserve_file,APR_APPEND | APR_WRITE | APR_CREATE, APR_OS_DEFAULT, r->pool); | ||
202 | #elif defined(WITH_APACHE13) | ||
203 | fp = ap_pfopen(r->pool, cls->preserve_file, "a"); | ||
204 | result = (fp)?0:errno; | ||
205 | #endif | ||
206 | if (result != APR_SUCCESS) { | ||
207 | log_error(APLOG_MARK, APLOG_ERR, result, r->server, | ||
208 | "attempted append of local preserve file '%s' but failed.",cls->preserve_file); | ||
209 | } else { | ||
210 | #if defined(WITH_APACHE20) | ||
211 | apr_file_printf(fp,"%s;\n", query); | ||
212 | apr_file_close(fp); | ||
213 | #elif defined(WITH_APACHE13) | ||
214 | fprintf(fp,"%s;\n", query); | ||
215 | ap_pfclose(r->pool, fp); | ||
216 | #endif | ||
217 | log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, | ||
218 | "mod_log_sql: entry preserved in %s", cls->preserve_file); | ||
219 | } | ||
220 | } | ||
221 | |||
222 | |||
223 | /* ------------------------------------------------* | ||
224 | * Command handlers that are called according * | ||
225 | * to the directives found at Apache runtime. * | ||
226 | * ------------------------------------------------*/ | ||
227 | |||
228 | |||
229 | static const char *set_global_flag_slot(cmd_parms *cmd, | ||
230 | void *struct_ptr, | ||
231 | int flag) | ||
232 | { | ||
233 | void *ptr = &global_config; | ||
234 | int offset = (int)(long)cmd->info; | ||
235 | |||
236 | *(int *)((char *)ptr + offset) = flag ? 1 : 0; | ||
237 | |||
238 | return NULL; | ||
239 | } | ||
240 | |||
241 | static const char *set_global_nmv_flag_slot(cmd_parms *cmd, | ||
242 | void *struct_ptr, | ||
243 | int flag) | ||
244 | { | ||
245 | if (global_config.massvirtual) { | ||
246 | return apr_psprintf(cmd->pool, | ||
247 | "mod_log_sql: do not set %s when LogSQLMassVirtualHosting(%d) is On.%d:%d", | ||
248 | cmd->cmd->name, global_config.massvirtual, | ||
249 | (int)(long)&global_config, (int)(long)struct_ptr); | ||
250 | } else { | ||
251 | return set_global_flag_slot(cmd,struct_ptr,flag); | ||
252 | } | ||
253 | } | ||
254 | |||
255 | static const char *set_global_string_slot(cmd_parms *cmd, | ||
256 | void *struct_ptr, | ||
257 | const char *arg) | ||
258 | { | ||
259 | void *ptr = &global_config; | ||
260 | int offset = (int)(long)cmd->info; | ||
261 | |||
262 | *(const char **)((char *)ptr + offset) = apr_pstrdup(cmd->pool,arg); | ||
263 | return NULL; | ||
264 | } | ||
265 | static const char *set_server_string_slot(cmd_parms *cmd, | ||
266 | void *struct_ptr, | ||
267 | const char *arg) | ||
268 | { | ||
269 | void *ptr = ap_get_module_config(cmd->server->module_config, | ||
270 | &log_sql_module); | ||
271 | int offset = (int)(long)cmd->info; | ||
272 | |||
273 | *(const char **)((char *)ptr + offset) = arg; | ||
274 | |||
275 | return NULL; | ||
276 | } | ||
277 | |||
278 | static const char *set_server_file_slot(cmd_parms *cmd, | ||
279 | void *struct_ptr, | ||
280 | const char *arg) | ||
281 | { | ||
282 | void *ptr = ap_get_module_config(cmd->server->module_config, | ||
283 | &log_sql_module); | ||
284 | int offset = (int)(long)cmd->info; | ||
285 | const char *path; | ||
286 | |||
287 | path = ap_server_root_relative(cmd->pool, (char *)arg); | ||
288 | |||
289 | if (!path) { | ||
290 | return apr_pstrcat(cmd->pool, "Invalid file path ", | ||
291 | arg, NULL); | ||
292 | } | ||
293 | |||
294 | *(const char **)((char*)ptr + offset) = path; | ||
295 | |||
296 | return NULL; | ||
297 | } | ||
298 | |||
299 | static const char *set_logformat_slot(cmd_parms *cmd, | ||
300 | void *struct_ptr, | ||
301 | const char *arg) | ||
302 | { | ||
303 | logsql_state *cfg = ap_get_module_config(cmd->server->module_config, | ||
304 | &log_sql_module); | ||
305 | |||
306 | cfg->transfer_log_format = arg; | ||
307 | /* apr_pool_clear(cfg->parsed_pool);*/ | ||
308 | cfg->parsed_log_format = apr_pcalloc(cfg->parsed_pool, | ||
309 | strlen(arg) * sizeof(logsql_item *)); | ||
310 | return NULL; | ||
311 | } | ||
312 | |||
313 | static const char *set_server_nmv_string_slot(cmd_parms *parms, | ||
314 | void *struct_ptr, | ||
315 | const char *arg) | ||
316 | { | ||
317 | if (global_config.massvirtual) | ||
318 | return apr_psprintf(parms->pool, | ||
319 | "mod_log_sql: do not set %s when LogSQLMassVirtualHosting is On.", | ||
320 | parms->cmd->name); | ||
321 | else | ||
322 | return set_server_string_slot(parms,struct_ptr,arg); | ||
323 | } | ||
324 | |||
325 | /* Set a DB connection parameter */ | ||
326 | static const char *set_dbparam(cmd_parms *cmd, | ||
327 | void *struct_ptr, | ||
328 | const char *key, | ||
329 | const char *val) | ||
330 | { | ||
331 | if (!global_config.db.parms) { | ||
332 | global_config.db.parms = apr_table_make(cmd->pool,5); | ||
333 | } | ||
334 | apr_table_set(global_config.db.parms,key,val); | ||
335 | return NULL; | ||
336 | } | ||
337 | |||
338 | static const char *set_dbparam_slot(cmd_parms *cmd, | ||
339 | void *struct_ptr, | ||
340 | const char *arg) | ||
341 | { | ||
342 | const char *param = (char *)cmd->info; | ||
343 | set_dbparam(cmd,NULL,param,arg); | ||
344 | return NULL; | ||
345 | } | ||
346 | |||
347 | /* Sets basic connection info */ | ||
348 | static const char *set_log_sql_info(cmd_parms *cmd, void *dummy, | ||
349 | const char *host, const char *user, const char *pwd) | ||
350 | { | ||
351 | if (!user) { /* user is null, so only one arg passed */ | ||
352 | /* TODO: to more error checking/force all params to be set */ | ||
353 | apr_uri_t uri; | ||
354 | apr_uri_parse(cmd->pool, host, &uri); | ||
355 | if (uri.scheme) { | ||
356 | set_dbparam(cmd, NULL, "driver", uri.scheme); | ||
357 | } | ||
358 | if (uri.hostname) { | ||
359 | set_dbparam(cmd, NULL, "hostname", uri.hostname); | ||
360 | } | ||
361 | if (uri.user) { | ||
362 | set_dbparam(cmd, NULL, "username", uri.user); | ||
363 | } | ||
364 | if (uri.password) { | ||
365 | set_dbparam(cmd, NULL, "password", uri.password); | ||
366 | } | ||
367 | if (uri.port_str) { | ||
368 | set_dbparam(cmd, NULL, "port", uri.port_str); | ||
369 | } | ||
370 | if (uri.path) { | ||
371 | /* extract Database name */ | ||
372 | char *off = ap_strchr(++uri.path,'/'); | ||
373 | if (off) | ||
374 | *off='\0'; | ||
375 | set_dbparam(cmd, NULL, "database", uri.path); | ||
376 | |||
377 | } | ||
378 | } else { | ||
379 | if (*host != '.') { | ||
380 | set_dbparam(cmd, NULL, "hostname", host); | ||
381 | } | ||
382 | if (*user != '.') { | ||
383 | set_dbparam(cmd, NULL, "username", user); | ||
384 | } | ||
385 | if (*pwd != '.') { | ||
386 | set_dbparam(cmd, NULL, "password", pwd); | ||
387 | } | ||
388 | } | ||
389 | return NULL; | ||
390 | } | ||
391 | |||
392 | static const char *add_server_string_slot(cmd_parms *cmd, | ||
393 | void *struct_ptr, | ||
394 | const char *arg) | ||
395 | { | ||
396 | char **addme; | ||
397 | void *ptr = ap_get_module_config(cmd->server->module_config, | ||
398 | &log_sql_module); | ||
399 | int offset = (int)(long)cmd->info; | ||
400 | apr_array_header_t *ary = *(apr_array_header_t **)((char *)ptr + offset); | ||
401 | addme = apr_array_push(ary); | ||
402 | *addme = apr_pstrdup(ary->pool, arg); | ||
403 | |||
404 | return NULL; | ||
405 | } | ||
406 | |||
407 | /*------------------------------------------------------------* | ||
408 | * Apache-specific hooks into the module code * | ||
409 | * that are defined in the array 'mysql_lgog_module' (at EOF) * | ||
410 | *------------------------------------------------------------*/ | ||
411 | /* Closing mysql link: child_exit(1.3), pool registration(2.0) */ | ||
412 | #if defined(WITH_APACHE20) | ||
413 | static apr_status_t log_sql_close_link(void *data) | ||
414 | { | ||
415 | if (global_config.driver) | ||
416 | global_config.driver->disconnect(&global_config.db); | ||
417 | return APR_SUCCESS; | ||
418 | } | ||
419 | #elif defined(WITH_APACHE13) | ||
420 | static void log_sql_child_exit(server_rec *s, apr_pool_t *p) | ||
421 | { | ||
422 | if (global_config.driver) | ||
423 | global_config.driver->disconnect(&global_config.db); | ||
424 | } | ||
425 | #endif | ||
426 | |||
427 | /* Child Init */ | ||
428 | #if defined(WITH_APACHE20) | ||
429 | static void log_sql_child_init(apr_pool_t *p, server_rec *s) | ||
430 | #elif defined(WITH_APACHE13) | ||
431 | static void log_sql_child_init(server_rec *s, apr_pool_t *p) | ||
432 | #endif | ||
433 | { | ||
434 | logsql_opendb_ret retval; | ||
435 | # if defined(WITH_APACHE20) | ||
436 | /* Register cleanup hook to close DDB connection (apache 2 doesn't have child_exit) */ | ||
437 | apr_pool_cleanup_register(p, NULL, log_sql_close_link, log_sql_close_link); | ||
438 | # endif | ||
439 | /* Open a link to the database */ | ||
440 | retval = log_sql_opendb_link(s); | ||
441 | switch (retval) { | ||
442 | case LOGSQL_OPENDB_FAIL: | ||
443 | if (global_config.driver==NULL) { | ||
444 | log_error(APLOG_MARK, APLOG_ERR, 0, s, | ||
445 | "mod_log_sql: Driver module not loaded"); | ||
446 | } else { | ||
447 | log_error(APLOG_MARK, APLOG_ERR, 0, s, | ||
448 | "mod_log_sql: child spawned but unable to open database link"); | ||
449 | } | ||
450 | break; | ||
451 | case LOGSQL_OPENDB_SUCCESS: | ||
452 | case LOGSQL_OPENDB_ALREADY: | ||
453 | log_error(APLOG_MARK,APLOG_DEBUG,0, s, | ||
454 | "mod_log_sql: open_logdb_link successful"); | ||
455 | break; | ||
456 | case LOGSQL_OPENDB_PRESERVE: | ||
457 | log_error(APLOG_MARK,APLOG_DEBUG, 0, s, | ||
458 | "mod_log_sql: open_logdb_link said that preservation is forced"); | ||
459 | break; | ||
460 | } | ||
461 | } | ||
462 | |||
463 | /* post_config / module_init */ | ||
464 | #if defined(WITH_APACHE20) | ||
465 | static int log_sql_post_config(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s) | ||
466 | #elif defined(WITH_APACHE13) | ||
467 | static void log_sql_module_init(server_rec *s, apr_pool_t *p) | ||
468 | #endif | ||
469 | { | ||
470 | /* TODO: Add local_address, remote_address, server_name, connection_status */ | ||
471 | /* Register handlers */ | ||
472 | log_sql_register_item(s,p,'A', extract_agent, "agent", 1, 1); | ||
473 | log_sql_register_item(s,p,'a', extract_request_query, "request_args", 1, 1); | ||
474 | log_sql_register_item(s,p,'b', extract_bytes_sent, "bytes_sent", 0, 0); | ||
475 | log_sql_register_item(s,p,'c', extract_cookie, "cookie", 0, 1); | ||
476 | /* TODO: Document */ | ||
477 | log_sql_register_item(s,p,'f', extract_request_file, "request_file", 0, 1); | ||
478 | log_sql_register_item(s,p,'H', extract_request_protocol, "request_protocol", 0, 1); | ||
479 | log_sql_register_item(s,p,'h', extract_remote_host, "remote_host", 0, 1); | ||
480 | log_sql_register_item(s,p,'I', extract_unique_id, "id", 0, 1); | ||
481 | log_sql_register_item(s,p,'l', extract_remote_logname, "remote_logname", 0, 1); | ||
482 | log_sql_register_item(s,p,'m', extract_request_method, "request_method", 0, 1); | ||
483 | log_sql_register_item(s,p,'M', extract_machine_id, "machine_id", 0, 1); | ||
484 | log_sql_register_item(s,p,'P', extract_child_pid, "child_pid", 0, 0); | ||
485 | log_sql_register_item(s,p,'p', extract_server_port, "server_port", 0, 0); | ||
486 | log_sql_register_item(s,p,'R', extract_referer, "referer", 1, 1); | ||
487 | log_sql_register_item(s,p,'r', extract_request_line, "request_line", 1, 1); | ||
488 | log_sql_register_item(s,p,'S', extract_request_timestamp, "time_stamp", 0, 0); | ||
489 | log_sql_register_item(s,p,'s', extract_status, "status", 1, 0); | ||
490 | log_sql_register_item(s,p,'T', extract_request_duration, "request_duration", 1, 0); | ||
491 | log_sql_register_item(s,p,'t', extract_request_time, "request_time", 0, 1); | ||
492 | log_sql_register_item(s,p,'u', extract_remote_user, "remote_user", 0, 1); | ||
493 | log_sql_register_item(s,p,'U', extract_request_uri, "request_uri", 1, 1); | ||
494 | log_sql_register_item(s,p,'v', extract_virtual_host, "virtual_host", 0, 1); | ||
495 | log_sql_register_item(s,p,'V', extract_server_name, "virtual_host", 0, 1); | ||
496 | |||
497 | if (global_config.announce) { | ||
498 | ap_add_version_component(p, PACKAGE_NAME"/"PACKAGE_VERSION); | ||
499 | } | ||
500 | /* ap_server_root_relative any default preserve file locations */ | ||
501 | { | ||
502 | server_rec *cur_s; | ||
503 | const char *default_p = ap_server_root_relative(p, DEFAULT_PRESERVE_FILE); | ||
504 | for (cur_s = s; cur_s != NULL; cur_s= cur_s->next) { | ||
505 | logsql_state *cls = ap_get_module_config(cur_s->module_config, | ||
506 | &log_sql_module); | ||
507 | if (cls->preserve_file == DEFAULT_PRESERVE_FILE) | ||
508 | cls->preserve_file = default_p; | ||
509 | } | ||
510 | } | ||
511 | global_config.db.p = p; | ||
512 | |||
513 | #if defined(WITH_APACHE20) | ||
514 | return OK; | ||
515 | #endif | ||
516 | } | ||
517 | |||
518 | /* This function handles calling the DB module, handling errors | ||
519 | * of missing tables and lost DB connections, and falling back to | ||
520 | * preserving the DB query. | ||
521 | * | ||
522 | * Parms: request record, table type, table name, and the full SQL command | ||
523 | */ | ||
524 | |||
525 | static logsql_query_ret safe_sql_insert(request_rec *r, logsql_tabletype table_type, | ||
526 | const char *table_name, const char *query) { | ||
527 | |||
528 | logsql_query_ret result; | ||
529 | logsql_state *cls = ap_get_module_config(r->server->module_config, | ||
530 | &log_sql_module); | ||
531 | |||
532 | if (!global_config.db.connected || global_config.driver == NULL) { | ||
533 | /* preserve query */ | ||
534 | return LOGSQL_QUERY_NOLINK; | ||
535 | } | ||
536 | |||
537 | result = global_config.driver->insert(r,&global_config.db,query); | ||
538 | |||
539 | /* If we ran the query and it returned an error, try to be robust. | ||
540 | * (After all, the module thought it had a valid mysql_log connection but the query | ||
541 | * could have failed for a number of reasons, so we have to be extra-safe and check.) */ | ||
542 | switch (result) { | ||
543 | case LOGSQL_QUERY_SUCCESS: | ||
544 | return LOGSQL_QUERY_SUCCESS; | ||
545 | case LOGSQL_QUERY_NOLINK: | ||
546 | return LOGSQL_QUERY_FAIL; | ||
547 | /* TODO: What do we do here */ | ||
548 | case LOGSQL_QUERY_FAIL: | ||
549 | global_config.driver->disconnect(&global_config.db); | ||
550 | global_config.db.connected = 0; | ||
551 | /* re-open the connection and try again */ | ||
552 | if (log_sql_opendb_link(r->server) != LOGSQL_OPENDB_FAIL) { | ||
553 | log_error(APLOG_MARK,APLOG_NOTICE,0, r->server,"db reconnect successful"); | ||
554 | # if defined(WITH_APACHE20) | ||
555 | apr_sleep(apr_time_from_sec(0.25)); /* pause for a quarter second */ | ||
556 | # elif defined(WITH_APACHE13) | ||
557 | # if defined(WIN32) | ||
558 | Sleep((DWORD)0.25); | ||
559 | # else | ||
560 | { | ||
561 | struct timespec delay, remainder; | ||
562 | int nanoret; | ||
563 | delay.tv_sec = 0; | ||
564 | delay.tv_nsec = 250000000; /* pause for a quarter second */ | ||
565 | nanoret = nanosleep(&delay, &remainder); | ||
566 | if (nanoret && errno != EINTR) { | ||
567 | log_error(APLOG_MARK,APLOG_ERR, errno, r->server,"nanosleep unsuccessful"); | ||
568 | } | ||
569 | } | ||
570 | # endif /* win32 */ | ||
571 | # endif | ||
572 | result = global_config.driver->insert(r,&global_config.db,query); | ||
573 | if (result == LOGSQL_QUERY_SUCCESS) { | ||
574 | return LOGSQL_QUERY_SUCCESS; | ||
575 | } else { | ||
576 | log_error(APLOG_MARK,APLOG_ERR,0,r->server,"second attempt failed"); | ||
577 | preserve_entry(r, query); | ||
578 | return LOGSQL_QUERY_PRESERVED; | ||
579 | } | ||
580 | } else { | ||
581 | log_error(APLOG_MARK,APLOG_ERR,0,r->server, | ||
582 | "reconnect failed, unable to reach database. SQL logging stopped until child regains a db connection."); | ||
583 | log_error(APLOG_MARK,APLOG_ERR,0,r->server, | ||
584 | "log entries are being preserved in %s",cls->preserve_file); | ||
585 | preserve_entry(r, query); | ||
586 | return LOGSQL_QUERY_PRESERVED; | ||
587 | } | ||
588 | break; | ||
589 | case LOGSQL_QUERY_NOTABLE: | ||
590 | if (global_config.createtables) { | ||
591 | log_error(APLOG_MARK,APLOG_ERR,0,r->server, | ||
592 | "table doesn't exist...creating now"); | ||
593 | if ((result = global_config.driver->create_table(r, &global_config.db, table_type, | ||
594 | table_name))!=LOGSQL_TABLE_SUCCESS) { | ||
595 | log_error(APLOG_MARK,APLOG_ERR,result,r->server, | ||
596 | "child attempted but failed to create one or more tables for %s, preserving query", ap_get_server_name(r)); | ||
597 | preserve_entry(r, query); | ||
598 | return LOGSQL_QUERY_PRESERVED; | ||
599 | } else { | ||
600 | log_error(APLOG_MARK,APLOG_ERR,result, r->server, | ||
601 | "tables successfully created - retrying query"); | ||
602 | if ((result = global_config.driver->insert(r,&global_config.db,query))!=LOGSQL_QUERY_SUCCESS) { | ||
603 | log_error(APLOG_MARK,APLOG_ERR,result, r->server, | ||
604 | "giving up, preserving query"); | ||
605 | preserve_entry(r, query); | ||
606 | return LOGSQL_QUERY_PRESERVED; | ||
607 | } else { | ||
608 | log_error(APLOG_MARK,APLOG_NOTICE,0, r->server, | ||
609 | "query successful after table creation"); | ||
610 | return LOGSQL_QUERY_SUCCESS; | ||
611 | } | ||
612 | } | ||
613 | } else { | ||
614 | log_error(APLOG_MARK,APLOG_ERR,0,r->server, | ||
615 | "table doesn't exist, creation denied by configuration, preserving query"); | ||
616 | preserve_entry(r, query); | ||
617 | return LOGSQL_QUERY_PRESERVED; | ||
618 | } | ||
619 | break; | ||
620 | default: | ||
621 | log_error(APLOG_MARK,APLOG_ERR,0, r->server, | ||
622 | "Invalid return code from mog_log_query"); | ||
623 | return LOGSQL_QUERY_FAIL; | ||
624 | break; | ||
625 | } | ||
626 | return LOGSQL_QUERY_FAIL; | ||
627 | } | ||
628 | |||
629 | /* This function gets called to create a per-server configuration | ||
630 | * record. It will always be called for the main server and | ||
631 | * for each virtual server that is established. Each server maintains | ||
632 | * its own state that is separate from the others' states. | ||
633 | * | ||
634 | * The return value is a pointer to the created module-specific | ||
635 | * structure. | ||
636 | */ | ||
637 | static void *log_sql_make_state(apr_pool_t *p, server_rec *s) | ||
638 | { | ||
639 | logsql_state *cls = (logsql_state *) apr_pcalloc(p, sizeof(logsql_state)); | ||
640 | |||
641 | /* These defaults are overridable in the httpd.conf file. */ | ||
642 | cls->transfer_log_format = DEFAULT_TRANSFER_LOG_FMT; | ||
643 | apr_pool_create(&cls->parsed_pool, p); | ||
644 | cls->parsed_log_format = apr_pcalloc(cls->parsed_pool, | ||
645 | strlen(cls->transfer_log_format) * sizeof(logsql_item *)); | ||
646 | cls->notes_table_name = DEFAULT_NOTES_TABLE_NAME; | ||
647 | cls->hin_table_name = DEFAULT_HIN_TABLE_NAME; | ||
648 | cls->hout_table_name = DEFAULT_HOUT_TABLE_NAME; | ||
649 | cls->cookie_table_name = DEFAULT_COOKIE_TABLE_NAME; | ||
650 | cls->preserve_file = DEFAULT_PRESERVE_FILE; | ||
651 | |||
652 | cls->transfer_ignore_list = apr_array_make(p, 1, sizeof(char *)); | ||
653 | cls->transfer_accept_list = apr_array_make(p, 1, sizeof(char *)); | ||
654 | cls->remhost_ignore_list = apr_array_make(p, 1, sizeof(char *)); | ||
655 | cls->notes_list = apr_array_make(p, 1, sizeof(char *)); | ||
656 | cls->hin_list = apr_array_make(p, 1, sizeof(char *)); | ||
657 | cls->hout_list = apr_array_make(p, 1, sizeof(char *)); | ||
658 | cls->cookie_list = apr_array_make(p, 1, sizeof(char *)); | ||
659 | |||
660 | return (void *) cls; | ||
661 | } | ||
662 | |||
663 | |||
664 | /* Iterates through an array of char* and searches for a matching element | ||
665 | * Returns 0 if not found, 1 if found */ | ||
666 | static int in_array(apr_array_header_t *ary, const char *elem) | ||
667 | { | ||
668 | int itr; | ||
669 | for (itr = 0; itr < ary->nelts; itr++) { | ||
670 | if (!strcmp(elem,((char **)ary->elts)[itr])) { | ||
671 | return 1; | ||
672 | } | ||
673 | } | ||
674 | return 0; | ||
675 | } | ||
676 | |||
677 | |||
678 | /* Parse through cookie lists and merge based on +/- prefixes */ | ||
679 | /* TODO: rewrite as a function */ | ||
680 | #define DO_MERGE_ARRAY(parent,child,pool) \ | ||
681 | if (apr_is_empty_array(child)) { \ | ||
682 | apr_array_cat(child, parent); \ | ||
683 | } else { \ | ||
684 | apr_array_header_t *addlist, *dellist; \ | ||
685 | char **elem, **ptr = (char **)(child->elts); \ | ||
686 | int itr, overwrite = 0; \ | ||
687 | addlist = apr_array_make(pool,5,sizeof(char *)); \ | ||
688 | dellist = apr_array_make(subp,5,sizeof(char *)); \ | ||
689 | \ | ||
690 | for (itr=0; itr<child->nelts; itr++) { \ | ||
691 | if (*ptr[itr] == '+') { \ | ||
692 | elem = (char **)apr_array_push(addlist); \ | ||
693 | *elem = (ptr[itr]+1); \ | ||
694 | } else if (*ptr[itr] == '-') { \ | ||
695 | elem = (char **)apr_array_push(dellist); \ | ||
696 | *elem = (ptr[itr]+1); \ | ||
697 | } else { \ | ||
698 | overwrite = 1; \ | ||
699 | elem = (char **)apr_array_push(addlist); \ | ||
700 | *elem = ptr[itr]; \ | ||
701 | } \ | ||
702 | } \ | ||
703 | child = apr_array_make(p,1,sizeof(char *)); \ | ||
704 | ptr = (char **)(parent->elts); \ | ||
705 | if (overwrite==0) { \ | ||
706 | /* if we are not overwriting the existing then prepare for merge */ \ | ||
707 | for (itr=0; itr<parent->nelts; itr++) { \ | ||
708 | if (!in_array(addlist, ptr[itr]) && !in_array(dellist,ptr[itr])) { \ | ||
709 | elem = apr_array_push(child); \ | ||
710 | *elem = apr_pstrdup(p, ptr[itr]); \ | ||
711 | } \ | ||
712 | } \ | ||
713 | } \ | ||
714 | apr_array_cat(child, addlist); \ | ||
715 | } | ||
716 | |||
717 | static void *log_sql_merge_state(apr_pool_t *p, void *basev, void *addv) | ||
718 | { | ||
719 | /* Fetch the two states to merge */ | ||
720 | logsql_state *parent = (logsql_state *) basev; | ||
721 | logsql_state *child = (logsql_state *) addv; | ||
722 | |||
723 | apr_pool_t *subp; | ||
724 | |||
725 | apr_pool_create(&subp,p); | ||
726 | |||
727 | /* Child can override these, otherwise they default to parent's choice. | ||
728 | * If the parent didn't set them, create reasonable defaults for the | ||
729 | * ones that should have such default settings. Leave the others null. */ | ||
730 | |||
731 | /* No default for transfer_table_name because we want its absence | ||
732 | * to disable logging. */ | ||
733 | if (!child->transfer_table_name) { | ||
734 | child->transfer_table_name = parent->transfer_table_name; | ||
735 | } | ||
736 | |||
737 | if (child->transfer_log_format == DEFAULT_TRANSFER_LOG_FMT) { | ||
738 | child->transfer_log_format = parent->transfer_log_format; | ||
739 | /*apr_pool_clear(child->parsed_pool);*/ | ||
740 | child->parsed_log_format = apr_pcalloc(child->parsed_pool, | ||
741 | strlen(child->transfer_log_format) * sizeof(logsql_item *)); | ||
742 | } | ||
743 | |||
744 | if (child->preserve_file == DEFAULT_PRESERVE_FILE) | ||
745 | child->preserve_file = parent->preserve_file; | ||
746 | /* server_root_relative the preserve file location */ | ||
747 | if (child->preserve_file == DEFAULT_PRESERVE_FILE) | ||
748 | child->preserve_file = ap_server_root_relative(p, DEFAULT_PRESERVE_FILE); | ||
749 | |||
750 | if (child->notes_table_name == DEFAULT_NOTES_TABLE_NAME) | ||
751 | child->notes_table_name = parent->notes_table_name; | ||
752 | |||
753 | if (child->hin_table_name == DEFAULT_HIN_TABLE_NAME) | ||
754 | child->hin_table_name = parent->hin_table_name; | ||
755 | |||
756 | if (child->hout_table_name == DEFAULT_HOUT_TABLE_NAME) | ||
757 | child->hout_table_name = parent->hout_table_name; | ||
758 | |||
759 | if (child->cookie_table_name == DEFAULT_COOKIE_TABLE_NAME) | ||
760 | child->cookie_table_name = parent->cookie_table_name; | ||
761 | |||
762 | DO_MERGE_ARRAY(parent->transfer_ignore_list, child->transfer_ignore_list, subp); | ||
763 | DO_MERGE_ARRAY(parent->transfer_accept_list, child->transfer_accept_list, subp); | ||
764 | DO_MERGE_ARRAY(parent->remhost_ignore_list, child->remhost_ignore_list, subp); | ||
765 | DO_MERGE_ARRAY(parent->notes_list, child->notes_list, subp); | ||
766 | DO_MERGE_ARRAY(parent->hin_list, child->hin_list, subp); | ||
767 | DO_MERGE_ARRAY(parent->hout_list, child->hout_list, subp); | ||
768 | DO_MERGE_ARRAY(parent->cookie_list,child->cookie_list, subp); | ||
769 | |||
770 | apr_pool_destroy(subp); | ||
771 | |||
772 | if (!child->cookie_name) | ||
773 | child->cookie_name = parent->cookie_name; | ||
774 | |||
775 | |||
776 | return (void*) child; | ||
777 | } | ||
778 | |||
779 | /* Routine to perform the actual construction and execution of the relevant | ||
780 | * INSERT statements. | ||
781 | */ | ||
782 | static int log_sql_transaction(request_rec *orig) | ||
783 | { | ||
784 | char **ptrptr, **ptrptr2; | ||
785 | logsql_state *cls = ap_get_module_config(orig->server->module_config, &log_sql_module); | ||
786 | const char *access_query; | ||
787 | request_rec *r; | ||
788 | const char *transfer_tablename = cls->transfer_table_name; | ||
789 | const char *notes_tablename = cls->notes_table_name; | ||
790 | const char *hout_tablename = cls->hout_table_name; | ||
791 | const char *hin_tablename = cls->hin_table_name; | ||
792 | const char *cookie_tablename = cls->cookie_table_name; | ||
793 | if (global_config.driver == NULL) { | ||
794 | return OK; | ||
795 | } | ||
796 | /* We handle mass virtual hosting differently. Dynamically determine the name | ||
797 | * of the table from the virtual server's name, and flag it for creation. | ||
798 | */ | ||
799 | if (global_config.massvirtual) { | ||
800 | /* TODO: Make these configurable? */ | ||
801 | char *access_base = "access_"; | ||
802 | char *notes_base = "notes_"; | ||
803 | char *hout_base = "headout_"; | ||
804 | char *hin_base = "headin_"; | ||
805 | char *cookie_base = "cookies_"; | ||
806 | |||
807 | |||
808 | /* Determine the hostname and convert it to all lower-case; */ | ||
809 | char *servername = apr_pstrdup(orig->pool,(char *)ap_get_server_name(orig)); | ||
810 | |||
811 | char *p=servername; | ||
812 | while (*p) { | ||
813 | *p = apr_tolower(*p); | ||
814 | if (*p == '.') *p = '_'; | ||
815 | if (*p == '-') *p = '_'; | ||
816 | ++p; | ||
817 | } | ||
818 | |||
819 | /* Find memory long enough to hold the table name + \0. */ | ||
820 | transfer_tablename = apr_pstrcat(orig->pool, access_base, servername, NULL); | ||
821 | notes_tablename = apr_pstrcat(orig->pool, notes_base, servername, NULL); | ||
822 | hin_tablename = apr_pstrcat(orig->pool, hin_base, servername, NULL); | ||
823 | hout_tablename = apr_pstrcat(orig->pool, hout_base, servername, NULL); | ||
824 | cookie_tablename = apr_pstrcat(orig->pool, cookie_base, servername, NULL); | ||
825 | |||
826 | /* Tell this virtual server its transfer table name, and | ||
827 | * turn on create_tables, which is implied by massvirtual. | ||
828 | */ | ||
829 | |||
830 | global_config.createtables = 1; | ||
831 | } | ||
832 | |||
833 | /* Do we have enough info to log? */ | ||
834 | if (!transfer_tablename) { | ||
835 | return DECLINED; | ||
836 | } else { | ||
837 | const char *thehost; | ||
838 | const char *theitem; | ||
839 | char *fields = "", *values = ""; | ||
840 | char *itemsets = ""; | ||
841 | char *note_query = NULL; | ||
842 | char *hin_query = NULL; | ||
843 | char *hout_query = NULL; | ||
844 | char *cookie_query = NULL; | ||
845 | const char *unique_id; | ||
846 | const char *formatted_item; | ||
847 | int i,length; | ||
848 | int proceed; | ||
849 | |||
850 | for (r = orig; r->next; r = r->next) { | ||
851 | continue; | ||
852 | } | ||
853 | |||
854 | /* The following is a stolen upsetting mess of pointers, I'm sorry. | ||
855 | * Anyone with the motiviation and/or the time should feel free | ||
856 | * to make this cleaner. :) */ | ||
857 | ptrptr2 = (char **) (cls->transfer_accept_list->elts + (cls->transfer_accept_list->nelts * cls->transfer_accept_list->elt_size)); | ||
858 | |||
859 | /* Go through each element of the accept list and compare it to the | ||
860 | * request_uri. If we don't get a match, return without logging */ | ||
861 | if ((r->uri) && (cls->transfer_accept_list->nelts)) { | ||
862 | proceed = 0; | ||
863 | for (ptrptr = (char **) cls->transfer_accept_list->elts; ptrptr < ptrptr2; ptrptr = (char **) ((char *) ptrptr + cls->transfer_accept_list->elt_size)) | ||
864 | if (ap_strstr(r->uri, *ptrptr)) { | ||
865 | proceed = 1; | ||
866 | break; | ||
867 | } | ||
868 | if (!proceed) | ||
869 | return OK; | ||
870 | } | ||
871 | |||
872 | /* Go through each element of the ignore list and compare it to the | ||
873 | * request_uri. If we get a match, return without logging */ | ||
874 | ptrptr2 = (char **) (cls->transfer_ignore_list->elts + (cls->transfer_ignore_list->nelts * cls->transfer_ignore_list->elt_size)); | ||
875 | if (r->uri) { | ||
876 | for (ptrptr = (char **) cls->transfer_ignore_list->elts; ptrptr < ptrptr2; ptrptr = (char **) ((char *) ptrptr + cls->transfer_ignore_list->elt_size)) | ||
877 | if (ap_strstr(r->uri, *ptrptr)) { | ||
878 | return OK; | ||
879 | } | ||
880 | } | ||
881 | |||
882 | /* Go through each element of the ignore list and compare it to the | ||
883 | * remote host. If we get a match, return without logging */ | ||
884 | ptrptr2 = (char **) (cls->remhost_ignore_list->elts + (cls->remhost_ignore_list->nelts * cls->remhost_ignore_list->elt_size)); | ||
885 | thehost = ap_get_remote_host(r->connection, r->per_dir_config, REMOTE_NAME, NULL); | ||
886 | if (thehost) { | ||
887 | for (ptrptr = (char **) cls->remhost_ignore_list->elts; ptrptr < ptrptr2; ptrptr = (char **) ((char *) ptrptr + cls->remhost_ignore_list->elt_size)) | ||
888 | if (ap_strstr(thehost, *ptrptr)) { | ||
889 | return OK; | ||
890 | } | ||
891 | } | ||
892 | |||
893 | |||
894 | /* Iterate through the format characters and set up the INSERT string according to | ||
895 | * what the user has configured. */ | ||
896 | length = strlen(cls->transfer_log_format); | ||
897 | for (i = 0; i<length; i++) { | ||
898 | logsql_item *item = cls->parsed_log_format[i]; | ||
899 | if (item==NULL) { | ||
900 | log_error(APLOG_MARK, APLOG_ERR, 0, orig->server, | ||
901 | "Log Format '%c' unknown",cls->transfer_log_format[i]); | ||
902 | continue; | ||
903 | } | ||
904 | |||
905 | /* Yes, this key is one of the configured keys. | ||
906 | * Call the key's function and put the returned value into 'formatted_item' */ | ||
907 | formatted_item = item->func(item->want_orig_default ? orig : r, ""); | ||
908 | |||
909 | /* Massage 'formatted_item' for proper SQL eligibility... */ | ||
910 | if (!formatted_item) { | ||
911 | formatted_item = ""; | ||
912 | } else if (formatted_item[0] == '-' && formatted_item[1] == '\0' && !item->string_contents) { | ||
913 | /* If apache tried to log a '-' character for a numeric field, convert that to a zero | ||
914 | * because the database expects a numeral and will reject the '-' character. */ | ||
915 | formatted_item = "0"; | ||
916 | } | ||
917 | |||
918 | /* Append the fieldname and value-to-insert to the appropriate strings, quoting stringvals with ' as appropriate */ | ||
919 | fields = apr_pstrcat(r->pool, fields, (i ? "," : ""), | ||
920 | item->sql_field_name, NULL); | ||
921 | values = apr_pstrcat(r->pool, values, (i ? "," : ""), | ||
922 | global_config.driver->escape(formatted_item, r->pool,&global_config.db), NULL); | ||
923 | } | ||
924 | |||
925 | /* Work through the list of notes defined by LogSQLWhichNotes */ | ||
926 | i = 0; | ||
927 | unique_id = extract_unique_id(r, ""); | ||
928 | |||
929 | ptrptr2 = (char **) (cls->notes_list->elts + (cls->notes_list->nelts * cls->notes_list->elt_size)); | ||
930 | for (ptrptr = (char **) cls->notes_list->elts; ptrptr < ptrptr2; ptrptr = (char **) ((char *) ptrptr + cls->notes_list->elt_size)) { | ||
931 | /* If the specified note (*ptrptr) exists for the current request... */ | ||
932 | if ((theitem = apr_table_get(r->notes, *ptrptr))) { | ||
933 | itemsets = apr_pstrcat(r->pool, itemsets, | ||
934 | (i > 0 ? "," : ""), | ||
935 | "(", | ||
936 | global_config.driver->escape(unique_id, r->pool, &global_config.db), | ||
937 | ",", | ||
938 | global_config.driver->escape(*ptrptr, r->pool,&global_config.db), | ||
939 | ",", | ||
940 | global_config.driver->escape(theitem, r->pool,&global_config.db), | ||
941 | ")", | ||
942 | NULL); | ||
943 | i++; | ||
944 | } | ||
945 | } | ||
946 | if ( *itemsets != '\0' ) { | ||
947 | note_query = apr_psprintf(r->pool, "insert %s into %s (id, item, val) values %s", | ||
948 | /*global_config.insertdelayed?"delayed":*/"", notes_tablename, itemsets); | ||
949 | |||
950 | log_error(APLOG_MARK,APLOG_DEBUG,0, orig->server,"mod_log_sql: note string: %s", note_query); | ||
951 | } | ||
952 | |||
953 | /* Work through the list of headers-out defined by LogSQLWhichHeadersOut*/ | ||
954 | i = 0; | ||
955 | itemsets = ""; | ||
956 | |||
957 | ptrptr2 = (char **) (cls->hout_list->elts + (cls->hout_list->nelts * cls->hout_list->elt_size)); | ||
958 | for (ptrptr = (char **) cls->hout_list->elts; ptrptr < ptrptr2; ptrptr = (char **) ((char *) ptrptr + cls->hout_list->elt_size)) { | ||
959 | /* If the specified header (*ptrptr) exists for the current request... */ | ||
960 | if ((theitem = apr_table_get(r->headers_out, *ptrptr))) { | ||
961 | itemsets = apr_pstrcat(r->pool, itemsets, | ||
962 | (i > 0 ? "," : ""), | ||
963 | "(", | ||
964 | global_config.driver->escape(unique_id, r->pool, &global_config.db), | ||
965 | ",", | ||
966 | global_config.driver->escape(*ptrptr, r->pool,&global_config.db), | ||
967 | ",", | ||
968 | global_config.driver->escape(theitem, r->pool,&global_config.db), | ||
969 | ")", | ||
970 | NULL); | ||
971 | i++; | ||
972 | } | ||
973 | } | ||
974 | if ( *itemsets != '\0' ) { | ||
975 | hout_query = apr_psprintf(r->pool, "insert %s into %s (id, item, val) values %s", | ||
976 | /*global_config.insertdelayed?"delayed":*/"", hout_tablename, itemsets); | ||
977 | |||
978 | log_error(APLOG_MARK,APLOG_DEBUG,0, orig->server,"mod_log_sql: header_out string: %s", hout_query); | ||
979 | } | ||
980 | |||
981 | |||
982 | /* Work through the list of headers-in defined by LogSQLWhichHeadersIn */ | ||
983 | i = 0; | ||
984 | itemsets = ""; | ||
985 | |||
986 | ptrptr2 = (char **) (cls->hin_list->elts + (cls->hin_list->nelts * cls->hin_list->elt_size)); | ||
987 | for (ptrptr = (char **) cls->hin_list->elts; ptrptr < ptrptr2; ptrptr = (char **) ((char *) ptrptr + cls->hin_list->elt_size)) { | ||
988 | /* If the specified header (*ptrptr) exists for the current request... */ | ||
989 | if ((theitem = apr_table_get(r->headers_in, *ptrptr))) { | ||
990 | itemsets = apr_pstrcat(r->pool, itemsets, | ||
991 | (i > 0 ? "," : ""), | ||
992 | "(", | ||
993 | global_config.driver->escape(unique_id, r->pool, &global_config.db), | ||
994 | ",", | ||
995 | global_config.driver->escape(*ptrptr, r->pool,&global_config.db), | ||
996 | ",", | ||
997 | global_config.driver->escape(theitem, r->pool,&global_config.db), | ||
998 | ")", | ||
999 | NULL); | ||
1000 | i++; | ||
1001 | } | ||
1002 | } | ||
1003 | if ( *itemsets != '\0' ) { | ||
1004 | hin_query = apr_psprintf(r->pool, "insert %s into %s (id, item, val) values %s", | ||
1005 | /*global_config.insertdelayed?"delayed":*/"", hin_tablename, itemsets); | ||
1006 | |||
1007 | log_error(APLOG_MARK,APLOG_DEBUG,0, orig->server,"mod_log_sql: header_in string: %s", hin_query); | ||
1008 | } | ||
1009 | |||
1010 | |||
1011 | /* Work through the list of cookies defined by LogSQLWhichCookies */ | ||
1012 | i = 0; | ||
1013 | itemsets = ""; | ||
1014 | |||
1015 | ptrptr2 = (char **) (cls->cookie_list->elts + (cls->cookie_list->nelts * cls->cookie_list->elt_size)); | ||
1016 | for (ptrptr = (char **) cls->cookie_list->elts; ptrptr < ptrptr2; ptrptr = (char **) ((char *) ptrptr + cls->cookie_list->elt_size)) { | ||
1017 | /* If the specified cookie (*ptrptr) exists for the current request... */ | ||
1018 | if ( strncmp((theitem = extract_specific_cookie(r, *ptrptr)), "-", 1) ) { | ||
1019 | itemsets = apr_pstrcat(r->pool, itemsets, | ||
1020 | (i > 0 ? "," : ""), | ||
1021 | "(", | ||
1022 | global_config.driver->escape(unique_id, r->pool, &global_config.db), | ||
1023 | ",", | ||
1024 | global_config.driver->escape(*ptrptr, r->pool,&global_config.db), | ||
1025 | ",", | ||
1026 | global_config.driver->escape(theitem, r->pool,&global_config.db), | ||
1027 | ")", | ||
1028 | NULL); | ||
1029 | i++; | ||
1030 | } | ||
1031 | |||
1032 | } | ||
1033 | if ( *itemsets != '\0' ) { | ||
1034 | cookie_query = apr_psprintf(r->pool, "insert %s into %s (id, item, val) values %s", | ||
1035 | /*global_config.insertdelayed?"delayed":*/"", cookie_tablename, itemsets); | ||
1036 | |||
1037 | log_error(APLOG_MARK,APLOG_DEBUG,0, orig->server,"mod_log_sql: cookie string: %s", cookie_query); | ||
1038 | } | ||
1039 | |||
1040 | |||
1041 | /* Set up the actual INSERT statement */ | ||
1042 | access_query = apr_psprintf(r->pool, "insert %s into %s (%s) values (%s)", | ||
1043 | /*global_config.insertdelayed?"delayed":*/"", transfer_tablename, fields, values); | ||
1044 | |||
1045 | log_error(APLOG_MARK,APLOG_DEBUG,0, r->server,"mod_log_sql: access string: %s", access_query); | ||
1046 | |||
1047 | /* If the person activated force-preserve, go ahead and push all the entries | ||
1048 | * into the preserve file, then return. | ||
1049 | */ | ||
1050 | if (global_config.forcepreserve) { | ||
1051 | log_error(APLOG_MARK,APLOG_DEBUG,0, orig->server,"mod_log_sql: preservation forced"); | ||
1052 | preserve_entry(orig, access_query); | ||
1053 | if ( note_query != NULL ) | ||
1054 | preserve_entry(orig, note_query); | ||
1055 | if ( hin_query != NULL ) | ||
1056 | preserve_entry(orig, hin_query); | ||
1057 | if ( hout_query != NULL ) | ||
1058 | preserve_entry(orig, hout_query); | ||
1059 | if ( cookie_query != NULL ) | ||
1060 | preserve_entry(orig, cookie_query); | ||
1061 | return OK; | ||
1062 | } | ||
1063 | |||
1064 | /* How's our mysql link integrity? */ | ||
1065 | if (!global_config.db.connected) { | ||
1066 | if (!global_config.forcepreserve) { | ||
1067 | /* Make a try to establish the link */ | ||
1068 | log_sql_opendb_link(r->server); | ||
1069 | } | ||
1070 | if (!global_config.db.connected) { | ||
1071 | /* Unable to re-establish a DB link, so assume that it's really | ||
1072 | * gone and send the entry to the preserve file instead. | ||
1073 | * This short-circuits safe_sql_query() during a db outage and therefore | ||
1074 | * we don't keep logging the db error over and over. | ||
1075 | */ | ||
1076 | preserve_entry(orig, access_query); | ||
1077 | if ( note_query != NULL ) | ||
1078 | preserve_entry(orig, note_query); | ||
1079 | if ( hin_query != NULL ) | ||
1080 | preserve_entry(orig, hin_query); | ||
1081 | if ( hout_query != NULL ) | ||
1082 | preserve_entry(orig, hout_query); | ||
1083 | if ( cookie_query != NULL ) | ||
1084 | preserve_entry(orig, cookie_query); | ||
1085 | |||
1086 | return OK; | ||
1087 | } else { | ||
1088 | /* Whew, we got the DB link back */ | ||
1089 | log_error(APLOG_MARK,APLOG_NOTICE,0, orig->server,"mod_log_sql: child established database connection"); | ||
1090 | } | ||
1091 | } | ||
1092 | |||
1093 | |||
1094 | /* ---> So as of here we have a non-null value of mysql_log. <--- */ | ||
1095 | /* ---> i.e. we have a good MySQL connection. <--- */ | ||
1096 | |||
1097 | /* Make the access-table insert */ | ||
1098 | safe_sql_insert(orig,LOGSQL_TABLE_ACCESS,transfer_tablename,access_query); | ||
1099 | |||
1100 | /* Log the optional notes, headers, etc. */ | ||
1101 | if (note_query) | ||
1102 | safe_sql_insert(orig, LOGSQL_TABLE_NOTES,notes_tablename,note_query); | ||
1103 | |||
1104 | if (hout_query) | ||
1105 | safe_sql_insert(orig, LOGSQL_TABLE_HEADERSOUT,hout_tablename,hout_query); | ||
1106 | |||
1107 | if (hin_query) | ||
1108 | safe_sql_insert(orig, LOGSQL_TABLE_HEADERSIN,hin_tablename,hin_query); | ||
1109 | |||
1110 | if (cookie_query) | ||
1111 | safe_sql_insert(orig, LOGSQL_TABLE_COOKIES,cookie_tablename,cookie_query); | ||
1112 | |||
1113 | return OK; | ||
1114 | } | ||
1115 | } | ||
1116 | |||
1117 | |||
1118 | /* Setup of the available httpd.conf configuration commands. | ||
1119 | * Structure: command, function called, NULL, where available, how many arguments, verbose description | ||
1120 | */ | ||
1121 | static const command_rec log_sql_cmds[] = { | ||
1122 | AP_INIT_FLAG("LogSQLAnnounce", set_global_flag_slot, | ||
1123 | (void *)APR_OFFSETOF(global_config_t, announce), RSRC_CONF, | ||
1124 | "Whether to announce that mod_log_sql is loaded in the server header") | ||
1125 | , | ||
1126 | /* DB connection parameters */ | ||
1127 | AP_INIT_TAKE13("LogSQLLoginInfo", set_log_sql_info, NULL, RSRC_CONF, | ||
1128 | "The database connection URI in the form "driver://user:password@hostname:port/database"") | ||
1129 | , | ||
1130 | AP_INIT_TAKE2("LogSQLDBParam", set_dbparam, NULL, RSRC_CONF, | ||
1131 | "First argument is the DB parameter, second is the value to assign") | ||
1132 | , | ||
1133 | AP_INIT_FLAG("LogSQLForcePreserve", set_global_flag_slot, | ||
1134 | (void *)APR_OFFSETOF(global_config_t, forcepreserve), RSRC_CONF, | ||
1135 | "Forces logging to preserve file and bypasses database") | ||
1136 | , | ||
1137 | AP_INIT_FLAG("LogSQLDisablePreserve", set_global_flag_slot, | ||
1138 | (void *)APR_OFFSETOF(global_config_t, disablepreserve), RSRC_CONF, | ||
1139 | "Completely disables use of the preserve file") | ||
1140 | , | ||
1141 | AP_INIT_TAKE1("LogSQLPreserveFile", set_server_file_slot, | ||
1142 | (void *)APR_OFFSETOF(logsql_state,preserve_file), RSRC_CONF, | ||
1143 | "Name of the file to use for data preservation during database downtime") | ||
1144 | , | ||
1145 | AP_INIT_FLAG("LogSQLCreateTables", set_global_nmv_flag_slot, | ||
1146 | (void *)APR_OFFSETOF(global_config_t, createtables), RSRC_CONF, | ||
1147 | "Turn on module's capability to create its SQL tables on the fly") | ||
1148 | , | ||
1149 | /* Table names */ | ||
1150 | AP_INIT_FLAG("LogSQLMassVirtualHosting", set_global_flag_slot, | ||
1151 | (void *)APR_OFFSETOF(global_config_t, massvirtual), RSRC_CONF, | ||
1152 | "Activates option(s) useful for ISPs performing mass virutal hosting") | ||
1153 | , | ||
1154 | AP_INIT_TAKE1("LogSQLTransferLogTable", set_server_nmv_string_slot, | ||
1155 | (void *)APR_OFFSETOF(logsql_state, transfer_table_name), RSRC_CONF, | ||
1156 | "The database table that holds the transfer log") | ||
1157 | , | ||
1158 | AP_INIT_TAKE1("LogSQLNotesLogTable", set_server_nmv_string_slot, | ||
1159 | (void *)APR_OFFSETOF(logsql_state, notes_table_name), RSRC_CONF, | ||
1160 | "The database table that holds the notes") | ||
1161 | , | ||
1162 | AP_INIT_TAKE1("LogSQLHeadersOutLogTable", set_server_nmv_string_slot, | ||
1163 | (void *)APR_OFFSETOF(logsql_state, hout_table_name), RSRC_CONF, | ||
1164 | "The database table that holds the outbound headers") | ||
1165 | , | ||
1166 | AP_INIT_TAKE1("LogSQLHeadersInLogTable", set_server_nmv_string_slot, | ||
1167 | (void *)APR_OFFSETOF(logsql_state, hin_table_name), RSRC_CONF, | ||
1168 | "The database table that holds the inbound headers") | ||
1169 | , | ||
1170 | AP_INIT_TAKE1("LogSQLCookieLogTable", set_server_nmv_string_slot, | ||
1171 | (void *)APR_OFFSETOF(logsql_state, cookie_table_name), RSRC_CONF, | ||
1172 | "The database table that holds the cookie info") | ||
1173 | , | ||
1174 | /* Log format */ | ||
1175 | AP_INIT_TAKE1("LogSQLTransferLogFormat", set_logformat_slot, | ||
1176 | NULL, RSRC_CONF, | ||
1177 | "Instruct the module what information to log to the database transfer log") | ||
1178 | , | ||
1179 | /* Machine ID */ | ||
1180 | AP_INIT_TAKE1("LogSQLMachineID", set_global_string_slot, | ||
1181 | (void *)APR_OFFSETOF(global_config_t, machid), RSRC_CONF, | ||
1182 | "Machine ID that the module will log, useful in web clusters to differentiate machines") | ||
1183 | , | ||
1184 | /* Limits on logging */ | ||
1185 | AP_INIT_ITERATE("LogSQLRequestAccept", add_server_string_slot, | ||
1186 | (void *)APR_OFFSETOF(logsql_state, transfer_accept_list), RSRC_CONF, | ||
1187 | "List of URIs to accept for logging. Accesses that don't match will not be logged") | ||
1188 | , | ||
1189 | AP_INIT_ITERATE("LogSQLRequestIgnore", add_server_string_slot, | ||
1190 | (void *)APR_OFFSETOF(logsql_state, transfer_ignore_list), RSRC_CONF, | ||
1191 | "List of URIs to ignore. Accesses that match will not be logged to database") | ||
1192 | , | ||
1193 | AP_INIT_ITERATE("LogSQLRemhostIgnore", add_server_string_slot, | ||
1194 | (void *)APR_OFFSETOF(logsql_state, remhost_ignore_list), RSRC_CONF, | ||
1195 | "List of remote hosts to ignore. Accesses that match will not be logged to database") | ||
1196 | , | ||
1197 | /* Special loggin table configuration */ | ||
1198 | AP_INIT_TAKE1("LogSQLWhichCookie", set_server_string_slot, | ||
1199 | (void *)APR_OFFSETOF(logsql_state, cookie_name), RSRC_CONF, | ||
1200 | "The single cookie that you want logged in the access_log when using the 'c' config directive") | ||
1201 | , | ||
1202 | AP_INIT_ITERATE("LogSQLWhichNotes", add_server_string_slot, | ||
1203 | (void *)APR_OFFSETOF(logsql_state, notes_list), RSRC_CONF, | ||
1204 | "Notes that you would like to log in a separate table") | ||
1205 | , | ||
1206 | AP_INIT_ITERATE("LogSQLWhichHeadersOut", add_server_string_slot, | ||
1207 | (void *)APR_OFFSETOF(logsql_state, hout_list), RSRC_CONF, | ||
1208 | "Outbound headers that you would like to log in a separate table") | ||
1209 | , | ||
1210 | AP_INIT_ITERATE("LogSQLWhichHeadersIn", add_server_string_slot, | ||
1211 | (void *)APR_OFFSETOF(logsql_state, hin_list), RSRC_CONF, | ||
1212 | "Inbound headers that you would like to log in a separate table") | ||
1213 | , | ||
1214 | AP_INIT_ITERATE("LogSQLWhichCookies", add_server_string_slot, | ||
1215 | (void *)APR_OFFSETOF(logsql_state, cookie_list), RSRC_CONF, | ||
1216 | "The cookie(s) that you would like to log in a separate table") | ||
1217 | , | ||
1218 | AP_INIT_RAW_ARGS("LogSQLDeprecated", ap_set_deprecated, NULL, RSRC_CONF, | ||
1219 | "<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>") | ||
1220 | , | ||
1221 | /* Deprecated commands */ | ||
1222 | AP_INIT_TAKE1("LogSQLDatabase", set_dbparam_slot, | ||
1223 | (void *)"database", RSRC_CONF, | ||
1224 | "<b>(Deprecated) Use LogSQLDBParam database dbname.</b> The name of the database database for logging") | ||
1225 | , | ||
1226 | AP_INIT_TAKE1("LogSQLTableType", set_dbparam_slot, | ||
1227 | (void *)"tabletype", RSRC_CONF, | ||
1228 | "<b>(Deprecated) Use LogSQLDBParam tabletype type.</b> What kind of table to create (MyISAM, InnoDB,...) when creating tables") | ||
1229 | , | ||
1230 | AP_INIT_TAKE1("LogSQLSocketFile", set_dbparam_slot, | ||
1231 | (void *)"socketfile", RSRC_CONF, | ||
1232 | "<b>(Deprecated) Use LogSQLDBParam socketfile socket.</b> Name of the file to employ for socket connections to database") | ||
1233 | , | ||
1234 | AP_INIT_TAKE1("LogSQLTCPPort", set_dbparam_slot, | ||
1235 | (void *)"port", RSRC_CONF, | ||
1236 | "<b>(Deprecated) Use LogSQLDBParam port port.</b> Port number to use for TCP connections to database, defaults to 3306 if not set") | ||
1237 | , | ||
1238 | {NULL} | ||
1239 | }; | ||
1240 | /* The configuration array that sets up the hooks into the module. */ | ||
1241 | #if defined(WITH_APACHE20) | ||
1242 | static void register_hooks(apr_pool_t *p) { | ||
1243 | ap_hook_post_config(log_sql_post_config, NULL, NULL, APR_HOOK_REALLY_FIRST); | ||
1244 | ap_hook_child_init(log_sql_child_init, NULL, NULL, APR_HOOK_MIDDLE); | ||
1245 | ap_hook_log_transaction(log_sql_transaction, NULL, NULL, APR_HOOK_MIDDLE); | ||
1246 | } | ||
1247 | |||
1248 | module AP_MODULE_DECLARE_DATA log_sql_module = { | ||
1249 | STANDARD20_MODULE_STUFF, | ||
1250 | NULL, /* create per-directory config structures */ | ||
1251 | NULL, /* merge per-directory config structures */ | ||
1252 | log_sql_make_state, /* create per-server config structures */ | ||
1253 | log_sql_merge_state, /* merge per-server config structures */ | ||
1254 | log_sql_cmds, /* command handlers */ | ||
1255 | register_hooks /* register hooks */ | ||
1256 | }; | ||
1257 | #elif defined(WITH_APACHE13) | ||
1258 | module MODULE_VAR_EXPORT log_sql_module = { | ||
1259 | STANDARD_MODULE_STUFF, | ||
1260 | log_sql_module_init, /* module initializer */ | ||
1261 | NULL, /* create per-dir config */ | ||
1262 | NULL, /* merge per-dir config */ | ||
1263 | log_sql_make_state, /* create server config */ | ||
1264 | log_sql_merge_state, /* merge server config */ | ||
1265 | log_sql_cmds, /* config directive table */ | ||
1266 | NULL, /* [9] content handlers */ | ||
1267 | NULL, /* [2] URI-to-filename translation */ | ||
1268 | NULL, /* [5] check/validate user_id */ | ||
1269 | NULL, /* [6] check authorization */ | ||
1270 | NULL, /* [4] check access by host */ | ||
1271 | NULL, /* [7] MIME type checker/setter */ | ||
1272 | NULL, /* [8] fixups */ | ||
1273 | log_sql_transaction, /* [10] logger */ | ||
1274 | NULL /* [3] header parser */ | ||
1275 | #if MODULE_MAGIC_NUMBER >= 19970728 /* 1.3-dev or later support these additionals... */ | ||
1276 | ,log_sql_child_init, /* child process initializer */ | ||
1277 | log_sql_child_exit, /* process exit/cleanup */ | ||
1278 | NULL /* [1] post read-request */ | ||
1279 | #endif | ||
1280 | |||
1281 | }; | ||
1282 | #endif | ||