chiark / gitweb /
debugging for thing that crashed
[innduct.git] / storage / tradindexed / tradindexed.c
1 /*  $Id: tradindexed.c 7138 2005-03-16 18:15:51Z hkehoe $
2 **
3 **  Interface implementation for the tradindexed overview method.
4 **
5 **  This code converts between the internal interface used by the tradindexed
6 **  implementation and the interface expected by the INN overview API.  The
7 **  internal interface is in some cases better suited to the data structures
8 **  that the tradindexed overview method uses, and this way the internal
9 **  interface can be kept isolated from the external interface.  (There are
10 **  also some operations that can be performed entirely in the interface
11 **  layer.)
12 */
13
14 #include "config.h"
15 #include "clibrary.h"
16
17 #include "inn/innconf.h"
18 #include "inn/messages.h"
19 #include "libinn.h"
20 #include "ov.h"
21 #include "storage.h"
22 #include "tdx-private.h"
23 #include "tdx-structure.h"
24 #include "tradindexed.h"
25
26 /* This structure holds all of the data about the open overview files.  We can
27    eventually pass one of these structures back to the caller of open when the
28    overview API is more object-oriented. */
29 struct tradindexed {
30     struct group_index *index;
31     struct cache *cache;
32     bool cutoff;
33 };
34
35 /* Global data about the open tradindexed method. */
36 static struct tradindexed *tradindexed;
37
38
39 /*
40 **  Helper function to open a group_data structure via the cache, inserting it
41 **  into the cache if it wasn't found in the cache.
42 */
43 static struct group_data *
44 data_cache_open(struct tradindexed *global, const char *group,
45                 struct group_entry *entry)
46 {
47     struct group_data *data;
48
49     data = tdx_cache_lookup(global->cache, entry->hash);
50     if (data == NULL) {
51         data = tdx_data_open(global->index, group, entry);
52         if (data == NULL)
53             return NULL;
54         tdx_cache_insert(global->cache, entry->hash, data);
55     }
56     return data;
57 }
58
59
60 /*
61 **  Helper function to reopen the data files and remove the old entry from the
62 **  cache if we think that might help better fulfill a search.
63 */
64 static struct group_data *
65 data_cache_reopen(struct tradindexed *global, const char *group,
66                   struct group_entry *entry)
67 {
68     struct group_data *data;
69
70     tdx_cache_delete(global->cache, entry->hash);
71     data = tdx_data_open(global->index, group, entry);
72     if (data == NULL)
73         return NULL;
74     tdx_cache_insert(global->cache, entry->hash, data);
75     return data;
76 }
77
78
79 /*
80 **  Open the overview method.
81 */
82 bool
83 tradindexed_open(int mode)
84 {
85     unsigned int cache_size, fdlimit;
86
87     if (tradindexed != NULL) {
88         warn("tradindexed: overview method already open");
89         return false;
90     }
91     tradindexed = xmalloc(sizeof(struct tradindexed));
92     tradindexed->index = tdx_index_open((mode & OV_WRITE) ? true : false);
93     tradindexed->cutoff = false;
94
95     /* Use a cache size of two for read-only connections.  We may want to
96        rethink the limitation of the cache for reading later based on
97        real-world experience. */
98     cache_size = (mode & OV_WRITE) ? innconf->overcachesize : 1;
99     fdlimit = getfdlimit();
100     if (fdlimit > 0 && fdlimit < cache_size * 2) {
101         warn("tradindexed: not enough file descriptors for an overview cache"
102              " size of %u; increase rlimitnofile or decrease overcachesize"
103              " to at most %u", cache_size, fdlimit / 2);
104         cache_size = (fdlimit > 2) ? fdlimit / 2 : 1;
105     }
106     tradindexed->cache = tdx_cache_create(cache_size);
107
108     return (tradindexed->index == NULL) ? false : true;
109 }
110
111
112 /*
113 **  Get statistics about a group.  Convert between the multiple pointer API
114 **  and the structure API used internally.
115 */
116 bool
117 tradindexed_groupstats(char *group, int *low, int *high, int *count,
118                        int *flag)
119 {
120     const struct group_entry *entry;
121
122     if (tradindexed == NULL || tradindexed->index == NULL) {
123         warn("tradindexed: overview method not initialized");
124         return false;
125     }
126     entry = tdx_index_entry(tradindexed->index, group);
127     if (entry == NULL)
128         return false;
129     if (low != NULL)
130         *low = entry->low;
131     if (high != NULL)
132         *high = entry->high;
133     if (count != NULL)
134         *count = entry->count;
135     if (flag != NULL)
136         *flag = entry->flag;
137     return true;
138 }
139
140
141 /*
142 **  Add a new newsgroup to the index.
143 */
144 bool
145 tradindexed_groupadd(char *group, ARTNUM low, ARTNUM high, char *flag)
146 {
147     if (tradindexed == NULL || tradindexed->index == NULL) {
148         warn("tradindexed: overview method not initialized");
149         return false;
150     }
151     return tdx_index_add(tradindexed->index, group, low, high, flag);
152 }
153
154
155 /*
156 **  Delete a newsgroup from the index.
157 */
158 bool
159 tradindexed_groupdel(char *group)
160 {
161     if (tradindexed == NULL || tradindexed->index == NULL) {
162         warn("tradindexed: overview method not initialized");
163         return false;
164     }
165     return tdx_index_delete(tradindexed->index, group);
166 }
167
168
169 /*
170 **  Add data about a single article.  Convert between the multiple argument
171 **  API and the structure API used internally, and also implement low article
172 **  cutoff if that was requested.
173 */
174 bool
175 tradindexed_add(char *group, ARTNUM artnum, TOKEN token, char *data,
176                 int length, time_t arrived, time_t expires)
177 {
178     struct article article;
179     struct group_data *group_data;
180     struct group_entry *entry;
181
182     if (tradindexed == NULL || tradindexed->index == NULL) {
183         warn("tradindexed: overview method not initialized");
184         return false;
185     }
186
187     /* Get the group index entry and don't do any work if cutoff is set and
188        the article number is lower than the low water mark for the group. */
189     entry = tdx_index_entry(tradindexed->index, group);
190     if (entry == NULL)
191         return true;
192     if (tradindexed->cutoff && entry->low > artnum)
193         return true;
194
195     /* Fill out the article data structure. */
196     article.number = artnum;
197     article.overview = data;
198     article.overlen = length;
199     article.token = token;
200     article.arrived = arrived;
201     article.expires = expires;
202
203     /* Open the appropriate data structures, using the cache. */
204     group_data = data_cache_open(tradindexed, group, entry);
205     if (group_data == NULL)
206         return false;
207     return tdx_data_add(tradindexed->index, entry, group_data, &article);
208 }
209
210
211 /*
212 **  Cancel an article.  At present, tradindexed can't do anything with this
213 **  information because we lack a mapping from the token to newsgroup names
214 **  and article numbers, so we just silently return true and let expiration
215 **  take care of this.
216 */
217 bool
218 tradindexed_cancel(TOKEN token UNUSED)
219 {
220     return true;
221 }
222
223
224 /*
225 **  Open an overview search.  Open the appropriate group and then start a
226 **  search in it.
227 */
228 void *
229 tradindexed_opensearch(char *group, int low, int high)
230 {
231     struct group_entry *entry;
232     struct group_data *data;
233
234     if (tradindexed == NULL || tradindexed->index == NULL) {
235         warn("tradindexed: overview method not initialized");
236         return NULL;
237     }
238     entry = tdx_index_entry(tradindexed->index, group);
239     if (entry == NULL)
240         return NULL;
241     data = data_cache_open(tradindexed, group, entry);
242     if (data == NULL)
243         return NULL;
244     if (entry->base != data->base)
245         if (data->base > (ARTNUM) low && entry->base < data->base) {
246             data = data_cache_reopen(tradindexed, group, entry);
247             if (data == NULL)
248                 return NULL;
249         }
250     return tdx_search_open(data, low, high, entry->high);
251 }
252
253
254 /*
255 **  Get the next article returned by a search.  Convert between the multiple
256 **  pointer API and the structure API we use internally.
257 */
258 bool
259 tradindexed_search(void *handle, ARTNUM *artnum, char **data, int *length,
260                    TOKEN *token, time_t *arrived)
261 {
262     struct article article;
263
264     if (tradindexed == NULL || tradindexed->index == NULL) {
265         warn("tradindexed: overview method not initialized");
266         return false;
267     }
268     if (!tdx_search(handle, &article))
269         return false;
270     if (artnum != NULL)
271         *artnum = article.number;
272     if (data != NULL)
273         *data = (char *) article.overview;
274     if (length != NULL)
275         *length = article.overlen;
276     if (token != NULL)
277         *token = article.token;
278     if (arrived != NULL)
279         *arrived = article.arrived;
280     return true;
281 }
282
283
284 /*
285 **  Close an overview search.
286 */
287 void
288 tradindexed_closesearch(void *handle)
289 {
290     tdx_search_close(handle);
291 }
292
293
294 /*
295 **  Get information for a single article.  Open the appropriate group and then
296 **  convert from the pointer API to the struct API used internally.
297 */
298 bool
299 tradindexed_getartinfo(char *group, ARTNUM artnum, TOKEN *token)
300 {
301     struct group_entry *entry;
302     struct group_data *data;
303     const struct index_entry *index_entry;
304
305     if (tradindexed == NULL || tradindexed->index == NULL) {
306         warn("tradindexed: overview method not initialized");
307         return false;
308     }
309     entry = tdx_index_entry(tradindexed->index, group);
310     if (entry == NULL)
311         return false;
312     data = data_cache_open(tradindexed, group, entry);
313     if (data == NULL)
314         return false;
315     if (entry->base != data->base)
316         if (data->base > artnum && entry->base <= artnum) {
317             data = data_cache_reopen(tradindexed, group, entry);
318             if (data == NULL)
319                 return false;
320         }
321     index_entry = tdx_article_entry(data, artnum, entry->high);
322     if (index_entry == NULL)
323         return false;
324     if (token != NULL)
325         *token = index_entry->token;
326     return true;
327 }
328
329
330 /*
331 **  Expire a single newsgroup.
332 */
333 bool
334 tradindexed_expiregroup(char *group, int *low, struct history *history)
335 {
336     ARTNUM new_low;
337     bool status;
338
339     /* tradindexed doesn't have any periodic cleanup. */
340     if (group == NULL)
341         return true;
342
343     status = tdx_expire(group, &new_low, history);
344     if (status && low != NULL)
345         *low = (int) new_low;
346     return status;
347 }
348
349
350 /*
351 **  Set various options or query various paramaters for the overview method.
352 **  The interface is, at present, not particularly sane.
353 */
354 bool
355 tradindexed_ctl(OVCTLTYPE type, void *val)
356 {
357     int *i;
358     bool *b;
359     OVSORTTYPE *sort;
360
361     if (tradindexed == NULL) {
362         warn("tradindexed: overview method not initialized");
363         return false;
364     }
365
366     switch (type) {
367     case OVSPACE:
368         i = (int *) val;
369         *i = -1;
370         return true;
371     case OVSORT:
372         sort = (OVSORTTYPE *) val;
373         *sort = OVNEWSGROUP;
374         return true;
375     case OVCUTOFFLOW:
376         b = (bool *) val;
377         tradindexed->cutoff = *b;
378         return true;
379     case OVSTATICSEARCH:
380         i = (int *) val;
381         *i = false;
382         return true;
383     case OVCACHEKEEP:
384     case OVCACHEFREE:
385         b = (bool *) val;
386         *b = false;
387         return true;
388     default:
389         return false;
390     }
391 }
392
393
394 /*
395 **  Close the overview method.
396 */
397 void
398 tradindexed_close(void)
399 {
400     if (tradindexed != NULL) {
401         if (tradindexed->index != NULL)
402             tdx_index_close(tradindexed->index);
403         if (tradindexed->cache != NULL)
404             tdx_cache_free(tradindexed->cache);
405         free(tradindexed);
406         tradindexed = NULL;
407     }
408 }