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