]> git.sur5r.net Git - openldap/blob - servers/slapd/overlays/pcache.c
- proxy cache erroneously returns the filtering attributes
[openldap] / servers / slapd / overlays / pcache.c
1 /* $OpenLDAP$ */
2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
3  *
4  * Copyright 2003-2004 The OpenLDAP Foundation.
5  * Portions Copyright 2003 IBM Corporation.
6  * Portions Copyright 2003 Symas Corporation.
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted only as authorized by the OpenLDAP
11  * Public License.
12  *
13  * A copy of this license is available in the file LICENSE in the
14  * top-level directory of the distribution or, alternatively, at
15  * <http://www.OpenLDAP.org/license.html>.
16  */
17 /* ACKNOWLEDGEMENTS:
18  * This work was initially developed by Apurva Kumar for inclusion
19  * in OpenLDAP Software and subsequently rewritten by Howard Chu.
20  */
21
22 #include "portable.h"
23
24 #ifdef SLAPD_OVER_PROXYCACHE
25
26 #include <stdio.h>
27
28 #include <ac/string.h>
29 #include <ac/time.h>
30
31 #include "slap.h"
32 #include "ldap_pvt.h"
33 #include "lutil.h"
34 #include "ldap_rq.h"
35
36 /* query cache structs */
37 /* query */
38
39 typedef struct Query_s {
40         Filter*         filter;         /* Search Filter */
41         AttributeName*  attrs;          /* Projected attributes */
42         AttributeName*  save_attrs;     /* original attributes, saved for response */
43         struct berval   base;           /* Search Base */
44         int             scope;          /* Search scope */
45 } Query;
46
47 /* struct representing a cached query */
48 typedef struct cached_query_s {
49         Query                           query;          /* LDAP query */
50         struct berval                   q_uuid;         /* query identifier */
51         int                             template_id;    /* template of the query */
52         time_t                          expiry_time;    /* time till the query is considered valid */
53         struct cached_query_s           *next;          /* next query in the template */
54         struct cached_query_s           *prev;          /* previous query in the template */
55         struct cached_query_s           *lru_up;        /* previous query in the LRU list */
56         struct cached_query_s           *lru_down;      /* next query in the LRU list */
57 } CachedQuery;
58
59 /* struct representing a query template
60  * e.g. template string = &(cn=)(mail=)
61  */
62 typedef struct query_template_s {
63         struct berval   querystr;       /* Filter string corresponding to the QT */
64         int             attr_set_index; /* determines the projected attributes */
65
66         CachedQuery*    query;          /* most recent query cached for the template */
67         CachedQuery*    query_last;     /* oldest query cached for the template */
68
69         int             no_of_queries;  /* Total number of queries in the template */
70         long            ttl;            /* TTL for the queries of this template */
71         ldap_pvt_thread_rdwr_t t_rwlock; /* Rd/wr lock for accessing queries in the template */
72 } QueryTemplate;
73
74 /*
75  * Represents a set of projected attributes and any
76  * supersets among all specified sets of attributes.
77  */
78
79 struct attr_set {
80         AttributeName*  attrs;          /* specifies the set */
81         int             count;          /* number of attributes */
82         int*            ID_array;       /* array of indices of supersets of 'attrs' */
83 };
84
85 struct query_manager_s;
86
87 /* prototypes for functions for 1) query containment
88  * 2) query addition, 3) cache replacement
89  */
90 typedef int     (QCfunc)(struct query_manager_s*, Query*, int );
91 typedef void    (AddQueryfunc)(struct query_manager_s*, Query*, int, struct berval*);
92 typedef void    (CRfunc)(struct query_manager_s*, struct berval * );
93
94 /* LDAP query cache */
95 typedef struct query_manager_s {
96         struct attr_set*        attr_sets;              /* possible sets of projected attributes */
97         QueryTemplate*          templates;              /* cacheable templates */
98
99         CachedQuery*            lru_top;                /* top and bottom of LRU list */
100         CachedQuery*            lru_bottom;
101
102         ldap_pvt_thread_mutex_t         lru_mutex;      /* mutex for accessing LRU list */
103
104         /* Query cache methods */
105         QCfunc                  *qcfunc;                        /* Query containment*/
106         CRfunc                  *crfunc;                        /* cache replacement */
107         AddQueryfunc    *addfunc;                       /* add query */
108 } query_manager;
109
110 /* LDAP query cache manager */
111 typedef struct cache_manager_s {
112         BackendDB       db;     /* underlying database */
113         unsigned long   num_cached_queries;             /* total number of cached queries */
114         unsigned long   max_queries;                    /* upper bound on # of cached queries */
115         int     numattrsets;                    /* number of attribute sets */
116         int     numtemplates;                   /* number of cacheable templates */
117         int     cur_entries;                    /* current number of entries cached */
118         int     max_entries;                    /* max number of entries cached */
119         int     num_entries_limit;              /* max # of entries in a cacheable query */
120
121         char    response_cb;                    /* install the response callback
122                                                  * at the tail of the callback list */
123 #define PCACHE_RESPONSE_CB_HEAD 0
124 #define PCACHE_RESPONSE_CB_TAIL 1
125
126         int     cc_period;              /* interval between successive consistency checks (sec) */
127         int     cc_paused;
128         void    *cc_arg;
129
130         ldap_pvt_thread_mutex_t         cache_mutex;
131         ldap_pvt_thread_mutex_t         remove_mutex;
132
133         query_manager*   qm;    /* query cache managed by the cache manager */
134 } cache_manager;
135
136 static AttributeDescription *ad_queryid;
137 static char *queryid_schema = "( 1.3.6.1.4.1.4203.666.1.12 NAME 'queryid' "
138                         "DESC 'list of queries the entry belongs to' "
139                         "EQUALITY octetStringMatch "
140                         "SYNTAX 1.3.6.1.4.1.1466.115.121.1.40{64} "
141                         "NO-USER-MODIFICATION USAGE directoryOperation )";
142
143 /* Return 1 for an added entry, else 0 */
144 static int
145 merge_entry(
146         Operation               *op,
147         Entry                   *e,
148         struct berval*          query_uuid )
149 {
150         int             rc;
151         Modifications* modlist = NULL;
152         const char*     text = NULL;
153         Attribute               *attr;
154         char                    textbuf[SLAP_TEXT_BUFLEN];
155         size_t                  textlen = sizeof(textbuf);
156
157         SlapReply sreply = {REP_RESULT};
158
159         slap_callback cb = { NULL, slap_null_cb, NULL, NULL };
160
161         attr = e->e_attrs;
162         e->e_attrs = NULL;
163
164         /* add queryid attribute */
165         attr_merge_one( e, ad_queryid, query_uuid, NULL );
166
167         /* append the attribute list from the fetched entry */
168         e->e_attrs->a_next = attr;
169
170         op->o_tag = LDAP_REQ_ADD;
171         op->o_protocol = LDAP_VERSION3;
172         op->o_callback = &cb;
173         op->o_time = slap_get_time();
174         op->o_do_not_cache = 1;
175
176         op->ora_e = e;
177         op->o_req_dn = e->e_name;
178         op->o_req_ndn = e->e_nname;
179         rc = op->o_bd->be_add( op, &sreply );
180
181         if ( rc != LDAP_SUCCESS ) {
182                 if ( rc == LDAP_ALREADY_EXISTS ) {
183                         slap_entry2mods( e, &modlist, &text, textbuf, textlen );
184                         modlist->sml_op = LDAP_MOD_ADD;
185                         op->o_tag = LDAP_REQ_MODIFY;
186                         op->orm_modlist = modlist;
187                         op->o_bd->be_modify( op, &sreply );
188                         slap_mods_free( modlist );
189                 } else if ( rc == LDAP_REFERRAL ||
190                                         rc == LDAP_NO_SUCH_OBJECT ) {
191                         syncrepl_add_glue( op, e );
192                         e = NULL;
193                         rc = 1;
194                 }
195                 if ( e ) {
196                         entry_free( e );
197                         rc = 0;
198                 }
199         } else {
200                 be_entry_release_w( op, e );
201                 rc = 1;
202         }
203
204         return rc;
205 }
206
207 /* compare base and scope of incoming and cached queries */
208 static int base_scope_compare(
209         struct berval* ndn_stored,
210         struct berval* ndn_incoming,
211         int scope_stored,
212         int scope_incoming      )
213 {
214         struct berval pdn_incoming = BER_BVNULL;
215
216         if (scope_stored < scope_incoming)
217                 return 0;
218
219         if ( !dnIsSuffix(ndn_incoming, ndn_stored))
220                 return 0;
221
222         switch(scope_stored) {
223         case LDAP_SCOPE_BASE:
224                 return (ndn_incoming->bv_len == ndn_stored->bv_len);
225
226         case LDAP_SCOPE_ONELEVEL:
227                 switch(scope_incoming){
228                 case LDAP_SCOPE_BASE:
229                         dnParent(ndn_incoming, &pdn_incoming);
230                         return (pdn_incoming.bv_len == ndn_stored->bv_len);
231
232                 case LDAP_SCOPE_ONELEVEL:
233                         return (ndn_incoming->bv_len == ndn_stored->bv_len);
234
235                 default:
236                         return 0;
237                 }
238         case LDAP_SCOPE_SUBTREE:
239                 return 1;
240                 break;
241         default:
242                 return 0;
243                 break;
244     }
245 }
246
247 /* add query on top of LRU list */
248 static void
249 add_query_on_top (query_manager* qm, CachedQuery* qc)
250 {
251         CachedQuery* top = qm->lru_top;
252         Query* q = (Query*)qc;
253
254         qm->lru_top = qc;
255
256         if (top)
257                 top->lru_up = qc;
258         else
259                 qm->lru_bottom = qc;
260
261         qc->lru_down = top;
262         qc->lru_up = NULL;
263 #ifdef NEW_LOGGING
264         LDAP_LOG( BACK_META, DETAIL1, "Base of added query = %s\n",
265                         q->base.bv_val, 0, 0 );
266 #else
267         Debug( LDAP_DEBUG_ANY, "Base of added query = %s\n",
268                         q->base.bv_val, 0, 0 );
269 #endif
270 }
271
272 /* remove_query from LRU list */
273
274 static void
275 remove_query (query_manager* qm, CachedQuery* qc)
276 {
277         CachedQuery* up;
278         CachedQuery* down;
279
280         if (!qc)
281                 return;
282
283         up = qc->lru_up;
284         down = qc->lru_down;
285
286         if (!up)
287                 qm->lru_top = down;
288
289         if (!down)
290                 qm->lru_bottom = up;
291
292         if (down)
293                 down->lru_up = up;
294
295         if (up)
296                 up->lru_down = down;
297
298         qc->lru_up = qc->lru_down = NULL;
299 }
300
301 static void
302 invert_string( struct berval *bv )
303 {
304         int i;
305         char c;
306
307         for (i=0; i<bv->bv_len/2; i++) {
308                 c = bv->bv_val[i];
309                 bv->bv_val[i] = bv->bv_val[bv->bv_len-i-1];
310                 bv->bv_val[bv->bv_len-i-1] = c;
311         }
312 }
313
314 /* find and remove string2 from string1
315  * from start if position = 1,
316  * from end if position = 3,
317  * from anywhere if position = 2
318  */
319
320 static int
321 find_and_remove(struct berval* ber1, struct berval* ber2, int position)
322 {
323         char* temp;
324         int len;
325         int ret=0;
326
327         char* arg1, *arg2;
328         char* string1=ber1->bv_val;
329         char* string2=ber2->bv_val;
330
331         if (string2 == NULL)
332                 return 1;
333         if (string1 == NULL)
334                 return 0;
335
336         if (position == 3) {
337                 invert_string(ber1);
338                 invert_string(ber2);
339         }
340
341         arg1 = string1;
342         arg2 = string2;
343
344         temp = strstr(arg1, arg2);
345
346         len = ber2->bv_len;
347
348         if ( temp && (position == 2 || temp == arg1) ) {
349                 string1 = temp+len;
350                 strcpy( arg1, string1 );
351                 ber1->bv_len -= len;
352                 ret = 1;
353         }
354         if ( position == 3 ) {
355                 invert_string(ber1);
356                 invert_string(ber2);
357         }
358         return ret;
359 }
360
361
362 static struct berval*
363 merge_init_final(struct berval* init, struct berval* any, struct berval* final)
364 {
365         struct berval* merged, *temp;
366         int i, any_count, count;
367
368         for (any_count=0; any && any[any_count].bv_val; any_count++)
369                 ;
370
371         count = any_count;
372
373         if (init->bv_val)
374                 count++;
375         if (final->bv_val)
376                 count++;
377
378         merged = (struct berval*)(ch_malloc((count+1)*sizeof(struct berval)));
379         temp = merged;
380
381         if (init->bv_val) {
382                 *temp++ = *init;
383         }
384
385         for (i=0; i<any_count; i++) {
386                 *temp++ = *any++;
387         }
388
389         if (final->bv_val){
390                 *temp++ = *final;
391         }
392         temp->bv_val = NULL;
393         temp->bv_len = 0;
394         return merged;
395 }
396
397 static int
398 strings_containment(struct berval* stored, struct berval* incoming)
399 {
400         struct berval* element;
401         int k=0;
402         int j, rc = 0;
403
404         for ( element=stored; element->bv_val != NULL; element++ ) {
405                 for (j = k; incoming[j].bv_val != NULL; j++) {
406                         if (find_and_remove(&(incoming[j]), element, 2)) {
407                                 k = j;
408                                 rc = 1;
409                                 break;
410                         }
411                         rc = 0;
412                 }
413                 if ( rc ) {
414                         continue;
415                 } else {
416                         return 0;
417                 }
418         }
419         return 1;
420 }
421
422 static int
423 substr_containment_substr(Filter* stored, Filter* incoming)
424 {
425         int i;
426         int rc = 0;
427         int any_count = 0;
428
429         struct berval init_incoming;
430         struct berval final_incoming;
431         struct berval *any_incoming = NULL;
432         struct berval *remaining_incoming = NULL;
433
434         if ((!(incoming->f_sub_initial.bv_val) && (stored->f_sub_initial.bv_val))
435            || (!(incoming->f_sub_final.bv_val) && (stored->f_sub_final.bv_val)))
436                 return 0;
437
438
439         ber_dupbv(&init_incoming, &(incoming->f_sub_initial));
440         ber_dupbv(&final_incoming, &(incoming->f_sub_final));
441
442         if (incoming->f_sub_any) {
443                 for ( any_count=0; incoming->f_sub_any[any_count].bv_val != NULL;
444                                 any_count++ )
445                         ;
446
447                 any_incoming = (struct berval*)ch_malloc((any_count+1) *
448                                                 sizeof(struct berval));
449
450                 for (i=0; i<any_count; i++) {
451                         ber_dupbv(&(any_incoming[i]), &(incoming->f_sub_any[i]));
452                 }
453                 any_incoming[any_count].bv_val = NULL;
454                 any_incoming[any_count].bv_len = 0;
455         }
456
457         if (find_and_remove(&init_incoming,
458                         &(stored->f_sub_initial), 1) && find_and_remove(&final_incoming,
459                         &(stored->f_sub_final), 3))
460         {
461                 if (stored->f_sub_any == NULL) {
462                         rc = 1;
463                         goto final;
464                 }
465                 remaining_incoming = merge_init_final(&init_incoming,
466                                                 any_incoming, &final_incoming);
467                 rc = strings_containment(stored->f_sub_any, remaining_incoming);
468         }
469 final:
470         free(init_incoming.bv_val);
471         free(final_incoming.bv_val);
472         if (any_incoming) ber_bvarray_free( any_incoming );
473         free(remaining_incoming);
474
475         return rc;
476 }
477
478 static int
479 substr_containment_equality(Filter* stored, Filter* incoming)
480 {
481         struct berval incoming_val[2];
482         int rc = 0;
483
484         ber_dupbv(incoming_val, &(incoming->f_av_value));
485         incoming_val[1].bv_val = NULL;
486         incoming_val[1].bv_len = 0;
487
488         if (find_and_remove(incoming_val,
489                         &(stored->f_sub_initial), 1) && find_and_remove(incoming_val,
490                         &(stored->f_sub_final), 3)) {
491                 if (stored->f_sub_any == NULL){
492                         rc = 1;
493                         goto final;
494                 }
495                 rc = strings_containment(stored->f_sub_any, incoming_val);
496         }
497 final:
498         free(incoming_val[0].bv_val);
499         return rc;
500 }
501
502 /* check whether query is contained in any of
503  * the cached queries in template template_index
504  */
505 static int
506 query_containment(query_manager *qm,
507                   Query *query,
508                   int template_index)
509 {
510         QueryTemplate* templa= qm->templates;
511         CachedQuery* qc;
512         Query* q;
513         Filter* inputf = query->filter;
514         struct berval* base = &(query->base);
515         int scope = query->scope;
516         int res=0;
517         Filter* fs;
518         Filter* fi;
519         int ret, rc;
520         const char* text;
521
522         MatchingRule* mrule = NULL;
523         if (inputf != NULL) {
524 #ifdef NEW_LOGGING
525                 LDAP_LOG( BACK_META, DETAIL1, "Lock QC index = %d\n",
526                                 template_index, 0, 0 );
527 #else
528                 Debug( LDAP_DEBUG_ANY, "Lock QC index = %d\n",
529                                 template_index, 0, 0 );
530 #endif
531                 ldap_pvt_thread_rdwr_rlock(&(templa[template_index].t_rwlock));
532                 for(qc=templa[template_index].query; qc != NULL; qc= qc->next) {
533                         q = (Query*)qc;
534                         if(base_scope_compare(&(q->base), base, q->scope, scope)) {
535                                 fi = inputf;
536                                 fs = q->filter;
537                                 do {
538                                         res=0;
539                                         switch (fs->f_choice) {
540                                         case LDAP_FILTER_EQUALITY:
541                                                 if (fi->f_choice == LDAP_FILTER_EQUALITY)
542                                                         mrule = fs->f_ava->aa_desc->ad_type->sat_equality;
543                                                 else
544                                                         ret = 1;
545                                                 break;
546                                         case LDAP_FILTER_GE:
547                                         case LDAP_FILTER_LE:
548                                                 mrule = fs->f_ava->aa_desc->ad_type->sat_ordering;
549                                                 break;
550                                         default:
551                                                 mrule = NULL; 
552                                         }
553                                         if (mrule) {
554                                                 rc = value_match(&ret, fs->f_ava->aa_desc, mrule,
555                                                         SLAP_MR_VALUE_OF_ASSERTION_SYNTAX,
556                                                         &(fi->f_ava->aa_value),
557                                                         &(fs->f_ava->aa_value), &text);
558                                                 if (rc != LDAP_SUCCESS) {
559                                                         ldap_pvt_thread_rdwr_runlock(&(templa[template_index].t_rwlock));
560 #ifdef NEW_LOGGING
561                                                         LDAP_LOG( BACK_META, DETAIL1,
562                                                         "Unlock: Exiting QC index=%d\n",
563                                                         template_index, 0, 0 );
564 #else
565                                                         Debug( LDAP_DEBUG_ANY,
566                                                         "Unlock: Exiting QC index=%d\n",
567                                                         template_index, 0, 0 );
568 #endif
569                                                         return 0;
570                                                 }
571                                         }
572                                         switch (fs->f_choice) {
573                                         case LDAP_FILTER_OR:
574                                         case LDAP_FILTER_AND:
575                                                 fs = fs->f_and;
576                                                 fi = fi->f_and;
577                                                 res=1;
578                                                 break;
579                                         case LDAP_FILTER_SUBSTRINGS:
580                                                 /* check if the equality query can be
581                                                 * answered with cached substring query */
582                                                 if ((fi->f_choice == LDAP_FILTER_EQUALITY)
583                                                         && substr_containment_equality(
584                                                         fs, fi))
585                                                         res=1;
586                                                 /* check if the substring query can be
587                                                 * answered with cached substring query */
588                                                 if ((fi->f_choice ==LDAP_FILTER_SUBSTRINGS
589                                                         ) && substr_containment_substr(
590                                                         fs, fi))
591                                                         res= 1;
592                                                 fs=fs->f_next;
593                                                 fi=fi->f_next;
594                                                 break;
595                                         case LDAP_FILTER_PRESENT:
596                                                 res=1;
597                                                 fs=fs->f_next;
598                                                 fi=fi->f_next;
599                                                 break;
600                                         case LDAP_FILTER_EQUALITY:
601                                                 if (ret == 0)
602                                                         res = 1;
603                                                 fs=fs->f_next;
604                                                 fi=fi->f_next;
605                                                 break;
606                                         case LDAP_FILTER_GE:
607                                                 if (ret >= 0)
608                                                         res = 1;
609                                                 fs=fs->f_next;
610                                                 fi=fi->f_next;
611                                                 break;
612                                         case LDAP_FILTER_LE:
613                                                 if (ret <= 0)
614                                                         res = 1;
615                                                 fs=fs->f_next;
616                                                 fi=fi->f_next;
617                                                 break;
618                                         case LDAP_FILTER_NOT:
619                                                 res=0;
620                                                 break;
621                                         default:
622                                                 break;
623                                         }
624                                 } while((res) && (fi != NULL) && (fs != NULL));
625
626                                 if(res) {
627                                         ldap_pvt_thread_mutex_lock(&qm->lru_mutex);
628                                         if (qm->lru_top != qc) {
629                                                 remove_query(qm, qc);
630                                                 add_query_on_top(qm, qc);
631                                         }
632                                         ldap_pvt_thread_mutex_unlock(&qm->lru_mutex);
633                                         return 1;
634                                 }
635                         }
636                 }
637 #ifdef NEW_LOGGING
638                 LDAP_LOG( BACK_META, DETAIL1,
639                         "Not answerable: Unlock QC index=%d\n",
640                         template_index, 0, 0 );
641 #else
642                 Debug( LDAP_DEBUG_ANY,
643                         "Not answerable: Unlock QC index=%d\n",
644                         template_index, 0, 0 );
645 #endif
646                 ldap_pvt_thread_rdwr_runlock(&(templa[template_index].t_rwlock));
647         }
648         return 0;
649 }
650
651 static void
652 free_query (CachedQuery* qc)
653 {
654         Query* q = (Query*)qc;
655         int i;
656
657         free(qc->q_uuid.bv_val);
658         filter_free(q->filter);
659         free (q->base.bv_val);
660         for (i=0; q->attrs[i].an_name.bv_val; i++) {
661                 free(q->attrs[i].an_name.bv_val);
662         }
663         free(q->attrs);
664         free(qc);
665 }
666
667
668 /* Add query to query cache */
669 static void add_query(
670         query_manager* qm,
671         Query* query,
672         int template_index,
673         struct berval* uuid)
674 {
675         CachedQuery* new_cached_query = (CachedQuery*) ch_malloc(sizeof(CachedQuery));
676         QueryTemplate* templ = (qm->templates)+template_index;
677         Query* new_query;
678         new_cached_query->template_id = template_index;
679         new_cached_query->q_uuid = *uuid;
680         new_cached_query->lru_up = NULL;
681         new_cached_query->lru_down = NULL;
682         new_cached_query->expiry_time = slap_get_time() + templ->ttl;
683 #ifdef NEW_LOGGING
684         LDAP_LOG( BACK_META, DETAIL1, "Added query expires at %ld\n",
685                         (long) new_cached_query->expiry_time, 0, 0 );
686 #else
687         Debug( LDAP_DEBUG_ANY, "Added query expires at %ld\n",
688                         (long) new_cached_query->expiry_time, 0, 0 );
689 #endif
690         new_query = (Query*)new_cached_query;
691
692         ber_dupbv(&new_query->base, &query->base);
693         new_query->scope = query->scope;
694         new_query->filter = query->filter;
695         new_query->attrs = query->attrs;
696
697         /* Adding a query    */
698 #ifdef NEW_LOGGING
699         LDAP_LOG( BACK_META, DETAIL1, "Lock AQ index = %d\n",
700                         template_index, 0, 0 );
701 #else
702         Debug( LDAP_DEBUG_ANY, "Lock AQ index = %d\n",
703                         template_index, 0, 0 );
704 #endif
705         ldap_pvt_thread_rdwr_wlock(&templ->t_rwlock);
706         if (templ->query == NULL)
707                 templ->query_last = new_cached_query;
708         else
709                 templ->query->prev = new_cached_query;
710         new_cached_query->next = templ->query;
711         new_cached_query->prev = NULL;
712         templ->query = new_cached_query;
713         templ->no_of_queries++;
714 #ifdef NEW_LOGGING
715         LDAP_LOG( BACK_META, DETAIL1, "TEMPLATE %d QUERIES++ %d\n",
716                         template_index, templ->no_of_queries, 0 );
717 #else
718         Debug( LDAP_DEBUG_ANY, "TEMPLATE %d QUERIES++ %d\n",
719                         template_index, templ->no_of_queries, 0 );
720 #endif
721
722 #ifdef NEW_LOGGING
723         LDAP_LOG( BACK_META, DETAIL1, "Unlock AQ index = %d \n",
724                         template_index, 0, 0 );
725 #else
726         Debug( LDAP_DEBUG_ANY, "Unlock AQ index = %d \n",
727                         template_index, 0, 0 );
728 #endif
729         ldap_pvt_thread_rdwr_wunlock(&templ->t_rwlock);
730
731         /* Adding on top of LRU list  */
732         ldap_pvt_thread_mutex_lock(&qm->lru_mutex);
733         add_query_on_top(qm, new_cached_query);
734         ldap_pvt_thread_mutex_unlock(&qm->lru_mutex);
735 }
736
737 static void
738 remove_from_template (CachedQuery* qc, QueryTemplate* template)
739 {
740         if (!qc->prev && !qc->next) {
741                 template->query_last = template->query = NULL;
742         } else if (qc->prev == NULL) {
743                 qc->next->prev = NULL;
744                 template->query = qc->next;
745         } else if (qc->next == NULL) {
746                 qc->prev->next = NULL;
747                 template->query_last = qc->prev;
748         } else {
749                 qc->next->prev = qc->prev;
750                 qc->prev->next = qc->next;
751         }
752
753         template->no_of_queries--;
754 }
755
756 /* remove bottom query of LRU list from the query cache */
757 static void cache_replacement(query_manager* qm, struct berval *result)
758 {
759         CachedQuery* bottom;
760         int temp_id;
761
762         ldap_pvt_thread_mutex_lock(&qm->lru_mutex);
763         bottom = qm->lru_bottom;
764
765         result->bv_val = NULL;
766         result->bv_len = 0;
767
768         if (!bottom) {
769 #ifdef NEW_LOGGING
770                 LDAP_LOG ( BACK_META, DETAIL1,
771                         "Cache replacement invoked without "
772                         "any query in LRU list\n", 0, 0, 0 );
773 #else
774                 Debug ( LDAP_DEBUG_ANY,
775                         "Cache replacement invoked without "
776                         "any query in LRU list\n", 0, 0, 0 );
777 #endif
778                 return;
779         }
780
781         temp_id = bottom->template_id;
782         remove_query(qm, bottom);
783         ldap_pvt_thread_mutex_unlock(&qm->lru_mutex);
784
785         *result = bottom->q_uuid;
786         bottom->q_uuid.bv_val = NULL;
787
788 #ifdef NEW_LOGGING
789         LDAP_LOG( BACK_META, DETAIL1, "Lock CR index = %d\n", temp_id, 0, 0 );
790 #else
791         Debug( LDAP_DEBUG_ANY, "Lock CR index = %d\n", temp_id, 0, 0 );
792 #endif
793         ldap_pvt_thread_rdwr_wlock(&(qm->templates[temp_id].t_rwlock));
794         remove_from_template(bottom, (qm->templates+temp_id));
795 #ifdef NEW_LOGGING
796         LDAP_LOG( BACK_META, DETAIL1, "TEMPLATE %d QUERIES-- %d\n",
797                 temp_id, qm->templates[temp_id].no_of_queries, 0 );
798 #else
799         Debug( LDAP_DEBUG_ANY, "TEMPLATE %d QUERIES-- %d\n",
800                 temp_id, qm->templates[temp_id].no_of_queries, 0 );
801 #endif
802 #ifdef NEW_LOGGING
803         LDAP_LOG( BACK_META, DETAIL1, "Unlock CR index = %d\n", temp_id, 0, 0 );
804 #else
805         Debug( LDAP_DEBUG_ANY, "Unlock CR index = %d\n", temp_id, 0, 0 );
806 #endif
807         ldap_pvt_thread_rdwr_wunlock(&(qm->templates[temp_id].t_rwlock));
808         free_query(bottom);
809 }
810
811 struct query_info {
812         struct query_info *next;
813         struct berval xdn;
814         int del;
815 };
816
817 static int
818 remove_func (
819         Operation       *op,
820         SlapReply       *rs
821 )
822 {
823         Attribute *attr;
824         struct query_info *qi;
825         int count = 0;
826
827         if ( rs->sr_type != REP_SEARCH ) return 0;
828
829         for (attr = rs->sr_entry->e_attrs; attr!= NULL; attr = attr->a_next) {
830                 if (attr->a_desc == ad_queryid) {
831                         for (count=0; attr->a_vals[count].bv_val; count++)
832                                 ;
833                         break;
834                 }
835         }
836         if ( count == 0 ) return 0;
837         qi = op->o_tmpalloc( sizeof( struct query_info ), op->o_tmpmemctx );
838         qi->next = op->o_callback->sc_private;
839         op->o_callback->sc_private = qi;
840         ber_dupbv_x( &qi->xdn, &rs->sr_entry->e_nname, op->o_tmpmemctx );
841         qi->del = ( count == 1 );
842
843         return 0;
844 }
845
846 static int
847 remove_query_data (
848         Operation       *op,
849         SlapReply       *rs,
850         struct berval* query_uuid)
851 {
852         struct query_info       *qi, *qnext;
853         char                    filter_str[64];
854         AttributeAssertion      ava;
855         Filter                  filter = {LDAP_FILTER_EQUALITY};
856         SlapReply               sreply = {REP_RESULT};
857         slap_callback cb = { NULL, remove_func, NULL, NULL };
858         int deleted = 0;
859
860         sreply.sr_entry = NULL;
861         sreply.sr_nentries = 0;
862         op->ors_filterstr.bv_len = snprintf(filter_str, sizeof(filter_str),
863                 "(%s=%s)", ad_queryid->ad_cname.bv_val, query_uuid->bv_val);
864         filter.f_ava = &ava;
865         filter.f_av_desc = ad_queryid;
866         filter.f_av_value = *query_uuid;
867
868         op->o_tag = LDAP_REQ_SEARCH;
869         op->o_protocol = LDAP_VERSION3;
870         op->o_callback = &cb;
871         op->o_time = slap_get_time();
872         op->o_do_not_cache = 1;
873
874         op->o_req_dn = op->o_bd->be_suffix[0];
875         op->o_req_ndn = op->o_bd->be_nsuffix[0];
876         op->ors_scope = LDAP_SCOPE_SUBTREE;
877         op->ors_deref = LDAP_DEREF_NEVER;
878         op->ors_slimit = SLAP_NO_LIMIT;
879         op->ors_tlimit = SLAP_NO_LIMIT;
880         op->ors_filter = &filter;
881         op->ors_filterstr.bv_val = filter_str;
882         op->ors_filterstr.bv_len = strlen(filter_str);
883         op->ors_attrs = NULL;
884         op->ors_attrsonly = 0;
885
886         op->o_bd->be_search( op, &sreply );
887
888         for ( qi=cb.sc_private; qi; qi=qnext ) {
889                 qnext = qi->next;
890
891                 op->o_req_dn = qi->xdn;
892                 op->o_req_ndn = qi->xdn;
893
894                 if ( qi->del) {
895 #ifdef NEW_LOGGING
896                         LDAP_LOG( BACK_META, DETAIL1,
897                                 "DELETING ENTRY TEMPLATE=%s\n",
898                                 query_uuid->bv_val, 0, 0 );
899 #else
900                         Debug( LDAP_DEBUG_ANY, "DELETING ENTRY TEMPLATE=%s\n",
901                                 query_uuid->bv_val, 0, 0 );
902 #endif
903
904                         op->o_tag = LDAP_REQ_DELETE;
905
906                         if (op->o_bd->be_delete(op, &sreply) == LDAP_SUCCESS) {
907                                 deleted++;
908                         }
909                 } else {
910                         Modifications mod;
911                         struct berval vals[2];
912
913                         vals[0] = *query_uuid;
914                         vals[1].bv_val = NULL;
915                         vals[1].bv_len = 0;
916                         mod.sml_op = LDAP_MOD_DELETE;
917                         mod.sml_desc = ad_queryid;
918                         mod.sml_type = ad_queryid->ad_cname;
919                         mod.sml_values = vals;
920                         mod.sml_nvalues = NULL;
921                         mod.sml_next = NULL;
922 #ifdef NEW_LOGGING
923                         LDAP_LOG( BACK_META, DETAIL1,
924                                 "REMOVING TEMP ATTR : TEMPLATE=%s\n",
925                                 query_uuid->bv_val, 0, 0 );
926 #else
927                         Debug( LDAP_DEBUG_ANY,
928                                 "REMOVING TEMP ATTR : TEMPLATE=%s\n",
929                                 query_uuid->bv_val, 0, 0 );
930 #endif
931
932                         op->orm_modlist = &mod;
933
934                         op->o_bd->be_modify( op, &sreply );
935                 }
936                 op->o_tmpfree( qi->xdn.bv_val, op->o_tmpmemctx );
937                 op->o_tmpfree( qi, op->o_tmpmemctx );
938         }
939         return deleted;
940 }
941
942 static int
943 get_attr_set(
944         AttributeName* attrs,
945         query_manager* qm,
946         int num
947 );
948
949 static int
950 attrscmp(
951         AttributeName* attrs_in,
952         AttributeName* attrs
953 );
954
955 static int
956 is_temp_answerable(
957         int attr_set,
958         struct berval* tempstr,
959         query_manager* qm,
960         int template_id )
961 {
962         QueryTemplate *qt = qm->templates + template_id;
963
964         if (attr_set != qt->attr_set_index) {
965                 int* id_array = qm->attr_sets[attr_set].ID_array;
966
967                 while (*id_array != -1) {
968                         if (*id_array == qt->attr_set_index)
969                                 break;
970                         id_array++;
971                 }
972                 if (*id_array == -1)
973                         return 0;
974         }
975         return (qt->querystr.bv_len == tempstr->bv_len &&
976                 strcasecmp(qt->querystr.bv_val, tempstr->bv_val) == 0);
977 }
978
979 static int
980 filter2template(
981         Filter                  *f,
982         struct                  berval *fstr,
983         AttributeName**         filter_attrs,
984         int*                    filter_cnt )
985 {
986         AttributeDescription *ad;
987
988         switch ( f->f_choice ) {
989         case LDAP_FILTER_EQUALITY:
990                 ad = f->f_av_desc;
991                 sprintf( fstr->bv_val+fstr->bv_len, "(%s=)", ad->ad_cname.bv_val );
992                 fstr->bv_len += ad->ad_cname.bv_len + ( sizeof("(=)") - 1 );
993                 break;
994
995         case LDAP_FILTER_GE:
996                 ad = f->f_av_desc;
997                 sprintf( fstr->bv_val+fstr->bv_len, "(%s>=)", ad->ad_cname.bv_val);
998                 fstr->bv_len += ad->ad_cname.bv_len + ( sizeof("(>=)") - 1 );
999                 break;
1000
1001         case LDAP_FILTER_LE:
1002                 ad = f->f_av_desc;
1003                 sprintf( fstr->bv_val+fstr->bv_len, "(%s<=)", ad->ad_cname.bv_val);
1004                 fstr->bv_len += ad->ad_cname.bv_len + ( sizeof("(<=)") - 1 );
1005                 break;
1006
1007         case LDAP_FILTER_APPROX:
1008                 ad = f->f_av_desc;
1009                 sprintf( fstr->bv_val+fstr->bv_len, "(%s~=)", ad->ad_cname.bv_val);
1010                 fstr->bv_len += ad->ad_cname.bv_len + ( sizeof("(~=)") - 1 );
1011                 break;
1012
1013         case LDAP_FILTER_SUBSTRINGS:
1014                 ad = f->f_sub_desc;
1015                 sprintf( fstr->bv_val+fstr->bv_len, "(%s=)", ad->ad_cname.bv_val );
1016                 fstr->bv_len += ad->ad_cname.bv_len + ( sizeof("(=)") - 1 );
1017                 break;
1018
1019         case LDAP_FILTER_PRESENT:
1020                 ad = f->f_desc;
1021                 sprintf( fstr->bv_val+fstr->bv_len, "(%s=*)", ad->ad_cname.bv_val );
1022                 fstr->bv_len += ad->ad_cname.bv_len + ( sizeof("(=*)") - 1 );
1023                 break;
1024
1025         case LDAP_FILTER_AND:
1026         case LDAP_FILTER_OR:
1027         case LDAP_FILTER_NOT: {
1028                 int rc = 0;
1029                 sprintf( fstr->bv_val+fstr->bv_len, "(%c",
1030                         f->f_choice == LDAP_FILTER_AND ? '&' :
1031                         f->f_choice == LDAP_FILTER_OR ? '|' : '!' );
1032                 fstr->bv_len += sizeof("(%") - 1;
1033
1034                 for ( f = f->f_list; f != NULL; f = f->f_next ) {
1035                         rc = filter2template( f, fstr, filter_attrs, filter_cnt );
1036                         if ( rc ) break;
1037                 }
1038                 sprintf( fstr->bv_val+fstr->bv_len, ")" );
1039                 fstr->bv_len += sizeof(")") - 1;
1040
1041                 return rc;
1042                 }
1043
1044         default:
1045                 strcpy( fstr->bv_val, "(?=?)" );
1046                 fstr->bv_len += sizeof("(?=?)")-1;
1047                 return -1;
1048         }
1049
1050         *filter_attrs = (AttributeName *)ch_realloc(*filter_attrs,
1051                                 (*filter_cnt + 2)*sizeof(AttributeName));
1052
1053         (*filter_attrs)[*filter_cnt].an_desc = ad;
1054         (*filter_attrs)[*filter_cnt].an_name = ad->ad_cname;
1055         (*filter_attrs)[*filter_cnt+1].an_name.bv_val = NULL;
1056         (*filter_attrs)[*filter_cnt+1].an_name.bv_len = 0;
1057         (*filter_cnt)++;
1058         return 0;
1059 }
1060
1061 struct search_info {
1062         slap_overinst *on;
1063         Query query;
1064         int template_id;
1065         int max;
1066         int over;
1067         int count;
1068         Entry *head, *tail;
1069 };
1070
1071 static int
1072 cache_entries(
1073         Operation       *op,
1074         SlapReply       *rs,
1075         struct berval *query_uuid)
1076 {
1077         struct search_info *si = op->o_callback->sc_private;
1078         slap_overinst *on = si->on;
1079         cache_manager *cm = on->on_bi.bi_private;
1080         query_manager*          qm = cm->qm;
1081         int             i;
1082         int             return_val = 0;
1083         Entry           *e;
1084         struct berval   crp_uuid;
1085         char            uuidbuf[ LDAP_LUTIL_UUIDSTR_BUFSIZE ];
1086         Operation op_tmp = *op;
1087
1088         query_uuid->bv_len = lutil_uuidstr(uuidbuf, sizeof(uuidbuf));
1089         ber_str2bv(uuidbuf, query_uuid->bv_len, 1, query_uuid);
1090
1091         op_tmp.o_bd = &cm->db;
1092         op_tmp.o_dn = cm->db.be_rootdn;
1093         op_tmp.o_ndn = cm->db.be_rootndn;
1094
1095 #ifdef NEW_LOGGING
1096         LDAP_LOG( BACK_META, DETAIL1, "UUID for query being added = %s\n",
1097                         uuidbuf, 0, 0 );
1098 #else /* !NEW_LOGGING */
1099         Debug( LDAP_DEBUG_ANY, "UUID for query being added = %s\n",
1100                         uuidbuf, 0, 0 );
1101 #endif /* !NEW_LOGGING */
1102
1103         for ( e=si->head; e; e=si->head ) {
1104                 si->head = e->e_private;
1105                 e->e_private = NULL;
1106 #ifdef NEW_LOGGING
1107                 LDAP_LOG( BACK_META, DETAIL2, "LOCKING REMOVE MUTEX\n",
1108                                 0, 0, 0 );
1109 #else /* !NEW_LOGGING */
1110                 Debug( LDAP_DEBUG_NONE, "LOCKING REMOVE MUTEX\n", 0, 0, 0 );
1111 #endif /* !NEW_LOGGING */
1112                 ldap_pvt_thread_mutex_lock(&cm->remove_mutex);
1113 #ifdef NEW_LOGGING
1114                 LDAP_LOG( BACK_META, DETAIL2, "LOCKED REMOVE MUTEX\n", 0, 0, 0);
1115 #else /* !NEW_LOGGING */
1116                 Debug( LDAP_DEBUG_NONE, "LOCKED REMOVE MUTEX\n", 0, 0, 0);
1117 #endif /* !NEW_LOGGING */
1118                 while ( cm->cur_entries > (cm->max_entries) ) {
1119                                 qm->crfunc(qm, &crp_uuid);
1120                                 if (crp_uuid.bv_val) {
1121 #ifdef NEW_LOGGING
1122                                         LDAP_LOG( BACK_META, DETAIL1,
1123                                                 "Removing query UUID %s\n",
1124                                                 crp_uuid.bv_val, 0, 0 );
1125 #else /* !NEW_LOGGING */
1126                                         Debug( LDAP_DEBUG_ANY,
1127                                                 "Removing query UUID %s\n",
1128                                                 crp_uuid.bv_val, 0, 0 );
1129 #endif /* !NEW_LOGGING */
1130                                         return_val = remove_query_data(&op_tmp, rs, &crp_uuid);
1131 #ifdef NEW_LOGGING
1132                                         LDAP_LOG( BACK_META, DETAIL1,
1133                                                 "QUERY REMOVED, SIZE=%d\n",
1134                                                 return_val, 0, 0);
1135 #else /* !NEW_LOGGING */
1136                                         Debug( LDAP_DEBUG_ANY,
1137                                                 "QUERY REMOVED, SIZE=%d\n",
1138                                                 return_val, 0, 0);
1139 #endif /* !NEW_LOGGING */
1140                                         ldap_pvt_thread_mutex_lock(
1141                                                         &cm->cache_mutex );
1142                                         cm->cur_entries -= return_val;
1143                                         cm->num_cached_queries--;
1144 #ifdef NEW_LOGGING
1145                                         LDAP_LOG( BACK_META, DETAIL1,
1146                                                 "STORED QUERIES = %lu\n",
1147                                                 cm->num_cached_queries, 0, 0 );
1148 #else /* !NEW_LOGGING */
1149                                         Debug( LDAP_DEBUG_ANY,
1150                                                 "STORED QUERIES = %lu\n",
1151                                                 cm->num_cached_queries, 0, 0 );
1152 #endif /* !NEW_LOGGING */
1153                                         ldap_pvt_thread_mutex_unlock(
1154                                                         &cm->cache_mutex );
1155 #ifdef NEW_LOGGING
1156                                         LDAP_LOG( BACK_META, DETAIL1,
1157                                                 "QUERY REMOVED, CACHE ="
1158                                                 "%d entries\n",
1159                                                 cm->cur_entries, 0, 0 );
1160 #else /* !NEW_LOGGING */
1161                                         Debug( LDAP_DEBUG_ANY,
1162                                                 "QUERY REMOVED, CACHE ="
1163                                                 "%d entries\n",
1164                                                 cm->cur_entries, 0, 0 );
1165 #endif /* !NEW_LOGGING */
1166                                 }
1167                 }
1168
1169                 return_val = merge_entry(&op_tmp, e, query_uuid);
1170                 ldap_pvt_thread_mutex_unlock(&cm->remove_mutex);
1171                 ldap_pvt_thread_mutex_lock(&cm->cache_mutex);
1172                 cm->cur_entries += return_val;
1173 #ifdef NEW_LOGGING
1174                 LDAP_LOG( BACK_META, DETAIL1,
1175                         "ENTRY ADDED/MERGED, CACHED ENTRIES=%d\n",
1176                         cm->cur_entries, 0, 0 );
1177 #else /* !NEW_LOGGING */
1178                 Debug( LDAP_DEBUG_ANY,
1179                         "ENTRY ADDED/MERGED, CACHED ENTRIES=%d\n",
1180                         cm->cur_entries, 0, 0 );
1181 #endif /* !NEW_LOGGING */
1182                 return_val = 0;
1183                 ldap_pvt_thread_mutex_unlock(&cm->cache_mutex);
1184         }
1185         ldap_pvt_thread_mutex_lock(&cm->cache_mutex);
1186         cm->num_cached_queries++;
1187 #ifdef NEW_LOGGING
1188         LDAP_LOG( BACK_META, DETAIL1, "STORED QUERIES = %lu\n",
1189                         cm->num_cached_queries, 0, 0 );
1190 #else /* !NEW_LOGGING */
1191         Debug( LDAP_DEBUG_ANY, "STORED QUERIES = %lu\n",
1192                         cm->num_cached_queries, 0, 0 );
1193 #endif /* !NEW_LOGGING */
1194         ldap_pvt_thread_mutex_unlock(&cm->cache_mutex);
1195
1196         return return_val;
1197 }
1198
1199 static int
1200 proxy_cache_response(
1201         Operation       *op,
1202         SlapReply       *rs )
1203 {
1204         struct search_info *si = op->o_callback->sc_private;
1205         slap_overinst *on = si->on;
1206         cache_manager *cm = on->on_bi.bi_private;
1207         query_manager*          qm = cm->qm;
1208         struct berval uuid;
1209
1210         if ( rs->sr_type == REP_SEARCH ) {
1211                 Entry *e;
1212                 /* If we haven't exceeded the limit for this query,
1213                  * build a chain of answers to store. If we hit the
1214                  * limit, empty the chain and ignore the rest.
1215                  */
1216                 if ( !si->over ) {
1217                         if ( si->count < si->max ) {
1218                                 si->count++;
1219                                 e = entry_dup( rs->sr_entry );
1220                                 if ( !si->head ) si->head = e;
1221                                 if ( si->tail ) si->tail->e_private = e;
1222                                 si->tail = e;
1223                         } else {
1224                                 si->over = 1;
1225                                 si->count = 0;
1226                                 for (;si->head; si->head=e) {
1227                                         e = si->head->e_private;
1228                                         si->head->e_private = NULL;
1229                                         entry_free(si->head);
1230                                 }
1231                                 si->tail = NULL;
1232                         }
1233                 }
1234
1235                 if (rs->sr_attrs != op->ors_attrs ) {
1236                         op->o_tmpfree( rs->sr_attrs, op->o_tmpmemctx );
1237                 }
1238                 rs->sr_attrs = si->query.save_attrs;
1239                 op->o_tmpfree( op->ors_attrs, op->o_tmpmemctx );
1240                 op->ors_attrs = si->query.save_attrs;
1241                 si->query.save_attrs = NULL;
1242
1243         } else if ( rs->sr_type == REP_RESULT ) {
1244                 if ( si->count && cache_entries( op, rs, &uuid ) == 0 ) {
1245                         qm->addfunc(qm, &si->query, si->template_id, &uuid);
1246                         /* If the consistency checker suspended itself,
1247                          * wake it back up
1248                          */
1249                         if ( cm->cc_paused ) {
1250                                 ldap_pvt_thread_mutex_lock( &syncrepl_rq.rq_mutex );
1251                                 if ( cm->cc_paused ) {
1252                                         cm->cc_paused = 0;
1253                                         ldap_pvt_runqueue_resched( &syncrepl_rq, cm->cc_arg, 0 );
1254                                 }
1255                                 ldap_pvt_thread_mutex_unlock( &syncrepl_rq.rq_mutex );
1256                         }
1257                 }
1258
1259                 /* free self */
1260                 op->o_callback->sc_cleanup = slap_freeself_cb;
1261         }
1262         return SLAP_CB_CONTINUE;
1263 }
1264
1265 static void
1266 add_filter_attrs(
1267         Operation *op,
1268         AttributeName** new_attrs,
1269         AttributeName* attrs,
1270         AttributeName* filter_attrs )
1271 {
1272         int alluser = 0;
1273         int allop = 0;
1274         int i;
1275         int count;
1276
1277         /* duplicate attrs */
1278         if (attrs == NULL) {
1279                 count = 1;
1280         } else {
1281                 for (count=0; attrs[count].an_name.bv_val; count++)
1282                         ;
1283         }
1284         *new_attrs = (AttributeName*)(op->o_tmpalloc((count+1)*
1285                 sizeof(AttributeName), op->o_tmpmemctx));
1286         if (attrs == NULL) {
1287                 (*new_attrs)[0].an_name.bv_val = "*";
1288                 (*new_attrs)[0].an_name.bv_len = 1;
1289                 (*new_attrs)[1].an_name.bv_val = NULL;
1290                 (*new_attrs)[1].an_name.bv_len = 0;
1291                 alluser = 1;
1292                 allop = 0;
1293         } else {
1294                 for (i=0; i<count; i++) {
1295                         (*new_attrs)[i].an_name = attrs[i].an_name;
1296                         (*new_attrs)[i].an_desc = attrs[i].an_desc;
1297                 }
1298                 (*new_attrs)[count].an_name.bv_val = NULL;
1299                 (*new_attrs)[count].an_name.bv_len = 0;
1300                 alluser = an_find(*new_attrs, &AllUser);
1301                 allop = an_find(*new_attrs, &AllOper);
1302         }
1303
1304         for ( i=0; filter_attrs[i].an_name.bv_val; i++ ) {
1305                 if ( an_find(*new_attrs, &filter_attrs[i].an_name ))
1306                         continue;
1307                 if ( is_at_operational(filter_attrs[i].an_desc->ad_type) ) {
1308                         if (allop)
1309                                 continue;
1310                 } else if (alluser)
1311                         continue;
1312                 *new_attrs = (AttributeName*)(op->o_tmprealloc(*new_attrs,
1313                                         (count+2)*sizeof(AttributeName), op->o_tmpmemctx));
1314                 (*new_attrs)[count].an_name = filter_attrs[i].an_name;
1315                 (*new_attrs)[count].an_desc = filter_attrs[i].an_desc;
1316                 count++;
1317                 (*new_attrs)[count].an_name.bv_val = NULL;
1318                 (*new_attrs)[count].an_name.bv_len = 0;
1319         }
1320 }
1321
1322 static int
1323 proxy_cache_search(
1324         Operation       *op,
1325         SlapReply       *rs )
1326 {
1327         slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
1328         cache_manager *cm = on->on_bi.bi_private;
1329         query_manager*          qm = cm->qm;
1330
1331         int count;
1332
1333         int i = -1;
1334
1335         AttributeName   *filter_attrs = NULL;
1336         AttributeName   *new_attrs = NULL;
1337
1338         Query           query;
1339
1340         int             attr_set = -1;
1341         int             template_id = -1;
1342         int             answerable = 0;
1343         int             cacheable = 0;
1344         int             fattr_cnt=0;
1345         int             oc_attr_absent = 1;
1346
1347         struct berval tempstr;
1348
1349         tempstr.bv_val = op->o_tmpalloc( op->ors_filterstr.bv_len+1, op->o_tmpmemctx );
1350         tempstr.bv_len = 0;
1351         if (filter2template(op->ors_filter, &tempstr, &filter_attrs, &fattr_cnt)) {
1352                 op->o_tmpfree( tempstr.bv_val, op->o_tmpmemctx );
1353                 return SLAP_CB_CONTINUE;
1354         }
1355
1356 #ifdef NEW_LOGGING
1357         LDAP_LOG( BACK_META, DETAIL1, "query template of incoming query = %s\n",
1358                                         tempstr.bv_val, 0, 0 );
1359 #else /* !NEW_LOGGING */
1360         Debug( LDAP_DEBUG_ANY, "query template of incoming query = %s\n",
1361                                         tempstr.bv_val, 0, 0 );
1362 #endif /* !NEW_LOGGING */
1363
1364         /* find attr set */
1365         attr_set = get_attr_set(op->ors_attrs, qm, cm->numattrsets);
1366
1367         query.filter = op->ors_filter;
1368         query.attrs = op->ors_attrs;
1369         query.save_attrs = NULL;
1370         query.base = op->o_req_ndn;
1371         query.scope = op->ors_scope;
1372
1373         /* check for query containment */
1374         if (attr_set > -1) {
1375                 for (i=0; i<cm->numtemplates; i++) {
1376                         /* find if template i can potentially answer tempstr */
1377                         if (!is_temp_answerable(attr_set, &tempstr, qm, i))
1378                                 continue;
1379                         if (attr_set == qm->templates[i].attr_set_index) {
1380                                 cacheable = 1;
1381                                 template_id = i;
1382                         }
1383 #ifdef NEW_LOGGING
1384                         LDAP_LOG( BACK_META, DETAIL2,
1385                                         "Entering QC, querystr = %s\n",
1386                                         op->ors_filterstr.bv_val, 0, 0 );
1387 #else /* !NEW_LOGGING */
1388                         Debug( LDAP_DEBUG_NONE, "Entering QC, querystr = %s\n",
1389                                         op->ors_filterstr.bv_val, 0, 0 );
1390 #endif /* !NEW_LOGGING */
1391                         answerable = (*(qm->qcfunc))(qm, &query, i);
1392
1393                         if (answerable)
1394                                 break;
1395                 }
1396         }
1397         op->o_tmpfree( tempstr.bv_val, op->o_tmpmemctx );
1398
1399         query.save_attrs = op->ors_attrs;
1400         query.attrs = NULL;
1401
1402         if (answerable) {
1403                 /* Need to clear the callbacks of the original operation,
1404                  * in case there are other overlays */
1405                 BackendDB       *save_bd = op->o_bd;
1406                 slap_callback   *save_cb = op->o_callback;
1407
1408 #ifdef NEW_LOGGING
1409                 LDAP_LOG( BACK_META, DETAIL1, "QUERY ANSWERABLE\n", 0, 0, 0 );
1410 #else /* !NEW_LOGGING */
1411                 Debug( LDAP_DEBUG_ANY, "QUERY ANSWERABLE\n", 0, 0, 0 );
1412 #endif /* !NEW_LOGGING */
1413                 free(filter_attrs);
1414                 ldap_pvt_thread_rdwr_runlock(&qm->templates[i].t_rwlock);
1415                 op->o_bd = &cm->db;
1416                 op->o_callback = NULL;
1417                 i = cm->db.bd_info->bi_op_search( op, rs );
1418                 op->o_bd = save_bd;
1419                 op->o_callback = save_cb;
1420                 return i;
1421         }
1422
1423 #ifdef NEW_LOGGING
1424         LDAP_LOG( BACK_META, DETAIL1, "QUERY NOT ANSWERABLE\n",
1425                                 0, 0, 0 );
1426 #else /* !NEW_LOGGING */
1427         Debug( LDAP_DEBUG_ANY, "QUERY NOT ANSWERABLE\n", 0, 0, 0 );
1428 #endif /* !NEW_LOGGING */
1429
1430         ldap_pvt_thread_mutex_lock(&cm->cache_mutex);
1431         if (cm->num_cached_queries >= cm->max_queries) {
1432                 cacheable = 0;
1433         }
1434         ldap_pvt_thread_mutex_unlock(&cm->cache_mutex);
1435
1436         if (cacheable) {
1437                 slap_callback           *cb;
1438                 struct search_info      *si;
1439
1440 #ifdef NEW_LOGGING
1441                 LDAP_LOG( BACK_META, DETAIL1,
1442                         "QUERY CACHEABLE\n", 0, 0, 0 );
1443 #else /* !NEW_LOGGING */
1444                 Debug( LDAP_DEBUG_ANY, "QUERY CACHEABLE\n", 0, 0, 0 );
1445 #endif /* !NEW_LOGGING */
1446                 query.filter = str2filter(op->ors_filterstr.bv_val);
1447                 if (op->ors_attrs) {
1448                         for ( count = 0; !BER_BVISNULL( &op->ors_attrs[ count ].an_name ); count++ ) {
1449                                 if ( op->ors_attrs[count].an_desc == slap_schema.si_ad_objectClass ) {
1450                                         oc_attr_absent = 0;
1451                                 }
1452                         }
1453                         query.attrs = (AttributeName *)ch_malloc( ( count + 1 + oc_attr_absent )
1454                                                                         *sizeof(AttributeName) );
1455                         for ( count = 0; !BER_BVISNULL( &op->ors_attrs[ count ].an_name ); count++ ) {
1456                                 ber_dupbv( &query.attrs[count].an_name, &op->ors_attrs[count].an_name );
1457                                 query.attrs[count].an_desc = op->ors_attrs[count].an_desc;
1458                         }
1459                         if ( oc_attr_absent ) {
1460                                 query.attrs[ count ].an_desc = slap_schema.si_ad_objectClass;
1461                                 ber_dupbv( &query.attrs[count].an_name,
1462                                         &slap_schema.si_ad_objectClass->ad_cname );
1463                                 count++;
1464                         }
1465                         query.attrs[ count ].an_name.bv_val = NULL;
1466                         query.attrs[ count ].an_name.bv_len = 0;
1467                 }
1468                 add_filter_attrs(op, &op->ors_attrs, query.attrs, filter_attrs);
1469
1470                 cb = op->o_tmpalloc( sizeof(*cb) + sizeof(*si), op->o_tmpmemctx);
1471                 cb->sc_response = proxy_cache_response;
1472                 cb->sc_cleanup = NULL;
1473                 cb->sc_private = (cb+1);
1474                 si = cb->sc_private;
1475                 si->on = on;
1476                 si->query = query;
1477                 si->template_id = template_id;
1478                 si->max = cm->num_entries_limit ;
1479                 si->over = 0;
1480                 si->count = 0;
1481                 si->head = NULL;
1482                 si->tail = NULL;
1483
1484                 if ( cm->response_cb == PCACHE_RESPONSE_CB_HEAD ) {
1485                         cb->sc_next = op->o_callback;
1486                         op->o_callback = cb;
1487
1488                 } else {
1489                         slap_callback           **pcb;
1490
1491                         /* need to move the callback at the end, in case other
1492                          * overlays are present, so that the final entry is
1493                          * actually cached */
1494                         cb->sc_next = NULL;
1495                         for ( pcb = &op->o_callback; *pcb; pcb = &(*pcb)->sc_next );
1496                         *pcb = cb;
1497                 }
1498
1499         } else {
1500 #ifdef NEW_LOGGING
1501                 LDAP_LOG( BACK_META, DETAIL1,
1502                                         "QUERY NOT CACHEABLE\n",
1503                                         0, 0, 0);
1504 #else /* !NEW_LOGGING */
1505                 Debug( LDAP_DEBUG_ANY, "QUERY NOT CACHEABLE\n",
1506                                         0, 0, 0);
1507 #endif /* !NEW_LOGGING */
1508         }
1509
1510         free(filter_attrs);
1511
1512         return SLAP_CB_CONTINUE;
1513 }
1514
1515 static int
1516 attrscmp(
1517         AttributeName* attrs_in,
1518         AttributeName* attrs)
1519 {
1520         int i, count1, count2;
1521         if ( attrs_in == NULL ) {
1522                 return (attrs ? 0 : 1);
1523         }
1524         if ( attrs == NULL )
1525                 return 0;
1526
1527         for ( count1=0;
1528               attrs_in && attrs_in[count1].an_name.bv_val != NULL;
1529               count1++ )
1530                 ;
1531         for ( count2=0;
1532               attrs && attrs[count2].an_name.bv_val != NULL;
1533               count2++)
1534                 ;
1535         if ( count1 != count2 )
1536                 return 0;
1537
1538         for ( i=0; i<count1; i++ ) {
1539                 if ( !an_find(attrs, &attrs_in[i].an_name ))
1540                         return 0;
1541         }
1542         return 1;
1543 }
1544
1545 static int
1546 get_attr_set(
1547         AttributeName* attrs,
1548         query_manager* qm,
1549         int num )
1550 {
1551         int i;
1552         for (i=0; i<num; i++) {
1553                 if (attrscmp(attrs, qm->attr_sets[i].attrs))
1554                         return i;
1555         }
1556         return -1;
1557 }
1558
1559 static void*
1560 consistency_check(
1561         void *ctx,
1562         void *arg )
1563 {
1564         struct re_s *rtask = arg;
1565         slap_overinst *on = rtask->arg;
1566         cache_manager *cm = on->on_bi.bi_private;
1567         query_manager *qm = cm->qm;
1568         Operation op = {0};
1569         Connection conn = {0};
1570
1571         SlapReply rs = {REP_RESULT};
1572         CachedQuery* query, *query_prev;
1573         int i, return_val, pause = 1;
1574         QueryTemplate* templ;
1575
1576         connection_fake_init( &conn, &op, ctx );
1577
1578         op.o_bd = &cm->db;
1579         op.o_dn = cm->db.be_rootdn;
1580         op.o_ndn = cm->db.be_rootndn;
1581
1582         cm->cc_arg = arg;
1583
1584         for (i=0; qm->templates[i].querystr.bv_val; i++) {
1585                 templ = qm->templates + i;
1586                 query = templ->query_last;
1587                 if ( query ) pause = 0;
1588                 op.o_time = slap_get_time();
1589                 ldap_pvt_thread_mutex_lock(&cm->remove_mutex);
1590                 while (query && (query->expiry_time < op.o_time)) {
1591                         ldap_pvt_thread_mutex_lock(&qm->lru_mutex);
1592                         remove_query(qm, query);
1593                         ldap_pvt_thread_mutex_unlock(&qm->lru_mutex);
1594 #ifdef NEW_LOGGING
1595                         LDAP_LOG( BACK_META, DETAIL1, "Lock CR index = %d\n",
1596                                         i, 0, 0 );
1597 #else /* !NEW_LOGGING */
1598                         Debug( LDAP_DEBUG_ANY, "Lock CR index = %d\n",
1599                                         i, 0, 0 );
1600 #endif /* !NEW_LOGGING */
1601                         ldap_pvt_thread_rdwr_wlock(&templ->t_rwlock);
1602                         remove_from_template(query, templ);
1603 #ifdef NEW_LOGGING
1604                         LDAP_LOG( BACK_META, DETAIL1,
1605                                         "TEMPLATE %d QUERIES-- %d\n",
1606                                         i, templ->no_of_queries, 0 );
1607 #else /* !NEW_LOGGING */
1608                         Debug( LDAP_DEBUG_ANY, "TEMPLATE %d QUERIES-- %d\n",
1609                                         i, templ->no_of_queries, 0 );
1610 #endif /* !NEW_LOGGING */
1611 #ifdef NEW_LOGGING
1612                         LDAP_LOG( BACK_META, DETAIL1, "Unlock CR index = %d\n",
1613                                         i, 0, 0 );
1614 #else /* !NEW_LOGGING */
1615                         Debug( LDAP_DEBUG_ANY, "Unlock CR index = %d\n",
1616                                         i, 0, 0 );
1617 #endif /* !NEW_LOGGING */
1618                         ldap_pvt_thread_rdwr_wunlock(&templ->t_rwlock);
1619                         return_val = remove_query_data(&op, &rs, &query->q_uuid);
1620 #ifdef NEW_LOGGING
1621                         LDAP_LOG( BACK_META, DETAIL1,
1622                                         "STALE QUERY REMOVED, SIZE=%d\n",
1623                                         return_val, 0, 0 );
1624 #else /* !NEW_LOGGING */
1625                         Debug( LDAP_DEBUG_ANY, "STALE QUERY REMOVED, SIZE=%d\n",
1626                                                 return_val, 0, 0 );
1627 #endif /* !NEW_LOGGING */
1628                         ldap_pvt_thread_mutex_lock(&cm->cache_mutex);
1629                         cm->cur_entries -= return_val;
1630                         cm->num_cached_queries--;
1631 #ifdef NEW_LOGGING
1632                         LDAP_LOG( BACK_META, DETAIL1, "STORED QUERIES = %lu\n",
1633                                         cm->num_cached_queries, 0, 0 );
1634 #else /* !NEW_LOGGING */
1635                         Debug( LDAP_DEBUG_ANY, "STORED QUERIES = %lu\n",
1636                                         cm->num_cached_queries, 0, 0 );
1637 #endif /* !NEW_LOGGING */
1638                         ldap_pvt_thread_mutex_unlock(&cm->cache_mutex);
1639 #ifdef NEW_LOGGING
1640                         LDAP_LOG( BACK_META, DETAIL1,
1641                                 "STALE QUERY REMOVED, CACHE ="
1642                                 "%d entries\n",
1643                                 cm->cur_entries, 0, 0 );
1644 #else /* !NEW_LOGGING */
1645                         Debug( LDAP_DEBUG_ANY,
1646                                 "STALE QUERY REMOVED, CACHE ="
1647                                 "%d entries\n",
1648                                 cm->cur_entries, 0, 0 );
1649 #endif /* !NEW_LOGGING */
1650                         query_prev = query;
1651                         query = query->prev;
1652                         free_query(query_prev);
1653                 }
1654                 ldap_pvt_thread_mutex_unlock(&cm->remove_mutex);
1655         }
1656         ldap_pvt_thread_mutex_lock( &syncrepl_rq.rq_mutex );
1657         if ( ldap_pvt_runqueue_isrunning( &syncrepl_rq, rtask )) {
1658                 ldap_pvt_runqueue_stoptask( &syncrepl_rq, rtask );
1659         }
1660         /* If there were no queries, defer processing for a while */
1661         cm->cc_paused = pause;
1662         ldap_pvt_runqueue_resched( &syncrepl_rq, rtask, pause );
1663
1664         ldap_pvt_thread_mutex_unlock( &syncrepl_rq.rq_mutex );
1665         return NULL;
1666 }
1667
1668
1669 #define MAX_ATTR_SETS 500
1670 static void find_supersets( struct attr_set* attr_sets, int numsets );
1671 static int compare_sets( struct attr_set* setA, int, int );
1672
1673 static int
1674 proxy_cache_config(
1675         BackendDB       *be,
1676         const char      *fname,
1677         int             lineno,
1678         int             argc,
1679         char            **argv
1680 )
1681 {
1682         slap_overinst   *on = (slap_overinst *)be->bd_info;
1683         cache_manager*  cm = on->on_bi.bi_private;
1684         query_manager*  qm = cm->qm;
1685         QueryTemplate*  temp;
1686         AttributeName*  attr_name;
1687         AttributeName*  attrarray;
1688         const char*     text=NULL;
1689         char            *save_argv0 = NULL;
1690
1691         int             index, i;
1692         int             num;
1693         int             rc = 0;
1694
1695         if ( strncasecmp( argv[0], "proxycache-", STRLENOF( "proxycache-" ) ) == 0 ) {
1696                 save_argv0 = argv[0];
1697                 argv[0] += STRLENOF( "proxycache-" );
1698         }
1699
1700         if ( strcasecmp( argv[0], "proxycache" ) == 0 ) {
1701                 if ( argc < 6 ) {
1702                         fprintf( stderr, "%s: line %d: missing arguments in \"proxycache"
1703                                 " <backend> <max_entries> <numattrsets> <entry limit> "
1704                                 "<cycle_time>\"\n", fname, lineno );
1705                         return( 1 );
1706                 }
1707
1708                 cm->db.bd_info = backend_info( argv[1] );
1709                 if ( !cm->db.bd_info ) {
1710                         fprintf( stderr, "%s: line %d: backend %s unknown\n",
1711                                 fname, lineno, argv[1] );
1712                         return( 1 );
1713                 }
1714                 if ( cm->db.bd_info->bi_db_init( &cm->db ) ) return( 1 );
1715
1716                 /* This type is in use, needs to be opened */
1717                 cm->db.bd_info->bi_nDB++;
1718
1719                 cm->max_entries = atoi( argv[2] );
1720
1721                 cm->numattrsets = atoi( argv[3] );
1722                 if ( cm->numattrsets > MAX_ATTR_SETS ) {
1723                         fprintf( stderr, "%s: line %d: numattrsets must be <= %d\n",
1724                                 fname, lineno, MAX_ATTR_SETS );
1725                         return( 1 );
1726                 }
1727
1728                 cm->num_entries_limit = atoi( argv[4] );
1729                 cm->cc_period = atoi( argv[5] );
1730 #ifdef NEW_LOGGING
1731                 LDAP_LOG( BACK_META, DETAIL1,
1732                                 "Total # of attribute sets to be cached = %d\n",
1733                                 cm->numattrsets, 0, 0 );
1734 #else
1735                 Debug( LDAP_DEBUG_ANY,
1736                                 "Total # of attribute sets to be cached = %d\n",
1737                                 cm->numattrsets, 0, 0 );
1738 #endif
1739                 qm->attr_sets = ( struct attr_set * )ch_malloc( cm->numattrsets *
1740                                                 sizeof( struct attr_set ));
1741                 for ( i = 0; i < cm->numattrsets; i++ ) {
1742                         qm->attr_sets[i].attrs = NULL;
1743                 }
1744
1745         } else if ( strcasecmp( argv[0], "proxyattrset" ) == 0 ) {
1746                 if ( argc < 3 ) {
1747                         fprintf( stderr, "%s: line %d: missing arguments in \"proxyattrset "
1748                                 "<index> <attributes>\"\n", fname, lineno );
1749                         return( 1 );
1750                 }
1751 #ifdef NEW_LOGGING
1752                 LDAP_LOG( BACK_META, DETAIL1, "Attribute Set # %d\n",
1753                                 atoi( argv[1] ), 0, 0 );
1754 #else
1755                 Debug( LDAP_DEBUG_ANY, "Attribute Set # %d\n",
1756                                 atoi( argv[1] ), 0, 0 );
1757 #endif
1758                 if (atoi(argv[1]) >= cm->numattrsets) {
1759                         fprintf( stderr, "%s; line %d index out of bounds \n",
1760                                         fname, lineno );
1761                         return 1;
1762                 }
1763                 index = atoi( argv[1] );
1764                 if ( argv[2] && strcmp( argv[2], "*" ) ) {
1765                         qm->attr_sets[index].count = argc - 2;
1766                         qm->attr_sets[index].attrs = (AttributeName*)ch_malloc(
1767                                                 (argc-1) * sizeof( AttributeName ));
1768                         attr_name = qm->attr_sets[index].attrs;
1769                         for ( i = 2; i < argc; i++ ) {
1770 #ifdef NEW_LOGGING
1771                                 LDAP_LOG( BACK_META, DETAIL1, "\t %s\n",
1772                                                 argv[i], 0, 0 );
1773 #else
1774                                 Debug( LDAP_DEBUG_ANY, "\t %s\n",
1775                                                 argv[i], 0, 0 );
1776 #endif
1777                                 ber_str2bv( argv[i], 0, 1,
1778                                                 &attr_name->an_name);
1779                                 attr_name->an_desc = NULL;
1780                                 slap_bv2ad( &attr_name->an_name,
1781                                                 &attr_name->an_desc, &text );
1782                                 attr_name++;
1783                                 attr_name->an_name.bv_val = NULL;
1784                                 attr_name->an_name.bv_len = 0;
1785                         }
1786                 }
1787         } else if ( strcasecmp( argv[0], "proxytemplate" ) == 0 ) {
1788                 if ( argc != 4 ) {
1789                         fprintf( stderr, "%s: line %d: missing argument(s) in "
1790                                 "\"proxytemplate <filter> <proj attr set> <TTL>\" line\n",
1791                                 fname, lineno );
1792                         return( 1 );
1793                 }
1794                 if (( i = atoi( argv[2] )) >= cm->numattrsets ) {
1795 #ifdef NEW_LOGGING
1796                         LDAP_LOG( BACK_META, DETAIL1,
1797                                         "%s: line %d, template index invalid\n",
1798                                         fname, lineno, 0 );
1799 #else
1800                         Debug( LDAP_DEBUG_ANY,
1801                                         "%s: line %d, template index invalid\n",
1802                                         fname, lineno, 0 );
1803 #endif
1804                         return 1;
1805                 }
1806                 num = cm->numtemplates;
1807                 if ( num == 0 )
1808                         find_supersets( qm->attr_sets, cm->numattrsets );
1809                 qm->templates = ( QueryTemplate* )ch_realloc( qm->templates,
1810                                 ( num + 2 ) * sizeof( QueryTemplate ));
1811                 temp = qm->templates + num;
1812                 ldap_pvt_thread_rdwr_init( &temp->t_rwlock );
1813                 temp->query = temp->query_last = NULL;
1814                 temp->ttl = atoi( argv[3] );
1815                 temp->no_of_queries = 0;
1816                 if ( argv[1] == NULL ) {
1817 #ifdef NEW_LOGGING
1818                         LDAP_LOG( BACK_META, DETAIL1,
1819                                         "Templates string not specified "
1820                                         "for template %d\n", num, 0, 0 );
1821 #else
1822                         Debug( LDAP_DEBUG_ANY,
1823                                         "Templates string not specified "
1824                                         "for template %d\n", num, 0, 0 );
1825 #endif
1826                         return 1;
1827                 }
1828                 ber_str2bv( argv[1], 0, 1, &temp->querystr );
1829 #ifdef NEW_LOGGING
1830                 LDAP_LOG( BACK_META, DETAIL1, "Template:\n", 0, 0, 0 );
1831 #else
1832                 Debug( LDAP_DEBUG_ANY, "Template:\n", 0, 0, 0 );
1833 #endif
1834 #ifdef NEW_LOGGING
1835                 LDAP_LOG( BACK_META, DETAIL1, "  query template: %s\n",
1836                                 temp->querystr.bv_val, 0, 0 );
1837 #else
1838                 Debug( LDAP_DEBUG_ANY, "  query template: %s\n",
1839                                 temp->querystr.bv_val, 0, 0 );
1840 #endif
1841                 temp->attr_set_index = i;
1842 #ifdef NEW_LOGGING
1843                 LDAP_LOG( BACK_META, DETAIL1, "  attributes: \n", 0, 0, 0 );
1844 #else
1845                 Debug( LDAP_DEBUG_ANY, "  attributes: \n", 0, 0, 0 );
1846 #endif
1847                 if ( ( attrarray = qm->attr_sets[i].attrs ) != NULL ) {
1848                         for ( i=0; attrarray[i].an_name.bv_val; i++ )
1849 #ifdef NEW_LOGGING
1850                                 LDAP_LOG( BACK_META, DETAIL1, "\t%s\n",
1851                                         attrarray[i].an_name.bv_val, 0, 0 );
1852 #else
1853                                 Debug( LDAP_DEBUG_ANY, "\t%s\n",
1854                                         attrarray[i].an_name.bv_val, 0, 0 );
1855 #endif
1856                 }
1857                 temp++; 
1858                 temp->querystr.bv_val = NULL;
1859                 cm->numtemplates++;
1860
1861         } else if ( strcasecmp( argv[0], "response-callback" ) == 0 ) {
1862                 /* set to "tail" to put the response callback
1863                  * at the end of the callback list; this is required
1864                  * in case other overlays are present, so that the
1865                  * final entry is cached. */
1866
1867                 if ( argc < 2 ) {
1868 #ifdef NEW_LOGGING
1869                         LDAP_LOG( BACK_META, DETAIL1,
1870                                         "missing specifier for \"response-callback {head(default)|tail}\" "
1871                                         "callback position\n", 0, 0, 0 );
1872 #else
1873                         Debug( LDAP_DEBUG_ANY,
1874                                         "missing specifier for \"response-callback {head(default)|tail}\" "
1875                                         "callback position\n", 0, 0, 0 );
1876 #endif
1877                         return 1;
1878                 }
1879
1880                 if ( strcasecmp( argv[1], "head" ) == 0 ) {
1881                         cm->response_cb = PCACHE_RESPONSE_CB_HEAD;
1882
1883                 } else if ( strcasecmp( argv[1], "tail" ) == 0 ) {
1884                         cm->response_cb = PCACHE_RESPONSE_CB_TAIL;
1885
1886                 } else {
1887 #ifdef NEW_LOGGING
1888                         LDAP_LOG( BACK_META, DETAIL1,
1889                                         "unknown specifier %s for \"response-callback {head(default)|tail}\" "
1890                                         "callback position\n", argv[1], 0, 0 );
1891 #else
1892                         Debug( LDAP_DEBUG_ANY,
1893                                         "unknown specifier %s for \"response-callback {head(default)|tail}\" "
1894                                         "callback position\n", argv[1], 0, 0 );
1895 #endif
1896                         return 1;
1897                 }
1898         }
1899         /* anything else */
1900         else {
1901                 rc = cm->db.bd_info->bi_db_config( &cm->db, fname, lineno, argc, argv );
1902         }
1903
1904         if ( save_argv0 ) {
1905                 argv[0] = save_argv0;
1906         }
1907
1908         return rc;
1909 }
1910
1911 static int
1912 proxy_cache_init(
1913         BackendDB *be
1914 )
1915 {
1916         slap_overinst *on = (slap_overinst *)be->bd_info;
1917         cache_manager *cm;
1918         query_manager *qm;
1919
1920         cm = (cache_manager *)ch_malloc(sizeof(cache_manager));
1921         on->on_bi.bi_private = cm;
1922
1923         qm = (query_manager*)ch_malloc(sizeof(query_manager));
1924
1925         cm->db = *be;
1926         SLAP_DBFLAGS(&cm->db) |= SLAP_DBFLAG_NO_SCHEMA_CHECK;
1927         cm->db.be_private = NULL;
1928         cm->db.be_pcl_mutexp = &cm->db.be_pcl_mutex;
1929         cm->qm = qm;
1930         cm->numattrsets = 0;
1931         cm->numtemplates = 0; 
1932         cm->num_entries_limit = 5;
1933         cm->num_cached_queries = 0;
1934         cm->max_entries = 0;
1935         cm->cur_entries = 0;
1936         cm->max_queries = 10000;
1937         cm->response_cb = PCACHE_RESPONSE_CB_TAIL;
1938         cm->cc_period = 1000;
1939         cm->cc_paused = 0;
1940
1941         qm->attr_sets = NULL;
1942         qm->templates = NULL;
1943         qm->lru_top = NULL;
1944         qm->lru_bottom = NULL;
1945
1946         qm->qcfunc = query_containment;
1947         qm->crfunc = cache_replacement;
1948         qm->addfunc = add_query;
1949         ldap_pvt_thread_mutex_init(&qm->lru_mutex);
1950
1951         ldap_pvt_thread_mutex_init(&cm->cache_mutex);
1952         ldap_pvt_thread_mutex_init(&cm->remove_mutex);
1953         return 0;
1954 }
1955
1956 static int
1957 proxy_cache_open(
1958         BackendDB *be
1959 )
1960 {
1961         slap_overinst   *on = (slap_overinst *)be->bd_info;
1962         cache_manager   *cm = on->on_bi.bi_private;
1963         int             rc = 0;
1964         int             i;
1965
1966         /* consistency check (add more...) */
1967         for ( i = 0; i < cm->numattrsets; i++ ) {
1968                 if ( cm->qm->attr_sets[i].attrs == NULL ) {
1969                         fprintf( stderr, "proxy_cache_open(): "
1970                                 "attr set %d (of %d) missing\n",
1971                                 i, cm->numattrsets );
1972                         return 1;
1973                 }
1974         }
1975
1976         rc = backend_startup_one( &cm->db );
1977
1978         /* There is no runqueue in TOOL mode */
1979         if ( slapMode & SLAP_SERVER_MODE ) {
1980                 ldap_pvt_thread_mutex_lock( &syncrepl_rq.rq_mutex );
1981                 ldap_pvt_runqueue_insert( &syncrepl_rq, cm->cc_period,
1982                         consistency_check, on );
1983                 ldap_pvt_thread_mutex_unlock( &syncrepl_rq.rq_mutex );
1984         }
1985
1986         return rc;
1987 }
1988
1989 static int
1990 proxy_cache_close(
1991         BackendDB *be
1992 )
1993 {
1994         slap_overinst *on = (slap_overinst *)be->bd_info;
1995         cache_manager *cm = on->on_bi.bi_private;
1996         query_manager *qm = cm->qm;
1997         int i, j, rc = 0;
1998
1999         if ( cm->db.bd_info->bi_db_close ) {
2000                 rc = cm->db.bd_info->bi_db_close( &cm->db );
2001         }
2002         for ( i=0; i<cm->numtemplates; i++ ) {
2003                 CachedQuery *qc, *qn;
2004                 for ( qc = qm->templates[i].query; qc; qc = qn ) {
2005                         qn = qc->next;
2006                         free_query( qc );
2007                 }
2008                 free( qm->templates[i].querystr.bv_val );
2009                 ldap_pvt_thread_rdwr_destroy( &qm->templates[i].t_rwlock );
2010         }
2011         free( qm->templates );
2012         qm->templates = NULL;
2013
2014         for ( i=0; i<cm->numattrsets; i++ ) {
2015                 free( qm->attr_sets[i].ID_array );
2016                 for ( j=0; j<qm->attr_sets[i].count; j++ ) {
2017                         free( qm->attr_sets[i].attrs[j].an_name.bv_val );
2018                 }
2019                 free( qm->attr_sets[i].attrs );
2020         }
2021         free( qm->attr_sets );
2022         qm->attr_sets = NULL;
2023
2024         return rc;
2025 }
2026
2027 static int
2028 proxy_cache_destroy(
2029         BackendDB *be
2030 )
2031 {
2032         slap_overinst *on = (slap_overinst *)be->bd_info;
2033         cache_manager *cm = on->on_bi.bi_private;
2034         query_manager *qm = cm->qm;
2035         int rc = 0;
2036
2037         if ( cm->db.bd_info->bi_db_destroy ) {
2038                 rc = cm->db.bd_info->bi_db_destroy( &cm->db );
2039         }
2040         ldap_pvt_thread_mutex_destroy(&qm->lru_mutex);
2041         ldap_pvt_thread_mutex_destroy(&cm->cache_mutex);
2042         ldap_pvt_thread_mutex_destroy(&cm->remove_mutex);
2043         free( qm );
2044         free( cm );
2045         return rc;
2046 }
2047
2048 static void
2049 find_supersets ( struct attr_set* attr_sets, int numsets )
2050 {
2051         int num[MAX_ATTR_SETS];
2052         int i, j, res;
2053         int* id_array;
2054         for ( i = 0; i < MAX_ATTR_SETS; i++ )
2055                 num[i] = 0;
2056
2057         for ( i = 0; i < numsets; i++ ) {
2058                 attr_sets[i].ID_array = (int*) ch_malloc( sizeof( int ) );
2059                 attr_sets[i].ID_array[0] = -1;
2060         }
2061
2062         for ( i = 0; i < numsets; i++ ) {
2063                 for ( j=i+1; j < numsets; j++ ) {
2064                         res = compare_sets( attr_sets, i, j );
2065                         switch ( res ) {
2066                         case 0:
2067                                 break;
2068                         case 3:
2069                         case 1:
2070                                 id_array = attr_sets[i].ID_array;
2071                                 attr_sets[i].ID_array = (int *) ch_realloc( id_array,
2072                                                         ( num[i] + 2 ) * sizeof( int ));
2073                                 attr_sets[i].ID_array[num[i]] = j;
2074                                 attr_sets[i].ID_array[num[i]+1] = -1;
2075                                 num[i]++;
2076                                 if (res == 1)
2077                                         break;
2078                         case 2:
2079                                 id_array = attr_sets[j].ID_array;
2080                                 attr_sets[j].ID_array = (int *) ch_realloc( id_array,
2081                                                 ( num[j] + 2 ) * sizeof( int ));
2082                                 attr_sets[j].ID_array[num[j]] = i;
2083                                 attr_sets[j].ID_array[num[j]+1] = -1;
2084                                 num[j]++;
2085                                 break;
2086                         }
2087                 }
2088         }
2089 }
2090
2091 /*
2092  * compares two sets of attributes (indices i and j)
2093  * returns 0: if neither set is contained in the other set
2094  *         1: if set i is contained in set j
2095  *         2: if set j is contained in set i
2096  *         3: the sets are equivalent
2097  */
2098
2099 static int
2100 compare_sets(struct attr_set* set, int i, int j)
2101 {
2102         int k,l,numI,numJ;
2103         int common=0;
2104         int result=0;
2105
2106         if (( set[i].attrs == NULL ) && ( set[j].attrs == NULL ))
2107                 return 3;
2108
2109         if ( set[i].attrs == NULL )
2110                 return 2;
2111
2112         if ( set[j].attrs == NULL )
2113                 return 1;
2114
2115         numI = set[i].count;
2116         numJ = set[j].count;
2117
2118         for ( l=0; l < numI; l++ ) {
2119                 for ( k = 0; k < numJ; k++ ) {
2120                         if ( strcmp( set[i].attrs[l].an_name.bv_val,
2121                                      set[j].attrs[k].an_name.bv_val ) == 0 )
2122                                 common++;
2123                 }
2124         }
2125
2126         if ( common == numI )
2127                 result = 1;
2128
2129         if ( common == numJ )
2130                 result += 2;
2131
2132         return result;
2133 }
2134
2135 static slap_overinst proxy_cache;
2136
2137 int pcache_init()
2138 {
2139         LDAPAttributeType *at;
2140         int code;
2141         const char *err;
2142
2143         at = ldap_str2attributetype( queryid_schema, &code, &err,
2144                 LDAP_SCHEMA_ALLOW_ALL );
2145         if ( !at ) {
2146                 fprintf( stderr, "AttributeType Load failed %s %s\n",
2147                         ldap_scherr2str(code), err );
2148                 return code;
2149         }
2150         code = at_add( at, &err );
2151         if ( !code ) {
2152                 slap_str2ad( at->at_names[0], &ad_queryid, &err );
2153         }
2154         ldap_memfree( at );
2155         if ( code ) {
2156                 fprintf( stderr, "AttributeType Load failed %s %s\n",
2157                         scherr2str(code), err );
2158                 return code;
2159         }
2160
2161         proxy_cache.on_bi.bi_type = "proxycache";
2162         proxy_cache.on_bi.bi_db_init = proxy_cache_init;
2163         proxy_cache.on_bi.bi_db_config = proxy_cache_config;
2164         proxy_cache.on_bi.bi_db_open = proxy_cache_open;
2165         proxy_cache.on_bi.bi_db_close = proxy_cache_close;
2166         proxy_cache.on_bi.bi_db_destroy = proxy_cache_destroy;
2167         proxy_cache.on_bi.bi_op_search = proxy_cache_search;
2168
2169         return overlay_register( &proxy_cache );
2170 }
2171
2172 #if SLAPD_OVER_PROXYCACHE == SLAPD_MOD_DYNAMIC
2173 int init_module(int argc, char *argv[]) {
2174         return pcache_init();
2175 }
2176 #endif
2177
2178 #endif  /* defined(SLAPD_OVER_PROXYCACHE) */