Add nsswitch support.
[dragonfly.git] / usr.sbin / nscd / cachelib.c
1 /*-
2  * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD: src/usr.sbin/nscd/cachelib.c,v 1.4 2008/10/23 00:31:15 delphij Exp $
27  */
28
29 #include <sys/time.h>
30 #include <assert.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include "cachelib.h"
34 #include "debug.h"
35
36 #define INITIAL_ENTRIES_CAPACITY 32
37 #define ENTRIES_CAPACITY_STEP 32
38
39 #define STRING_SIMPLE_HASH_BODY(in_var, var, a, M)              \
40         for ((var) = 0; *(in_var) != '\0'; ++(in_var))          \
41                 (var) = ((a)*(var) + *(in_var)) % (M)
42
43 #define STRING_SIMPLE_MP2_HASH_BODY(in_var, var, a, M)          \
44         for ((var) = 0; *(in_var) != 0; ++(in_var))             \
45                 (var) = ((a)*(var) + *(in_var)) & (M - 1)
46
47 static int cache_elemsize_common_continue_func(struct cache_common_entry_ *,
48         struct cache_policy_item_ *);
49 static int cache_lifetime_common_continue_func(struct cache_common_entry_ *,
50         struct cache_policy_item_ *);
51 static void clear_cache_entry(struct cache_entry_ *);
52 static void destroy_cache_entry(struct cache_entry_ *);
53 static void destroy_cache_mp_read_session(struct cache_mp_read_session_ *);
54 static void destroy_cache_mp_write_session(struct cache_mp_write_session_ *);
55 static int entries_bsearch_cmp_func(const void *, const void *);
56 static int entries_qsort_cmp_func(const void *, const void *);
57 static struct cache_entry_ ** find_cache_entry_p(struct cache_ *,
58         const char *);
59 static void flush_cache_entry(struct cache_entry_ *);
60 static void flush_cache_policy(struct cache_common_entry_ *,
61         struct cache_policy_ *, struct cache_policy_ *,
62                 int (*)(struct cache_common_entry_ *,
63                 struct cache_policy_item_ *));
64 static int ht_items_cmp_func(const void *, const void *);
65 static int ht_items_fixed_size_left_cmp_func(const void *, const void *);
66 static hashtable_index_t ht_item_hash_func(const void *, size_t);
67
68 /*
69  * Hashing and comparing routines, that are used with the hash tables
70  */
71 static int
72 ht_items_cmp_func(const void *p1, const void *p2)
73 {
74         struct cache_ht_item_data_ *hp1, *hp2;
75         size_t min_size;
76         int result;
77
78         hp1 = (struct cache_ht_item_data_ *)p1;
79         hp2 = (struct cache_ht_item_data_ *)p2;
80
81         assert(hp1->key != NULL);
82         assert(hp2->key != NULL);
83
84         if (hp1->key_size != hp2->key_size) {
85                 min_size = (hp1->key_size < hp2->key_size) ? hp1->key_size :
86                         hp2->key_size;
87                 result = memcmp(hp1->key, hp2->key, min_size);
88
89                 if (result == 0)
90                         return ((hp1->key_size < hp2->key_size) ? -1 : 1);
91                 else
92                         return (result);
93         } else
94                 return (memcmp(hp1->key, hp2->key, hp1->key_size));
95 }
96
97 static int
98 ht_items_fixed_size_left_cmp_func(const void *p1, const void *p2)
99 {
100         struct cache_ht_item_data_ *hp1, *hp2;
101         size_t min_size;
102         int result;
103
104         hp1 = (struct cache_ht_item_data_ *)p1;
105         hp2 = (struct cache_ht_item_data_ *)p2;
106
107         assert(hp1->key != NULL);
108         assert(hp2->key != NULL);
109
110         if (hp1->key_size != hp2->key_size) {
111                 min_size = (hp1->key_size < hp2->key_size) ? hp1->key_size :
112                         hp2->key_size;
113                 result = memcmp(hp1->key, hp2->key, min_size);
114
115                 if (result == 0)
116                         if (min_size == hp1->key_size)
117                             return (0);
118                         else
119                             return ((hp1->key_size < hp2->key_size) ? -1 : 1);
120                 else
121                         return (result);
122         } else
123                 return (memcmp(hp1->key, hp2->key, hp1->key_size));
124 }
125
126 static hashtable_index_t
127 ht_item_hash_func(const void *p, size_t cache_entries_size)
128 {
129         struct cache_ht_item_data_ *hp;
130         size_t i;
131
132         hashtable_index_t retval;
133
134         hp = (struct cache_ht_item_data_ *)p;
135         assert(hp->key != NULL);
136
137         retval = 0;
138         for (i = 0; i < hp->key_size; ++i)
139             retval = (127 * retval + (unsigned char)hp->key[i]) %
140                 cache_entries_size;
141
142         return retval;
143 }
144
145 HASHTABLE_GENERATE(cache_ht_, cache_ht_item_, struct cache_ht_item_data_, data,
146         ht_item_hash_func, ht_items_cmp_func);
147
148 /*
149  * Routines to sort and search the entries by name
150  */
151 static int
152 entries_bsearch_cmp_func(const void *key, const void *ent)
153 {
154
155         assert(key != NULL);
156         assert(ent != NULL);
157
158         return (strcmp((char const *)key,
159                 (*(struct cache_entry_ const **)ent)->name));
160 }
161
162 static int
163 entries_qsort_cmp_func(const void *e1, const void *e2)
164 {
165
166         assert(e1 != NULL);
167         assert(e2 != NULL);
168
169         return (strcmp((*(struct cache_entry_ const **)e1)->name,
170                 (*(struct cache_entry_ const **)e2)->name));
171 }
172
173 static struct cache_entry_ **
174 find_cache_entry_p(struct cache_ *the_cache, const char *entry_name)
175 {
176
177         return ((struct cache_entry_ **)(bsearch(entry_name, the_cache->entries,
178                 the_cache->entries_size, sizeof(struct cache_entry_ *),
179                 entries_bsearch_cmp_func)));
180 }
181
182 static void
183 destroy_cache_mp_write_session(struct cache_mp_write_session_ *ws)
184 {
185
186         struct cache_mp_data_item_      *data_item;
187
188         TRACE_IN(destroy_cache_mp_write_session);
189         assert(ws != NULL);
190         while (!TAILQ_EMPTY(&ws->items)) {
191                 data_item = TAILQ_FIRST(&ws->items);
192                 TAILQ_REMOVE(&ws->items, data_item, entries);
193                 free(data_item->value);
194                 free(data_item);
195         }
196
197         free(ws);
198         TRACE_OUT(destroy_cache_mp_write_session);
199 }
200
201 static void
202 destroy_cache_mp_read_session(struct cache_mp_read_session_ *rs)
203 {
204
205         TRACE_IN(destroy_cache_mp_read_session);
206         assert(rs != NULL);
207         free(rs);
208         TRACE_OUT(destroy_cache_mp_read_session);
209 }
210
211 static void
212 destroy_cache_entry(struct cache_entry_ *entry)
213 {
214         struct cache_common_entry_      *common_entry;
215         struct cache_mp_entry_          *mp_entry;
216         struct cache_mp_read_session_   *rs;
217         struct cache_mp_write_session_  *ws;
218         struct cache_ht_item_ *ht_item;
219         struct cache_ht_item_data_ *ht_item_data;
220
221         TRACE_IN(destroy_cache_entry);
222         assert(entry != NULL);
223
224         if (entry->params->entry_type == CET_COMMON) {
225                 common_entry = (struct cache_common_entry_ *)entry;
226
227                 HASHTABLE_FOREACH(&(common_entry->items), ht_item) {
228                         HASHTABLE_ENTRY_FOREACH(ht_item, data, ht_item_data)
229                         {
230                                 free(ht_item_data->key);
231                                 free(ht_item_data->value);
232                         }
233                         HASHTABLE_ENTRY_CLEAR(ht_item, data);
234                 }
235
236                 HASHTABLE_DESTROY(&(common_entry->items), data);
237
238                 /* FIFO policy is always first */
239                 destroy_cache_fifo_policy(common_entry->policies[0]);
240                 switch (common_entry->common_params.policy) {
241                 case CPT_LRU:
242                         destroy_cache_lru_policy(common_entry->policies[1]);
243                         break;
244                 case CPT_LFU:
245                         destroy_cache_lfu_policy(common_entry->policies[1]);
246                         break;
247                 default:
248                 break;
249                 }
250                 free(common_entry->policies);
251         } else {
252                 mp_entry = (struct cache_mp_entry_ *)entry;
253
254                 while (!TAILQ_EMPTY(&mp_entry->ws_head)) {
255                         ws = TAILQ_FIRST(&mp_entry->ws_head);
256                         TAILQ_REMOVE(&mp_entry->ws_head, ws, entries);
257                         destroy_cache_mp_write_session(ws);
258                 }
259
260                 while (!TAILQ_EMPTY(&mp_entry->rs_head)) {
261                         rs = TAILQ_FIRST(&mp_entry->rs_head);
262                         TAILQ_REMOVE(&mp_entry->rs_head, rs, entries);
263                         destroy_cache_mp_read_session(rs);
264                 }
265
266                 if (mp_entry->completed_write_session != NULL)
267                         destroy_cache_mp_write_session(
268                                 mp_entry->completed_write_session);
269
270                 if (mp_entry->pending_write_session != NULL)
271                         destroy_cache_mp_write_session(
272                                 mp_entry->pending_write_session);
273         }
274
275         free(entry->name);
276         free(entry);
277         TRACE_OUT(destroy_cache_entry);
278 }
279
280 static void
281 clear_cache_entry(struct cache_entry_ *entry)
282 {
283         struct cache_mp_entry_          *mp_entry;
284         struct cache_common_entry_      *common_entry;
285         struct cache_ht_item_ *ht_item;
286         struct cache_ht_item_data_ *ht_item_data;
287         struct cache_policy_ *policy;
288         struct cache_policy_item_ *item, *next_item;
289         size_t entry_size;
290         int i;
291
292         if (entry->params->entry_type == CET_COMMON) {
293                 common_entry = (struct cache_common_entry_ *)entry;
294
295                 entry_size = 0;
296                 HASHTABLE_FOREACH(&(common_entry->items), ht_item) {
297                         HASHTABLE_ENTRY_FOREACH(ht_item, data, ht_item_data)
298                         {
299                                 free(ht_item_data->key);
300                                 free(ht_item_data->value);
301                         }
302                         entry_size += HASHTABLE_ENTRY_SIZE(ht_item, data);
303                         HASHTABLE_ENTRY_CLEAR(ht_item, data);
304                 }
305
306                 common_entry->items_size -= entry_size;
307                 for (i = 0; i < common_entry->policies_size; ++i) {
308                         policy = common_entry->policies[i];
309
310                         next_item = NULL;
311                         item = policy->get_first_item_func(policy);
312                         while (item != NULL) {
313                                 next_item = policy->get_next_item_func(policy,
314                                         item);
315                                 policy->remove_item_func(policy, item);
316                                 policy->destroy_item_func(item);
317                                 item = next_item;
318                         }
319                 }
320         } else {
321                 mp_entry = (struct cache_mp_entry_ *)entry;
322
323                 if (mp_entry->rs_size == 0) {
324                         if (mp_entry->completed_write_session != NULL) {
325                                 destroy_cache_mp_write_session(
326                                         mp_entry->completed_write_session);
327                                 mp_entry->completed_write_session = NULL;
328                         }
329
330                         memset(&mp_entry->creation_time, 0,
331                                 sizeof(struct timeval));
332                         memset(&mp_entry->last_request_time, 0,
333                                 sizeof(struct timeval));
334                 }
335         }
336 }
337
338 /*
339  * When passed to the flush_cache_policy, ensures that all old elements are
340  * deleted.
341  */
342 static int
343 cache_lifetime_common_continue_func(struct cache_common_entry_ *entry,
344         struct cache_policy_item_ *item)
345 {
346
347         return ((item->last_request_time.tv_sec - item->creation_time.tv_sec >
348                 entry->common_params.max_lifetime.tv_sec) ? 1: 0);
349 }
350
351 /*
352  * When passed to the flush_cache_policy, ensures that all elements, that
353  * exceed the size limit, are deleted.
354  */
355 static int
356 cache_elemsize_common_continue_func(struct cache_common_entry_ *entry,
357         struct cache_policy_item_ *item)
358 {
359
360         return ((entry->items_size > entry->common_params.satisf_elemsize) ? 1
361                 : 0);
362 }
363
364 /*
365  * Removes the elements from the cache entry, while the continue_func returns 1.
366  */
367 static void
368 flush_cache_policy(struct cache_common_entry_ *entry,
369         struct cache_policy_ *policy,
370         struct cache_policy_ *connected_policy,
371         int (*continue_func)(struct cache_common_entry_ *,
372                 struct cache_policy_item_ *))
373 {
374         struct cache_policy_item_ *item, *next_item, *connected_item;
375         struct cache_ht_item_ *ht_item;
376         struct cache_ht_item_data_ *ht_item_data, ht_key;
377         hashtable_index_t hash;
378
379         assert(policy != NULL);
380
381         next_item = NULL;
382         item = policy->get_first_item_func(policy);
383         while ((item != NULL) && (continue_func(entry, item) == 1)) {
384                 next_item = policy->get_next_item_func(policy, item);
385
386                 connected_item = item->connected_item;
387                 policy->remove_item_func(policy, item);
388
389                 memset(&ht_key, 0, sizeof(struct cache_ht_item_data_));
390                 ht_key.key = item->key;
391                 ht_key.key_size = item->key_size;
392
393                 hash = HASHTABLE_CALCULATE_HASH(cache_ht_, &entry->items,
394                         &ht_key);
395                 assert(hash >= 0);
396                 assert(hash < HASHTABLE_ENTRIES_COUNT(&entry->items));
397
398                 ht_item = HASHTABLE_GET_ENTRY(&(entry->items), hash);
399                 ht_item_data = HASHTABLE_ENTRY_FIND(cache_ht_, ht_item,
400                         &ht_key);
401                 assert(ht_item_data != NULL);
402                 free(ht_item_data->key);
403                 free(ht_item_data->value);
404                 HASHTABLE_ENTRY_REMOVE(cache_ht_, ht_item, ht_item_data);
405                 --entry->items_size;
406
407                 policy->destroy_item_func(item);
408
409                 if (connected_item != NULL) {
410                         connected_policy->remove_item_func(connected_policy,
411                                 connected_item);
412                         connected_policy->destroy_item_func(connected_item);
413                 }
414
415                 item = next_item;
416         }
417 }
418
419 static void
420 flush_cache_entry(struct cache_entry_ *entry)
421 {
422         struct cache_mp_entry_          *mp_entry;
423         struct cache_common_entry_      *common_entry;
424         struct cache_policy_ *policy, *connected_policy;
425
426         connected_policy = NULL;
427         if (entry->params->entry_type == CET_COMMON) {
428                 common_entry = (struct cache_common_entry_ *)entry;
429                 if ((common_entry->common_params.max_lifetime.tv_sec != 0) ||
430                     (common_entry->common_params.max_lifetime.tv_usec != 0)) {
431
432                         policy = common_entry->policies[0];
433                         if (common_entry->policies_size > 1)
434                                 connected_policy = common_entry->policies[1];
435
436                         flush_cache_policy(common_entry, policy,
437                                 connected_policy,
438                                 cache_lifetime_common_continue_func);
439                 }
440
441
442                 if ((common_entry->common_params.max_elemsize != 0) &&
443                         common_entry->items_size >
444                         common_entry->common_params.max_elemsize) {
445
446                         if (common_entry->policies_size > 1) {
447                                 policy = common_entry->policies[1];
448                                 connected_policy = common_entry->policies[0];
449                         } else {
450                                 policy = common_entry->policies[0];
451                                 connected_policy = NULL;
452                         }
453
454                         flush_cache_policy(common_entry, policy,
455                                 connected_policy,
456                                 cache_elemsize_common_continue_func);
457                 }
458         } else {
459                 mp_entry = (struct cache_mp_entry_ *)entry;
460
461                 if ((mp_entry->mp_params.max_lifetime.tv_sec != 0)
462                         || (mp_entry->mp_params.max_lifetime.tv_usec != 0)) {
463
464                         if (mp_entry->last_request_time.tv_sec -
465                                 mp_entry->last_request_time.tv_sec >
466                                 mp_entry->mp_params.max_lifetime.tv_sec)
467                                 clear_cache_entry(entry);
468                 }
469         }
470 }
471
472 struct cache_ *
473 init_cache(struct cache_params const *params)
474 {
475         struct cache_ *retval;
476
477         TRACE_IN(init_cache);
478         assert(params != NULL);
479
480         retval = (struct cache_ *)calloc(1, sizeof(struct cache_));
481         assert(retval != NULL);
482
483         assert(params != NULL);
484         memcpy(&retval->params, params, sizeof(struct cache_params));
485
486         retval->entries = (struct cache_entry_ **)calloc(1,
487                 sizeof(struct cache_entry_ *) * INITIAL_ENTRIES_CAPACITY);
488         assert(retval->entries != NULL);
489
490         retval->entries_capacity = INITIAL_ENTRIES_CAPACITY;
491         retval->entries_size = 0;
492
493         TRACE_OUT(init_cache);
494         return (retval);
495 }
496
497 void
498 destroy_cache(struct cache_ *the_cache)
499 {
500
501         TRACE_IN(destroy_cache);
502         assert(the_cache != NULL);
503
504         if (the_cache->entries != NULL) {
505                 size_t i;
506                 for (i = 0; i < the_cache->entries_size; ++i)
507                         destroy_cache_entry(the_cache->entries[i]);
508
509                 free(the_cache->entries);
510         }
511
512         free(the_cache);
513         TRACE_OUT(destroy_cache);
514 }
515
516 int
517 register_cache_entry(struct cache_ *the_cache,
518         struct cache_entry_params const *params)
519 {
520         int policies_size;
521         size_t entry_name_size;
522         struct cache_common_entry_      *new_common_entry;
523         struct cache_mp_entry_          *new_mp_entry;
524
525         TRACE_IN(register_cache_entry);
526         assert(the_cache != NULL);
527
528         if (find_cache_entry(the_cache, params->entry_name) != NULL) {
529                 TRACE_OUT(register_cache_entry);
530                 return (-1);
531         }
532
533         if (the_cache->entries_size == the_cache->entries_capacity) {
534                 struct cache_entry_ **new_entries;
535                 size_t  new_capacity;
536
537                 new_capacity = the_cache->entries_capacity +
538                         ENTRIES_CAPACITY_STEP;
539                 new_entries = (struct cache_entry_ **)calloc(1,
540                         sizeof(struct cache_entry_ *) * new_capacity);
541                 assert(new_entries != NULL);
542
543                 memcpy(new_entries, the_cache->entries,
544                         sizeof(struct cache_entry_ *)
545                         * the_cache->entries_size);
546
547                 free(the_cache->entries);
548                 the_cache->entries = new_entries;
549         }
550
551         entry_name_size = strlen(params->entry_name) + 1;
552         switch (params->entry_type)
553         {
554         case CET_COMMON:
555                 new_common_entry = (struct cache_common_entry_ *)calloc(1,
556                         sizeof(struct cache_common_entry_));
557                 assert(new_common_entry != NULL);
558
559                 memcpy(&new_common_entry->common_params, params,
560                         sizeof(struct common_cache_entry_params));
561                 new_common_entry->params =
562                   (struct cache_entry_params *)&new_common_entry->common_params;
563
564                 new_common_entry->common_params.entry_name = (char *)calloc(1,
565                         entry_name_size);
566                 assert(new_common_entry->common_params.entry_name != NULL);
567                 strlcpy(new_common_entry->common_params.entry_name,
568                         params->entry_name, entry_name_size);
569                 new_common_entry->name =
570                         new_common_entry->common_params.entry_name;
571
572                 HASHTABLE_INIT(&(new_common_entry->items),
573                         struct cache_ht_item_data_, data,
574                         new_common_entry->common_params.cache_entries_size);
575
576                 if (new_common_entry->common_params.policy == CPT_FIFO)
577                         policies_size = 1;
578                 else
579                         policies_size = 2;
580
581                 new_common_entry->policies = (struct cache_policy_ **)calloc(1,
582                         sizeof(struct cache_policy_ *) * policies_size);
583                 assert(new_common_entry->policies != NULL);
584
585                 new_common_entry->policies_size = policies_size;
586                 new_common_entry->policies[0] = init_cache_fifo_policy();
587
588                 if (policies_size > 1) {
589                         switch (new_common_entry->common_params.policy) {
590                         case CPT_LRU:
591                                 new_common_entry->policies[1] =
592                                         init_cache_lru_policy();
593                         break;
594                         case CPT_LFU:
595                                 new_common_entry->policies[1] =
596                                         init_cache_lfu_policy();
597                         break;
598                         default:
599                         break;
600                         }
601                 }
602
603                 new_common_entry->get_time_func =
604                         the_cache->params.get_time_func;
605                 the_cache->entries[the_cache->entries_size++] =
606                         (struct cache_entry_ *)new_common_entry;
607                 break;
608         case CET_MULTIPART:
609                 new_mp_entry = (struct cache_mp_entry_ *)calloc(1,
610                         sizeof(struct cache_mp_entry_));
611                 assert(new_mp_entry != NULL);
612
613                 memcpy(&new_mp_entry->mp_params, params,
614                         sizeof(struct mp_cache_entry_params));
615                 new_mp_entry->params =
616                         (struct cache_entry_params *)&new_mp_entry->mp_params;
617
618                 new_mp_entry->mp_params.entry_name = (char *)calloc(1,
619                         entry_name_size);
620                 assert(new_mp_entry->mp_params.entry_name != NULL);
621                 strlcpy(new_mp_entry->mp_params.entry_name, params->entry_name,
622                         entry_name_size);
623                 new_mp_entry->name = new_mp_entry->mp_params.entry_name;
624
625                 TAILQ_INIT(&new_mp_entry->ws_head);
626                 TAILQ_INIT(&new_mp_entry->rs_head);
627
628                 new_mp_entry->get_time_func = the_cache->params.get_time_func;
629                 the_cache->entries[the_cache->entries_size++] =
630                         (struct cache_entry_ *)new_mp_entry;
631                 break;
632         }
633
634
635         qsort(the_cache->entries, the_cache->entries_size,
636                 sizeof(struct cache_entry_ *), entries_qsort_cmp_func);
637
638         TRACE_OUT(register_cache_entry);
639         return (0);
640 }
641
642 int
643 unregister_cache_entry(struct cache_ *the_cache, const char *entry_name)
644 {
645         struct cache_entry_ **del_ent;
646
647         TRACE_IN(unregister_cache_entry);
648         assert(the_cache != NULL);
649
650         del_ent = find_cache_entry_p(the_cache, entry_name);
651         if (del_ent != NULL) {
652                 destroy_cache_entry(*del_ent);
653                 --the_cache->entries_size;
654
655                 memmove(del_ent, del_ent + 1,
656                         (&(the_cache->entries[--the_cache->entries_size]) -
657                         del_ent) * sizeof(struct cache_entry_ *));
658
659                 TRACE_OUT(unregister_cache_entry);
660                 return (0);
661         } else {
662                 TRACE_OUT(unregister_cache_entry);
663                 return (-1);
664         }
665 }
666
667 struct cache_entry_ *
668 find_cache_entry(struct cache_ *the_cache, const char *entry_name)
669 {
670         struct cache_entry_ **result;
671
672         TRACE_IN(find_cache_entry);
673         result = find_cache_entry_p(the_cache, entry_name);
674
675         if (result == NULL) {
676                 TRACE_OUT(find_cache_entry);
677                 return (NULL);
678         } else {
679                 TRACE_OUT(find_cache_entry);
680                 return (*result);
681         }
682 }
683
684 /*
685  * Tries to read the element with the specified key from the cache. If the
686  * value_size is too small, it will be filled with the proper number, and
687  * the user will need to call cache_read again with the value buffer, that
688  * is large enough.
689  * Function returns 0 on success, -1 on error, and -2 if the value_size is too
690  * small.
691  */
692 int
693 cache_read(struct cache_entry_ *entry, const char *key, size_t key_size,
694         char *value, size_t *value_size)
695 {
696         struct cache_common_entry_      *common_entry;
697         struct cache_ht_item_data_      item_data, *find_res;
698         struct cache_ht_item_           *item;
699         hashtable_index_t       hash;
700         struct cache_policy_item_ *connected_item;
701
702         TRACE_IN(cache_read);
703         assert(entry != NULL);
704         assert(key != NULL);
705         assert(value_size != NULL);
706         assert(entry->params->entry_type == CET_COMMON);
707
708         common_entry = (struct cache_common_entry_ *)entry;
709
710         memset(&item_data, 0, sizeof(struct cache_ht_item_data_));
711         /* can't avoid the cast here */
712         item_data.key = (char *)key;
713         item_data.key_size = key_size;
714
715         hash = HASHTABLE_CALCULATE_HASH(cache_ht_, &common_entry->items,
716                 &item_data);
717         assert(hash >= 0);
718         assert(hash < HASHTABLE_ENTRIES_COUNT(&common_entry->items));
719
720         item = HASHTABLE_GET_ENTRY(&(common_entry->items), hash);
721         find_res = HASHTABLE_ENTRY_FIND(cache_ht_, item, &item_data);
722         if (find_res == NULL) {
723                 TRACE_OUT(cache_read);
724                 return (-1);
725         }
726
727         if ((common_entry->common_params.max_lifetime.tv_sec != 0) ||
728                 (common_entry->common_params.max_lifetime.tv_usec != 0)) {
729
730                 if (find_res->fifo_policy_item->last_request_time.tv_sec -
731                         find_res->fifo_policy_item->creation_time.tv_sec >
732                         common_entry->common_params.max_lifetime.tv_sec) {
733
734                         free(find_res->key);
735                         free(find_res->value);
736
737                         connected_item =
738                             find_res->fifo_policy_item->connected_item;
739                         if (connected_item != NULL) {
740                                 common_entry->policies[1]->remove_item_func(
741                                         common_entry->policies[1],
742                                         connected_item);
743                                 common_entry->policies[1]->destroy_item_func(
744                                         connected_item);
745                         }
746
747                         common_entry->policies[0]->remove_item_func(
748                                 common_entry->policies[0],
749                                         find_res->fifo_policy_item);
750                         common_entry->policies[0]->destroy_item_func(
751                                 find_res->fifo_policy_item);
752
753                         HASHTABLE_ENTRY_REMOVE(cache_ht_, item, find_res);
754                         --common_entry->items_size;
755                 }
756         }
757
758         if ((*value_size < find_res->value_size) || (value == NULL)) {
759                 *value_size = find_res->value_size;
760                 TRACE_OUT(cache_read);
761                 return (-2);
762         }
763
764         *value_size = find_res->value_size;
765         memcpy(value, find_res->value, find_res->value_size);
766
767         ++find_res->fifo_policy_item->request_count;
768         common_entry->get_time_func(
769                 &find_res->fifo_policy_item->last_request_time);
770         common_entry->policies[0]->update_item_func(common_entry->policies[0],
771                 find_res->fifo_policy_item);
772
773         if (find_res->fifo_policy_item->connected_item != NULL) {
774                 connected_item = find_res->fifo_policy_item->connected_item;
775                 memcpy(&connected_item->last_request_time,
776                         &find_res->fifo_policy_item->last_request_time,
777                         sizeof(struct timeval));
778                 connected_item->request_count =
779                         find_res->fifo_policy_item->request_count;
780
781                 common_entry->policies[1]->update_item_func(
782                         common_entry->policies[1], connected_item);
783         }
784
785         TRACE_OUT(cache_read);
786         return (0);
787 }
788
789 /*
790  * Writes the value with the specified key into the cache entry.
791  * Functions returns 0 on success, and -1 on error.
792  */
793 int
794 cache_write(struct cache_entry_ *entry, const char *key, size_t key_size,
795         char const *value, size_t value_size)
796 {
797         struct cache_common_entry_      *common_entry;
798         struct cache_ht_item_data_      item_data, *find_res;
799         struct cache_ht_item_           *item;
800         hashtable_index_t       hash;
801
802         struct cache_policy_            *policy, *connected_policy;
803         struct cache_policy_item_       *policy_item;
804         struct cache_policy_item_       *connected_policy_item;
805
806         TRACE_IN(cache_write);
807         assert(entry != NULL);
808         assert(key != NULL);
809         assert(value != NULL);
810         assert(entry->params->entry_type == CET_COMMON);
811
812         common_entry = (struct cache_common_entry_ *)entry;
813
814         memset(&item_data, 0, sizeof(struct cache_ht_item_data_));
815         /* can't avoid the cast here */
816         item_data.key = (char *)key;
817         item_data.key_size = key_size;
818
819         hash = HASHTABLE_CALCULATE_HASH(cache_ht_, &common_entry->items,
820                 &item_data);
821         assert(hash >= 0);
822         assert(hash < HASHTABLE_ENTRIES_COUNT(&common_entry->items));
823
824         item = HASHTABLE_GET_ENTRY(&(common_entry->items), hash);
825         find_res = HASHTABLE_ENTRY_FIND(cache_ht_, item, &item_data);
826         if (find_res != NULL) {
827                 TRACE_OUT(cache_write);
828                 return (-1);
829         }
830
831         item_data.key = (char *)malloc(key_size);
832         memcpy(item_data.key, key, key_size);
833
834         item_data.value = (char *)malloc(value_size);
835         assert(item_data.value != NULL);
836
837         memcpy(item_data.value, value, value_size);
838         item_data.value_size = value_size;
839
840         policy_item = common_entry->policies[0]->create_item_func();
841         policy_item->key = item_data.key;
842         policy_item->key_size = item_data.key_size;
843         common_entry->get_time_func(&policy_item->creation_time);
844
845         if (common_entry->policies_size > 1) {
846                 connected_policy_item =
847                         common_entry->policies[1]->create_item_func();
848                 memcpy(&connected_policy_item->creation_time,
849                         &policy_item->creation_time,
850                         sizeof(struct timeval));
851                 connected_policy_item->key = policy_item->key;
852                 connected_policy_item->key_size = policy_item->key_size;
853
854                 connected_policy_item->connected_item = policy_item;
855                 policy_item->connected_item = connected_policy_item;
856         }
857
858         item_data.fifo_policy_item = policy_item;
859
860         common_entry->policies[0]->add_item_func(common_entry->policies[0],
861                 policy_item);
862         if (common_entry->policies_size > 1)
863                 common_entry->policies[1]->add_item_func(
864                         common_entry->policies[1], connected_policy_item);
865
866         HASHTABLE_ENTRY_STORE(cache_ht_, item, &item_data);
867         ++common_entry->items_size;
868
869         if ((common_entry->common_params.max_elemsize != 0) &&
870                 (common_entry->items_size >
871                 common_entry->common_params.max_elemsize)) {
872                 if (common_entry->policies_size > 1) {
873                         policy = common_entry->policies[1];
874                         connected_policy = common_entry->policies[0];
875                 } else {
876                         policy = common_entry->policies[0];
877                         connected_policy = NULL;
878                 }
879
880                 flush_cache_policy(common_entry, policy, connected_policy,
881                         cache_elemsize_common_continue_func);
882         }
883
884         TRACE_OUT(cache_write);
885         return (0);
886 }
887
888 /*
889  * Initializes the write session for the specified multipart entry. This
890  * session then should be filled with data either committed or abandoned by
891  * using close_cache_mp_write_session or abandon_cache_mp_write_session
892  * respectively.
893  * Returns NULL on errors (when there are too many opened write sessions for
894  * the entry).
895  */
896 struct cache_mp_write_session_ *
897 open_cache_mp_write_session(struct cache_entry_ *entry)
898 {
899         struct cache_mp_entry_  *mp_entry;
900         struct cache_mp_write_session_  *retval;
901
902         TRACE_IN(open_cache_mp_write_session);
903         assert(entry != NULL);
904         assert(entry->params->entry_type == CET_MULTIPART);
905         mp_entry = (struct cache_mp_entry_ *)entry;
906
907         if ((mp_entry->mp_params.max_sessions > 0) &&
908                 (mp_entry->ws_size == mp_entry->mp_params.max_sessions)) {
909                 TRACE_OUT(open_cache_mp_write_session);
910                 return (NULL);
911         }
912
913         retval = (struct cache_mp_write_session_ *)calloc(1,
914                 sizeof(struct cache_mp_write_session_));
915         assert(retval != NULL);
916
917         TAILQ_INIT(&retval->items);
918         retval->parent_entry = mp_entry;
919
920         TAILQ_INSERT_HEAD(&mp_entry->ws_head, retval, entries);
921         ++mp_entry->ws_size;
922
923         TRACE_OUT(open_cache_mp_write_session);
924         return (retval);
925 }
926
927 /*
928  * Writes data to the specified session. Return 0 on success and -1 on errors
929  * (when write session size limit is exceeded).
930  */
931 int
932 cache_mp_write(struct cache_mp_write_session_ *ws, char *data,
933         size_t data_size)
934 {
935         struct cache_mp_data_item_      *new_item;
936
937         TRACE_IN(cache_mp_write);
938         assert(ws != NULL);
939         assert(ws->parent_entry != NULL);
940         assert(ws->parent_entry->params->entry_type == CET_MULTIPART);
941
942         if ((ws->parent_entry->mp_params.max_elemsize > 0) &&
943                 (ws->parent_entry->mp_params.max_elemsize == ws->items_size)) {
944                 TRACE_OUT(cache_mp_write);
945                 return (-1);
946         }
947
948         new_item = (struct cache_mp_data_item_ *)calloc(1,
949                 sizeof(struct cache_mp_data_item_));
950         assert(new_item != NULL);
951
952         new_item->value = (char *)malloc(data_size);
953         assert(new_item->value != NULL);
954         memcpy(new_item->value, data, data_size);
955         new_item->value_size = data_size;
956
957         TAILQ_INSERT_TAIL(&ws->items, new_item, entries);
958         ++ws->items_size;
959
960         TRACE_OUT(cache_mp_write);
961         return (0);
962 }
963
964 /*
965  * Abandons the write session and frees all the connected resources.
966  */
967 void
968 abandon_cache_mp_write_session(struct cache_mp_write_session_ *ws)
969 {
970
971         TRACE_IN(abandon_cache_mp_write_session);
972         assert(ws != NULL);
973         assert(ws->parent_entry != NULL);
974         assert(ws->parent_entry->params->entry_type == CET_MULTIPART);
975
976         TAILQ_REMOVE(&ws->parent_entry->ws_head, ws, entries);
977         --ws->parent_entry->ws_size;
978
979         destroy_cache_mp_write_session(ws);
980         TRACE_OUT(abandon_cache_mp_write_session);
981 }
982
983 /*
984  * Commits the session to the entry, for which it was created.
985  */
986 void
987 close_cache_mp_write_session(struct cache_mp_write_session_ *ws)
988 {
989
990         TRACE_IN(close_cache_mp_write_session);
991         assert(ws != NULL);
992         assert(ws->parent_entry != NULL);
993         assert(ws->parent_entry->params->entry_type == CET_MULTIPART);
994
995         TAILQ_REMOVE(&ws->parent_entry->ws_head, ws, entries);
996         --ws->parent_entry->ws_size;
997
998         if (ws->parent_entry->completed_write_session == NULL) {
999                 /*
1000                  * If there is no completed session yet, this will be the one
1001                  */
1002                 ws->parent_entry->get_time_func(
1003                         &ws->parent_entry->creation_time);
1004                 ws->parent_entry->completed_write_session = ws;
1005         } else {
1006                 /*
1007                  * If there is a completed session, then we'll save our session
1008                  * as a pending session. If there is already a pending session,
1009                  * it would be destroyed.
1010                  */
1011                 if (ws->parent_entry->pending_write_session != NULL)
1012                         destroy_cache_mp_write_session(
1013                                 ws->parent_entry->pending_write_session);
1014
1015                 ws->parent_entry->pending_write_session = ws;
1016         }
1017         TRACE_OUT(close_cache_mp_write_session);
1018 }
1019
1020 /*
1021  * Opens read session for the specified entry. Returns NULL on errors (when
1022  * there are no data in the entry, or the data are obsolete).
1023  */
1024 struct cache_mp_read_session_ *
1025 open_cache_mp_read_session(struct cache_entry_ *entry)
1026 {
1027         struct cache_mp_entry_                  *mp_entry;
1028         struct cache_mp_read_session_   *retval;
1029
1030         TRACE_IN(open_cache_mp_read_session);
1031         assert(entry != NULL);
1032         assert(entry->params->entry_type == CET_MULTIPART);
1033         mp_entry = (struct cache_mp_entry_ *)entry;
1034
1035         if (mp_entry->completed_write_session == NULL) {
1036                 TRACE_OUT(open_cache_mp_read_session);
1037                 return (NULL);
1038         }
1039
1040         if ((mp_entry->mp_params.max_lifetime.tv_sec != 0)
1041                 || (mp_entry->mp_params.max_lifetime.tv_usec != 0)) {
1042                 if (mp_entry->last_request_time.tv_sec -
1043                         mp_entry->last_request_time.tv_sec >
1044                         mp_entry->mp_params.max_lifetime.tv_sec) {
1045                         flush_cache_entry(entry);
1046                         TRACE_OUT(open_cache_mp_read_session);
1047                         return (NULL);
1048                 }
1049         }
1050
1051         retval = (struct cache_mp_read_session_ *)calloc(1,
1052                 sizeof(struct cache_mp_read_session_));
1053         assert(retval != NULL);
1054
1055         retval->parent_entry = mp_entry;
1056         retval->current_item = TAILQ_FIRST(
1057                 &mp_entry->completed_write_session->items);
1058
1059         TAILQ_INSERT_HEAD(&mp_entry->rs_head, retval, entries);
1060         ++mp_entry->rs_size;
1061
1062         mp_entry->get_time_func(&mp_entry->last_request_time);
1063         TRACE_OUT(open_cache_mp_read_session);
1064         return (retval);
1065 }
1066
1067 /*
1068  * Reads the data from the read session - step by step.
1069  * Returns 0 on success, -1 on error (when there are no more data), and -2 if
1070  * the data_size is too small.  In the last case, data_size would be filled
1071  * the proper value.
1072  */
1073 int
1074 cache_mp_read(struct cache_mp_read_session_ *rs, char *data, size_t *data_size)
1075 {
1076
1077         TRACE_IN(cache_mp_read);
1078         assert(rs != NULL);
1079
1080         if (rs->current_item == NULL) {
1081                 TRACE_OUT(cache_mp_read);
1082                 return (-1);
1083         }
1084
1085         if (rs->current_item->value_size > *data_size) {
1086                 *data_size = rs->current_item->value_size;
1087                 if (data == NULL) {
1088                         TRACE_OUT(cache_mp_read);
1089                         return (0);
1090                 }
1091
1092                 TRACE_OUT(cache_mp_read);
1093                 return (-2);
1094         }
1095
1096         *data_size = rs->current_item->value_size;
1097         memcpy(data, rs->current_item->value, rs->current_item->value_size);
1098         rs->current_item = TAILQ_NEXT(rs->current_item, entries);
1099
1100         TRACE_OUT(cache_mp_read);
1101         return (0);
1102 }
1103
1104 /*
1105  * Closes the read session. If there are no more read sessions and there is
1106  * a pending write session, it will be committed and old
1107  * completed_write_session will be destroyed.
1108  */
1109 void
1110 close_cache_mp_read_session(struct cache_mp_read_session_ *rs)
1111 {
1112
1113         TRACE_IN(close_cache_mp_read_session);
1114         assert(rs != NULL);
1115         assert(rs->parent_entry != NULL);
1116
1117         TAILQ_REMOVE(&rs->parent_entry->rs_head, rs, entries);
1118         --rs->parent_entry->rs_size;
1119
1120         if ((rs->parent_entry->rs_size == 0) &&
1121                 (rs->parent_entry->pending_write_session != NULL)) {
1122                 destroy_cache_mp_write_session(
1123                         rs->parent_entry->completed_write_session);
1124                 rs->parent_entry->completed_write_session =
1125                         rs->parent_entry->pending_write_session;
1126                 rs->parent_entry->pending_write_session = NULL;
1127         }
1128
1129         destroy_cache_mp_read_session(rs);
1130         TRACE_OUT(close_cache_mp_read_session);
1131 }
1132
1133 int
1134 transform_cache_entry(struct cache_entry_ *entry,
1135         enum cache_transformation_t transformation)
1136 {
1137
1138         TRACE_IN(transform_cache_entry);
1139         switch (transformation) {
1140         case CTT_CLEAR:
1141                 clear_cache_entry(entry);
1142                 TRACE_OUT(transform_cache_entry);
1143                 return (0);
1144         case CTT_FLUSH:
1145                 flush_cache_entry(entry);
1146                 TRACE_OUT(transform_cache_entry);
1147                 return (0);
1148         default:
1149                 TRACE_OUT(transform_cache_entry);
1150                 return (-1);
1151         }
1152 }
1153
1154 int
1155 transform_cache_entry_part(struct cache_entry_ *entry,
1156         enum cache_transformation_t transformation, const char *key_part,
1157         size_t key_part_size, enum part_position_t part_position)
1158 {
1159         struct cache_common_entry_ *common_entry;
1160         struct cache_ht_item_ *ht_item;
1161         struct cache_ht_item_data_ *ht_item_data, ht_key;
1162
1163         struct cache_policy_item_ *item, *connected_item;
1164
1165         TRACE_IN(transform_cache_entry_part);
1166         if (entry->params->entry_type != CET_COMMON) {
1167                 TRACE_OUT(transform_cache_entry_part);
1168                 return (-1);
1169         }
1170
1171         if (transformation != CTT_CLEAR) {
1172                 TRACE_OUT(transform_cache_entry_part);
1173                 return (-1);
1174         }
1175
1176         memset(&ht_key, 0, sizeof(struct cache_ht_item_data_));
1177         ht_key.key = (char *)key_part;  /* can't avoid casting here */
1178         ht_key.key_size = key_part_size;
1179
1180         common_entry = (struct cache_common_entry_ *)entry;
1181         HASHTABLE_FOREACH(&(common_entry->items), ht_item) {
1182                 do {
1183                         ht_item_data = HASHTABLE_ENTRY_FIND_SPECIAL(cache_ht_,
1184                                 ht_item, &ht_key,
1185                                 ht_items_fixed_size_left_cmp_func);
1186
1187                         if (ht_item_data != NULL) {
1188                             item = ht_item_data->fifo_policy_item;
1189                             connected_item = item->connected_item;
1190
1191                             common_entry->policies[0]->remove_item_func(
1192                                 common_entry->policies[0],
1193                                 item);
1194
1195                             free(ht_item_data->key);
1196                             free(ht_item_data->value);
1197                             HASHTABLE_ENTRY_REMOVE(cache_ht_, ht_item,
1198                                 ht_item_data);
1199                             --common_entry->items_size;
1200
1201                             common_entry->policies[0]->destroy_item_func(
1202                                 item);
1203                             if (common_entry->policies_size == 2) {
1204                                 common_entry->policies[1]->remove_item_func(
1205                                     common_entry->policies[1],
1206                                     connected_item);
1207                                 common_entry->policies[1]->destroy_item_func(
1208                                     connected_item);
1209                             }
1210                         }
1211                 } while (ht_item_data != NULL);
1212         }
1213
1214         TRACE_OUT(transform_cache_entry_part);
1215         return (0);
1216 }