chiark / gitweb /
debugging for thing that crashed
[inn-innduct.git] / history / his.c
1 /*  $Id: his.c 6351 2003-05-19 02:00:06Z rra $
2 **
3 **  API to history routines 
4 **
5 **  Copyright (c) 2001, Thus plc 
6 **  
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:
10 **  
11 **  1. Redistributions of the source code must retain the above 
12 **  copyright notice, this list of conditions and the disclaimer 
13 **  set out below. 
14 **  
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. 
19 **  
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. 
24 **  
25 **  Disclaimer:
26 **  
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."
38 */
39
40 #include "config.h"
41 #include "clibrary.h"
42 #include "portable/time.h"
43 #include <errno.h>
44 #include <syslog.h>
45
46 #include "inn/history.h"
47 #include "inn/messages.h"
48 #include "inn/timer.h"
49 #include "libinn.h"
50 #include "storage.h"
51
52 #include "hisinterface.h"
53 #include "hismethods.h"
54
55 struct hiscache {
56     HASH Hash;  /* Hash value of the message-id using Hash() */
57     bool Found; /* Whether this entry is in the dbz file yet */
58 };
59
60 struct history {
61     struct hismethod *methods;
62     void *sub;
63     struct hiscache *cache;
64     size_t cachesize;
65     const char *error;
66     struct histstats stats;
67 };
68
69 enum HISRESULT {HIScachehit, HIScachemiss, HIScachedne};
70
71 static const struct histstats nullhist = {0};
72
73 /*
74 ** Put an entry into the history cache 
75 */
76 static void
77 his_cacheadd(struct history *h, HASH MessageID, bool Found)
78 {
79     unsigned int  i, loc;
80
81     his_logger("HIScacheadd begin", S_HIScacheadd);
82     if (h->cache != NULL) {
83         memcpy(&loc, ((char *)&MessageID) + (sizeof(HASH) - sizeof(loc)),
84                sizeof(loc));
85         i = loc % h->cachesize;
86         memcpy((char *)&h->cache[i].Hash, (char *)&MessageID, sizeof(HASH));
87         h->cache[i].Found = Found;
88     }
89     his_logger("HIScacheadd end", S_HIScacheadd);
90 }
91
92 /*
93 ** Lookup an entry in the history cache
94 */
95 static enum HISRESULT
96 his_cachelookup(struct history *h, HASH MessageID)
97 {
98     unsigned int i, loc;
99
100     if (h->cache == NULL)
101         return HIScachedne;
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) {
108             return HIScachehit;
109         } else {
110             return HIScachemiss;
111         }
112     } else {
113         his_logger("HIScachelookup end", S_HIScachelookup);
114         return HIScachedne;
115     }
116 }
117
118 /*
119 **  set error status to that indicated by s; doesn't copy the string,
120 **  assumes the caller did that for us
121 */
122 void
123 his_seterror(struct history *h, const char *s)
124 {
125     if (h != NULL) {
126         if (h->error)
127             free((void *)h->error);
128         h->error = s;
129     }
130     if (s != NULL)
131         warn("%s", s);
132 }
133
134 struct history *
135 HISopen(const char *path, const char *method, int flags)
136 {
137     struct history *h;
138     int i;
139
140     for (i = 0; i < NUM_HIS_METHODS; ++i) {
141         if (strcmp(method, his_methods[i].name) == 0)
142             break;
143     }
144     if (i == NUM_HIS_METHODS) {
145         warn("`%s' isn't a valid history method", method);
146         return NULL;
147     }
148
149     /* allocate up our structure and start subordinate history
150      * manager */
151     h = xmalloc(sizeof *h);
152     h->methods = &his_methods[i];
153     h->cache = NULL;
154     h->error = NULL;
155     h->cachesize = 0;
156     h->stats = nullhist;
157     h->sub = (*h->methods->open)(path, flags, h);
158     if (h->sub == NULL) {
159         free(h);
160         h = NULL;
161     }
162     return h;
163 }
164
165 static bool
166 his_checknull(struct history *h)
167 {
168     if (h != NULL)
169         return false;
170     errno = EBADF;
171     return true;
172
173 }
174
175 bool 
176 HISclose(struct history *h)
177 {
178     bool r;
179
180     if (his_checknull(h))
181         return false;
182     r = (*h->methods->close)(h->sub);
183     if (h->cache) {
184         free(h->cache);
185         h->cache = NULL;
186     }
187     if (h->error) {
188         free((void *)h->error);
189         h->error = NULL;
190     }
191     free(h);
192     return r;
193 }
194
195 bool
196 HISsync(struct history *h)
197 {
198     bool r = false;
199
200     if (his_checknull(h))
201         return false;
202     TMRstart(TMR_HISSYNC);
203     r = (*h->methods->sync)(h->sub);
204     TMRstop(TMR_HISSYNC);
205     return r;
206 }
207
208 bool
209 HISlookup(struct history *h, const char *key, time_t *arrived,
210           time_t *posted, time_t *expires, TOKEN *token)
211 {
212     bool r;
213
214     if (his_checknull(h))
215         return false;
216     TMRstart(TMR_HISGREP);
217     r = (*h->methods->lookup)(h->sub, key, arrived, posted, expires, token);
218     TMRstop(TMR_HISGREP);
219     return r;
220 }
221
222 bool
223 HIScheck(struct history *h, const char *key)
224 {
225     bool r = false;
226     HASH hash;
227
228     if (his_checknull(h))
229         return false;
230     TMRstart(TMR_HISHAVE);
231     hash = HashMessageID(key);
232     switch (his_cachelookup(h, hash)) {
233     case HIScachehit:
234         h->stats.hitpos++;
235         r = true;
236         break;
237
238     case HIScachemiss:
239         h->stats.hitneg++;
240         r = false;
241         break;
242
243     case HIScachedne:
244         r = (*h->methods->check)(h->sub, key);
245         his_cacheadd(h, hash, r);
246         if (r)
247             h->stats.misses++;
248         else
249             h->stats.dne++;
250         break;
251     }
252     TMRstop(TMR_HISHAVE);
253     return r;
254 }
255
256 bool
257 HISwrite(struct history *h, const char *key, time_t arrived,
258          time_t posted, time_t expires, const TOKEN *token)
259 {
260     bool r;
261
262     if (his_checknull(h))
263         return false;
264     TMRstart(TMR_HISWRITE);
265     r = (*h->methods->write)(h->sub, key, arrived, posted, expires, token);
266     if (r == true) {
267         HASH hash;
268
269         /* if we successfully wrote it, add it to the cache */
270         hash = HashMessageID(key);
271         his_cacheadd(h, hash, true);
272     }
273     TMRstop(TMR_HISWRITE);
274
275     return r;
276 }
277
278 bool
279 HISremember(struct history *h, const char *key, time_t arrived)
280 {
281     bool r;
282
283     if (his_checknull(h))
284         return false;
285     TMRstart(TMR_HISWRITE);
286     r = (*h->methods->remember)(h->sub, key, arrived);
287     if (r == true) {
288         HASH hash;
289
290         /* if we successfully wrote it, add it to the cache */
291         hash = HashMessageID(key);
292         his_cacheadd(h, hash, true);
293     }
294     TMRstop(TMR_HISWRITE);
295
296     return r;
297 }
298
299 bool
300 HISreplace(struct history *h, const char *key, time_t arrived,
301            time_t posted, time_t expires, const TOKEN *token)
302 {
303     bool r;
304
305     if (his_checknull(h))
306         return false;
307     r = (*h->methods->replace)(h->sub, key, arrived, posted, expires, token);
308     if (r == true) {
309         HASH hash;
310
311         /* if we successfully wrote it, add it to the cache */
312         hash = HashMessageID(key);
313         his_cacheadd(h, hash, true);
314     }
315     return r;
316 }
317
318 bool
319 HISwalk(struct history *h, const char *reason, void *cookie,
320         bool (*callback)(void *, time_t, time_t, time_t, const TOKEN *))
321 {
322     bool r;
323
324     if (his_checknull(h))
325         return false;
326     r = (*h->methods->walk)(h->sub, reason, cookie, callback);
327     return r;
328 }
329
330 bool
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 *))
334 {
335     bool r;
336
337     if (his_checknull(h))
338         return false;
339     r = (*h->methods->expire)(h->sub, path, reason, writing,
340                               cookie, threshold, exists);
341     return r;
342 }
343
344 void
345 HISsetcache(struct history *h, size_t size)
346 {
347     if (h == NULL)
348         return;
349     if (h->cache) {
350         free(h->cache);
351         h->cache = NULL;
352     }
353     h->cachesize = size / sizeof(struct hiscache);
354     if (h->cachesize != 0)
355         h->cache = xcalloc(h->cachesize, sizeof(struct hiscache));
356     h->stats = nullhist;
357 }
358
359
360 /*
361 **  return current history cache stats and zero the counters
362 */
363 struct histstats
364 HISstats(struct history *h)
365 {
366     struct histstats r;
367
368     if (h == NULL)
369         return nullhist;
370     r = h->stats;
371     h->stats = nullhist;
372     return r;
373 }
374
375
376 /*
377 **  return current error status to caller
378 */
379 const char *
380 HISerror(struct history *h)
381 {
382     if (h == NULL)
383         return NULL;
384     return h->error;
385 }
386
387
388 /*
389 **  control interface to underlying method
390 */
391 bool
392 HISctl(struct history *h, int selector, void *val)
393 {
394     bool r;
395
396     if (his_checknull(h))
397         return false;
398     r = (*h->methods->ctl)(h->sub, selector, val);
399     return r;
400 }
401
402
403 /*
404 **  This code doesn't fit well with the generic history API, it really
405 **  needs migrating to use the new nested timers
406 */
407
408 FILE             *HISfdlog = NULL; /* filehandle for history logging purpose */
409
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];
413
414 void HISlogclose(void) {
415    if (HISfdlog != NULL)
416        Fclose(HISfdlog);
417    HISfdlog = NULL;
418 }
419
420 void HISlogto(const char *s) {
421    int i;
422
423    HISlogclose();
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;
433    }
434 }
435
436 void
437 his_logger(char *s, int code)
438 {
439     struct timeval tv;
440     struct tm *tm;
441
442     if (HISfdlog == NULL) /* do nothing unless HISlogto() has been called */
443         return;
444
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;
457       }
458       else
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;
465     }
466     else {
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;
472     }
473 }