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