]> git.sur5r.net Git - openldap/blob - servers/slapd/overlays/pcache.c
use UUID syntax for queryId
[openldap] / servers / slapd / overlays / pcache.c
1 /* $OpenLDAP$ */
2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
3  *
4  * Copyright 2003-2007 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 "lutil.h"
33 #include "ldap_rq.h"
34 #include "avl.h"
35
36 #include "config.h"
37
38 #ifdef LDAP_DEVEL
39 /*
40  * Control that allows to access the private DB
41  * instead of the public one
42  */
43 #define PCACHE_CONTROL_PRIVDB           "1.3.6.1.4.1.4203.666.11.9.5.1"
44
45 /*
46  * Extended Operation that allows to remove a query from the cache
47  */
48 #define PCACHE_EXOP_QUERY_DELETE        "1.3.6.1.4.1.4203.666.11.9.6.1"
49 #endif
50
51 /* query cache structs */
52 /* query */
53
54 typedef struct Query_s {
55         Filter*         filter;         /* Search Filter */
56         struct berval   base;           /* Search Base */
57         int             scope;          /* Search scope */
58 } Query;
59
60 struct query_template_s;
61
62 typedef struct Qbase_s {
63         Avlnode *scopes[4];             /* threaded AVL trees of cached queries */
64         struct berval base;
65         int queries;
66 } Qbase;
67
68 /* struct representing a cached query */
69 typedef struct cached_query_s {
70         Filter                                  *filter;
71         Filter                                  *first;
72         Qbase                                   *qbase;
73         int                                             scope;
74         struct berval                   q_uuid;         /* query identifier */
75         struct query_template_s         *qtemp; /* template of the query */
76         time_t                          expiry_time;    /* time till the query is considered valid */
77         struct cached_query_s           *next;          /* next query in the template */
78         struct cached_query_s           *prev;          /* previous query in the template */
79         struct cached_query_s           *lru_up;        /* previous query in the LRU list */
80         struct cached_query_s           *lru_down;      /* next query in the LRU list */
81 } CachedQuery;
82
83 /*
84  * URL representation:
85  *
86  * ldap:///<base>??<scope>?<filter>?x-uuid=<uid>,x-template=<template>,x-attrset=<attrset>,x-expiry=<expiry>
87  *
88  * <base> ::= CachedQuery.qbase->base
89  * <scope> ::= CachedQuery.scope
90  * <filter> ::= filter2bv(CachedQuery.filter)
91  * <uuid> ::= CachedQuery.q_uuid
92  * <template> ::= CachedQuery.qtemp->querystr           [FIXME: better give it an ID?]
93  * <attrset> ::= CachedQuery.qtemp->attr_set_index      [FIXME: better give it an ID?]
94  * <expiry> ::= CachedQuery.expiry_time
95  *
96  * quick hack: parse URI, call add_query() and then fix
97  * CachedQuery.expiry_time and CachedQuery.q_uuid
98  */
99
100 /*
101  * Represents a set of projected attributes.
102  */
103
104 struct attr_set {
105         struct query_template_s *templates;
106         AttributeName*  attrs;          /* specifies the set */
107         unsigned        flags;
108 #define PC_CONFIGURED   (0x1)
109 #define PC_REFERENCED   (0x2)
110 #define PC_GOT_OC               (0x4)
111         int             count;          /* number of attributes */
112 };
113
114 /* struct representing a query template
115  * e.g. template string = &(cn=)(mail=)
116  */
117 typedef struct query_template_s {
118         struct query_template_s *qtnext;
119         struct query_template_s *qmnext;
120
121         Avlnode*                qbase;
122         CachedQuery*    query;          /* most recent query cached for the template */
123         CachedQuery*    query_last;     /* oldest query cached for the template */
124         ldap_pvt_thread_rdwr_t t_rwlock; /* Rd/wr lock for accessing queries in the template */
125         struct berval   querystr;       /* Filter string corresponding to the QT */
126
127         int             attr_set_index; /* determines the projected attributes */
128         int             no_of_queries;  /* Total number of queries in the template */
129         time_t          ttl;            /* TTL for the queries of this template */
130         time_t          negttl;         /* TTL for negative results */
131         struct attr_set t_attrs;        /* filter attrs + attr_set */
132 } QueryTemplate;
133
134 struct query_manager_s;
135
136 /* prototypes for functions for 1) query containment
137  * 2) query addition, 3) cache replacement
138  */
139 typedef CachedQuery *   (QCfunc)(Operation *op, struct query_manager_s*, Query*, QueryTemplate*);
140 typedef CachedQuery *   (AddQueryfunc)(Operation *op, struct query_manager_s*, Query*, QueryTemplate*, int positive);
141 typedef void    (CRfunc)(struct query_manager_s*, struct berval * );
142
143 /* LDAP query cache */
144 typedef struct query_manager_s {
145         struct attr_set*        attr_sets;              /* possible sets of projected attributes */
146         QueryTemplate*          templates;              /* cacheable templates */
147
148         CachedQuery*            lru_top;                /* top and bottom of LRU list */
149         CachedQuery*            lru_bottom;
150
151         ldap_pvt_thread_mutex_t         lru_mutex;      /* mutex for accessing LRU list */
152
153         /* Query cache methods */
154         QCfunc                  *qcfunc;                        /* Query containment*/
155         CRfunc                  *crfunc;                        /* cache replacement */
156         AddQueryfunc    *addfunc;                       /* add query */
157 } query_manager;
158
159 /* LDAP query cache manager */
160 typedef struct cache_manager_s {
161         BackendDB       db;     /* underlying database */
162         unsigned long   num_cached_queries;             /* total number of cached queries */
163         unsigned long   max_queries;                    /* upper bound on # of cached queries */
164         int             save_queries;                   /* save cached queries across restarts */
165         int     numattrsets;                    /* number of attribute sets */
166         int     cur_entries;                    /* current number of entries cached */
167         int     max_entries;                    /* max number of entries cached */
168         int     num_entries_limit;              /* max # of entries in a cacheable query */
169
170         char    response_cb;                    /* install the response callback
171                                                  * at the tail of the callback list */
172 #define PCACHE_RESPONSE_CB_HEAD 0
173 #define PCACHE_RESPONSE_CB_TAIL 1
174
175         time_t  cc_period;              /* interval between successive consistency checks (sec) */
176         int     cc_paused;
177         void    *cc_arg;
178
179         ldap_pvt_thread_mutex_t         cache_mutex;
180
181         query_manager*   qm;    /* query cache managed by the cache manager */
182 } cache_manager;
183
184 static int pcache_debug;
185
186 #ifdef PCACHE_CONTROL_PRIVDB
187 static int privDB_cid;
188 #endif /* PCACHE_CONTROL_PRIVDB */
189
190 static AttributeDescription *ad_queryId, *ad_cachedQueryURL;
191 static struct {
192         char    *desc;
193         AttributeDescription **adp;
194 } as[] = {
195         { "( 1.3.6.1.4.1.4203.666.11.9.1.1 "
196                 "NAME 'queryId' "
197                 "DESC 'List of queries the entry belongs to' "
198                 "EQUALITY UUIDMatch "
199                 "SYNTAX 1.3.6.1.1.16.1 "
200                 "NO-USER-MODIFICATION "
201                 "USAGE directoryOperation )",
202                 &ad_queryId },
203         { "( 1.3.6.1.4.1.4203.666.11.9.1.2 "
204                 "NAME 'cachedQueryURL' "
205                 "DESC 'URI describing a cached query' "
206                 "EQUALITY caseExactMatch "
207                 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 "
208                 "NO-USER-MODIFICATION "
209                 "USAGE directoryOperation )",
210                 &ad_cachedQueryURL },
211         { NULL }
212 };
213
214 static int
215 filter2template(
216         Operation               *op,
217         Filter                  *f,
218         struct                  berval *fstr,
219         AttributeName**         filter_attrs,
220         int*                    filter_cnt,
221         int*                    filter_got_oc );
222
223 static CachedQuery *
224 add_query(
225         Operation *op,
226         query_manager* qm,
227         Query* query,
228         QueryTemplate *templ,
229         int positive);
230
231 static int
232 remove_query_data(
233         Operation       *op,
234         SlapReply       *rs,
235         struct berval   *query_uuid );
236
237 /*
238  * Turn a cached query into its URL representation
239  */
240 static int
241 query2url( Operation *op, CachedQuery *q, struct berval *urlbv )
242 {
243         struct berval   bv_scope,
244                         bv_filter;
245         char            attrset_buf[ 32 ],
246                         expiry_buf[ 32 ],
247                         *ptr;
248         ber_len_t       attrset_len,
249                         expiry_len;
250
251         ldap_pvt_scope2bv( q->scope, &bv_scope );
252         filter2bv_x( op, q->filter, &bv_filter );
253         attrset_len = snprintf( attrset_buf, sizeof( attrset_buf ),
254                 "%lu", (unsigned long)q->qtemp->attr_set_index );
255         expiry_len = snprintf( expiry_buf, sizeof( expiry_buf ),
256                 "%lu", (unsigned long)q->expiry_time );
257
258         urlbv->bv_len = STRLENOF( "ldap:///" )
259                 + q->qbase->base.bv_len
260                 + STRLENOF( "??" )
261                 + bv_scope.bv_len
262                 + STRLENOF( "?" )
263                 + bv_filter.bv_len
264                 + STRLENOF( "?x-uuid=" )
265                 + q->q_uuid.bv_len
266                 + STRLENOF( ",x-attrset=" )
267                 + attrset_len
268                 + STRLENOF( ",x-expiry=" )
269                 + expiry_len;
270         ptr = urlbv->bv_val = ber_memalloc_x( urlbv->bv_len + 1, op->o_tmpmemctx );
271         ptr = lutil_strcopy( ptr, "ldap:///" );
272         ptr = lutil_strcopy( ptr, q->qbase->base.bv_val );
273         ptr = lutil_strcopy( ptr, "??" );
274         ptr = lutil_strcopy( ptr, bv_scope.bv_val );
275         ptr = lutil_strcopy( ptr, "?" );
276         ptr = lutil_strcopy( ptr, bv_filter.bv_val );
277         ptr = lutil_strcopy( ptr, "?x-uuid=" );
278         ptr = lutil_strcopy( ptr, q->q_uuid.bv_val );
279         ptr = lutil_strcopy( ptr, ",x-attrset=" );
280         ptr = lutil_strcopy( ptr, attrset_buf );
281         ptr = lutil_strcopy( ptr, ",x-expiry=" );
282         ptr = lutil_strcopy( ptr, expiry_buf );
283
284         ber_memfree_x( bv_filter.bv_val, op->o_tmpmemctx );
285
286         return 0;
287 }
288
289 /*
290  * Turn an URL representing a formerly cached query into a cached query,
291  * and try to cache it
292  */
293 static int
294 url2query(
295         char            *url,
296         Operation       *op,
297         query_manager   *qm )
298 {
299         Query           query = { 0 };
300         QueryTemplate   *qt;
301         CachedQuery     *cq;
302         LDAPURLDesc     *lud = NULL;
303         struct berval   base,
304                         tempstr = BER_BVNULL,
305                         uuid;
306         int             attrset;
307         time_t          expiry_time;
308         int             i,
309                         got_uuid = 0,
310                         got_attrset = 0,
311                         got_expiry = 0,
312                         rc = 0;
313
314         rc = ldap_url_parse( url, &lud );
315         if ( rc != LDAP_URL_SUCCESS ) {
316                 return -1;
317         }
318
319         /* non-allowed fields */
320         if ( lud->lud_host != NULL ) {
321                 rc = 1;
322                 goto error;
323         }
324
325         if ( lud->lud_attrs != NULL ) {
326                 rc = 1;
327                 goto error;
328         }
329
330         /* be pedantic */
331         if ( strcmp( lud->lud_scheme, "ldap" ) != 0 ) {
332                 rc = 1;
333                 goto error;
334         }
335
336         /* required fields */
337         if ( lud->lud_dn == NULL || lud->lud_dn[ 0 ] == '\0' ) {
338                 rc = 1;
339                 goto error;
340         }
341
342         switch ( lud->lud_scope ) {
343         case LDAP_SCOPE_BASE:
344         case LDAP_SCOPE_ONELEVEL:
345         case LDAP_SCOPE_SUBTREE:
346         case LDAP_SCOPE_SUBORDINATE:
347                 break;
348
349         default:
350                 rc = 1;
351                 goto error;
352         }
353
354         if ( lud->lud_filter == NULL || lud->lud_filter[ 0 ] == '\0' ) {
355                 rc = 1;
356                 goto error;
357         }
358
359         if ( lud->lud_exts == NULL ) {
360                 rc = 1;
361                 goto error;
362         }
363
364         for ( i = 0; lud->lud_exts[ i ] != NULL; i++ ) {
365                 if ( strncmp( lud->lud_exts[ i ], "x-uuid=", STRLENOF( "x-uuid=" ) ) == 0 ) {
366                         struct berval   tmpUUID;
367                         Syntax          *syn_UUID = slap_schema.si_ad_entryUUID->ad_type->sat_syntax;
368
369                         ber_str2bv( &lud->lud_exts[ i ][ STRLENOF( "x-uuid=" ) ], 0, 0, &tmpUUID );
370                         rc = syn_UUID->ssyn_pretty( syn_UUID, &tmpUUID, &uuid, NULL );
371                         if ( rc != LDAP_SUCCESS ) {
372                                 goto error;
373                         }
374                         got_uuid = 1;
375
376                 } else if ( strncmp( lud->lud_exts[ i ], "x-attrset=", STRLENOF( "x-attrset=" ) ) == 0 ) {
377                         rc = lutil_atoi( &attrset, &lud->lud_exts[ i ][ STRLENOF( "x-attrset=" ) ] );
378                         if ( rc ) {
379                                 goto error;
380                         }
381                         got_attrset = 1;
382
383                 } else if ( strncmp( lud->lud_exts[ i ], "x-expiry=", STRLENOF( "x-expiry=" ) ) == 0 ) {
384                         unsigned long l;
385
386                         rc = lutil_atoul( &l, &lud->lud_exts[ i ][ STRLENOF( "x-expiry=" ) ] );
387                         if ( rc ) {
388                                 goto error;
389                         }
390                         expiry_time = (time_t)l;
391                         got_expiry = 1;
392
393                 } else {
394                         rc = -1;
395                         goto error;
396                 }
397         }
398
399         if ( !got_uuid ) {
400                 rc = 1;
401                 goto error;
402         }
403
404         if ( !got_attrset ) {
405                 rc = 1;
406                 goto error;
407         }
408
409         if ( !got_expiry ) {
410                 rc = 1;
411                 goto error;
412         }
413
414         /* ignore expired queries */
415         if ( expiry_time <= slap_get_time()) {
416                 Operation       op2 = *op;
417                 SlapReply       rs2 = { 0 };
418
419                 memset( &op2.oq_search, 0, sizeof( op2.oq_search ) );
420
421                 (void)remove_query_data( &op2, &rs2, &uuid );
422
423                 rc = 0;
424
425         } else {
426                 ber_str2bv( lud->lud_dn, 0, 0, &base );
427                 rc = dnNormalize( 0, NULL, NULL, &base, &query.base, NULL );
428                 if ( rc != LDAP_SUCCESS ) {
429                         goto error;
430                 }
431                 query.scope = lud->lud_scope;
432                 query.filter = str2filter( lud->lud_filter );
433
434                 tempstr.bv_val = ch_malloc( strlen( lud->lud_filter ) + 1 );
435                 tempstr.bv_len = 0;
436                 if ( filter2template( op, query.filter, &tempstr, NULL, NULL, NULL ) ) {
437                         ch_free( tempstr.bv_val );
438                         rc = -1;
439                         goto error;
440                 }
441
442                 /* check for query containment */
443                 qt = qm->attr_sets[attrset].templates;
444                 for ( ; qt; qt = qt->qtnext ) {
445                         /* find if template i can potentially answer tempstr */
446                         if ( bvmatch( &qt->querystr, &tempstr ) ) {
447                                 break;
448                         }
449                 }
450
451                 if ( qt == NULL ) {
452                         rc = 1;
453                         goto error;
454                 }
455
456                 cq = add_query( op, qm, &query, qt, 1 );
457                 if ( cq != NULL ) {
458                         cq->expiry_time = expiry_time;
459                         cq->q_uuid = uuid;
460
461                         /* it's now into cq->filter */
462                         BER_BVZERO( &uuid );
463                         query.filter = NULL;
464
465                 } else {
466                         rc = 1;
467                 }
468         }
469
470 error:;
471         if ( query.filter != NULL ) filter_free( query.filter );
472         if ( !BER_BVISNULL( &tempstr ) ) ch_free( tempstr.bv_val );
473         if ( !BER_BVISNULL( &query.base ) ) ch_free( query.base.bv_val );
474         if ( !BER_BVISNULL( &uuid ) ) ch_free( uuid.bv_val );
475         if ( lud != NULL ) ldap_free_urldesc( lud );
476
477         return rc;
478 }
479
480 /* Return 1 for an added entry, else 0 */
481 static int
482 merge_entry(
483         Operation               *op,
484         Entry                   *e,
485         struct berval*          query_uuid )
486 {
487         int             rc;
488         Modifications* modlist = NULL;
489         const char*     text = NULL;
490         Attribute               *attr;
491         char                    textbuf[SLAP_TEXT_BUFLEN];
492         size_t                  textlen = sizeof(textbuf);
493
494         SlapReply sreply = {REP_RESULT};
495
496         slap_callback cb = { NULL, slap_null_cb, NULL, NULL };
497
498         attr = e->e_attrs;
499         e->e_attrs = NULL;
500
501         /* add queryId attribute */
502         attr_merge_one( e, ad_queryId, query_uuid, NULL );
503
504         /* append the attribute list from the fetched entry */
505         e->e_attrs->a_next = attr;
506
507         op->o_tag = LDAP_REQ_ADD;
508         op->o_protocol = LDAP_VERSION3;
509         op->o_callback = &cb;
510         op->o_time = slap_get_time();
511         op->o_do_not_cache = 1;
512
513         op->ora_e = e;
514         op->o_req_dn = e->e_name;
515         op->o_req_ndn = e->e_nname;
516         rc = op->o_bd->be_add( op, &sreply );
517
518         if ( rc != LDAP_SUCCESS ) {
519                 if ( rc == LDAP_ALREADY_EXISTS ) {
520                         slap_entry2mods( e, &modlist, &text, textbuf, textlen );
521                         modlist->sml_op = LDAP_MOD_ADD;
522                         op->o_tag = LDAP_REQ_MODIFY;
523                         op->orm_modlist = modlist;
524                         op->o_bd->be_modify( op, &sreply );
525                         slap_mods_free( modlist, 1 );
526                 } else if ( rc == LDAP_REFERRAL ||
527                                         rc == LDAP_NO_SUCH_OBJECT ) {
528                         syncrepl_add_glue( op, e );
529                         e = NULL;
530                         rc = 1;
531                 }
532                 if ( e ) {
533                         entry_free( e );
534                         rc = 0;
535                 }
536         } else {
537                 if ( op->ora_e == e )
538                         be_entry_release_w( op, e );
539                 rc = 1;
540         }
541
542         return rc;
543 }
544
545 /* Length-ordered sort on normalized DNs */
546 static int pcache_dn_cmp( const void *v1, const void *v2 )
547 {
548         const Qbase *q1 = v1, *q2 = v2;
549
550         int rc = q1->base.bv_len - q2->base.bv_len;
551         if ( rc == 0 )
552                 rc = strncmp( q1->base.bv_val, q2->base.bv_val, q1->base.bv_len );
553         return rc;
554 }
555
556 static int lex_bvcmp( struct berval *bv1, struct berval *bv2 )
557 {
558         int len, dif;
559         dif = bv1->bv_len - bv2->bv_len;
560         len = bv1->bv_len;
561         if ( dif > 0 ) len -= dif;
562         len = memcmp( bv1->bv_val, bv2->bv_val, len );
563         if ( !len )
564                 len = dif;
565         return len;
566 }
567
568 /* compare the first value in each filter */
569 static int pcache_filter_cmp( const void *v1, const void *v2 )
570 {
571         const CachedQuery *q1 = v1, *q2 =v2;
572         int rc, weight1, weight2;
573
574         switch( q1->first->f_choice ) {
575         case LDAP_FILTER_PRESENT:
576                 weight1 = 0;
577                 break;
578         case LDAP_FILTER_EQUALITY:
579         case LDAP_FILTER_GE:
580         case LDAP_FILTER_LE:
581                 weight1 = 1;
582                 break;
583         default:
584                 weight1 = 2;
585         }
586         switch( q2->first->f_choice ) {
587         case LDAP_FILTER_PRESENT:
588                 weight2 = 0;
589                 break;
590         case LDAP_FILTER_EQUALITY:
591         case LDAP_FILTER_GE:
592         case LDAP_FILTER_LE:
593                 weight2 = 1;
594                 break;
595         default:
596                 weight2 = 2;
597         }
598         rc = weight1 - weight2;
599         if ( !rc ) {
600                 switch( weight1 ) {
601                 case 0: return 0;
602                 case 1:
603                         rc = lex_bvcmp( &q1->first->f_av_value, &q2->first->f_av_value );
604                         break;
605                 case 2:
606                         if ( q1->first->f_choice == LDAP_FILTER_SUBSTRINGS ) {
607                                 rc = 0;
608                                 if ( !BER_BVISNULL( &q1->first->f_sub_initial )) {
609                                         if ( !BER_BVISNULL( &q2->first->f_sub_initial )) {
610                                                 rc = lex_bvcmp( &q1->first->f_sub_initial,
611                                                         &q2->first->f_sub_initial );
612                                         } else {
613                                                 rc = 1;
614                                         }
615                                 } else if ( !BER_BVISNULL( &q2->first->f_sub_initial )) {
616                                         rc = -1;
617                                 }
618                                 if ( rc ) break;
619                                 if ( q1->first->f_sub_any ) {
620                                         if ( q2->first->f_sub_any ) {
621                                                 rc = lex_bvcmp( q1->first->f_sub_any,
622                                                         q2->first->f_sub_any );
623                                         } else {
624                                                 rc = 1;
625                                         }
626                                 } else if ( q2->first->f_sub_any ) {
627                                         rc = -1;
628                                 }
629                                 if ( rc ) break;
630                                 if ( !BER_BVISNULL( &q1->first->f_sub_final )) {
631                                         if ( !BER_BVISNULL( &q2->first->f_sub_final )) {
632                                                 rc = lex_bvcmp( &q1->first->f_sub_final,
633                                                         &q2->first->f_sub_final );
634                                         } else {
635                                                 rc = 1;
636                                         }
637                                 } else if ( !BER_BVISNULL( &q2->first->f_sub_final )) {
638                                         rc = -1;
639                                 }
640                         } else {
641                                 rc = lex_bvcmp( &q1->first->f_mr_value,
642                                         &q2->first->f_mr_value );
643                         }
644                         break;
645                 }
646         }
647
648         return rc;
649 }
650
651 /* add query on top of LRU list */
652 static void
653 add_query_on_top (query_manager* qm, CachedQuery* qc)
654 {
655         CachedQuery* top = qm->lru_top;
656
657         qm->lru_top = qc;
658
659         if (top)
660                 top->lru_up = qc;
661         else
662                 qm->lru_bottom = qc;
663
664         qc->lru_down = top;
665         qc->lru_up = NULL;
666         Debug( pcache_debug, "Base of added query = %s\n",
667                         qc->qbase->base.bv_val, 0, 0 );
668 }
669
670 /* remove_query from LRU list */
671
672 static void
673 remove_query (query_manager* qm, CachedQuery* qc)
674 {
675         CachedQuery* up;
676         CachedQuery* down;
677
678         if (!qc)
679                 return;
680
681         up = qc->lru_up;
682         down = qc->lru_down;
683
684         if (!up)
685                 qm->lru_top = down;
686
687         if (!down)
688                 qm->lru_bottom = up;
689
690         if (down)
691                 down->lru_up = up;
692
693         if (up)
694                 up->lru_down = down;
695
696         qc->lru_up = qc->lru_down = NULL;
697 }
698
699 /* find and remove string2 from string1
700  * from start if position = 1,
701  * from end if position = 3,
702  * from anywhere if position = 2
703  * string1 is overwritten if position = 2.
704  */
705
706 static int
707 find_and_remove(struct berval* ber1, struct berval* ber2, int position)
708 {
709         int ret=0;
710
711         if ( !ber2->bv_val )
712                 return 1;
713         if ( !ber1->bv_val )
714                 return 0;
715
716         switch( position ) {
717         case 1:
718                 if ( ber1->bv_len >= ber2->bv_len && !memcmp( ber1->bv_val,
719                         ber2->bv_val, ber2->bv_len )) {
720                         ret = 1;
721                         ber1->bv_val += ber2->bv_len;
722                         ber1->bv_len -= ber2->bv_len;
723                 }
724                 break;
725         case 2: {
726                 char *temp;
727                 ber1->bv_val[ber1->bv_len] = '\0';
728                 temp = strstr( ber1->bv_val, ber2->bv_val );
729                 if ( temp ) {
730                         strcpy( temp, temp+ber2->bv_len );
731                         ber1->bv_len -= ber2->bv_len;
732                         ret = 1;
733                 }
734                 break;
735                 }
736         case 3:
737                 if ( ber1->bv_len >= ber2->bv_len &&
738                         !memcmp( ber1->bv_val+ber1->bv_len-ber2->bv_len, ber2->bv_val,
739                                 ber2->bv_len )) {
740                         ret = 1;
741                         ber1->bv_len -= ber2->bv_len;
742                 }
743                 break;
744         }
745         return ret;
746 }
747
748
749 static struct berval*
750 merge_init_final(Operation *op, struct berval* init, struct berval* any,
751         struct berval* final)
752 {
753         struct berval* merged, *temp;
754         int i, any_count, count;
755
756         for (any_count=0; any && any[any_count].bv_val; any_count++)
757                 ;
758
759         count = any_count;
760
761         if (init->bv_val)
762                 count++;
763         if (final->bv_val)
764                 count++;
765
766         merged = (struct berval*)op->o_tmpalloc( (count+1)*sizeof(struct berval),
767                 op->o_tmpmemctx );
768         temp = merged;
769
770         if (init->bv_val) {
771                 ber_dupbv_x( temp, init, op->o_tmpmemctx );
772                 temp++;
773         }
774
775         for (i=0; i<any_count; i++) {
776                 ber_dupbv_x( temp, any, op->o_tmpmemctx );
777                 temp++; any++;
778         }
779
780         if (final->bv_val){
781                 ber_dupbv_x( temp, final, op->o_tmpmemctx );
782                 temp++;
783         }
784         BER_BVZERO( temp );
785         return merged;
786 }
787
788 /* Each element in stored must be found in incoming. Incoming is overwritten.
789  */
790 static int
791 strings_containment(struct berval* stored, struct berval* incoming)
792 {
793         struct berval* element;
794         int k=0;
795         int j, rc = 0;
796
797         for ( element=stored; element->bv_val != NULL; element++ ) {
798                 for (j = k; incoming[j].bv_val != NULL; j++) {
799                         if (find_and_remove(&(incoming[j]), element, 2)) {
800                                 k = j;
801                                 rc = 1;
802                                 break;
803                         }
804                         rc = 0;
805                 }
806                 if ( rc ) {
807                         continue;
808                 } else {
809                         return 0;
810                 }
811         }
812         return 1;
813 }
814
815 static int
816 substr_containment_substr(Operation *op, Filter* stored, Filter* incoming)
817 {
818         int rc = 0;
819
820         struct berval init_incoming;
821         struct berval final_incoming;
822         struct berval *remaining_incoming = NULL;
823
824         if ((!(incoming->f_sub_initial.bv_val) && (stored->f_sub_initial.bv_val))
825            || (!(incoming->f_sub_final.bv_val) && (stored->f_sub_final.bv_val)))
826                 return 0;
827
828         init_incoming = incoming->f_sub_initial;
829         final_incoming =  incoming->f_sub_final;
830
831         if (find_and_remove(&init_incoming,
832                         &(stored->f_sub_initial), 1) && find_and_remove(&final_incoming,
833                         &(stored->f_sub_final), 3))
834         {
835                 if (stored->f_sub_any == NULL) {
836                         rc = 1;
837                         goto final;
838                 }
839                 remaining_incoming = merge_init_final(op, &init_incoming,
840                                                 incoming->f_sub_any, &final_incoming);
841                 rc = strings_containment(stored->f_sub_any, remaining_incoming);
842                 ber_bvarray_free_x( remaining_incoming, op->o_tmpmemctx );
843         }
844 final:
845         return rc;
846 }
847
848 static int
849 substr_containment_equality(Operation *op, Filter* stored, Filter* incoming)
850 {
851         struct berval incoming_val[2];
852         int rc = 0;
853
854         incoming_val[1] = incoming->f_av_value;
855
856         if (find_and_remove(incoming_val+1,
857                         &(stored->f_sub_initial), 1) && find_and_remove(incoming_val+1,
858                         &(stored->f_sub_final), 3)) {
859                 if (stored->f_sub_any == NULL){
860                         rc = 1;
861                         goto final;
862                 }
863                 ber_dupbv_x( incoming_val, incoming_val+1, op->o_tmpmemctx );
864                 BER_BVZERO( incoming_val+1 );
865                 rc = strings_containment(stored->f_sub_any, incoming_val);
866                 op->o_tmpfree( incoming_val[0].bv_val, op->o_tmpmemctx );
867         }
868 final:
869         return rc;
870 }
871
872 static Filter *
873 filter_first( Filter *f )
874 {
875         while ( f->f_choice == LDAP_FILTER_OR || f->f_choice == LDAP_FILTER_AND )
876                 f = f->f_and;
877         return f;
878 }
879
880
881 static CachedQuery *
882 find_filter( Operation *op, Avlnode *root, Filter *inputf, Filter *first )
883 {
884         Filter* fs;
885         Filter* fi;
886         MatchingRule* mrule = NULL;
887         int res=0, eqpass= 0;
888         int ret, rc, dir;
889         Avlnode *ptr;
890         CachedQuery cq, *qc;
891
892         cq.filter = inputf;
893         cq.first = first;
894
895         /* substring matches sort to the end, and we just have to
896          * walk the entire list.
897          */
898         if ( first->f_choice == LDAP_FILTER_SUBSTRINGS ) {
899                 ptr = tavl_end( root, 1 );
900                 dir = TAVL_DIR_LEFT;
901         } else {
902                 ptr = tavl_find3( root, &cq, pcache_filter_cmp, &ret );
903                 dir = (first->f_choice == LDAP_FILTER_GE) ? TAVL_DIR_LEFT :
904                         TAVL_DIR_RIGHT;
905         }
906
907         while (ptr) {
908                 qc = ptr->avl_data;
909                 fi = inputf;
910                 fs = qc->filter;
911
912                 /* an incoming substr query can only be satisfied by a cached
913                  * substr query.
914                  */
915                 if ( first->f_choice == LDAP_FILTER_SUBSTRINGS &&
916                         qc->first->f_choice != LDAP_FILTER_SUBSTRINGS )
917                         break;
918
919                 /* an incoming eq query can be satisfied by a cached eq or substr
920                  * query
921                  */
922                 if ( first->f_choice == LDAP_FILTER_EQUALITY ) {
923                         if ( eqpass == 0 ) {
924                                 if ( qc->first->f_choice != LDAP_FILTER_EQUALITY ) {
925 nextpass:                       eqpass = 1;
926                                         ptr = tavl_end( root, 1 );
927                                         dir = TAVL_DIR_LEFT;
928                                         continue;
929                                 }
930                         } else {
931                                 if ( qc->first->f_choice != LDAP_FILTER_SUBSTRINGS )
932                                         break;
933                         }
934                 }
935                 do {
936                         res=0;
937                         switch (fs->f_choice) {
938                         case LDAP_FILTER_EQUALITY:
939                                 if (fi->f_choice == LDAP_FILTER_EQUALITY)
940                                         mrule = fs->f_ava->aa_desc->ad_type->sat_equality;
941                                 else
942                                         ret = 1;
943                                 break;
944                         case LDAP_FILTER_GE:
945                         case LDAP_FILTER_LE:
946                                 mrule = fs->f_ava->aa_desc->ad_type->sat_ordering;
947                                 break;
948                         default:
949                                 mrule = NULL; 
950                         }
951                         if (mrule) {
952                                 const char *text;
953                                 rc = value_match(&ret, fs->f_ava->aa_desc, mrule,
954                                         SLAP_MR_VALUE_OF_ASSERTION_SYNTAX,
955                                         &(fi->f_ava->aa_value),
956                                         &(fs->f_ava->aa_value), &text);
957                                 if (rc != LDAP_SUCCESS) {
958                                         return NULL;
959                                 }
960                                 if ( fi==first && fi->f_choice==LDAP_FILTER_EQUALITY && ret )
961                                         goto nextpass;
962                         }
963                         switch (fs->f_choice) {
964                         case LDAP_FILTER_OR:
965                         case LDAP_FILTER_AND:
966                                 fs = fs->f_and;
967                                 fi = fi->f_and;
968                                 res=1;
969                                 break;
970                         case LDAP_FILTER_SUBSTRINGS:
971                                 /* check if the equality query can be
972                                 * answered with cached substring query */
973                                 if ((fi->f_choice == LDAP_FILTER_EQUALITY)
974                                         && substr_containment_equality( op,
975                                         fs, fi))
976                                         res=1;
977                                 /* check if the substring query can be
978                                 * answered with cached substring query */
979                                 if ((fi->f_choice ==LDAP_FILTER_SUBSTRINGS
980                                         ) && substr_containment_substr( op,
981                                         fs, fi))
982                                         res= 1;
983                                 fs=fs->f_next;
984                                 fi=fi->f_next;
985                                 break;
986                         case LDAP_FILTER_PRESENT:
987                                 res=1;
988                                 fs=fs->f_next;
989                                 fi=fi->f_next;
990                                 break;
991                         case LDAP_FILTER_EQUALITY:
992                                 if (ret == 0)
993                                         res = 1;
994                                 fs=fs->f_next;
995                                 fi=fi->f_next;
996                                 break;
997                         case LDAP_FILTER_GE:
998                                 if (mrule && ret >= 0)
999                                         res = 1;
1000                                 fs=fs->f_next;
1001                                 fi=fi->f_next;
1002                                 break;
1003                         case LDAP_FILTER_LE:
1004                                 if (mrule && ret <= 0)
1005                                         res = 1;
1006                                 fs=fs->f_next;
1007                                 fi=fi->f_next;
1008                                 break;
1009                         case LDAP_FILTER_NOT:
1010                                 res=0;
1011                                 break;
1012                         default:
1013                                 break;
1014                         }
1015                 } while((res) && (fi != NULL) && (fs != NULL));
1016
1017                 if ( res )
1018                         return qc;
1019                 ptr = tavl_next( ptr, dir );
1020         }
1021         return NULL;
1022 }
1023
1024 /* check whether query is contained in any of
1025  * the cached queries in template
1026  */
1027 static CachedQuery *
1028 query_containment(Operation *op, query_manager *qm,
1029                   Query *query,
1030                   QueryTemplate *templa)
1031 {
1032         CachedQuery* qc;
1033         int depth = 0, tscope;
1034         Qbase qbase, *qbptr = NULL;
1035         struct berval pdn;
1036
1037         if (query->filter != NULL) {
1038                 Filter *first;
1039
1040                 Debug( pcache_debug, "Lock QC index = %p\n",
1041                                 (void *) templa, 0, 0 );
1042                 qbase.base = query->base;
1043
1044                 first = filter_first( query->filter );
1045
1046                 ldap_pvt_thread_rdwr_rlock(&templa->t_rwlock);
1047                 for( ;; ) {
1048                         /* Find the base */
1049                         qbptr = avl_find( templa->qbase, &qbase, pcache_dn_cmp );
1050                         if ( qbptr ) {
1051                                 tscope = query->scope;
1052                                 /* Find a matching scope:
1053                                  * match at depth 0 OK
1054                                  * scope is BASE,
1055                                  *      one at depth 1 OK
1056                                  *  subord at depth > 0 OK
1057                                  *      subtree at any depth OK
1058                                  * scope is ONE,
1059                                  *  subtree or subord at any depth OK
1060                                  * scope is SUBORD,
1061                                  *  subtree or subord at any depth OK
1062                                  * scope is SUBTREE,
1063                                  *  subord at depth > 0 OK
1064                                  *  subtree at any depth OK
1065                                  */
1066                                 for ( tscope = 0 ; tscope <= LDAP_SCOPE_CHILDREN; tscope++ ) {
1067                                         switch ( query->scope ) {
1068                                         case LDAP_SCOPE_BASE:
1069                                                 if ( tscope == LDAP_SCOPE_BASE && depth ) continue;
1070                                                 if ( tscope == LDAP_SCOPE_ONE && depth != 1) continue;
1071                                                 if ( tscope == LDAP_SCOPE_CHILDREN && !depth ) continue;
1072                                                 break;
1073                                         case LDAP_SCOPE_ONE:
1074                                                 if ( tscope == LDAP_SCOPE_BASE )
1075                                                         tscope = LDAP_SCOPE_ONE;
1076                                                 if ( tscope == LDAP_SCOPE_ONE && depth ) continue;
1077                                                 if ( !depth ) break;
1078                                                 if ( tscope < LDAP_SCOPE_SUBTREE )
1079                                                         tscope = LDAP_SCOPE_SUBTREE;
1080                                                 break;
1081                                         case LDAP_SCOPE_SUBTREE:
1082                                                 if ( tscope < LDAP_SCOPE_SUBTREE )
1083                                                         tscope = LDAP_SCOPE_SUBTREE;
1084                                                 if ( tscope == LDAP_SCOPE_CHILDREN && !depth ) continue;
1085                                                 break;
1086                                         case LDAP_SCOPE_CHILDREN:
1087                                                 if ( tscope < LDAP_SCOPE_SUBTREE )
1088                                                         tscope = LDAP_SCOPE_SUBTREE;
1089                                                 break;
1090                                         }
1091                                         if ( !qbptr->scopes[tscope] ) continue;
1092
1093                                         /* Find filter */
1094                                         qc = find_filter( op, qbptr->scopes[tscope],
1095                                                         query->filter, first );
1096                                         if ( qc ) {
1097                                                 ldap_pvt_thread_mutex_lock(&qm->lru_mutex);
1098                                                 if (qm->lru_top != qc) {
1099                                                         remove_query(qm, qc);
1100                                                         add_query_on_top(qm, qc);
1101                                                 }
1102                                                 ldap_pvt_thread_mutex_unlock(&qm->lru_mutex);
1103                                                 return qc;
1104                                         }
1105                                 }
1106                         }
1107                         if ( be_issuffix( op->o_bd, &qbase.base ))
1108                                 break;
1109                         /* Up a level */
1110                         dnParent( &qbase.base, &pdn );
1111                         qbase.base = pdn;
1112                         depth++;
1113                 }
1114
1115                 Debug( pcache_debug,
1116                         "Not answerable: Unlock QC index=%p\n",
1117                         (void *) templa, 0, 0 );
1118                 ldap_pvt_thread_rdwr_runlock(&templa->t_rwlock);
1119         }
1120         return NULL;
1121 }
1122
1123 static void
1124 free_query (CachedQuery* qc)
1125 {
1126         free(qc->q_uuid.bv_val);
1127         filter_free(qc->filter);
1128         free(qc);
1129 }
1130
1131
1132 /* Add query to query cache */
1133 static CachedQuery *
1134 add_query(
1135         Operation *op,
1136         query_manager* qm,
1137         Query* query,
1138         QueryTemplate *templ,
1139         int positive)
1140 {
1141         CachedQuery* new_cached_query = (CachedQuery*) ch_malloc(sizeof(CachedQuery));
1142         Qbase *qbase, qb;
1143         Filter *first;
1144         int rc;
1145
1146         new_cached_query->qtemp = templ;
1147         BER_BVZERO( &new_cached_query->q_uuid );
1148         if ( positive ) {
1149                 new_cached_query->expiry_time = slap_get_time() + templ->ttl;
1150         } else {
1151                 new_cached_query->expiry_time = slap_get_time() + templ->negttl;
1152         }
1153         new_cached_query->lru_up = NULL;
1154         new_cached_query->lru_down = NULL;
1155         Debug( pcache_debug, "Added query expires at %ld\n",
1156                         (long) new_cached_query->expiry_time, 0, 0 );
1157
1158         new_cached_query->scope = query->scope;
1159         new_cached_query->filter = query->filter;
1160         new_cached_query->first = first = filter_first( query->filter );
1161
1162         qb.base = query->base;
1163
1164         /* Adding a query    */
1165         Debug( pcache_debug, "Lock AQ index = %p\n",
1166                         (void *) templ, 0, 0 );
1167         ldap_pvt_thread_rdwr_wlock(&templ->t_rwlock);
1168         qbase = avl_find( templ->qbase, &qb, pcache_dn_cmp );
1169         if ( !qbase ) {
1170                 qbase = ch_calloc( 1, sizeof(Qbase) + qb.base.bv_len + 1 );
1171                 qbase->base.bv_len = qb.base.bv_len;
1172                 qbase->base.bv_val = (char *)(qbase+1);
1173                 memcpy( qbase->base.bv_val, qb.base.bv_val, qb.base.bv_len );
1174                 qbase->base.bv_val[qbase->base.bv_len] = '\0';
1175                 avl_insert( &templ->qbase, qbase, pcache_dn_cmp, avl_dup_error );
1176         }
1177         new_cached_query->next = templ->query;
1178         new_cached_query->prev = NULL;
1179         new_cached_query->qbase = qbase;
1180         rc = tavl_insert( &qbase->scopes[query->scope], new_cached_query,
1181                 pcache_filter_cmp, avl_dup_error );
1182         if ( rc == 0 ) {
1183                 qbase->queries++;
1184                 if (templ->query == NULL)
1185                         templ->query_last = new_cached_query;
1186                 else
1187                         templ->query->prev = new_cached_query;
1188                 templ->query = new_cached_query;
1189                 templ->no_of_queries++;
1190         } else {
1191                 ch_free( new_cached_query );
1192                 new_cached_query = find_filter( op, qbase->scopes[query->scope],
1193                                                         query->filter, first );
1194                 filter_free( query->filter );
1195         }
1196         Debug( pcache_debug, "TEMPLATE %p QUERIES++ %d\n",
1197                         (void *) templ, templ->no_of_queries, 0 );
1198
1199         Debug( pcache_debug, "Unlock AQ index = %p \n",
1200                         (void *) templ, 0, 0 );
1201         ldap_pvt_thread_rdwr_wunlock(&templ->t_rwlock);
1202
1203         /* Adding on top of LRU list  */
1204         if ( rc == 0 ) {
1205                 ldap_pvt_thread_mutex_lock(&qm->lru_mutex);
1206                 add_query_on_top(qm, new_cached_query);
1207                 ldap_pvt_thread_mutex_unlock(&qm->lru_mutex);
1208         }
1209         return rc == 0 ? new_cached_query : NULL;
1210 }
1211
1212 static void
1213 remove_from_template (CachedQuery* qc, QueryTemplate* template)
1214 {
1215         if (!qc->prev && !qc->next) {
1216                 template->query_last = template->query = NULL;
1217         } else if (qc->prev == NULL) {
1218                 qc->next->prev = NULL;
1219                 template->query = qc->next;
1220         } else if (qc->next == NULL) {
1221                 qc->prev->next = NULL;
1222                 template->query_last = qc->prev;
1223         } else {
1224                 qc->next->prev = qc->prev;
1225                 qc->prev->next = qc->next;
1226         }
1227         tavl_delete( &qc->qbase->scopes[qc->scope], qc, pcache_filter_cmp );
1228         qc->qbase->queries--;
1229         if ( qc->qbase->queries == 0 ) {
1230                 avl_delete( &template->qbase, qc->qbase, pcache_dn_cmp );
1231                 ch_free( qc->qbase );
1232                 qc->qbase = NULL;
1233         }
1234
1235         template->no_of_queries--;
1236 }
1237
1238 /* remove bottom query of LRU list from the query cache */
1239 /*
1240  * NOTE: slight change in functionality.
1241  *
1242  * - if result->bv_val is NULL, the query at the bottom of the LRU
1243  *   is removed
1244  * - otherwise, the query whose UUID is *result is removed
1245  *      - if not found, result->bv_val is zeroed
1246  */
1247 static void
1248 cache_replacement(query_manager* qm, struct berval *result)
1249 {
1250         CachedQuery* bottom;
1251         QueryTemplate *temp;
1252
1253         ldap_pvt_thread_mutex_lock(&qm->lru_mutex);
1254         if ( BER_BVISNULL( result ) ) {
1255                 bottom = qm->lru_bottom;
1256
1257                 if (!bottom) {
1258                         Debug ( pcache_debug,
1259                                 "Cache replacement invoked without "
1260                                 "any query in LRU list\n", 0, 0, 0 );
1261                         ldap_pvt_thread_mutex_unlock(&qm->lru_mutex);
1262                         return;
1263                 }
1264
1265         } else {
1266                 for ( bottom = qm->lru_bottom;
1267                         bottom != NULL;
1268                         bottom = bottom->lru_up )
1269                 {
1270                         if ( bvmatch( result, &bottom->q_uuid ) ) {
1271                                 break;
1272                         }
1273                 }
1274
1275                 if ( !bottom ) {
1276                         Debug ( pcache_debug,
1277                                 "Could not find query with uuid=\"%s\""
1278                                 "in LRU list\n", result->bv_val, 0, 0 );
1279                         ldap_pvt_thread_mutex_unlock(&qm->lru_mutex);
1280                         BER_BVZERO( result );
1281                         return;
1282                 }
1283         }
1284
1285         temp = bottom->qtemp;
1286         remove_query(qm, bottom);
1287         ldap_pvt_thread_mutex_unlock(&qm->lru_mutex);
1288
1289         *result = bottom->q_uuid;
1290         bottom->q_uuid.bv_val = NULL;
1291
1292         Debug( pcache_debug, "Lock CR index = %p\n", (void *) temp, 0, 0 );
1293         ldap_pvt_thread_rdwr_wlock(&temp->t_rwlock);
1294         remove_from_template(bottom, temp);
1295         Debug( pcache_debug, "TEMPLATE %p QUERIES-- %d\n",
1296                 (void *) temp, temp->no_of_queries, 0 );
1297         Debug( pcache_debug, "Unlock CR index = %p\n", (void *) temp, 0, 0 );
1298         ldap_pvt_thread_rdwr_wunlock(&temp->t_rwlock);
1299         free_query(bottom);
1300 }
1301
1302 struct query_info {
1303         struct query_info *next;
1304         struct berval xdn;
1305         int del;
1306 };
1307
1308 static int
1309 remove_func (
1310         Operation       *op,
1311         SlapReply       *rs
1312 )
1313 {
1314         Attribute *attr;
1315         struct query_info *qi;
1316         int count = 0;
1317
1318         if ( rs->sr_type != REP_SEARCH ) return 0;
1319
1320         attr = attr_find( rs->sr_entry->e_attrs,  ad_queryId );
1321         if ( attr == NULL ) return 0;
1322
1323         for ( count = 0; !BER_BVISNULL( &attr->a_vals[count] ); count++ )
1324                 ;
1325         assert( count > 0 );
1326         qi = op->o_tmpalloc( sizeof( struct query_info ), op->o_tmpmemctx );
1327         qi->next = op->o_callback->sc_private;
1328         op->o_callback->sc_private = qi;
1329         ber_dupbv_x( &qi->xdn, &rs->sr_entry->e_nname, op->o_tmpmemctx );
1330         qi->del = ( count == 1 );
1331
1332         return 0;
1333 }
1334
1335 static int
1336 remove_query_data(
1337         Operation       *op,
1338         SlapReply       *rs,
1339         struct berval   *query_uuid )
1340 {
1341         struct query_info       *qi, *qnext;
1342         char                    filter_str[ LDAP_LUTIL_UUIDSTR_BUFSIZE + STRLENOF( "(queryId=)" ) ];
1343 #ifdef LDAP_COMP_MATCH
1344         AttributeAssertion      ava = { NULL, BER_BVNULL, NULL };
1345 #else
1346         AttributeAssertion      ava = { NULL, BER_BVNULL };
1347 #endif
1348         Filter                  filter = {LDAP_FILTER_EQUALITY};
1349         SlapReply               sreply = {REP_RESULT};
1350         slap_callback cb = { NULL, remove_func, NULL, NULL };
1351         int deleted = 0;
1352
1353         sreply.sr_entry = NULL;
1354         sreply.sr_nentries = 0;
1355         op->ors_filterstr.bv_len = snprintf(filter_str, sizeof(filter_str),
1356                 "(%s=%s)", ad_queryId->ad_cname.bv_val, query_uuid->bv_val);
1357         filter.f_ava = &ava;
1358         filter.f_av_desc = ad_queryId;
1359         filter.f_av_value = *query_uuid;
1360
1361         op->o_tag = LDAP_REQ_SEARCH;
1362         op->o_protocol = LDAP_VERSION3;
1363         op->o_callback = &cb;
1364         op->o_time = slap_get_time();
1365         op->o_do_not_cache = 1;
1366
1367         op->o_req_dn = op->o_bd->be_suffix[0];
1368         op->o_req_ndn = op->o_bd->be_nsuffix[0];
1369         op->ors_scope = LDAP_SCOPE_SUBTREE;
1370         op->ors_deref = LDAP_DEREF_NEVER;
1371         op->ors_slimit = SLAP_NO_LIMIT;
1372         op->ors_tlimit = SLAP_NO_LIMIT;
1373         op->ors_filter = &filter;
1374         op->ors_filterstr.bv_val = filter_str;
1375         op->ors_filterstr.bv_len = strlen(filter_str);
1376         op->ors_attrs = NULL;
1377         op->ors_attrsonly = 0;
1378
1379         op->o_bd->be_search( op, &sreply );
1380
1381         for ( qi=cb.sc_private; qi; qi=qnext ) {
1382                 qnext = qi->next;
1383
1384                 op->o_req_dn = qi->xdn;
1385                 op->o_req_ndn = qi->xdn;
1386
1387                 if ( qi->del ) {
1388                         Debug( pcache_debug, "DELETING ENTRY TEMPLATE=%s\n",
1389                                 query_uuid->bv_val, 0, 0 );
1390
1391                         op->o_tag = LDAP_REQ_DELETE;
1392
1393                         if (op->o_bd->be_delete(op, &sreply) == LDAP_SUCCESS) {
1394                                 deleted++;
1395                         }
1396
1397                 } else {
1398                         Modifications mod;
1399                         struct berval vals[2];
1400
1401                         vals[0] = *query_uuid;
1402                         vals[1].bv_val = NULL;
1403                         vals[1].bv_len = 0;
1404                         mod.sml_op = LDAP_MOD_DELETE;
1405                         mod.sml_flags = 0;
1406                         mod.sml_desc = ad_queryId;
1407                         mod.sml_type = ad_queryId->ad_cname;
1408                         mod.sml_values = vals;
1409                         mod.sml_nvalues = NULL;
1410                         mod.sml_next = NULL;
1411                         Debug( pcache_debug,
1412                                 "REMOVING TEMP ATTR : TEMPLATE=%s\n",
1413                                 query_uuid->bv_val, 0, 0 );
1414
1415                         op->orm_modlist = &mod;
1416
1417                         op->o_bd->be_modify( op, &sreply );
1418                 }
1419                 op->o_tmpfree( qi->xdn.bv_val, op->o_tmpmemctx );
1420                 op->o_tmpfree( qi, op->o_tmpmemctx );
1421         }
1422         return deleted;
1423 }
1424
1425 static int
1426 get_attr_set(
1427         AttributeName* attrs,
1428         query_manager* qm,
1429         int num
1430 );
1431
1432 static int
1433 filter2template(
1434         Operation               *op,
1435         Filter                  *f,
1436         struct                  berval *fstr,
1437         AttributeName**         filter_attrs,
1438         int*                    filter_cnt,
1439         int*                    filter_got_oc )
1440 {
1441         AttributeDescription *ad;
1442
1443         switch ( f->f_choice ) {
1444         case LDAP_FILTER_EQUALITY:
1445                 ad = f->f_av_desc;
1446                 sprintf( fstr->bv_val+fstr->bv_len, "(%s=)", ad->ad_cname.bv_val );
1447                 fstr->bv_len += ad->ad_cname.bv_len + ( sizeof("(=)") - 1 );
1448                 break;
1449
1450         case LDAP_FILTER_GE:
1451                 ad = f->f_av_desc;
1452                 sprintf( fstr->bv_val+fstr->bv_len, "(%s>=)", ad->ad_cname.bv_val);
1453                 fstr->bv_len += ad->ad_cname.bv_len + ( sizeof("(>=)") - 1 );
1454                 break;
1455
1456         case LDAP_FILTER_LE:
1457                 ad = f->f_av_desc;
1458                 sprintf( fstr->bv_val+fstr->bv_len, "(%s<=)", ad->ad_cname.bv_val);
1459                 fstr->bv_len += ad->ad_cname.bv_len + ( sizeof("(<=)") - 1 );
1460                 break;
1461
1462         case LDAP_FILTER_APPROX:
1463                 ad = f->f_av_desc;
1464                 sprintf( fstr->bv_val+fstr->bv_len, "(%s~=)", ad->ad_cname.bv_val);
1465                 fstr->bv_len += ad->ad_cname.bv_len + ( sizeof("(~=)") - 1 );
1466                 break;
1467
1468         case LDAP_FILTER_SUBSTRINGS:
1469                 ad = f->f_sub_desc;
1470                 sprintf( fstr->bv_val+fstr->bv_len, "(%s=)", ad->ad_cname.bv_val );
1471                 fstr->bv_len += ad->ad_cname.bv_len + ( sizeof("(=)") - 1 );
1472                 break;
1473
1474         case LDAP_FILTER_PRESENT:
1475                 ad = f->f_desc;
1476                 sprintf( fstr->bv_val+fstr->bv_len, "(%s=*)", ad->ad_cname.bv_val );
1477                 fstr->bv_len += ad->ad_cname.bv_len + ( sizeof("(=*)") - 1 );
1478                 break;
1479
1480         case LDAP_FILTER_AND:
1481         case LDAP_FILTER_OR:
1482         case LDAP_FILTER_NOT: {
1483                 int rc = 0;
1484                 sprintf( fstr->bv_val+fstr->bv_len, "(%c",
1485                         f->f_choice == LDAP_FILTER_AND ? '&' :
1486                         f->f_choice == LDAP_FILTER_OR ? '|' : '!' );
1487                 fstr->bv_len += sizeof("(%") - 1;
1488
1489                 for ( f = f->f_list; f != NULL; f = f->f_next ) {
1490                         rc = filter2template( op, f, fstr, filter_attrs, filter_cnt,
1491                                 filter_got_oc );
1492                         if ( rc ) break;
1493                 }
1494                 sprintf( fstr->bv_val+fstr->bv_len, ")" );
1495                 fstr->bv_len += sizeof(")") - 1;
1496
1497                 return rc;
1498                 }
1499
1500         default:
1501                 strcpy( fstr->bv_val, "(?=?)" );
1502                 fstr->bv_len += sizeof("(?=?)")-1;
1503                 return -1;
1504         }
1505
1506         if ( filter_attrs != NULL ) {
1507                 *filter_attrs = (AttributeName *)op->o_tmprealloc(*filter_attrs,
1508                                 (*filter_cnt + 2)*sizeof(AttributeName), op->o_tmpmemctx);
1509
1510                 (*filter_attrs)[*filter_cnt].an_desc = ad;
1511                 (*filter_attrs)[*filter_cnt].an_name = ad->ad_cname;
1512                 (*filter_attrs)[*filter_cnt].an_oc = NULL;
1513                 (*filter_attrs)[*filter_cnt].an_oc_exclude = 0;
1514                 BER_BVZERO( &(*filter_attrs)[*filter_cnt+1].an_name );
1515                 (*filter_cnt)++;
1516                 if ( ad == slap_schema.si_ad_objectClass )
1517                         *filter_got_oc = 1;
1518         }
1519
1520         return 0;
1521 }
1522
1523 struct search_info {
1524         slap_overinst *on;
1525         Query query;
1526         QueryTemplate *qtemp;
1527         AttributeName*  save_attrs;     /* original attributes, saved for response */
1528         int max;
1529         int over;
1530         int count;
1531         Entry *head, *tail;
1532 };
1533
1534 static void
1535 remove_query_and_data(
1536         Operation       *op,
1537         SlapReply       *rs,
1538         cache_manager   *cm,
1539         struct berval   *uuid )
1540 {
1541         query_manager*          qm = cm->qm;
1542
1543         qm->crfunc( qm, uuid );
1544         if ( !BER_BVISNULL( uuid ) ) {
1545                 int     return_val;
1546
1547                 Debug( pcache_debug,
1548                         "Removing query UUID %s\n",
1549                         uuid->bv_val, 0, 0 );
1550                 return_val = remove_query_data( op, rs, uuid );
1551                 Debug( pcache_debug,
1552                         "QUERY REMOVED, SIZE=%d\n",
1553                         return_val, 0, 0);
1554                 ldap_pvt_thread_mutex_lock( &cm->cache_mutex );
1555                 cm->cur_entries -= return_val;
1556                 cm->num_cached_queries--;
1557                 Debug( pcache_debug,
1558                         "STORED QUERIES = %lu\n",
1559                         cm->num_cached_queries, 0, 0 );
1560                 ldap_pvt_thread_mutex_unlock( &cm->cache_mutex );
1561                 Debug( pcache_debug,
1562                         "QUERY REMOVED, CACHE ="
1563                         "%d entries\n",
1564                         cm->cur_entries, 0, 0 );
1565         }
1566 }
1567
1568 /*
1569  * Callback used to fetch queryId values based on entryUUID;
1570  * used by pcache_remove_entries_from_cache()
1571  */
1572 static int
1573 fetch_queryId_cb( Operation *op, SlapReply *rs )
1574 {
1575         int             rc = 0;
1576
1577         /* only care about searchEntry responses */
1578         if ( rs->sr_type != REP_SEARCH ) {
1579                 return 0;
1580         }
1581
1582         /* allow only one response per entryUUID */
1583         if ( op->o_callback->sc_private != NULL ) {
1584                 rc = 1;
1585
1586         } else {
1587                 Attribute       *a;
1588
1589                 /* copy all queryId values into callback's private data */
1590                 a = attr_find( rs->sr_entry->e_attrs, ad_queryId );
1591                 if ( a != NULL ) {
1592                         BerVarray       vals = NULL;
1593
1594                         ber_bvarray_dup_x( &vals, a->a_nvals, op->o_tmpmemctx );
1595                         op->o_callback->sc_private = (void *)vals;
1596                 }
1597         }
1598
1599         /* clear entry if required */
1600         if ( rs->sr_flags & REP_ENTRY_MUSTBEFREED ) {
1601                 entry_free( rs->sr_entry );
1602                 rs->sr_entry = NULL;
1603                 rs->sr_flags ^= REP_ENTRY_MUSTBEFREED;
1604         }
1605
1606         return rc;
1607 }
1608
1609 /*
1610  * Call that allows to remove a set of entries from the cache,
1611  * by forcing the removal of all the related queries.
1612  */
1613 int
1614 pcache_remove_entries_from_cache(
1615         Operation       *op,
1616         cache_manager   *cm,
1617         BerVarray       UUIDs )
1618 {
1619         Connection      conn = { 0 };
1620         OperationBuffer opbuf;
1621         Operation       op2;
1622         slap_callback   sc = { 0 };
1623         SlapReply       rs = { REP_RESULT };
1624         Filter          f = { 0 };
1625         char            filtbuf[ LDAP_LUTIL_UUIDSTR_BUFSIZE + STRLENOF( "(entryUUID=)" ) ];
1626 #ifdef LDAP_COMP_MATCH
1627         AttributeAssertion ava = { NULL, BER_BVNULL, NULL };
1628 #else
1629         AttributeAssertion ava = { NULL, BER_BVNULL };
1630 #endif
1631         AttributeName   attrs[ 2 ] = { 0 };
1632         int             s, rc;
1633
1634         if ( op == NULL ) {
1635                 void    *thrctx = ldap_pvt_thread_pool_context();
1636
1637                 connection_fake_init( &conn, &opbuf, thrctx );
1638                 op = &opbuf.ob_op;
1639
1640         } else {
1641                 op2 = *op;
1642                 op = &op2;
1643         }
1644
1645         memset( &op->oq_search, 0, sizeof( op->oq_search ) );
1646         op->ors_scope = LDAP_SCOPE_SUBTREE;
1647         op->ors_deref = LDAP_DEREF_NEVER;
1648         f.f_choice = LDAP_FILTER_EQUALITY;
1649         f.f_ava = &ava;
1650         ava.aa_desc = slap_schema.si_ad_entryUUID;
1651         op->ors_filter = &f;
1652         op->ors_slimit = 1;
1653         op->ors_tlimit = SLAP_NO_LIMIT;
1654         attrs[ 0 ].an_desc = ad_queryId;
1655         attrs[ 0 ].an_name = ad_queryId->ad_cname;
1656         op->ors_attrs = attrs;
1657         op->ors_attrsonly = 0;
1658
1659         op->o_req_dn = cm->db.be_suffix[ 0 ];
1660         op->o_req_ndn = cm->db.be_nsuffix[ 0 ];
1661
1662         op->o_tag = LDAP_REQ_SEARCH;
1663         op->o_protocol = LDAP_VERSION3;
1664         op->o_managedsait = SLAP_CONTROL_CRITICAL;
1665         op->o_bd = &cm->db;
1666         op->o_dn = op->o_bd->be_rootdn;
1667         op->o_ndn = op->o_bd->be_rootndn;
1668         sc.sc_response = fetch_queryId_cb;
1669         op->o_callback = &sc;
1670
1671         for ( s = 0; !BER_BVISNULL( &UUIDs[ s ] ); s++ ) {
1672                 BerVarray       vals = NULL;
1673
1674                 op->ors_filterstr.bv_len = snprintf( filtbuf, sizeof( filtbuf ),
1675                         "(entryUUID=%s)", UUIDs[ s ].bv_val );
1676                 op->ors_filterstr.bv_val = filtbuf;
1677                 ava.aa_value = UUIDs[ s ];
1678
1679                 rc = op->o_bd->be_search( op, &rs );
1680                 if ( rc != LDAP_SUCCESS ) {
1681                         continue;
1682                 }
1683
1684                 vals = (BerVarray)op->o_callback->sc_private;
1685                 if ( vals != NULL ) {
1686                         int             i;
1687
1688                         for ( i = 0; !BER_BVISNULL( &vals[ i ] ); i++ ) {
1689                                 struct berval   val = vals[ i ];
1690
1691                                 remove_query_and_data( op, &rs, cm, &val );
1692
1693                                 if ( !BER_BVISNULL( &val ) && val.bv_val != vals[ i ].bv_val ) {
1694                                         ch_free( val.bv_val );
1695                                 }
1696                         }
1697
1698                         ber_bvarray_free_x( vals, op->o_tmpmemctx );
1699                         op->o_callback->sc_private = NULL;
1700                 }
1701         }
1702
1703         return 0;
1704 }
1705
1706 /*
1707  * Call that allows to remove a set of queries from the cache
1708  */
1709 int
1710 pcache_remove_entry_queries_from_cache(
1711         Operation       *op,
1712         cache_manager   *cm,
1713         struct berval   *ndn,
1714         struct berval   *uuid )
1715 {
1716         Connection              conn = { 0 };
1717         OperationBuffer         opbuf;
1718         Operation               op2;
1719         slap_callback           sc = { 0 };
1720         SlapReply               rs = { REP_RESULT };
1721         Filter                  f = { 0 };
1722         char                    filter_str[ LDAP_LUTIL_UUIDSTR_BUFSIZE + STRLENOF( "(queryId=)" ) ];
1723 #ifdef LDAP_COMP_MATCH
1724         AttributeAssertion      ava = { NULL, BER_BVNULL, NULL };
1725 #else
1726         AttributeAssertion      ava = { NULL, BER_BVNULL };
1727 #endif
1728         AttributeName           attrs[ 2 ] = { 0 };
1729         int                     rc;
1730
1731         BerVarray               vals = NULL;
1732
1733         if ( op == NULL ) {
1734                 void    *thrctx = ldap_pvt_thread_pool_context();
1735
1736                 connection_fake_init( &conn, &opbuf, thrctx );
1737                 op = &opbuf.ob_op;
1738
1739         } else {
1740                 op2 = *op;
1741                 op = &op2;
1742         }
1743
1744         memset( &op->oq_search, 0, sizeof( op->oq_search ) );
1745         op->ors_scope = LDAP_SCOPE_BASE;
1746         op->ors_deref = LDAP_DEREF_NEVER;
1747         if ( uuid == NULL || BER_BVISNULL( uuid ) ) {
1748                 BER_BVSTR( &op->ors_filterstr, "(objectClass=*)" );
1749                 f.f_choice = LDAP_FILTER_PRESENT;
1750                 f.f_desc = slap_schema.si_ad_objectClass;
1751
1752         } else {
1753                 op->ors_filterstr.bv_len = snprintf( filter_str,
1754                         sizeof( filter_str ), "(%s=%s)",
1755                         ad_queryId->ad_cname.bv_val, uuid->bv_val );
1756                 f.f_choice = LDAP_FILTER_EQUALITY;
1757                 f.f_ava = &ava;
1758                 f.f_av_desc = ad_queryId;
1759                 f.f_av_value = *uuid;
1760         }
1761         op->ors_filter = &f;
1762         op->ors_slimit = 1;
1763         op->ors_tlimit = SLAP_NO_LIMIT;
1764         attrs[ 0 ].an_desc = ad_queryId;
1765         attrs[ 0 ].an_name = ad_queryId->ad_cname;
1766         op->ors_attrs = attrs;
1767         op->ors_attrsonly = 0;
1768
1769         op->o_req_dn = *ndn;
1770         op->o_req_ndn = *ndn;
1771
1772         op->o_tag = LDAP_REQ_SEARCH;
1773         op->o_protocol = LDAP_VERSION3;
1774         op->o_managedsait = SLAP_CONTROL_CRITICAL;
1775         op->o_bd = &cm->db;
1776         op->o_dn = op->o_bd->be_rootdn;
1777         op->o_ndn = op->o_bd->be_rootndn;
1778         sc.sc_response = fetch_queryId_cb;
1779         op->o_callback = &sc;
1780
1781         rc = op->o_bd->be_search( op, &rs );
1782         if ( rc != LDAP_SUCCESS ) {
1783                 return rc;
1784         }
1785
1786         vals = (BerVarray)op->o_callback->sc_private;
1787         if ( vals != NULL ) {
1788                 int             i;
1789
1790                 for ( i = 0; !BER_BVISNULL( &vals[ i ] ); i++ ) {
1791                         struct berval   val = vals[ i ];
1792
1793                         remove_query_and_data( op, &rs, cm, &val );
1794
1795                         if ( !BER_BVISNULL( &val ) && val.bv_val != vals[ i ].bv_val ) {
1796                                 ch_free( val.bv_val );
1797                         }
1798                 }
1799
1800                 ber_bvarray_free_x( vals, op->o_tmpmemctx );
1801         }
1802
1803         return LDAP_SUCCESS;
1804 }
1805
1806 static int
1807 cache_entries(
1808         Operation       *op,
1809         SlapReply       *rs,
1810         struct berval *query_uuid )
1811 {
1812         struct search_info *si = op->o_callback->sc_private;
1813         slap_overinst *on = si->on;
1814         cache_manager *cm = on->on_bi.bi_private;
1815         int             return_val = 0;
1816         Entry           *e;
1817         struct berval   crp_uuid;
1818         char            uuidbuf[ LDAP_LUTIL_UUIDSTR_BUFSIZE ];
1819         Operation op_tmp = *op;
1820
1821         query_uuid->bv_len = lutil_uuidstr(uuidbuf, sizeof(uuidbuf));
1822         ber_str2bv(uuidbuf, query_uuid->bv_len, 1, query_uuid);
1823
1824         op_tmp.o_bd = &cm->db;
1825         op_tmp.o_dn = cm->db.be_rootdn;
1826         op_tmp.o_ndn = cm->db.be_rootndn;
1827
1828         Debug( pcache_debug, "UUID for query being added = %s\n",
1829                         uuidbuf, 0, 0 );
1830
1831         for ( e=si->head; e; e=si->head ) {
1832                 si->head = e->e_private;
1833                 e->e_private = NULL;
1834                 while ( cm->cur_entries > (cm->max_entries) ) {
1835                         BER_BVZERO( &crp_uuid );
1836                         remove_query_and_data( &op_tmp, rs, cm, &crp_uuid );
1837                 }
1838
1839                 return_val = merge_entry(&op_tmp, e, query_uuid);
1840                 ldap_pvt_thread_mutex_lock(&cm->cache_mutex);
1841                 cm->cur_entries += return_val;
1842                 Debug( pcache_debug,
1843                         "ENTRY ADDED/MERGED, CACHED ENTRIES=%d\n",
1844                         cm->cur_entries, 0, 0 );
1845                 return_val = 0;
1846                 ldap_pvt_thread_mutex_unlock(&cm->cache_mutex);
1847         }
1848
1849         return return_val;
1850 }
1851
1852 static int
1853 pcache_op_cleanup( Operation *op, SlapReply *rs ) {
1854         slap_callback   *cb = op->o_callback;
1855         struct search_info *si = cb->sc_private;
1856         if ( si->save_attrs != NULL ) {
1857                 rs->sr_attrs = si->save_attrs;
1858                 op->ors_attrs = si->save_attrs;
1859         }
1860         op->o_callback = op->o_callback->sc_next;
1861         op->o_tmpfree( cb, op->o_tmpmemctx );
1862         return SLAP_CB_CONTINUE;
1863 }
1864
1865 static int
1866 pcache_response(
1867         Operation       *op,
1868         SlapReply       *rs )
1869 {
1870         struct search_info *si = op->o_callback->sc_private;
1871         slap_overinst *on = si->on;
1872         cache_manager *cm = on->on_bi.bi_private;
1873         query_manager*          qm = cm->qm;
1874
1875         if ( si->save_attrs != NULL ) {
1876                 rs->sr_attrs = si->save_attrs;
1877                 op->ors_attrs = si->save_attrs;
1878         }
1879
1880         if ( rs->sr_type == REP_SEARCH ) {
1881                 Entry *e;
1882                 /* If we haven't exceeded the limit for this query,
1883                  * build a chain of answers to store. If we hit the
1884                  * limit, empty the chain and ignore the rest.
1885                  */
1886                 if ( !si->over ) {
1887                         if ( si->count < si->max ) {
1888                                 si->count++;
1889                                 e = entry_dup( rs->sr_entry );
1890                                 if ( !si->head ) si->head = e;
1891                                 if ( si->tail ) si->tail->e_private = e;
1892                                 si->tail = e;
1893                         } else {
1894                                 si->over = 1;
1895                                 si->count = 0;
1896                                 for (;si->head; si->head=e) {
1897                                         e = si->head->e_private;
1898                                         si->head->e_private = NULL;
1899                                         entry_free(si->head);
1900                                 }
1901                                 si->tail = NULL;
1902                         }
1903                 }
1904
1905         } else if ( rs->sr_type == REP_RESULT ) {
1906                 if ( si->count ||
1907                         ( si->qtemp->negttl && !si->count && !si->over &&
1908                                 rs->sr_err == LDAP_SUCCESS )) {
1909                         CachedQuery *qc = qm->addfunc(op, qm, &si->query, si->qtemp,
1910                                 si->count);
1911
1912                         if ( qc != NULL ) {
1913                                 if ( si->count )
1914                                         cache_entries( op, rs, &qc->q_uuid );
1915                                 ldap_pvt_thread_mutex_lock(&cm->cache_mutex);
1916                                 cm->num_cached_queries++;
1917                                 Debug( pcache_debug, "STORED QUERIES = %lu\n",
1918                                                 cm->num_cached_queries, 0, 0 );
1919                                 ldap_pvt_thread_mutex_unlock(&cm->cache_mutex);
1920
1921                                 /* If the consistency checker suspended itself,
1922                                  * wake it back up
1923                                  */
1924                                 if ( cm->cc_paused ) {
1925                                         ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
1926                                         if ( cm->cc_paused ) {
1927                                                 cm->cc_paused = 0;
1928                                                 ldap_pvt_runqueue_resched( &slapd_rq, cm->cc_arg, 0 );
1929                                         }
1930                                         ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
1931                                 }
1932                         } else if ( si->count ) {
1933                                 /* duplicate query, free it */
1934                                 Entry *e;
1935                                 for (;si->head; si->head=e) {
1936                                         e = si->head->e_private;
1937                                         si->head->e_private = NULL;
1938                                         entry_free(si->head);
1939                                 }
1940                         }
1941                 } else {
1942                         filter_free( si->query.filter );
1943                 }
1944
1945                 op->o_callback->sc_cleanup = pcache_op_cleanup;
1946         }
1947         return SLAP_CB_CONTINUE;
1948 }
1949
1950 static int
1951 add_filter_attrs(
1952         Operation *op,
1953         AttributeName** new_attrs,
1954         struct attr_set *attrs,
1955         AttributeName* filter_attrs,
1956         int fattr_cnt,
1957         int fattr_got_oc)
1958 {
1959         int alluser = 0;
1960         int allop = 0;
1961         int i, j;
1962         int count;
1963         int addoc = 0;
1964
1965         /* duplicate attrs */
1966         count = attrs->count + fattr_cnt;
1967         if ( !fattr_got_oc && !(attrs->flags & PC_GOT_OC)) {
1968                 addoc = 1;
1969                 count++;
1970         }
1971
1972         *new_attrs = (AttributeName*)ch_calloc( count + 1,
1973                 sizeof(AttributeName) );
1974         for (i=0; i<attrs->count; i++) {
1975                 (*new_attrs)[i].an_name = attrs->attrs[i].an_name;
1976                 (*new_attrs)[i].an_desc = attrs->attrs[i].an_desc;
1977         }
1978         BER_BVZERO( &(*new_attrs)[i].an_name );
1979         alluser = an_find(*new_attrs, &AllUser);
1980         allop = an_find(*new_attrs, &AllOper);
1981
1982         j = i;
1983         for ( i=0; i<fattr_cnt; i++ ) {
1984                 if ( an_find(*new_attrs, &filter_attrs[i].an_name ) ) {
1985                         continue;
1986                 }
1987                 if ( is_at_operational(filter_attrs[i].an_desc->ad_type) ) {
1988                         if ( allop ) {
1989                                 continue;
1990                         }
1991                 } else if ( alluser ) {
1992                         continue;
1993                 }
1994                 (*new_attrs)[j].an_name = filter_attrs[i].an_name;
1995                 (*new_attrs)[j].an_desc = filter_attrs[i].an_desc;
1996                 (*new_attrs)[j].an_oc = NULL;
1997                 (*new_attrs)[j].an_oc_exclude = 0;
1998                 j++;
1999         }
2000         if ( addoc ) {
2001                 (*new_attrs)[j].an_name = slap_schema.si_ad_objectClass->ad_cname;
2002                 (*new_attrs)[j].an_desc = slap_schema.si_ad_objectClass;
2003                 (*new_attrs)[j].an_oc = NULL;
2004                 (*new_attrs)[j].an_oc_exclude = 0;
2005                 j++;
2006         }
2007         BER_BVZERO( &(*new_attrs)[j].an_name );
2008
2009         return count;
2010 }
2011
2012 /* NOTE: this is a quick workaround to let pcache minimally interact
2013  * with pagedResults.  A more articulated solutions would be to
2014  * perform the remote query without control and cache all results,
2015  * performing the pagedResults search only within the client
2016  * and the proxy.  This requires pcache to understand pagedResults. */
2017 static int
2018 pcache_chk_controls(
2019         Operation       *op,
2020         SlapReply       *rs )
2021 {
2022         const char      *non = "";
2023         const char      *stripped = "";
2024
2025         switch( op->o_pagedresults ) {
2026         case SLAP_CONTROL_NONCRITICAL:
2027                 non = "non-";
2028                 stripped = "; stripped";
2029                 /* fallthru */
2030
2031         case SLAP_CONTROL_CRITICAL:
2032                 Debug( pcache_debug, "%s: "
2033                         "%scritical pagedResults control "
2034                         "disabled with proxy cache%s.\n",
2035                         op->o_log_prefix, non, stripped );
2036                 
2037                 slap_remove_control( op, rs, slap_cids.sc_pagedResults, NULL );
2038                 break;
2039
2040         default:
2041                 rs->sr_err = SLAP_CB_CONTINUE;
2042                 break;
2043         }
2044
2045         return rs->sr_err;
2046 }
2047
2048 #ifdef PCACHE_CONTROL_PRIVDB
2049 static int
2050 pcache_op_privdb(
2051         Operation               *op,
2052         SlapReply               *rs )
2053 {
2054         slap_overinst   *on = (slap_overinst *)op->o_bd->bd_info;
2055         cache_manager   *cm = on->on_bi.bi_private;
2056         slap_callback   *save_cb;
2057         slap_op_t       type;
2058
2059         /* skip if control is unset */
2060         if ( op->o_ctrlflag[ privDB_cid ] != SLAP_CONTROL_CRITICAL ) {
2061                 return SLAP_CB_CONTINUE;
2062         }
2063
2064         /* FIXME: might be a little bit exaggerated... */
2065         if ( !be_isroot( op ) ) {
2066                 save_cb = op->o_callback;
2067                 op->o_callback = NULL;
2068                 send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM,
2069                         "pcachePrivDB: operation not allowed" );
2070                 op->o_callback = save_cb;
2071
2072                 return rs->sr_err;
2073         }
2074
2075         /* map tag to operation */
2076         type = slap_req2op( op->o_tag );
2077         if ( type != SLAP_OP_LAST ) {
2078                 BI_op_func      **func;
2079
2080                 /* execute, if possible */
2081                 func = &cm->db.be_bind;
2082                 if ( func[ type ] != NULL ) {
2083                         Operation       op2 = *op;
2084         
2085                         op2.o_bd = &cm->db;
2086
2087                         return func[ type ]( &op2, rs );
2088                 }
2089         }
2090
2091         /* otherwise fall back to error */
2092         save_cb = op->o_callback;
2093         op->o_callback = NULL;
2094         send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM,
2095                 "operation not supported with pcachePrivDB control" );
2096         op->o_callback = save_cb;
2097
2098         return rs->sr_err;
2099 }
2100 #endif /* PCACHE_CONTROL_PRIVDB */
2101
2102 static int
2103 pcache_op_search(
2104         Operation       *op,
2105         SlapReply       *rs )
2106 {
2107         slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
2108         cache_manager *cm = on->on_bi.bi_private;
2109         query_manager*          qm = cm->qm;
2110
2111         int i = -1;
2112
2113         AttributeName   *filter_attrs = NULL;
2114
2115         Query           query;
2116         QueryTemplate   *qtemp = NULL;
2117
2118         int             attr_set = -1;
2119         CachedQuery     *answerable = NULL;
2120         int             cacheable = 0;
2121         int             fattr_cnt=0;
2122         int             fattr_got_oc = 0;
2123
2124         struct berval   tempstr;
2125
2126 #ifdef PCACHE_CONTROL_PRIVDB
2127         if ( op->o_ctrlflag[ privDB_cid ] == SLAP_CONTROL_CRITICAL ) {
2128                 return pcache_op_privdb( op, rs );
2129         }
2130 #endif /* PCACHE_CONTROL_PRIVDB */
2131
2132         tempstr.bv_val = op->o_tmpalloc( op->ors_filterstr.bv_len+1, op->o_tmpmemctx );
2133         tempstr.bv_len = 0;
2134         if ( filter2template( op, op->ors_filter, &tempstr, &filter_attrs,
2135                 &fattr_cnt, &fattr_got_oc )) {
2136                 op->o_tmpfree( tempstr.bv_val, op->o_tmpmemctx );
2137                 return SLAP_CB_CONTINUE;
2138         }
2139
2140         Debug( pcache_debug, "query template of incoming query = %s\n",
2141                                         tempstr.bv_val, 0, 0 );
2142
2143         /* FIXME: cannot cache/answer requests with pagedResults control */
2144
2145         /* find attr set */
2146         attr_set = get_attr_set(op->ors_attrs, qm, cm->numattrsets);
2147
2148         query.filter = op->ors_filter;
2149         query.base = op->o_req_ndn;
2150         query.scope = op->ors_scope;
2151
2152         /* check for query containment */
2153         if (attr_set > -1) {
2154                 QueryTemplate *qt = qm->attr_sets[attr_set].templates;
2155                 for (; qt; qt = qt->qtnext ) {
2156                         /* find if template i can potentially answer tempstr */
2157                         if (qt->querystr.bv_len != tempstr.bv_len ||
2158                                 strcasecmp( qt->querystr.bv_val, tempstr.bv_val ))
2159                                 continue;
2160                         cacheable = 1;
2161                         qtemp = qt;
2162                         Debug( pcache_debug, "Entering QC, querystr = %s\n",
2163                                         op->ors_filterstr.bv_val, 0, 0 );
2164                         answerable = (*(qm->qcfunc))(op, qm, &query, qt);
2165
2166                         if (answerable)
2167                                 break;
2168                 }
2169         }
2170         op->o_tmpfree( tempstr.bv_val, op->o_tmpmemctx );
2171
2172         if (answerable) {
2173                 /* Need to clear the callbacks of the original operation,
2174                  * in case there are other overlays */
2175                 BackendDB       *save_bd = op->o_bd;
2176                 slap_callback   *save_cb = op->o_callback;
2177
2178                 Debug( pcache_debug, "QUERY ANSWERABLE\n", 0, 0, 0 );
2179                 op->o_tmpfree( filter_attrs, op->o_tmpmemctx );
2180                 if ( BER_BVISNULL( &answerable->q_uuid )) {
2181                         /* No entries cached, just an empty result set */
2182                         i = rs->sr_err = 0;
2183                         send_ldap_result( op, rs );
2184                 } else {
2185                         op->o_bd = &cm->db;
2186                         op->o_callback = NULL;
2187                         i = cm->db.bd_info->bi_op_search( op, rs );
2188                 }
2189                 ldap_pvt_thread_rdwr_runlock(&qtemp->t_rwlock);
2190                 op->o_bd = save_bd;
2191                 op->o_callback = save_cb;
2192                 return i;
2193         }
2194
2195         Debug( pcache_debug, "QUERY NOT ANSWERABLE\n", 0, 0, 0 );
2196
2197         ldap_pvt_thread_mutex_lock(&cm->cache_mutex);
2198         if (cm->num_cached_queries >= cm->max_queries) {
2199                 cacheable = 0;
2200         }
2201         ldap_pvt_thread_mutex_unlock(&cm->cache_mutex);
2202
2203         if (op->ors_attrsonly)
2204                 cacheable = 0;
2205
2206         if (cacheable) {
2207                 slap_callback           *cb;
2208                 struct search_info      *si;
2209
2210                 Debug( pcache_debug, "QUERY CACHEABLE\n", 0, 0, 0 );
2211                 query.filter = filter_dup(op->ors_filter, NULL);
2212                 ldap_pvt_thread_rdwr_wlock(&qtemp->t_rwlock);
2213                 if ( !qtemp->t_attrs.count ) {
2214                         qtemp->t_attrs.count = add_filter_attrs(op,
2215                                 &qtemp->t_attrs.attrs,
2216                                 &qm->attr_sets[attr_set],
2217                                 filter_attrs, fattr_cnt, fattr_got_oc);
2218                 }
2219                 ldap_pvt_thread_rdwr_wunlock(&qtemp->t_rwlock);
2220
2221                 cb = op->o_tmpalloc( sizeof(*cb) + sizeof(*si), op->o_tmpmemctx );
2222                 cb->sc_response = pcache_response;
2223                 cb->sc_cleanup = NULL;
2224                 cb->sc_private = (cb+1);
2225                 si = cb->sc_private;
2226                 si->on = on;
2227                 si->query = query;
2228                 si->qtemp = qtemp;
2229                 si->max = cm->num_entries_limit ;
2230                 si->over = 0;
2231                 si->count = 0;
2232                 si->head = NULL;
2233                 si->tail = NULL;
2234                 si->save_attrs = op->ors_attrs;
2235
2236                 op->ors_attrs = qtemp->t_attrs.attrs;
2237
2238                 if ( cm->response_cb == PCACHE_RESPONSE_CB_HEAD ) {
2239                         cb->sc_next = op->o_callback;
2240                         op->o_callback = cb;
2241
2242                 } else {
2243                         slap_callback           **pcb;
2244
2245                         /* need to move the callback at the end, in case other
2246                          * overlays are present, so that the final entry is
2247                          * actually cached */
2248                         cb->sc_next = NULL;
2249                         for ( pcb = &op->o_callback; *pcb; pcb = &(*pcb)->sc_next );
2250                         *pcb = cb;
2251                 }
2252
2253         } else {
2254                 Debug( pcache_debug, "QUERY NOT CACHEABLE\n",
2255                                         0, 0, 0);
2256         }
2257
2258         op->o_tmpfree( filter_attrs, op->o_tmpmemctx );
2259
2260         return SLAP_CB_CONTINUE;
2261 }
2262
2263 static int
2264 get_attr_set(
2265         AttributeName* attrs,
2266         query_manager* qm,
2267         int num )
2268 {
2269         int i;
2270         int count = 0;
2271
2272         if ( attrs ) {
2273                 for ( ; attrs[count].an_name.bv_val; count++ );
2274         }
2275
2276         /* recognize a single "*" or a "1.1" */
2277         if ( count == 0 ) {
2278                 count = 1;
2279                 attrs = slap_anlist_all_user_attributes;
2280
2281         } else if ( count == 1 && strcmp( attrs[0].an_name.bv_val, LDAP_NO_ATTRS ) == 0 ) {
2282                 count = 0;
2283                 attrs = NULL;
2284         }
2285
2286         for ( i = 0; i < num; i++ ) {
2287                 AttributeName *a2;
2288                 int found = 1;
2289
2290                 if ( count > qm->attr_sets[i].count ) {
2291                         continue;
2292                 }
2293
2294                 if ( !count ) {
2295                         if ( !qm->attr_sets[i].count ) {
2296                                 break;
2297                         }
2298                         continue;
2299                 }
2300
2301                 for ( a2 = attrs; a2->an_name.bv_val; a2++ ) {
2302                         if ( !an_find( qm->attr_sets[i].attrs, &a2->an_name ) ) {
2303                                 found = 0;
2304                                 break;
2305                         }
2306                 }
2307
2308                 if ( found ) {
2309                         break;
2310                 }
2311         }
2312
2313         if ( i == num ) {
2314                 i = -1;
2315         }
2316
2317         return i;
2318 }
2319
2320 static void*
2321 consistency_check(
2322         void *ctx,
2323         void *arg )
2324 {
2325         struct re_s *rtask = arg;
2326         slap_overinst *on = rtask->arg;
2327         cache_manager *cm = on->on_bi.bi_private;
2328         query_manager *qm = cm->qm;
2329         Connection conn = {0};
2330         OperationBuffer opbuf;
2331         Operation *op;
2332
2333         SlapReply rs = {REP_RESULT};
2334         CachedQuery* query;
2335         int return_val, pause = 1;
2336         QueryTemplate* templ;
2337
2338         connection_fake_init( &conn, &opbuf, ctx );
2339         op = &opbuf.ob_op;
2340
2341         op->o_bd = &cm->db;
2342         op->o_dn = cm->db.be_rootdn;
2343         op->o_ndn = cm->db.be_rootndn;
2344
2345         cm->cc_arg = arg;
2346
2347         for (templ = qm->templates; templ; templ=templ->qmnext) {
2348                 query = templ->query_last;
2349                 if ( query ) pause = 0;
2350                 op->o_time = slap_get_time();
2351                 while (query && (query->expiry_time < op->o_time)) {
2352                         int rem = 0;
2353                         Debug( pcache_debug, "Lock CR index = %p\n",
2354                                         (void *) templ, 0, 0 );
2355                         ldap_pvt_thread_rdwr_wlock(&templ->t_rwlock);
2356                         if ( query == templ->query_last ) {
2357                                 rem = 1;
2358                                 remove_from_template(query, templ);
2359                                 Debug( pcache_debug, "TEMPLATE %p QUERIES-- %d\n",
2360                                                 (void *) templ, templ->no_of_queries, 0 );
2361                                 Debug( pcache_debug, "Unlock CR index = %p\n",
2362                                                 (void *) templ, 0, 0 );
2363                         }
2364                         ldap_pvt_thread_rdwr_wunlock(&templ->t_rwlock);
2365                         if ( !rem ) {
2366                                 query = templ->query_last;
2367                                 continue;
2368                         }
2369                         ldap_pvt_thread_mutex_lock(&qm->lru_mutex);
2370                         remove_query(qm, query);
2371                         ldap_pvt_thread_mutex_unlock(&qm->lru_mutex);
2372                         if ( BER_BVISNULL( &query->q_uuid ))
2373                                 return_val = 0;
2374                         else
2375                                 return_val = remove_query_data(op, &rs, &query->q_uuid);
2376                         Debug( pcache_debug, "STALE QUERY REMOVED, SIZE=%d\n",
2377                                                 return_val, 0, 0 );
2378                         ldap_pvt_thread_mutex_lock(&cm->cache_mutex);
2379                         cm->cur_entries -= return_val;
2380                         cm->num_cached_queries--;
2381                         Debug( pcache_debug, "STORED QUERIES = %lu\n",
2382                                         cm->num_cached_queries, 0, 0 );
2383                         ldap_pvt_thread_mutex_unlock(&cm->cache_mutex);
2384                         Debug( pcache_debug,
2385                                 "STALE QUERY REMOVED, CACHE ="
2386                                 "%d entries\n",
2387                                 cm->cur_entries, 0, 0 );
2388                         free_query(query);
2389                         query = templ->query_last;
2390                 }
2391         }
2392         ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
2393         if ( ldap_pvt_runqueue_isrunning( &slapd_rq, rtask )) {
2394                 ldap_pvt_runqueue_stoptask( &slapd_rq, rtask );
2395         }
2396         /* If there were no queries, defer processing for a while */
2397         cm->cc_paused = pause;
2398         ldap_pvt_runqueue_resched( &slapd_rq, rtask, pause );
2399
2400         ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
2401         return NULL;
2402 }
2403
2404
2405 #define MAX_ATTR_SETS 500
2406
2407 enum {
2408         PC_MAIN = 1,
2409         PC_ATTR,
2410         PC_TEMP,
2411         PC_RESP,
2412         PC_QUERIES
2413 };
2414
2415 static ConfigDriver pc_cf_gen;
2416 static ConfigLDAPadd pc_ldadd;
2417 static ConfigCfAdd pc_cfadd;
2418
2419 static ConfigTable pccfg[] = {
2420         { "proxycache", "backend> <max_entries> <numattrsets> <entry limit> "
2421                                 "<cycle_time",
2422                 6, 6, 0, ARG_MAGIC|ARG_NO_DELETE|PC_MAIN, pc_cf_gen,
2423                 "( OLcfgOvAt:2.1 NAME 'olcProxyCache' "
2424                         "DESC 'ProxyCache basic parameters' "
2425                         "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
2426         { "proxyattrset", "index> <attributes...",
2427                 2, 0, 0, ARG_MAGIC|PC_ATTR, pc_cf_gen,
2428                 "( OLcfgOvAt:2.2 NAME 'olcProxyAttrset' "
2429                         "DESC 'A set of attributes to cache' "
2430                         "SYNTAX OMsDirectoryString )", NULL, NULL },
2431         { "proxytemplate", "filter> <attrset-index> <TTL> <negTTL",
2432                 4, 5, 0, ARG_MAGIC|PC_TEMP, pc_cf_gen,
2433                 "( OLcfgOvAt:2.3 NAME 'olcProxyTemplate' "
2434                         "DESC 'Filter template, attrset, cache TTL, optional negative TTL' "
2435                         "SYNTAX OMsDirectoryString )", NULL, NULL },
2436         { "response-callback", "head|tail(default)",
2437                 2, 2, 0, ARG_MAGIC|PC_RESP, pc_cf_gen,
2438                 "( OLcfgOvAt:2.4 NAME 'olcProxyResponseCB' "
2439                         "DESC 'Response callback position in overlay stack' "
2440                         "SYNTAX OMsDirectoryString )", NULL, NULL },
2441         { "proxyCacheQueries", "queries",
2442                 2, 2, 0, ARG_INT|ARG_MAGIC|PC_QUERIES, pc_cf_gen,
2443                 "( OLcfgOvAt:2.5 NAME 'olcProxyCacheQueries' "
2444                         "DESC 'Maximum number of queries to cache' "
2445                         "SYNTAX OMsInteger )", NULL, NULL },
2446         { "proxySaveQueries", "TRUE|FALSE",
2447                 2, 2, 0, ARG_ON_OFF|ARG_OFFSET, (void *)offsetof(cache_manager, save_queries),
2448                 "( OLcfgOvAt:2.6 NAME 'olcProxySaveQueries' "
2449                         "DESC 'Save cached queries for hot restart' "
2450                         "SYNTAX OMsBoolean )", NULL, NULL },
2451
2452         { NULL, NULL, 0, 0, 0, ARG_IGNORED }
2453 };
2454
2455 static ConfigOCs pcocs[] = {
2456         { "( OLcfgOvOc:2.1 "
2457                 "NAME 'olcPcacheConfig' "
2458                 "DESC 'ProxyCache configuration' "
2459                 "SUP olcOverlayConfig "
2460                 "MUST ( olcProxyCache $ olcProxyAttrset $ olcProxyTemplate ) "
2461                 "MAY ( olcProxyResponseCB $ olcProxyCacheQueries $ olcProxySaveQueries ) )",
2462                 Cft_Overlay, pccfg, NULL, pc_cfadd },
2463         { "( OLcfgOvOc:2.2 "
2464                 "NAME 'olcPcacheDatabase' "
2465                 "DESC 'Cache database configuration' "
2466                 "AUXILIARY )", Cft_Misc, pccfg, pc_ldadd },
2467         { NULL, 0, NULL }
2468 };
2469
2470 static int
2471 pc_ldadd( CfEntryInfo *p, Entry *e, ConfigArgs *ca )
2472 {
2473         slap_overinst *on;
2474         cache_manager *cm;
2475
2476         if ( p->ce_type != Cft_Overlay || !p->ce_bi ||
2477                 p->ce_bi->bi_cf_ocs != pcocs )
2478                 return LDAP_CONSTRAINT_VIOLATION;
2479
2480         on = (slap_overinst *)p->ce_bi;
2481         cm = on->on_bi.bi_private;
2482         ca->be = &cm->db;
2483         return LDAP_SUCCESS;
2484 }
2485
2486 static int
2487 pc_cfadd( Operation *op, SlapReply *rs, Entry *p, ConfigArgs *ca )
2488 {
2489         CfEntryInfo *pe = p->e_private;
2490         slap_overinst *on = (slap_overinst *)pe->ce_bi;
2491         cache_manager *cm = on->on_bi.bi_private;
2492         struct berval bv;
2493
2494         /* FIXME: should not hardcode "olcDatabase" here */
2495         bv.bv_len = sprintf( ca->cr_msg, "olcDatabase=%s", cm->db.bd_info->bi_type );
2496         bv.bv_val = ca->cr_msg;
2497         ca->be = &cm->db;
2498
2499         /* We can only create this entry if the database is table-driven
2500          */
2501         if ( cm->db.bd_info->bi_cf_ocs )
2502                 config_build_entry( op, rs, pe, ca, &bv, cm->db.bd_info->bi_cf_ocs,
2503                         &pcocs[1] );
2504
2505         return 0;
2506 }
2507
2508 static int
2509 pc_cf_gen( ConfigArgs *c )
2510 {
2511         slap_overinst   *on = (slap_overinst *)c->bi;
2512         cache_manager*  cm = on->on_bi.bi_private;
2513         query_manager*  qm = cm->qm;
2514         QueryTemplate*  temp;
2515         AttributeName*  attr_name;
2516         AttributeName*  attrarray;
2517         const char*     text=NULL;
2518         int             i, num, rc = 0;
2519         char            *ptr;
2520         unsigned long   t;
2521
2522         if ( c->op == SLAP_CONFIG_EMIT ) {
2523                 struct berval bv;
2524                 switch( c->type ) {
2525                 case PC_MAIN:
2526                         bv.bv_len = snprintf( c->cr_msg, sizeof( c->cr_msg ), "%s %d %d %d %ld",
2527                                 cm->db.bd_info->bi_type, cm->max_entries, cm->numattrsets,
2528                                 cm->num_entries_limit, cm->cc_period );
2529                         bv.bv_val = c->cr_msg;
2530                         value_add_one( &c->rvalue_vals, &bv );
2531                         break;
2532                 case PC_ATTR:
2533                         for (i=0; i<cm->numattrsets; i++) {
2534                                 if ( !qm->attr_sets[i].count ) continue;
2535
2536                                 bv.bv_len = snprintf( c->cr_msg, sizeof( c->cr_msg ), "%d", i );
2537
2538                                 /* count the attr length */
2539                                 for ( attr_name = qm->attr_sets[i].attrs;
2540                                         attr_name->an_name.bv_val; attr_name++ )
2541                                         bv.bv_len += attr_name->an_name.bv_len + 1;
2542
2543                                 bv.bv_val = ch_malloc( bv.bv_len+1 );
2544                                 ptr = lutil_strcopy( bv.bv_val, c->cr_msg );
2545                                 for ( attr_name = qm->attr_sets[i].attrs;
2546                                         attr_name->an_name.bv_val; attr_name++ ) {
2547                                         *ptr++ = ' ';
2548                                         ptr = lutil_strcopy( ptr, attr_name->an_name.bv_val );
2549                                 }
2550                                 ber_bvarray_add( &c->rvalue_vals, &bv );
2551                         }
2552                         if ( !c->rvalue_vals )
2553                                 rc = 1;
2554                         break;
2555                 case PC_TEMP:
2556                         for (temp=qm->templates; temp; temp=temp->qmnext) {
2557                                 if ( temp->negttl ) {
2558                                         bv.bv_len = snprintf( c->cr_msg, sizeof( c->cr_msg ),
2559                                                 " %d %ld %ld",
2560                                                 temp->attr_set_index,
2561                                                 temp->ttl,
2562                                                 temp->negttl );
2563                                 } else {
2564                                         bv.bv_len = snprintf( c->cr_msg, sizeof( c->cr_msg ), " %d %ld",
2565                                                 temp->attr_set_index,
2566                                                 temp->ttl );
2567                                 }
2568                                 bv.bv_len += temp->querystr.bv_len + 2;
2569                                 bv.bv_val = ch_malloc( bv.bv_len+1 );
2570                                 ptr = bv.bv_val;
2571                                 *ptr++ = '"';
2572                                 ptr = lutil_strcopy( ptr, temp->querystr.bv_val );
2573                                 *ptr++ = '"';
2574                                 strcpy( ptr, c->cr_msg );
2575                                 ber_bvarray_add( &c->rvalue_vals, &bv );
2576                         }
2577                         if ( !c->rvalue_vals )
2578                                 rc = 1;
2579                         break;
2580                 case PC_RESP:
2581                         if ( cm->response_cb == PCACHE_RESPONSE_CB_HEAD ) {
2582                                 BER_BVSTR( &bv, "head" );
2583                         } else {
2584                                 BER_BVSTR( &bv, "tail" );
2585                         }
2586                         value_add_one( &c->rvalue_vals, &bv );
2587                         break;
2588                 case PC_QUERIES:
2589                         c->value_int = cm->max_queries;
2590                         break;
2591                 }
2592                 return rc;
2593         } else if ( c->op == LDAP_MOD_DELETE ) {
2594                 return 1;       /* FIXME */
2595 #if 0
2596                 switch( c->type ) {
2597                 case PC_ATTR:
2598                 case PC_TEMP:
2599                 }
2600                 return rc;
2601 #endif
2602         }
2603
2604         switch( c->type ) {
2605         case PC_MAIN:
2606                 if ( cm->numattrsets > 0 ) {
2607                         snprintf( c->cr_msg, sizeof( c->cr_msg ), "\"proxycache\" directive already provided" );
2608                         Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 );
2609                         return( 1 );
2610                 }
2611
2612                 if ( lutil_atoi( &cm->numattrsets, c->argv[3] ) != 0 ) {
2613                         snprintf( c->cr_msg, sizeof( c->cr_msg ), "unable to parse num attrsets=\"%s\" (arg #3)",
2614                                 c->argv[3] );
2615                         Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 );
2616                         return( 1 );
2617                 }
2618                 if ( cm->numattrsets <= 0 ) {
2619                         snprintf( c->cr_msg, sizeof( c->cr_msg ), "numattrsets (arg #3) must be positive" );
2620                         Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 );
2621                         return( 1 );
2622                 }
2623                 if ( cm->numattrsets > MAX_ATTR_SETS ) {
2624                         snprintf( c->cr_msg, sizeof( c->cr_msg ), "numattrsets (arg #3) must be <= %d", MAX_ATTR_SETS );
2625                         Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 );
2626                         return( 1 );
2627                 }
2628
2629                 if ( !backend_db_init( c->argv[1], &cm->db, -1, NULL )) {
2630                         snprintf( c->cr_msg, sizeof( c->cr_msg ), "unknown backend type (arg #1)" );
2631                         Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 );
2632                         return( 1 );
2633                 }
2634
2635                 if ( lutil_atoi( &cm->max_entries, c->argv[2] ) != 0 ) {
2636                         snprintf( c->cr_msg, sizeof( c->cr_msg ), "unable to parse max entries=\"%s\" (arg #2)",
2637                                 c->argv[2] );
2638                         Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 );
2639                         return( 1 );
2640                 }
2641                 if ( cm->max_entries <= 0 ) {
2642                         snprintf( c->cr_msg, sizeof( c->cr_msg ), "max entries (arg #2) must be positive.\n" );
2643                         Debug( LDAP_DEBUG_CONFIG, "%s: %s\n", c->log, c->cr_msg, 0 );
2644                         return( 1 );
2645                 }
2646
2647                 if ( lutil_atoi( &cm->num_entries_limit, c->argv[4] ) != 0 ) {
2648                         snprintf( c->cr_msg, sizeof( c->cr_msg ), "unable to parse entry limit=\"%s\" (arg #4)",
2649                                 c->argv[4] );
2650                         Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 );
2651                         return( 1 );
2652                 }
2653                 if ( cm->num_entries_limit <= 0 ) {
2654                         snprintf( c->cr_msg, sizeof( c->cr_msg ), "entry limit (arg #4) must be positive" );
2655                         Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 );
2656                         return( 1 );
2657                 }
2658                 if ( cm->num_entries_limit > cm->max_entries ) {
2659                         snprintf( c->cr_msg, sizeof( c->cr_msg ), "entry limit (arg #4) must be less than max entries %d (arg #2)", cm->max_entries );
2660                         Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 );
2661                         return( 1 );
2662                 }
2663
2664                 if ( lutil_parse_time( c->argv[5], &t ) != 0 ) {
2665                         snprintf( c->cr_msg, sizeof( c->cr_msg ), "unable to parse period=\"%s\" (arg #5)",
2666                                 c->argv[5] );
2667                         Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 );
2668                         return( 1 );
2669                 }
2670                 cm->cc_period = (time_t)t;
2671                 Debug( pcache_debug,
2672                                 "Total # of attribute sets to be cached = %d.\n",
2673                                 cm->numattrsets, 0, 0 );
2674                 qm->attr_sets = ( struct attr_set * )ch_calloc( cm->numattrsets,
2675                                                 sizeof( struct attr_set ) );
2676                 break;
2677         case PC_ATTR:
2678                 if ( cm->numattrsets == 0 ) {
2679                         snprintf( c->cr_msg, sizeof( c->cr_msg ), "\"proxycache\" directive not provided yet" );
2680                         Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 );
2681                         return( 1 );
2682                 }
2683                 if ( lutil_atoi( &num, c->argv[1] ) != 0 ) {
2684                         snprintf( c->cr_msg, sizeof( c->cr_msg ), "unable to parse attrset #=\"%s\"",
2685                                 c->argv[1] );
2686                         Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 );
2687                         return( 1 );
2688                 }
2689
2690                 if ( num < 0 || num >= cm->numattrsets ) {
2691                         snprintf( c->cr_msg, sizeof( c->cr_msg ), "attrset index %d out of bounds (must be %s%d)",
2692                                 num, cm->numattrsets > 1 ? "0->" : "", cm->numattrsets - 1 );
2693                         Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 );
2694                         return 1;
2695                 }
2696                 qm->attr_sets[num].flags |= PC_CONFIGURED;
2697                 if ( c->argc == 2 ) {
2698                         /* assume "1.1" */
2699                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
2700                                 "need an explicit attr in attrlist; use \"*\" to indicate all attrs" );
2701                         Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 );
2702                         return 1;
2703
2704                 } else if ( c->argc == 3 ) {
2705                         if ( strcmp( c->argv[2], LDAP_ALL_USER_ATTRIBUTES ) == 0 ) {
2706                                 qm->attr_sets[num].count = 1;
2707                                 qm->attr_sets[num].attrs = (AttributeName*)ch_calloc( 2,
2708                                         sizeof( AttributeName ) );
2709                                 BER_BVSTR( &qm->attr_sets[num].attrs[0].an_name, LDAP_ALL_USER_ATTRIBUTES );
2710                                 break;
2711
2712                         } else if ( strcmp( c->argv[2], LDAP_ALL_OPERATIONAL_ATTRIBUTES ) == 0 ) {
2713                                 qm->attr_sets[num].count = 1;
2714                                 qm->attr_sets[num].attrs = (AttributeName*)ch_calloc( 2,
2715                                         sizeof( AttributeName ) );
2716                                 BER_BVSTR( &qm->attr_sets[num].attrs[0].an_name, LDAP_ALL_OPERATIONAL_ATTRIBUTES );
2717                                 break;
2718
2719                         } else if ( strcmp( c->argv[2], LDAP_NO_ATTRS ) == 0 ) {
2720                                 break;
2721                         }
2722                         /* else: fallthru */
2723
2724                 } else if ( c->argc == 4 ) {
2725                         if ( ( strcmp( c->argv[2], LDAP_ALL_USER_ATTRIBUTES ) == 0 && strcmp( c->argv[3], LDAP_ALL_OPERATIONAL_ATTRIBUTES ) == 0 )
2726                                 || ( strcmp( c->argv[2], LDAP_ALL_OPERATIONAL_ATTRIBUTES ) == 0 && strcmp( c->argv[3], LDAP_ALL_USER_ATTRIBUTES ) == 0 ) )
2727                         {
2728                                 qm->attr_sets[num].count = 2;
2729                                 qm->attr_sets[num].attrs = (AttributeName*)ch_calloc( 3,
2730                                         sizeof( AttributeName ) );
2731                                 BER_BVSTR( &qm->attr_sets[num].attrs[0].an_name, LDAP_ALL_USER_ATTRIBUTES );
2732                                 BER_BVSTR( &qm->attr_sets[num].attrs[1].an_name, LDAP_ALL_OPERATIONAL_ATTRIBUTES );
2733                                 break;
2734                         }
2735                         /* else: fallthru */
2736                 }
2737
2738                 if ( c->argc > 2 ) {
2739                         int all_user = 0, all_op = 0;
2740
2741                         qm->attr_sets[num].count = c->argc - 2;
2742                         qm->attr_sets[num].attrs = (AttributeName*)ch_calloc( c->argc - 1,
2743                                 sizeof( AttributeName ) );
2744                         attr_name = qm->attr_sets[num].attrs;
2745                         for ( i = 2; i < c->argc; i++ ) {
2746                                 attr_name->an_desc = NULL;
2747                                 if ( strcmp( c->argv[i], LDAP_NO_ATTRS ) == 0 ) {
2748                                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
2749                                                 "invalid attr #%d \"%s\" in attrlist",
2750                                                 i - 2, c->argv[i] );
2751                                         Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 );
2752                                         ch_free( qm->attr_sets[num].attrs );
2753                                         qm->attr_sets[num].attrs = NULL;
2754                                         qm->attr_sets[num].count = 0;
2755                                         return 1;
2756                                 }
2757                                 if ( strcmp( c->argv[i], LDAP_ALL_USER_ATTRIBUTES ) == 0 ) {
2758                                         all_user = 1;
2759                                         BER_BVSTR( &attr_name->an_name, LDAP_ALL_USER_ATTRIBUTES );
2760                                 } else if ( strcmp( c->argv[i], LDAP_ALL_OPERATIONAL_ATTRIBUTES ) == 0 ) {
2761                                         all_op = 1;
2762                                         BER_BVSTR( &attr_name->an_name, LDAP_ALL_OPERATIONAL_ATTRIBUTES );
2763                                 } else {
2764                                         if ( slap_str2ad( c->argv[i], &attr_name->an_desc, &text ) ) {
2765                                                 strcpy( c->cr_msg, text );
2766                                                 Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 );
2767                                                 ch_free( qm->attr_sets[num].attrs );
2768                                                 qm->attr_sets[num].attrs = NULL;
2769                                                 qm->attr_sets[num].count = 0;
2770                                                 return 1;
2771                                         }
2772                                         attr_name->an_name = attr_name->an_desc->ad_cname;
2773                                 }
2774                                 attr_name->an_oc = NULL;
2775                                 attr_name->an_oc_exclude = 0;
2776                                 if ( attr_name->an_desc == slap_schema.si_ad_objectClass )
2777                                         qm->attr_sets[num].flags |= PC_GOT_OC;
2778                                 attr_name++;
2779                                 BER_BVZERO( &attr_name->an_name );
2780                         }
2781
2782                         /* warn if list contains both "*" and "+" */
2783                         if ( i > 4 && all_user && all_op ) {
2784                                 snprintf( c->cr_msg, sizeof( c->cr_msg ),
2785                                         "warning: attribute list contains \"*\" and \"+\"" );
2786                                 Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 );
2787                         }
2788                 }
2789                 break;
2790         case PC_TEMP:
2791                 if ( cm->numattrsets == 0 ) {
2792                         snprintf( c->cr_msg, sizeof( c->cr_msg ), "\"proxycache\" directive not provided yet" );
2793                         Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 );
2794                         return( 1 );
2795                 }
2796                 if ( lutil_atoi( &i, c->argv[2] ) != 0 ) {
2797                         snprintf( c->cr_msg, sizeof( c->cr_msg ), "unable to parse template #=\"%s\"",
2798                                 c->argv[2] );
2799                         Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 );
2800                         return( 1 );
2801                 }
2802
2803                 if ( i < 0 || i >= cm->numattrsets || 
2804                         !(qm->attr_sets[i].flags & PC_CONFIGURED )) {
2805                         snprintf( c->cr_msg, sizeof( c->cr_msg ), "template index %d invalid (%s%d)",
2806                                 i, cm->numattrsets > 1 ? "0->" : "", cm->numattrsets - 1 );
2807                         Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 );
2808                         return 1;
2809                 }
2810                 temp = ch_calloc( 1, sizeof( QueryTemplate ));
2811                 temp->qmnext = qm->templates;
2812                 qm->templates = temp;
2813                 ldap_pvt_thread_rdwr_init( &temp->t_rwlock );
2814                 temp->query = temp->query_last = NULL;
2815                 if ( lutil_parse_time( c->argv[3], &t ) != 0 ) {
2816                         snprintf( c->cr_msg, sizeof( c->cr_msg ), "unable to parse template ttl=\"%s\"",
2817                                 c->argv[3] );
2818                         Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 );
2819                         return( 1 );
2820                 }
2821                 temp->ttl = (time_t)t;
2822                 if ( c->argc == 5 ) {
2823                         if ( lutil_parse_time( c->argv[4], &t ) != 0 ) {
2824                                 snprintf( c->cr_msg, sizeof( c->cr_msg ),
2825                                         "unable to parse template negttl=\"%s\"",
2826                                         c->argv[4] );
2827                                 Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 );
2828                                         return( 1 );
2829                         }
2830                         temp->negttl = (time_t)t;
2831                 } else {
2832                         temp->negttl = 0;
2833                 }
2834
2835                 temp->no_of_queries = 0;
2836
2837                 ber_str2bv( c->argv[1], 0, 1, &temp->querystr );
2838                 Debug( pcache_debug, "Template:\n", 0, 0, 0 );
2839                 Debug( pcache_debug, "  query template: %s\n",
2840                                 temp->querystr.bv_val, 0, 0 );
2841                 temp->attr_set_index = i;
2842                 qm->attr_sets[i].flags |= PC_REFERENCED;
2843                 temp->qtnext = qm->attr_sets[i].templates;
2844                 qm->attr_sets[i].templates = temp;
2845                 Debug( pcache_debug, "  attributes: \n", 0, 0, 0 );
2846                 if ( ( attrarray = qm->attr_sets[i].attrs ) != NULL ) {
2847                         for ( i=0; attrarray[i].an_name.bv_val; i++ )
2848                                 Debug( pcache_debug, "\t%s\n",
2849                                         attrarray[i].an_name.bv_val, 0, 0 );
2850                 }
2851                 break;
2852         case PC_RESP:
2853                 if ( strcasecmp( c->argv[1], "head" ) == 0 ) {
2854                         cm->response_cb = PCACHE_RESPONSE_CB_HEAD;
2855
2856                 } else if ( strcasecmp( c->argv[1], "tail" ) == 0 ) {
2857                         cm->response_cb = PCACHE_RESPONSE_CB_TAIL;
2858
2859                 } else {
2860                         snprintf( c->cr_msg, sizeof( c->cr_msg ), "unknown specifier" );
2861                         Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 );
2862                         return 1;
2863                 }
2864                 break;
2865         case PC_QUERIES:
2866                 if ( c->value_int <= 0 ) {
2867                         snprintf( c->cr_msg, sizeof( c->cr_msg ), "max queries must be positive" );
2868                         Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 );
2869                         return( 1 );
2870                 }
2871                 cm->max_queries = c->value_int;
2872                 break;
2873         }
2874         return rc;
2875 }
2876
2877 static int
2878 pcache_db_config(
2879         BackendDB       *be,
2880         const char      *fname,
2881         int             lineno,
2882         int             argc,
2883         char            **argv
2884 )
2885 {
2886         slap_overinst   *on = (slap_overinst *)be->bd_info;
2887         cache_manager*  cm = on->on_bi.bi_private;
2888
2889         /* Something for the cache database? */
2890         if ( cm->db.bd_info && cm->db.bd_info->bi_db_config )
2891                 return cm->db.bd_info->bi_db_config( &cm->db, fname, lineno,
2892                         argc, argv );
2893         return SLAP_CONF_UNKNOWN;
2894 }
2895
2896 static int
2897 pcache_db_init(
2898         BackendDB *be,
2899         ConfigReply *cr)
2900 {
2901         slap_overinst *on = (slap_overinst *)be->bd_info;
2902         cache_manager *cm;
2903         query_manager *qm;
2904
2905         cm = (cache_manager *)ch_malloc(sizeof(cache_manager));
2906         on->on_bi.bi_private = cm;
2907
2908         qm = (query_manager*)ch_malloc(sizeof(query_manager));
2909
2910         cm->db = *be;
2911         SLAP_DBFLAGS(&cm->db) |= SLAP_DBFLAG_NO_SCHEMA_CHECK;
2912         cm->db.be_private = NULL;
2913         cm->db.be_pcl_mutexp = &cm->db.be_pcl_mutex;
2914         cm->qm = qm;
2915         cm->numattrsets = 0;
2916         cm->num_entries_limit = 5;
2917         cm->num_cached_queries = 0;
2918         cm->max_entries = 0;
2919         cm->cur_entries = 0;
2920         cm->max_queries = 10000;
2921         cm->save_queries = 0;
2922         cm->response_cb = PCACHE_RESPONSE_CB_TAIL;
2923         cm->cc_period = 1000;
2924         cm->cc_paused = 0;
2925
2926         qm->attr_sets = NULL;
2927         qm->templates = NULL;
2928         qm->lru_top = NULL;
2929         qm->lru_bottom = NULL;
2930
2931         qm->qcfunc = query_containment;
2932         qm->crfunc = cache_replacement;
2933         qm->addfunc = add_query;
2934         ldap_pvt_thread_mutex_init(&qm->lru_mutex);
2935
2936         ldap_pvt_thread_mutex_init(&cm->cache_mutex);
2937         return 0;
2938 }
2939
2940 static int
2941 pcache_cachedquery_open_cb( Operation *op, SlapReply *rs )
2942 {
2943         assert( op->o_tag == LDAP_REQ_SEARCH );
2944
2945         if ( rs->sr_type == REP_SEARCH ) {
2946                 Attribute       *a;
2947
2948                 a = attr_find( rs->sr_entry->e_attrs, ad_cachedQueryURL );
2949                 if ( a != NULL ) {
2950                         BerVarray       *valsp;
2951
2952                         assert( a->a_nvals != NULL );
2953
2954                         valsp = op->o_callback->sc_private;
2955                         assert( *valsp == NULL );
2956
2957                         ber_bvarray_dup_x( valsp, a->a_nvals, op->o_tmpmemctx );
2958                 }
2959         }
2960
2961         return 0;
2962 }
2963
2964 static int
2965 pcache_db_open(
2966         BackendDB *be,
2967         ConfigReply *cr )
2968 {
2969         slap_overinst   *on = (slap_overinst *)be->bd_info;
2970         cache_manager   *cm = on->on_bi.bi_private;
2971         query_manager*  qm = cm->qm;
2972         int             i, ncf = 0, rf = 0, nrf = 0, rc = 0;
2973
2974         /* check attr sets */
2975         for ( i = 0; i < cm->numattrsets; i++) {
2976                 if ( !( qm->attr_sets[i].flags & PC_CONFIGURED ) ) {
2977                         if ( qm->attr_sets[i].flags & PC_REFERENCED ) {
2978                                 Debug( LDAP_DEBUG_CONFIG, "pcache: attr set #%d not configured but referenced.\n", i, 0, 0 );
2979                                 rf++;
2980
2981                         } else {
2982                                 Debug( LDAP_DEBUG_CONFIG, "pcache: warning, attr set #%d not configured.\n", i, 0, 0 );
2983                         }
2984                         ncf++;
2985
2986                 } else if ( !( qm->attr_sets[i].flags & PC_REFERENCED ) ) {
2987                         Debug( LDAP_DEBUG_CONFIG, "pcache: attr set #%d configured but not referenced.\n", i, 0, 0 );
2988                         nrf++;
2989                 }
2990         }
2991
2992         if ( ncf || rf || nrf ) {
2993                 Debug( LDAP_DEBUG_CONFIG, "pcache: warning, %d attr sets configured but not referenced.\n", nrf, 0, 0 );
2994                 Debug( LDAP_DEBUG_CONFIG, "pcache: warning, %d attr sets not configured.\n", ncf, 0, 0 );
2995                 Debug( LDAP_DEBUG_CONFIG, "pcache: %d attr sets not configured but referenced.\n", rf, 0, 0 );
2996
2997                 if ( rf > 0 ) {
2998                         return 1;
2999                 }
3000         }
3001
3002         /* need to inherit something from the original database... */
3003         cm->db.be_def_limit = be->be_def_limit;
3004         cm->db.be_limits = be->be_limits;
3005         cm->db.be_acl = be->be_acl;
3006         cm->db.be_dfltaccess = be->be_dfltaccess;
3007
3008         if ( SLAP_DBMONITORING( be ) ) {
3009                 SLAP_DBFLAGS( &cm->db ) |= SLAP_DBFLAG_MONITORING;
3010
3011         } else {
3012                 SLAP_DBFLAGS( &cm->db ) &= ~SLAP_DBFLAG_MONITORING;
3013         }
3014
3015         rc = backend_startup_one( &cm->db, NULL );
3016
3017         /* There is no runqueue in TOOL mode */
3018         if ( slapMode & SLAP_SERVER_MODE ) {
3019                 ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
3020                 ldap_pvt_runqueue_insert( &slapd_rq, cm->cc_period,
3021                         consistency_check, on,
3022                         "pcache_consistency", be->be_suffix[0].bv_val );
3023                 ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
3024
3025                 /* Cached database must have the rootdn */
3026                 if ( BER_BVISNULL( &cm->db.be_rootndn )
3027                                 || BER_BVISEMPTY( &cm->db.be_rootndn ) )
3028                 {
3029                         Debug( LDAP_DEBUG_ANY, "pcache_db_open(): "
3030                                 "underlying database of type \"%s\"\n"
3031                                 "    serving naming context \"%s\"\n"
3032                                 "    has no \"rootdn\", required by \"proxycache\".\n",
3033                                 on->on_info->oi_orig->bi_type,
3034                                 cm->db.be_suffix[0].bv_val, 0 );
3035                         return 1;
3036                 }
3037
3038                 if ( cm->save_queries ) {
3039                         void            *thrctx = ldap_pvt_thread_pool_context();
3040                         Connection      conn = { 0 };
3041                         OperationBuffer opbuf;
3042                         Operation       *op;
3043                         slap_callback   cb = { 0 };
3044                         SlapReply       rs = { 0 };
3045                         BerVarray       vals = NULL;
3046                         Filter          f = { 0 };
3047                         AttributeName   attrs[ 2 ] = { 0 };
3048
3049                         connection_fake_init( &conn, &opbuf, thrctx );
3050                         op = &opbuf.ob_op;
3051
3052                         op->o_bd = &cm->db;
3053
3054                         op->o_tag = LDAP_REQ_SEARCH;
3055                         op->o_protocol = LDAP_VERSION3;
3056                         cb.sc_response = pcache_cachedquery_open_cb;
3057                         cb.sc_private = &vals;
3058                         op->o_callback = &cb;
3059                         op->o_time = slap_get_time();
3060                         op->o_do_not_cache = 1;
3061                         op->o_managedsait = SLAP_CONTROL_CRITICAL;
3062
3063                         op->o_dn = cm->db.be_rootdn;
3064                         op->o_ndn = cm->db.be_rootndn;
3065                         op->o_req_dn = cm->db.be_suffix[ 0 ];
3066                         op->o_req_ndn = cm->db.be_nsuffix[ 0 ];
3067
3068                         op->ors_scope = LDAP_SCOPE_BASE;
3069                         op->ors_deref = LDAP_DEREF_NEVER;
3070                         op->ors_slimit = 1;
3071                         op->ors_tlimit = SLAP_NO_LIMIT;
3072                         ber_str2bv( "(cachedQueryURL=*)", 0, 0, &op->ors_filterstr );
3073                         f.f_choice = LDAP_FILTER_PRESENT;
3074                         f.f_desc = ad_cachedQueryURL;
3075                         op->ors_filter = &f;
3076                         attrs[ 0 ].an_desc = ad_cachedQueryURL;
3077                         attrs[ 0 ].an_name = ad_cachedQueryURL->ad_cname;
3078                         op->ors_attrs = attrs;
3079                         op->ors_attrsonly = 0;
3080
3081                         rc = op->o_bd->be_search( op, &rs );
3082                         if ( rc == LDAP_SUCCESS && vals != NULL ) {
3083                                 int     i;
3084
3085                                 for ( i = 0; !BER_BVISNULL( &vals[ i ] ); i++ ) {
3086                                         if ( url2query( vals[ i ].bv_val, op, qm ) == 0 ) {
3087                                                 cm->num_cached_queries++;
3088                                         }
3089                                 }
3090
3091                                 ber_bvarray_free_x( vals, op->o_tmpmemctx );
3092                         }
3093
3094                         /* ignore errors */
3095                         rc = 0;
3096                 }
3097         }
3098
3099         return rc;
3100 }
3101
3102 static void
3103 pcache_free_qbase( void *v )
3104 {
3105         Qbase *qb = v;
3106         int i;
3107
3108         for (i=0; i<3; i++)
3109                 tavl_free( qb->scopes[i], NULL );
3110         ch_free( qb );
3111 }
3112
3113 static int
3114 pcache_db_close(
3115         BackendDB *be,
3116         ConfigReply *cr
3117 )
3118 {
3119         slap_overinst *on = (slap_overinst *)be->bd_info;
3120         cache_manager *cm = on->on_bi.bi_private;
3121         query_manager *qm = cm->qm;
3122         QueryTemplate *tm;
3123         int i, rc = 0;
3124
3125         if ( cm->save_queries ) {
3126                 CachedQuery     *qc;
3127                 BerVarray       vals = NULL;
3128
3129                 void            *thrctx;
3130                 Connection      conn = { 0 };
3131                 OperationBuffer opbuf;
3132                 Operation       *op;
3133                 slap_callback   cb = { 0 };
3134
3135                 SlapReply       rs = { REP_RESULT };
3136                 Modifications   mod = { 0 };
3137
3138                 thrctx = ldap_pvt_thread_pool_context();
3139
3140                 connection_fake_init( &conn, &opbuf, thrctx );
3141                 op = &opbuf.ob_op;
3142
3143                 if ( qm->templates != NULL ) {
3144                         for ( tm = qm->templates; tm != NULL; tm = tm->qmnext ) {
3145                                 for ( qc = tm->query; qc; qc = qc->next ) {
3146                                         struct berval   bv;
3147
3148                                         if ( query2url( op, qc, &bv ) == 0 ) {
3149                                                 ber_bvarray_add_x( &vals, &bv, op->o_tmpmemctx );
3150                                         }
3151                                 }
3152                         }
3153                 }
3154
3155                 op->o_bd = &cm->db;
3156                 op->o_dn = cm->db.be_rootdn;
3157                 op->o_ndn = cm->db.be_rootndn;
3158
3159                 op->o_tag = LDAP_REQ_MODIFY;
3160                 op->o_protocol = LDAP_VERSION3;
3161                 cb.sc_response = slap_null_cb;
3162                 op->o_callback = &cb;
3163                 op->o_time = slap_get_time();
3164                 op->o_do_not_cache = 1;
3165                 op->o_managedsait = SLAP_CONTROL_CRITICAL;
3166
3167                 op->o_req_dn = op->o_bd->be_suffix[0];
3168                 op->o_req_ndn = op->o_bd->be_nsuffix[0];
3169
3170                 mod.sml_op = LDAP_MOD_REPLACE;
3171                 mod.sml_flags = 0;
3172                 mod.sml_desc = ad_cachedQueryURL;
3173                 mod.sml_type = ad_cachedQueryURL->ad_cname;
3174                 mod.sml_values = vals;
3175                 mod.sml_nvalues = NULL;
3176                 mod.sml_next = NULL;
3177                 Debug( pcache_debug,
3178                         "%sSETTING CACHED QUERY URLS\n",
3179                         vals == NULL ? "RE" : "", 0, 0 );
3180
3181                 op->orm_modlist = &mod;
3182
3183                 op->o_bd->be_modify( op, &rs );
3184
3185                 ber_bvarray_free_x( vals, op->o_tmpmemctx );
3186         }
3187
3188         /* cleanup stuff inherited from the original database... */
3189         cm->db.be_limits = NULL;
3190         cm->db.be_acl = NULL;
3191
3192         /* stop the thread ... */
3193         if ( cm->cc_arg ) {
3194                 ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
3195                 if ( ldap_pvt_runqueue_isrunning( &slapd_rq, cm->cc_arg ) ) {
3196                         ldap_pvt_runqueue_stoptask( &slapd_rq, cm->cc_arg );
3197                 }
3198                 ldap_pvt_runqueue_remove( &slapd_rq, cm->cc_arg );
3199                 ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
3200         }
3201
3202         if ( cm->db.bd_info->bi_db_close ) {
3203                 rc = cm->db.bd_info->bi_db_close( &cm->db, NULL );
3204         }
3205         while ( (tm = qm->templates) != NULL ) {
3206                 CachedQuery *qc, *qn;
3207                 qm->templates = tm->qmnext;
3208                 for ( qc = tm->query; qc; qc = qn ) {
3209                         qn = qc->next;
3210                         free_query( qc );
3211                 }
3212                 avl_free( tm->qbase, pcache_free_qbase );
3213                 free( tm->querystr.bv_val );
3214                 ldap_pvt_thread_rdwr_destroy( &tm->t_rwlock );
3215                 free( tm->t_attrs.attrs );
3216                 free( tm );
3217         }
3218
3219         for ( i=0; i<cm->numattrsets; i++ ) {
3220                 free( qm->attr_sets[i].attrs );
3221         }
3222         free( qm->attr_sets );
3223         qm->attr_sets = NULL;
3224
3225         return rc;
3226 }
3227
3228 static int
3229 pcache_db_destroy(
3230         BackendDB *be,
3231         ConfigReply *cr
3232 )
3233 {
3234         slap_overinst *on = (slap_overinst *)be->bd_info;
3235         cache_manager *cm = on->on_bi.bi_private;
3236         query_manager *qm = cm->qm;
3237
3238         if ( cm->db.be_private != NULL ) {
3239                 backend_stopdown_one( &cm->db );
3240         }
3241
3242         ldap_pvt_thread_mutex_destroy( &qm->lru_mutex );
3243         ldap_pvt_thread_mutex_destroy( &cm->cache_mutex );
3244         free( qm );
3245         free( cm );
3246
3247         return 0;
3248 }
3249
3250 #ifdef PCACHE_CONTROL_PRIVDB
3251 /*
3252         Control ::= SEQUENCE {
3253              controlType             LDAPOID,
3254              criticality             BOOLEAN DEFAULT FALSE,
3255              controlValue            OCTET STRING OPTIONAL }
3256
3257         controlType ::= 1.3.6.1.4.1.4203.666.11.9.5.1
3258
3259  * criticality must be TRUE; controlValue must be absent.
3260  */
3261 static int
3262 parse_privdb_ctrl(
3263         Operation       *op,
3264         SlapReply       *rs,
3265         LDAPControl     *ctrl )
3266 {
3267         if ( op->o_ctrlflag[ privDB_cid ] != SLAP_CONTROL_NONE ) {
3268                 rs->sr_text = "privateDB control specified multiple times";
3269                 return LDAP_PROTOCOL_ERROR;
3270         }
3271
3272         if ( !BER_BVISNULL( &ctrl->ldctl_value ) ) {
3273                 rs->sr_text = "privateDB control value not absent";
3274                 return LDAP_PROTOCOL_ERROR;
3275         }
3276
3277         if ( !ctrl->ldctl_iscritical ) {
3278                 rs->sr_text = "privateDB control criticality required";
3279                 return LDAP_PROTOCOL_ERROR;
3280         }
3281
3282         op->o_ctrlflag[ privDB_cid ] = SLAP_CONTROL_CRITICAL;
3283
3284         return LDAP_SUCCESS;
3285 }
3286
3287 static char *extops[] = {
3288         LDAP_EXOP_MODIFY_PASSWD,
3289         NULL
3290 };
3291 #endif /* PCACHE_CONTROL_PRIVDB */
3292
3293 #ifdef PCACHE_EXOP_QUERY_DELETE
3294 static struct berval pcache_exop_QUERY_DELETE = BER_BVC( PCACHE_EXOP_QUERY_DELETE );
3295
3296 #define LDAP_TAG_EXOP_QUERY_DELETE_BASE ((LBER_CLASS_CONTEXT|LBER_CONSTRUCTED) + 0)
3297 #define LDAP_TAG_EXOP_QUERY_DELETE_DN   ((LBER_CLASS_CONTEXT|LBER_CONSTRUCTED) + 1)
3298 #define LDAP_TAG_EXOP_QUERY_DELETE_UUID ((LBER_CLASS_CONTEXT|LBER_CONSTRUCTED) + 2)
3299
3300 /*
3301         ExtendedRequest ::= [APPLICATION 23] SEQUENCE {
3302              requestName      [0] LDAPOID,
3303              requestValue     [1] OCTET STRING OPTIONAL }
3304
3305         requestName ::= 1.3.6.1.4.1.4203.666.11.9.6.1
3306
3307         requestValue ::= SEQUENCE { CHOICE {
3308                   baseDN           [0] LDAPDN
3309                   entryDN          [1] LDAPDN },
3310              queryID          [2] OCTET STRING (SIZE(16))
3311                   -- constrained to UUID }
3312
3313  * Either baseDN or entryDN must be present, to allow database selection.
3314  *
3315  * 1. if baseDN and queryID are present, then the query corresponding
3316  *    to queryID is deleted;
3317  * 2. if baseDN is present and queryID is absent, then all queries
3318  *    are deleted;
3319  * 3. if entryDN is present and queryID is absent, then all queries
3320  *    corresponding to the queryID values present in entryDN are deleted;
3321  * 4. if entryDN and queryID are present, then all queries
3322  *    corresponding to the queryID values present in entryDN are deleted,
3323  *    but only if the value of queryID is contained in the entry;
3324  *
3325  * Currently, only 1, 3 and 4 are implemented.  2 can be obtained by either
3326  * recursively deleting the database (ldapdelete -r) with PRIVDB control,
3327  * or by removing the database files.
3328
3329         ExtendedResponse ::= [APPLICATION 24] SEQUENCE {
3330              COMPONENTS OF LDAPResult,
3331              responseName     [10] LDAPOID OPTIONAL,
3332              responseValue    [11] OCTET STRING OPTIONAL }
3333
3334  * responseName and responseValue must be absent.
3335  */
3336
3337 /*
3338  * - on success, *tagp is either LDAP_TAG_EXOP_QUERY_DELETE_BASE
3339  *   or LDAP_TAG_EXOP_QUERY_DELETE_DN.
3340  * - if ndn != NULL, it is set to the normalized DN in the request
3341  *   corresponding to either the baseDN or the entryDN, according
3342  *   to *tagp; memory is malloc'ed on the Operation's slab, and must
3343  *   be freed by the caller.
3344  * - if uuid != NULL, it is set to point to the normalized UUID;
3345  *   memory is malloc'ed on the Operation's slab, and must
3346  *   be freed by the caller.
3347  */
3348 static int
3349 pcache_parse_query_delete(
3350         struct berval   *in,
3351         ber_tag_t       *tagp,
3352         struct berval   *ndn,
3353         struct berval   *uuid,
3354         const char      **text,
3355         void            *ctx )
3356 {
3357         int                     rc = LDAP_SUCCESS;
3358         ber_tag_t               tag;
3359         ber_len_t               len = -1;
3360         BerElementBuffer        berbuf;
3361         BerElement              *ber = (BerElement *)&berbuf;
3362         struct berval           reqdata = BER_BVNULL;
3363
3364         *text = NULL;
3365
3366         if ( ndn ) {
3367                 BER_BVZERO( ndn );
3368         }
3369
3370         if ( uuid ) {
3371                 BER_BVZERO( uuid );
3372         }
3373
3374         if ( in == NULL || in->bv_len == 0 ) {
3375                 *text = "empty request data field in queryDelete exop";
3376                 return LDAP_PROTOCOL_ERROR;
3377         }
3378
3379         ber_dupbv_x( &reqdata, in, ctx );
3380
3381         /* ber_init2 uses reqdata directly, doesn't allocate new buffers */
3382         ber_init2( ber, &reqdata, 0 );
3383
3384         tag = ber_scanf( ber, "{" /*}*/ );
3385
3386         if ( tag == LBER_ERROR ) {
3387                 Debug( LDAP_DEBUG_TRACE,
3388                         "pcache_parse_query_delete: decoding error.\n",
3389                         0, 0, 0 );
3390                 goto decoding_error;
3391         }
3392
3393         tag = ber_peek_tag( ber, &len );
3394         if ( tag == LDAP_TAG_EXOP_QUERY_DELETE_BASE
3395                 || tag == LDAP_TAG_EXOP_QUERY_DELETE_DN )
3396         {
3397                 *tagp = tag;
3398
3399                 if ( ndn != NULL ) {
3400                         struct berval   dn;
3401
3402                         tag = ber_scanf( ber, "m", &dn );
3403                         if ( tag == LBER_ERROR ) {
3404                                 Debug( LDAP_DEBUG_TRACE,
3405                                         "pcache_parse_query_delete: DN parse failed.\n",
3406                                         0, 0, 0 );
3407                                 goto decoding_error;
3408                         }
3409
3410                         rc = dnNormalize( 0, NULL, NULL, &dn, ndn, ctx );
3411                         if ( rc != LDAP_SUCCESS ) {
3412                                 *text = "invalid DN in queryDelete exop request data";
3413                                 goto done;
3414                         }
3415
3416                 } else {
3417                         tag = ber_scanf( ber, "x" /* "m" */ );
3418                         if ( tag == LBER_DEFAULT ) {
3419                                 goto decoding_error;
3420                         }
3421                 }
3422
3423                 tag = ber_peek_tag( ber, &len );
3424         }
3425
3426         if ( tag == LDAP_TAG_EXOP_QUERY_DELETE_UUID ) {
3427                 if ( uuid != NULL ) {
3428                         struct berval   bv;
3429                         Syntax          *syn_UUID = slap_schema.si_ad_entryUUID->ad_type->sat_syntax;
3430
3431                         tag = ber_scanf( ber, "m", &bv );
3432                         if ( tag == LBER_ERROR ) {
3433                                 Debug( LDAP_DEBUG_TRACE,
3434                                         "pcache_parse_query_delete: UUID parse failed.\n",
3435                                         0, 0, 0 );
3436                                 goto decoding_error;
3437                         }
3438
3439                         if ( bv.bv_len != 16 ) {
3440                                 Debug( LDAP_DEBUG_TRACE,
3441                                         "pcache_parse_query_delete: invalid UUID length %lu.\n",
3442                                         (unsigned long)bv.bv_len, 0, 0 );
3443                                 goto decoding_error;
3444                         }
3445
3446                         ber_dupbv_x( uuid, &bv, ctx );
3447
3448                 } else {
3449                         tag = ber_skip_tag( ber, &len );
3450                         if ( tag == LBER_DEFAULT ) {
3451                                 goto decoding_error;
3452                         }
3453
3454                         if ( len != 16 ) {
3455                                 Debug( LDAP_DEBUG_TRACE,
3456                                         "pcache_parse_query_delete: invalid UUID length %lu.\n",
3457                                         (unsigned long)len, 0, 0 );
3458                                 goto decoding_error;
3459                         }
3460                 }
3461
3462                 tag = ber_peek_tag( ber, &len );
3463         }
3464
3465         if ( tag != LBER_DEFAULT || len != 0 ) {
3466 decoding_error:;
3467                 Debug( LDAP_DEBUG_TRACE,
3468                         "pcache_parse_query_delete: decoding error\n",
3469                         0, 0, 0 );
3470                 rc = LDAP_PROTOCOL_ERROR;
3471                 *text = "queryDelete data decoding error";
3472
3473 done:;
3474                 if ( ndn && !BER_BVISNULL( ndn ) ) {
3475                         slap_sl_free( ndn->bv_val, ctx );
3476                         BER_BVZERO( ndn );
3477                 }
3478
3479                 if ( uuid && !BER_BVISNULL( uuid ) ) {
3480                         slap_sl_free( uuid->bv_val, ctx );
3481                         BER_BVZERO( uuid );
3482                 }
3483         }
3484
3485         if ( !BER_BVISNULL( &reqdata ) ) {
3486                 ber_memfree_x( reqdata.bv_val, ctx );
3487         }
3488
3489         return rc;
3490 }
3491
3492 static int
3493 pcache_exop_query_delete(
3494         Operation       *op,
3495         SlapReply       *rs )
3496 {
3497         BackendDB       *bd = op->o_bd;
3498
3499         struct berval   uuid = BER_BVNULL;
3500         char            buf[ SLAP_TEXT_BUFLEN ] = { '\0' };
3501         int             len = 0;
3502         ber_tag_t       tag = LBER_DEFAULT;
3503
3504         rs->sr_err = pcache_parse_query_delete( op->ore_reqdata,
3505                 &tag, &op->o_req_ndn, &uuid,
3506                 &rs->sr_text, op->o_tmpmemctx );
3507         if ( rs->sr_err != LDAP_SUCCESS ) {
3508                 return rs->sr_err;
3509         }
3510
3511         if ( LogTest( LDAP_DEBUG_STATS ) ) {
3512                 if ( !BER_BVISNULL( &op->o_req_ndn ) ) {
3513                         len = snprintf( buf, sizeof( buf ), " dn=\"%s\"", op->o_req_ndn.bv_val );
3514                 }
3515
3516                 if ( !BER_BVISNULL( &uuid ) ) {
3517                         char    uuidbuf[ LDAP_LUTIL_UUIDSTR_BUFSIZE ];
3518
3519                         lutil_uuidstr_from_normalized(
3520                                 uuid.bv_val, uuid.bv_len,
3521                                 uuidbuf, sizeof( uuidbuf ) );
3522
3523                         snprintf( &buf[ len ], sizeof( buf ) - len, " UUID=\"%s\"", uuidbuf );
3524                 }
3525
3526                 Debug( LDAP_DEBUG_STATS, "%s QUERY DELETE%s\n",
3527                         op->o_log_prefix, buf, 0 );
3528                 op->o_req_dn = op->o_req_ndn;
3529         }
3530
3531         op->o_bd = select_backend( &op->o_req_ndn, 0 );
3532         rs->sr_err = backend_check_restrictions( op, rs,
3533                 (struct berval *)&pcache_exop_QUERY_DELETE );
3534         if ( rs->sr_err != LDAP_SUCCESS ) {
3535                 goto done;
3536         }
3537
3538         if ( op->o_bd->be_extended == NULL ) {
3539                 send_ldap_error( op, rs, LDAP_UNAVAILABLE_CRITICAL_EXTENSION,
3540                         "backend does not support extended operations" );
3541                 goto done;
3542         }
3543
3544         op->o_bd->be_extended( op, rs );
3545
3546 done:;
3547         if ( !BER_BVISNULL( &op->o_req_ndn ) ) {
3548                 op->o_tmpfree( op->o_req_ndn.bv_val, op->o_tmpmemctx );
3549                 BER_BVZERO( &op->o_req_ndn );
3550                 BER_BVZERO( &op->o_req_dn );
3551         }
3552
3553         if ( !BER_BVISNULL( &uuid ) ) {
3554                 op->o_tmpfree( uuid.bv_val, op->o_tmpmemctx );
3555         }
3556
3557         op->o_bd = bd;
3558
3559         return rs->sr_err;
3560 }
3561
3562 static int
3563 pcache_op_extended( Operation *op, SlapReply *rs )
3564 {
3565         slap_overinst   *on = (slap_overinst *)op->o_bd->bd_info;
3566         cache_manager   *cm = on->on_bi.bi_private;
3567
3568 #ifdef PCACHE_CONTROL_PRIVDB
3569         if ( op->o_ctrlflag[ privDB_cid ] == SLAP_CONTROL_CRITICAL ) {
3570                 return pcache_op_privdb( op, rs );
3571         }
3572 #endif /* PCACHE_CONTROL_PRIVDB */
3573
3574         if ( bvmatch( &op->ore_reqoid, &pcache_exop_QUERY_DELETE ) ) {
3575                 struct berval   uuid = BER_BVNULL;
3576                 ber_tag_t       tag = LBER_DEFAULT;
3577
3578                 rs->sr_err = pcache_parse_query_delete( op->ore_reqdata,
3579                         &tag, NULL, &uuid, &rs->sr_text, op->o_tmpmemctx );
3580                 assert( rs->sr_err == LDAP_SUCCESS );
3581
3582                 if ( tag == LDAP_TAG_EXOP_QUERY_DELETE_DN ) {
3583                         /* remove all queries related to the selected entry */
3584                         rs->sr_err = pcache_remove_entry_queries_from_cache( op,
3585                                 cm, &op->o_req_ndn, &uuid );
3586
3587                 } else if ( tag == LDAP_TAG_EXOP_QUERY_DELETE_BASE ) {
3588                         if ( !BER_BVISNULL( &uuid ) ) {
3589                                 /* remove the selected query */
3590                                 remove_query_and_data( op, rs, cm, &uuid );
3591                                 op->o_tmpfree( uuid.bv_val, op->o_tmpmemctx );
3592                                 rs->sr_err = LDAP_SUCCESS;
3593
3594                         } else {
3595                                 /* TODO: remove all queries */
3596                                 rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
3597                                 rs->sr_text = "deletion of all queries not implemented";
3598                         }
3599                 }
3600         }
3601
3602         return rs->sr_err;
3603 }
3604 #endif /* PCACHE_EXOP_QUERY_DELETE */
3605
3606 static slap_overinst pcache;
3607
3608 static char *obsolete_names[] = {
3609         "proxycache",
3610         NULL
3611 };
3612
3613 #if SLAPD_OVER_PROXYCACHE == SLAPD_MOD_DYNAMIC
3614 static
3615 #endif /* SLAPD_OVER_PROXYCACHE == SLAPD_MOD_DYNAMIC */
3616 int
3617 pcache_initialize()
3618 {
3619         int i, code;
3620         struct berval debugbv = BER_BVC("pcache");
3621
3622         code = slap_loglevel_get( &debugbv, &pcache_debug );
3623         if ( code ) {
3624                 return code;
3625         }
3626
3627 #ifdef PCACHE_CONTROL_PRIVDB
3628         code = register_supported_control( PCACHE_CONTROL_PRIVDB,
3629                 SLAP_CTRL_BIND|SLAP_CTRL_ACCESS|SLAP_CTRL_HIDE, extops,
3630                 parse_privdb_ctrl, &privDB_cid );
3631         if ( code != LDAP_SUCCESS ) {
3632                 Debug( LDAP_DEBUG_ANY,
3633                         "pcache_initialize: failed to register control %s (%d)\n",
3634                         PCACHE_CONTROL_PRIVDB, code, 0 );
3635                 return code;
3636         }
3637 #endif /* PCACHE_CONTROL_PRIVDB */
3638
3639 #ifdef PCACHE_EXOP_QUERY_DELETE
3640         code = load_extop2( (struct berval *)&pcache_exop_QUERY_DELETE,
3641                 SLAP_EXOP_WRITES|SLAP_EXOP_HIDE, pcache_exop_query_delete,
3642                 0 );
3643         if ( code != LDAP_SUCCESS ) {
3644                 Debug( LDAP_DEBUG_ANY,
3645                         "pcache_initialize: unable to register queryDelete exop: %d.\n",
3646                         code, 0, 0 );
3647                 return code;
3648         }
3649 #endif /* PCACHE_EXOP_QUERY_DELETE */
3650
3651         for ( i = 0; as[i].desc != NULL; i++ ) {
3652                 code = register_at( as[i].desc, as[i].adp, 0 );
3653                 if ( code ) {
3654                         Debug( LDAP_DEBUG_ANY,
3655                                 "pcache_initialize: register_at #%d failed\n", i, 0, 0 );
3656                         return code;
3657                 }
3658         }
3659
3660         pcache.on_bi.bi_type = "pcache";
3661         pcache.on_bi.bi_obsolete_names = obsolete_names;
3662         pcache.on_bi.bi_db_init = pcache_db_init;
3663         pcache.on_bi.bi_db_config = pcache_db_config;
3664         pcache.on_bi.bi_db_open = pcache_db_open;
3665         pcache.on_bi.bi_db_close = pcache_db_close;
3666         pcache.on_bi.bi_db_destroy = pcache_db_destroy;
3667
3668         pcache.on_bi.bi_op_search = pcache_op_search;
3669 #ifdef PCACHE_CONTROL_PRIVDB
3670         pcache.on_bi.bi_op_bind = pcache_op_privdb;
3671         pcache.on_bi.bi_op_compare = pcache_op_privdb;
3672         pcache.on_bi.bi_op_modrdn = pcache_op_privdb;
3673         pcache.on_bi.bi_op_modify = pcache_op_privdb;
3674         pcache.on_bi.bi_op_add = pcache_op_privdb;
3675         pcache.on_bi.bi_op_delete = pcache_op_privdb;
3676 #endif /* PCACHE_CONTROL_PRIVDB */
3677 #ifdef PCACHE_EXOP_QUERY_DELETE
3678         pcache.on_bi.bi_extended = pcache_op_extended;
3679 #elif defined( PCACHE_CONTROL_PRIVDB )
3680         pcache.on_bi.bi_extended = pcache_op_privdb;
3681 #endif
3682
3683         pcache.on_bi.bi_chk_controls = pcache_chk_controls;
3684
3685         pcache.on_bi.bi_cf_ocs = pcocs;
3686
3687         code = config_register_schema( pccfg, pcocs );
3688         if ( code ) return code;
3689
3690         return overlay_register( &pcache );
3691 }
3692
3693 #if SLAPD_OVER_PROXYCACHE == SLAPD_MOD_DYNAMIC
3694 int init_module(int argc, char *argv[]) {
3695         return pcache_initialize();
3696 }
3697 #endif
3698
3699 #endif  /* defined(SLAPD_OVER_PROXYCACHE) */