1 /* $Id: his.c 6351 2003-05-19 02:00:06Z rra $
3 ** API to history routines
5 ** Copyright (c) 2001, Thus plc
7 ** Redistribution and use of the source code in source and binary
8 ** forms, with or without modification, are permitted provided that
9 ** the following 3 conditions are met:
11 ** 1. Redistributions of the source code must retain the above
12 ** copyright notice, this list of conditions and the disclaimer
15 ** 2. Redistributions of the source code in binary form must
16 ** reproduce the above copyright notice, this list of conditions
17 ** and the disclaimer set out below in the documentation and/or
18 ** other materials provided with the distribution.
20 ** 3. Neither the name of the Thus plc nor the names of its
21 ** contributors may be used to endorse or promote products
22 ** derived from this software without specific prior written
23 ** permission from Thus plc.
27 ** "THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
28 ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
29 ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
30 ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE DIRECTORS
31 ** OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
32 ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
33 ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
34 ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
35 ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
36 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
37 ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
42 #include "portable/time.h"
46 #include "inn/history.h"
47 #include "inn/messages.h"
48 #include "inn/timer.h"
52 #include "hisinterface.h"
53 #include "hismethods.h"
56 HASH Hash; /* Hash value of the message-id using Hash() */
57 bool Found; /* Whether this entry is in the dbz file yet */
61 struct hismethod *methods;
63 struct hiscache *cache;
66 struct histstats stats;
69 enum HISRESULT {HIScachehit, HIScachemiss, HIScachedne};
71 static const struct histstats nullhist = {0};
74 ** Put an entry into the history cache
77 his_cacheadd(struct history *h, HASH MessageID, bool Found)
81 his_logger("HIScacheadd begin", S_HIScacheadd);
82 if (h->cache != NULL) {
83 memcpy(&loc, ((char *)&MessageID) + (sizeof(HASH) - sizeof(loc)),
85 i = loc % h->cachesize;
86 memcpy((char *)&h->cache[i].Hash, (char *)&MessageID, sizeof(HASH));
87 h->cache[i].Found = Found;
89 his_logger("HIScacheadd end", S_HIScacheadd);
93 ** Lookup an entry in the history cache
96 his_cachelookup(struct history *h, HASH MessageID)
100 if (h->cache == NULL)
102 his_logger("HIScachelookup begin", S_HIScachelookup);
103 memcpy(&loc, ((char *)&MessageID) + (sizeof(HASH) - sizeof(loc)), sizeof(loc));
104 i = loc % h->cachesize;
105 if (memcmp((char *)&h->cache[i].Hash, (char *)&MessageID, sizeof(HASH)) == 0) {
106 his_logger("HIScachelookup end", S_HIScachelookup);
107 if (h->cache[i].Found) {
113 his_logger("HIScachelookup end", S_HIScachelookup);
119 ** set error status to that indicated by s; doesn't copy the string,
120 ** assumes the caller did that for us
123 his_seterror(struct history *h, const char *s)
127 free((void *)h->error);
135 HISopen(const char *path, const char *method, int flags)
140 for (i = 0; i < NUM_HIS_METHODS; ++i) {
141 if (strcmp(method, his_methods[i].name) == 0)
144 if (i == NUM_HIS_METHODS) {
145 warn("`%s' isn't a valid history method", method);
149 /* allocate up our structure and start subordinate history
151 h = xmalloc(sizeof *h);
152 h->methods = &his_methods[i];
157 h->sub = (*h->methods->open)(path, flags, h);
158 if (h->sub == NULL) {
166 his_checknull(struct history *h)
176 HISclose(struct history *h)
180 if (his_checknull(h))
182 r = (*h->methods->close)(h->sub);
188 free((void *)h->error);
196 HISsync(struct history *h)
200 if (his_checknull(h))
202 TMRstart(TMR_HISSYNC);
203 r = (*h->methods->sync)(h->sub);
204 TMRstop(TMR_HISSYNC);
209 HISlookup(struct history *h, const char *key, time_t *arrived,
210 time_t *posted, time_t *expires, TOKEN *token)
214 if (his_checknull(h))
216 TMRstart(TMR_HISGREP);
217 r = (*h->methods->lookup)(h->sub, key, arrived, posted, expires, token);
218 TMRstop(TMR_HISGREP);
223 HIScheck(struct history *h, const char *key)
228 if (his_checknull(h))
230 TMRstart(TMR_HISHAVE);
231 hash = HashMessageID(key);
232 switch (his_cachelookup(h, hash)) {
244 r = (*h->methods->check)(h->sub, key);
245 his_cacheadd(h, hash, r);
252 TMRstop(TMR_HISHAVE);
257 HISwrite(struct history *h, const char *key, time_t arrived,
258 time_t posted, time_t expires, const TOKEN *token)
262 if (his_checknull(h))
264 TMRstart(TMR_HISWRITE);
265 r = (*h->methods->write)(h->sub, key, arrived, posted, expires, token);
269 /* if we successfully wrote it, add it to the cache */
270 hash = HashMessageID(key);
271 his_cacheadd(h, hash, true);
273 TMRstop(TMR_HISWRITE);
279 HISremember(struct history *h, const char *key, time_t arrived)
283 if (his_checknull(h))
285 TMRstart(TMR_HISWRITE);
286 r = (*h->methods->remember)(h->sub, key, arrived);
290 /* if we successfully wrote it, add it to the cache */
291 hash = HashMessageID(key);
292 his_cacheadd(h, hash, true);
294 TMRstop(TMR_HISWRITE);
300 HISreplace(struct history *h, const char *key, time_t arrived,
301 time_t posted, time_t expires, const TOKEN *token)
305 if (his_checknull(h))
307 r = (*h->methods->replace)(h->sub, key, arrived, posted, expires, token);
311 /* if we successfully wrote it, add it to the cache */
312 hash = HashMessageID(key);
313 his_cacheadd(h, hash, true);
319 HISwalk(struct history *h, const char *reason, void *cookie,
320 bool (*callback)(void *, time_t, time_t, time_t, const TOKEN *))
324 if (his_checknull(h))
326 r = (*h->methods->walk)(h->sub, reason, cookie, callback);
331 HISexpire(struct history *h, const char *path, const char *reason,
332 bool writing, void *cookie, time_t threshold,
333 bool (*exists)(void *, time_t, time_t, time_t, TOKEN *))
337 if (his_checknull(h))
339 r = (*h->methods->expire)(h->sub, path, reason, writing,
340 cookie, threshold, exists);
345 HISsetcache(struct history *h, size_t size)
353 h->cachesize = size / sizeof(struct hiscache);
354 if (h->cachesize != 0)
355 h->cache = xcalloc(h->cachesize, sizeof(struct hiscache));
361 ** return current history cache stats and zero the counters
364 HISstats(struct history *h)
377 ** return current error status to caller
380 HISerror(struct history *h)
389 ** control interface to underlying method
392 HISctl(struct history *h, int selector, void *val)
396 if (his_checknull(h))
398 r = (*h->methods->ctl)(h->sub, selector, val);
404 ** This code doesn't fit well with the generic history API, it really
405 ** needs migrating to use the new nested timers
408 FILE *HISfdlog = NULL; /* filehandle for history logging purpose */
410 static struct timeval HISstat_start[S_HIS_MAX];
411 static struct timeval HISstat_total[S_HIS_MAX];
412 static unsigned long HISstat_count[S_HIS_MAX];
414 void HISlogclose(void) {
415 if (HISfdlog != NULL)
420 void HISlogto(const char *s) {
424 if ((HISfdlog = Fopen(s, "a", INND_HISLOG)) == NULL)
425 syslog(L_FATAL, "cant open %s %m", s);
426 /* initialize our counters */
427 for (i = 0; i < S_HIS_MAX; i++) {
428 HISstat_start[i].tv_sec = 0;
429 HISstat_start[i].tv_usec = 0;
430 HISstat_total[i].tv_sec = 0;
431 HISstat_total[i].tv_usec = 0;
432 HISstat_count[i] = 0;
437 his_logger(char *s, int code)
442 if (HISfdlog == NULL) /* do nothing unless HISlogto() has been called */
445 gettimeofday(&tv, NULL);
446 tm = localtime((const time_t *)&(tv.tv_sec));
447 if (HISstat_start[code].tv_sec != 0) {
448 fprintf(HISfdlog, "%d/%d/%d %02d:%02d:%02d.%06d: [%d] %s (%.6f)\n",
449 tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour,
450 tm->tm_min, tm->tm_sec, (int)tv.tv_usec, code, s, (float) tv.tv_sec +
451 (float) tv.tv_usec / 1000000 - (float) HISstat_start[code].tv_sec -
452 (float) HISstat_start[code].tv_usec / 1000000);
453 if (tv.tv_usec < HISstat_start[code].tv_usec) {
454 HISstat_total[code].tv_sec++;
455 HISstat_total[code].tv_usec +=
456 tv.tv_usec - HISstat_start[code].tv_usec + 1000000;
459 HISstat_total[code].tv_usec +=
460 tv.tv_usec - HISstat_start[code].tv_usec;
461 HISstat_total[code].tv_sec += tv.tv_sec - HISstat_start[code].tv_sec;
462 HISstat_count[code]++;
463 HISstat_start[code].tv_sec = 0;
464 HISstat_start[code].tv_usec = 0;
467 fprintf(HISfdlog, "%d/%d/%d %02d:%02d:%02d.%06d: [%d] %s\n",
468 tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour,
469 tm->tm_min, tm->tm_sec, (int)tv.tv_usec, code, s);
470 HISstat_start[code].tv_sec = tv.tv_sec;
471 HISstat_start[code].tv_usec = tv.tv_usec;