upload http
[bottlenecks.git] / rubbos / app / httpd-2.0.64 / modules / loggers / mod_log_forensic.c
1 /* Licensed to the Apache Software Foundation (ASF) under one or more
2  * contributor license agreements.  See the NOTICE file distributed with
3  * this work for additional information regarding copyright ownership.
4  * The ASF licenses this file to You under the Apache License, Version 2.0
5  * (the "License"); you may not use this file except in compliance with
6  * the License.  You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 /*
18  * See also support/check_forensic.
19  * Relate the forensic log to the transfer log by including
20  * %{forensic-id}n in the custom log format, for example:
21  * CustomLog logs/custom "%h %l %u %t \"%r\" %>s %b %{forensic-id}n"
22  *
23  * Credit is due to Tina Bird <tbird precision-guesswork.com>, whose
24  * idea this module was.
25  *
26  *   Ben Laurie 29/12/2003
27  */
28
29 #include "httpd.h"
30 #include "http_config.h"
31 #include "http_log.h"
32 #include "apr_strings.h"
33 #include "apr_atomic.h"
34 #include "http_protocol.h"
35 #include "test_char.h"
36 #if APR_HAVE_UNISTD_H
37 #include <unistd.h>
38 #endif
39
40 module AP_MODULE_DECLARE_DATA log_forensic_module;
41
42 typedef struct fcfg {
43     const char *logname;
44     apr_file_t *fd;
45 } fcfg;
46
47 static void *make_forensic_log_scfg(apr_pool_t *p, server_rec *s)
48 {
49     fcfg *cfg = apr_pcalloc(p, sizeof *cfg);
50
51     cfg->logname = NULL;
52     cfg->fd = NULL;
53
54     return cfg;
55 }
56
57 static void *merge_forensic_log_scfg(apr_pool_t *p, void *parent, void *new)
58 {
59     fcfg *cfg = apr_pcalloc(p, sizeof *cfg);
60     fcfg *pc = parent;
61     fcfg *nc = new;
62
63     cfg->logname = apr_pstrdup(p, nc->logname ? nc->logname : pc->logname);
64     cfg->fd = NULL;
65
66     return cfg;
67 }
68
69 static int open_log(server_rec *s, apr_pool_t *p)
70 {
71     fcfg *cfg = ap_get_module_config(s->module_config, &log_forensic_module);
72
73     if (!cfg->logname || cfg->fd)
74         return 1;
75
76     if (*cfg->logname == '|') {
77         piped_log *pl;
78         const char *pname = ap_server_root_relative(p, cfg->logname + 1);
79
80         pl = ap_open_piped_log(p, pname);
81         if (pl == NULL) {
82             ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
83                          "couldn't spawn forensic log pipe %s", cfg->logname);
84             return 0;
85         }
86         cfg->fd = ap_piped_log_write_fd(pl);
87     }
88     else {
89         const char *fname = ap_server_root_relative(p, cfg->logname);
90         apr_status_t rv;
91
92         if ((rv = apr_file_open(&cfg->fd, fname,
93                                 APR_WRITE | APR_APPEND | APR_CREATE,
94                                 APR_OS_DEFAULT, p)) != APR_SUCCESS) {
95             ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
96                          "could not open forensic log file %s.", fname);
97             return 0;
98         }
99     }
100
101     return 1;
102 }
103
104 static int log_init(apr_pool_t *pc, apr_pool_t *p, apr_pool_t *pt,
105                      server_rec *s)
106 {
107     for ( ; s ; s = s->next) {
108         if (!open_log(s, p)) {
109             return HTTP_INTERNAL_SERVER_ERROR;
110         }
111     }
112
113     return OK;
114 }
115
116   
117 /* e is the first _invalid_ location in q
118    N.B. returns the terminating NUL.
119  */
120 static char *log_escape(char *q, const char *e, const char *p)
121 {
122     for ( ; *p ; ++p) {
123         ap_assert(q < e);
124         if (test_char_table[*(unsigned char *)p]&T_ESCAPE_FORENSIC) {
125             ap_assert(q+2 < e);
126             *q++ = '%';
127             sprintf(q, "%02x", *(unsigned char *)p);
128             q += 2;
129         }
130         else
131             *q++ = *p;
132     }
133     ap_assert(q < e);
134     *q = '\0';
135
136     return q;
137 }
138
139 typedef struct hlog {
140     char *log;
141     char *pos;
142     char *end;
143     apr_pool_t *p;
144     apr_size_t count;
145 } hlog;
146
147 static int count_string(const char *p)
148 {
149     int n;
150
151     for (n = 0 ; *p ; ++p, ++n)
152         if (test_char_table[*(unsigned char *)p]&T_ESCAPE_FORENSIC)
153             n += 2;
154     return n;
155 }
156
157 static int count_headers(void *h_, const char *key, const char *value)
158 {
159     hlog *h = h_;
160
161     h->count += count_string(key)+count_string(value)+2;
162
163     return 1;
164 }
165
166 static int log_headers(void *h_, const char *key, const char *value)
167 {
168     hlog *h = h_;
169
170     /* note that we don't have to check h->pos here, coz its been done
171        for us by log_escape */
172     *h->pos++ = '|';
173     h->pos = log_escape(h->pos, h->end, key);
174     *h->pos++ = ':';
175     h->pos = log_escape(h->pos, h->end, value);
176
177     return 1;
178 }
179
180 static int log_before(request_rec *r)
181 {
182     fcfg *cfg = ap_get_module_config(r->server->module_config,
183                                      &log_forensic_module);
184     const char *id;
185     hlog h;
186     apr_size_t n;
187     apr_status_t rv;
188
189     if (!cfg->fd || r->prev) {
190         return DECLINED;
191     }
192
193     if (!(id = apr_table_get(r->subprocess_env, "UNIQUE_ID"))) {
194         /* we make the assumption that we can't go through all the PIDs in
195            under 1 second */
196         ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
197                     "mod_log_forensic: mod_unique_id must also be active");
198         return DECLINED;
199     }
200     ap_set_module_config(r->request_config, &log_forensic_module, (char *)id);
201
202     h.p = r->pool;
203     h.count = 0;
204
205     apr_table_do(count_headers, &h, r->headers_in, NULL);
206
207     h.count += 1+strlen(id)+1+count_string(r->the_request)+1+1;
208     h.log = apr_palloc(r->pool, h.count);
209     h.pos = h.log;
210     h.end = h.log+h.count;
211
212     *h.pos++ = '+';
213     strcpy(h.pos, id);
214     h.pos += strlen(h.pos);
215     *h.pos++ = '|';
216     h.pos = log_escape(h.pos, h.end, r->the_request);
217
218     apr_table_do(log_headers, &h, r->headers_in, NULL);
219
220     ap_assert(h.pos < h.end);
221     *h.pos++ = '\n';
222
223     n = h.count-1;
224     rv = apr_file_write(cfg->fd, h.log, &n);
225     ap_assert(rv == APR_SUCCESS && n == h.count-1);
226
227     apr_table_setn(r->notes, "forensic-id", id);
228
229     return OK;
230 }
231
232 static int log_after(request_rec *r)
233 {
234     fcfg *cfg = ap_get_module_config(r->server->module_config,
235                                      &log_forensic_module);
236     const char *id = ap_get_module_config(r->request_config,
237                                           &log_forensic_module);
238     char *s;
239     apr_size_t l, n;
240     apr_status_t rv;
241
242     if (!cfg->fd) {
243         return DECLINED;
244     }
245
246     s = apr_pstrcat(r->pool, "-", id, "\n", NULL);
247     l = n = strlen(s);
248     rv = apr_file_write(cfg->fd, s, &n);
249     ap_assert(rv == APR_SUCCESS && n == l);
250
251     return OK;
252 }
253
254 static const char *set_forensic_log(cmd_parms *cmd, void *dummy, const char *fn)
255 {
256     fcfg *cfg = ap_get_module_config(cmd->server->module_config,
257                                      &log_forensic_module);
258
259     cfg->logname = fn;
260     return NULL;
261 }
262
263 static const command_rec forensic_log_cmds[] =
264 {
265     AP_INIT_TAKE1("ForensicLog",  set_forensic_log,  NULL,  RSRC_CONF,
266                   "the filename of the forensic log"),
267     { NULL }
268 };
269
270 static void register_hooks(apr_pool_t *p)
271 {
272     static const char * const pre[] = { "mod_unique_id.c", NULL };
273
274     ap_hook_open_logs(log_init,NULL,NULL,APR_HOOK_MIDDLE);
275     ap_hook_post_read_request(log_before,pre,NULL,APR_HOOK_REALLY_FIRST);
276     ap_hook_log_transaction(log_after,NULL,NULL,APR_HOOK_REALLY_LAST);
277 }
278
279 module AP_MODULE_DECLARE_DATA log_forensic_module =
280 {
281     STANDARD20_MODULE_STUFF,
282     NULL,                       /* create per-dir config */
283     NULL,                       /* merge per-dir config */
284     make_forensic_log_scfg,     /* server config */
285     merge_forensic_log_scfg,    /* merge server config */
286     forensic_log_cmds,          /* command apr_table_t */
287     register_hooks              /* register hooks */
288 };