]> git.ipfire.org Git - thirdparty/squid.git/blame - src/access_log.cc
merge changes from SQUID_2_3 branch
[thirdparty/squid.git] / src / access_log.cc
CommitLineData
f892c2bf 1
47130615 2
f892c2bf 3/*
7e3ce7b9 4 * $Id: access_log.cc,v 1.53 1999/12/30 17:36:19 wessels Exp $
f892c2bf 5 *
6 * DEBUG: section 46 Access Log
7 * AUTHOR: Duane Wessels
8 *
9 * SQUID Internet Object Cache http://squid.nlanr.net/Squid/
e25c139f 10 * ----------------------------------------------------------
f892c2bf 11 *
12 * Squid is the result of efforts by numerous individuals from the
13 * Internet community. Development is led by Duane Wessels of the
e25c139f 14 * National Laboratory for Applied Network Research and funded by the
15 * National Science Foundation. Squid is Copyrighted (C) 1998 by
16 * Duane Wessels and the University of California San Diego. Please
17 * see the COPYRIGHT file for full details. Squid incorporates
18 * software developed and/or copyrighted by other sources. Please see
19 * the CREDITS file for full details.
f892c2bf 20 *
21 * This program is free software; you can redistribute it and/or modify
22 * it under the terms of the GNU General Public License as published by
23 * the Free Software Foundation; either version 2 of the License, or
24 * (at your option) any later version.
25 *
26 * This program is distributed in the hope that it will be useful,
27 * but WITHOUT ANY WARRANTY; without even the implied warranty of
28 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
29 * GNU General Public License for more details.
30 *
31 * You should have received a copy of the GNU General Public License
32 * along with this program; if not, write to the Free Software
cbdec147 33 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
e25c139f 34 *
f892c2bf 35 */
36
37
38#include "squid.h"
39
a7c05555 40static void accessLogOpen(const char *fname);
41static char *log_quote(const char *header);
eeb423fb 42static void accessLogSquid(AccessLogEntry * al, MemBuf * mb);
43static void accessLogCommon(AccessLogEntry * al, MemBuf * mb);
a7c05555 44
e66d7923 45#if MULTICAST_MISS_STREAM
46static int mcast_miss_fd = -1;
47static struct sockaddr_in mcast_miss_to;
48static void mcast_encode(unsigned int *, size_t, const unsigned int *);
49#endif
50
7a2f978b 51const char *log_tags[] =
52{
53 "NONE",
54 "TCP_HIT",
55 "TCP_MISS",
56 "TCP_REFRESH_HIT",
57 "TCP_REF_FAIL_HIT",
58 "TCP_REFRESH_MISS",
ee1679df 59 "TCP_CLIENT_REFRESH_MISS",
7a2f978b 60 "TCP_IMS_HIT",
7a2f978b 61 "TCP_SWAPFAIL_MISS",
62 "TCP_NEGATIVE_HIT",
63 "TCP_MEM_HIT",
79a15e0a 64 "TCP_DENIED",
b540e168 65 "TCP_OFFLINE_HIT",
7a2f978b 66 "UDP_HIT",
7a2f978b 67 "UDP_MISS",
68 "UDP_DENIED",
69 "UDP_INVALID",
70 "UDP_MISS_NOFETCH",
071a3ae7 71 "ICP_QUERY",
7a2f978b 72 "LOG_TYPE_MAX"
73};
74
d21f1c54 75#if FORW_VIA_DB
76typedef struct {
1afe05c5 77 char *key;
78 void *next;
79 int n;
d21f1c54 80} fvdb_entry;
81static hash_table *via_table = NULL;
82static hash_table *forw_table = NULL;
83static void fvdbInit(void);
1afe05c5 84static void fvdbDumpTable(StoreEntry * e, hash_table * hash);
85static void fvdbCount(hash_table * hash, const char *key);
d21f1c54 86static OBJH fvdbDumpVia;
87static OBJH fvdbDumpForw;
88static FREE fvdbFreeEntry;
89static void fvdbClear(void);
90#endif
f892c2bf 91
92static int LogfileStatus = LOG_DISABLE;
93static int LogfileFD = -1;
94static char LogfileName[SQUID_MAXPATHLEN];
95#define LOG_BUF_SZ (MAX_URL<<2)
f892c2bf 96
97static const char c2x[] =
98"000102030405060708090a0b0c0d0e0f"
99"101112131415161718191a1b1c1d1e1f"
100"202122232425262728292a2b2c2d2e2f"
101"303132333435363738393a3b3c3d3e3f"
102"404142434445464748494a4b4c4d4e4f"
103"505152535455565758595a5b5c5d5e5f"
104"606162636465666768696a6b6c6d6e6f"
105"707172737475767778797a7b7c7d7e7f"
106"808182838485868788898a8b8c8d8e8f"
107"909192939495969798999a9b9c9d9e9f"
108"a0a1a2a3a4a5a6a7a8a9aaabacadaeaf"
109"b0b1b2b3b4b5b6b7b8b9babbbcbdbebf"
110"c0c1c2c3c4c5c6c7c8c9cacbcccdcecf"
111"d0d1d2d3d4d5d6d7d8d9dadbdcdddedf"
112"e0e1e2e3e4e5e6e7e8e9eaebecedeeef"
113"f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff";
114
115/* log_quote -- URL-style encoding on MIME headers. */
116
117static char *
118log_quote(const char *header)
119{
718644ff 120 int c;
121 int i;
122 char *buf;
123 char *buf_cursor;
f892c2bf 124 if (header == NULL) {
125 buf = xcalloc(1, 1);
126 *buf = '\0';
127 return buf;
128 }
129 buf = xcalloc((strlen(header) * 3) + 1, 1);
130 buf_cursor = buf;
131 /*
132 * We escape: \x00-\x1F"#%;<>?{}|\\\\^~`\[\]\x7F-\xFF
133 * which is the default escape list for the CPAN Perl5 URI module
134 * modulo the inclusion of space (x40) to make the raw logs a bit
135 * more readable.
136 */
79d39a72 137 while ((c = *(const unsigned char *) header++) != '\0') {
7e3ce7b9 138#if !OLD_LOG_MIME
139 if (c == '\r') {
140 *buf_cursor++ = '\\';
141 *buf_cursor++ = 'r';
142 } else if (c == '\n') {
143 *buf_cursor++ = '\\';
144 *buf_cursor++ = 'n';
145 } else
146#endif
147 if (c <= 0x1F
148 || c >= 0x7F
149#if OLD_LOG_MIME
150 || c == '"'
151 || c == '#'
152 || c == '%'
153 || c == ';'
154 || c == '<'
155 || c == '>'
156 || c == '?'
157 || c == '{'
158 || c == '}'
159 || c == '|'
160 || c == '\\'
161 || c == '^'
162 || c == '~'
163 || c == '`'
164#endif
165 || c == '['
f892c2bf 166 || c == ']') {
167 *buf_cursor++ = '%';
168 i = c * 2;
169 *buf_cursor++ = c2x[i];
170 *buf_cursor++ = c2x[i + 1];
7e3ce7b9 171#if !OLD_LOG_MIME
172 } else if (c == '\\') {
173 *buf_cursor++ = '\\';
174 *buf_cursor++ = '\\';
175#endif
f892c2bf 176 } else {
79d39a72 177 *buf_cursor++ = (char) c;
f892c2bf 178 }
179 }
180 *buf_cursor = '\0';
181 return buf;
182}
183
137ee196 184static void
eeb423fb 185accessLogSquid(AccessLogEntry * al, MemBuf * mb)
f892c2bf 186{
e7b53d5d 187 const char *client = NULL;
17a0a4ee 188 if (Config.onoff.log_fqdn)
b22b7951 189 client = fqdncache_gethostbyaddr(al->cache.caddr, FQDN_LOOKUP_IF_MISS);
f892c2bf 190 if (client == NULL)
191 client = inet_ntoa(al->cache.caddr);
137ee196 192 memBufPrintf(mb, "%9d.%03d %6d %s %s/%03d %d %s %s %s %s%s/%s %s",
f892c2bf 193 (int) current_time.tv_sec,
194 (int) current_time.tv_usec / 1000,
195 al->cache.msec,
196 client,
197 log_tags[al->cache.code],
198 al->http.code,
199 al->cache.size,
200 al->private.method_str,
201 al->url,
202 al->cache.ident,
b4e7f82d 203 al->hier.ping.timedout ? "TIMEOUT_" : "",
f892c2bf 204 hier_strings[al->hier.code],
205 al->hier.host,
206 al->http.content_type);
207}
208
137ee196 209static void
eeb423fb 210accessLogCommon(AccessLogEntry * al, MemBuf * mb)
f892c2bf 211{
3a0bd885 212 const char *client = NULL;
17a0a4ee 213 if (Config.onoff.log_fqdn)
f892c2bf 214 client = fqdncache_gethostbyaddr(al->cache.caddr, 0);
215 if (client == NULL)
216 client = inet_ntoa(al->cache.caddr);
c68e9c6b 217 memBufPrintf(mb, "%s %s - [%s] \"%s %s HTTP/%.1f\" %d %d %s:%s",
f892c2bf 218 client,
219 al->cache.ident,
220 mkhttpdlogtime(&squid_curtime),
221 al->private.method_str,
222 al->url,
c68e9c6b 223 al->http.version,
f892c2bf 224 al->http.code,
225 al->cache.size,
226 log_tags[al->cache.code],
227 hier_strings[al->hier.code]);
228}
229
a7c05555 230static void
231accessLogOpen(const char *fname)
232{
233 assert(fname);
234 xstrncpy(LogfileName, fname, SQUID_MAXPATHLEN);
1a94f598 235 LogfileFD = file_open(LogfileName, O_WRONLY | O_CREAT);
a7c05555 236 if (LogfileFD == DISK_ERROR) {
237 debug(50, 0) ("%s: %s\n", LogfileName, xstrerror());
6b8e7481 238 fatalf("Cannot open %s: %s", LogfileName, xstrerror());
a7c05555 239 }
240 LogfileStatus = LOG_ENABLE;
241}
242
f892c2bf 243void
244accessLogLog(AccessLogEntry * al)
245{
137ee196 246 MemBuf mb;
3a0bd885 247 char *xbuf = NULL;
3a459d9d 248 LOCAL_ARRAY(char, ident_buf, USER_IDENT_SZ);
137ee196 249
f892c2bf 250 if (LogfileStatus != LOG_ENABLE)
251 return;
252 if (al->url == NULL)
253 al->url = dash_str;
254 if (!al->http.content_type || *al->http.content_type == '\0')
255 al->http.content_type = dash_str;
3a0bd885 256 if (!al->cache.ident || *al->cache.ident == '\0') {
3a459d9d 257 al->cache.ident = dash_str;
258 } else {
259 xstrncpy(ident_buf, rfc1738_escape(al->cache.ident), USER_IDENT_SZ);
260 al->cache.ident = ident_buf;
3a0bd885 261 }
f892c2bf 262 if (al->icp.opcode)
27cd7235 263 al->private.method_str = icp_opcode_str[al->icp.opcode];
f892c2bf 264 else
265 al->private.method_str = RequestMethodStr[al->http.method];
4ea8f861 266 if (al->hier.host[0] == '\0')
267 xstrncpy(al->hier.host, dash_str, SQUIDHOSTNAMELEN);
137ee196 268
269 memBufDefInit(&mb);
270
17a0a4ee 271 if (Config.onoff.common_log)
137ee196 272 accessLogCommon(al, &mb);
f892c2bf 273 else
137ee196 274 accessLogSquid(al, &mb);
17a0a4ee 275 if (Config.onoff.log_mime_hdrs) {
f892c2bf 276 char *ereq = log_quote(al->headers.request);
277 char *erep = log_quote(al->headers.reply);
137ee196 278 memBufPrintf(&mb, " [%s] [%s]\n", ereq, erep);
f892c2bf 279 safe_free(ereq);
280 safe_free(erep);
137ee196 281 } else {
282 memBufPrintf(&mb, "\n");
f892c2bf 283 }
137ee196 284 file_write_mbuf(LogfileFD, -1, mb, NULL, NULL);
3a0bd885 285 safe_free(xbuf);
e66d7923 286#if MULTICAST_MISS_STREAM
287 if (al->cache.code != LOG_TCP_MISS)
288 (void) 0;
289 else if (al->http.method != METHOD_GET)
290 (void) 0;
291 else if (mcast_miss_fd < 0)
292 (void) 0;
293 else {
294 unsigned int ibuf[365];
295 size_t isize;
296 xstrncpy((char *) ibuf, al->url, 364 * sizeof(int));
297 isize = ((strlen(al->url) + 8) / 8) * 2;
6355b0f5 298 if (isize > 364)
299 isize = 364;
e66d7923 300 mcast_encode((unsigned int *) ibuf, isize,
0bdf2621 301 (const unsigned int *) Config.mcast_miss.encode_key);
e66d7923 302 comm_udp_sendto(mcast_miss_fd,
303 &mcast_miss_to, sizeof(mcast_miss_to),
304 ibuf, isize * sizeof(int));
305 }
306#endif
f892c2bf 307}
308
309void
310accessLogRotate(void)
311{
312 int i;
313 LOCAL_ARRAY(char, from, MAXPATHLEN);
314 LOCAL_ARRAY(char, to, MAXPATHLEN);
315 char *fname = NULL;
316 struct stat sb;
d21f1c54 317#if FORW_VIA_DB
318 fvdbClear();
319#endif
f892c2bf 320 if ((fname = LogfileName) == NULL)
321 return;
322#ifdef S_ISREG
323 if (stat(fname, &sb) == 0)
324 if (S_ISREG(sb.st_mode) == 0)
325 return;
326#endif
327 debug(46, 1) ("accessLogRotate: Rotating\n");
328 /* Rotate numbers 0 through N up one */
329 for (i = Config.Log.rotateNumber; i > 1;) {
330 i--;
331 snprintf(from, MAXPATHLEN, "%s.%d", fname, i - 1);
332 snprintf(to, MAXPATHLEN, "%s.%d", fname, i);
1f38f50a 333 xrename(from, to);
f892c2bf 334 }
335 /* Rotate the current log to .0 */
f9b5f6d0 336 file_close(LogfileFD); /* always close */
f892c2bf 337 if (Config.Log.rotateNumber > 0) {
338 snprintf(to, MAXPATHLEN, "%s.%d", fname, 0);
1f38f50a 339 xrename(fname, to);
f892c2bf 340 }
f9b5f6d0 341 /* Reopen the log. It may have been renamed "manually" */
1a94f598 342 LogfileFD = file_open(fname, O_WRONLY | O_CREAT);
f892c2bf 343 if (LogfileFD == DISK_ERROR) {
344 debug(46, 0) ("accessLogRotate: Cannot open logfile: %s\n", fname);
345 LogfileStatus = LOG_DISABLE;
6b8e7481 346 fatalf("Cannot open %s: %s", fname, xstrerror());
f892c2bf 347 }
348}
349
350void
351accessLogClose(void)
352{
353 file_close(LogfileFD);
354}
355
f892c2bf 356void
365e5b34 357hierarchyNote(HierarchyLogEntry * hl,
f892c2bf 358 hier_code code,
43c3424b 359 const char *cache_peer)
f892c2bf 360{
361 assert(hl != NULL);
362 hl->code = code;
43c3424b 363 xstrncpy(hl->host, cache_peer, SQUIDHOSTNAMELEN);
f892c2bf 364}
7a2f978b 365
366void
367accessLogInit(void)
368{
79d39a72 369 assert(sizeof(log_tags) == (LOG_TYPE_MAX + 1) * sizeof(char *));
a7c05555 370 accessLogOpen(Config.Log.access);
d21f1c54 371#if FORW_VIA_DB
372 fvdbInit();
373#endif
e66d7923 374#if MULTICAST_MISS_STREAM
375 if (Config.mcast_miss.addr.s_addr != no_addr.s_addr) {
376 memset(&mcast_miss_to, '\0', sizeof(mcast_miss_to));
377 mcast_miss_to.sin_family = AF_INET;
378 mcast_miss_to.sin_port = htons(Config.mcast_miss.port);
379 mcast_miss_to.sin_addr.s_addr = Config.mcast_miss.addr.s_addr;
380 mcast_miss_fd = comm_open(SOCK_DGRAM,
381 0,
382 Config.Addrs.udp_incoming,
383 Config.mcast_miss.port,
384 COMM_NONBLOCKING,
385 "Multicast Miss Stream");
386 if (mcast_miss_fd < 0)
387 fatal("Cannot open Multicast Miss Stream Socket");
388 debug(46, 1) ("Multicast Miss Stream Socket opened on FD %d\n",
389 mcast_miss_fd);
7e3ce7b9 390 mcastSetTtl(mcast_miss_fd, Config.mcast_miss.ttl);
e66d7923 391 if (strlen(Config.mcast_miss.encode_key) < 16)
392 fatal("mcast_encode_key is too short, must be 16 characters");
393 }
394#endif
7a2f978b 395}
c6e7cab0 396
397const char *
398accessLogTime(time_t t)
399{
400 struct tm *tm;
401 static char buf[128];
402 static time_t last_t = 0;
403 if (t != last_t) {
404 tm = localtime(&t);
405 strftime(buf, 127, "%Y/%m/%d %H:%M:%S", tm);
406 last_t = t;
407 }
408 return buf;
409}
d21f1c54 410
411
412#if FORW_VIA_DB
1afe05c5 413
d21f1c54 414static void
415fvdbInit(void)
416{
417 via_table = hash_create((HASHCMP *) strcmp, 977, hash4);
418 forw_table = hash_create((HASHCMP *) strcmp, 977, hash4);
749e40bf 419 cachemgrRegister("via_headers", "Via Request Headers", fvdbDumpVia, 0, 1);
d21f1c54 420 cachemgrRegister("forw_headers", "X-Forwarded-For Request Headers",
1da3b90b 421 fvdbDumpForw, 0, 1);
d21f1c54 422}
423
424static void
1afe05c5 425fvdbCount(hash_table * hash, const char *key)
426{
427 fvdb_entry *fv;
428 if (NULL == hash)
429 return;
430 fv = hash_lookup(hash, key);
431 if (NULL == fv) {
432 fv = xcalloc(1, sizeof(fvdb_entry));
433 fv->key = xstrdup(key);
434 hash_join(hash, (hash_link *) fv);
435 }
436 fv->n++;
d21f1c54 437}
438
439void
440fvdbCountVia(const char *key)
441{
1afe05c5 442 fvdbCount(via_table, key);
d21f1c54 443}
444
445void
446fvdbCountForw(const char *key)
447{
1afe05c5 448 fvdbCount(forw_table, key);
d21f1c54 449}
450
1afe05c5 451static void
452fvdbDumpTable(StoreEntry * e, hash_table * hash)
d21f1c54 453{
1afe05c5 454 hash_link *h;
455 fvdb_entry *fv;
456 if (hash == NULL)
457 return;
0f6bebac 458 hash_first(hash);
b68ee087 459 while ((h = hash_next(hash))) {
1afe05c5 460 fv = (fvdb_entry *) h;
461 storeAppendPrintf(e, "%9d %s\n", fv->n, fv->key);
462 }
d21f1c54 463}
464
465static void
1afe05c5 466fvdbDumpVia(StoreEntry * e)
d21f1c54 467{
1afe05c5 468 fvdbDumpTable(e, via_table);
d21f1c54 469}
1afe05c5 470
d21f1c54 471static void
1afe05c5 472fvdbDumpForw(StoreEntry * e)
d21f1c54 473{
1afe05c5 474 fvdbDumpTable(e, forw_table);
d21f1c54 475}
476
477static
b644367b 478void
1afe05c5 479fvdbFreeEntry(void *data)
d21f1c54 480{
1afe05c5 481 fvdb_entry *fv = data;
482 xfree(fv->key);
483 xfree(fv);
d21f1c54 484}
485
486static void
487fvdbClear(void)
488{
489 hashFreeItems(via_table, fvdbFreeEntry);
f6cb924d 490 hashFreeMemory(via_table);
491 via_table = hash_create((HASHCMP *) strcmp, 977, hash4);
d21f1c54 492 hashFreeItems(forw_table, fvdbFreeEntry);
f6cb924d 493 hashFreeMemory(forw_table);
494 forw_table = hash_create((HASHCMP *) strcmp, 977, hash4);
d21f1c54 495}
496
1afe05c5 497#endif
e66d7923 498
499#if MULTICAST_MISS_STREAM
500/*
501 * From http://www.io.com/~paulhart/game/algorithms/tea.html
502 *
503 * size of 'ibuf' must be a multiple of 2.
504 * size of 'key' must be 4.
505 * 'ibuf' is modified in place, encrypted data is written in
506 * network byte order.
507 */
508static void
509mcast_encode(unsigned int *ibuf, size_t isize, const unsigned int *key)
510{
511 unsigned int y;
512 unsigned int z;
513 unsigned int sum;
514 const unsigned int delta = 0x9e3779b9;
515 unsigned int n = 32;
516 const unsigned int k0 = htonl(key[0]);
517 const unsigned int k1 = htonl(key[1]);
518 const unsigned int k2 = htonl(key[2]);
519 const unsigned int k3 = htonl(key[3]);
520 int i;
521 for (i = 0; i < isize; i += 2) {
522 y = htonl(ibuf[i]);
523 z = htonl(ibuf[i + 1]);
524 sum = 0;
525 for (n = 32; n; n--) {
526 sum += delta;
527 y += (z << 4) + (k0 ^ z) + (sum ^ (z >> 5)) + k1;
528 z += (y << 4) + (k2 ^ y) + (sum ^ (y >> 5)) + k3;
529 }
530 ibuf[i] = htonl(y);
531 ibuf[i + 1] = htonl(z);
532 }
533}
534
535#endif