summaryrefslogtreecommitdiffstats
path: root/mod_log_sql.c
diff options
context:
space:
mode:
Diffstat (limited to 'mod_log_sql.c')
-rw-r--r--mod_log_sql.c1687
1 files changed, 0 insertions, 1687 deletions
diff --git a/mod_log_sql.c b/mod_log_sql.c
deleted file mode 100644
index f02243e..0000000
--- a/mod_log_sql.c
+++ /dev/null
@@ -1,1687 +0,0 @@
1
2/* --------*
3 * DEFINES *
4 * --------*/
5
6/* The enduser may wish to modify this */
7#undef DEBUG
8
9/* The enduser won't modify these */
10#define MYSQL_ERROR(mysql) ((mysql)?(mysql_error(mysql)):"MySQL server has gone away")
11#define ERRLEVEL APLOG_ERR|APLOG_NOERRNO
12#define WARNINGLEVEL APLOG_WARNING|APLOG_NOERRNO
13#define NOTICELEVEL APLOG_NOTICE|APLOG_NOERRNO
14#define DEBUGLEVEL APLOG_DEBUG|APLOG_NOERRNO
15
16/* ---------*
17 * INCLUDES *
18 * ---------*/
19#include <time.h>
20#include <stdio.h>
21#include <stdlib.h>
22#include <string.h>
23#include "httpd.h"
24#include "http_config.h"
25#include "http_log.h"
26#include "http_core.h"
27#include "mysql.h"
28#include "mysqld_error.h"
29
30#if MODULE_MAGIC_NUMBER >= 19980324 /* M_M_N is defined in /usr/local/Apache/include/ap_mmn.h, 19990320 as of this writing. */
31 #include "ap_compat.h"
32#endif
33
34#ifdef WANT_SSL_LOGGING
35 #include "mod_ssl.h"
36#endif
37
38
39/* -------------*
40 * DECLARATIONS *
41 * -------------*/
42
43/* Declare ourselves so the configuration routines can find and know us. */
44module sql_log_module;
45
46/* The contents of these are known 'Apache wide' and are not variable
47 * on a per-virtual-server basis. Every virtual server 'knows' the
48 * same versions of these variables.
49 */
50MYSQL logsql_server, *logsql_server_p = NULL;
51
52int logsql_massvirtual = 0;
53int logsql_createtables = 0;
54int logsql_forcepreserve = 0;
55char *logsql_dbname = NULL;
56char *logsql_dbhost = NULL;
57char *logsql_dbuser = NULL;
58char *logsql_dbpwd = NULL;
59char *logsql_machid = NULL;
60char *logsql_socketfile = "/tmp/mysql.sock";
61unsigned int logsql_tcpport = 3306;
62
63#ifdef WANT_DELAYED_MYSQL_INSERT
64 char *logsql_insertclause = "insert delayed into ";
65#else
66 char *logsql_insertclause = "insert into ";
67#endif
68
69typedef const char *(*item_key_func) (request_rec *, char *);
70
71/* But the contents of this structure will vary by virtual server.
72 * This permits each virtual server to vary its configuration slightly
73 * for per-server customization.
74 *
75 * Each child process has its own segregated copy of this structure.
76 */
77typedef struct {
78 array_header *transfer_ignore_list;
79 array_header *transfer_accept_list;
80 array_header *remhost_ignore_list;
81 array_header *notes_list;
82 array_header *hout_list;
83 array_header *hin_list;
84 array_header *cookie_list;
85 char *notes_table_name;
86 char *hout_table_name;
87 char *hin_table_name;
88 char *cookie_table_name;
89 char *transfer_table_name;
90 char *transfer_log_format;
91 char *preserve_file;
92 char *cookie_name;
93} logsql_state;
94
95
96/* -----------------*
97 * HELPER FUNCTIONS *
98 * -----------------*/
99
100int safe_create_tables(logsql_state *cls, request_rec *r);
101
102static char *format_integer(pool *p, int i)
103{
104 char dummy[40];
105 ap_snprintf(dummy, sizeof(dummy), "%d", i);
106 return pstrdup(p, dummy);
107}
108
109static char *pfmt(pool *p, int i)
110{
111 if (i <= 0) {
112 return "-";
113 } else {
114 return format_integer(p, i);
115 }
116}
117
118/* Begin the individual functions that, given a request r,
119 * extract the needed information from it and return the
120 * value to the calling entity.
121 */
122
123static const char *extract_remote_host(request_rec *r, char *a)
124{
125 return (char *) get_remote_host(r->connection, r->per_dir_config, REMOTE_NAME);
126}
127
128static const char *extract_remote_logname(request_rec *r, char *a)
129{
130 return (char *) get_remote_logname(r);
131}
132
133static const char *extract_remote_user(request_rec *r, char *a)
134{
135 char *rvalue = r->connection->user;
136
137 if (rvalue == NULL) {
138 rvalue = "-";
139 } else if (strlen(rvalue) == 0) {
140 rvalue = "\"\"";
141 }
142 return rvalue;
143}
144
145#ifdef WANT_SSL_LOGGING
146static const char *extract_ssl_keysize(request_rec *r, char *a)
147{
148 char *result = NULL;
149
150 if (ap_ctx_get(r->connection->client->ctx, "ssl") != NULL) {
151 result = ssl_var_lookup(r->pool, r->server, r->connection, r, "SSL_CIPHER_USEKEYSIZE");
152 #ifdef DEBUG
153 ap_log_error(APLOG_MARK,DEBUGLEVEL,r->server,"SSL_KEYSIZE: %s", result);
154 #endif
155 if (result != NULL && result[0] == '\0')
156 result = NULL;
157 return result;
158 } else {
159 return "0";
160 }
161}
162
163static const char *extract_ssl_maxkeysize(request_rec *r, char *a)
164{
165 char *result = NULL;
166
167 if (ap_ctx_get(r->connection->client->ctx, "ssl") != NULL) {
168 result = ssl_var_lookup(r->pool, r->server, r->connection, r, "SSL_CIPHER_ALGKEYSIZE");
169 #ifdef DEBUG
170 ap_log_error(APLOG_MARK,DEBUGLEVEL,r->server,"SSL_ALGKEYSIZE: %s", result);
171 #endif
172 if (result != NULL && result[0] == '\0')
173 result = NULL;
174 return result;
175 } else {
176 return "0";
177 }
178}
179
180static const char *extract_ssl_cipher(request_rec *r, char *a)
181{
182 char *result = NULL;
183
184 if (ap_ctx_get(r->connection->client->ctx, "ssl") != NULL) {
185 result = ssl_var_lookup(r->pool, r->server, r->connection, r, "SSL_CIPHER");
186 #ifdef DEBUG
187 ap_log_error(APLOG_MARK,DEBUGLEVEL,r->server,"SSL_CIPHER: %s", result);
188 #endif
189 if (result != NULL && result[0] == '\0')
190 result = NULL;
191 return result;
192 } else {
193 return "-";
194 }
195}
196#endif /* WANT_SSL_LOGGING */
197
198static const char *extract_request_method(request_rec *r, char *a)
199{
200 return r->method;
201}
202
203static const char *extract_request_protocol(request_rec *r, char *a)
204{
205 return r->protocol;
206}
207
208static const char *extract_request_line(request_rec *r, char *a)
209{
210 return r->the_request;
211}
212
213static const char *extract_request_file(request_rec *r, char *a)
214{
215 return r->filename;
216}
217
218static const char *extract_request_uri(request_rec *r, char *a)
219{
220 return r->uri;
221}
222
223static const char *extract_request_args(request_rec *r, char *a)
224{
225 return r->args;
226}
227
228static const char *extract_status(request_rec *r, char *a)
229{
230 return pfmt(r->pool, r->status);
231}
232
233static const char *extract_bytes_sent(request_rec *r, char *a)
234{
235 if (!r->sent_bodyct) {
236 return "-";
237 } else {
238 long int bs;
239 char dummy[40];
240 bgetopt(r->connection->client, BO_BYTECT, &bs);
241 ap_snprintf(dummy, sizeof(dummy), "%ld", bs);
242 return pstrdup(r->pool, dummy);
243 }
244}
245
246/*
247static const char *extract_header_in(request_rec *r, char *a)
248{
249 return table_get(r->headers_in, a);
250}
251
252static const char *extract_header_out(request_rec *r, char *a)
253{
254 const char *cp = table_get(r->headers_out, a);
255 if (!strcasecmp(a, "Content-type") && r->content_type) {
256 cp = r->content_type;
257 }
258 if (cp) {
259 return cp;
260 }
261 return table_get(r->err_headers_out, a);
262}
263*/
264
265static const char *extract_request_time(request_rec *r, char *a)
266{
267 int timz;
268 struct tm *t;
269 char tstr[MAX_STRING_LEN];
270
271 t = get_gmtoff(&timz);
272
273 if (a && *a) { /* Custom format */
274 strftime(tstr, MAX_STRING_LEN, a, t);
275 } else { /* CLF format */
276 char sign = (timz < 0 ? '-' : '+');
277
278 if (timz < 0) {
279 timz = -timz;
280 }
281 strftime(tstr, MAX_STRING_LEN, "[%d/%b/%Y:%H:%M:%S ", t);
282 ap_snprintf(tstr + strlen(tstr), sizeof(tstr) - strlen(tstr), "%c%.2d%.2d]", sign, timz / 60, timz % 60);
283 }
284
285 return pstrdup(r->pool, tstr);
286}
287
288static const char *extract_request_duration(request_rec *r, char *a)
289{
290 char duration[22]; /* Long enough for 2^64 */
291
292 ap_snprintf(duration, sizeof(duration), "%ld", time(NULL) - r->request_time);
293 return pstrdup(r->pool, duration);
294}
295
296static const char *extract_virtual_host(request_rec *r, char *a)
297{
298 return ap_get_server_name(r);
299}
300
301static const char *extract_machine_id(request_rec *r, char *a)
302{
303 if (!logsql_machid)
304 return "-";
305 else
306 return logsql_machid;
307}
308
309static const char *extract_server_port(request_rec *r, char *a)
310{
311 char portnum[22];
312
313 ap_snprintf(portnum, sizeof(portnum), "%u", r->server->port);
314 return pstrdup(r->pool, portnum);
315}
316
317static const char *extract_child_pid(request_rec *r, char *a)
318{
319 char pidnum[22];
320 ap_snprintf(pidnum, sizeof(pidnum), "%ld", (long) getpid());
321 return pstrdup(r->pool, pidnum);
322}
323
324static const char *extract_referer(request_rec *r, char *a)
325{
326 const char *tempref;
327
328 tempref = table_get(r->headers_in, "Referer");
329 if (!tempref)
330 {
331 return "-";
332 } else {
333 return tempref;
334 }
335}
336
337static const char *extract_agent(request_rec *r, char *a)
338{
339 const char *tempag;
340
341 tempag = table_get(r->headers_in, "User-Agent");
342 if (!tempag)
343 {
344 return "-";
345 } else {
346 return tempag;
347 }
348}
349
350static const char *extract_cookie(request_rec *r, char *a)
351{
352 const char *cookiestr;
353 char *cookieend;
354 char *isvalid;
355 char *cookiebuf;
356
357 logsql_state *cls = get_module_config(r->server->module_config, &sql_log_module);
358
359 if (cls->cookie_name != NULL) {
360 #ifdef DEBUG
361 ap_log_error(APLOG_MARK,DEBUGLEVEL,r->server,"watching for cookie '%s'", cls->cookie_name);
362 #endif
363
364 /* Fetch out the cookie header */
365 cookiestr = (char *)table_get(r->headers_in, "cookie2");
366 if (cookiestr != NULL) {
367 #ifdef DEBUG
368 ap_log_error(APLOG_MARK,DEBUGLEVEL,r->server,"Cookie2: [%s]", cookiestr);
369 #endif
370 /* Does the cookie string contain one with our name? */
371 isvalid = strstr(cookiestr, cls->cookie_name);
372 if (isvalid != NULL) {
373 /* Move past the cookie name and equal sign */
374 isvalid += strlen(cls->cookie_name) + 1;
375 /* Duplicate it into the pool */
376 cookiebuf = ap_pstrdup(r->pool, isvalid);
377 /* Segregate just this cookie out of the string
378 * with a terminating nul at the first semicolon */
379 cookieend = strchr(cookiebuf, ';');
380 if (cookieend != NULL)
381 *cookieend = '\0';
382 return cookiebuf;
383 }
384 }
385
386 cookiestr = (char *)table_get(r->headers_in, "cookie");
387 if (cookiestr != NULL) {
388 #ifdef DEBUG
389 ap_log_error(APLOG_MARK,DEBUGLEVEL,r->server,"Cookie: [%s]", cookiestr);
390 #endif
391 isvalid = strstr(cookiestr, cls->cookie_name);
392 if (isvalid != NULL) {
393 isvalid += strlen(cls->cookie_name) + 1;
394 cookiebuf = ap_pstrdup(r->pool, isvalid);
395 cookieend = strchr(cookiebuf, ';');
396 if (cookieend != NULL)
397 *cookieend = '\0';
398 return cookiebuf;
399 }
400 }
401
402 cookiestr = table_get(r->headers_out, "set-cookie");
403 if (cookiestr != NULL) {
404 #ifdef DEBUG
405 ap_log_error(APLOG_MARK,DEBUGLEVEL,r->server,"Set-Cookie: [%s]", cookiestr);
406 #endif
407 isvalid = strstr(cookiestr, cls->cookie_name);
408 if (isvalid != NULL) {
409 isvalid += strlen(cls->cookie_name) + 1;
410 cookiebuf = ap_pstrdup(r->pool, isvalid);
411 cookieend = strchr(cookiebuf, ';');
412 if (cookieend != NULL)
413 *cookieend = '\0';
414 return cookiebuf;
415 }
416 }
417 }
418
419 return "-";
420}
421
422static const char *extract_specific_cookie(request_rec *r, char *a)
423{
424 const char *cookiestr;
425 char *cookieend;
426 char *isvalid;
427 char *cookiebuf;
428
429 if (a != NULL) {
430 #ifdef DEBUG
431 ap_log_error(APLOG_MARK,DEBUGLEVEL,r->server,"watching for cookie '%s'", a);
432 #endif
433
434 /* Fetch out the cookie header */
435 cookiestr = (char *)table_get(r->headers_in, "cookie2");
436 if (cookiestr != NULL) {
437 #ifdef DEBUG
438 ap_log_error(APLOG_MARK,DEBUGLEVEL,r->server,"Cookie2: [%s]", cookiestr);
439 #endif
440 /* Does the cookie string contain one with our name? */
441 isvalid = strstr(cookiestr, a);
442 if (isvalid != NULL) {
443 /* Move past the cookie name and equal sign */
444 isvalid += strlen(a) + 1;
445 /* Duplicate it into the pool */
446 cookiebuf = ap_pstrdup(r->pool, isvalid);
447 /* Segregate just this cookie out of the string
448 * with a terminating nul at the first semicolon */
449 cookieend = strchr(cookiebuf, ';');
450 if (cookieend != NULL)
451 *cookieend = '\0';
452 return cookiebuf;
453 }
454 }
455
456 cookiestr = (char *)table_get(r->headers_in, "cookie");
457 if (cookiestr != NULL) {
458 #ifdef DEBUG
459 ap_log_error(APLOG_MARK,DEBUGLEVEL,r->server,"Cookie: [%s]", cookiestr);
460 #endif
461 isvalid = strstr(cookiestr, a);
462 if (isvalid != NULL) {
463 isvalid += strlen(a) + 1;
464 cookiebuf = ap_pstrdup(r->pool, isvalid);
465 cookieend = strchr(cookiebuf, ';');
466 if (cookieend != NULL)
467 *cookieend = '\0';
468 return cookiebuf;
469 }
470 }
471
472 cookiestr = table_get(r->headers_out, "set-cookie");
473 if (cookiestr != NULL) {
474 #ifdef DEBUG
475 ap_log_error(APLOG_MARK,DEBUGLEVEL,r->server,"Set-Cookie: [%s]", cookiestr);
476 #endif
477 isvalid = strstr(cookiestr, a);
478 if (isvalid != NULL) {
479 isvalid += strlen(a) + 1;
480 cookiebuf = ap_pstrdup(r->pool, isvalid);
481 cookieend = strchr(cookiebuf, ';');
482 if (cookieend != NULL)
483 *cookieend = '\0';
484 return cookiebuf;
485 }
486 }
487 }
488
489 return "-";
490}
491
492
493static const char *extract_request_timestamp(request_rec *r, char *a)
494{
495 char tstr[32];
496
497 ap_snprintf(tstr, 32, "%ld", time(NULL));
498 return pstrdup(r->pool, tstr);
499}
500
501/*
502static const char *extract_note(request_rec *r, char *a)
503{
504 return ap_table_get(r->notes, a);
505
506}
507*/
508
509static const char *extract_env_var(request_rec *r, char *a)
510{
511 return ap_table_get(r->subprocess_env, a);
512}
513
514static const char *extract_unique_id(request_rec *r, char *a)
515{
516 const char *tempid;
517
518 tempid = ap_table_get(r->subprocess_env, "UNIQUE_ID");
519 if (!tempid)
520 return "-";
521 else
522 return tempid;
523}
524
525/* End declarations of various extract_ functions */
526
527
528
529struct log_sql_item_list {
530 char ch; /* its letter code */
531 item_key_func func; /* its extraction function */
532 const char *sql_field_name; /* its column in SQL */
533 int want_orig_default; /* if it requires the original request prior to internal redirection */
534 int string_contents; /* if it returns a string */
535 } log_sql_item_keys[] = {
536
537 { 'A', extract_agent, "agent", 1, 1 },
538 { 'a', extract_request_args, "request_args", 1, 1 },
539 { 'b', extract_bytes_sent, "bytes_sent", 0, 0 },
540 { 'c', extract_cookie, "cookie", 0, 1 },
541 { 'e', extract_env_var, "env_var", 0, 1 },
542 { 'f', extract_request_file, "request_file", 0, 1 },
543 { 'H', extract_request_protocol, "request_protocol", 0, 1 },
544 { 'h', extract_remote_host, "remote_host", 0, 1 },
545 { 'I', extract_unique_id, "id", 0, 1 },
546 { 'l', extract_remote_logname, "remote_logname", 0, 1 },
547 { 'm', extract_request_method, "request_method", 0, 1 },
548 { 'M', extract_machine_id, "machine_id", 0, 1 },
549 { 'P', extract_child_pid, "child_pid", 0, 0 },
550 { 'p', extract_server_port, "server_port", 0, 0 },
551 { 'R', extract_referer, "referer", 1, 1 },
552 { 'r', extract_request_line, "request_line", 1, 1 },
553 { 'S', extract_request_timestamp, "time_stamp", 0, 0 },
554 { 's', extract_status, "status", 1, 0 },
555 { 'T', extract_request_duration, "request_duration", 1, 0 },
556 { 't', extract_request_time, "request_time", 0, 1 },
557 { 'u', extract_remote_user, "remote_user", 0, 1 },
558 { 'U', extract_request_uri, "request_uri", 1, 1 },
559 { 'v', extract_virtual_host, "virtual_host", 0, 1 },
560 #ifdef WANT_SSL_LOGGING
561 { 'q', extract_ssl_keysize, "ssl_keysize", 0, 1 },
562 { 'Q', extract_ssl_maxkeysize, "ssl_maxkeysize", 0, 1 },
563 { 'z', extract_ssl_cipher, "ssl_cipher", 0, 1 },
564 #endif
565 {'\0'}
566};
567
568
569/* Routine to escape the 'dangerous' characters that would otherwise
570 * corrupt the INSERT string: ', \, and "
571 */
572const char *escape_query(const char *from_str, pool *p)
573{
574 if (!from_str)
575 return NULL;
576 else {
577 char *to_str;
578 unsigned long length = strlen(from_str);
579 unsigned long retval;
580
581 /* Pre-allocate a new string that could hold twice the original, which would only
582 * happen if the whole original string was 'dangerous' characters.
583 */
584 to_str = (char *) ap_palloc(p, length * 2 + 1);
585 if (!to_str) {
586 return from_str;
587 }
588
589 if (!logsql_server_p) {
590 /* Well, I would have liked to use the current database charset. mysql is
591 * unavailable, however, so I fall back to the slightly less respectful
592 * mysql_escape_string() function that uses the default charset.
593 */
594 retval = mysql_escape_string(to_str, from_str, length);
595 } else {
596 /* MySQL is available, so I'll go ahead and respect the current charset when
597 * I perform the escape.
598 */
599 retval = mysql_real_escape_string(logsql_server_p, to_str, from_str, length);
600 }
601
602 if (retval)
603 return to_str;
604 else
605 return from_str;
606 }
607}
608
609int open_logdb_link(server_rec* s)
610{
611 /* Returns:
612 3 if preserve forced
613 2 if already connected
614 1 if successful
615 0 if unsuccessful
616 */
617
618 if (logsql_forcepreserve)
619 return 3;
620
621 if (logsql_server_p)
622 return 2;
623
624 if ((logsql_dbname) && (logsql_dbhost) && (logsql_dbuser) && (logsql_dbpwd)) {
625 mysql_init(&logsql_server);
626 logsql_server_p = mysql_real_connect(&logsql_server, logsql_dbhost, logsql_dbuser, logsql_dbpwd, logsql_dbname, logsql_tcpport, logsql_socketfile, 0);
627
628 if (logsql_server_p) {
629 #ifdef DEBUG
630 ap_log_error(APLOG_MARK,DEBUGLEVEL,s,"HOST: '%s' PORT: '%d' DB: '%s' USER: '%s' SOCKET: '%s'",
631 logsql_dbhost, logsql_tcpport, logsql_dbname, logsql_dbuser, logsql_socketfile);
632 #endif
633 return 1;
634 } else {
635 #ifdef DEBUG
636 ap_log_error(APLOG_MARK,DEBUGLEVEL,s,"mod_log_sql: database connection error: %s",MYSQL_ERROR(&logsql_server));
637 ap_log_error(APLOG_MARK,DEBUGLEVEL,s,"HOST: '%s' PORT: '%d' DB: '%s' USER: '%s' SOCKET: '%s'",
638 logsql_dbhost, logsql_tcpport, logsql_dbname, logsql_dbuser, logsql_socketfile);
639 #endif
640 return 0;
641 }
642 } else {
643 ap_log_error(APLOG_MARK,ERRLEVEL,s,"mod_log_sql: insufficient configuration info to establish database link");
644 return 0;
645 }
646}
647
648const char *extract_table(void *data, const char *key, const char *val)
649{
650 request_rec *r = (request_rec *)data;
651
652 return ap_pstrcat(r->pool, key, " = ", val, " ", NULL);
653}
654
655void preserve_entry(request_rec *r, const char *query)
656{
657 FILE *fp;
658 logsql_state *cls = get_module_config(r->server->module_config, &sql_log_module);
659
660 fp = pfopen(r->pool, cls->preserve_file, "a");
661 if (fp == NULL)
662 ap_log_error(APLOG_MARK,ERRLEVEL,r->server,"mod_log_sql: attempted append of local preserve file but failed.");
663 else {
664 fprintf(fp,"%s;\n", query);
665 pfclose(r->pool, fp);
666 #ifdef DEBUG
667 ap_log_error(APLOG_MARK,DEBUGLEVEL,r->server,"mod_log_sql: entry preserved in %s", cls->preserve_file);
668 #endif
669 }
670}
671
672
673/*-----------------------------------------------------*/
674/* safe_sql_query: perform a database query with */
675/* a degree of safety and error checking. */
676/* */
677/* Parms: request record, SQL insert statement */
678/* Returns: 0 (OK) on success */
679/* 1 if have no log handle */
680/* 2 if insert delayed failed (kluge) */
681/* the actual MySQL return code on error */
682/*-----------------------------------------------------*/
683unsigned int safe_sql_query(request_rec *r, const char *query)
684{
685 int retval;
686 struct timespec delay, remainder;
687 int ret;
688 void (*handler) (int);
689 logsql_state *cls;
690 unsigned int real_error;
691 #ifdef WANT_DELAYED_MYSQL_INSERT
692 char *real_error_str;
693 #endif
694
695 /* A failed mysql_query() may send a SIGPIPE, so we ignore that signal momentarily. */
696 handler = signal(SIGPIPE, SIG_IGN);
697
698 /* First attempt for the query */
699 if (!logsql_server_p) {
700 signal(SIGPIPE, handler);
701 return 1;
702 } else if (!(retval = mysql_query(logsql_server_p, query))) {
703 signal(SIGPIPE, handler);
704 return 0;
705 }
706
707 /* If we ran the query and it returned an error, try to be robust.
708 * (After all, the module thought it had a valid mysql_log connection but the query
709 * could have failed for a number of reasons, so we have to be extra-safe and check.) */
710 #ifdef WANT_DELAYED_MYSQL_INSERT
711 real_error_str = MYSQL_ERROR(logsql_server_p);
712 #else
713 real_error = mysql_errno(logsql_server_p);
714 #endif
715
716 /* Check to see if the error is "nonexistent table" */
717 #ifdef WANT_DELAYED_MYSQL_INSERT
718 if ((strstr(real_error_str, "Table")) && (strstr(real_error_str,"doesn't exist"))) {
719 #else
720 if (real_error == ER_NO_SUCH_TABLE) {
721 #endif
722 if (logsql_createtables) {
723 ap_log_error(APLOG_MARK,ERRLEVEL,r->server,"mod_log_sql: table doesn't exist...creating now");
724 cls = get_module_config(r->server->module_config, &sql_log_module);
725 if (safe_create_tables(cls, r)) {
726 ap_log_error(APLOG_MARK,ERRLEVEL,r->server,"mod_log_sql: child attempted but failed to create one or more tables for %s, preserving query", ap_get_server_name(r));
727 preserve_entry(r, query);
728 retval = mysql_errno(logsql_server_p);
729 } else {
730 ap_log_error(APLOG_MARK,ERRLEVEL,r->server,"mod_log_sql: tables successfully created - retrying query");
731 if (mysql_query(logsql_server_p, query)) {
732 ap_log_error(APLOG_MARK,ERRLEVEL,r->server,"mod_log_sql: giving up, preserving query");
733 preserve_entry(r, query);
734 retval = mysql_errno(logsql_server_p);
735 } else
736 ap_log_error(APLOG_MARK,ERRLEVEL,r->server,"mod_log_sql: query successful after table creation");
737 retval = 0;
738 }
739 } else {
740 ap_log_error(APLOG_MARK,ERRLEVEL,r->server,"mod_log_sql, table doesn't exist, creation denied by configuration, preserving query");
741 preserve_entry(r, query);
742 retval = ER_NO_SUCH_TABLE;
743 }
744 /* Restore SIGPIPE to its original handler function */
745 signal(SIGPIPE, handler);
746 return retval;
747 }
748
749 /* Handle all other types of errors */
750
751 cls = get_module_config(r->server->module_config, &sql_log_module);
752
753 /* Something went wrong, so start by trying to restart the db link. */
754 #ifdef WANT_DELAYED_MYSQL_INSERT
755 real_error = 2;
756 #else
757 real_error = mysql_errno(logsql_server_p);
758 #endif
759 ap_log_error(APLOG_MARK,ERRLEVEL,r->server,"mod_log_sql: first attempt failed, API said: error %d, \"%s\"", real_error, MYSQL_ERROR(logsql_server_p));
760 mysql_close(logsql_server_p);
761 logsql_server_p = NULL;
762 open_logdb_link(r->server);
763
764 if (logsql_server_p == NULL) { /* still unable to link */
765 signal(SIGPIPE, handler);
766 ap_log_error(APLOG_MARK,ERRLEVEL,r->server,"mod_log_sql: reconnect failed, unable to reach database. SQL logging stopped until child regains a db connection.");
767 ap_log_error(APLOG_MARK,ERRLEVEL,r->server,"mod_log_sql: log entries are being preserved in %s", cls->preserve_file);
768 return 1;
769 } else
770 ap_log_error(APLOG_MARK,ERRLEVEL,r->server,"mod_log_sql: db reconnect successful");
771
772 /* First sleep for a tiny amount of time. */
773 delay.tv_sec = 0;
774 delay.tv_nsec = 250000000; /* max is 999999999 (nine nines) */
775 ret = nanosleep(&delay, &remainder);
776 if (ret && errno != EINTR)
777 ap_log_error(APLOG_MARK,ERRLEVEL,r->server,"mod_log_sql: nanosleep unsuccessful");
778
779 /* Then make our second attempt */
780 retval = mysql_query(logsql_server_p,query);
781
782 /* If this one also failed, log that and append to our local offline file */
783 if (retval) {
784 #ifdef WANT_DELAYED_MYSQL_INSERT
785 real_error = 2;
786 #else
787 real_error = mysql_errno(logsql_server_p);
788 #endif
789 ap_log_error(APLOG_MARK,ERRLEVEL,r->server,"mod_log_sql: second attempt failed, API said: error %d, \"%s\" -- preserving", real_error, MYSQL_ERROR(logsql_server_p));
790 preserve_entry(r, query);
791 retval = real_error;
792 } else
793 ap_log_error(APLOG_MARK,ERRLEVEL,r->server,"mod_log_sql: second attempt successful");
794
795 /* Restore SIGPIPE to its original handler function */
796 signal(SIGPIPE, handler);
797 return retval;
798}
799
800/*-----------------------------------------------------*/
801/* safe_create_tables: create SQL table set for the */
802/* virtual server represented by cls. */
803/* */
804/* Parms: virtserver structure, request record */
805/* Returns: 0 on no errors */
806/* mysql error code on failure */
807/*-----------------------------------------------------*/
808int safe_create_tables(logsql_state *cls, request_rec *r)
809{
810 int retval;
811 unsigned int create_results;
812 char *create_access = NULL;
813 char *create_notes = NULL;
814 char *create_hout = NULL;
815 char *create_hin = NULL;
816 char *create_cookies = NULL;
817
818 char *createprefix = "create table if not exists `";
819 char *access_suffix =
820 "` (id char(19),\
821 agent varchar(255),\
822 bytes_sent int unsigned,\
823 child_pid smallint unsigned,\
824 cookie varchar(255),\
825 machine_id varchar(25),\
826 request_file varchar(255),\
827 referer varchar(255),\
828 remote_host varchar(50),\
829 remote_logname varchar(50),\
830 remote_user varchar(50),\
831 request_duration smallint unsigned,\
832 request_line varchar(255),\
833 request_method varchar(10),\
834 request_protocol varchar(10),\
835 request_time char(28),\
836 request_uri varchar(50),\
837 request_args varchar(255),\
838 server_port smallint unsigned,\
839 ssl_cipher varchar(25),\
840 ssl_keysize smallint unsigned,\
841 ssl_maxkeysize smallint unsigned,\
842 status smallint unsigned,\
843 time_stamp int unsigned,\
844 virtual_host varchar(50))";
845
846 char *notes_suffix =
847 "` (id char(19),\
848 item varchar(80),\
849 val varchar(80))";
850
851 char *headers_suffix =
852 "` (id char(19),\
853 item varchar(80),\
854 val varchar(80))";
855
856 char *cookies_suffix =
857 "` (id char(19),\
858 item varchar(80),\
859 val varchar(80))";
860
861 /* Find memory long enough to hold the whole CREATE string + \0 */
862 create_access = ap_pstrcat(r->pool, createprefix, cls->transfer_table_name, access_suffix, NULL);
863 create_notes = ap_pstrcat(r->pool, createprefix, cls->notes_table_name, notes_suffix, NULL);
864 create_hout = ap_pstrcat(r->pool, createprefix, cls->hout_table_name, headers_suffix, NULL);
865 create_hin = ap_pstrcat(r->pool, createprefix, cls->hin_table_name, headers_suffix, NULL);
866 create_cookies= ap_pstrcat(r->pool, createprefix, cls->cookie_table_name, cookies_suffix, NULL);
867
868 #ifdef DEBUG
869 ap_log_error(APLOG_MARK,DEBUGLEVEL,r->server,"mod_log_sql: create string: %s", create_access);
870 ap_log_error(APLOG_MARK,DEBUGLEVEL,r->server,"mod_log_sql: create string: %s", create_notes);
871 ap_log_error(APLOG_MARK,DEBUGLEVEL,r->server,"mod_log_sql: create string: %s", create_hout);
872 ap_log_error(APLOG_MARK,DEBUGLEVEL,r->server,"mod_log_sql: create string: %s", create_hin);
873 ap_log_error(APLOG_MARK,DEBUGLEVEL,r->server,"mod_log_sql: create string: %s", create_cookies);
874 #endif
875
876 /* Assume that things worked unless told otherwise */
877 retval = 0;
878
879 if ((create_results = safe_sql_query(r, create_access))) {
880 ap_log_error(APLOG_MARK,ERRLEVEL,r->server,"mod_log_sql: failed to create access table");
881 retval = create_results;
882 }
883
884 if ((create_results = safe_sql_query(r, create_notes))) {
885 ap_log_error(APLOG_MARK,ERRLEVEL,r->server,"mod_log_sql: failed to create notes table");
886 retval = create_results;
887 }
888
889 if ((create_results = safe_sql_query(r, create_hin))) {
890 ap_log_error(APLOG_MARK,ERRLEVEL,r->server,"mod_log_sql: failed to create header_out table");
891 retval = create_results;
892 }
893
894 if ((create_results = safe_sql_query(r, create_hout))) {
895 ap_log_error(APLOG_MARK,ERRLEVEL,r->server,"mod_log_sql: failed to create header_in table");
896 retval = create_results;
897 }
898
899 if ((create_results = safe_sql_query(r, create_cookies))) {
900 ap_log_error(APLOG_MARK,ERRLEVEL,r->server,"mod_log_sql: failed to create cookies table");
901 retval = create_results;
902 }
903
904 return retval;
905}
906
907/* ------------------------------------------------*
908 * Command handlers that are called according *
909 * to the directives found at Apache runtime. *
910 * ------------------------------------------------*/
911
912const char *set_log_sql_massvirtual(cmd_parms *parms, void *dummy, int flag)
913{
914 logsql_massvirtual = ( flag ? 1 : 0);
915 return NULL;
916}
917
918const char *set_log_sql_force_preserve(cmd_parms *parms, void *dummy, int flag)
919{
920 logsql_forcepreserve = ( flag ? 1 : 0);
921 return NULL;
922}
923
924const char *set_log_sql_machine_id(cmd_parms *parms, void *dummy, char *arg)
925{
926 logsql_machid = arg;
927 return NULL;
928}
929
930const char *set_log_sql_create(cmd_parms *parms, void *dummy, int flag)
931{
932 if (logsql_massvirtual)
933 ap_log_error(APLOG_MARK,WARNINGLEVEL,parms->server,"mod_log_sql: do not set LogSQLCreateTables when LogSQLMassVirtualHosting is On. Ignoring.");
934 else
935 logsql_createtables = ( flag ? 1 : 0);
936 return NULL;
937}
938
939const char *set_log_sql_db(cmd_parms *parms, void *dummy, char *arg)
940{
941 logsql_dbname = arg;
942 return NULL;
943}
944
945const char *set_log_sql_cookie(cmd_parms *parms, void *dummy, char *arg)
946{
947 logsql_state *cls = get_module_config(parms->server->module_config, &sql_log_module);
948
949 cls->cookie_name = arg;
950 return NULL;
951}
952
953const char *set_log_sql_preserve_file(cmd_parms *parms, void *dummy, char *arg)
954{
955 /* char *pfile; */
956 logsql_state *cls = get_module_config(parms->server->module_config, &sql_log_module);
957
958 /* pfile = ap_pstrcat(parms->pool, "/tmp/", arg, NULL); */
959 cls->preserve_file = arg;
960 return NULL;
961}
962
963const char *set_log_sql_info(cmd_parms *parms, void *dummy, char *host, char *user, char *pwd)
964{
965 if (*host != '.') {
966 logsql_dbhost = host;
967 }
968 if (*user != '.') {
969 logsql_dbuser = user;
970 }
971 if (*pwd != '.') {
972 logsql_dbpwd = pwd;
973 }
974 return NULL;
975}
976
977const char *set_log_sql_transfer_table(cmd_parms *parms, void *dummy, char *arg)
978{
979 logsql_state *cls = get_module_config(parms->server->module_config, &sql_log_module);
980
981 if (logsql_massvirtual != 0)
982 ap_log_error(APLOG_MARK,WARNINGLEVEL,parms->server,"mod_log_sql: do not set LogSQLTransferLogTable when LogSQLMassVirtualHosting is On. Ignoring.");
983 else
984 cls->transfer_table_name = arg;
985 return NULL;
986}
987
988const char *set_log_sql_cookie_table(cmd_parms *parms, void *dummy, char *arg)
989{
990 logsql_state *cls = get_module_config(parms->server->module_config, &sql_log_module);
991
992 if (logsql_massvirtual != 0)
993 ap_log_error(APLOG_MARK,WARNINGLEVEL,parms->server,"mod_log_sql: do not set LogSQLCookieLogTable when LogSQLMassVirtualHosting is On. Ignoring.");
994 else
995 cls->cookie_table_name = arg;
996 return NULL;
997}
998
999const char *set_log_sql_notes_table(cmd_parms *parms, void *dummy, char *arg)
1000{
1001 logsql_state *cls = get_module_config(parms->server->module_config, &sql_log_module);
1002
1003 if (logsql_massvirtual != 0)
1004 ap_log_error(APLOG_MARK,WARNINGLEVEL,parms->server,"mod_log_sql: do not set LogSQLNotesLogTable when LogSQLMassVirtualHosting is On. Ignoring.");
1005 else
1006 cls->notes_table_name = arg;
1007 return NULL;
1008}
1009
1010const char *set_log_sql_hin_table(cmd_parms *parms, void *dummy, char *arg)
1011{
1012 logsql_state *cls = get_module_config(parms->server->module_config, &sql_log_module);
1013
1014 if (logsql_massvirtual != 0)
1015 ap_log_error(APLOG_MARK,WARNINGLEVEL,parms->server,"mod_log_sql: do not set LogSQLHeadersInLogTable when LogSQLMassVirtualHosting is On. Ignoring.");
1016 else
1017 cls->hin_table_name = arg;
1018 return NULL;
1019}
1020
1021const char *set_log_sql_hout_table(cmd_parms *parms, void *dummy, char *arg)
1022{
1023 logsql_state *cls = get_module_config(parms->server->module_config, &sql_log_module);
1024
1025 if (logsql_massvirtual != 0)
1026 ap_log_error(APLOG_MARK,WARNINGLEVEL,parms->server,"mod_log_sql: do not set LogSQLHeadersOutLogTable when LogSQLMassVirtualHosting is On. Ignoring.");
1027 else
1028 cls->hout_table_name = arg;
1029 return NULL;
1030}
1031
1032const char *set_log_sql_transfer_log_format(cmd_parms *parms, void *dummy, char *arg)
1033{
1034 logsql_state *cls = get_module_config(parms->server->module_config, &sql_log_module);
1035
1036 cls->transfer_log_format = arg;
1037 return NULL;
1038}
1039
1040const char *set_log_sql_socket_file(cmd_parms *parms, void *dummy, char *arg)
1041{
1042 logsql_socketfile = arg;
1043
1044 return NULL;
1045}
1046
1047const char *set_log_sql_tcp_port(cmd_parms *parms, void *dummy, char *arg)
1048{
1049 logsql_tcpport = (unsigned int)atoi(arg);
1050
1051 return NULL;
1052}
1053
1054const char *add_log_sql_transfer_accept(cmd_parms *parms, void *dummy, char *arg)
1055{
1056 char **addme;
1057 logsql_state *cls = get_module_config(parms->server->module_config, &sql_log_module);
1058
1059 addme = push_array(cls->transfer_accept_list);
1060 *addme = pstrdup(cls->transfer_accept_list->pool, arg);
1061 return NULL;
1062}
1063
1064const char *add_log_sql_transfer_ignore(cmd_parms *parms, void *dummy, char *arg)
1065{
1066 char **addme;
1067 logsql_state *cls = get_module_config(parms->server->module_config, &sql_log_module);
1068
1069 addme = push_array(cls->transfer_ignore_list);
1070 *addme = pstrdup(cls->transfer_ignore_list->pool, arg);
1071 return NULL;
1072}
1073
1074const char *add_log_sql_remhost_ignore(cmd_parms *parms, void *dummy, char *arg)
1075{
1076 char **addme;
1077 logsql_state *cls = get_module_config(parms->server->module_config, &sql_log_module);
1078
1079 addme = push_array(cls->remhost_ignore_list);
1080 *addme = pstrdup(cls->remhost_ignore_list->pool, arg);
1081 return NULL;
1082}
1083
1084const char *add_log_sql_note(cmd_parms *parms, void *dummy, char *arg)
1085{
1086 char **addme;
1087 logsql_state *cls = get_module_config(parms->server->module_config, &sql_log_module);
1088
1089 addme = push_array(cls->notes_list);
1090 *addme = pstrdup(cls->notes_list->pool, arg);
1091 return NULL;
1092}
1093
1094const char *add_log_sql_hout(cmd_parms *parms, void *dummy, char *arg)
1095{
1096 char **addme;
1097 logsql_state *cls = get_module_config(parms->server->module_config, &sql_log_module);
1098
1099 addme = push_array(cls->hout_list);
1100 *addme = pstrdup(cls->hout_list->pool, arg);
1101 return NULL;
1102}
1103
1104const char *add_log_sql_hin(cmd_parms *parms, void *dummy, char *arg)
1105{
1106 char **addme;
1107 logsql_state *cls = get_module_config(parms->server->module_config, &sql_log_module);
1108
1109 addme = push_array(cls->hin_list);
1110 *addme = pstrdup(cls->hin_list->pool, arg);
1111 return NULL;
1112}
1113
1114const char *add_log_sql_cookie(cmd_parms *parms, void *dummy, char *arg)
1115{
1116 char **addme;
1117 logsql_state *cls = get_module_config(parms->server->module_config, &sql_log_module);
1118
1119 addme = push_array(cls->cookie_list);
1120 *addme = pstrdup(cls->cookie_list->pool, arg);
1121 return NULL;
1122}
1123
1124
1125
1126/*------------------------------------------------------------*
1127 * Apache-specific hooks into the module code *
1128 * that are defined in the array 'mysql_lgog_module' (at EOF) *
1129 *------------------------------------------------------------*/
1130
1131
1132/*
1133 * This function is called during server initialisation when an heavy-weight
1134 * process (such as a child) is being initialised. As with the
1135 * module-initialisation function, any information that needs to be recorded
1136 * must be in static cells, since there's no configuration record.
1137 *
1138 * There is no return value.
1139 */
1140static void log_sql_child_init(server_rec *s, pool *p)
1141{
1142 int retval;
1143
1144 /* Open a link to the database */
1145 retval = open_logdb_link(s);
1146 if (!retval)
1147 ap_log_error(APLOG_MARK,ERRLEVEL,s,"mod_log_sql: child spawned but unable to open database link");
1148
1149 #ifdef DEBUG
1150 if ( (retval == 1) || (retval == 2) )
1151 ap_log_error(APLOG_MARK,DEBUGLEVEL,s,"mod_log_sql: open_logdb_link successful");
1152 if (retval == 3)
1153 ap_log_error(APLOG_MARK,DEBUGLEVEL,s,"mod_log_sql: open_logdb_link said that preservation is forced");
1154 #endif
1155}
1156
1157/*
1158 * This function is called when an heavy-weight process (such as a child) is
1159 * being run down or destroyed. As with the child-initialisation function,
1160 * any information that needs to be recorded must be in static cells, since
1161 * there's no configuration record.
1162 *
1163 * There is no return value.
1164 */
1165static void log_sql_child_exit(server_rec *s, pool *p)
1166{
1167 mysql_close(logsql_server_p);
1168}
1169
1170
1171/*
1172void *log_sql_initializer(server_rec *main_server, pool *p)
1173{
1174 server_rec *s;
1175
1176 logsql_state main_conf = ap_get_module_config(main_server->module_config, &sql_log_module);
1177
1178 for (server_rec *s = main_server; s; s = s->next) {
1179 conf = ap_get_module_config(s->module_config, &sql_log_module);
1180 if (conf->transfer_log_format == NULL && s != main_server) {
1181 *conf = *main_conf;
1182 }
1183
1184}
1185 */
1186
1187/*
1188 * This function gets called to create a per-server configuration
1189 * record. It will always be called for the main server and
1190 * for each virtual server that is established. Each server maintains
1191 * its own state that is separate from the others' states.
1192 *
1193 * The return value is a pointer to the created module-specific
1194 * structure.
1195 */
1196void *log_sql_make_state(pool *p, server_rec *s)
1197{
1198
1199 logsql_state *cls = (logsql_state *) ap_palloc(p, sizeof(logsql_state));
1200
1201 /* These defaults are overridable in the httpd.conf file. */
1202 cls->transfer_table_name = NULL; /* No default b/c we want its absence to disable logging */
1203 cls->transfer_log_format = "AbHhmRSsTUuv";
1204 cls->notes_table_name = "notes";
1205 cls->hin_table_name = "headers_in";
1206 cls->hout_table_name = "headers_out";
1207 cls->cookie_table_name = "cookies";
1208 cls->preserve_file = "/tmp/sql-preserve";
1209
1210 cls->transfer_ignore_list = make_array(p, 1, sizeof(char *));
1211 cls->transfer_accept_list = make_array(p, 1, sizeof(char *));
1212 cls->remhost_ignore_list = make_array(p, 1, sizeof(char *));
1213 cls->notes_list = make_array(p, 1, sizeof(char *));
1214 cls->hin_list = make_array(p, 1, sizeof(char *));
1215 cls->hout_list = make_array(p, 1, sizeof(char *));
1216 cls->cookie_list = make_array(p, 1, sizeof(char *));
1217 cls->cookie_name = NULL;
1218
1219 return (void *) cls;
1220}
1221
1222
1223/* Setup of the available httpd.conf configuration commands.
1224 * Structure: command, function called, NULL, where available, how many arguments, verbose description
1225 */
1226command_rec log_sql_cmds[] = {
1227 {"LogSQLTransferLogTable", set_log_sql_transfer_table, NULL, RSRC_CONF, TAKE1,
1228 "The database table that holds the transfer log"}
1229 ,
1230 {"LogSQLNotesLogTable", set_log_sql_notes_table, NULL, RSRC_CONF, TAKE1,
1231 "The database table that holds the notes"}
1232 ,
1233 {"LogSQLHeadersOutLogTable", set_log_sql_hout_table, NULL, RSRC_CONF, TAKE1,
1234 "The database table that holds the outbound headers"}
1235 ,
1236 {"LogSQLHeadersInLogTable", set_log_sql_hin_table, NULL, RSRC_CONF, TAKE1,
1237 "The database table that holds the inbound headers"}
1238 ,
1239 {"LogSQLCookieLogTable", set_log_sql_cookie_table, NULL, RSRC_CONF, TAKE1,
1240 "The database table that holds the cookie info"}
1241 ,
1242 {"LogSQLTransferLogFormat", set_log_sql_transfer_log_format, NULL, RSRC_CONF, TAKE1,
1243 "Instruct the module what information to log to the database transfer log"}
1244 ,
1245 {"LogSQLMachineID", set_log_sql_machine_id, NULL, RSRC_CONF, TAKE1,
1246 "Machine ID that the module will log, useful in web clusters to differentiate machines"}
1247 ,
1248 {"LogSQLRequestAccept", add_log_sql_transfer_accept, NULL, RSRC_CONF, ITERATE,
1249 "List of URIs to accept for logging. Accesses that don't match will not be logged"}
1250 ,
1251 {"LogSQLRequestIgnore", add_log_sql_transfer_ignore, NULL, RSRC_CONF, ITERATE,
1252 "List of URIs to ignore. Accesses that match will not be logged to database"}
1253 ,
1254 {"LogSQLRemhostIgnore", add_log_sql_remhost_ignore, NULL, RSRC_CONF, ITERATE,
1255 "List of remote hosts to ignore. Accesses that match will not be logged to database"}
1256 ,
1257 {"LogSQLDatabase", set_log_sql_db, NULL, RSRC_CONF, TAKE1,
1258 "The name of the database database for logging"}
1259 ,
1260 {"LogSQLWhichCookie", set_log_sql_cookie, NULL, RSRC_CONF, TAKE1,
1261 "The single cookie that you want logged in the access_log when using the 'c' config directive"}
1262 ,
1263 {"LogSQLLoginInfo", set_log_sql_info, NULL, RSRC_CONF, TAKE3,
1264 "The database host, user-id and password for logging"}
1265 ,
1266 {"LogSQLCreateTables", set_log_sql_create, NULL, RSRC_CONF, FLAG,
1267 "Turn on module's capability to create its SQL tables on the fly"}
1268 ,
1269 {"LogSQLMassVirtualHosting", set_log_sql_massvirtual, NULL, RSRC_CONF, FLAG,
1270 "Activates option(s) useful for ISPs performing mass virutal hosting"}
1271 ,
1272 {"LogSQLForcePreserve", set_log_sql_force_preserve, NULL, RSRC_CONF, FLAG,
1273 "Forces logging to preserve file and bypasses database"}
1274 ,
1275 {"LogSQLPreserveFile", set_log_sql_preserve_file, NULL, RSRC_CONF, TAKE1,
1276 "Name of the file to use for data preservation during database downtime"}
1277 ,
1278 {"LogSQLSocketFile", set_log_sql_socket_file, NULL, RSRC_CONF, TAKE1,
1279 "Name of the file to employ for socket connections to database"}
1280 ,
1281 {"LogSQLTCPPort", set_log_sql_tcp_port, NULL, RSRC_CONF, TAKE1,
1282 "Port number to use for TCP connections to database, defaults to 3306 if not set"}
1283 ,
1284 {"LogSQLWhichNotes", add_log_sql_note, NULL, RSRC_CONF, ITERATE,
1285 "Notes that you would like to log in a separate table"}
1286 ,
1287 {"LogSQLWhichHeadersOut", add_log_sql_hout, NULL, RSRC_CONF, ITERATE,
1288 "Outbound headers that you would like to log in a separate table"}
1289 ,
1290 {"LogSQLWhichHeadersIn", add_log_sql_hin, NULL, RSRC_CONF, ITERATE,
1291 "Inbound headers that you would like to log in a separate table"}
1292 ,
1293 {"LogSQLWhichCookies", add_log_sql_cookie, NULL, RSRC_CONF, ITERATE,
1294 "The cookie(s) that you would like to log in a separate table"}
1295 ,
1296 {NULL}
1297};
1298
1299
1300
1301/* Routine to perform the actual construction and execution of the relevant
1302 * INSERT statements.
1303 */
1304int log_sql_transaction(request_rec *orig)
1305{
1306 char **ptrptr, **ptrptr2;
1307 logsql_state *cls = get_module_config(orig->server->module_config, &sql_log_module);
1308 const char *access_query;
1309 request_rec *r;
1310
1311 /* We handle mass virtual hosting differently. Dynamically determine the name
1312 * of the table from the virtual server's name, and flag it for creation.
1313 */
1314 if (logsql_massvirtual) {
1315 char *access_base = "access_";
1316 char *notes_base = "notes_";
1317 char *hout_base = "headout_";
1318 char *hin_base = "headin_";
1319 char *cookie_base = "cookies_";
1320 char *a_tablename;
1321 char *n_tablename;
1322 char *i_tablename;
1323 char *o_tablename;
1324 char *c_tablename;
1325 unsigned int i;
1326
1327 /* Find memory long enough to hold the table name + \0. */
1328 a_tablename = ap_pstrcat(orig->pool, access_base, ap_get_server_name(orig), NULL);
1329 n_tablename = ap_pstrcat(orig->pool, notes_base, ap_get_server_name(orig), NULL);
1330 i_tablename = ap_pstrcat(orig->pool, hin_base, ap_get_server_name(orig), NULL);
1331 o_tablename = ap_pstrcat(orig->pool, hout_base, ap_get_server_name(orig), NULL);
1332 c_tablename = ap_pstrcat(orig->pool, cookie_base, ap_get_server_name(orig), NULL);
1333
1334 /* Transform any dots to underscores */
1335 for (i = 0; i < strlen(a_tablename); i++) {
1336 if (a_tablename[i] == '.')
1337 a_tablename[i] = '_';
1338 }
1339 for (i = 0; i < strlen(n_tablename); i++) {
1340 if (n_tablename[i] == '.')
1341 n_tablename[i] = '_';
1342 }
1343 for (i = 0; i < strlen(i_tablename); i++) {
1344 if (i_tablename[i] == '.')
1345 i_tablename[i] = '_';
1346 }
1347 for (i = 0; i < strlen(o_tablename); i++) {
1348 if (o_tablename[i] == '.')
1349 o_tablename[i] = '_';
1350 }
1351 for (i = 0; i < strlen(c_tablename); i++) {
1352 if (c_tablename[i] == '.')
1353 c_tablename[i] = '_';
1354 }
1355
1356 /* Tell this virtual server its transfer table name, and
1357 * turn on create_tables, which is implied by massvirtual.
1358 */
1359 cls->transfer_table_name = a_tablename;
1360 cls->notes_table_name = n_tablename;
1361 cls->hout_table_name = o_tablename;
1362 cls->hin_table_name = i_tablename;
1363 cls->cookie_table_name = c_tablename;
1364 logsql_createtables = 1;
1365 }
1366
1367 /* Do we have enough info to log? */
1368 if (!cls->transfer_table_name) {
1369 return DECLINED;
1370 } else {
1371 const char *thehost;
1372 const char *theitem;
1373 char *fields = "", *values = "";
1374 char *itemsets = "";
1375 char *note_query = NULL;
1376 char *hin_query = NULL;
1377 char *hout_query = NULL;
1378 char *cookie_query = NULL;
1379 const char *unique_id;
1380 const char *formatted_item;
1381 int i, j, length;
1382 int proceed;
1383
1384 for (r = orig; r->next; r = r->next) {
1385 continue;
1386 }
1387
1388 /* The following is a stolen upsetting mess of pointers, I'm sorry.
1389 * Anyone with the motiviation and/or the time should feel free
1390 * to make this cleaner. :) */
1391 ptrptr2 = (char **) (cls->transfer_accept_list->elts + (cls->transfer_accept_list->nelts * cls->transfer_accept_list->elt_size));
1392
1393 /* Go through each element of the accept list and compare it to the
1394 * request_uri. If we don't get a match, return without logging */
1395 if ((r->uri) && (cls->transfer_accept_list->nelts)) {
1396 proceed = 0;
1397 for (ptrptr = (char **) cls->transfer_accept_list->elts; ptrptr < ptrptr2; ptrptr = (char **) ((char *) ptrptr + cls->transfer_accept_list->elt_size))
1398 if (strstr(r->uri, *ptrptr)) {
1399 proceed = 1;
1400 break;
1401 }
1402 if (!proceed)
1403 return OK;
1404 }
1405
1406 /* Go through each element of the ignore list and compare it to the
1407 * request_uri. If we get a match, return without logging */
1408 ptrptr2 = (char **) (cls->transfer_ignore_list->elts + (cls->transfer_ignore_list->nelts * cls->transfer_ignore_list->elt_size));
1409 if (r->uri) {
1410 for (ptrptr = (char **) cls->transfer_ignore_list->elts; ptrptr < ptrptr2; ptrptr = (char **) ((char *) ptrptr + cls->transfer_ignore_list->elt_size))
1411 if (strstr(r->uri, *ptrptr)) {
1412 return OK;
1413 }
1414 }
1415
1416 /* Go through each element of the ignore list and compare it to the
1417 * remote host. If we get a match, return without logging */
1418 ptrptr2 = (char **) (cls->remhost_ignore_list->elts + (cls->remhost_ignore_list->nelts * cls->remhost_ignore_list->elt_size));
1419 thehost = get_remote_host(r->connection, r->per_dir_config, REMOTE_NAME);
1420 if (thehost) {
1421 for (ptrptr = (char **) cls->remhost_ignore_list->elts; ptrptr < ptrptr2; ptrptr = (char **) ((char *) ptrptr + cls->remhost_ignore_list->elt_size))
1422 if (strstr(thehost, *ptrptr)) {
1423 return OK;
1424 }
1425 }
1426
1427 length = strlen(cls->transfer_log_format);
1428
1429 /* Iterate through the format characters and set up the INSERT string according to
1430 * what the user has configured. */
1431 for (i = 0; i < length; i++) {
1432 j = 0;
1433
1434 while (log_sql_item_keys[j].ch) {
1435
1436 if (log_sql_item_keys[j].ch == cls->transfer_log_format[i]) {
1437 /* Yes, this key is one of the configured keys.
1438 * Call the key's function and put the returned value into 'formatted_item' */
1439 formatted_item = log_sql_item_keys[j].func(log_sql_item_keys[j].want_orig_default ? orig : r, "");
1440
1441 /* Massage 'formatted_item' for proper SQL eligibility... */
1442 if (!formatted_item) {
1443 formatted_item = "";
1444 } else if (formatted_item[0] == '-' && formatted_item[1] == '\0' && !log_sql_item_keys[j].string_contents) {
1445 /* If apache tried to log a '-' character for a numeric field, convert that to a zero
1446 * because the database expects a numeral and will reject the '-' character. */
1447 formatted_item = "0";
1448 }
1449
1450 /* Append the fieldname and value-to-insert to the appropriate strings, quoting stringvals with ' as appropriate */
1451 fields = pstrcat(r->pool, fields, (i > 0 ? "," : ""),
1452 log_sql_item_keys[j].sql_field_name, NULL);
1453
1454 values = pstrcat(r->pool, values, (i > 0 ? "," : ""),
1455 (log_sql_item_keys[j].string_contents ? "'" : ""),
1456 escape_query(formatted_item, r->pool),
1457 (log_sql_item_keys[j].string_contents ? "'" : ""), NULL);
1458 break;
1459 }
1460 j++;
1461
1462 }
1463 }
1464
1465 /* Work through the list of notes defined by LogSQLWhichNotes */
1466 i = 0;
1467 unique_id = extract_unique_id(r, "");
1468
1469 ptrptr2 = (char **) (cls->notes_list->elts + (cls->notes_list->nelts * cls->notes_list->elt_size));
1470 for (ptrptr = (char **) cls->notes_list->elts; ptrptr < ptrptr2; ptrptr = (char **) ((char *) ptrptr + cls->notes_list->elt_size)) {
1471 /* If the specified note (*ptrptr) exists for the current request... */
1472 if ((theitem = ap_table_get(r->notes, *ptrptr))) {
1473 itemsets = ap_pstrcat(r->pool, itemsets,
1474 (i > 0 ? "," : ""),
1475 "('",
1476 unique_id,
1477 "','",
1478 escape_query(*ptrptr, r->pool),
1479 "','",
1480 escape_query(theitem, r->pool),
1481 "')",
1482 NULL);
1483 i++;
1484 }
1485 }
1486 if ( itemsets != "" ) {
1487 note_query = ap_pstrcat(r->pool, logsql_insertclause, "`", cls->notes_table_name, "` (id, item, val) values ", itemsets, NULL);
1488 #ifdef DEBUG
1489 ap_log_error(APLOG_MARK,DEBUGLEVEL,orig->server,"mod_log_sql: note string: %s", note_query);
1490 #endif
1491 }
1492
1493 /* Work through the list of headers-out defined by LogSQLWhichHeadersOut*/
1494 i = 0;
1495 itemsets = "";
1496
1497 ptrptr2 = (char **) (cls->hout_list->elts + (cls->hout_list->nelts * cls->hout_list->elt_size));
1498 for (ptrptr = (char **) cls->hout_list->elts; ptrptr < ptrptr2; ptrptr = (char **) ((char *) ptrptr + cls->hout_list->elt_size)) {
1499 /* If the specified header (*ptrptr) exists for the current request... */
1500 if ((theitem = ap_table_get(r->headers_out, *ptrptr))) {
1501 itemsets = ap_pstrcat(r->pool, itemsets,
1502 (i > 0 ? "," : ""),
1503 "('",
1504 unique_id,
1505 "','",
1506 escape_query(*ptrptr, r->pool),
1507 "','",
1508 escape_query(theitem, r->pool),
1509 "')",
1510 NULL);
1511 i++;
1512 }
1513 }
1514 if ( itemsets != "" ) {
1515 hout_query = ap_pstrcat(r->pool, logsql_insertclause, "`", cls->hout_table_name, "` (id, item, val) values ", itemsets, NULL);
1516 #ifdef DEBUG
1517 ap_log_error(APLOG_MARK,DEBUGLEVEL,orig->server,"mod_log_sql: header_out string: %s", hout_query);
1518 #endif
1519 }
1520
1521
1522 /* Work through the list of headers-in defined by LogSQLWhichHeadersIn */
1523 i = 0;
1524 itemsets = "";
1525
1526 ptrptr2 = (char **) (cls->hin_list->elts + (cls->hin_list->nelts * cls->hin_list->elt_size));
1527 for (ptrptr = (char **) cls->hin_list->elts; ptrptr < ptrptr2; ptrptr = (char **) ((char *) ptrptr + cls->hin_list->elt_size)) {
1528 /* If the specified header (*ptrptr) exists for the current request... */
1529 if ((theitem = ap_table_get(r->headers_in, *ptrptr))) {
1530 itemsets = ap_pstrcat(r->pool, itemsets,
1531 (i > 0 ? "," : ""),
1532 "('",
1533 unique_id,
1534 "','",
1535 escape_query(*ptrptr, r->pool),
1536 "','",
1537 escape_query(theitem, r->pool),
1538 "')",
1539 NULL);
1540 i++;
1541 }
1542 }
1543 if ( itemsets != "" ) {
1544 hin_query = ap_pstrcat(r->pool, logsql_insertclause, "`", cls->hin_table_name, "` (id, item, val) values ", itemsets, NULL);
1545 #ifdef DEBUG
1546 ap_log_error(APLOG_MARK,DEBUGLEVEL,orig->server,"mod_log_sql: header_in string: %s", hin_query);
1547 #endif
1548 }
1549
1550
1551 /* Work through the list of cookies defined by LogSQLWhichCookies */
1552 i = 0;
1553 itemsets = "";
1554
1555 ptrptr2 = (char **) (cls->cookie_list->elts + (cls->cookie_list->nelts * cls->cookie_list->elt_size));
1556 for (ptrptr = (char **) cls->cookie_list->elts; ptrptr < ptrptr2; ptrptr = (char **) ((char *) ptrptr + cls->cookie_list->elt_size)) {
1557 /* If the specified cookie (*ptrptr) exists for the current request... */
1558 if ( strncmp((theitem = extract_specific_cookie(r, *ptrptr)), "-", 1) ) {
1559 itemsets = ap_pstrcat(r->pool, itemsets,
1560 (i > 0 ? "," : ""),
1561 "('",
1562 unique_id,
1563 "','",
1564 escape_query(*ptrptr, r->pool),
1565 "','",
1566 escape_query(theitem, r->pool),
1567 "')",
1568 NULL);
1569 i++;
1570 }
1571
1572 }
1573 if ( itemsets != "" ) {
1574 cookie_query = ap_pstrcat(r->pool, logsql_insertclause, "`", cls->cookie_table_name, "` (id, item, val) values ", itemsets, NULL);
1575 #ifdef DEBUG
1576 ap_log_error(APLOG_MARK,DEBUGLEVEL,orig->server,"mod_log_sql: cookie string: %s", cookie_query);
1577 #endif
1578 }
1579
1580
1581 /* Set up the actual INSERT statement */
1582 access_query = ap_pstrcat(r->pool, logsql_insertclause, "`", cls->transfer_table_name, "` (", fields, ") values (", values, ")", NULL);
1583
1584 #ifdef DEBUG
1585 ap_log_error(APLOG_MARK,DEBUGLEVEL,r->server,"mod_log_sql: access string: %s", access_query);
1586 #endif
1587
1588 /* If the person activated force-preserve, go ahead and push all the entries
1589 * into the preserve file, then return.
1590 */
1591 if (logsql_forcepreserve) {
1592 #ifdef DEBUG
1593 ap_log_error(APLOG_MARK,DEBUGLEVEL,orig->server,"mod_log_sql: preservation forced");
1594 #endif
1595 preserve_entry(orig, access_query);
1596 if ( note_query != NULL )
1597 preserve_entry(orig, note_query);
1598 if ( hin_query != NULL )
1599 preserve_entry(orig, hin_query);
1600 if ( hout_query != NULL )
1601 preserve_entry(orig, hout_query);
1602 if ( cookie_query != NULL )
1603 preserve_entry(orig, cookie_query);
1604 return OK;
1605 }
1606
1607 /* How's our mysql link integrity? */
1608 if (logsql_server_p == NULL) {
1609
1610 /* Make a try to establish the link */
1611 open_logdb_link(r->server);
1612
1613 if (logsql_server_p == NULL) {
1614 /* Unable to re-establish a DB link, so assume that it's really
1615 * gone and send the entry to the preserve file instead.
1616 * This short-circuits safe_sql_query() during a db outage and therefore
1617 * we don't keep logging the db error over and over.
1618 */
1619 preserve_entry(orig, access_query);
1620 if ( note_query != NULL )
1621 preserve_entry(orig, note_query);
1622 if ( hin_query != NULL )
1623 preserve_entry(orig, hin_query);
1624 if ( hout_query != NULL )
1625 preserve_entry(orig, hout_query);
1626 if ( cookie_query != NULL )
1627 preserve_entry(orig, cookie_query);
1628
1629 return OK;
1630 } else {
1631 /* Whew, we got the DB link back */
1632 ap_log_error(APLOG_MARK,NOTICELEVEL,orig->server,"mod_log_sql: child established database connection");
1633 }
1634 }
1635
1636
1637 /* ---> So as of here we have a non-null value of mysql_log. <--- */
1638 /* ---> i.e. we have a good MySQL connection. <--- */
1639
1640 /* Make the access-table insert */
1641 safe_sql_query(orig, access_query);
1642
1643 /* Log the optional notes, headers, etc. */
1644 if (note_query)
1645 safe_sql_query(orig, note_query);
1646
1647 if (hout_query)
1648 safe_sql_query(orig, hout_query);
1649
1650 if (hin_query)
1651 safe_sql_query(orig, hin_query);
1652
1653 if (cookie_query)
1654 safe_sql_query(orig, cookie_query);
1655
1656 return OK;
1657 }
1658}
1659
1660
1661
1662
1663/* The configuration array that sets up the hooks into the module. */
1664module sql_log_module = {
1665 STANDARD_MODULE_STUFF,
1666 NULL, /* module initializer */
1667 NULL, /* create per-dir config */
1668 NULL, /* merge per-dir config */
1669 log_sql_make_state, /* create server config */
1670 NULL, /* merge server config */
1671 log_sql_cmds, /* config directive table */
1672 NULL, /* [9] content handlers */
1673 NULL, /* [2] URI-to-filename translation */
1674 NULL, /* [5] check/validate user_id */
1675 NULL, /* [6] check authorization */
1676 NULL, /* [4] check access by host */
1677 NULL, /* [7] MIME type checker/setter */
1678 NULL, /* [8] fixups */
1679 log_sql_transaction, /* [10] logger */
1680 NULL /* [3] header parser */
1681#if MODULE_MAGIC_NUMBER >= 19970728 /* 1.3-dev or later support these additionals... */
1682 ,log_sql_child_init, /* child process initializer */
1683 log_sql_child_exit, /* process exit/cleanup */
1684 NULL /* [1] post read-request */
1685#endif
1686
1687};