Home | History | Annotate | Line # | Download | only in overlays
      1 /*	$NetBSD: pcache.c,v 1.4 2025/09/05 21:16:32 christos Exp $	*/
      2 
      3 /* $OpenLDAP$ */
      4 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
      5  *
      6  * Copyright 2003-2024 The OpenLDAP Foundation.
      7  * Portions Copyright 2003 IBM Corporation.
      8  * Portions Copyright 2003-2009 Symas Corporation.
      9  * All rights reserved.
     10  *
     11  * Redistribution and use in source and binary forms, with or without
     12  * modification, are permitted only as authorized by the OpenLDAP
     13  * Public License.
     14  *
     15  * A copy of this license is available in the file LICENSE in the
     16  * top-level directory of the distribution or, alternatively, at
     17  * <http://www.OpenLDAP.org/license.html>.
     18  */
     19 /* ACKNOWLEDGEMENTS:
     20  * This work was initially developed by Apurva Kumar for inclusion
     21  * in OpenLDAP Software and subsequently rewritten by Howard Chu.
     22  */
     23 
     24 #include <sys/cdefs.h>
     25 __RCSID("$NetBSD: pcache.c,v 1.4 2025/09/05 21:16:32 christos Exp $");
     26 
     27 #include "portable.h"
     28 
     29 #ifdef SLAPD_OVER_PROXYCACHE
     30 
     31 #include <stdio.h>
     32 
     33 #include <ac/string.h>
     34 #include <ac/time.h>
     35 
     36 #include "slap.h"
     37 #include "lutil.h"
     38 #include "ldap_rq.h"
     39 #include "ldap_avl.h"
     40 
     41 #include "../back-monitor/back-monitor.h"
     42 
     43 #include "slap-config.h"
     44 
     45 /*
     46  * Control that allows to access the private DB
     47  * instead of the public one
     48  */
     49 #define	PCACHE_CONTROL_PRIVDB		"1.3.6.1.4.1.4203.666.11.9.5.1"
     50 
     51 /*
     52  * Extended Operation that allows to remove a query from the cache
     53  */
     54 #define PCACHE_EXOP_QUERY_DELETE	"1.3.6.1.4.1.4203.666.11.9.6.1"
     55 
     56 /*
     57  * Monitoring
     58  */
     59 #define PCACHE_MONITOR
     60 
     61 /* query cache structs */
     62 /* query */
     63 
     64 typedef struct Query_s {
     65 	Filter* 	filter; 	/* Search Filter */
     66 	struct berval 	base; 		/* Search Base */
     67 	int 		scope;		/* Search scope */
     68 } Query;
     69 
     70 struct query_template_s;
     71 
     72 typedef struct Qbase_s {
     73 	TAvlnode *scopes[4];		/* threaded AVL trees of cached queries */
     74 	struct berval base;
     75 	int queries;
     76 } Qbase;
     77 
     78 /* struct representing a cached query */
     79 typedef struct cached_query_s {
     80 	Filter					*filter;
     81 	Filter					*first;
     82 	Qbase					*qbase;
     83 	int						scope;
     84 	struct berval			q_uuid;		/* query identifier */
     85 	int						q_sizelimit;
     86 	struct query_template_s		*qtemp;	/* template of the query */
     87 	time_t						expiry_time;	/* time till the query is considered invalid */
     88 	time_t						refresh_time;	/* time till the query is refreshed */
     89 	time_t						bindref_time;	/* time till the bind is refreshed */
     90 	int						bind_refcnt;	/* number of bind operation referencing this query */
     91 	unsigned long			answerable_cnt; /* how many times it was answerable */
     92 	int						refcnt;	/* references since last refresh */
     93 	int						in_lru;	/* query is in LRU list */
     94 	ldap_pvt_thread_mutex_t		answerable_cnt_mutex;
     95 	struct cached_query_s  		*next;  	/* next query in the template */
     96 	struct cached_query_s  		*prev;  	/* previous query in the template */
     97 	struct cached_query_s		*lru_up;	/* previous query in the LRU list */
     98 	struct cached_query_s		*lru_down;	/* next query in the LRU list */
     99 	ldap_pvt_thread_rdwr_t		rwlock;
    100 } CachedQuery;
    101 
    102 /*
    103  * URL representation:
    104  *
    105  * ldap:///<base>??<scope>?<filter>?x-uuid=<uid>,x-template=<template>,x-attrset=<attrset>,x-expiry=<expiry>,x-refresh=<refresh>
    106  *
    107  * <base> ::= CachedQuery.qbase->base
    108  * <scope> ::= CachedQuery.scope
    109  * <filter> ::= filter2bv(CachedQuery.filter)
    110  * <uuid> ::= CachedQuery.q_uuid
    111  * <attrset> ::= CachedQuery.qtemp->attr_set_index
    112  * <expiry> ::= CachedQuery.expiry_time
    113  * <refresh> ::= CachedQuery.refresh_time
    114  *
    115  * quick hack: parse URI, call add_query() and then fix
    116  * CachedQuery.expiry_time and CachedQuery.q_uuid
    117  *
    118  * NOTE: if the <attrset> changes, all stored URLs will be invalidated.
    119  */
    120 
    121 /*
    122  * Represents a set of projected attributes.
    123  */
    124 
    125 struct attr_set {
    126 	struct query_template_s *templates;
    127 	AttributeName*	attrs; 		/* specifies the set */
    128 	unsigned	flags;
    129 #define	PC_CONFIGURED	(0x1)
    130 #define	PC_REFERENCED	(0x2)
    131 #define	PC_GOT_OC		(0x4)
    132 	int 		count;		/* number of attributes */
    133 };
    134 
    135 /* struct representing a query template
    136  * e.g. template string = &(cn=)(mail=)
    137  */
    138 typedef struct query_template_s {
    139 	struct query_template_s *qtnext;
    140 	struct query_template_s *qmnext;
    141 
    142 	Avlnode*		qbase;
    143 	CachedQuery* 	query;	        /* most recent query cached for the template */
    144 	CachedQuery* 	query_last;     /* oldest query cached for the template */
    145 	ldap_pvt_thread_rdwr_t t_rwlock; /* Rd/wr lock for accessing queries in the template */
    146 	struct berval	querystr;	/* Filter string corresponding to the QT */
    147 	struct berval	bindbase;	/* base DN for Bind request */
    148 	struct berval	bindfilterstr;	/* Filter string for Bind request */
    149 	struct berval	bindftemp;	/* bind filter template */
    150 	Filter		*bindfilter;
    151 	AttributeDescription **bindfattrs;	/* attrs to substitute in ftemp */
    152 
    153 	int			bindnattrs;		/* number of bindfattrs */
    154 	int			bindscope;
    155 	int 		attr_set_index; /* determines the projected attributes */
    156 	int 		no_of_queries;  /* Total number of queries in the template */
    157 	time_t		ttl;		/* TTL for the queries of this template */
    158 	time_t		negttl;		/* TTL for negative results */
    159 	time_t		limitttl;	/* TTL for sizelimit exceeding results */
    160 	time_t		ttr;	/* time to refresh */
    161 	time_t		bindttr;	/* TTR for cached binds */
    162 	struct attr_set t_attrs;	/* filter attrs + attr_set */
    163 } QueryTemplate;
    164 
    165 typedef enum {
    166 	PC_IGNORE = 0,
    167 	PC_POSITIVE,
    168 	PC_NEGATIVE,
    169 	PC_SIZELIMIT
    170 } pc_caching_reason_t;
    171 
    172 static const char *pc_caching_reason_str[] = {
    173 	"IGNORE",
    174 	"POSITIVE",
    175 	"NEGATIVE",
    176 	"SIZELIMIT",
    177 
    178 	NULL
    179 };
    180 
    181 struct query_manager_s;
    182 
    183 /* prototypes for functions for 1) query containment
    184  * 2) query addition, 3) cache replacement
    185  */
    186 typedef CachedQuery *(QCfunc)(Operation *op, struct query_manager_s*,
    187 	Query*, QueryTemplate*);
    188 typedef CachedQuery *(AddQueryfunc)(Operation *op, struct query_manager_s*,
    189 	Query*, QueryTemplate*, pc_caching_reason_t, int wlock);
    190 typedef void (CRfunc)(struct query_manager_s*, struct berval*);
    191 
    192 /* LDAP query cache */
    193 typedef struct query_manager_s {
    194 	struct attr_set* 	attr_sets;		/* possible sets of projected attributes */
    195 	QueryTemplate*	  	templates;		/* cacheable templates */
    196 
    197 	CachedQuery*		lru_top;		/* top and bottom of LRU list */
    198 	CachedQuery*		lru_bottom;
    199 
    200 	ldap_pvt_thread_mutex_t		lru_mutex;	/* mutex for accessing LRU list */
    201 
    202 	/* Query cache methods */
    203 	QCfunc			*qcfunc;			/* Query containment*/
    204 	CRfunc 			*crfunc;			/* cache replacement */
    205 	AddQueryfunc	*addfunc;			/* add query */
    206 } query_manager;
    207 
    208 /* LDAP query cache manager */
    209 typedef struct cache_manager_s {
    210 	BackendDB	db;	/* underlying database */
    211 	unsigned long	num_cached_queries; 		/* total number of cached queries */
    212 	unsigned long   max_queries;			/* upper bound on # of cached queries */
    213 	int		save_queries;			/* save cached queries across restarts */
    214 	int	check_cacheability;		/* check whether a query is cacheable */
    215 	int 	numattrsets;			/* number of attribute sets */
    216 	int 	cur_entries;			/* current number of entries cached */
    217 	int 	max_entries;			/* max number of entries cached */
    218 	int 	num_entries_limit;		/* max # of entries in a cacheable query */
    219 
    220 	char	response_cb;			/* install the response callback
    221 						 * at the tail of the callback list */
    222 #define PCACHE_RESPONSE_CB_HEAD	0
    223 #define PCACHE_RESPONSE_CB_TAIL	1
    224 	char	defer_db_open;			/* defer open for online add */
    225 	char	cache_binds;			/* cache binds or just passthru */
    226 
    227 	time_t	cc_period;		/* interval between successive consistency checks (sec) */
    228 #define PCACHE_CC_PAUSED	1
    229 #define PCACHE_CC_OFFLINE	2
    230 	int 	cc_paused;
    231 	void	*cc_arg;
    232 
    233 	ldap_pvt_thread_mutex_t		cache_mutex;
    234 
    235 	query_manager*   qm;	/* query cache managed by the cache manager */
    236 
    237 #ifdef PCACHE_MONITOR
    238 	void		*monitor_cb;
    239 	struct berval	monitor_ndn;
    240 #endif /* PCACHE_MONITOR */
    241 } cache_manager;
    242 
    243 #ifdef PCACHE_MONITOR
    244 static int pcache_monitor_db_init( BackendDB *be );
    245 static int pcache_monitor_db_open( BackendDB *be );
    246 static int pcache_monitor_db_close( BackendDB *be );
    247 static int pcache_monitor_db_destroy( BackendDB *be );
    248 #endif /* PCACHE_MONITOR */
    249 
    250 static int pcache_debug;
    251 
    252 #ifdef PCACHE_CONTROL_PRIVDB
    253 static int privDB_cid;
    254 #endif /* PCACHE_CONTROL_PRIVDB */
    255 
    256 static AttributeDescription	*ad_queryId, *ad_cachedQueryURL;
    257 
    258 #ifdef PCACHE_MONITOR
    259 static AttributeDescription	*ad_numQueries, *ad_numEntries;
    260 static ObjectClass		*oc_olmPCache;
    261 #endif /* PCACHE_MONITOR */
    262 
    263 static struct {
    264 	char			*name;
    265 	char			*oid;
    266 }		s_oid[] = {
    267 	{ "PCacheOID",			"1.3.6.1.4.1.4203.666.11.9.1" },
    268 	{ "PCacheAttributes",		"PCacheOID:1" },
    269 	{ "PCacheObjectClasses",	"PCacheOID:2" },
    270 
    271 	{ NULL }
    272 };
    273 
    274 static struct {
    275 	char	*desc;
    276 	AttributeDescription **adp;
    277 } s_ad[] = {
    278 	{ "( PCacheAttributes:1 "
    279 		"NAME 'pcacheQueryID' "
    280 		"DESC 'ID of query the entry belongs to, formatted as a UUID' "
    281 		"EQUALITY octetStringMatch "
    282 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.40{64} "
    283 		"NO-USER-MODIFICATION "
    284 		"USAGE directoryOperation )",
    285 		&ad_queryId },
    286 	{ "( PCacheAttributes:2 "
    287 		"NAME 'pcacheQueryURL' "
    288 		"DESC 'URI describing a cached query' "
    289 		"EQUALITY caseExactMatch "
    290 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 "
    291 		"NO-USER-MODIFICATION "
    292 		"USAGE directoryOperation )",
    293 		&ad_cachedQueryURL },
    294 #ifdef PCACHE_MONITOR
    295 	{ "( PCacheAttributes:3 "
    296 		"NAME 'pcacheNumQueries' "
    297 		"DESC 'Number of cached queries' "
    298 		"EQUALITY integerMatch "
    299 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 "
    300 		"NO-USER-MODIFICATION "
    301 		"USAGE directoryOperation )",
    302 		&ad_numQueries },
    303 	{ "( PCacheAttributes:4 "
    304 		"NAME 'pcacheNumEntries' "
    305 		"DESC 'Number of cached entries' "
    306 		"EQUALITY integerMatch "
    307 		"SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 "
    308 		"NO-USER-MODIFICATION "
    309 		"USAGE directoryOperation )",
    310 		&ad_numEntries },
    311 #endif /* PCACHE_MONITOR */
    312 
    313 	{ NULL }
    314 };
    315 
    316 static struct {
    317 	char		*desc;
    318 	ObjectClass	**ocp;
    319 }		s_oc[] = {
    320 #ifdef PCACHE_MONITOR
    321 	/* augments an existing object, so it must be AUXILIARY */
    322 	{ "( PCacheObjectClasses:1 "
    323 		"NAME ( 'olmPCache' ) "
    324 		"SUP top AUXILIARY "
    325 		"MAY ( "
    326 			"pcacheQueryURL "
    327 			"$ pcacheNumQueries "
    328 			"$ pcacheNumEntries "
    329 			" ) )",
    330 		&oc_olmPCache },
    331 #endif /* PCACHE_MONITOR */
    332 
    333 	{ NULL }
    334 };
    335 
    336 static int
    337 filter2template(
    338 	Operation		*op,
    339 	Filter			*f,
    340 	struct			berval *fstr );
    341 
    342 static CachedQuery *
    343 add_query(
    344 	Operation *op,
    345 	query_manager* qm,
    346 	Query* query,
    347 	QueryTemplate *templ,
    348 	pc_caching_reason_t why,
    349 	int wlock);
    350 
    351 static int
    352 remove_query_data(
    353 	Operation	*op,
    354 	struct berval	*query_uuid );
    355 
    356 /*
    357  * Turn a cached query into its URL representation
    358  */
    359 static int
    360 query2url( Operation *op, CachedQuery *q, struct berval *urlbv, int dolock )
    361 {
    362 	struct berval	bv_scope,
    363 			bv_filter;
    364 	char		attrset_buf[ LDAP_PVT_INTTYPE_CHARS( unsigned long ) ],
    365 			expiry_buf[ LDAP_PVT_INTTYPE_CHARS( unsigned long ) ],
    366 			refresh_buf[ LDAP_PVT_INTTYPE_CHARS( unsigned long ) ],
    367 			answerable_buf[ LDAP_PVT_INTTYPE_CHARS( unsigned long ) ],
    368 			*ptr;
    369 	ber_len_t	attrset_len,
    370 			expiry_len,
    371 			refresh_len,
    372 			answerable_len;
    373 
    374 	if ( dolock ) {
    375 		ldap_pvt_thread_rdwr_rlock( &q->rwlock );
    376 	}
    377 
    378 	ldap_pvt_scope2bv( q->scope, &bv_scope );
    379 	filter2bv_x( op, q->filter, &bv_filter );
    380 	attrset_len = sprintf( attrset_buf,
    381 		"%lu", (unsigned long)q->qtemp->attr_set_index );
    382 	expiry_len = sprintf( expiry_buf,
    383 		"%lu", (unsigned long)q->expiry_time );
    384 	answerable_len = snprintf( answerable_buf, sizeof( answerable_buf ),
    385 		"%lu", q->answerable_cnt );
    386 	if ( q->refresh_time )
    387 		refresh_len = sprintf( refresh_buf,
    388 			"%lu", (unsigned long)q->refresh_time );
    389 	else
    390 		refresh_len = 0;
    391 
    392 	urlbv->bv_len = STRLENOF( "ldap:///" )
    393 		+ q->qbase->base.bv_len
    394 		+ STRLENOF( "??" )
    395 		+ bv_scope.bv_len
    396 		+ STRLENOF( "?" )
    397 		+ bv_filter.bv_len
    398 		+ STRLENOF( "?x-uuid=" )
    399 		+ q->q_uuid.bv_len
    400 		+ STRLENOF( ",x-attrset=" )
    401 		+ attrset_len
    402 		+ STRLENOF( ",x-expiry=" )
    403 		+ expiry_len
    404 		+ STRLENOF( ",x-answerable=" )
    405 		+ answerable_len;
    406 	if ( refresh_len )
    407 		urlbv->bv_len += STRLENOF( ",x-refresh=" )
    408 		+ refresh_len;
    409 
    410 	ptr = urlbv->bv_val = ber_memalloc_x( urlbv->bv_len + 1, op->o_tmpmemctx );
    411 	ptr = lutil_strcopy( ptr, "ldap:///" );
    412 	ptr = lutil_strcopy( ptr, q->qbase->base.bv_val );
    413 	ptr = lutil_strcopy( ptr, "??" );
    414 	ptr = lutil_strcopy( ptr, bv_scope.bv_val );
    415 	ptr = lutil_strcopy( ptr, "?" );
    416 	ptr = lutil_strcopy( ptr, bv_filter.bv_val );
    417 	ptr = lutil_strcopy( ptr, "?x-uuid=" );
    418 	ptr = lutil_strcopy( ptr, q->q_uuid.bv_val );
    419 	ptr = lutil_strcopy( ptr, ",x-attrset=" );
    420 	ptr = lutil_strcopy( ptr, attrset_buf );
    421 	ptr = lutil_strcopy( ptr, ",x-expiry=" );
    422 	ptr = lutil_strcopy( ptr, expiry_buf );
    423 	ptr = lutil_strcopy( ptr, ",x-answerable=" );
    424 	ptr = lutil_strcopy( ptr, answerable_buf );
    425 	if ( refresh_len ) {
    426 		ptr = lutil_strcopy( ptr, ",x-refresh=" );
    427 		ptr = lutil_strcopy( ptr, refresh_buf );
    428 	}
    429 
    430 	ber_memfree_x( bv_filter.bv_val, op->o_tmpmemctx );
    431 
    432 	if ( dolock ) {
    433 		ldap_pvt_thread_rdwr_runlock( &q->rwlock );
    434 	}
    435 
    436 	return 0;
    437 }
    438 
    439 /* Find and record the empty filter clauses */
    440 
    441 static int
    442 ftemp_attrs( struct berval *ftemp, struct berval *template,
    443 	AttributeDescription ***ret, const char **text )
    444 {
    445 	int i;
    446 	int attr_cnt=0;
    447 	struct berval bv;
    448 	char *p1, *p2, *t1;
    449 	AttributeDescription *ad;
    450 	AttributeDescription **descs = NULL;
    451 	char *temp2;
    452 
    453 	temp2 = ch_malloc( ftemp->bv_len + 1 );
    454 	p1 = ftemp->bv_val;
    455 	t1 = temp2;
    456 
    457 	*ret = NULL;
    458 
    459 	for (;;) {
    460 		while ( *p1 == '(' || *p1 == '&' || *p1 == '|' || *p1 == ')' )
    461 			*t1++ = *p1++;
    462 
    463 		p2 = strchr( p1, '=' );
    464 		if ( !p2 ) {
    465 			if ( !descs ) {
    466 				ch_free( temp2 );
    467 				return -1;
    468 			}
    469 			break;
    470 		}
    471 		i = p2 - p1;
    472 		AC_MEMCPY( t1, p1, i );
    473 		t1 += i;
    474 		*t1++ = '=';
    475 
    476 		if ( p2[-1] == '<' || p2[-1] == '>' ) p2--;
    477 		bv.bv_val = p1;
    478 		bv.bv_len = p2 - p1;
    479 		ad = NULL;
    480 		i = slap_bv2ad( &bv, &ad, text );
    481 		if ( i ) {
    482 			ch_free( temp2 );
    483 			ch_free( descs );
    484 			return -1;
    485 		}
    486 		if ( *p2 == '<' || *p2 == '>' ) p2++;
    487 		if ( p2[1] != ')' ) {
    488 			p2++;
    489 			while ( *p2 != ')' ) p2++;
    490 			p1 = p2;
    491 			continue;
    492 		}
    493 
    494 		descs = (AttributeDescription **)ch_realloc(descs,
    495 				(attr_cnt + 2)*sizeof(AttributeDescription *));
    496 
    497 		descs[attr_cnt++] = ad;
    498 
    499 		p1 = p2+1;
    500 	}
    501 	*t1 = '\0';
    502 	descs[attr_cnt] = NULL;
    503 	*ret = descs;
    504 	template->bv_val = temp2;
    505 	template->bv_len = t1 - temp2;
    506 	return attr_cnt;
    507 }
    508 
    509 static int
    510 template_attrs( char *template, struct attr_set *set, AttributeName **ret,
    511 	const char **text )
    512 {
    513 	int got_oc = 0;
    514 	int alluser = 0;
    515 	int allop = 0;
    516 	int i;
    517 	int attr_cnt;
    518 	int t_cnt = 0;
    519 	struct berval bv;
    520 	char *p1, *p2;
    521 	AttributeDescription *ad;
    522 	AttributeName *attrs;
    523 
    524 	p1 = template;
    525 
    526 	*ret = NULL;
    527 
    528 	attrs = ch_calloc( set->count + 1, sizeof(AttributeName) );
    529 	for ( i=0; i < set->count; i++ )
    530 		attrs[i] = set->attrs[i];
    531 	attr_cnt = i;
    532 	alluser = an_find( attrs, slap_bv_all_user_attrs );
    533 	allop = an_find( attrs, slap_bv_all_operational_attrs );
    534 
    535 	for (;;) {
    536 		while ( *p1 == '(' || *p1 == '&' || *p1 == '|' || *p1 == ')' ) p1++;
    537 		p2 = strchr( p1, '=' );
    538 		if ( !p2 )
    539 			break;
    540 		if ( p2[-1] == '<' || p2[-1] == '>' ) p2--;
    541 		bv.bv_val = p1;
    542 		bv.bv_len = p2 - p1;
    543 		ad = NULL;
    544 		i = slap_bv2ad( &bv, &ad, text );
    545 		if ( i ) {
    546 			ch_free( attrs );
    547 			return -1;
    548 		}
    549 		t_cnt++;
    550 
    551 		if ( ad == slap_schema.si_ad_objectClass )
    552 			got_oc = 1;
    553 
    554 		if ( is_at_operational(ad->ad_type)) {
    555 			if ( allop ) {
    556 				goto bottom;
    557 			}
    558 		} else if ( alluser ) {
    559 			goto bottom;
    560 		}
    561 		if ( !ad_inlist( ad, attrs )) {
    562 			attrs = (AttributeName *)ch_realloc(attrs,
    563 					(attr_cnt + 2)*sizeof(AttributeName));
    564 
    565 			attrs[attr_cnt].an_desc = ad;
    566 			attrs[attr_cnt].an_name = ad->ad_cname;
    567 			attrs[attr_cnt].an_oc = NULL;
    568 			attrs[attr_cnt].an_flags = 0;
    569 			BER_BVZERO( &attrs[attr_cnt+1].an_name );
    570 			attr_cnt++;
    571 		}
    572 
    573 bottom:
    574 		p1 = p2+2;
    575 	}
    576 	if ( !t_cnt ) {
    577 		*text = "couldn't parse template";
    578 		ch_free(attrs);
    579 		return -1;
    580 	}
    581 	if ( !got_oc && !( set->flags & PC_GOT_OC )) {
    582 		attrs = (AttributeName *)ch_realloc(attrs,
    583 				(attr_cnt + 2)*sizeof(AttributeName));
    584 
    585 		ad = slap_schema.si_ad_objectClass;
    586 		attrs[attr_cnt].an_desc = ad;
    587 		attrs[attr_cnt].an_name = ad->ad_cname;
    588 		attrs[attr_cnt].an_oc = NULL;
    589 		attrs[attr_cnt].an_flags = 0;
    590 		BER_BVZERO( &attrs[attr_cnt+1].an_name );
    591 		attr_cnt++;
    592 	}
    593 	*ret = attrs;
    594 	return attr_cnt;
    595 }
    596 
    597 /*
    598  * Turn an URL representing a formerly cached query into a cached query,
    599  * and try to cache it
    600  */
    601 static int
    602 url2query(
    603 	char		*url,
    604 	Operation	*op,
    605 	query_manager	*qm )
    606 {
    607 	Query		query = { 0 };
    608 	QueryTemplate	*qt;
    609 	CachedQuery	*cq;
    610 	LDAPURLDesc	*lud = NULL;
    611 	struct berval	base,
    612 			tempstr = BER_BVNULL,
    613 			uuid = BER_BVNULL;
    614 	int		attrset;
    615 	time_t		expiry_time;
    616 	time_t		refresh_time;
    617 	unsigned long	answerable_cnt;
    618 	int		i,
    619 			got = 0,
    620 #define GOT_UUID	0x1U
    621 #define GOT_ATTRSET	0x2U
    622 #define GOT_EXPIRY	0x4U
    623 #define GOT_ANSWERABLE	0x8U
    624 #define GOT_REFRESH	0x10U
    625 #define GOT_ALL		(GOT_UUID|GOT_ATTRSET|GOT_EXPIRY|GOT_ANSWERABLE)
    626 			rc = 0;
    627 
    628 	rc = ldap_url_parse( url, &lud );
    629 	if ( rc != LDAP_URL_SUCCESS ) {
    630 		return -1;
    631 	}
    632 
    633 	/* non-allowed fields */
    634 	if ( lud->lud_host != NULL ) {
    635 		rc = 1;
    636 		goto error;
    637 	}
    638 
    639 	if ( lud->lud_attrs != NULL ) {
    640 		rc = 1;
    641 		goto error;
    642 	}
    643 
    644 	/* be pedantic */
    645 	if ( strcmp( lud->lud_scheme, "ldap" ) != 0 ) {
    646 		rc = 1;
    647 		goto error;
    648 	}
    649 
    650 	/* required fields */
    651 	if ( lud->lud_dn == NULL || lud->lud_dn[ 0 ] == '\0' ) {
    652 		rc = 1;
    653 		goto error;
    654 	}
    655 
    656 	switch ( lud->lud_scope ) {
    657 	case LDAP_SCOPE_BASE:
    658 	case LDAP_SCOPE_ONELEVEL:
    659 	case LDAP_SCOPE_SUBTREE:
    660 	case LDAP_SCOPE_SUBORDINATE:
    661 		break;
    662 
    663 	default:
    664 		rc = 1;
    665 		goto error;
    666 	}
    667 
    668 	if ( lud->lud_filter == NULL || lud->lud_filter[ 0 ] == '\0' ) {
    669 		rc = 1;
    670 		goto error;
    671 	}
    672 
    673 	if ( lud->lud_exts == NULL ) {
    674 		rc = 1;
    675 		goto error;
    676 	}
    677 
    678 	for ( i = 0; lud->lud_exts[ i ] != NULL; i++ ) {
    679 		if ( strncmp( lud->lud_exts[ i ], "x-uuid=", STRLENOF( "x-uuid=" ) ) == 0 ) {
    680 			struct berval	tmpUUID;
    681 			Syntax		*syn_UUID = slap_schema.si_ad_entryUUID->ad_type->sat_syntax;
    682 
    683 			if ( got & GOT_UUID ) {
    684 				rc = 1;
    685 				goto error;
    686 			}
    687 
    688 			ber_str2bv( &lud->lud_exts[ i ][ STRLENOF( "x-uuid=" ) ], 0, 0, &tmpUUID );
    689 			if ( !BER_BVISEMPTY( &tmpUUID ) ) {
    690 				rc = syn_UUID->ssyn_pretty( syn_UUID, &tmpUUID, &uuid, NULL );
    691 				if ( rc != LDAP_SUCCESS ) {
    692 					goto error;
    693 				}
    694 			}
    695 			got |= GOT_UUID;
    696 
    697 		} else if ( strncmp( lud->lud_exts[ i ], "x-attrset=", STRLENOF( "x-attrset=" ) ) == 0 ) {
    698 			if ( got & GOT_ATTRSET ) {
    699 				rc = 1;
    700 				goto error;
    701 			}
    702 
    703 			rc = lutil_atoi( &attrset, &lud->lud_exts[ i ][ STRLENOF( "x-attrset=" ) ] );
    704 			if ( rc ) {
    705 				goto error;
    706 			}
    707 			got |= GOT_ATTRSET;
    708 
    709 		} else if ( strncmp( lud->lud_exts[ i ], "x-expiry=", STRLENOF( "x-expiry=" ) ) == 0 ) {
    710 			unsigned long l;
    711 
    712 			if ( got & GOT_EXPIRY ) {
    713 				rc = 1;
    714 				goto error;
    715 			}
    716 
    717 			rc = lutil_atoul( &l, &lud->lud_exts[ i ][ STRLENOF( "x-expiry=" ) ] );
    718 			if ( rc ) {
    719 				goto error;
    720 			}
    721 			expiry_time = (time_t)l;
    722 			got |= GOT_EXPIRY;
    723 
    724 		} else if ( strncmp( lud->lud_exts[ i ], "x-answerable=", STRLENOF( "x-answerable=" ) ) == 0 ) {
    725 			if ( got & GOT_ANSWERABLE ) {
    726 				rc = 1;
    727 				goto error;
    728 			}
    729 
    730 			rc = lutil_atoul( &answerable_cnt, &lud->lud_exts[ i ][ STRLENOF( "x-answerable=" ) ] );
    731 			if ( rc ) {
    732 				goto error;
    733 			}
    734 			got |= GOT_ANSWERABLE;
    735 
    736 		} else if ( strncmp( lud->lud_exts[ i ], "x-refresh=", STRLENOF( "x-refresh=" ) ) == 0 ) {
    737 			unsigned long l;
    738 
    739 			if ( got & GOT_REFRESH ) {
    740 				rc = 1;
    741 				goto error;
    742 			}
    743 
    744 			rc = lutil_atoul( &l, &lud->lud_exts[ i ][ STRLENOF( "x-refresh=" ) ] );
    745 			if ( rc ) {
    746 				goto error;
    747 			}
    748 			refresh_time = (time_t)l;
    749 			got |= GOT_REFRESH;
    750 
    751 		} else {
    752 			rc = -1;
    753 			goto error;
    754 		}
    755 	}
    756 
    757 	if ( (got & GOT_ALL) != GOT_ALL) {
    758 		rc = 1;
    759 		goto error;
    760 	}
    761 
    762 	if ( !(got & GOT_REFRESH ))
    763 		refresh_time = 0;
    764 
    765 	/* ignore expired queries */
    766 	if ( expiry_time <= slap_get_time()) {
    767 		Operation	op2 = *op;
    768 
    769 		memset( &op2.oq_search, 0, sizeof( op2.oq_search ) );
    770 
    771 		(void)remove_query_data( &op2, &uuid );
    772 
    773 		rc = 0;
    774 
    775 	} else {
    776 		ber_str2bv( lud->lud_dn, 0, 0, &base );
    777 		rc = dnNormalize( 0, NULL, NULL, &base, &query.base, NULL );
    778 		if ( rc != LDAP_SUCCESS ) {
    779 			goto error;
    780 		}
    781 		query.scope = lud->lud_scope;
    782 		query.filter = str2filter( lud->lud_filter );
    783 		if ( query.filter == NULL ) {
    784 			rc = -1;
    785 			goto error;
    786 		}
    787 
    788 		tempstr.bv_val = ch_malloc( strlen( lud->lud_filter ) + 1 );
    789 		tempstr.bv_len = 0;
    790 		if ( filter2template( op, query.filter, &tempstr ) ) {
    791 			ch_free( tempstr.bv_val );
    792 			rc = -1;
    793 			goto error;
    794 		}
    795 
    796 		/* check for query containment */
    797 		qt = qm->attr_sets[attrset].templates;
    798 		for ( ; qt; qt = qt->qtnext ) {
    799 			/* find if template i can potentially answer tempstr */
    800 			if ( bvmatch( &qt->querystr, &tempstr ) ) {
    801 				break;
    802 			}
    803 		}
    804 
    805 		if ( qt == NULL ) {
    806 			rc = 1;
    807 			goto error;
    808 		}
    809 
    810 		if (BER_BVISNULL( &uuid )) {
    811 		  cq = add_query( op, qm, &query, qt, PC_NEGATIVE, 0 );
    812 		} else {
    813 		  cq = add_query( op, qm, &query, qt, PC_POSITIVE, 0 );
    814 		}
    815 		if ( cq != NULL ) {
    816 			cq->expiry_time = expiry_time;
    817 			cq->refresh_time = refresh_time;
    818 			cq->q_uuid = uuid;
    819 			cq->answerable_cnt = answerable_cnt;
    820 			cq->refcnt = 0;
    821 
    822 			/* it's now into cq->filter */
    823 			BER_BVZERO( &uuid );
    824 			query.filter = NULL;
    825 
    826 		} else {
    827 			rc = 1;
    828 		}
    829 	}
    830 
    831 error:;
    832 	if ( query.filter != NULL ) filter_free( query.filter );
    833 	if ( !BER_BVISNULL( &tempstr ) ) ch_free( tempstr.bv_val );
    834 	if ( !BER_BVISNULL( &query.base ) ) ch_free( query.base.bv_val );
    835 	if ( !BER_BVISNULL( &uuid ) ) ch_free( uuid.bv_val );
    836 	if ( lud != NULL ) ldap_free_urldesc( lud );
    837 
    838 	return rc;
    839 }
    840 
    841 /* Return 1 for an added entry, else 0 */
    842 static int
    843 merge_entry(
    844 	Operation		*op,
    845 	Entry			*e,
    846 	int			dup,
    847 	struct berval*		query_uuid )
    848 {
    849 	int		rc;
    850 	Modifications* modlist = NULL;
    851 	const char* 	text = NULL;
    852 	Attribute		*attr;
    853 	char			textbuf[SLAP_TEXT_BUFLEN];
    854 	size_t			textlen = sizeof(textbuf);
    855 
    856 	SlapReply sreply = {REP_RESULT};
    857 
    858 	slap_callback cb = { NULL, slap_null_cb, NULL, NULL };
    859 
    860 	if ( dup )
    861 		e = entry_dup( e );
    862 	attr = e->e_attrs;
    863 	e->e_attrs = NULL;
    864 
    865 	/* add queryId attribute */
    866 	attr_merge_one( e, ad_queryId, query_uuid, NULL );
    867 
    868 	/* append the attribute list from the fetched entry */
    869 	e->e_attrs->a_next = attr;
    870 
    871 	op->o_tag = LDAP_REQ_ADD;
    872 	op->o_protocol = LDAP_VERSION3;
    873 	op->o_callback = &cb;
    874 	op->o_time = slap_get_time();
    875 	op->o_do_not_cache = 1;
    876 
    877 	op->ora_e = e;
    878 	op->o_req_dn = e->e_name;
    879 	op->o_req_ndn = e->e_nname;
    880 	rc = op->o_bd->be_add( op, &sreply );
    881 
    882 	if ( rc != LDAP_SUCCESS ) {
    883 		if ( rc == LDAP_ALREADY_EXISTS ) {
    884 			rs_reinit( &sreply, REP_RESULT );
    885 			slap_entry2mods( e, &modlist, &text, textbuf, textlen );
    886 			modlist->sml_op = LDAP_MOD_ADD;
    887 			op->o_tag = LDAP_REQ_MODIFY;
    888 			op->orm_modlist = modlist;
    889 			op->o_managedsait = SLAP_CONTROL_CRITICAL;
    890 			op->o_bd->be_modify( op, &sreply );
    891 			slap_mods_free( modlist, 1 );
    892 		} else if ( rc == LDAP_REFERRAL ||
    893 					rc == LDAP_NO_SUCH_OBJECT ) {
    894 			syncrepl_add_glue( op, e );
    895 			e = NULL;
    896 			rc = 1;
    897 		}
    898 		if ( e ) {
    899 			entry_free( e );
    900 			rc = 0;
    901 		}
    902 	} else {
    903 		if ( op->ora_e == e )
    904 			entry_free( e );
    905 		rc = 1;
    906 	}
    907 
    908 	return rc;
    909 }
    910 
    911 /* Length-ordered sort on normalized DNs */
    912 static int pcache_dn_cmp( const void *v1, const void *v2 )
    913 {
    914 	const Qbase *q1 = v1, *q2 = v2;
    915 
    916 	int rc = q1->base.bv_len - q2->base.bv_len;
    917 	if ( rc == 0 )
    918 		rc = strncmp( q1->base.bv_val, q2->base.bv_val, q1->base.bv_len );
    919 	return rc;
    920 }
    921 
    922 static int lex_bvcmp( struct berval *bv1, struct berval *bv2 )
    923 {
    924 	int len, dif;
    925 	dif = bv1->bv_len - bv2->bv_len;
    926 	len = bv1->bv_len;
    927 	if ( dif > 0 ) len -= dif;
    928 	len = memcmp( bv1->bv_val, bv2->bv_val, len );
    929 	if ( !len )
    930 		len = dif;
    931 	return len;
    932 }
    933 
    934 /* compare the current value in each filter */
    935 static int pcache_filter_cmp( Filter *f1, Filter *f2 )
    936 {
    937 	int rc, weight1, weight2;
    938 
    939 	switch( f1->f_choice ) {
    940 	case LDAP_FILTER_AND:
    941 	case LDAP_FILTER_OR:
    942 		weight1 = 0;
    943 		break;
    944 	case LDAP_FILTER_PRESENT:
    945 		weight1 = 1;
    946 		break;
    947 	case LDAP_FILTER_EQUALITY:
    948 	case LDAP_FILTER_GE:
    949 	case LDAP_FILTER_LE:
    950 		weight1 = 2;
    951 		break;
    952 	default:
    953 		weight1 = 3;
    954 	}
    955 	switch( f2->f_choice ) {
    956 	case LDAP_FILTER_AND:
    957 	case LDAP_FILTER_OR:
    958 		weight2 = 0;
    959 		break;
    960 	case LDAP_FILTER_PRESENT:
    961 		weight2 = 1;
    962 		break;
    963 	case LDAP_FILTER_EQUALITY:
    964 	case LDAP_FILTER_GE:
    965 	case LDAP_FILTER_LE:
    966 		weight2 = 2;
    967 		break;
    968 	default:
    969 		weight2 = 3;
    970 	}
    971 	rc = weight1 - weight2;
    972 	if ( !rc ) {
    973 		switch( weight1 ) {
    974 		case 0:
    975 			rc = pcache_filter_cmp( f1->f_and, f2->f_and );
    976 			break;
    977 		case 1:
    978 			break;
    979 		case 2:
    980 			rc = lex_bvcmp( &f1->f_av_value, &f2->f_av_value );
    981 			break;
    982 		case 3:
    983 			if ( f1->f_choice == LDAP_FILTER_SUBSTRINGS ) {
    984 				rc = 0;
    985 				if ( !BER_BVISNULL( &f1->f_sub_initial )) {
    986 					if ( !BER_BVISNULL( &f2->f_sub_initial )) {
    987 						rc = lex_bvcmp( &f1->f_sub_initial,
    988 							&f2->f_sub_initial );
    989 					} else {
    990 						rc = 1;
    991 					}
    992 				} else if ( !BER_BVISNULL( &f2->f_sub_initial )) {
    993 					rc = -1;
    994 				}
    995 				if ( rc ) break;
    996 				if ( f1->f_sub_any ) {
    997 					if ( f2->f_sub_any ) {
    998 						rc = lex_bvcmp( f1->f_sub_any,
    999 							f2->f_sub_any );
   1000 					} else {
   1001 						rc = 1;
   1002 					}
   1003 				} else if ( f2->f_sub_any ) {
   1004 					rc = -1;
   1005 				}
   1006 				if ( rc ) break;
   1007 				if ( !BER_BVISNULL( &f1->f_sub_final )) {
   1008 					if ( !BER_BVISNULL( &f2->f_sub_final )) {
   1009 						rc = lex_bvcmp( &f1->f_sub_final,
   1010 							&f2->f_sub_final );
   1011 					} else {
   1012 						rc = 1;
   1013 					}
   1014 				} else if ( !BER_BVISNULL( &f2->f_sub_final )) {
   1015 					rc = -1;
   1016 				}
   1017 			} else {
   1018 				rc = lex_bvcmp( &f1->f_mr_value,
   1019 					&f2->f_mr_value );
   1020 			}
   1021 			break;
   1022 		}
   1023 		while ( !rc ) {
   1024 			f1 = f1->f_next;
   1025 			f2 = f2->f_next;
   1026 			if ( f1 || f2 ) {
   1027 				if ( !f1 )
   1028 					rc = -1;
   1029 				else if ( !f2 )
   1030 					rc = 1;
   1031 				else {
   1032 					rc = pcache_filter_cmp( f1, f2 );
   1033 				}
   1034 			} else {
   1035 				break;
   1036 			}
   1037 		}
   1038 	}
   1039 	return rc;
   1040 }
   1041 
   1042 /* compare filters in each query */
   1043 static int pcache_query_cmp( const void *v1, const void *v2 )
   1044 {
   1045 	const CachedQuery *q1 = v1, *q2 =v2;
   1046 	return pcache_filter_cmp( q1->filter, q2->filter );
   1047 }
   1048 
   1049 /* add query on top of LRU list */
   1050 static void
   1051 add_query_on_top (query_manager* qm, CachedQuery* qc)
   1052 {
   1053 	CachedQuery* top = qm->lru_top;
   1054 
   1055 	qc->in_lru = 1;
   1056 	qm->lru_top = qc;
   1057 
   1058 	if (top)
   1059 		top->lru_up = qc;
   1060 	else
   1061 		qm->lru_bottom = qc;
   1062 
   1063 	qc->lru_down = top;
   1064 	qc->lru_up = NULL;
   1065 	Debug( pcache_debug, "Base of added query = %s\n",
   1066 			qc->qbase->base.bv_val );
   1067 }
   1068 
   1069 /* remove_query from LRU list */
   1070 
   1071 static void
   1072 remove_query (query_manager* qm, CachedQuery* qc)
   1073 {
   1074 	CachedQuery* up;
   1075 	CachedQuery* down;
   1076 
   1077 	if (!qc || !qc->in_lru)
   1078 		return;
   1079 
   1080 	qc->in_lru = 0;
   1081 	up = qc->lru_up;
   1082 	down = qc->lru_down;
   1083 
   1084 	if (!up)
   1085 		qm->lru_top = down;
   1086 
   1087 	if (!down)
   1088 		qm->lru_bottom = up;
   1089 
   1090 	if (down)
   1091 		down->lru_up = up;
   1092 
   1093 	if (up)
   1094 		up->lru_down = down;
   1095 
   1096 	qc->lru_up = qc->lru_down = NULL;
   1097 }
   1098 
   1099 /* find and remove string2 from string1
   1100  * from start if position = 1,
   1101  * from end if position = 3,
   1102  * from anywhere if position = 2
   1103  * string1 is overwritten if position = 2.
   1104  */
   1105 
   1106 static int
   1107 find_and_remove(struct berval* ber1, struct berval* ber2, int position)
   1108 {
   1109 	int ret=0;
   1110 
   1111 	if ( !ber2->bv_val )
   1112 		return 1;
   1113 	if ( !ber1->bv_val )
   1114 		return 0;
   1115 
   1116 	switch( position ) {
   1117 	case 1:
   1118 		if ( ber1->bv_len >= ber2->bv_len && !memcmp( ber1->bv_val,
   1119 			ber2->bv_val, ber2->bv_len )) {
   1120 			ret = 1;
   1121 			ber1->bv_val += ber2->bv_len;
   1122 			ber1->bv_len -= ber2->bv_len;
   1123 		}
   1124 		break;
   1125 	case 2: {
   1126 		char *temp;
   1127 		ber1->bv_val[ber1->bv_len] = '\0';
   1128 		temp = strstr( ber1->bv_val, ber2->bv_val );
   1129 		if ( temp ) {
   1130 			strcpy( temp, temp+ber2->bv_len );
   1131 			ber1->bv_len -= ber2->bv_len;
   1132 			ret = 1;
   1133 		}
   1134 		break;
   1135 		}
   1136 	case 3:
   1137 		if ( ber1->bv_len >= ber2->bv_len &&
   1138 			!memcmp( ber1->bv_val+ber1->bv_len-ber2->bv_len, ber2->bv_val,
   1139 				ber2->bv_len )) {
   1140 			ret = 1;
   1141 			ber1->bv_len -= ber2->bv_len;
   1142 		}
   1143 		break;
   1144 	}
   1145 	return ret;
   1146 }
   1147 
   1148 
   1149 static struct berval*
   1150 merge_init_final(Operation *op, struct berval* init, struct berval* any,
   1151 	struct berval* final)
   1152 {
   1153 	struct berval* merged, *temp;
   1154 	int i, any_count, count;
   1155 
   1156 	for (any_count=0; any && any[any_count].bv_val; any_count++)
   1157 		;
   1158 
   1159 	count = any_count;
   1160 
   1161 	if (init->bv_val)
   1162 		count++;
   1163 	if (final->bv_val)
   1164 		count++;
   1165 
   1166 	merged = (struct berval*)op->o_tmpalloc( (count+1)*sizeof(struct berval),
   1167 		op->o_tmpmemctx );
   1168 	temp = merged;
   1169 
   1170 	if (init->bv_val) {
   1171 		ber_dupbv_x( temp, init, op->o_tmpmemctx );
   1172 		temp++;
   1173 	}
   1174 
   1175 	for (i=0; i<any_count; i++) {
   1176 		ber_dupbv_x( temp, any, op->o_tmpmemctx );
   1177 		temp++; any++;
   1178 	}
   1179 
   1180 	if (final->bv_val){
   1181 		ber_dupbv_x( temp, final, op->o_tmpmemctx );
   1182 		temp++;
   1183 	}
   1184 	BER_BVZERO( temp );
   1185 	return merged;
   1186 }
   1187 
   1188 /* Each element in stored must be found in incoming. Incoming is overwritten.
   1189  */
   1190 static int
   1191 strings_containment(struct berval* stored, struct berval* incoming)
   1192 {
   1193 	struct berval* element;
   1194 	int k=0;
   1195 	int j, rc = 0;
   1196 
   1197 	for ( element=stored; element->bv_val != NULL; element++ ) {
   1198 		for (j = k; incoming[j].bv_val != NULL; j++) {
   1199 			if (find_and_remove(&(incoming[j]), element, 2)) {
   1200 				k = j;
   1201 				rc = 1;
   1202 				break;
   1203 			}
   1204 			rc = 0;
   1205 		}
   1206 		if ( rc ) {
   1207 			continue;
   1208 		} else {
   1209 			return 0;
   1210 		}
   1211 	}
   1212 	return 1;
   1213 }
   1214 
   1215 static int
   1216 substr_containment_substr(Operation *op, Filter* stored, Filter* incoming)
   1217 {
   1218 	int rc = 0;
   1219 
   1220 	struct berval init_incoming;
   1221 	struct berval final_incoming;
   1222 	struct berval *remaining_incoming = NULL;
   1223 
   1224 	if ((!(incoming->f_sub_initial.bv_val) && (stored->f_sub_initial.bv_val))
   1225 	   || (!(incoming->f_sub_final.bv_val) && (stored->f_sub_final.bv_val)))
   1226 		return 0;
   1227 
   1228 	init_incoming = incoming->f_sub_initial;
   1229 	final_incoming =  incoming->f_sub_final;
   1230 
   1231 	if (find_and_remove(&init_incoming,
   1232 			&(stored->f_sub_initial), 1) && find_and_remove(&final_incoming,
   1233 			&(stored->f_sub_final), 3))
   1234 	{
   1235 		if (stored->f_sub_any == NULL) {
   1236 			rc = 1;
   1237 			goto final;
   1238 		}
   1239 		remaining_incoming = merge_init_final(op, &init_incoming,
   1240 						incoming->f_sub_any, &final_incoming);
   1241 		rc = strings_containment(stored->f_sub_any, remaining_incoming);
   1242 		ber_bvarray_free_x( remaining_incoming, op->o_tmpmemctx );
   1243 	}
   1244 final:
   1245 	return rc;
   1246 }
   1247 
   1248 static int
   1249 substr_containment_equality(Operation *op, Filter* stored, Filter* incoming)
   1250 {
   1251 	struct berval incoming_val[2];
   1252 	int rc = 0;
   1253 
   1254 	incoming_val[1] = incoming->f_av_value;
   1255 
   1256 	if (find_and_remove(incoming_val+1,
   1257 			&(stored->f_sub_initial), 1) && find_and_remove(incoming_val+1,
   1258 			&(stored->f_sub_final), 3)) {
   1259 		if (stored->f_sub_any == NULL){
   1260 			rc = 1;
   1261 			goto final;
   1262 		}
   1263 		ber_dupbv_x( incoming_val, incoming_val+1, op->o_tmpmemctx );
   1264 		BER_BVZERO( incoming_val+1 );
   1265 		rc = strings_containment(stored->f_sub_any, incoming_val);
   1266 		op->o_tmpfree( incoming_val[0].bv_val, op->o_tmpmemctx );
   1267 	}
   1268 final:
   1269 	return rc;
   1270 }
   1271 
   1272 static Filter *
   1273 filter_first( Filter *f )
   1274 {
   1275 	while ( f->f_choice == LDAP_FILTER_OR || f->f_choice == LDAP_FILTER_AND )
   1276 		f = f->f_and;
   1277 	return f;
   1278 }
   1279 
   1280 typedef struct fstack {
   1281 	struct fstack *fs_next;
   1282 	Filter *fs_fs;
   1283 	Filter *fs_fi;
   1284 } fstack;
   1285 
   1286 static CachedQuery *
   1287 find_filter( Operation *op, TAvlnode *root, Filter *inputf, Filter *first )
   1288 {
   1289 	Filter* fs;
   1290 	Filter* fi;
   1291 	MatchingRule* mrule = NULL;
   1292 	int res=0, eqpass= 0;
   1293 	int ret, rc, dir;
   1294 	TAvlnode *ptr;
   1295 	CachedQuery cq, *qc;
   1296 	fstack *stack = NULL, *fsp;
   1297 
   1298 	cq.filter = inputf;
   1299 	cq.first = first;
   1300 
   1301 	/* substring matches sort to the end, and we just have to
   1302 	 * walk the entire list.
   1303 	 */
   1304 	if ( first->f_choice == LDAP_FILTER_SUBSTRINGS ) {
   1305 		ptr = ldap_tavl_end( root, 1 );
   1306 		dir = TAVL_DIR_LEFT;
   1307 	} else {
   1308 		ptr = ldap_tavl_find3( root, &cq, pcache_query_cmp, &ret );
   1309 		dir = (first->f_choice == LDAP_FILTER_GE) ? TAVL_DIR_LEFT :
   1310 			TAVL_DIR_RIGHT;
   1311 	}
   1312 
   1313 	while (ptr) {
   1314 		qc = ptr->avl_data;
   1315 		fi = inputf;
   1316 		fs = qc->filter;
   1317 
   1318 		/* an incoming substr query can only be satisfied by a cached
   1319 		 * substr query.
   1320 		 */
   1321 		if ( first->f_choice == LDAP_FILTER_SUBSTRINGS &&
   1322 			qc->first->f_choice != LDAP_FILTER_SUBSTRINGS )
   1323 			break;
   1324 
   1325 		/* an incoming eq query can be satisfied by a cached eq or substr
   1326 		 * query
   1327 		 */
   1328 		if ( first->f_choice == LDAP_FILTER_EQUALITY ) {
   1329 			if ( eqpass == 0 ) {
   1330 				if ( qc->first->f_choice != LDAP_FILTER_EQUALITY ) {
   1331 nextpass:			eqpass = 1;
   1332 					ptr = ldap_tavl_end( root, 1 );
   1333 					dir = TAVL_DIR_LEFT;
   1334 					continue;
   1335 				}
   1336 			} else {
   1337 				if ( qc->first->f_choice != LDAP_FILTER_SUBSTRINGS )
   1338 					break;
   1339 			}
   1340 		}
   1341 		do {
   1342 			res=0;
   1343 			switch (fs->f_choice) {
   1344 			case LDAP_FILTER_EQUALITY:
   1345 				if (fi->f_choice == LDAP_FILTER_EQUALITY)
   1346 					mrule = fs->f_ava->aa_desc->ad_type->sat_equality;
   1347 				else
   1348 					ret = 1;
   1349 				break;
   1350 			case LDAP_FILTER_GE:
   1351 			case LDAP_FILTER_LE:
   1352 				mrule = fs->f_ava->aa_desc->ad_type->sat_ordering;
   1353 				break;
   1354 			default:
   1355 				mrule = NULL;
   1356 			}
   1357 			if (mrule) {
   1358 				const char *text;
   1359 				rc = value_match(&ret, fs->f_ava->aa_desc, mrule,
   1360 					SLAP_MR_VALUE_OF_ASSERTION_SYNTAX,
   1361 					&(fi->f_ava->aa_value),
   1362 					&(fs->f_ava->aa_value), &text);
   1363 				if (rc != LDAP_SUCCESS) {
   1364 					return NULL;
   1365 				}
   1366 				if ( fi==first && fi->f_choice==LDAP_FILTER_EQUALITY && ret )
   1367 					goto nextpass;
   1368 			}
   1369 			switch (fs->f_choice) {
   1370 			case LDAP_FILTER_OR:
   1371 			case LDAP_FILTER_AND:
   1372 				if ( fs->f_next ) {
   1373 					/* save our stack position */
   1374 					fsp = op->o_tmpalloc(sizeof(fstack), op->o_tmpmemctx);
   1375 					fsp->fs_next = stack;
   1376 					fsp->fs_fs = fs->f_next;
   1377 					fsp->fs_fi = fi->f_next;
   1378 					stack = fsp;
   1379 				}
   1380 				fs = fs->f_and;
   1381 				fi = fi->f_and;
   1382 				res=1;
   1383 				break;
   1384 			case LDAP_FILTER_SUBSTRINGS:
   1385 				/* check if the equality query can be
   1386 				* answered with cached substring query */
   1387 				if ((fi->f_choice == LDAP_FILTER_EQUALITY)
   1388 					&& substr_containment_equality( op,
   1389 					fs, fi))
   1390 					res=1;
   1391 				/* check if the substring query can be
   1392 				* answered with cached substring query */
   1393 				if ((fi->f_choice ==LDAP_FILTER_SUBSTRINGS
   1394 					) && substr_containment_substr( op,
   1395 					fs, fi))
   1396 					res= 1;
   1397 				fs=fs->f_next;
   1398 				fi=fi->f_next;
   1399 				break;
   1400 			case LDAP_FILTER_PRESENT:
   1401 				res=1;
   1402 				fs=fs->f_next;
   1403 				fi=fi->f_next;
   1404 				break;
   1405 			case LDAP_FILTER_EQUALITY:
   1406 				if (ret == 0)
   1407 					res = 1;
   1408 				fs=fs->f_next;
   1409 				fi=fi->f_next;
   1410 				break;
   1411 			case LDAP_FILTER_GE:
   1412 				if (mrule && ret >= 0)
   1413 					res = 1;
   1414 				fs=fs->f_next;
   1415 				fi=fi->f_next;
   1416 				break;
   1417 			case LDAP_FILTER_LE:
   1418 				if (mrule && ret <= 0)
   1419 					res = 1;
   1420 				fs=fs->f_next;
   1421 				fi=fi->f_next;
   1422 				break;
   1423 			case LDAP_FILTER_NOT:
   1424 				res=0;
   1425 				break;
   1426 			default:
   1427 				break;
   1428 			}
   1429 			if (!fs && !fi && stack) {
   1430 				/* pop the stack */
   1431 				fsp = stack;
   1432 				stack = fsp->fs_next;
   1433 				fs = fsp->fs_fs;
   1434 				fi = fsp->fs_fi;
   1435 				op->o_tmpfree(fsp, op->o_tmpmemctx);
   1436 			}
   1437 		} while((res) && (fi != NULL) && (fs != NULL));
   1438 
   1439 		if ( res )
   1440 			return qc;
   1441 		ptr = ldap_tavl_next( ptr, dir );
   1442 	}
   1443 	return NULL;
   1444 }
   1445 
   1446 /* check whether query is contained in any of
   1447  * the cached queries in template
   1448  */
   1449 static CachedQuery *
   1450 query_containment(Operation *op, query_manager *qm,
   1451 		  Query *query,
   1452 		  QueryTemplate *templa)
   1453 {
   1454 	CachedQuery* qc;
   1455 	int depth = 0, tscope;
   1456 	Qbase qbase, *qbptr = NULL;
   1457 	struct berval pdn;
   1458 
   1459 	if (query->filter != NULL) {
   1460 		Filter *first;
   1461 
   1462 		Debug( pcache_debug, "Lock QC index = %p\n",
   1463 				(void *) templa );
   1464 		qbase.base = query->base;
   1465 
   1466 		first = filter_first( query->filter );
   1467 
   1468 		ldap_pvt_thread_rdwr_rlock(&templa->t_rwlock);
   1469 		for( ;; ) {
   1470 			/* Find the base */
   1471 			qbptr = ldap_avl_find( templa->qbase, &qbase, pcache_dn_cmp );
   1472 			if ( qbptr ) {
   1473 				tscope = query->scope;
   1474 				/* Find a matching scope:
   1475 				 * match at depth 0 OK
   1476 				 * scope is BASE,
   1477 				 *	one at depth 1 OK
   1478 				 *  subord at depth > 0 OK
   1479 				 *	subtree at any depth OK
   1480 				 * scope is ONE,
   1481 				 *  subtree or subord at any depth OK
   1482 				 * scope is SUBORD,
   1483 				 *  subtree or subord at any depth OK
   1484 				 * scope is SUBTREE,
   1485 				 *  subord at depth > 0 OK
   1486 				 *  subtree at any depth OK
   1487 				 */
   1488 				for ( tscope = 0 ; tscope <= LDAP_SCOPE_CHILDREN; tscope++ ) {
   1489 					switch ( query->scope ) {
   1490 					case LDAP_SCOPE_BASE:
   1491 						if ( tscope == LDAP_SCOPE_BASE && depth ) continue;
   1492 						if ( tscope == LDAP_SCOPE_ONE && depth != 1) continue;
   1493 						if ( tscope == LDAP_SCOPE_CHILDREN && !depth ) continue;
   1494 						break;
   1495 					case LDAP_SCOPE_ONE:
   1496 						if ( tscope == LDAP_SCOPE_BASE )
   1497 							tscope = LDAP_SCOPE_ONE;
   1498 						if ( tscope == LDAP_SCOPE_ONE && depth ) continue;
   1499 						if ( !depth ) break;
   1500 						if ( tscope < LDAP_SCOPE_SUBTREE )
   1501 							tscope = LDAP_SCOPE_SUBTREE;
   1502 						break;
   1503 					case LDAP_SCOPE_SUBTREE:
   1504 						if ( tscope < LDAP_SCOPE_SUBTREE )
   1505 							tscope = LDAP_SCOPE_SUBTREE;
   1506 						if ( tscope == LDAP_SCOPE_CHILDREN && !depth ) continue;
   1507 						break;
   1508 					case LDAP_SCOPE_CHILDREN:
   1509 						if ( tscope < LDAP_SCOPE_SUBTREE )
   1510 							tscope = LDAP_SCOPE_SUBTREE;
   1511 						break;
   1512 					}
   1513 					if ( !qbptr->scopes[tscope] ) continue;
   1514 
   1515 					/* Find filter */
   1516 					qc = find_filter( op, qbptr->scopes[tscope],
   1517 							query->filter, first );
   1518 					if ( qc ) {
   1519 						if ( qc->q_sizelimit ) {
   1520 							ldap_pvt_thread_rdwr_runlock(&templa->t_rwlock);
   1521 							return NULL;
   1522 						}
   1523 						ldap_pvt_thread_mutex_lock(&qm->lru_mutex);
   1524 						if (qm->lru_top != qc) {
   1525 							remove_query(qm, qc);
   1526 							add_query_on_top(qm, qc);
   1527 						}
   1528 						ldap_pvt_thread_mutex_unlock(&qm->lru_mutex);
   1529 						return qc;
   1530 					}
   1531 				}
   1532 			}
   1533 			if ( be_issuffix( op->o_bd, &qbase.base ))
   1534 				break;
   1535 			/* Up a level */
   1536 			dnParent( &qbase.base, &pdn );
   1537 			qbase.base = pdn;
   1538 			depth++;
   1539 		}
   1540 
   1541 		Debug( pcache_debug,
   1542 			"Not answerable: Unlock QC index=%p\n",
   1543 			(void *) templa );
   1544 		ldap_pvt_thread_rdwr_runlock(&templa->t_rwlock);
   1545 	}
   1546 	return NULL;
   1547 }
   1548 
   1549 static void
   1550 free_query (CachedQuery* qc)
   1551 {
   1552 	free(qc->q_uuid.bv_val);
   1553 	filter_free(qc->filter);
   1554 	ldap_pvt_thread_mutex_destroy(&qc->answerable_cnt_mutex);
   1555 	ldap_pvt_thread_rdwr_destroy( &qc->rwlock );
   1556 	memset(qc, 0, sizeof(*qc));
   1557 	free(qc);
   1558 }
   1559 
   1560 
   1561 /* Add query to query cache, the returned Query is locked for writing */
   1562 static CachedQuery *
   1563 add_query(
   1564 	Operation *op,
   1565 	query_manager* qm,
   1566 	Query* query,
   1567 	QueryTemplate *templ,
   1568 	pc_caching_reason_t why,
   1569 	int wlock)
   1570 {
   1571 	CachedQuery* new_cached_query = (CachedQuery*) ch_malloc(sizeof(CachedQuery));
   1572 	Qbase *qbase, qb;
   1573 	Filter *first;
   1574 	int rc;
   1575 	time_t ttl = 0, ttr = 0;
   1576 	time_t now;
   1577 
   1578 	new_cached_query->qtemp = templ;
   1579 	BER_BVZERO( &new_cached_query->q_uuid );
   1580 	new_cached_query->q_sizelimit = 0;
   1581 
   1582 	now = slap_get_time();
   1583 	switch ( why ) {
   1584 	case PC_POSITIVE:
   1585 		ttl = templ->ttl;
   1586 		if ( templ->ttr )
   1587 			ttr = now + templ->ttr;
   1588 		break;
   1589 
   1590 	case PC_NEGATIVE:
   1591 		ttl = templ->negttl;
   1592 		if ( templ->ttr )
   1593 			ttr = now + templ->ttr;
   1594 		break;
   1595 
   1596 	case PC_SIZELIMIT:
   1597 		ttl = templ->limitttl;
   1598 		break;
   1599 
   1600 	default:
   1601 		assert( 0 );
   1602 		break;
   1603 	}
   1604 	new_cached_query->expiry_time = now + ttl;
   1605 	new_cached_query->refresh_time = ttr;
   1606 	new_cached_query->bindref_time = 0;
   1607 
   1608 	new_cached_query->bind_refcnt = 0;
   1609 	new_cached_query->answerable_cnt = 0;
   1610 	new_cached_query->refcnt = 1;
   1611 	ldap_pvt_thread_mutex_init(&new_cached_query->answerable_cnt_mutex);
   1612 
   1613 	new_cached_query->lru_up = NULL;
   1614 	new_cached_query->lru_down = NULL;
   1615 	Debug( pcache_debug, "Added query expires at %ld (%s)\n",
   1616 			(long) new_cached_query->expiry_time,
   1617 			pc_caching_reason_str[ why ] );
   1618 
   1619 	new_cached_query->scope = query->scope;
   1620 	new_cached_query->filter = query->filter;
   1621 	new_cached_query->first = first = filter_first( query->filter );
   1622 
   1623 	ldap_pvt_thread_rdwr_init(&new_cached_query->rwlock);
   1624 	if (wlock)
   1625 		ldap_pvt_thread_rdwr_wlock(&new_cached_query->rwlock);
   1626 
   1627 	qb.base = query->base;
   1628 
   1629 	/* Adding a query    */
   1630 	Debug( pcache_debug, "Lock AQ index = %p\n",
   1631 			(void *) templ );
   1632 	ldap_pvt_thread_rdwr_wlock(&templ->t_rwlock);
   1633 	qbase = ldap_avl_find( templ->qbase, &qb, pcache_dn_cmp );
   1634 	if ( !qbase ) {
   1635 		qbase = ch_calloc( 1, sizeof(Qbase) + qb.base.bv_len + 1 );
   1636 		qbase->base.bv_len = qb.base.bv_len;
   1637 		qbase->base.bv_val = (char *)(qbase+1);
   1638 		memcpy( qbase->base.bv_val, qb.base.bv_val, qb.base.bv_len );
   1639 		qbase->base.bv_val[qbase->base.bv_len] = '\0';
   1640 		ldap_avl_insert( &templ->qbase, qbase, pcache_dn_cmp, ldap_avl_dup_error );
   1641 	}
   1642 	new_cached_query->next = templ->query;
   1643 	new_cached_query->prev = NULL;
   1644 	new_cached_query->qbase = qbase;
   1645 	rc = ldap_tavl_insert( &qbase->scopes[query->scope], new_cached_query,
   1646 		pcache_query_cmp, ldap_avl_dup_error );
   1647 	if ( rc == 0 ) {
   1648 		qbase->queries++;
   1649 		if (templ->query == NULL)
   1650 			templ->query_last = new_cached_query;
   1651 		else
   1652 			templ->query->prev = new_cached_query;
   1653 		templ->query = new_cached_query;
   1654 		templ->no_of_queries++;
   1655 	} else {
   1656 		ldap_pvt_thread_mutex_destroy(&new_cached_query->answerable_cnt_mutex);
   1657 		if (wlock)
   1658 			ldap_pvt_thread_rdwr_wunlock(&new_cached_query->rwlock);
   1659 		ldap_pvt_thread_rdwr_destroy( &new_cached_query->rwlock );
   1660 		ch_free( new_cached_query );
   1661 		new_cached_query = find_filter( op, qbase->scopes[query->scope],
   1662 							query->filter, first );
   1663 		filter_free( query->filter );
   1664 		query->filter = NULL;
   1665 	}
   1666 	Debug( pcache_debug, "TEMPLATE %p QUERIES++ %d\n",
   1667 			(void *) templ, templ->no_of_queries );
   1668 
   1669 	/* Adding on top of LRU list  */
   1670 	if ( rc == 0 ) {
   1671 		ldap_pvt_thread_mutex_lock(&qm->lru_mutex);
   1672 		add_query_on_top(qm, new_cached_query);
   1673 		ldap_pvt_thread_mutex_unlock(&qm->lru_mutex);
   1674 	}
   1675 	Debug( pcache_debug, "Unlock AQ index = %p \n",
   1676 			(void *) templ );
   1677 	ldap_pvt_thread_rdwr_wunlock(&templ->t_rwlock);
   1678 
   1679 	return rc == 0 ? new_cached_query : NULL;
   1680 }
   1681 
   1682 static void
   1683 remove_from_template (CachedQuery* qc, QueryTemplate* template)
   1684 {
   1685 	if (!qc->prev && !qc->next) {
   1686 		template->query_last = template->query = NULL;
   1687 	} else if (qc->prev == NULL) {
   1688 		qc->next->prev = NULL;
   1689 		template->query = qc->next;
   1690 	} else if (qc->next == NULL) {
   1691 		qc->prev->next = NULL;
   1692 		template->query_last = qc->prev;
   1693 	} else {
   1694 		qc->next->prev = qc->prev;
   1695 		qc->prev->next = qc->next;
   1696 	}
   1697 	ldap_tavl_delete( &qc->qbase->scopes[qc->scope], qc, pcache_query_cmp );
   1698 	qc->qbase->queries--;
   1699 	if ( qc->qbase->queries == 0 ) {
   1700 		ldap_avl_delete( &template->qbase, qc->qbase, pcache_dn_cmp );
   1701 		ch_free( qc->qbase );
   1702 		qc->qbase = NULL;
   1703 	}
   1704 
   1705 	template->no_of_queries--;
   1706 }
   1707 
   1708 /* remove bottom query of LRU list from the query cache */
   1709 /*
   1710  * NOTE: slight change in functionality.
   1711  *
   1712  * - if result->bv_val is NULL, the query at the bottom of the LRU
   1713  *   is removed
   1714  * - otherwise, the query whose UUID is *result is removed
   1715  *	- if not found, result->bv_val is zeroed
   1716  */
   1717 static void
   1718 cache_replacement(query_manager* qm, struct berval *result)
   1719 {
   1720 	CachedQuery* bottom;
   1721 	QueryTemplate *temp;
   1722 
   1723 	ldap_pvt_thread_mutex_lock(&qm->lru_mutex);
   1724 	if ( BER_BVISNULL( result ) ) {
   1725 		bottom = qm->lru_bottom;
   1726 
   1727 		if (!bottom) {
   1728 			Debug ( pcache_debug,
   1729 				"Cache replacement invoked without "
   1730 				"any query in LRU list\n" );
   1731 			ldap_pvt_thread_mutex_unlock(&qm->lru_mutex);
   1732 			return;
   1733 		}
   1734 
   1735 	} else {
   1736 		for ( bottom = qm->lru_bottom;
   1737 			bottom != NULL;
   1738 			bottom = bottom->lru_up )
   1739 		{
   1740 			if ( bvmatch( result, &bottom->q_uuid ) ) {
   1741 				break;
   1742 			}
   1743 		}
   1744 
   1745 		if ( !bottom ) {
   1746 			Debug ( pcache_debug,
   1747 				"Could not find query with uuid=\"%s\""
   1748 				"in LRU list\n", result->bv_val );
   1749 			ldap_pvt_thread_mutex_unlock(&qm->lru_mutex);
   1750 			BER_BVZERO( result );
   1751 			return;
   1752 		}
   1753 	}
   1754 
   1755 	temp = bottom->qtemp;
   1756 	remove_query(qm, bottom);
   1757 	ldap_pvt_thread_mutex_unlock(&qm->lru_mutex);
   1758 
   1759 	*result = bottom->q_uuid;
   1760 	BER_BVZERO( &bottom->q_uuid );
   1761 
   1762 	Debug( pcache_debug, "Lock CR index = %p\n", (void *) temp );
   1763 	ldap_pvt_thread_rdwr_wlock(&temp->t_rwlock);
   1764 	remove_from_template(bottom, temp);
   1765 	Debug( pcache_debug, "TEMPLATE %p QUERIES-- %d\n",
   1766 		(void *) temp, temp->no_of_queries );
   1767 	Debug( pcache_debug, "Unlock CR index = %p\n", (void *) temp );
   1768 	ldap_pvt_thread_rdwr_wunlock(&temp->t_rwlock);
   1769 	free_query(bottom);
   1770 }
   1771 
   1772 struct query_info {
   1773 	struct query_info *next;
   1774 	struct berval xdn;
   1775 	int del;
   1776 };
   1777 
   1778 static int
   1779 remove_func (
   1780 	Operation	*op,
   1781 	SlapReply	*rs
   1782 )
   1783 {
   1784 	Attribute *attr;
   1785 	struct query_info *qi;
   1786 	int count = 0;
   1787 
   1788 	if ( rs->sr_type != REP_SEARCH ) return 0;
   1789 
   1790 	attr = attr_find( rs->sr_entry->e_attrs,  ad_queryId );
   1791 	if ( attr == NULL ) return 0;
   1792 
   1793 	count = attr->a_numvals;
   1794 	assert( count > 0 );
   1795 	qi = op->o_tmpalloc( sizeof( struct query_info ), op->o_tmpmemctx );
   1796 	qi->next = op->o_callback->sc_private;
   1797 	op->o_callback->sc_private = qi;
   1798 	ber_dupbv_x( &qi->xdn, &rs->sr_entry->e_nname, op->o_tmpmemctx );
   1799 	qi->del = ( count == 1 );
   1800 
   1801 	return 0;
   1802 }
   1803 
   1804 static int
   1805 remove_query_data(
   1806 	Operation	*op,
   1807 	struct berval	*query_uuid )
   1808 {
   1809 	struct query_info	*qi, *qnext;
   1810 	char			filter_str[ LDAP_LUTIL_UUIDSTR_BUFSIZE + STRLENOF( "(pcacheQueryID=)" ) ];
   1811 	AttributeAssertion	ava = ATTRIBUTEASSERTION_INIT;
   1812 	Filter			filter = {LDAP_FILTER_EQUALITY};
   1813 	SlapReply 		sreply = {REP_RESULT};
   1814 	slap_callback cb = { NULL, remove_func, NULL, NULL };
   1815 	int deleted = 0;
   1816 
   1817 	op->ors_filterstr.bv_len = snprintf(filter_str, sizeof(filter_str),
   1818 		"(%s=%s)", ad_queryId->ad_cname.bv_val, query_uuid->bv_val);
   1819 	filter.f_ava = &ava;
   1820 	filter.f_av_desc = ad_queryId;
   1821 	filter.f_av_value = *query_uuid;
   1822 
   1823 	op->o_tag = LDAP_REQ_SEARCH;
   1824 	op->o_protocol = LDAP_VERSION3;
   1825 	op->o_callback = &cb;
   1826 	op->o_time = slap_get_time();
   1827 	op->o_do_not_cache = 1;
   1828 
   1829 	op->o_req_dn = op->o_bd->be_suffix[0];
   1830 	op->o_req_ndn = op->o_bd->be_nsuffix[0];
   1831 	op->ors_scope = LDAP_SCOPE_SUBTREE;
   1832 	op->ors_deref = LDAP_DEREF_NEVER;
   1833 	op->ors_slimit = SLAP_NO_LIMIT;
   1834 	op->ors_tlimit = SLAP_NO_LIMIT;
   1835 	op->ors_limit = NULL;
   1836 	op->ors_filter = &filter;
   1837 	op->ors_filterstr.bv_val = filter_str;
   1838 	op->ors_filterstr.bv_len = strlen(filter_str);
   1839 	op->ors_attrs = NULL;
   1840 	op->ors_attrsonly = 0;
   1841 
   1842 	op->o_bd->be_search( op, &sreply );
   1843 
   1844 	for ( qi=cb.sc_private; qi; qi=qnext ) {
   1845 		qnext = qi->next;
   1846 
   1847 		op->o_req_dn = qi->xdn;
   1848 		op->o_req_ndn = qi->xdn;
   1849 		rs_reinit( &sreply, REP_RESULT );
   1850 
   1851 		if ( qi->del ) {
   1852 			Debug( pcache_debug, "DELETING ENTRY TEMPLATE=%s\n",
   1853 				query_uuid->bv_val );
   1854 
   1855 			op->o_tag = LDAP_REQ_DELETE;
   1856 
   1857 			if (op->o_bd->be_delete(op, &sreply) == LDAP_SUCCESS) {
   1858 				deleted++;
   1859 			}
   1860 
   1861 		} else {
   1862 			Modifications mod;
   1863 			struct berval vals[2];
   1864 
   1865 			vals[0] = *query_uuid;
   1866 			vals[1].bv_val = NULL;
   1867 			vals[1].bv_len = 0;
   1868 			mod.sml_op = LDAP_MOD_DELETE;
   1869 			mod.sml_flags = 0;
   1870 			mod.sml_desc = ad_queryId;
   1871 			mod.sml_type = ad_queryId->ad_cname;
   1872 			mod.sml_values = vals;
   1873 			mod.sml_nvalues = NULL;
   1874                         mod.sml_numvals = 1;
   1875 			mod.sml_next = NULL;
   1876 			Debug( pcache_debug,
   1877 				"REMOVING TEMP ATTR : TEMPLATE=%s\n",
   1878 				query_uuid->bv_val );
   1879 
   1880 			op->orm_modlist = &mod;
   1881 
   1882 			op->o_bd->be_modify( op, &sreply );
   1883 		}
   1884 		op->o_tmpfree( qi->xdn.bv_val, op->o_tmpmemctx );
   1885 		op->o_tmpfree( qi, op->o_tmpmemctx );
   1886 	}
   1887 	return deleted;
   1888 }
   1889 
   1890 static int
   1891 get_attr_set(
   1892 	AttributeName* attrs,
   1893 	query_manager* qm,
   1894 	int num
   1895 );
   1896 
   1897 static int
   1898 filter2template(
   1899 	Operation		*op,
   1900 	Filter			*f,
   1901 	struct			berval *fstr )
   1902 {
   1903 	AttributeDescription *ad;
   1904 	int len, ret;
   1905 
   1906 	switch ( f->f_choice ) {
   1907 	case LDAP_FILTER_EQUALITY:
   1908 		ad = f->f_av_desc;
   1909 		len = STRLENOF( "(=)" ) + ad->ad_cname.bv_len;
   1910 		ret = snprintf( fstr->bv_val+fstr->bv_len, len + 1, "(%s=)", ad->ad_cname.bv_val );
   1911 		assert( ret == len );
   1912 		fstr->bv_len += len;
   1913 		break;
   1914 
   1915 	case LDAP_FILTER_GE:
   1916 		ad = f->f_av_desc;
   1917 		len = STRLENOF( "(>=)" ) + ad->ad_cname.bv_len;
   1918 		ret = snprintf( fstr->bv_val+fstr->bv_len, len + 1, "(%s>=)", ad->ad_cname.bv_val);
   1919 		assert( ret == len );
   1920 		fstr->bv_len += len;
   1921 		break;
   1922 
   1923 	case LDAP_FILTER_LE:
   1924 		ad = f->f_av_desc;
   1925 		len = STRLENOF( "(<=)" ) + ad->ad_cname.bv_len;
   1926 		ret = snprintf( fstr->bv_val+fstr->bv_len, len + 1, "(%s<=)", ad->ad_cname.bv_val);
   1927 		assert( ret == len );
   1928 		fstr->bv_len += len;
   1929 		break;
   1930 
   1931 	case LDAP_FILTER_APPROX:
   1932 		ad = f->f_av_desc;
   1933 		len = STRLENOF( "(~=)" ) + ad->ad_cname.bv_len;
   1934 		ret = snprintf( fstr->bv_val+fstr->bv_len, len + 1, "(%s~=)", ad->ad_cname.bv_val);
   1935 		assert( ret == len );
   1936 		fstr->bv_len += len;
   1937 		break;
   1938 
   1939 	case LDAP_FILTER_SUBSTRINGS:
   1940 		ad = f->f_sub_desc;
   1941 		len = STRLENOF( "(=)" ) + ad->ad_cname.bv_len;
   1942 		ret = snprintf( fstr->bv_val+fstr->bv_len, len + 1, "(%s=)", ad->ad_cname.bv_val );
   1943 		assert( ret == len );
   1944 		fstr->bv_len += len;
   1945 		break;
   1946 
   1947 	case LDAP_FILTER_PRESENT:
   1948 		ad = f->f_desc;
   1949 		len = STRLENOF( "(=*)" ) + ad->ad_cname.bv_len;
   1950 		ret = snprintf( fstr->bv_val+fstr->bv_len, len + 1, "(%s=*)", ad->ad_cname.bv_val );
   1951 		assert( ret == len );
   1952 		fstr->bv_len += len;
   1953 		break;
   1954 
   1955 	case LDAP_FILTER_AND:
   1956 	case LDAP_FILTER_OR:
   1957 	case LDAP_FILTER_NOT: {
   1958 		int rc = 0;
   1959 		fstr->bv_val[fstr->bv_len++] = '(';
   1960 		switch ( f->f_choice ) {
   1961 		case LDAP_FILTER_AND:
   1962 			fstr->bv_val[fstr->bv_len] = '&';
   1963 			break;
   1964 		case LDAP_FILTER_OR:
   1965 			fstr->bv_val[fstr->bv_len] = '|';
   1966 			break;
   1967 		case LDAP_FILTER_NOT:
   1968 			fstr->bv_val[fstr->bv_len] = '!';
   1969 			break;
   1970 		}
   1971 		fstr->bv_len++;
   1972 
   1973 		for ( f = f->f_list; f != NULL; f = f->f_next ) {
   1974 			rc = filter2template( op, f, fstr );
   1975 			if ( rc ) break;
   1976 		}
   1977 		fstr->bv_val[fstr->bv_len++] = ')';
   1978 		fstr->bv_val[fstr->bv_len] = '\0';
   1979 
   1980 		return rc;
   1981 		}
   1982 
   1983 	default:
   1984 		/* a filter should at least have room for "()",
   1985 		 * an "=" and for a 1-char attr */
   1986 		strcpy( fstr->bv_val, "(?=)" );
   1987 		fstr->bv_len += STRLENOF("(?=)");
   1988 		return -1;
   1989 	}
   1990 
   1991 	return 0;
   1992 }
   1993 
   1994 #define	BI_HASHED	0x01
   1995 #define	BI_DIDCB	0x02
   1996 #define	BI_LOOKUP	0x04
   1997 
   1998 struct search_info;
   1999 
   2000 typedef struct bindinfo {
   2001 	cache_manager *bi_cm;
   2002 	CachedQuery *bi_cq;
   2003 	QueryTemplate *bi_templ;
   2004 	struct search_info *bi_si;
   2005 	int bi_flags;
   2006 	slap_callback bi_cb;
   2007 } bindinfo;
   2008 
   2009 struct search_info {
   2010 	slap_overinst *on;
   2011 	Query query;
   2012 	QueryTemplate *qtemp;
   2013 	AttributeName*  save_attrs;	/* original attributes, saved for response */
   2014 	int swap_saved_attrs;
   2015 	int max;
   2016 	int over;
   2017 	int count;
   2018 	int slimit;
   2019 	int slimit_exceeded;
   2020 	pc_caching_reason_t caching_reason;
   2021 	Entry *head, *tail;
   2022 	bindinfo *pbi;
   2023 };
   2024 
   2025 static void
   2026 remove_query_and_data(
   2027 	Operation	*op,
   2028 	cache_manager	*cm,
   2029 	struct berval	*uuid )
   2030 {
   2031 	query_manager*		qm = cm->qm;
   2032 
   2033 	qm->crfunc( qm, uuid );
   2034 	if ( !BER_BVISNULL( uuid ) ) {
   2035 		int	return_val;
   2036 
   2037 		Debug( pcache_debug,
   2038 			"Removing query UUID %s\n",
   2039 			uuid->bv_val );
   2040 		return_val = remove_query_data( op, uuid );
   2041 		Debug( pcache_debug,
   2042 			"QUERY REMOVED, SIZE=%d\n",
   2043 			return_val );
   2044 		ldap_pvt_thread_mutex_lock( &cm->cache_mutex );
   2045 		cm->cur_entries -= return_val;
   2046 		cm->num_cached_queries--;
   2047 		Debug( pcache_debug,
   2048 			"STORED QUERIES = %lu\n",
   2049 			cm->num_cached_queries );
   2050 		ldap_pvt_thread_mutex_unlock( &cm->cache_mutex );
   2051 		Debug( pcache_debug,
   2052 			"QUERY REMOVED, CACHE ="
   2053 			"%d entries\n",
   2054 			cm->cur_entries );
   2055 	}
   2056 }
   2057 
   2058 /*
   2059  * Callback used to fetch queryId values based on entryUUID;
   2060  * used by pcache_remove_entries_from_cache()
   2061  */
   2062 static int
   2063 fetch_queryId_cb( Operation *op, SlapReply *rs )
   2064 {
   2065 	int		rc = 0;
   2066 
   2067 	/* only care about searchEntry responses */
   2068 	if ( rs->sr_type != REP_SEARCH ) {
   2069 		return 0;
   2070 	}
   2071 
   2072 	/* allow only one response per entryUUID */
   2073 	if ( op->o_callback->sc_private != NULL ) {
   2074 		rc = 1;
   2075 
   2076 	} else {
   2077 		Attribute	*a;
   2078 
   2079 		/* copy all queryId values into callback's private data */
   2080 		a = attr_find( rs->sr_entry->e_attrs, ad_queryId );
   2081 		if ( a != NULL ) {
   2082 			BerVarray	vals = NULL;
   2083 
   2084 			ber_bvarray_dup_x( &vals, a->a_nvals, op->o_tmpmemctx );
   2085 			op->o_callback->sc_private = (void *)vals;
   2086 		}
   2087 	}
   2088 
   2089 	/* clear entry if required */
   2090 	rs_flush_entry( op, rs, (slap_overinst *) op->o_bd->bd_info );
   2091 
   2092 	return rc;
   2093 }
   2094 
   2095 /*
   2096  * Call that allows to remove a set of entries from the cache,
   2097  * by forcing the removal of all the related queries.
   2098  */
   2099 int
   2100 pcache_remove_entries_from_cache(
   2101 	Operation	*op,
   2102 	cache_manager	*cm,
   2103 	BerVarray	entryUUIDs )
   2104 {
   2105 	Connection	conn = { 0 };
   2106 	OperationBuffer opbuf;
   2107 	Operation	op2;
   2108 	slap_callback	sc = { 0 };
   2109 	Filter		f = { 0 };
   2110 	char		filtbuf[ LDAP_LUTIL_UUIDSTR_BUFSIZE + STRLENOF( "(entryUUID=)" ) ];
   2111 	AttributeAssertion ava = ATTRIBUTEASSERTION_INIT;
   2112 	AttributeName	attrs[ 2 ] = {{{ 0 }}};
   2113 	int		s, rc;
   2114 
   2115 	if ( op == NULL ) {
   2116 		void	*thrctx = ldap_pvt_thread_pool_context();
   2117 
   2118 		connection_fake_init( &conn, &opbuf, thrctx );
   2119 		op = &opbuf.ob_op;
   2120 
   2121 	} else {
   2122 		op2 = *op;
   2123 		op = &op2;
   2124 	}
   2125 
   2126 	memset( &op->oq_search, 0, sizeof( op->oq_search ) );
   2127 	op->ors_scope = LDAP_SCOPE_SUBTREE;
   2128 	op->ors_deref = LDAP_DEREF_NEVER;
   2129 	f.f_choice = LDAP_FILTER_EQUALITY;
   2130 	f.f_ava = &ava;
   2131 	ava.aa_desc = slap_schema.si_ad_entryUUID;
   2132 	op->ors_filter = &f;
   2133 	op->ors_slimit = 1;
   2134 	op->ors_tlimit = SLAP_NO_LIMIT;
   2135 	op->ors_limit = NULL;
   2136 	attrs[ 0 ].an_desc = ad_queryId;
   2137 	attrs[ 0 ].an_name = ad_queryId->ad_cname;
   2138 	op->ors_attrs = attrs;
   2139 	op->ors_attrsonly = 0;
   2140 
   2141 	op->o_req_dn = cm->db.be_suffix[ 0 ];
   2142 	op->o_req_ndn = cm->db.be_nsuffix[ 0 ];
   2143 
   2144 	op->o_tag = LDAP_REQ_SEARCH;
   2145 	op->o_protocol = LDAP_VERSION3;
   2146 	op->o_managedsait = SLAP_CONTROL_CRITICAL;
   2147 	op->o_bd = &cm->db;
   2148 	op->o_dn = op->o_bd->be_rootdn;
   2149 	op->o_ndn = op->o_bd->be_rootndn;
   2150 	sc.sc_response = fetch_queryId_cb;
   2151 	op->o_callback = &sc;
   2152 
   2153 	for ( s = 0; !BER_BVISNULL( &entryUUIDs[ s ] ); s++ ) {
   2154 		BerVarray	vals = NULL;
   2155 		SlapReply	rs = { REP_RESULT };
   2156 
   2157 		op->ors_filterstr.bv_len = snprintf( filtbuf, sizeof( filtbuf ),
   2158 			"(entryUUID=%s)", entryUUIDs[ s ].bv_val );
   2159 		op->ors_filterstr.bv_val = filtbuf;
   2160 		ava.aa_value = entryUUIDs[ s ];
   2161 
   2162 		rc = op->o_bd->be_search( op, &rs );
   2163 		if ( rc != LDAP_SUCCESS ) {
   2164 			continue;
   2165 		}
   2166 
   2167 		vals = (BerVarray)op->o_callback->sc_private;
   2168 		if ( vals != NULL ) {
   2169 			int		i;
   2170 
   2171 			for ( i = 0; !BER_BVISNULL( &vals[ i ] ); i++ ) {
   2172 				struct berval	val = vals[ i ];
   2173 
   2174 				remove_query_and_data( op, cm, &val );
   2175 
   2176 				if ( !BER_BVISNULL( &val ) && val.bv_val != vals[ i ].bv_val ) {
   2177 					ch_free( val.bv_val );
   2178 				}
   2179 			}
   2180 
   2181 			ber_bvarray_free_x( vals, op->o_tmpmemctx );
   2182 			op->o_callback->sc_private = NULL;
   2183 		}
   2184 	}
   2185 
   2186 	return 0;
   2187 }
   2188 
   2189 /*
   2190  * Call that allows to remove a query from the cache.
   2191  */
   2192 int
   2193 pcache_remove_query_from_cache(
   2194 	Operation	*op,
   2195 	cache_manager	*cm,
   2196 	struct berval	*queryid )
   2197 {
   2198 	Operation	op2 = *op;
   2199 
   2200 	op2.o_bd = &cm->db;
   2201 
   2202 	/* remove the selected query */
   2203 	remove_query_and_data( &op2, cm, queryid );
   2204 
   2205 	return LDAP_SUCCESS;
   2206 }
   2207 
   2208 /*
   2209  * Call that allows to remove a set of queries related to an entry
   2210  * from the cache; if queryid is not null, the entry must belong to
   2211  * the query indicated by queryid.
   2212  */
   2213 int
   2214 pcache_remove_entry_queries_from_cache(
   2215 	Operation	*op,
   2216 	cache_manager	*cm,
   2217 	struct berval	*ndn,
   2218 	struct berval	*queryid )
   2219 {
   2220 	Connection		conn = { 0 };
   2221 	OperationBuffer 	opbuf;
   2222 	Operation		op2;
   2223 	slap_callback		sc = { 0 };
   2224 	SlapReply		rs = { REP_RESULT };
   2225 	Filter			f = { 0 };
   2226 	char			filter_str[ LDAP_LUTIL_UUIDSTR_BUFSIZE + STRLENOF( "(pcacheQueryID=)" ) ];
   2227 	AttributeAssertion	ava = ATTRIBUTEASSERTION_INIT;
   2228 	AttributeName		attrs[ 2 ] = {{{ 0 }}};
   2229 	int			rc;
   2230 
   2231 	BerVarray		vals = NULL;
   2232 
   2233 	if ( op == NULL ) {
   2234 		void	*thrctx = ldap_pvt_thread_pool_context();
   2235 
   2236 		connection_fake_init( &conn, &opbuf, thrctx );
   2237 		op = &opbuf.ob_op;
   2238 
   2239 	} else {
   2240 		op2 = *op;
   2241 		op = &op2;
   2242 	}
   2243 
   2244 	memset( &op->oq_search, 0, sizeof( op->oq_search ) );
   2245 	op->ors_scope = LDAP_SCOPE_BASE;
   2246 	op->ors_deref = LDAP_DEREF_NEVER;
   2247 	if ( queryid == NULL || BER_BVISNULL( queryid ) ) {
   2248 		BER_BVSTR( &op->ors_filterstr, "(objectClass=*)" );
   2249 		f.f_choice = LDAP_FILTER_PRESENT;
   2250 		f.f_desc = slap_schema.si_ad_objectClass;
   2251 
   2252 	} else {
   2253 		op->ors_filterstr.bv_len = snprintf( filter_str,
   2254 			sizeof( filter_str ), "(%s=%s)",
   2255 			ad_queryId->ad_cname.bv_val, queryid->bv_val );
   2256 		f.f_choice = LDAP_FILTER_EQUALITY;
   2257 		f.f_ava = &ava;
   2258 		f.f_av_desc = ad_queryId;
   2259 		f.f_av_value = *queryid;
   2260 	}
   2261 	op->ors_filter = &f;
   2262 	op->ors_slimit = 1;
   2263 	op->ors_tlimit = SLAP_NO_LIMIT;
   2264 	op->ors_limit = NULL;
   2265 	attrs[ 0 ].an_desc = ad_queryId;
   2266 	attrs[ 0 ].an_name = ad_queryId->ad_cname;
   2267 	op->ors_attrs = attrs;
   2268 	op->ors_attrsonly = 0;
   2269 
   2270 	op->o_req_dn = *ndn;
   2271 	op->o_req_ndn = *ndn;
   2272 
   2273 	op->o_tag = LDAP_REQ_SEARCH;
   2274 	op->o_protocol = LDAP_VERSION3;
   2275 	op->o_managedsait = SLAP_CONTROL_CRITICAL;
   2276 	op->o_bd = &cm->db;
   2277 	op->o_dn = op->o_bd->be_rootdn;
   2278 	op->o_ndn = op->o_bd->be_rootndn;
   2279 	sc.sc_response = fetch_queryId_cb;
   2280 	op->o_callback = &sc;
   2281 
   2282 	rc = op->o_bd->be_search( op, &rs );
   2283 	if ( rc != LDAP_SUCCESS ) {
   2284 		return rc;
   2285 	}
   2286 
   2287 	vals = (BerVarray)op->o_callback->sc_private;
   2288 	if ( vals != NULL ) {
   2289 		int		i;
   2290 
   2291 		for ( i = 0; !BER_BVISNULL( &vals[ i ] ); i++ ) {
   2292 			struct berval	val = vals[ i ];
   2293 
   2294 			remove_query_and_data( op, cm, &val );
   2295 
   2296 			if ( !BER_BVISNULL( &val ) && val.bv_val != vals[ i ].bv_val ) {
   2297 				ch_free( val.bv_val );
   2298 			}
   2299 		}
   2300 
   2301 		ber_bvarray_free_x( vals, op->o_tmpmemctx );
   2302 	}
   2303 
   2304 	return LDAP_SUCCESS;
   2305 }
   2306 
   2307 static int
   2308 cache_entries(
   2309 	Operation	*op,
   2310 	struct berval *query_uuid )
   2311 {
   2312 	struct search_info *si = op->o_callback->sc_private;
   2313 	slap_overinst *on = si->on;
   2314 	cache_manager *cm = on->on_bi.bi_private;
   2315 	int		return_val = 0;
   2316 	Entry		*e;
   2317 	struct berval	crp_uuid;
   2318 	char		uuidbuf[ LDAP_LUTIL_UUIDSTR_BUFSIZE ];
   2319 	Operation	*op_tmp;
   2320 	Connection	conn = {0};
   2321 	OperationBuffer opbuf;
   2322 	void		*thrctx = ldap_pvt_thread_pool_context();
   2323 
   2324 	query_uuid->bv_len = lutil_uuidstr(uuidbuf, sizeof(uuidbuf));
   2325 	ber_str2bv(uuidbuf, query_uuid->bv_len, 1, query_uuid);
   2326 
   2327 	connection_fake_init2( &conn, &opbuf, thrctx, 0 );
   2328 	op_tmp = &opbuf.ob_op;
   2329 	op_tmp->o_bd = &cm->db;
   2330 	op_tmp->o_dn = cm->db.be_rootdn;
   2331 	op_tmp->o_ndn = cm->db.be_rootndn;
   2332 
   2333 	Debug( pcache_debug, "UUID for query being added = %s\n",
   2334 			uuidbuf );
   2335 
   2336 	for ( e=si->head; e; e=si->head ) {
   2337 		si->head = e->e_private;
   2338 		e->e_private = NULL;
   2339 		while ( cm->cur_entries > (cm->max_entries) ) {
   2340 			BER_BVZERO( &crp_uuid );
   2341 			remove_query_and_data( op_tmp, cm, &crp_uuid );
   2342 		}
   2343 
   2344 		return_val = merge_entry(op_tmp, e, 0, query_uuid);
   2345 		ldap_pvt_thread_mutex_lock(&cm->cache_mutex);
   2346 		cm->cur_entries += return_val;
   2347 		Debug( pcache_debug,
   2348 			"ENTRY ADDED/MERGED, CACHED ENTRIES=%d\n",
   2349 			cm->cur_entries );
   2350 		return_val = 0;
   2351 		ldap_pvt_thread_mutex_unlock(&cm->cache_mutex);
   2352 	}
   2353 
   2354 	return return_val;
   2355 }
   2356 
   2357 static int
   2358 pcache_op_cleanup( Operation *op, SlapReply *rs ) {
   2359 	slap_callback	*cb = op->o_callback;
   2360 	struct search_info *si = cb->sc_private;
   2361 	slap_overinst *on = si->on;
   2362 	cache_manager *cm = on->on_bi.bi_private;
   2363 	query_manager*		qm = cm->qm;
   2364 
   2365 	if ( rs->sr_type == REP_RESULT ||
   2366 		op->o_abandon || rs->sr_err == SLAPD_ABANDON )
   2367 	{
   2368 		if ( si->swap_saved_attrs ) {
   2369 			rs->sr_attrs = si->save_attrs;
   2370 			op->ors_attrs = si->save_attrs;
   2371 		}
   2372 		if ( (op->o_abandon || rs->sr_err == SLAPD_ABANDON) &&
   2373 				si->caching_reason == PC_IGNORE )
   2374 		{
   2375 			filter_free( si->query.filter );
   2376 			if ( si->count ) {
   2377 				/* duplicate query, free it */
   2378 				Entry *e;
   2379 				for (;si->head; si->head=e) {
   2380 					e = si->head->e_private;
   2381 					si->head->e_private = NULL;
   2382 					entry_free(si->head);
   2383 				}
   2384 			}
   2385 
   2386 		} else if ( si->caching_reason != PC_IGNORE ) {
   2387 			CachedQuery *qc = qm->addfunc(op, qm, &si->query,
   2388 				si->qtemp, si->caching_reason, 1 );
   2389 
   2390 			if ( qc != NULL ) {
   2391 				switch ( si->caching_reason ) {
   2392 				case PC_POSITIVE:
   2393 					cache_entries( op, &qc->q_uuid );
   2394 					if ( si->pbi ) {
   2395 						qc->bind_refcnt++;
   2396 						si->pbi->bi_cq = qc;
   2397 					}
   2398 					break;
   2399 
   2400 				case PC_SIZELIMIT:
   2401 					qc->q_sizelimit = rs->sr_nentries;
   2402 					break;
   2403 
   2404 				case PC_NEGATIVE:
   2405 					break;
   2406 
   2407 				default:
   2408 					assert( 0 );
   2409 					break;
   2410 				}
   2411 				ldap_pvt_thread_rdwr_wunlock(&qc->rwlock);
   2412 				ldap_pvt_thread_mutex_lock(&cm->cache_mutex);
   2413 				cm->num_cached_queries++;
   2414 				Debug( pcache_debug, "STORED QUERIES = %lu\n",
   2415 						cm->num_cached_queries );
   2416 				ldap_pvt_thread_mutex_unlock(&cm->cache_mutex);
   2417 
   2418 				/* If the consistency checker suspended itself,
   2419 				 * wake it back up
   2420 				 */
   2421 				if ( cm->cc_paused == PCACHE_CC_PAUSED ) {
   2422 					ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
   2423 					if ( cm->cc_paused == PCACHE_CC_PAUSED ) {
   2424 						cm->cc_paused = 0;
   2425 						ldap_pvt_runqueue_resched( &slapd_rq, cm->cc_arg, 0 );
   2426 					}
   2427 					ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
   2428 				}
   2429 
   2430 			} else if ( si->count ) {
   2431 				/* duplicate query, free it */
   2432 				Entry *e;
   2433 				for (;si->head; si->head=e) {
   2434 					e = si->head->e_private;
   2435 					si->head->e_private = NULL;
   2436 					entry_free(si->head);
   2437 				}
   2438 			}
   2439 
   2440 		} else {
   2441 			filter_free( si->query.filter );
   2442 		}
   2443 
   2444 		op->o_callback = op->o_callback->sc_next;
   2445 		op->o_tmpfree( cb, op->o_tmpmemctx );
   2446 	}
   2447 
   2448 	return SLAP_CB_CONTINUE;
   2449 }
   2450 
   2451 static int
   2452 pcache_response(
   2453 	Operation	*op,
   2454 	SlapReply	*rs )
   2455 {
   2456 	struct search_info *si = op->o_callback->sc_private;
   2457 
   2458 	if ( si->swap_saved_attrs ) {
   2459 		rs->sr_attrs = si->save_attrs;
   2460 		rs->sr_attr_flags = slap_attr_flags( si->save_attrs );
   2461 		op->ors_attrs = si->save_attrs;
   2462 	}
   2463 
   2464 	if ( rs->sr_type == REP_SEARCH ) {
   2465 		Entry *e;
   2466 
   2467 		/* don't return more entries than requested by the client */
   2468 		if ( si->slimit > 0 && rs->sr_nentries >= si->slimit ) {
   2469 			si->slimit_exceeded = 1;
   2470 		}
   2471 
   2472 		/* If we haven't exceeded the limit for this query,
   2473 		 * build a chain of answers to store. If we hit the
   2474 		 * limit, empty the chain and ignore the rest.
   2475 		 */
   2476 		if ( !si->over ) {
   2477 			slap_overinst *on = si->on;
   2478 			cache_manager *cm = on->on_bi.bi_private;
   2479 
   2480 			/* check if the entry contains undefined
   2481 			 * attributes/objectClasses (ITS#5680) */
   2482 			if ( cm->check_cacheability && test_filter( op, rs->sr_entry, si->query.filter ) != LDAP_COMPARE_TRUE ) {
   2483 				Debug( pcache_debug, "%s: query not cacheable because of schema issues in DN \"%s\"\n",
   2484 					op->o_log_prefix, rs->sr_entry->e_name.bv_val );
   2485 				goto over;
   2486 			}
   2487 
   2488 			/* check for malformed entries: attrs with no values */
   2489 			{
   2490 				Attribute *a = rs->sr_entry->e_attrs;
   2491 				for (; a; a=a->a_next) {
   2492 					if ( !a->a_numvals ) {
   2493 						Debug( pcache_debug, "%s: query not cacheable because of attrs without values in DN \"%s\" (%s)\n",
   2494 						op->o_log_prefix, rs->sr_entry->e_name.bv_val,
   2495 						a->a_desc->ad_cname.bv_val );
   2496 						goto over;
   2497 					}
   2498 				}
   2499 			}
   2500 
   2501 			if ( si->count < si->max ) {
   2502 				si->count++;
   2503 				e = entry_dup( rs->sr_entry );
   2504 				if ( !si->head ) si->head = e;
   2505 				if ( si->tail ) si->tail->e_private = e;
   2506 				si->tail = e;
   2507 
   2508 			} else {
   2509 over:;
   2510 				si->over = 1;
   2511 				si->count = 0;
   2512 				for (;si->head; si->head=e) {
   2513 					e = si->head->e_private;
   2514 					si->head->e_private = NULL;
   2515 					entry_free(si->head);
   2516 				}
   2517 				si->tail = NULL;
   2518 			}
   2519 		}
   2520 		if ( si->slimit_exceeded ) {
   2521 			return 0;
   2522 		}
   2523 	} else if ( rs->sr_type == REP_RESULT ) {
   2524 
   2525 		if ( si->count ) {
   2526 			if ( rs->sr_err == LDAP_SUCCESS ) {
   2527 				si->caching_reason = PC_POSITIVE;
   2528 
   2529 			} else if ( rs->sr_err == LDAP_SIZELIMIT_EXCEEDED
   2530 				&& si->qtemp->limitttl )
   2531 			{
   2532 				Entry *e;
   2533 
   2534 				si->caching_reason = PC_SIZELIMIT;
   2535 				for (;si->head; si->head=e) {
   2536 					e = si->head->e_private;
   2537 					si->head->e_private = NULL;
   2538 					entry_free(si->head);
   2539 				}
   2540 			}
   2541 
   2542 		} else if ( si->qtemp->negttl && !si->count && !si->over &&
   2543 				rs->sr_err == LDAP_SUCCESS )
   2544 		{
   2545 			si->caching_reason = PC_NEGATIVE;
   2546 		}
   2547 
   2548 
   2549 		if ( si->slimit_exceeded ) {
   2550 			rs->sr_err = LDAP_SIZELIMIT_EXCEEDED;
   2551 		}
   2552 	}
   2553 
   2554 	return SLAP_CB_CONTINUE;
   2555 }
   2556 
   2557 /* NOTE: this is a quick workaround to let pcache minimally interact
   2558  * with pagedResults.  A more articulated solutions would be to
   2559  * perform the remote query without control and cache all results,
   2560  * performing the pagedResults search only within the client
   2561  * and the proxy.  This requires pcache to understand pagedResults. */
   2562 static int
   2563 pcache_chk_controls(
   2564 	Operation	*op,
   2565 	SlapReply	*rs )
   2566 {
   2567 	const char	*non = "";
   2568 	const char	*stripped = "";
   2569 
   2570 	switch( op->o_pagedresults ) {
   2571 	case SLAP_CONTROL_NONCRITICAL:
   2572 		non = "non-";
   2573 		stripped = "; stripped";
   2574 		/* fallthru */
   2575 
   2576 	case SLAP_CONTROL_CRITICAL:
   2577 		Debug( pcache_debug, "%s: "
   2578 			"%scritical pagedResults control "
   2579 			"disabled with proxy cache%s.\n",
   2580 			op->o_log_prefix, non, stripped );
   2581 
   2582 		slap_remove_control( op, rs, slap_cids.sc_pagedResults, NULL );
   2583 		break;
   2584 
   2585 	default:
   2586 		rs->sr_err = SLAP_CB_CONTINUE;
   2587 		break;
   2588 	}
   2589 
   2590 	return rs->sr_err;
   2591 }
   2592 
   2593 static int
   2594 pc_setpw( Operation *op, struct berval *pwd, cache_manager *cm )
   2595 {
   2596 	struct berval vals[2];
   2597 
   2598 	{
   2599 		const char *text = NULL;
   2600 		BER_BVZERO( &vals[0] );
   2601 		slap_passwd_hash( pwd, &vals[0], &text );
   2602 		if ( BER_BVISEMPTY( &vals[0] )) {
   2603 			Debug( pcache_debug, "pc_setpw: hash failed %s\n",
   2604 				text );
   2605 			return LDAP_OTHER;
   2606 		}
   2607 	}
   2608 
   2609 	BER_BVZERO( &vals[1] );
   2610 
   2611 	{
   2612 		Modifications mod;
   2613 		SlapReply sr = { REP_RESULT };
   2614 		slap_callback cb = { 0, slap_null_cb, 0, 0 };
   2615 		int rc;
   2616 
   2617 		mod.sml_op = LDAP_MOD_REPLACE;
   2618 		mod.sml_flags = 0;
   2619 		mod.sml_desc = slap_schema.si_ad_userPassword;
   2620 		mod.sml_type = mod.sml_desc->ad_cname;
   2621 		mod.sml_values = vals;
   2622 		mod.sml_nvalues = NULL;
   2623 		mod.sml_numvals = 1;
   2624 		mod.sml_next = NULL;
   2625 
   2626 		op->o_tag = LDAP_REQ_MODIFY;
   2627 		op->orm_modlist = &mod;
   2628 		op->o_bd = &cm->db;
   2629 		op->o_dn = op->o_bd->be_rootdn;
   2630 		op->o_ndn = op->o_bd->be_rootndn;
   2631 		op->o_callback = &cb;
   2632 		Debug( pcache_debug, "pc_setpw: CACHING BIND for %s\n",
   2633 			op->o_req_dn.bv_val );
   2634 		rc = op->o_bd->be_modify( op, &sr );
   2635 		ch_free( vals[0].bv_val );
   2636 		return rc;
   2637 	}
   2638 }
   2639 
   2640 typedef struct bindcacheinfo {
   2641 	slap_overinst *on;
   2642 	CachedQuery *qc;
   2643 } bindcacheinfo;
   2644 
   2645 static int
   2646 pc_bind_save( Operation *op, SlapReply *rs )
   2647 {
   2648 	if ( rs->sr_err == LDAP_SUCCESS ) {
   2649 		bindcacheinfo *bci = op->o_callback->sc_private;
   2650 		slap_overinst *on = bci->on;
   2651 		cache_manager *cm = on->on_bi.bi_private;
   2652 		CachedQuery *qc = bci->qc;
   2653 		int delete = 0;
   2654 
   2655 		ldap_pvt_thread_rdwr_wlock( &qc->rwlock );
   2656 		if ( qc->bind_refcnt-- ) {
   2657 			Operation op2 = *op;
   2658 			if ( pc_setpw( &op2, &op->orb_cred, cm ) == LDAP_SUCCESS )
   2659 				bci->qc->bindref_time = op->o_time + bci->qc->qtemp->bindttr;
   2660 		} else {
   2661 			bci->qc = NULL;
   2662 			delete = 1;
   2663 		}
   2664 		ldap_pvt_thread_rdwr_wunlock( &qc->rwlock );
   2665 		if ( delete ) free_query(qc);
   2666 	}
   2667 	return SLAP_CB_CONTINUE;
   2668 }
   2669 
   2670 static Filter *
   2671 pc_bind_attrs( Operation *op, Entry *e, QueryTemplate *temp,
   2672 	struct berval *fbv )
   2673 {
   2674 	int i, len = 0;
   2675 	struct berval *vals, pres = BER_BVC("*");
   2676 	char *p1, *p2;
   2677 	Attribute *a;
   2678 
   2679 	vals = op->o_tmpalloc( temp->bindnattrs * sizeof( struct berval ),
   2680 		op->o_tmpmemctx );
   2681 
   2682 	for ( i=0; i<temp->bindnattrs; i++ ) {
   2683 		a = attr_find( e->e_attrs, temp->bindfattrs[i] );
   2684 		if ( a && a->a_vals ) {
   2685 			vals[i] = a->a_vals[0];
   2686 			len += a->a_vals[0].bv_len;
   2687 		} else {
   2688 			vals[i] = pres;
   2689 		}
   2690 	}
   2691 	fbv->bv_len = len + temp->bindftemp.bv_len;
   2692 	fbv->bv_val = op->o_tmpalloc( fbv->bv_len + 1, op->o_tmpmemctx );
   2693 
   2694 	p1 = temp->bindftemp.bv_val;
   2695 	p2 = fbv->bv_val;
   2696 	i = 0;
   2697 	while ( *p1 ) {
   2698 		*p2++ = *p1;
   2699 		if ( p1[0] == '=' && p1[1] == ')' ) {
   2700 			AC_MEMCPY( p2, vals[i].bv_val, vals[i].bv_len );
   2701 			p2 += vals[i].bv_len;
   2702 			i++;
   2703 		}
   2704 		p1++;
   2705 	}
   2706 	*p2 = '\0';
   2707 	op->o_tmpfree( vals, op->o_tmpmemctx );
   2708 
   2709 	/* FIXME: are we sure str2filter_x can't fail?
   2710 	 * caller needs to check */
   2711 	{
   2712 		Filter *f = str2filter_x( op, fbv->bv_val );
   2713 		assert( f != NULL );
   2714 		return f;
   2715 	}
   2716 }
   2717 
   2718 /* Check if the requested entry is from the cache and has a valid
   2719  * ttr and password hash
   2720  */
   2721 static int
   2722 pc_bind_search( Operation *op, SlapReply *rs )
   2723 {
   2724 	if ( rs->sr_type == REP_SEARCH ) {
   2725 		bindinfo *pbi = op->o_callback->sc_private;
   2726 
   2727 		/* We only care if this is an already cached result and we're
   2728 		 * below the refresh time, or we're offline.
   2729 		 */
   2730 		if ( pbi->bi_cq ) {
   2731 			if (( pbi->bi_cm->cc_paused & PCACHE_CC_OFFLINE ) ||
   2732 				op->o_time < pbi->bi_cq->bindref_time ) {
   2733 				Attribute *a;
   2734 
   2735 				/* See if a recognized password is hashed here */
   2736 				a = attr_find( rs->sr_entry->e_attrs,
   2737 					slap_schema.si_ad_userPassword );
   2738 				if ( a && a->a_vals[0].bv_val[0] == '{' &&
   2739 					lutil_passwd_scheme( a->a_vals[0].bv_val ))
   2740 					pbi->bi_flags |= BI_HASHED;
   2741 			} else {
   2742 				Debug( pcache_debug, "pc_bind_search: cache is stale, "
   2743 					"reftime: %ld, current time: %ld\n",
   2744 					pbi->bi_cq->bindref_time, op->o_time );
   2745 			}
   2746 		} else if ( pbi->bi_si ) {
   2747 			/* This search result is going into the cache */
   2748 			struct berval fbv;
   2749 			Filter *f;
   2750 
   2751 			filter_free( pbi->bi_si->query.filter );
   2752 			f = pc_bind_attrs( op, rs->sr_entry, pbi->bi_templ, &fbv );
   2753 			op->o_tmpfree( fbv.bv_val, op->o_tmpmemctx );
   2754 			pbi->bi_si->query.filter = filter_dup( f, NULL );
   2755 			filter_free_x( op, f, 1 );
   2756 		}
   2757 	}
   2758 	return 0;
   2759 }
   2760 
   2761 /* We always want pc_bind_search to run after the search handlers */
   2762 static int
   2763 pc_bind_resp( Operation *op, SlapReply *rs )
   2764 {
   2765 	bindinfo *pbi = op->o_callback->sc_private;
   2766 	if ( !( pbi->bi_flags & BI_DIDCB )) {
   2767 		slap_callback *sc = op->o_callback;
   2768 		while ( sc && sc->sc_response != pcache_response )
   2769 			sc = sc->sc_next;
   2770 		if ( !sc )
   2771 			sc = op->o_callback;
   2772 		pbi->bi_cb.sc_next = sc->sc_next;
   2773 		sc->sc_next = &pbi->bi_cb;
   2774 		pbi->bi_flags |= BI_DIDCB;
   2775 	}
   2776 	return SLAP_CB_CONTINUE;
   2777 }
   2778 
   2779 #ifdef PCACHE_CONTROL_PRIVDB
   2780 static int
   2781 pcache_op_privdb(
   2782 	Operation		*op,
   2783 	SlapReply		*rs )
   2784 {
   2785 	slap_overinst 	*on = (slap_overinst *)op->o_bd->bd_info;
   2786 	cache_manager 	*cm = on->on_bi.bi_private;
   2787 	slap_callback	*save_cb;
   2788 	slap_op_t	type;
   2789 
   2790 	/* skip if control is unset */
   2791 	if ( op->o_ctrlflag[ privDB_cid ] != SLAP_CONTROL_CRITICAL ) {
   2792 		return SLAP_CB_CONTINUE;
   2793 	}
   2794 
   2795 	/* The cache DB isn't open yet */
   2796 	if ( cm->defer_db_open ) {
   2797 		send_ldap_error( op, rs, LDAP_UNAVAILABLE,
   2798 			"pcachePrivDB: cacheDB not available" );
   2799 		return rs->sr_err;
   2800 	}
   2801 
   2802 	/* FIXME: might be a little bit exaggerated... */
   2803 	if ( !be_isroot( op ) ) {
   2804 		save_cb = op->o_callback;
   2805 		op->o_callback = NULL;
   2806 		send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM,
   2807 			"pcachePrivDB: operation not allowed" );
   2808 		op->o_callback = save_cb;
   2809 
   2810 		return rs->sr_err;
   2811 	}
   2812 
   2813 	/* map tag to operation */
   2814 	type = slap_req2op( op->o_tag );
   2815 	if ( type != SLAP_OP_LAST ) {
   2816 		BackendInfo	*bi = cm->db.bd_info;
   2817 		int		rc;
   2818 
   2819 		/* execute, if possible */
   2820 		if ( (&bi->bi_op_bind)[ type ] ) {
   2821 			Operation	op2 = *op;
   2822 
   2823 			op2.o_bd = &cm->db;
   2824 
   2825 			rc = (&bi->bi_op_bind)[ type ]( &op2, rs );
   2826 			if ( type == SLAP_OP_BIND && rc == LDAP_SUCCESS ) {
   2827 				op->o_conn->c_authz_cookie = cm->db.be_private;
   2828 			}
   2829 
   2830 			return rs->sr_err;
   2831 		}
   2832 	}
   2833 
   2834 	/* otherwise fall back to error */
   2835 	save_cb = op->o_callback;
   2836 	op->o_callback = NULL;
   2837 	send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM,
   2838 		"operation not supported with pcachePrivDB control" );
   2839 	op->o_callback = save_cb;
   2840 
   2841 	return rs->sr_err;
   2842 }
   2843 #endif /* PCACHE_CONTROL_PRIVDB */
   2844 
   2845 static int
   2846 pcache_op_bind(
   2847 	Operation		*op,
   2848 	SlapReply		*rs )
   2849 {
   2850 	slap_overinst 	*on = (slap_overinst *)op->o_bd->bd_info;
   2851 	cache_manager 	*cm = on->on_bi.bi_private;
   2852 	QueryTemplate *temp;
   2853 	Entry *e;
   2854 	slap_callback	cb = { 0 }, *sc;
   2855 	bindinfo bi = { 0 };
   2856 	bindcacheinfo *bci;
   2857 	Operation op2;
   2858 	int rc;
   2859 
   2860 #ifdef PCACHE_CONTROL_PRIVDB
   2861 	if ( op->o_ctrlflag[ privDB_cid ] == SLAP_CONTROL_CRITICAL )
   2862 		return pcache_op_privdb( op, rs );
   2863 #endif /* PCACHE_CONTROL_PRIVDB */
   2864 
   2865 	/* Skip if we're not configured for Binds, or cache DB isn't open yet */
   2866 	if ( !cm->cache_binds || cm->defer_db_open )
   2867 		return SLAP_CB_CONTINUE;
   2868 
   2869 	/* First find a matching template with Bind info */
   2870 	for ( temp=cm->qm->templates; temp; temp=temp->qmnext ) {
   2871 		if ( temp->bindttr && dnIsSuffix( &op->o_req_ndn, &temp->bindbase ))
   2872 			break;
   2873 	}
   2874 	/* Didn't find a suitable template, just passthru */
   2875 	if ( !temp )
   2876 		return SLAP_CB_CONTINUE;
   2877 
   2878 	/* See if the entry is already locally cached. If so, we can
   2879 	 * populate the query filter to retrieve the cached query. We
   2880 	 * need to check the bindrefresh time in the query.
   2881 	 */
   2882 	op2 = *op;
   2883 	op2.o_dn = op->o_bd->be_rootdn;
   2884 	op2.o_ndn = op->o_bd->be_rootndn;
   2885 
   2886 	op2.o_bd = &cm->db;
   2887 	e = NULL;
   2888 	rc = be_entry_get_rw( &op2, &op->o_req_ndn, NULL, NULL, 0, &e );
   2889 	if ( rc == LDAP_SUCCESS && e ) {
   2890 		bi.bi_flags |= BI_LOOKUP;
   2891 		op2.ors_filter = pc_bind_attrs( op, e, temp, &op2.ors_filterstr );
   2892 		be_entry_release_r( &op2, e );
   2893 	} else {
   2894 		op2.ors_filter = temp->bindfilter;
   2895 		op2.ors_filterstr = temp->bindfilterstr;
   2896 	}
   2897 
   2898 	op2.o_bd = op->o_bd;
   2899 	op2.o_tag = LDAP_REQ_SEARCH;
   2900 	op2.ors_scope = LDAP_SCOPE_BASE;
   2901 	op2.ors_deref = LDAP_DEREF_NEVER;
   2902 	op2.ors_slimit = 1;
   2903 	op2.ors_tlimit = SLAP_NO_LIMIT;
   2904 	op2.ors_limit = NULL;
   2905 	op2.ors_attrs = cm->qm->attr_sets[temp->attr_set_index].attrs;
   2906 	op2.ors_attrsonly = 0;
   2907 
   2908 	/* We want to invoke search at the same level of the stack
   2909 	 * as we're already at...
   2910 	 */
   2911 	bi.bi_cm = cm;
   2912 	bi.bi_templ = temp;
   2913 
   2914 	bi.bi_cb.sc_response = pc_bind_search;
   2915 	bi.bi_cb.sc_private = &bi;
   2916 	cb.sc_private = &bi;
   2917 	cb.sc_response = pc_bind_resp;
   2918 	op2.o_callback = &cb;
   2919 	overlay_op_walk( &op2, rs, op_search, on->on_info, on );
   2920 
   2921 	/* OK, just bind locally */
   2922 	if ( bi.bi_flags & BI_HASHED ) {
   2923 		int delete = 0;
   2924 		BackendDB *be = op->o_bd;
   2925 		op->o_bd = &cm->db;
   2926 
   2927 		Debug( pcache_debug, "pcache_op_bind: CACHED BIND for %s\n",
   2928 			op->o_req_dn.bv_val );
   2929 
   2930 		if ( op->o_bd->be_bind( op, rs ) == LDAP_SUCCESS ) {
   2931 			op->o_conn->c_authz_cookie = cm->db.be_private;
   2932 		}
   2933 		op->o_bd = be;
   2934 		ldap_pvt_thread_rdwr_wlock( &bi.bi_cq->rwlock );
   2935 		if ( !bi.bi_cq->bind_refcnt-- ) {
   2936 			delete = 1;
   2937 		}
   2938 		ldap_pvt_thread_rdwr_wunlock( &bi.bi_cq->rwlock );
   2939 		if ( delete ) free_query( bi.bi_cq );
   2940 		return rs->sr_err;
   2941 	}
   2942 
   2943 	/* We have a cached query to work with */
   2944 	if ( bi.bi_cq ) {
   2945 		sc = op->o_tmpalloc( sizeof(slap_callback) + sizeof(bindcacheinfo),
   2946 			op->o_tmpmemctx );
   2947 		sc->sc_response = pc_bind_save;
   2948 		sc->sc_cleanup = NULL;
   2949 		sc->sc_private = sc+1;
   2950 		sc->sc_writewait = NULL;
   2951 		bci = sc->sc_private;
   2952 		sc->sc_next = op->o_callback;
   2953 		op->o_callback = sc;
   2954 		bci->on = on;
   2955 		bci->qc = bi.bi_cq;
   2956 	}
   2957 	return SLAP_CB_CONTINUE;
   2958 }
   2959 
   2960 static slap_response refresh_merge;
   2961 
   2962 static int
   2963 pcache_op_search(
   2964 	Operation	*op,
   2965 	SlapReply	*rs )
   2966 {
   2967 	slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
   2968 	cache_manager *cm = on->on_bi.bi_private;
   2969 	query_manager*		qm = cm->qm;
   2970 
   2971 	int i = -1;
   2972 
   2973 	Query		query;
   2974 	QueryTemplate	*qtemp = NULL;
   2975 	bindinfo *pbi = NULL;
   2976 
   2977 	int 		attr_set = -1;
   2978 	CachedQuery 	*answerable = NULL;
   2979 	int 		cacheable = 0;
   2980 
   2981 	struct berval	tempstr;
   2982 
   2983 #ifdef PCACHE_CONTROL_PRIVDB
   2984 	if ( op->o_ctrlflag[ privDB_cid ] == SLAP_CONTROL_CRITICAL ) {
   2985 		return pcache_op_privdb( op, rs );
   2986 	}
   2987 #endif /* PCACHE_CONTROL_PRIVDB */
   2988 
   2989 	/* The cache DB isn't open yet */
   2990 	if ( cm->defer_db_open ) {
   2991 		send_ldap_error( op, rs, LDAP_UNAVAILABLE,
   2992 			"pcachePrivDB: cacheDB not available" );
   2993 		return rs->sr_err;
   2994 	}
   2995 
   2996 	/* pickup runtime ACL changes */
   2997 	cm->db.be_acl = op->o_bd->be_acl;
   2998 
   2999 	{
   3000 		/* See if we're processing a Bind request
   3001 		 * or a cache refresh */
   3002 		slap_callback *cb = op->o_callback;
   3003 
   3004 		for ( ; cb; cb=cb->sc_next ) {
   3005 			if ( cb->sc_response == pc_bind_resp ) {
   3006 				pbi = cb->sc_private;
   3007 				break;
   3008 			}
   3009 			if ( cb->sc_response == refresh_merge ) {
   3010 				/* This is a refresh, do not search the cache */
   3011 				return SLAP_CB_CONTINUE;
   3012 			}
   3013 		}
   3014 	}
   3015 
   3016 	/* FIXME: cannot cache/answer requests with pagedResults control */
   3017 
   3018 	query.filter = op->ors_filter;
   3019 
   3020 	if ( pbi ) {
   3021 		query.base = pbi->bi_templ->bindbase;
   3022 		query.scope = pbi->bi_templ->bindscope;
   3023 		attr_set = pbi->bi_templ->attr_set_index;
   3024 		cacheable = 1;
   3025 		qtemp = pbi->bi_templ;
   3026 		if ( pbi->bi_flags & BI_LOOKUP )
   3027 			answerable = qm->qcfunc(op, qm, &query, qtemp);
   3028 
   3029 	} else {
   3030 		tempstr.bv_val = op->o_tmpalloc( op->ors_filterstr.bv_len+1,
   3031 			op->o_tmpmemctx );
   3032 		tempstr.bv_len = 0;
   3033 		if ( filter2template( op, op->ors_filter, &tempstr ))
   3034 		{
   3035 			op->o_tmpfree( tempstr.bv_val, op->o_tmpmemctx );
   3036 			return SLAP_CB_CONTINUE;
   3037 		}
   3038 
   3039 		Debug( pcache_debug, "query template of incoming query = %s\n",
   3040 						tempstr.bv_val );
   3041 
   3042 		/* find attr set */
   3043 		attr_set = get_attr_set(op->ors_attrs, qm, cm->numattrsets);
   3044 
   3045 		query.base = op->o_req_ndn;
   3046 		query.scope = op->ors_scope;
   3047 
   3048 		/* check for query containment */
   3049 		if (attr_set > -1) {
   3050 			QueryTemplate *qt = qm->attr_sets[attr_set].templates;
   3051 			for (; qt; qt = qt->qtnext ) {
   3052 				/* find if template i can potentially answer tempstr */
   3053 				if ( ber_bvstrcasecmp( &qt->querystr, &tempstr ) != 0 )
   3054 					continue;
   3055 				cacheable = 1;
   3056 				qtemp = qt;
   3057 				Debug( pcache_debug, "Entering QC, querystr = %s\n",
   3058 						op->ors_filterstr.bv_val );
   3059 				answerable = qm->qcfunc(op, qm, &query, qt);
   3060 
   3061 				/* if != NULL, rlocks qtemp->t_rwlock */
   3062 				if (answerable)
   3063 					break;
   3064 			}
   3065 		}
   3066 		op->o_tmpfree( tempstr.bv_val, op->o_tmpmemctx );
   3067 	}
   3068 
   3069 	if (answerable) {
   3070 		BackendDB	*save_bd = op->o_bd;
   3071 
   3072 		ldap_pvt_thread_mutex_lock( &answerable->answerable_cnt_mutex );
   3073 		answerable->answerable_cnt++;
   3074 		/* we only care about refcnts if we're refreshing */
   3075 		if ( answerable->refresh_time )
   3076 			answerable->refcnt++;
   3077 		Debug( pcache_debug, "QUERY ANSWERABLE (answered %lu times)\n",
   3078 			answerable->answerable_cnt );
   3079 		ldap_pvt_thread_mutex_unlock( &answerable->answerable_cnt_mutex );
   3080 
   3081 		ldap_pvt_thread_rdwr_wlock(&answerable->rwlock);
   3082 		if ( BER_BVISNULL( &answerable->q_uuid )) {
   3083 			/* No entries cached, just an empty result set */
   3084 			i = rs->sr_err = 0;
   3085 			send_ldap_result( op, rs );
   3086 		} else {
   3087 			/* Let Bind know we used a cached query */
   3088 			if ( pbi ) {
   3089 				answerable->bind_refcnt++;
   3090 				pbi->bi_cq = answerable;
   3091 			}
   3092 
   3093 			op->o_bd = &cm->db;
   3094 			if ( cm->response_cb == PCACHE_RESPONSE_CB_TAIL ) {
   3095 				slap_callback cb;
   3096 				/* The cached entry was already processed by any
   3097 				 * other overlays, so don't let it get processed again.
   3098 				 *
   3099 				 * This loop removes over_back_response from the stack.
   3100 				 */
   3101 				if ( overlay_callback_after_backover( op, &cb, 0) == 0 ) {
   3102 					slap_callback **scp;
   3103 					for ( scp = &op->o_callback; *scp != NULL;
   3104 						scp = &(*scp)->sc_next ) {
   3105 						if ( (*scp)->sc_next == &cb ) {
   3106 							*scp = cb.sc_next;
   3107 							break;
   3108 						}
   3109 					}
   3110 				}
   3111 			}
   3112 			i = cm->db.bd_info->bi_op_search( op, rs );
   3113 		}
   3114 		ldap_pvt_thread_rdwr_wunlock(&answerable->rwlock);
   3115 		/* locked by qtemp->qcfunc (query_containment) */
   3116 		ldap_pvt_thread_rdwr_runlock(&qtemp->t_rwlock);
   3117 		op->o_bd = save_bd;
   3118 		return i;
   3119 	}
   3120 
   3121 	Debug( pcache_debug, "QUERY NOT ANSWERABLE\n" );
   3122 
   3123 	ldap_pvt_thread_mutex_lock(&cm->cache_mutex);
   3124 	if (cm->num_cached_queries >= cm->max_queries) {
   3125 		cacheable = 0;
   3126 	}
   3127 	ldap_pvt_thread_mutex_unlock(&cm->cache_mutex);
   3128 
   3129 	if (op->ors_attrsonly)
   3130 		cacheable = 0;
   3131 
   3132 	if (cacheable) {
   3133 		slap_callback		*cb;
   3134 		struct search_info	*si;
   3135 
   3136 		Debug( pcache_debug, "QUERY CACHEABLE\n" );
   3137 		query.filter = filter_dup(op->ors_filter, NULL);
   3138 
   3139 		cb = op->o_tmpalloc( sizeof(*cb) + sizeof(*si), op->o_tmpmemctx );
   3140 		cb->sc_response = pcache_response;
   3141 		cb->sc_cleanup = pcache_op_cleanup;
   3142 		cb->sc_private = (cb+1);
   3143 		cb->sc_writewait = 0;
   3144 		si = cb->sc_private;
   3145 		si->on = on;
   3146 		si->query = query;
   3147 		si->qtemp = qtemp;
   3148 		si->max = cm->num_entries_limit ;
   3149 		si->over = 0;
   3150 		si->count = 0;
   3151 		si->slimit = 0;
   3152 		si->slimit_exceeded = 0;
   3153 		si->caching_reason = PC_IGNORE;
   3154 		if ( op->ors_slimit > 0 && op->ors_slimit < cm->num_entries_limit ) {
   3155 			si->slimit = op->ors_slimit;
   3156 			op->ors_slimit = cm->num_entries_limit;
   3157 		}
   3158 		si->head = NULL;
   3159 		si->tail = NULL;
   3160 		si->swap_saved_attrs = 1;
   3161 		si->save_attrs = op->ors_attrs;
   3162 		si->pbi = pbi;
   3163 		if ( pbi )
   3164 			pbi->bi_si = si;
   3165 
   3166 		op->ors_attrs = qtemp->t_attrs.attrs;
   3167 
   3168 		if ( cm->response_cb == PCACHE_RESPONSE_CB_HEAD ) {
   3169 			cb->sc_next = op->o_callback;
   3170 			op->o_callback = cb;
   3171 
   3172 		} else {
   3173 			slap_callback		**pcb;
   3174 
   3175 			/* need to move the callback at the end, in case other
   3176 			 * overlays are present, so that the final entry is
   3177 			 * actually cached */
   3178 			cb->sc_next = NULL;
   3179 			for ( pcb = &op->o_callback; *pcb; pcb = &(*pcb)->sc_next );
   3180 			*pcb = cb;
   3181 		}
   3182 
   3183 	} else {
   3184 		Debug( pcache_debug, "QUERY NOT CACHEABLE\n" );
   3185 	}
   3186 
   3187 	return SLAP_CB_CONTINUE;
   3188 }
   3189 
   3190 static int
   3191 get_attr_set(
   3192 	AttributeName* attrs,
   3193 	query_manager* qm,
   3194 	int num )
   3195 {
   3196 	int i = 0;
   3197 	int count = 0;
   3198 
   3199 	if ( attrs ) {
   3200 		for ( ; attrs[i].an_name.bv_val; i++ ) {
   3201 			/* only count valid attribute names
   3202 			 * (searches ignore others, this overlay does the same) */
   3203 			if ( attrs[i].an_desc ) {
   3204 				count++;
   3205 			}
   3206 		}
   3207 	}
   3208 
   3209 	/* recognize default or explicit single "*" */
   3210 	if ( ! attrs ||
   3211 		( i == 1 && bvmatch( &attrs[0].an_name, slap_bv_all_user_attrs ) ) )
   3212 	{
   3213 		count = 1;
   3214 		attrs = slap_anlist_all_user_attributes;
   3215 
   3216 	/* recognize implicit (no valid attributes) or explicit single "1.1" */
   3217 	} else if ( count == 0 ||
   3218 		( i == 1 && bvmatch( &attrs[0].an_name, slap_bv_no_attrs ) ) )
   3219 	{
   3220 		count = 0;
   3221 		attrs = NULL;
   3222 	}
   3223 
   3224 	for ( i = 0; i < num; i++ ) {
   3225 		AttributeName *a2;
   3226 		int found = 1;
   3227 
   3228 		if ( count > qm->attr_sets[i].count ) {
   3229 			if ( qm->attr_sets[i].count &&
   3230 				bvmatch( &qm->attr_sets[i].attrs[0].an_name, slap_bv_all_user_attrs )) {
   3231 				break;
   3232 			}
   3233 			continue;
   3234 		}
   3235 
   3236 		if ( !count ) {
   3237 			if ( !qm->attr_sets[i].count ) {
   3238 				break;
   3239 			}
   3240 			continue;
   3241 		}
   3242 
   3243 		for ( a2 = attrs; a2->an_name.bv_val; a2++ ) {
   3244 			if ( !a2->an_desc && !bvmatch( &a2->an_name, slap_bv_all_user_attrs ) ) continue;
   3245 
   3246 			if ( !an_find( qm->attr_sets[i].attrs, &a2->an_name ) ) {
   3247 				found = 0;
   3248 				break;
   3249 			}
   3250 		}
   3251 
   3252 		if ( found ) {
   3253 			break;
   3254 		}
   3255 	}
   3256 
   3257 	if ( i == num ) {
   3258 		i = -1;
   3259 	}
   3260 
   3261 	return i;
   3262 }
   3263 
   3264 /* Refresh a cached query:
   3265  * 1: Replay the query on the remote DB and merge each entry into
   3266  * the local DB. Remember the DNs of each remote entry.
   3267  * 2: Search the local DB for all entries matching this queryID.
   3268  * Delete any entry whose DN is not in the list from (1).
   3269  */
   3270 typedef struct dnlist {
   3271 	struct dnlist *next;
   3272 	struct berval dn;
   3273 	char delete;
   3274 } dnlist;
   3275 
   3276 typedef struct refresh_info {
   3277 	dnlist *ri_dns;
   3278 	dnlist *ri_tail;
   3279 	dnlist *ri_dels;
   3280 	BackendDB *ri_be;
   3281 	CachedQuery *ri_q;
   3282 } refresh_info;
   3283 
   3284 static dnlist *dnl_alloc( Operation *op, struct berval *bvdn )
   3285 {
   3286 	dnlist *dn = op->o_tmpalloc( sizeof(dnlist) + bvdn->bv_len + 1,
   3287 			op->o_tmpmemctx );
   3288 	dn->dn.bv_len = bvdn->bv_len;
   3289 	dn->dn.bv_val = (char *)(dn+1);
   3290 	AC_MEMCPY( dn->dn.bv_val, bvdn->bv_val, dn->dn.bv_len );
   3291 	dn->dn.bv_val[dn->dn.bv_len] = '\0';
   3292 	return dn;
   3293 }
   3294 
   3295 static int
   3296 refresh_merge( Operation *op, SlapReply *rs )
   3297 {
   3298 	if ( rs->sr_type == REP_SEARCH ) {
   3299 		refresh_info *ri = op->o_callback->sc_private;
   3300 		Entry *e;
   3301 		dnlist *dnl;
   3302 		slap_callback *ocb;
   3303 		int rc;
   3304 
   3305 		ocb = op->o_callback;
   3306 		/* Find local entry, merge */
   3307 		op->o_bd = ri->ri_be;
   3308 		rc = be_entry_get_rw( op, &rs->sr_entry->e_nname, NULL, NULL, 0, &e );
   3309 		if ( rc != LDAP_SUCCESS || e == NULL ) {
   3310 			/* No local entry, just add it. FIXME: we are not checking
   3311 			 * the cache entry limit here
   3312 			 */
   3313 			 merge_entry( op, rs->sr_entry, 1, &ri->ri_q->q_uuid );
   3314 		} else {
   3315 			/* Entry exists, update it */
   3316 			Entry ne;
   3317 			Attribute *a, **b;
   3318 			Modifications *modlist, *mods = NULL;
   3319 			const char* 	text = NULL;
   3320 			char			textbuf[SLAP_TEXT_BUFLEN];
   3321 			size_t			textlen = sizeof(textbuf);
   3322 			slap_callback cb = { NULL, slap_null_cb, NULL, NULL };
   3323 
   3324 			ne = *e;
   3325 			b = &ne.e_attrs;
   3326 			/* Get a copy of only the attrs we requested */
   3327 			for ( a=e->e_attrs; a; a=a->a_next ) {
   3328 				if ( ad_inlist( a->a_desc, rs->sr_attrs )) {
   3329 					*b = attr_alloc( a->a_desc );
   3330 					*(*b) = *a;
   3331 					/* The actual values still belong to e */
   3332 					(*b)->a_flags |= SLAP_ATTR_DONT_FREE_VALS |
   3333 						SLAP_ATTR_DONT_FREE_DATA;
   3334 					b = &((*b)->a_next);
   3335 				}
   3336 			}
   3337 			*b = NULL;
   3338 			slap_entry2mods( rs->sr_entry, &modlist, &text, textbuf, textlen );
   3339 			syncrepl_diff_entry( op, ne.e_attrs, rs->sr_entry->e_attrs,
   3340 				&mods, &modlist, 0 );
   3341 			be_entry_release_r( op, e );
   3342 			attrs_free( ne.e_attrs );
   3343 			slap_mods_free( modlist, 1 );
   3344 			/* mods is NULL if there are no changes */
   3345 			if ( mods ) {
   3346 				SlapReply rs2 = { REP_RESULT };
   3347 				struct berval dn = op->o_req_dn;
   3348 				struct berval ndn = op->o_req_ndn;
   3349 				op->o_tag = LDAP_REQ_MODIFY;
   3350 				op->orm_modlist = mods;
   3351 				op->o_req_dn = rs->sr_entry->e_name;
   3352 				op->o_req_ndn = rs->sr_entry->e_nname;
   3353 				op->o_callback = &cb;
   3354 				op->o_bd->be_modify( op, &rs2 );
   3355 				rs->sr_err = rs2.sr_err;
   3356 				rs_assert_done( &rs2 );
   3357 				slap_mods_free( mods, 1 );
   3358 				op->o_req_dn = dn;
   3359 				op->o_req_ndn = ndn;
   3360 			}
   3361 		}
   3362 
   3363 		/* Add DN to list */
   3364 		dnl = dnl_alloc( op, &rs->sr_entry->e_nname );
   3365 		dnl->next = NULL;
   3366 		if ( ri->ri_tail ) {
   3367 			ri->ri_tail->next = dnl;
   3368 		} else {
   3369 			ri->ri_dns = dnl;
   3370 		}
   3371 		ri->ri_tail = dnl;
   3372 		op->o_callback = ocb;
   3373 	}
   3374 	return 0;
   3375 }
   3376 
   3377 static int
   3378 refresh_purge( Operation *op, SlapReply *rs )
   3379 {
   3380 	if ( rs->sr_type == REP_SEARCH ) {
   3381 		refresh_info *ri = op->o_callback->sc_private;
   3382 		dnlist **dn;
   3383 		int del = 1;
   3384 
   3385 		/* Did the entry exist on the remote? */
   3386 		for ( dn=&ri->ri_dns; *dn; dn = &(*dn)->next ) {
   3387 			if ( dn_match( &(*dn)->dn, &rs->sr_entry->e_nname )) {
   3388 				dnlist *dnext = (*dn)->next;
   3389 				op->o_tmpfree( *dn, op->o_tmpmemctx );
   3390 				*dn = dnext;
   3391 				del = 0;
   3392 				break;
   3393 			}
   3394 		}
   3395 		/* No, so put it on the list to delete */
   3396 		if ( del ) {
   3397 			Attribute *a;
   3398 			dnlist *dnl = dnl_alloc( op, &rs->sr_entry->e_nname );
   3399 			dnl->next = ri->ri_dels;
   3400 			ri->ri_dels = dnl;
   3401 			a = attr_find( rs->sr_entry->e_attrs, ad_queryId );
   3402 			/* If ours is the only queryId, delete entry */
   3403 			dnl->delete = ( a->a_numvals == 1 );
   3404 		}
   3405 	}
   3406 	return 0;
   3407 }
   3408 
   3409 static int
   3410 refresh_query( Operation *op, CachedQuery *query, slap_overinst *on )
   3411 {
   3412 	SlapReply rs = {REP_RESULT};
   3413 	slap_callback cb = { 0 };
   3414 	refresh_info ri = { 0 };
   3415 	char filter_str[ LDAP_LUTIL_UUIDSTR_BUFSIZE + STRLENOF( "(pcacheQueryID=)" ) ];
   3416 	AttributeAssertion	ava = ATTRIBUTEASSERTION_INIT;
   3417 	Filter filter = {LDAP_FILTER_EQUALITY};
   3418 	AttributeName attrs[ 2 ] = {{{ 0 }}};
   3419 	dnlist *dn;
   3420 	int rc;
   3421 
   3422 	ldap_pvt_thread_mutex_lock( &query->answerable_cnt_mutex );
   3423 	query->refcnt = 0;
   3424 	ldap_pvt_thread_mutex_unlock( &query->answerable_cnt_mutex );
   3425 
   3426 	cb.sc_response = refresh_merge;
   3427 	cb.sc_private = &ri;
   3428 
   3429 	/* cache DB */
   3430 	ri.ri_be = op->o_bd;
   3431 	ri.ri_q = query;
   3432 
   3433 	op->o_tag = LDAP_REQ_SEARCH;
   3434 	op->o_protocol = LDAP_VERSION3;
   3435 	op->o_callback = &cb;
   3436 	op->o_do_not_cache = 1;
   3437 
   3438 	op->o_req_dn = query->qbase->base;
   3439 	op->o_req_ndn = query->qbase->base;
   3440 	op->ors_scope = query->scope;
   3441 	op->ors_deref = LDAP_DEREF_NEVER;
   3442 	op->ors_slimit = SLAP_NO_LIMIT;
   3443 	op->ors_tlimit = SLAP_NO_LIMIT;
   3444 	op->ors_limit = NULL;
   3445 	op->ors_filter = query->filter;
   3446 	filter2bv_x( op, query->filter, &op->ors_filterstr );
   3447 	op->ors_attrs = query->qtemp->t_attrs.attrs;
   3448 	op->ors_attrsonly = 0;
   3449 
   3450 	op->o_bd = on->on_info->oi_origdb;
   3451 	rc = op->o_bd->be_search( op, &rs );
   3452 	if ( rc ) {
   3453 		op->o_bd = ri.ri_be;
   3454 		goto leave;
   3455 	}
   3456 
   3457 	/* Get the DNs of all entries matching this query */
   3458 	cb.sc_response = refresh_purge;
   3459 
   3460 	op->o_bd = ri.ri_be;
   3461 	op->o_req_dn = op->o_bd->be_suffix[0];
   3462 	op->o_req_ndn = op->o_bd->be_nsuffix[0];
   3463 	op->ors_scope = LDAP_SCOPE_SUBTREE;
   3464 	op->ors_deref = LDAP_DEREF_NEVER;
   3465 	op->ors_filterstr.bv_len = snprintf(filter_str, sizeof(filter_str),
   3466 		"(%s=%s)", ad_queryId->ad_cname.bv_val, query->q_uuid.bv_val);
   3467 	filter.f_ava = &ava;
   3468 	filter.f_av_desc = ad_queryId;
   3469 	filter.f_av_value = query->q_uuid;
   3470 	attrs[ 0 ].an_desc = ad_queryId;
   3471 	attrs[ 0 ].an_name = ad_queryId->ad_cname;
   3472 	op->ors_attrs = attrs;
   3473 	op->ors_attrsonly = 0;
   3474 	rs_reinit( &rs, REP_RESULT );
   3475 	rc = op->o_bd->be_search( op, &rs );
   3476 	if ( rc ) goto leave;
   3477 
   3478 	while (( dn = ri.ri_dels )) {
   3479 		op->o_req_dn = dn->dn;
   3480 		op->o_req_ndn = dn->dn;
   3481 		rs_reinit( &rs, REP_RESULT );
   3482 		if ( dn->delete ) {
   3483 			op->o_tag = LDAP_REQ_DELETE;
   3484 			op->o_bd->be_delete( op, &rs );
   3485 		} else {
   3486 			Modifications mod;
   3487 			struct berval vals[2];
   3488 
   3489 			vals[0] = query->q_uuid;
   3490 			BER_BVZERO( &vals[1] );
   3491 			mod.sml_op = LDAP_MOD_DELETE;
   3492 			mod.sml_flags = 0;
   3493 			mod.sml_desc = ad_queryId;
   3494 			mod.sml_type = ad_queryId->ad_cname;
   3495 			mod.sml_values = vals;
   3496 			mod.sml_nvalues = NULL;
   3497 			mod.sml_numvals = 1;
   3498 			mod.sml_next = NULL;
   3499 
   3500 			op->o_tag = LDAP_REQ_MODIFY;
   3501 			op->orm_modlist = &mod;
   3502 			op->o_bd->be_modify( op, &rs );
   3503 		}
   3504 		ri.ri_dels = dn->next;
   3505 		op->o_tmpfree( dn, op->o_tmpmemctx );
   3506 	}
   3507 
   3508 leave:
   3509 	/* reset our local heap, we're done with it */
   3510 	slap_sl_mem_create(SLAP_SLAB_SIZE, SLAP_SLAB_STACK, op->o_threadctx, 1 );
   3511 	return rc;
   3512 }
   3513 
   3514 static void*
   3515 consistency_check(
   3516 	void *ctx,
   3517 	void *arg )
   3518 {
   3519 	struct re_s *rtask = arg;
   3520 	slap_overinst *on = rtask->arg;
   3521 	cache_manager *cm = on->on_bi.bi_private;
   3522 	query_manager *qm = cm->qm;
   3523 	Connection conn = {0};
   3524 	OperationBuffer opbuf;
   3525 	Operation *op;
   3526 
   3527 	CachedQuery *query, *qprev;
   3528 	CachedQuery *expires;
   3529 	int return_val, pause = PCACHE_CC_PAUSED;
   3530 	QueryTemplate *templ;
   3531 
   3532 	/* Don't expire anything when we're offline */
   3533 	if ( cm->cc_paused & PCACHE_CC_OFFLINE ) {
   3534 		pause = PCACHE_CC_OFFLINE;
   3535 		goto leave;
   3536 	}
   3537 
   3538 	connection_fake_init( &conn, &opbuf, ctx );
   3539 	op = &opbuf.ob_op;
   3540 
   3541 	op->o_bd = &cm->db;
   3542 	op->o_dn = cm->db.be_rootdn;
   3543 	op->o_ndn = cm->db.be_rootndn;
   3544 
   3545 	cm->cc_arg = arg;
   3546 
   3547 	for (templ = qm->templates; templ; templ=templ->qmnext) {
   3548 		time_t ttl;
   3549 		if ( !templ->query_last ) continue;
   3550 		pause = 0;
   3551 		expires = NULL;
   3552 		op->o_time = slap_get_time();
   3553 		if ( !templ->ttr ) {
   3554 			ttl = templ->ttl;
   3555 			if ( templ->negttl && templ->negttl < ttl )
   3556 				ttl = templ->negttl;
   3557 			if ( templ->limitttl && templ->limitttl < ttl )
   3558 				ttl = templ->limitttl;
   3559 			/* The oldest timestamp that needs expiration checking */
   3560 			ttl += op->o_time;
   3561 		}
   3562 
   3563 		Debug( pcache_debug, "Lock CR index = %p\n",
   3564 				(void *) templ );
   3565 		ldap_pvt_thread_rdwr_wlock(&templ->t_rwlock);
   3566 		for ( query=templ->query_last; query; query=qprev ) {
   3567 			qprev = query->prev;
   3568 			if ( query->refresh_time && query->refresh_time < op->o_time ) {
   3569 				/* A refresh will extend the expiry if the query has been
   3570 				 * referenced, but not if it's unreferenced. If the
   3571 				 * expiration has been hit, then skip the refresh since
   3572 				 * we're just going to discard the result anyway.
   3573 				 */
   3574 				if ( query->refcnt )
   3575 					query->expiry_time = op->o_time + templ->ttl;
   3576 				if ( query->expiry_time > op->o_time ) {
   3577 					/* perform actual refresh below */
   3578 					continue;
   3579 				}
   3580 			}
   3581 
   3582 			if (query->expiry_time < op->o_time) {
   3583 				int rem = 0;
   3584 				if ( query != templ->query_last )
   3585 					continue;
   3586 				ldap_pvt_thread_mutex_lock(&qm->lru_mutex);
   3587 				if (query->in_lru) {
   3588 					remove_query(qm, query);
   3589 					rem = 1;
   3590 				}
   3591 				ldap_pvt_thread_mutex_unlock(&qm->lru_mutex);
   3592 				if (!rem)
   3593 					continue;
   3594 				remove_from_template(query, templ);
   3595 				Debug( pcache_debug, "TEMPLATE %p QUERIES-- %d\n",
   3596 						(void *) templ, templ->no_of_queries );
   3597 				query->prev = expires;
   3598 				expires = query;
   3599 				query->qtemp = NULL;
   3600 			} else if ( !templ->ttr && query->expiry_time > ttl ) {
   3601 				/* We don't need to check for refreshes, and this
   3602 				 * query's expiry is too new, and all subsequent queries
   3603 				 * will be newer yet. So stop looking.
   3604 				 *
   3605 				 * If we have refreshes, then we always have to walk the
   3606 				 * entire query list.
   3607 				 */
   3608 				break;
   3609 			}
   3610 		}
   3611 		Debug( pcache_debug, "Unlock CR index = %p\n",
   3612 				(void *) templ );
   3613 		ldap_pvt_thread_rdwr_wunlock(&templ->t_rwlock);
   3614 		for ( query=expires; query; query=qprev ) {
   3615 			int rem;
   3616 			qprev = query->prev;
   3617 			if ( BER_BVISNULL( &query->q_uuid ))
   3618 				return_val = 0;
   3619 			else
   3620 				return_val = remove_query_data(op, &query->q_uuid);
   3621 			Debug( pcache_debug, "STALE QUERY REMOVED, SIZE=%d\n",
   3622 						return_val );
   3623 			ldap_pvt_thread_mutex_lock(&cm->cache_mutex);
   3624 			cm->cur_entries -= return_val;
   3625 			cm->num_cached_queries--;
   3626 			Debug( pcache_debug, "STORED QUERIES = %lu\n",
   3627 					cm->num_cached_queries );
   3628 			ldap_pvt_thread_mutex_unlock(&cm->cache_mutex);
   3629 			Debug( pcache_debug,
   3630 				"STALE QUERY REMOVED, CACHE ="
   3631 				"%d entries\n",
   3632 				cm->cur_entries );
   3633 			ldap_pvt_thread_rdwr_wlock( &query->rwlock );
   3634 			if ( query->bind_refcnt-- ) {
   3635 				rem = 0;
   3636 			} else {
   3637 				rem = 1;
   3638 			}
   3639 			ldap_pvt_thread_rdwr_wunlock( &query->rwlock );
   3640 			if ( rem ) free_query(query);
   3641 		}
   3642 
   3643 		/* handle refreshes that we skipped earlier */
   3644 		if ( templ->ttr ) {
   3645 			ldap_pvt_thread_rdwr_rlock(&templ->t_rwlock);
   3646 			for ( query=templ->query_last; query; query=qprev ) {
   3647 				qprev = query->prev;
   3648 				if ( query->refresh_time && query->refresh_time < op->o_time ) {
   3649 					/* A refresh will extend the expiry if the query has been
   3650 					 * referenced, but not if it's unreferenced. If the
   3651 					 * expiration has been hit, then skip the refresh since
   3652 					 * we're just going to discard the result anyway.
   3653 					 */
   3654 					if ( query->expiry_time > op->o_time ) {
   3655 						refresh_query( op, query, on );
   3656 						query->refresh_time = op->o_time + templ->ttr;
   3657 					}
   3658 				}
   3659 			}
   3660 			ldap_pvt_thread_rdwr_runlock(&templ->t_rwlock);
   3661 		}
   3662 	}
   3663 
   3664 leave:
   3665 	ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
   3666 	if ( ldap_pvt_runqueue_isrunning( &slapd_rq, rtask )) {
   3667 		ldap_pvt_runqueue_stoptask( &slapd_rq, rtask );
   3668 	}
   3669 	/* If there were no queries, defer processing for a while */
   3670 	if ( cm->cc_paused != pause )
   3671 		cm->cc_paused = pause;
   3672 	ldap_pvt_runqueue_resched( &slapd_rq, rtask, pause );
   3673 
   3674 	ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
   3675 	return NULL;
   3676 }
   3677 
   3678 
   3679 #define MAX_ATTR_SETS 500
   3680 
   3681 enum {
   3682 	PC_MAIN = 1,
   3683 	PC_ATTR,
   3684 	PC_TEMP,
   3685 	PC_RESP,
   3686 	PC_QUERIES,
   3687 	PC_OFFLINE,
   3688 	PC_BIND,
   3689 	PC_PRIVATE_DB
   3690 };
   3691 
   3692 static ConfigDriver pc_cf_gen;
   3693 static ConfigLDAPadd pc_ldadd;
   3694 static ConfigCfAdd pc_cfadd;
   3695 
   3696 static ConfigTable pccfg[] = {
   3697 	{ "pcache", "backend> <max_entries> <numattrsets> <entry limit> "
   3698 				"<cycle_time",
   3699 		6, 6, 0, ARG_MAGIC|ARG_NO_DELETE|PC_MAIN, pc_cf_gen,
   3700 		"( OLcfgOvAt:2.1 NAME ( 'olcPcache' 'olcProxyCache' ) "
   3701 			"DESC 'Proxy Cache basic parameters' "
   3702 			"EQUALITY caseIgnoreMatch "
   3703 			"SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
   3704 	{ "pcacheAttrset", "index> <attributes...",
   3705 		2, 0, 0, ARG_MAGIC|PC_ATTR, pc_cf_gen,
   3706 		"( OLcfgOvAt:2.2 NAME ( 'olcPcacheAttrset' 'olcProxyAttrset' ) "
   3707 			"DESC 'A set of attributes to cache' "
   3708 			"EQUALITY caseIgnoreMatch "
   3709 			"SYNTAX OMsDirectoryString )", NULL, NULL },
   3710 	{ "pcacheTemplate", "filter> <attrset-index> <TTL> <negTTL> "
   3711 			"<limitTTL> <TTR",
   3712 		4, 7, 0, ARG_MAGIC|PC_TEMP, pc_cf_gen,
   3713 		"( OLcfgOvAt:2.3 NAME ( 'olcPcacheTemplate' 'olcProxyCacheTemplate' ) "
   3714 			"DESC 'Filter template, attrset, cache TTL, "
   3715 				"optional negative TTL, optional sizelimit TTL, "
   3716 				"optional TTR' "
   3717 			"EQUALITY caseIgnoreMatch "
   3718 			"SYNTAX OMsDirectoryString )", NULL, NULL },
   3719 	{ "pcachePosition", "head|tail(default)",
   3720 		2, 2, 0, ARG_MAGIC|PC_RESP, pc_cf_gen,
   3721 		"( OLcfgOvAt:2.4 NAME 'olcPcachePosition' "
   3722 			"DESC 'Response callback position in overlay stack' "
   3723 			"EQUALITY caseIgnoreMatch "
   3724 			"SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
   3725 	{ "pcacheMaxQueries", "queries",
   3726 		2, 2, 0, ARG_INT|ARG_MAGIC|PC_QUERIES, pc_cf_gen,
   3727 		"( OLcfgOvAt:2.5 NAME ( 'olcPcacheMaxQueries' 'olcProxyCacheQueries' ) "
   3728 			"DESC 'Maximum number of queries to cache' "
   3729 			"EQUALITY integerMatch "
   3730 			"SYNTAX OMsInteger SINGLE-VALUE )", NULL, NULL },
   3731 	{ "pcachePersist", "TRUE|FALSE",
   3732 		2, 2, 0, ARG_ON_OFF|ARG_OFFSET, (void *)offsetof(cache_manager, save_queries),
   3733 		"( OLcfgOvAt:2.6 NAME ( 'olcPcachePersist' 'olcProxySaveQueries' ) "
   3734 			"DESC 'Save cached queries for hot restart' "
   3735 			"EQUALITY booleanMatch "
   3736 			"SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
   3737 	{ "pcacheValidate", "TRUE|FALSE",
   3738 		2, 2, 0, ARG_ON_OFF|ARG_OFFSET, (void *)offsetof(cache_manager, check_cacheability),
   3739 		"( OLcfgOvAt:2.7 NAME ( 'olcPcacheValidate' 'olcProxyCheckCacheability' ) "
   3740 			"DESC 'Check whether the results of a query are cacheable, e.g. for schema issues' "
   3741 			"EQUALITY booleanMatch "
   3742 			"SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
   3743 	{ "pcacheOffline", "TRUE|FALSE",
   3744 		2, 2, 0, ARG_ON_OFF|ARG_MAGIC|PC_OFFLINE, pc_cf_gen,
   3745 		"( OLcfgOvAt:2.8 NAME 'olcPcacheOffline' "
   3746 			"DESC 'Set cache to offline mode and disable expiration' "
   3747 			"EQUALITY booleanMatch "
   3748 			"SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
   3749 	{ "pcacheBind", "filter> <attrset-index> <TTR> <scope> <base",
   3750 		6, 6, 0, ARG_MAGIC|PC_BIND, pc_cf_gen,
   3751 		"( OLcfgOvAt:2.9 NAME 'olcPcacheBind' "
   3752 			"DESC 'Parameters for caching Binds' "
   3753 			"EQUALITY caseIgnoreMatch "
   3754 			"SYNTAX OMsDirectoryString )", NULL, NULL },
   3755 	{ "pcache-", "private database args",
   3756 		1, 0, STRLENOF("pcache-"), ARG_MAGIC|PC_PRIVATE_DB, pc_cf_gen,
   3757 		NULL, NULL, NULL },
   3758 
   3759 	/* Legacy keywords */
   3760 	{ "proxycache", "backend> <max_entries> <numattrsets> <entry limit> "
   3761 				"<cycle_time",
   3762 		6, 6, 0, ARG_MAGIC|ARG_NO_DELETE|PC_MAIN, pc_cf_gen,
   3763 		NULL, NULL, NULL },
   3764 	{ "proxyattrset", "index> <attributes...",
   3765 		2, 0, 0, ARG_MAGIC|PC_ATTR, pc_cf_gen,
   3766 		NULL, NULL, NULL },
   3767 	{ "proxytemplate", "filter> <attrset-index> <TTL> <negTTL",
   3768 		4, 7, 0, ARG_MAGIC|PC_TEMP, pc_cf_gen,
   3769 		NULL, NULL, NULL },
   3770 	{ "response-callback", "head|tail(default)",
   3771 		2, 2, 0, ARG_MAGIC|PC_RESP, pc_cf_gen,
   3772 		NULL, NULL, NULL },
   3773 	{ "proxyCacheQueries", "queries",
   3774 		2, 2, 0, ARG_INT|ARG_MAGIC|PC_QUERIES, pc_cf_gen,
   3775 		NULL, NULL, NULL },
   3776 	{ "proxySaveQueries", "TRUE|FALSE",
   3777 		2, 2, 0, ARG_ON_OFF|ARG_OFFSET, (void *)offsetof(cache_manager, save_queries),
   3778 		NULL, NULL, NULL },
   3779 	{ "proxyCheckCacheability", "TRUE|FALSE",
   3780 		2, 2, 0, ARG_ON_OFF|ARG_OFFSET, (void *)offsetof(cache_manager, check_cacheability),
   3781 		NULL, NULL, NULL },
   3782 
   3783 	{ NULL, NULL, 0, 0, 0, ARG_IGNORED }
   3784 };
   3785 
   3786 static ConfigOCs pcocs[] = {
   3787 	{ "( OLcfgOvOc:2.1 "
   3788 		"NAME 'olcPcacheConfig' "
   3789 		"DESC 'ProxyCache configuration' "
   3790 		"SUP olcOverlayConfig "
   3791 		"MUST ( olcPcache $ olcPcacheAttrset $ olcPcacheTemplate ) "
   3792 		"MAY ( olcPcachePosition $ olcPcacheMaxQueries $ olcPcachePersist $ "
   3793 			"olcPcacheValidate $ olcPcacheOffline $ olcPcacheBind ) )",
   3794 		Cft_Overlay, pccfg, NULL, pc_cfadd },
   3795 	{ "( OLcfgOvOc:2.2 "
   3796 		"NAME 'olcPcacheDatabase' "
   3797 		"DESC 'Cache database configuration' "
   3798 		/* co_table is initialized in pcache_initialize */
   3799 		"AUXILIARY )", Cft_Misc, NULL, pc_ldadd },
   3800 	{ NULL, 0, NULL }
   3801 };
   3802 
   3803 static int pcache_db_open2( slap_overinst *on, ConfigReply *cr );
   3804 
   3805 static int
   3806 pc_ldadd_cleanup( ConfigArgs *c )
   3807 {
   3808 	slap_overinst *on = c->ca_private;
   3809 	return pcache_db_open2( on, &c->reply );
   3810 }
   3811 
   3812 static int
   3813 pc_ldadd( CfEntryInfo *p, Entry *e, ConfigArgs *ca )
   3814 {
   3815 	slap_overinst *on;
   3816 	cache_manager *cm;
   3817 
   3818 	if ( p->ce_type != Cft_Overlay || !p->ce_bi ||
   3819 		p->ce_bi->bi_cf_ocs != pcocs )
   3820 		return LDAP_CONSTRAINT_VIOLATION;
   3821 
   3822 	on = (slap_overinst *)p->ce_bi;
   3823 	cm = on->on_bi.bi_private;
   3824 	ca->be = &cm->db;
   3825 	/* Defer open if this is an LDAPadd */
   3826 	if ( CONFIG_ONLINE_ADD( ca ))
   3827 		config_push_cleanup( ca, pc_ldadd_cleanup );
   3828 	else
   3829 		cm->defer_db_open = 0;
   3830 	ca->ca_private = on;
   3831 	return LDAP_SUCCESS;
   3832 }
   3833 
   3834 static int
   3835 pc_cfadd( Operation *op, SlapReply *rs, Entry *p, ConfigArgs *ca )
   3836 {
   3837 	CfEntryInfo *pe = p->e_private;
   3838 	slap_overinst *on = (slap_overinst *)pe->ce_bi;
   3839 	cache_manager *cm = on->on_bi.bi_private;
   3840 	struct berval bv;
   3841 
   3842 	/* FIXME: should not hardcode "olcDatabase" here */
   3843 	bv.bv_len = snprintf( ca->cr_msg, sizeof( ca->cr_msg ),
   3844 		"olcDatabase=" SLAP_X_ORDERED_FMT "%s",
   3845 		0, cm->db.bd_info->bi_type );
   3846 	if ( bv.bv_len >= sizeof( ca->cr_msg ) ) {
   3847 		return -1;
   3848 	}
   3849 	bv.bv_val = ca->cr_msg;
   3850 	ca->be = &cm->db;
   3851 	cm->defer_db_open = 0;
   3852 
   3853 	/* We can only create this entry if the database is table-driven
   3854 	 */
   3855 	if ( cm->db.be_cf_ocs )
   3856 		config_build_entry( op, rs, pe, ca, &bv, cm->db.be_cf_ocs,
   3857 			&pcocs[1] );
   3858 
   3859 	return 0;
   3860 }
   3861 
   3862 static int
   3863 pc_cf_gen( ConfigArgs *c )
   3864 {
   3865 	slap_overinst	*on = (slap_overinst *)c->bi;
   3866 	cache_manager* 	cm = on->on_bi.bi_private;
   3867 	query_manager*  qm = cm->qm;
   3868 	QueryTemplate* 	temp;
   3869 	AttributeName*  attr_name;
   3870 	AttributeName* 	attrarray;
   3871 	const char* 	text=NULL;
   3872 	int		i, num, rc = 0;
   3873 	char		*ptr;
   3874 	unsigned long	t;
   3875 
   3876 	if ( c->op == SLAP_CONFIG_EMIT ) {
   3877 		struct berval bv;
   3878 		switch( c->type ) {
   3879 		case PC_MAIN:
   3880 			bv.bv_len = snprintf( c->cr_msg, sizeof( c->cr_msg ), "%s %d %d %d %ld",
   3881 				cm->db.bd_info->bi_type, cm->max_entries, cm->numattrsets,
   3882 				cm->num_entries_limit, cm->cc_period );
   3883 			bv.bv_val = c->cr_msg;
   3884 			value_add_one( &c->rvalue_vals, &bv );
   3885 			break;
   3886 		case PC_ATTR:
   3887 			for (i=0; i<cm->numattrsets; i++) {
   3888 				if ( !qm->attr_sets[i].count ) continue;
   3889 
   3890 				bv.bv_len = snprintf( c->cr_msg, sizeof( c->cr_msg ), "%d", i );
   3891 
   3892 				/* count the attr length */
   3893 				for ( attr_name = qm->attr_sets[i].attrs;
   3894 					attr_name->an_name.bv_val; attr_name++ )
   3895 				{
   3896 					bv.bv_len += attr_name->an_name.bv_len + 1;
   3897 					if ( attr_name->an_desc &&
   3898 							( attr_name->an_desc->ad_flags & SLAP_DESC_TEMPORARY ) ) {
   3899 						bv.bv_len += STRLENOF("undef:");
   3900 					}
   3901 				}
   3902 
   3903 				bv.bv_val = ch_malloc( bv.bv_len+1 );
   3904 				ptr = lutil_strcopy( bv.bv_val, c->cr_msg );
   3905 				for ( attr_name = qm->attr_sets[i].attrs;
   3906 					attr_name->an_name.bv_val; attr_name++ ) {
   3907 					*ptr++ = ' ';
   3908 					if ( attr_name->an_desc &&
   3909 							( attr_name->an_desc->ad_flags & SLAP_DESC_TEMPORARY ) ) {
   3910 						ptr = lutil_strcopy( ptr, "undef:" );
   3911 					}
   3912 					ptr = lutil_strcopy( ptr, attr_name->an_name.bv_val );
   3913 				}
   3914 				ber_bvarray_add( &c->rvalue_vals, &bv );
   3915 			}
   3916 			if ( !c->rvalue_vals )
   3917 				rc = 1;
   3918 			break;
   3919 		case PC_TEMP:
   3920 			for (temp=qm->templates; temp; temp=temp->qmnext) {
   3921 				/* HEADS-UP: always print all;
   3922 				 * if optional == 0, ignore */
   3923 				bv.bv_len = snprintf( c->cr_msg, sizeof( c->cr_msg ),
   3924 					" %d %ld %ld %ld %ld",
   3925 					temp->attr_set_index,
   3926 					temp->ttl,
   3927 					temp->negttl,
   3928 					temp->limitttl,
   3929 					temp->ttr );
   3930 				bv.bv_len += temp->querystr.bv_len + 2;
   3931 				bv.bv_val = ch_malloc( bv.bv_len+1 );
   3932 				ptr = bv.bv_val;
   3933 				*ptr++ = '"';
   3934 				ptr = lutil_strcopy( ptr, temp->querystr.bv_val );
   3935 				*ptr++ = '"';
   3936 				strcpy( ptr, c->cr_msg );
   3937 				ber_bvarray_add( &c->rvalue_vals, &bv );
   3938 			}
   3939 			if ( !c->rvalue_vals )
   3940 				rc = 1;
   3941 			break;
   3942 		case PC_BIND:
   3943 			for (temp=qm->templates; temp; temp=temp->qmnext) {
   3944 				if ( !temp->bindttr ) continue;
   3945 				bv.bv_len = snprintf( c->cr_msg, sizeof( c->cr_msg ),
   3946 					" %d %ld %s ",
   3947 					temp->attr_set_index,
   3948 					temp->bindttr,
   3949 					ldap_pvt_scope2str( temp->bindscope ));
   3950 				bv.bv_len += temp->bindbase.bv_len + temp->bindftemp.bv_len + 4;
   3951 				bv.bv_val = ch_malloc( bv.bv_len + 1 );
   3952 				ptr = bv.bv_val;
   3953 				*ptr++ = '"';
   3954 				ptr = lutil_strcopy( ptr, temp->bindftemp.bv_val );
   3955 				*ptr++ = '"';
   3956 				ptr = lutil_strcopy( ptr, c->cr_msg );
   3957 				*ptr++ = '"';
   3958 				ptr = lutil_strcopy( ptr, temp->bindbase.bv_val );
   3959 				*ptr++ = '"';
   3960 				*ptr = '\0';
   3961 				ber_bvarray_add( &c->rvalue_vals, &bv );
   3962 			}
   3963 			if ( !c->rvalue_vals )
   3964 				rc = 1;
   3965 			break;
   3966 		case PC_RESP:
   3967 			if ( cm->response_cb == PCACHE_RESPONSE_CB_HEAD ) {
   3968 				BER_BVSTR( &bv, "head" );
   3969 			} else {
   3970 				BER_BVSTR( &bv, "tail" );
   3971 			}
   3972 			value_add_one( &c->rvalue_vals, &bv );
   3973 			break;
   3974 		case PC_QUERIES:
   3975 			c->value_int = cm->max_queries;
   3976 			break;
   3977 		case PC_OFFLINE:
   3978 			c->value_int = (cm->cc_paused & PCACHE_CC_OFFLINE) != 0;
   3979 			break;
   3980 		}
   3981 		return rc;
   3982 	} else if ( c->op == LDAP_MOD_DELETE ) {
   3983 		rc = 1;
   3984 		switch( c->type ) {
   3985 		case PC_ATTR: /* FIXME */
   3986 		case PC_TEMP:
   3987 		case PC_BIND:
   3988 			break;
   3989 		case PC_OFFLINE:
   3990 			cm->cc_paused &= ~PCACHE_CC_OFFLINE;
   3991 			/* If there were cached queries when we went offline,
   3992 			 * restart the checker now.
   3993 			 */
   3994 			if ( cm->num_cached_queries ) {
   3995 				ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
   3996 				cm->cc_paused = 0;
   3997 				ldap_pvt_runqueue_resched( &slapd_rq, cm->cc_arg, 0 );
   3998 				ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
   3999 			}
   4000 			rc = 0;
   4001 			break;
   4002 		}
   4003 		return rc;
   4004 	}
   4005 
   4006 	switch( c->type ) {
   4007 	case PC_MAIN:
   4008 		if ( cm->numattrsets > 0 ) {
   4009 			snprintf( c->cr_msg, sizeof( c->cr_msg ), "\"pcache\" directive already provided" );
   4010 			Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
   4011 			return( 1 );
   4012 		}
   4013 
   4014 		if ( lutil_atoi( &cm->numattrsets, c->argv[3] ) != 0 ) {
   4015 			snprintf( c->cr_msg, sizeof( c->cr_msg ), "unable to parse num attrsets=\"%s\" (arg #3)",
   4016 				c->argv[3] );
   4017 			Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
   4018 			return( 1 );
   4019 		}
   4020 		if ( cm->numattrsets <= 0 ) {
   4021 			snprintf( c->cr_msg, sizeof( c->cr_msg ), "numattrsets (arg #3) must be positive" );
   4022 			Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
   4023 			return( 1 );
   4024 		}
   4025 		if ( cm->numattrsets > MAX_ATTR_SETS ) {
   4026 			snprintf( c->cr_msg, sizeof( c->cr_msg ), "numattrsets (arg #3) must be <= %d", MAX_ATTR_SETS );
   4027 			Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
   4028 			return( 1 );
   4029 		}
   4030 
   4031 		if ( !backend_db_init( c->argv[1], &cm->db, -1, NULL )) {
   4032 			snprintf( c->cr_msg, sizeof( c->cr_msg ), "unknown backend type (arg #1)" );
   4033 			Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
   4034 			return( 1 );
   4035 		}
   4036 
   4037 		if ( lutil_atoi( &cm->max_entries, c->argv[2] ) != 0 ) {
   4038 			snprintf( c->cr_msg, sizeof( c->cr_msg ), "unable to parse max entries=\"%s\" (arg #2)",
   4039 				c->argv[2] );
   4040 			Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
   4041 			return( 1 );
   4042 		}
   4043 		if ( cm->max_entries <= 0 ) {
   4044 			snprintf( c->cr_msg, sizeof( c->cr_msg ), "max entries (arg #2) must be positive.\n" );
   4045 			Debug( LDAP_DEBUG_CONFIG, "%s: %s\n", c->log, c->cr_msg );
   4046 			return( 1 );
   4047 		}
   4048 
   4049 		if ( lutil_atoi( &cm->num_entries_limit, c->argv[4] ) != 0 ) {
   4050 			snprintf( c->cr_msg, sizeof( c->cr_msg ), "unable to parse entry limit=\"%s\" (arg #4)",
   4051 				c->argv[4] );
   4052 			Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
   4053 			return( 1 );
   4054 		}
   4055 		if ( cm->num_entries_limit <= 0 ) {
   4056 			snprintf( c->cr_msg, sizeof( c->cr_msg ), "entry limit (arg #4) must be positive" );
   4057 			Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
   4058 			return( 1 );
   4059 		}
   4060 		if ( cm->num_entries_limit > cm->max_entries ) {
   4061 			snprintf( c->cr_msg, sizeof( c->cr_msg ), "entry limit (arg #4) must be less than max entries %d (arg #2)", cm->max_entries );
   4062 			Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
   4063 			return( 1 );
   4064 		}
   4065 
   4066 		if ( lutil_parse_time( c->argv[5], &t ) != 0 ) {
   4067 			snprintf( c->cr_msg, sizeof( c->cr_msg ), "unable to parse period=\"%s\" (arg #5)",
   4068 				c->argv[5] );
   4069 			Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
   4070 			return( 1 );
   4071 		}
   4072 
   4073 		cm->cc_period = (time_t)t;
   4074 		Debug( pcache_debug,
   4075 				"Total # of attribute sets to be cached = %d.\n",
   4076 				cm->numattrsets );
   4077 		qm->attr_sets = ( struct attr_set * )ch_calloc( cm->numattrsets,
   4078 			    			sizeof( struct attr_set ) );
   4079 		break;
   4080 	case PC_ATTR:
   4081 		if ( cm->numattrsets == 0 ) {
   4082 			snprintf( c->cr_msg, sizeof( c->cr_msg ), "\"pcache\" directive not provided yet" );
   4083 			Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
   4084 			return( 1 );
   4085 		}
   4086 		if ( lutil_atoi( &num, c->argv[1] ) != 0 ) {
   4087 			snprintf( c->cr_msg, sizeof( c->cr_msg ), "unable to parse attrset #=\"%s\"",
   4088 				c->argv[1] );
   4089 			Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
   4090 			return( 1 );
   4091 		}
   4092 
   4093 		if ( num < 0 || num >= cm->numattrsets ) {
   4094 			snprintf( c->cr_msg, sizeof( c->cr_msg ), "attrset index %d out of bounds (must be %s%d)",
   4095 				num, cm->numattrsets > 1 ? "0->" : "", cm->numattrsets - 1 );
   4096 			Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
   4097 			return 1;
   4098 		}
   4099 		qm->attr_sets[num].flags |= PC_CONFIGURED;
   4100 		if ( c->argc == 2 ) {
   4101 			/* assume "1.1" */
   4102 			snprintf( c->cr_msg, sizeof( c->cr_msg ),
   4103 				"need an explicit attr in attrlist; use \"*\" to indicate all attrs" );
   4104 			Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
   4105 			return 1;
   4106 
   4107 		} else if ( c->argc == 3 ) {
   4108 			if ( strcmp( c->argv[2], LDAP_ALL_USER_ATTRIBUTES ) == 0 ) {
   4109 				qm->attr_sets[num].count = 1;
   4110 				qm->attr_sets[num].attrs = (AttributeName*)ch_calloc( 2,
   4111 					sizeof( AttributeName ) );
   4112 				BER_BVSTR( &qm->attr_sets[num].attrs[0].an_name, LDAP_ALL_USER_ATTRIBUTES );
   4113 				break;
   4114 
   4115 			} else if ( strcmp( c->argv[2], LDAP_ALL_OPERATIONAL_ATTRIBUTES ) == 0 ) {
   4116 				qm->attr_sets[num].count = 1;
   4117 				qm->attr_sets[num].attrs = (AttributeName*)ch_calloc( 2,
   4118 					sizeof( AttributeName ) );
   4119 				BER_BVSTR( &qm->attr_sets[num].attrs[0].an_name, LDAP_ALL_OPERATIONAL_ATTRIBUTES );
   4120 				break;
   4121 
   4122 			} else if ( strcmp( c->argv[2], LDAP_NO_ATTRS ) == 0 ) {
   4123 				break;
   4124 			}
   4125 			/* else: fallthru */
   4126 
   4127 		} else if ( c->argc == 4 ) {
   4128 			if ( ( strcmp( c->argv[2], LDAP_ALL_USER_ATTRIBUTES ) == 0 && strcmp( c->argv[3], LDAP_ALL_OPERATIONAL_ATTRIBUTES ) == 0 )
   4129 				|| ( strcmp( c->argv[2], LDAP_ALL_OPERATIONAL_ATTRIBUTES ) == 0 && strcmp( c->argv[3], LDAP_ALL_USER_ATTRIBUTES ) == 0 ) )
   4130 			{
   4131 				qm->attr_sets[num].count = 2;
   4132 				qm->attr_sets[num].attrs = (AttributeName*)ch_calloc( 3,
   4133 					sizeof( AttributeName ) );
   4134 				BER_BVSTR( &qm->attr_sets[num].attrs[0].an_name, LDAP_ALL_USER_ATTRIBUTES );
   4135 				BER_BVSTR( &qm->attr_sets[num].attrs[1].an_name, LDAP_ALL_OPERATIONAL_ATTRIBUTES );
   4136 				break;
   4137 			}
   4138 			/* else: fallthru */
   4139 		}
   4140 
   4141 		if ( c->argc > 2 ) {
   4142 			int all_user = 0, all_op = 0;
   4143 
   4144 			qm->attr_sets[num].count = c->argc - 2;
   4145 			qm->attr_sets[num].attrs = (AttributeName*)ch_calloc( c->argc - 1,
   4146 				sizeof( AttributeName ) );
   4147 			attr_name = qm->attr_sets[num].attrs;
   4148 			for ( i = 2; i < c->argc; i++ ) {
   4149 				attr_name->an_desc = NULL;
   4150 				if ( strcmp( c->argv[i], LDAP_NO_ATTRS ) == 0 ) {
   4151 					snprintf( c->cr_msg, sizeof( c->cr_msg ),
   4152 						"invalid attr #%d \"%s\" in attrlist",
   4153 						i - 2, c->argv[i] );
   4154 					Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
   4155 					ch_free( qm->attr_sets[num].attrs );
   4156 					qm->attr_sets[num].attrs = NULL;
   4157 					qm->attr_sets[num].count = 0;
   4158 					return 1;
   4159 				}
   4160 				if ( strcmp( c->argv[i], LDAP_ALL_USER_ATTRIBUTES ) == 0 ) {
   4161 					all_user = 1;
   4162 					BER_BVSTR( &attr_name->an_name, LDAP_ALL_USER_ATTRIBUTES );
   4163 				} else if ( strcmp( c->argv[i], LDAP_ALL_OPERATIONAL_ATTRIBUTES ) == 0 ) {
   4164 					all_op = 1;
   4165 					BER_BVSTR( &attr_name->an_name, LDAP_ALL_OPERATIONAL_ATTRIBUTES );
   4166 				} else {
   4167 					if ( strncasecmp( c->argv[i], "undef:", STRLENOF("undef:") ) == 0 ) {
   4168 						struct berval bv;
   4169 						ber_str2bv( c->argv[i] + STRLENOF("undef:"), 0, 0, &bv );
   4170 						attr_name->an_desc = slap_bv2tmp_ad( &bv, NULL );
   4171 
   4172 					} else if ( slap_str2ad( c->argv[i], &attr_name->an_desc, &text ) ) {
   4173 						strcpy( c->cr_msg, text );
   4174 						Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
   4175 						ch_free( qm->attr_sets[num].attrs );
   4176 						qm->attr_sets[num].attrs = NULL;
   4177 						qm->attr_sets[num].count = 0;
   4178 						return 1;
   4179 					}
   4180 					attr_name->an_name = attr_name->an_desc->ad_cname;
   4181 				}
   4182 				attr_name->an_oc = NULL;
   4183 				attr_name->an_flags = 0;
   4184 				if ( attr_name->an_desc == slap_schema.si_ad_objectClass )
   4185 					qm->attr_sets[num].flags |= PC_GOT_OC;
   4186 				attr_name++;
   4187 				BER_BVZERO( &attr_name->an_name );
   4188 			}
   4189 
   4190 			/* warn if list contains both "*" and "+" */
   4191 			if ( i > 4 && all_user && all_op ) {
   4192 				snprintf( c->cr_msg, sizeof( c->cr_msg ),
   4193 					"warning: attribute list contains \"*\" and \"+\"" );
   4194 				Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
   4195 			}
   4196 		}
   4197 		break;
   4198 	case PC_TEMP:
   4199 		if ( cm->numattrsets == 0 ) {
   4200 			snprintf( c->cr_msg, sizeof( c->cr_msg ), "\"pcache\" directive not provided yet" );
   4201 			Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
   4202 			return( 1 );
   4203 		}
   4204 		if ( lutil_atoi( &i, c->argv[2] ) != 0 ) {
   4205 			snprintf( c->cr_msg, sizeof( c->cr_msg ), "unable to parse template #=\"%s\"",
   4206 				c->argv[2] );
   4207 			Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
   4208 			return( 1 );
   4209 		}
   4210 
   4211 		if ( i < 0 || i >= cm->numattrsets ||
   4212 			!(qm->attr_sets[i].flags & PC_CONFIGURED )) {
   4213 			snprintf( c->cr_msg, sizeof( c->cr_msg ), "template index %d invalid (%s%d)",
   4214 				i, cm->numattrsets > 1 ? "0->" : "", cm->numattrsets - 1 );
   4215 			Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
   4216 			return 1;
   4217 		}
   4218 		{
   4219 			AttributeName *attrs;
   4220 			int cnt;
   4221 			cnt = template_attrs( c->argv[1], &qm->attr_sets[i], &attrs, &text );
   4222 			if ( cnt < 0 ) {
   4223 				snprintf( c->cr_msg, sizeof( c->cr_msg ), "unable to parse template: %s",
   4224 					text );
   4225 				Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
   4226 				return 1;
   4227 			}
   4228 			temp = ch_calloc( 1, sizeof( QueryTemplate ));
   4229 			temp->qmnext = qm->templates;
   4230 			qm->templates = temp;
   4231 			temp->t_attrs.attrs = attrs;
   4232 			temp->t_attrs.count = cnt;
   4233 		}
   4234 		ldap_pvt_thread_rdwr_init( &temp->t_rwlock );
   4235 		temp->query = temp->query_last = NULL;
   4236 		if ( lutil_parse_time( c->argv[3], &t ) != 0 ) {
   4237 			snprintf( c->cr_msg, sizeof( c->cr_msg ),
   4238 				"unable to parse template ttl=\"%s\"",
   4239 				c->argv[3] );
   4240 			Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
   4241 pc_temp_fail:
   4242 			ch_free( temp->t_attrs.attrs );
   4243 			ch_free( temp );
   4244 			return( 1 );
   4245 		}
   4246 		temp->ttl = (time_t)t;
   4247 		temp->negttl = (time_t)0;
   4248 		temp->limitttl = (time_t)0;
   4249 		temp->ttr = (time_t)0;
   4250 		switch ( c->argc ) {
   4251 		case 7:
   4252 			if ( lutil_parse_time( c->argv[6], &t ) != 0 ) {
   4253 				snprintf( c->cr_msg, sizeof( c->cr_msg ),
   4254 					"unable to parse template ttr=\"%s\"",
   4255 					c->argv[6] );
   4256 				Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
   4257 				goto pc_temp_fail;
   4258 			}
   4259 			temp->ttr = (time_t)t;
   4260 			/* fallthru */
   4261 
   4262 		case 6:
   4263 			if ( lutil_parse_time( c->argv[5], &t ) != 0 ) {
   4264 				snprintf( c->cr_msg, sizeof( c->cr_msg ),
   4265 					"unable to parse template sizelimit ttl=\"%s\"",
   4266 					c->argv[5] );
   4267 				Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
   4268 				goto pc_temp_fail;
   4269 			}
   4270 			temp->limitttl = (time_t)t;
   4271 			/* fallthru */
   4272 
   4273 		case 5:
   4274 			if ( lutil_parse_time( c->argv[4], &t ) != 0 ) {
   4275 				snprintf( c->cr_msg, sizeof( c->cr_msg ),
   4276 					"unable to parse template negative ttl=\"%s\"",
   4277 					c->argv[4] );
   4278 				Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
   4279 				goto pc_temp_fail;
   4280 			}
   4281 			temp->negttl = (time_t)t;
   4282 			break;
   4283 		}
   4284 
   4285 		temp->no_of_queries = 0;
   4286 
   4287 		ber_str2bv( c->argv[1], 0, 1, &temp->querystr );
   4288 		Debug( pcache_debug, "Template:\n" );
   4289 		Debug( pcache_debug, "  query template: %s\n",
   4290 				temp->querystr.bv_val );
   4291 		temp->attr_set_index = i;
   4292 		qm->attr_sets[i].flags |= PC_REFERENCED;
   4293 		temp->qtnext = qm->attr_sets[i].templates;
   4294 		qm->attr_sets[i].templates = temp;
   4295 		Debug( pcache_debug, "  attributes: \n" );
   4296 		if ( ( attrarray = qm->attr_sets[i].attrs ) != NULL ) {
   4297 			for ( i=0; attrarray[i].an_name.bv_val; i++ )
   4298 				Debug( pcache_debug, "\t%s\n",
   4299 					attrarray[i].an_name.bv_val );
   4300 		}
   4301 		break;
   4302 	case PC_BIND:
   4303 		if ( !qm->templates ) {
   4304 			snprintf( c->cr_msg, sizeof( c->cr_msg ), "\"pcacheTemplate\" directive not provided yet" );
   4305 			Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
   4306 			return( 1 );
   4307 		}
   4308 		if ( lutil_atoi( &i, c->argv[2] ) != 0 ) {
   4309 			snprintf( c->cr_msg, sizeof( c->cr_msg ), "unable to parse Bind index #=\"%s\"",
   4310 				c->argv[2] );
   4311 			Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
   4312 			return( 1 );
   4313 		}
   4314 
   4315 		if ( i < 0 || i >= cm->numattrsets ||
   4316 			!(qm->attr_sets[i].flags & PC_CONFIGURED )) {
   4317 			snprintf( c->cr_msg, sizeof( c->cr_msg ), "Bind index %d invalid (%s%d)",
   4318 				i, cm->numattrsets > 1 ? "0->" : "", cm->numattrsets - 1 );
   4319 			Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
   4320 			return 1;
   4321 		}
   4322 		{	struct berval bv, tempbv;
   4323 			AttributeDescription **descs;
   4324 			int ndescs;
   4325 			ber_str2bv( c->argv[1], 0, 0, &bv );
   4326 			ndescs = ftemp_attrs( &bv, &tempbv, &descs, &text );
   4327 			if ( ndescs < 0 ) {
   4328 				snprintf( c->cr_msg, sizeof( c->cr_msg ), "unable to parse template: %s",
   4329 					text );
   4330 				Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
   4331 				return 1;
   4332 			}
   4333 			for ( temp = qm->templates; temp; temp=temp->qmnext ) {
   4334 				if ( temp->attr_set_index == i && bvmatch( &tempbv,
   4335 					&temp->querystr ))
   4336 					break;
   4337 			}
   4338 			ch_free( tempbv.bv_val );
   4339 			if ( !temp ) {
   4340 				ch_free( descs );
   4341 				snprintf( c->cr_msg, sizeof( c->cr_msg ), "Bind template %s %d invalid",
   4342 					c->argv[1], i );
   4343 				Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
   4344 				return 1;
   4345 			}
   4346 			ber_dupbv( &temp->bindftemp, &bv );
   4347 			temp->bindfattrs = descs;
   4348 			temp->bindnattrs = ndescs;
   4349 		}
   4350 		if ( lutil_parse_time( c->argv[3], &t ) != 0 ) {
   4351 			snprintf( c->cr_msg, sizeof( c->cr_msg ),
   4352 				"unable to parse bind ttr=\"%s\"",
   4353 				c->argv[3] );
   4354 			Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
   4355 pc_bind_fail:
   4356 			ch_free( temp->bindfattrs );
   4357 			temp->bindfattrs = NULL;
   4358 			ch_free( temp->bindftemp.bv_val );
   4359 			BER_BVZERO( &temp->bindftemp );
   4360 			return( 1 );
   4361 		}
   4362 		num = ldap_pvt_str2scope( c->argv[4] );
   4363 		if ( num < 0 ) {
   4364 			snprintf( c->cr_msg, sizeof( c->cr_msg ),
   4365 				"unable to parse bind scope=\"%s\"",
   4366 				c->argv[4] );
   4367 			Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
   4368 			goto pc_bind_fail;
   4369 		}
   4370 		{
   4371 			struct berval dn, ndn;
   4372 			ber_str2bv( c->argv[5], 0, 0, &dn );
   4373 			rc = dnNormalize( 0, NULL, NULL, &dn, &ndn, NULL );
   4374 			if ( rc ) {
   4375 				snprintf( c->cr_msg, sizeof( c->cr_msg ),
   4376 					"invalid bind baseDN=\"%s\"",
   4377 					c->argv[5] );
   4378 				Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
   4379 				goto pc_bind_fail;
   4380 			}
   4381 			if ( temp->bindbase.bv_val )
   4382 				ch_free( temp->bindbase.bv_val );
   4383 			temp->bindbase = ndn;
   4384 		}
   4385 		{
   4386 			/* convert the template into dummy filter */
   4387 			struct berval bv;
   4388 			char *eq = temp->bindftemp.bv_val, *e2;
   4389 			Filter *f;
   4390 			i = 0;
   4391 			while ((eq = strchr(eq, '=' ))) {
   4392 				eq++;
   4393 				if ( eq[0] == ')' )
   4394 					i++;
   4395 			}
   4396 			bv.bv_len = temp->bindftemp.bv_len + i;
   4397 			bv.bv_val = ch_malloc( bv.bv_len + 1 );
   4398 			for ( e2 = bv.bv_val, eq = temp->bindftemp.bv_val;
   4399 				*eq; eq++ ) {
   4400 				if ( *eq == '=' ) {
   4401 					*e2++ = '=';
   4402 					if ( eq[1] == ')' )
   4403 						*e2++ = '*';
   4404 				} else {
   4405 					*e2++ = *eq;
   4406 				}
   4407 			}
   4408 			*e2 = '\0';
   4409 			f = str2filter( bv.bv_val );
   4410 			if ( !f ) {
   4411 				ch_free( bv.bv_val );
   4412 				snprintf( c->cr_msg, sizeof( c->cr_msg ),
   4413 					"unable to parse bindfilter=\"%s\"", bv.bv_val );
   4414 				Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
   4415 				ch_free( temp->bindbase.bv_val );
   4416 				BER_BVZERO( &temp->bindbase );
   4417 				goto pc_bind_fail;
   4418 			}
   4419 			if ( temp->bindfilter )
   4420 				filter_free( temp->bindfilter );
   4421 			if ( temp->bindfilterstr.bv_val )
   4422 				ch_free( temp->bindfilterstr.bv_val );
   4423 			temp->bindfilterstr = bv;
   4424 			temp->bindfilter = f;
   4425 		}
   4426 		temp->bindttr = (time_t)t;
   4427 		temp->bindscope = num;
   4428 		cm->cache_binds = 1;
   4429 		break;
   4430 
   4431 	case PC_RESP:
   4432 		if ( strcasecmp( c->argv[1], "head" ) == 0 ) {
   4433 			cm->response_cb = PCACHE_RESPONSE_CB_HEAD;
   4434 
   4435 		} else if ( strcasecmp( c->argv[1], "tail" ) == 0 ) {
   4436 			cm->response_cb = PCACHE_RESPONSE_CB_TAIL;
   4437 
   4438 		} else {
   4439 			snprintf( c->cr_msg, sizeof( c->cr_msg ), "unknown specifier" );
   4440 			Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
   4441 			return 1;
   4442 		}
   4443 		break;
   4444 	case PC_QUERIES:
   4445 		if ( c->value_int <= 0 ) {
   4446 			snprintf( c->cr_msg, sizeof( c->cr_msg ), "max queries must be positive" );
   4447 			Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
   4448 			return( 1 );
   4449 		}
   4450 		cm->max_queries = c->value_int;
   4451 		break;
   4452 	case PC_OFFLINE:
   4453 		if ( c->value_int )
   4454 			cm->cc_paused |= PCACHE_CC_OFFLINE;
   4455 		else
   4456 			cm->cc_paused &= ~PCACHE_CC_OFFLINE;
   4457 		break;
   4458 	case PC_PRIVATE_DB:
   4459 		if ( cm->db.be_private == NULL ) {
   4460 			snprintf( c->cr_msg, sizeof( c->cr_msg ),
   4461 				"private database must be defined before setting database specific options" );
   4462 			Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
   4463 			return( 1 );
   4464 		}
   4465 
   4466 		if ( cm->db.bd_info->bi_cf_ocs ) {
   4467 			ConfigTable	*ct;
   4468 			ConfigArgs	c2 = *c;
   4469 			char		*argv0 = c->argv[ 0 ];
   4470 
   4471 			c->argv[ 0 ] = &argv0[ STRLENOF( "pcache-" ) ];
   4472 
   4473 			ct = config_find_keyword( cm->db.bd_info->bi_cf_ocs->co_table, c );
   4474 			if ( ct == NULL ) {
   4475 				snprintf( c->cr_msg, sizeof( c->cr_msg ),
   4476 					"private database does not recognize specific option '%s'",
   4477 					c->argv[ 0 ] );
   4478 				Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
   4479 				rc = 1;
   4480 
   4481 			} else {
   4482 				c->table = cm->db.bd_info->bi_cf_ocs->co_type;
   4483 				c->be = &cm->db;
   4484 				c->bi = c->be->bd_info;
   4485 
   4486 				rc = config_add_vals( ct, c );
   4487 
   4488 				c->bi = c2.bi;
   4489 				c->be = c2.be;
   4490 				c->table = c2.table;
   4491 			}
   4492 
   4493 			c->argv[ 0 ] = argv0;
   4494 
   4495 		} else if ( cm->db.be_config != NULL ) {
   4496 			char	*argv0 = c->argv[ 0 ];
   4497 
   4498 			c->argv[ 0 ] = &argv0[ STRLENOF( "pcache-" ) ];
   4499 			rc = cm->db.be_config( &cm->db, c->fname, c->lineno, c->argc, c->argv );
   4500 			c->argv[ 0 ] = argv0;
   4501 
   4502 		} else {
   4503 			snprintf( c->cr_msg, sizeof( c->cr_msg ),
   4504 				"no means to set private database specific options" );
   4505 			Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg );
   4506 			return 1;
   4507 		}
   4508 		break;
   4509 	default:
   4510 		rc = SLAP_CONF_UNKNOWN;
   4511 		break;
   4512 	}
   4513 
   4514 	return rc;
   4515 }
   4516 
   4517 static int
   4518 pcache_db_config(
   4519 	BackendDB	*be,
   4520 	const char	*fname,
   4521 	int		lineno,
   4522 	int		argc,
   4523 	char		**argv
   4524 )
   4525 {
   4526 	slap_overinst	*on = (slap_overinst *)be->bd_info;
   4527 	cache_manager* 	cm = on->on_bi.bi_private;
   4528 
   4529 	/* Something for the cache database? */
   4530 	if ( cm->db.bd_info && cm->db.bd_info->bi_db_config )
   4531 		return cm->db.bd_info->bi_db_config( &cm->db, fname, lineno,
   4532 			argc, argv );
   4533 	return SLAP_CONF_UNKNOWN;
   4534 }
   4535 
   4536 static int
   4537 pcache_db_init(
   4538 	BackendDB *be,
   4539 	ConfigReply *cr)
   4540 {
   4541 	slap_overinst *on = (slap_overinst *)be->bd_info;
   4542 	cache_manager *cm;
   4543 	query_manager *qm;
   4544 
   4545 	cm = (cache_manager *)ch_malloc(sizeof(cache_manager));
   4546 	on->on_bi.bi_private = cm;
   4547 
   4548 	qm = (query_manager*)ch_malloc(sizeof(query_manager));
   4549 
   4550 	cm->db = *be;
   4551 	cm->db.bd_info = NULL;
   4552 	SLAP_DBFLAGS(&cm->db) |= SLAP_DBFLAG_NO_SCHEMA_CHECK;
   4553 	cm->db.be_private = NULL;
   4554 	cm->db.bd_self = &cm->db;
   4555 	cm->qm = qm;
   4556 	cm->numattrsets = 0;
   4557 	cm->num_entries_limit = 5;
   4558 	cm->num_cached_queries = 0;
   4559 	cm->max_entries = 0;
   4560 	cm->cur_entries = 0;
   4561 	cm->max_queries = 10000;
   4562 	cm->save_queries = 0;
   4563 	cm->check_cacheability = 0;
   4564 	cm->response_cb = PCACHE_RESPONSE_CB_TAIL;
   4565 	cm->defer_db_open = 1;
   4566 	cm->cache_binds = 0;
   4567 	cm->cc_period = 1000;
   4568 	cm->cc_paused = 0;
   4569 	cm->cc_arg = NULL;
   4570 #ifdef PCACHE_MONITOR
   4571 	cm->monitor_cb = NULL;
   4572 #endif /* PCACHE_MONITOR */
   4573 
   4574 	qm->attr_sets = NULL;
   4575 	qm->templates = NULL;
   4576 	qm->lru_top = NULL;
   4577 	qm->lru_bottom = NULL;
   4578 
   4579 	qm->qcfunc = query_containment;
   4580 	qm->crfunc = cache_replacement;
   4581 	qm->addfunc = add_query;
   4582 	ldap_pvt_thread_mutex_init(&qm->lru_mutex);
   4583 
   4584 	ldap_pvt_thread_mutex_init(&cm->cache_mutex);
   4585 
   4586 #ifndef PCACHE_MONITOR
   4587 	return 0;
   4588 #else /* PCACHE_MONITOR */
   4589 	return pcache_monitor_db_init( be );
   4590 #endif /* PCACHE_MONITOR */
   4591 }
   4592 
   4593 static int
   4594 pcache_cachedquery_open_cb( Operation *op, SlapReply *rs )
   4595 {
   4596 	assert( op->o_tag == LDAP_REQ_SEARCH );
   4597 
   4598 	if ( rs->sr_type == REP_SEARCH ) {
   4599 		Attribute	*a;
   4600 
   4601 		a = attr_find( rs->sr_entry->e_attrs, ad_cachedQueryURL );
   4602 		if ( a != NULL ) {
   4603 			BerVarray	*valsp;
   4604 
   4605 			assert( a->a_nvals != NULL );
   4606 
   4607 			valsp = op->o_callback->sc_private;
   4608 			assert( *valsp == NULL );
   4609 
   4610 			ber_bvarray_dup_x( valsp, a->a_nvals, op->o_tmpmemctx );
   4611 		}
   4612 	}
   4613 
   4614 	return 0;
   4615 }
   4616 
   4617 static int
   4618 pcache_cachedquery_count_cb( Operation *op, SlapReply *rs )
   4619 {
   4620 	assert( op->o_tag == LDAP_REQ_SEARCH );
   4621 
   4622 	if ( rs->sr_type == REP_SEARCH ) {
   4623 		int	*countp = (int *)op->o_callback->sc_private;
   4624 
   4625 		(*countp)++;
   4626 	}
   4627 
   4628 	return 0;
   4629 }
   4630 
   4631 static int
   4632 pcache_db_open2(
   4633 	slap_overinst *on,
   4634 	ConfigReply *cr )
   4635 {
   4636 	cache_manager	*cm = on->on_bi.bi_private;
   4637 	query_manager*  qm = cm->qm;
   4638 	int rc;
   4639 
   4640 	rc = backend_startup_one( &cm->db, cr );
   4641 	if ( rc == 0 ) {
   4642 		cm->defer_db_open = 0;
   4643 	}
   4644 
   4645 	/* There is no runqueue in TOOL mode */
   4646 	if (( slapMode & SLAP_SERVER_MODE ) && rc == 0 ) {
   4647 		ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
   4648 		ldap_pvt_runqueue_insert( &slapd_rq, cm->cc_period,
   4649 			consistency_check, on,
   4650 			"pcache_consistency", cm->db.be_suffix[0].bv_val );
   4651 		ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
   4652 
   4653 		/* Cached database must have the rootdn */
   4654 		if ( BER_BVISNULL( &cm->db.be_rootndn )
   4655 				|| BER_BVISEMPTY( &cm->db.be_rootndn ) )
   4656 		{
   4657 			Debug( LDAP_DEBUG_ANY, "pcache_db_open(): "
   4658 				"underlying database of type \"%s\"\n"
   4659 				"    serving naming context \"%s\"\n"
   4660 				"    has no \"rootdn\", required by \"pcache\".\n",
   4661 				on->on_info->oi_orig->bi_type,
   4662 				cm->db.be_suffix[0].bv_val );
   4663 			return 1;
   4664 		}
   4665 
   4666 		if ( cm->save_queries ) {
   4667 			void		*thrctx = ldap_pvt_thread_pool_context();
   4668 			Connection	conn = { 0 };
   4669 			OperationBuffer	opbuf;
   4670 			Operation	*op;
   4671 			slap_callback	cb = { 0 };
   4672 			SlapReply	rs = { REP_RESULT };
   4673 			BerVarray	vals = NULL;
   4674 			Filter		f = { 0 }, f2 = { 0 };
   4675 			AttributeAssertion	ava = ATTRIBUTEASSERTION_INIT;
   4676 			AttributeName	attrs[ 2 ] = {{{ 0 }}};
   4677 
   4678 			connection_fake_init2( &conn, &opbuf, thrctx, 0 );
   4679 			op = &opbuf.ob_op;
   4680 
   4681 			op->o_bd = &cm->db;
   4682 
   4683 			op->o_tag = LDAP_REQ_SEARCH;
   4684 			op->o_protocol = LDAP_VERSION3;
   4685 			cb.sc_response = pcache_cachedquery_open_cb;
   4686 			cb.sc_private = &vals;
   4687 			op->o_callback = &cb;
   4688 			op->o_time = slap_get_time();
   4689 			op->o_do_not_cache = 1;
   4690 			op->o_managedsait = SLAP_CONTROL_CRITICAL;
   4691 
   4692 			op->o_dn = cm->db.be_rootdn;
   4693 			op->o_ndn = cm->db.be_rootndn;
   4694 			op->o_req_dn = cm->db.be_suffix[ 0 ];
   4695 			op->o_req_ndn = cm->db.be_nsuffix[ 0 ];
   4696 
   4697 			op->ors_scope = LDAP_SCOPE_BASE;
   4698 			op->ors_deref = LDAP_DEREF_NEVER;
   4699 			op->ors_slimit = 1;
   4700 			op->ors_tlimit = SLAP_NO_LIMIT;
   4701 			op->ors_limit = NULL;
   4702 			ber_str2bv( "(pcacheQueryURL=*)", 0, 0, &op->ors_filterstr );
   4703 			f.f_choice = LDAP_FILTER_PRESENT;
   4704 			f.f_desc = ad_cachedQueryURL;
   4705 			op->ors_filter = &f;
   4706 			attrs[ 0 ].an_desc = ad_cachedQueryURL;
   4707 			attrs[ 0 ].an_name = ad_cachedQueryURL->ad_cname;
   4708 			op->ors_attrs = attrs;
   4709 			op->ors_attrsonly = 0;
   4710 
   4711 			rc = op->o_bd->be_search( op, &rs );
   4712 			if ( rc == LDAP_SUCCESS && vals != NULL ) {
   4713 				int	i;
   4714 
   4715 				for ( i = 0; !BER_BVISNULL( &vals[ i ] ); i++ ) {
   4716 					if ( url2query( vals[ i ].bv_val, op, qm ) == 0 ) {
   4717 						cm->num_cached_queries++;
   4718 					}
   4719 				}
   4720 
   4721 				ber_bvarray_free_x( vals, op->o_tmpmemctx );
   4722 			}
   4723 
   4724 			/* count cached entries */
   4725 			f.f_choice = LDAP_FILTER_NOT;
   4726 			f.f_not = &f2;
   4727 			f2.f_choice = LDAP_FILTER_EQUALITY;
   4728 			f2.f_ava = &ava;
   4729 			f2.f_av_desc = slap_schema.si_ad_objectClass;
   4730 			BER_BVSTR( &f2.f_av_value, "glue" );
   4731 			ber_str2bv( "(!(objectClass=glue))", 0, 0, &op->ors_filterstr );
   4732 
   4733 			op->ors_slimit = SLAP_NO_LIMIT;
   4734 			op->ors_scope = LDAP_SCOPE_SUBTREE;
   4735 			op->ors_attrs = slap_anlist_no_attrs;
   4736 
   4737 			rs_reinit( &rs, REP_RESULT );
   4738 			op->o_callback->sc_response = pcache_cachedquery_count_cb;
   4739 			op->o_callback->sc_private = &rs.sr_nentries;
   4740 
   4741 			rc = op->o_bd->be_search( op, &rs );
   4742 
   4743 			cm->cur_entries = rs.sr_nentries;
   4744 
   4745 			/* ignore errors */
   4746 			rc = 0;
   4747 		}
   4748 	}
   4749 	return rc;
   4750 }
   4751 
   4752 static int
   4753 pcache_db_open(
   4754 	BackendDB *be,
   4755 	ConfigReply *cr )
   4756 {
   4757 	slap_overinst	*on = (slap_overinst *)be->bd_info;
   4758 	cache_manager	*cm = on->on_bi.bi_private;
   4759 	query_manager*  qm = cm->qm;
   4760 	int		i, ncf = 0, rf = 0, nrf = 0, rc = 0;
   4761 
   4762 	/* check attr sets */
   4763 	for ( i = 0; i < cm->numattrsets; i++) {
   4764 		if ( !( qm->attr_sets[i].flags & PC_CONFIGURED ) ) {
   4765 			if ( qm->attr_sets[i].flags & PC_REFERENCED ) {
   4766 				Debug( LDAP_DEBUG_CONFIG, "pcache: attr set #%d not configured but referenced.\n", i );
   4767 				rf++;
   4768 
   4769 			} else {
   4770 				Debug( LDAP_DEBUG_CONFIG, "pcache: warning, attr set #%d not configured.\n", i );
   4771 			}
   4772 			ncf++;
   4773 
   4774 		} else if ( !( qm->attr_sets[i].flags & PC_REFERENCED ) ) {
   4775 			Debug( LDAP_DEBUG_CONFIG, "pcache: attr set #%d configured but not referenced.\n", i );
   4776 			nrf++;
   4777 		}
   4778 	}
   4779 
   4780 	if ( ncf || rf || nrf ) {
   4781 		Debug( LDAP_DEBUG_CONFIG, "pcache: warning, %d attr sets configured but not referenced.\n", nrf );
   4782 		Debug( LDAP_DEBUG_CONFIG, "pcache: warning, %d attr sets not configured.\n", ncf );
   4783 		Debug( LDAP_DEBUG_CONFIG, "pcache: %d attr sets not configured but referenced.\n", rf );
   4784 
   4785 		if ( rf > 0 ) {
   4786 			return 1;
   4787 		}
   4788 	}
   4789 
   4790 	/* need to inherit something from the original database... */
   4791 	cm->db.be_def_limit = be->be_def_limit;
   4792 	cm->db.be_limits = be->be_limits;
   4793 	cm->db.be_acl = be->be_acl;
   4794 	cm->db.be_dfltaccess = be->be_dfltaccess;
   4795 
   4796 	if ( SLAP_DBMONITORING( be ) ) {
   4797 		SLAP_DBFLAGS( &cm->db ) |= SLAP_DBFLAG_MONITORING;
   4798 
   4799 	} else {
   4800 		SLAP_DBFLAGS( &cm->db ) &= ~SLAP_DBFLAG_MONITORING;
   4801 	}
   4802 
   4803 	if ( !cm->defer_db_open ) {
   4804 		rc = pcache_db_open2( on, cr );
   4805 	}
   4806 
   4807 #ifdef PCACHE_MONITOR
   4808 	if ( rc == LDAP_SUCCESS ) {
   4809 		rc = pcache_monitor_db_open( be );
   4810 	}
   4811 #endif /* PCACHE_MONITOR */
   4812 
   4813 	return rc;
   4814 }
   4815 
   4816 static void
   4817 pcache_free_qbase( void *v )
   4818 {
   4819 	Qbase *qb = v;
   4820 	int i;
   4821 
   4822 	for (i=0; i<3; i++)
   4823 		ldap_tavl_free( qb->scopes[i], NULL );
   4824 	ch_free( qb );
   4825 }
   4826 
   4827 static int
   4828 pcache_db_close(
   4829 	BackendDB *be,
   4830 	ConfigReply *cr
   4831 )
   4832 {
   4833 	slap_overinst *on = (slap_overinst *)be->bd_info;
   4834 	cache_manager *cm = on->on_bi.bi_private;
   4835 	query_manager *qm = cm->qm;
   4836 	QueryTemplate *tm;
   4837 	int rc = 0;
   4838 
   4839 	/* stop the thread ... */
   4840 	if ( cm->cc_arg ) {
   4841 		ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
   4842 		if ( ldap_pvt_runqueue_isrunning( &slapd_rq, cm->cc_arg ) ) {
   4843 			ldap_pvt_runqueue_stoptask( &slapd_rq, cm->cc_arg );
   4844 		}
   4845 		ldap_pvt_runqueue_remove( &slapd_rq, cm->cc_arg );
   4846 		ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
   4847 		cm->cc_arg = NULL;
   4848 	}
   4849 
   4850 	if ( cm->save_queries ) {
   4851 		CachedQuery	*qc;
   4852 		BerVarray	vals = NULL;
   4853 
   4854 		void		*thrctx;
   4855 		Connection	conn = { 0 };
   4856 		OperationBuffer	opbuf;
   4857 		Operation	*op;
   4858 		slap_callback	cb = { 0 };
   4859 
   4860 		SlapReply	rs = { REP_RESULT };
   4861 		Modifications	mod = {{ 0 }};
   4862 
   4863 		thrctx = ldap_pvt_thread_pool_context();
   4864 
   4865 		connection_fake_init2( &conn, &opbuf, thrctx, 0 );
   4866 		op = &opbuf.ob_op;
   4867 
   4868                 mod.sml_numvals = 0;
   4869 		if ( qm->templates != NULL ) {
   4870 			for ( tm = qm->templates; tm != NULL; tm = tm->qmnext ) {
   4871 				for ( qc = tm->query; qc; qc = qc->next ) {
   4872 					struct berval	bv;
   4873 
   4874 					if ( query2url( op, qc, &bv, 0 ) == 0 ) {
   4875 						ber_bvarray_add_x( &vals, &bv, op->o_tmpmemctx );
   4876                 				mod.sml_numvals++;
   4877 					}
   4878 				}
   4879 			}
   4880 		}
   4881 
   4882 		op->o_bd = &cm->db;
   4883 		op->o_dn = cm->db.be_rootdn;
   4884 		op->o_ndn = cm->db.be_rootndn;
   4885 
   4886 		op->o_tag = LDAP_REQ_MODIFY;
   4887 		op->o_protocol = LDAP_VERSION3;
   4888 		cb.sc_response = slap_null_cb;
   4889 		op->o_callback = &cb;
   4890 		op->o_time = slap_get_time();
   4891 		op->o_do_not_cache = 1;
   4892 		op->o_managedsait = SLAP_CONTROL_CRITICAL;
   4893 
   4894 		op->o_req_dn = op->o_bd->be_suffix[0];
   4895 		op->o_req_ndn = op->o_bd->be_nsuffix[0];
   4896 
   4897 		mod.sml_op = LDAP_MOD_REPLACE;
   4898 		mod.sml_flags = 0;
   4899 		mod.sml_desc = ad_cachedQueryURL;
   4900 		mod.sml_type = ad_cachedQueryURL->ad_cname;
   4901 		mod.sml_values = vals;
   4902 		mod.sml_nvalues = NULL;
   4903 		mod.sml_next = NULL;
   4904 		Debug( pcache_debug,
   4905 			"%sSETTING CACHED QUERY URLS\n",
   4906 			vals == NULL ? "RE" : "" );
   4907 
   4908 		op->orm_modlist = &mod;
   4909 
   4910 		op->o_bd->be_modify( op, &rs );
   4911 
   4912 		ber_bvarray_free_x( vals, op->o_tmpmemctx );
   4913 	}
   4914 
   4915 	/* cleanup stuff inherited from the original database... */
   4916 	cm->db.be_limits = NULL;
   4917 	cm->db.be_acl = NULL;
   4918 
   4919 	if ( cm->db.bd_info->bi_db_close ) {
   4920 		rc = cm->db.bd_info->bi_db_close( &cm->db, NULL );
   4921 	}
   4922 
   4923 #ifdef PCACHE_MONITOR
   4924 	if ( rc == LDAP_SUCCESS ) {
   4925 		rc = pcache_monitor_db_close( be );
   4926 	}
   4927 #endif /* PCACHE_MONITOR */
   4928 
   4929 	return rc;
   4930 }
   4931 
   4932 static int
   4933 pcache_db_destroy(
   4934 	BackendDB *be,
   4935 	ConfigReply *cr
   4936 )
   4937 {
   4938 	slap_overinst *on = (slap_overinst *)be->bd_info;
   4939 	cache_manager *cm = on->on_bi.bi_private;
   4940 	query_manager *qm = cm->qm;
   4941 	QueryTemplate *tm;
   4942 	int i;
   4943 
   4944 	if ( cm->db.be_private != NULL ) {
   4945 		backend_stopdown_one( &cm->db );
   4946 	}
   4947 
   4948 	while ( (tm = qm->templates) != NULL ) {
   4949 		CachedQuery *qc, *qn;
   4950 		qm->templates = tm->qmnext;
   4951 		for ( qc = tm->query; qc; qc = qn ) {
   4952 			qn = qc->next;
   4953 			free_query( qc );
   4954 		}
   4955 		ldap_avl_free( tm->qbase, pcache_free_qbase );
   4956 		free( tm->querystr.bv_val );
   4957 		free( tm->bindfattrs );
   4958 		free( tm->bindftemp.bv_val );
   4959 		free( tm->bindfilterstr.bv_val );
   4960 		free( tm->bindbase.bv_val );
   4961 		filter_free( tm->bindfilter );
   4962 		ldap_pvt_thread_rdwr_destroy( &tm->t_rwlock );
   4963 		free( tm->t_attrs.attrs );
   4964 		free( tm );
   4965 	}
   4966 
   4967 	for ( i = 0; i < cm->numattrsets; i++ ) {
   4968 		int j;
   4969 
   4970 		/* Account of LDAP_NO_ATTRS */
   4971 		if ( !qm->attr_sets[i].count ) continue;
   4972 
   4973 		for ( j = 0; !BER_BVISNULL( &qm->attr_sets[i].attrs[j].an_name ); j++ ) {
   4974 			if ( qm->attr_sets[i].attrs[j].an_desc &&
   4975 					( qm->attr_sets[i].attrs[j].an_desc->ad_flags &
   4976 					  SLAP_DESC_TEMPORARY ) ) {
   4977 				slap_sl_mfuncs.bmf_free( qm->attr_sets[i].attrs[j].an_desc, NULL );
   4978 			}
   4979 		}
   4980 		free( qm->attr_sets[i].attrs );
   4981 	}
   4982 	free( qm->attr_sets );
   4983 	qm->attr_sets = NULL;
   4984 
   4985 	ldap_pvt_thread_mutex_destroy( &qm->lru_mutex );
   4986 	ldap_pvt_thread_mutex_destroy( &cm->cache_mutex );
   4987 	free( qm );
   4988 	free( cm );
   4989 
   4990 #ifdef PCACHE_MONITOR
   4991 	pcache_monitor_db_destroy( be );
   4992 #endif /* PCACHE_MONITOR */
   4993 
   4994 	return 0;
   4995 }
   4996 
   4997 #ifdef PCACHE_CONTROL_PRIVDB
   4998 /*
   4999         Control ::= SEQUENCE {
   5000              controlType             LDAPOID,
   5001              criticality             BOOLEAN DEFAULT FALSE,
   5002              controlValue            OCTET STRING OPTIONAL }
   5003 
   5004         controlType ::= 1.3.6.1.4.1.4203.666.11.9.5.1
   5005 
   5006  * criticality must be TRUE; controlValue must be absent.
   5007  */
   5008 static int
   5009 parse_privdb_ctrl(
   5010 	Operation	*op,
   5011 	SlapReply	*rs,
   5012 	LDAPControl	*ctrl )
   5013 {
   5014 	if ( op->o_ctrlflag[ privDB_cid ] != SLAP_CONTROL_NONE ) {
   5015 		rs->sr_text = "privateDB control specified multiple times";
   5016 		return LDAP_PROTOCOL_ERROR;
   5017 	}
   5018 
   5019 	if ( !BER_BVISNULL( &ctrl->ldctl_value ) ) {
   5020 		rs->sr_text = "privateDB control value not absent";
   5021 		return LDAP_PROTOCOL_ERROR;
   5022 	}
   5023 
   5024 	if ( !ctrl->ldctl_iscritical ) {
   5025 		rs->sr_text = "privateDB control criticality required";
   5026 		return LDAP_PROTOCOL_ERROR;
   5027 	}
   5028 
   5029 	op->o_ctrlflag[ privDB_cid ] = SLAP_CONTROL_CRITICAL;
   5030 
   5031 	return LDAP_SUCCESS;
   5032 }
   5033 
   5034 static char *extops[] = {
   5035 	LDAP_EXOP_MODIFY_PASSWD,
   5036 	NULL
   5037 };
   5038 #endif /* PCACHE_CONTROL_PRIVDB */
   5039 
   5040 static struct berval pcache_exop_MODIFY_PASSWD = BER_BVC( LDAP_EXOP_MODIFY_PASSWD );
   5041 #ifdef PCACHE_EXOP_QUERY_DELETE
   5042 static struct berval pcache_exop_QUERY_DELETE = BER_BVC( PCACHE_EXOP_QUERY_DELETE );
   5043 
   5044 #define	LDAP_TAG_EXOP_QUERY_DELETE_BASE	((LBER_CLASS_CONTEXT|LBER_CONSTRUCTED) + 0)
   5045 #define	LDAP_TAG_EXOP_QUERY_DELETE_DN	((LBER_CLASS_CONTEXT|LBER_CONSTRUCTED) + 1)
   5046 #define	LDAP_TAG_EXOP_QUERY_DELETE_UUID	((LBER_CLASS_CONTEXT|LBER_CONSTRUCTED) + 2)
   5047 
   5048 /*
   5049         ExtendedRequest ::= [APPLICATION 23] SEQUENCE {
   5050              requestName      [0] LDAPOID,
   5051              requestValue     [1] OCTET STRING OPTIONAL }
   5052 
   5053         requestName ::= 1.3.6.1.4.1.4203.666.11.9.6.1
   5054 
   5055         requestValue ::= SEQUENCE { CHOICE {
   5056                   baseDN           [0] LDAPDN
   5057                   entryDN          [1] LDAPDN },
   5058              queryID          [2] OCTET STRING (SIZE(16))
   5059                   -- constrained to UUID }
   5060 
   5061  * Either baseDN or entryDN must be present, to allow database selection.
   5062  *
   5063  * 1. if baseDN and queryID are present, then the query corresponding
   5064  *    to queryID is deleted;
   5065  * 2. if baseDN is present and queryID is absent, then all queries
   5066  *    are deleted;
   5067  * 3. if entryDN is present and queryID is absent, then all queries
   5068  *    corresponding to the queryID values present in entryDN are deleted;
   5069  * 4. if entryDN and queryID are present, then all queries
   5070  *    corresponding to the queryID values present in entryDN are deleted,
   5071  *    but only if the value of queryID is contained in the entry;
   5072  *
   5073  * Currently, only 1, 3 and 4 are implemented.  2 can be obtained by either
   5074  * recursively deleting the database (ldapdelete -r) with PRIVDB control,
   5075  * or by removing the database files.
   5076 
   5077         ExtendedResponse ::= [APPLICATION 24] SEQUENCE {
   5078              COMPONENTS OF LDAPResult,
   5079              responseName     [10] LDAPOID OPTIONAL,
   5080              responseValue    [11] OCTET STRING OPTIONAL }
   5081 
   5082  * responseName and responseValue must be absent.
   5083  */
   5084 
   5085 /*
   5086  * - on success, *tagp is either LDAP_TAG_EXOP_QUERY_DELETE_BASE
   5087  *   or LDAP_TAG_EXOP_QUERY_DELETE_DN.
   5088  * - if ndn != NULL, it is set to the normalized DN in the request
   5089  *   corresponding to either the baseDN or the entryDN, according
   5090  *   to *tagp; memory is malloc'ed on the Operation's slab, and must
   5091  *   be freed by the caller.
   5092  * - if uuid != NULL, it is set to point to the normalized UUID;
   5093  *   memory is malloc'ed on the Operation's slab, and must
   5094  *   be freed by the caller.
   5095  */
   5096 static int
   5097 pcache_parse_query_delete(
   5098 	struct berval	*in,
   5099 	ber_tag_t	*tagp,
   5100 	struct berval	*ndn,
   5101 	struct berval	*uuid,
   5102 	const char	**text,
   5103 	void		*ctx )
   5104 {
   5105 	int			rc = LDAP_SUCCESS;
   5106 	ber_tag_t		tag;
   5107 	ber_len_t		len = -1;
   5108 	BerElementBuffer	berbuf;
   5109 	BerElement		*ber = (BerElement *)&berbuf;
   5110 	struct berval		reqdata = BER_BVNULL;
   5111 
   5112 	*text = NULL;
   5113 
   5114 	if ( ndn ) {
   5115 		BER_BVZERO( ndn );
   5116 	}
   5117 
   5118 	if ( uuid ) {
   5119 		BER_BVZERO( uuid );
   5120 	}
   5121 
   5122 	if ( in == NULL || in->bv_len == 0 ) {
   5123 		*text = "empty request data field in queryDelete exop";
   5124 		return LDAP_PROTOCOL_ERROR;
   5125 	}
   5126 
   5127 	ber_dupbv_x( &reqdata, in, ctx );
   5128 
   5129 	/* ber_init2 uses reqdata directly, doesn't allocate new buffers */
   5130 	ber_init2( ber, &reqdata, 0 );
   5131 
   5132 	tag = ber_scanf( ber, "{" /*}*/ );
   5133 
   5134 	if ( tag == LBER_ERROR ) {
   5135 		Debug( LDAP_DEBUG_TRACE,
   5136 			"pcache_parse_query_delete: decoding error.\n" );
   5137 		goto decoding_error;
   5138 	}
   5139 
   5140 	tag = ber_peek_tag( ber, &len );
   5141 	if ( tag == LDAP_TAG_EXOP_QUERY_DELETE_BASE
   5142 		|| tag == LDAP_TAG_EXOP_QUERY_DELETE_DN )
   5143 	{
   5144 		*tagp = tag;
   5145 
   5146 		if ( ndn != NULL ) {
   5147 			struct berval	dn;
   5148 
   5149 			tag = ber_scanf( ber, "m", &dn );
   5150 			if ( tag == LBER_ERROR ) {
   5151 				Debug( LDAP_DEBUG_TRACE,
   5152 					"pcache_parse_query_delete: DN parse failed.\n" );
   5153 				goto decoding_error;
   5154 			}
   5155 
   5156 			rc = dnNormalize( 0, NULL, NULL, &dn, ndn, ctx );
   5157 			if ( rc != LDAP_SUCCESS ) {
   5158 				*text = "invalid DN in queryDelete exop request data";
   5159 				goto done;
   5160 			}
   5161 
   5162 		} else {
   5163 			tag = ber_scanf( ber, "x" /* "m" */ );
   5164 			if ( tag == LBER_DEFAULT ) {
   5165 				goto decoding_error;
   5166 			}
   5167 		}
   5168 
   5169 		tag = ber_peek_tag( ber, &len );
   5170 	}
   5171 
   5172 	if ( tag == LDAP_TAG_EXOP_QUERY_DELETE_UUID ) {
   5173 		if ( uuid != NULL ) {
   5174 			struct berval	bv;
   5175 			char		uuidbuf[ LDAP_LUTIL_UUIDSTR_BUFSIZE ];
   5176 
   5177 			tag = ber_scanf( ber, "m", &bv );
   5178 			if ( tag == LBER_ERROR ) {
   5179 				Debug( LDAP_DEBUG_TRACE,
   5180 					"pcache_parse_query_delete: UUID parse failed.\n" );
   5181 				goto decoding_error;
   5182 			}
   5183 
   5184 			if ( bv.bv_len != 16 ) {
   5185 				Debug( LDAP_DEBUG_TRACE,
   5186 					"pcache_parse_query_delete: invalid UUID length %lu.\n",
   5187 					(unsigned long)bv.bv_len );
   5188 				goto decoding_error;
   5189 			}
   5190 
   5191 			rc = lutil_uuidstr_from_normalized(
   5192 				bv.bv_val, bv.bv_len,
   5193 				uuidbuf, sizeof( uuidbuf ) );
   5194 			if ( rc == -1 ) {
   5195 				goto decoding_error;
   5196 			}
   5197 			ber_str2bv( uuidbuf, rc, 1, uuid );
   5198 			rc = LDAP_SUCCESS;
   5199 
   5200 		} else {
   5201 			tag = ber_skip_tag( ber, &len );
   5202 			if ( tag == LBER_DEFAULT ) {
   5203 				goto decoding_error;
   5204 			}
   5205 
   5206 			if ( len != 16 ) {
   5207 				Debug( LDAP_DEBUG_TRACE,
   5208 					"pcache_parse_query_delete: invalid UUID length %lu.\n",
   5209 					(unsigned long)len );
   5210 				goto decoding_error;
   5211 			}
   5212 		}
   5213 
   5214 		tag = ber_peek_tag( ber, &len );
   5215 	}
   5216 
   5217 	if ( tag != LBER_DEFAULT || len != 0 ) {
   5218 decoding_error:;
   5219 		Debug( LDAP_DEBUG_TRACE,
   5220 			"pcache_parse_query_delete: decoding error\n" );
   5221 		rc = LDAP_PROTOCOL_ERROR;
   5222 		*text = "queryDelete data decoding error";
   5223 
   5224 done:;
   5225 		if ( ndn && !BER_BVISNULL( ndn ) ) {
   5226 			slap_sl_free( ndn->bv_val, ctx );
   5227 			BER_BVZERO( ndn );
   5228 		}
   5229 
   5230 		if ( uuid && !BER_BVISNULL( uuid ) ) {
   5231 			slap_sl_free( uuid->bv_val, ctx );
   5232 			BER_BVZERO( uuid );
   5233 		}
   5234 	}
   5235 
   5236 	if ( !BER_BVISNULL( &reqdata ) ) {
   5237 		ber_memfree_x( reqdata.bv_val, ctx );
   5238 	}
   5239 
   5240 	return rc;
   5241 }
   5242 
   5243 static int
   5244 pcache_exop_query_delete(
   5245 	Operation	*op,
   5246 	SlapReply	*rs )
   5247 {
   5248 	BackendDB	*bd = op->o_bd;
   5249 
   5250 	struct berval	uuid = BER_BVNULL,
   5251 			*uuidp = NULL;
   5252 	char		buf[ SLAP_TEXT_BUFLEN ];
   5253 	unsigned	len;
   5254 	ber_tag_t	tag = LBER_DEFAULT;
   5255 
   5256 	if ( LogTest( LDAP_DEBUG_STATS ) ) {
   5257 		uuidp = &uuid;
   5258 	}
   5259 
   5260 	rs->sr_err = pcache_parse_query_delete( op->ore_reqdata,
   5261 		&tag, &op->o_req_ndn, uuidp,
   5262 		&rs->sr_text, op->o_tmpmemctx );
   5263 	if ( rs->sr_err != LDAP_SUCCESS ) {
   5264 		return rs->sr_err;
   5265 	}
   5266 
   5267 	if ( LogTest( LDAP_DEBUG_STATS ) ) {
   5268 		assert( !BER_BVISNULL( &op->o_req_ndn ) );
   5269 		len = snprintf( buf, sizeof( buf ), " dn=\"%s\"", op->o_req_ndn.bv_val );
   5270 
   5271 		if ( !BER_BVISNULL( &uuid ) && len < sizeof( buf ) ) {
   5272 			snprintf( &buf[ len ], sizeof( buf ) - len, " pcacheQueryId=\"%s\"", uuid.bv_val );
   5273 		}
   5274 
   5275 		Debug( LDAP_DEBUG_STATS, "%s QUERY DELETE%s\n",
   5276 			op->o_log_prefix, buf );
   5277 	}
   5278 	op->o_req_dn = op->o_req_ndn;
   5279 
   5280 	op->o_bd = select_backend( &op->o_req_ndn, 0 );
   5281 	if ( op->o_bd == NULL ) {
   5282 		send_ldap_error( op, rs, LDAP_NO_SUCH_OBJECT,
   5283 			"no global superior knowledge" );
   5284 	}
   5285 	rs->sr_err = backend_check_restrictions( op, rs,
   5286 		(struct berval *)&pcache_exop_QUERY_DELETE );
   5287 	if ( rs->sr_err != LDAP_SUCCESS ) {
   5288 		goto done;
   5289 	}
   5290 
   5291 	if ( op->o_bd->be_extended == NULL ) {
   5292 		send_ldap_error( op, rs, LDAP_UNAVAILABLE_CRITICAL_EXTENSION,
   5293 			"backend does not support extended operations" );
   5294 		goto done;
   5295 	}
   5296 
   5297 	op->o_bd->be_extended( op, rs );
   5298 
   5299 done:;
   5300 	if ( !BER_BVISNULL( &op->o_req_ndn ) ) {
   5301 		op->o_tmpfree( op->o_req_ndn.bv_val, op->o_tmpmemctx );
   5302 		BER_BVZERO( &op->o_req_ndn );
   5303 		BER_BVZERO( &op->o_req_dn );
   5304 	}
   5305 
   5306 	if ( !BER_BVISNULL( &uuid ) ) {
   5307 		op->o_tmpfree( uuid.bv_val, op->o_tmpmemctx );
   5308 	}
   5309 
   5310 	op->o_bd = bd;
   5311 
   5312         return rs->sr_err;
   5313 }
   5314 #endif /* PCACHE_EXOP_QUERY_DELETE */
   5315 
   5316 static int
   5317 pcache_op_extended( Operation *op, SlapReply *rs )
   5318 {
   5319 	slap_overinst	*on = (slap_overinst *)op->o_bd->bd_info;
   5320 	cache_manager	*cm = on->on_bi.bi_private;
   5321 
   5322 #ifdef PCACHE_CONTROL_PRIVDB
   5323 	if ( op->o_ctrlflag[ privDB_cid ] == SLAP_CONTROL_CRITICAL ) {
   5324 		return pcache_op_privdb( op, rs );
   5325 	}
   5326 #endif /* PCACHE_CONTROL_PRIVDB */
   5327 
   5328 #ifdef PCACHE_EXOP_QUERY_DELETE
   5329 	if ( bvmatch( &op->ore_reqoid, &pcache_exop_QUERY_DELETE ) ) {
   5330 		struct berval	uuid = BER_BVNULL;
   5331 		ber_tag_t	tag = LBER_DEFAULT;
   5332 
   5333 		rs->sr_err = pcache_parse_query_delete( op->ore_reqdata,
   5334 			&tag, NULL, &uuid, &rs->sr_text, op->o_tmpmemctx );
   5335 		assert( rs->sr_err == LDAP_SUCCESS );
   5336 
   5337 		if ( tag == LDAP_TAG_EXOP_QUERY_DELETE_DN ) {
   5338 			/* remove all queries related to the selected entry */
   5339 			rs->sr_err = pcache_remove_entry_queries_from_cache( op,
   5340 				cm, &op->o_req_ndn, &uuid );
   5341 
   5342 		} else if ( tag == LDAP_TAG_EXOP_QUERY_DELETE_BASE ) {
   5343 			if ( !BER_BVISNULL( &uuid ) ) {
   5344 				/* remove the selected query */
   5345 				rs->sr_err = pcache_remove_query_from_cache( op,
   5346 					cm, &uuid );
   5347 
   5348 			} else {
   5349 				/* TODO: remove all queries */
   5350 				rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
   5351 				rs->sr_text = "deletion of all queries not implemented";
   5352 			}
   5353 		}
   5354 
   5355 		op->o_tmpfree( uuid.bv_val, op->o_tmpmemctx );
   5356 		return rs->sr_err;
   5357 	}
   5358 #endif /* PCACHE_EXOP_QUERY_DELETE */
   5359 
   5360 	/* We only care if we're configured for Bind caching */
   5361 	if ( bvmatch( &op->ore_reqoid, &pcache_exop_MODIFY_PASSWD ) &&
   5362 		cm->cache_binds ) {
   5363 		/* See if the local entry exists and has a password.
   5364 		 * It's too much work to find the matching query, so
   5365 		 * we just see if there's a hashed password to update.
   5366 		 */
   5367 		Operation op2 = *op;
   5368 		Entry *e = NULL;
   5369 		int rc;
   5370 		int doit = 0;
   5371 
   5372 		op2.o_bd = &cm->db;
   5373 		op2.o_dn = op->o_bd->be_rootdn;
   5374 		op2.o_ndn = op->o_bd->be_rootndn;
   5375 		rc = be_entry_get_rw( &op2, &op->o_req_ndn, NULL,
   5376 			slap_schema.si_ad_userPassword, 0, &e );
   5377 		if ( rc == LDAP_SUCCESS && e ) {
   5378 			/* See if a recognized password is hashed here */
   5379 			Attribute *a = attr_find( e->e_attrs,
   5380 				slap_schema.si_ad_userPassword );
   5381 			if ( a && a->a_vals[0].bv_val[0] == '{' &&
   5382 				lutil_passwd_scheme( a->a_vals[0].bv_val )) {
   5383 				doit = 1;
   5384 			}
   5385 			be_entry_release_r( &op2, e );
   5386 		}
   5387 
   5388 		if ( doit ) {
   5389 			rc = overlay_op_walk( op, rs, op_extended, on->on_info,
   5390 				on->on_next );
   5391 			if ( rc == LDAP_SUCCESS ) {
   5392 				req_pwdexop_s *qpw = &op->oq_pwdexop;
   5393 
   5394 				/* We don't care if it succeeds or not */
   5395 				pc_setpw( &op2, &qpw->rs_new, cm );
   5396 			}
   5397 			return rc;
   5398 		}
   5399 	}
   5400 	return SLAP_CB_CONTINUE;
   5401 }
   5402 
   5403 static int
   5404 pcache_entry_release( Operation  *op, Entry *e, int rw )
   5405 {
   5406 	slap_overinst	*on = (slap_overinst *)op->o_bd->bd_info;
   5407 	cache_manager	*cm = on->on_bi.bi_private;
   5408 	BackendDB *db = op->o_bd;
   5409 	int rc;
   5410 
   5411 	op->o_bd = &cm->db;
   5412 	rc = be_entry_release_rw( op, e, rw );
   5413 	op->o_bd = db;
   5414 	return rc;
   5415 }
   5416 
   5417 #ifdef PCACHE_MONITOR
   5418 
   5419 static int
   5420 pcache_monitor_update(
   5421 	Operation	*op,
   5422 	SlapReply	*rs,
   5423 	Entry		*e,
   5424 	void		*priv )
   5425 {
   5426 	cache_manager	*cm = (cache_manager *) priv;
   5427 	query_manager	*qm = cm->qm;
   5428 
   5429 	CachedQuery	*qc;
   5430 	BerVarray	vals = NULL;
   5431 
   5432 	attr_delete( &e->e_attrs, ad_cachedQueryURL );
   5433 	if ( ( SLAP_OPATTRS( rs->sr_attr_flags ) || ad_inlist( ad_cachedQueryURL, rs->sr_attrs ) )
   5434 		&& qm->templates != NULL )
   5435 	{
   5436 		QueryTemplate *tm;
   5437 
   5438 		for ( tm = qm->templates; tm != NULL; tm = tm->qmnext ) {
   5439 			for ( qc = tm->query; qc; qc = qc->next ) {
   5440 				struct berval	bv;
   5441 
   5442 				if ( query2url( op, qc, &bv, 1 ) == 0 ) {
   5443 					ber_bvarray_add_x( &vals, &bv, op->o_tmpmemctx );
   5444 				}
   5445 			}
   5446 		}
   5447 
   5448 
   5449 		if ( vals != NULL ) {
   5450 			attr_merge_normalize( e, ad_cachedQueryURL, vals, NULL );
   5451 			ber_bvarray_free_x( vals, op->o_tmpmemctx );
   5452 		}
   5453 	}
   5454 
   5455 	{
   5456 		Attribute	*a;
   5457 		char		buf[ SLAP_TEXT_BUFLEN ];
   5458 		struct berval	bv;
   5459 
   5460 		/* number of cached queries */
   5461 		a = attr_find( e->e_attrs, ad_numQueries );
   5462 		assert( a != NULL );
   5463 
   5464 		bv.bv_val = buf;
   5465 		bv.bv_len = snprintf( buf, sizeof( buf ), "%lu", cm->num_cached_queries );
   5466 
   5467 		if ( a->a_nvals != a->a_vals ) {
   5468 			ber_bvreplace( &a->a_nvals[ 0 ], &bv );
   5469 		}
   5470 		ber_bvreplace( &a->a_vals[ 0 ], &bv );
   5471 
   5472 		/* number of cached entries */
   5473 		a = attr_find( e->e_attrs, ad_numEntries );
   5474 		assert( a != NULL );
   5475 
   5476 		bv.bv_val = buf;
   5477 		bv.bv_len = snprintf( buf, sizeof( buf ), "%d", cm->cur_entries );
   5478 
   5479 		if ( a->a_nvals != a->a_vals ) {
   5480 			ber_bvreplace( &a->a_nvals[ 0 ], &bv );
   5481 		}
   5482 		ber_bvreplace( &a->a_vals[ 0 ], &bv );
   5483 	}
   5484 
   5485 	return SLAP_CB_CONTINUE;
   5486 }
   5487 
   5488 static int
   5489 pcache_monitor_free(
   5490 	Entry		*e,
   5491 	void		**priv )
   5492 {
   5493 	struct berval	values[ 2 ];
   5494 	Modification	mod = { 0 };
   5495 
   5496 	const char	*text;
   5497 	char		textbuf[ SLAP_TEXT_BUFLEN ];
   5498 
   5499 	int		rc;
   5500 
   5501 	/* NOTE: if slap_shutdown != 0, priv might have already been freed */
   5502 	*priv = NULL;
   5503 
   5504 	/* Remove objectClass */
   5505 	mod.sm_op = LDAP_MOD_DELETE;
   5506 	mod.sm_desc = slap_schema.si_ad_objectClass;
   5507 	mod.sm_values = values;
   5508 	mod.sm_numvals = 1;
   5509 	values[ 0 ] = oc_olmPCache->soc_cname;
   5510 	BER_BVZERO( &values[ 1 ] );
   5511 
   5512 	rc = modify_delete_values( e, &mod, 1, &text,
   5513 		textbuf, sizeof( textbuf ) );
   5514 	/* don't care too much about return code... */
   5515 
   5516 	/* remove attrs */
   5517 	mod.sm_values = NULL;
   5518 	mod.sm_desc = ad_cachedQueryURL;
   5519 	mod.sm_numvals = 0;
   5520 	rc = modify_delete_values( e, &mod, 1, &text,
   5521 		textbuf, sizeof( textbuf ) );
   5522 	/* don't care too much about return code... */
   5523 
   5524 	/* remove attrs */
   5525 	mod.sm_values = NULL;
   5526 	mod.sm_desc = ad_numQueries;
   5527 	mod.sm_numvals = 0;
   5528 	rc = modify_delete_values( e, &mod, 1, &text,
   5529 		textbuf, sizeof( textbuf ) );
   5530 	/* don't care too much about return code... */
   5531 
   5532 	/* remove attrs */
   5533 	mod.sm_values = NULL;
   5534 	mod.sm_desc = ad_numEntries;
   5535 	mod.sm_numvals = 0;
   5536 	rc = modify_delete_values( e, &mod, 1, &text,
   5537 		textbuf, sizeof( textbuf ) );
   5538 	/* don't care too much about return code... */
   5539 
   5540 	return SLAP_CB_CONTINUE;
   5541 }
   5542 
   5543 /*
   5544  * call from within pcache_initialize()
   5545  */
   5546 static int
   5547 pcache_monitor_initialize( void )
   5548 {
   5549 	static int	pcache_monitor_initialized = 0;
   5550 
   5551 	if ( backend_info( "monitor" ) == NULL ) {
   5552 		return -1;
   5553 	}
   5554 
   5555 	if ( pcache_monitor_initialized++ ) {
   5556 		return 0;
   5557 	}
   5558 
   5559 	return 0;
   5560 }
   5561 
   5562 static int
   5563 pcache_monitor_db_init( BackendDB *be )
   5564 {
   5565 	if ( pcache_monitor_initialize() == LDAP_SUCCESS ) {
   5566 		SLAP_DBFLAGS( be ) |= SLAP_DBFLAG_MONITORING;
   5567 	}
   5568 
   5569 	return 0;
   5570 }
   5571 
   5572 static int
   5573 pcache_monitor_db_open( BackendDB *be )
   5574 {
   5575 	slap_overinst		*on = (slap_overinst *)be->bd_info;
   5576 	cache_manager		*cm = on->on_bi.bi_private;
   5577 	Attribute		*a, *next;
   5578 	monitor_callback_t	*cb = NULL;
   5579 	int			rc = 0;
   5580 	BackendInfo		*mi;
   5581 	monitor_extra_t		*mbe;
   5582 
   5583 	if ( !SLAP_DBMONITORING( be ) ) {
   5584 		return 0;
   5585 	}
   5586 
   5587 	mi = backend_info( "monitor" );
   5588 	if ( !mi || !mi->bi_extra ) {
   5589 		SLAP_DBFLAGS( be ) ^= SLAP_DBFLAG_MONITORING;
   5590 		return 0;
   5591 	}
   5592 	mbe = mi->bi_extra;
   5593 
   5594 	/* don't bother if monitor is not configured */
   5595 	if ( !mbe->is_configured() ) {
   5596 		static int warning = 0;
   5597 
   5598 		if ( warning++ == 0 ) {
   5599 			Debug( LDAP_DEBUG_CONFIG, "pcache_monitor_db_open: "
   5600 				"monitoring disabled; "
   5601 				"configure monitor database to enable\n" );
   5602 		}
   5603 
   5604 		return 0;
   5605 	}
   5606 
   5607 	/* alloc as many as required (plus 1 for objectClass) */
   5608 	a = attrs_alloc( 1 + 2 );
   5609 	if ( a == NULL ) {
   5610 		rc = 1;
   5611 		goto cleanup;
   5612 	}
   5613 
   5614 	a->a_desc = slap_schema.si_ad_objectClass;
   5615 	attr_valadd( a, &oc_olmPCache->soc_cname, NULL, 1 );
   5616 	next = a->a_next;
   5617 
   5618 	{
   5619 		struct berval	bv = BER_BVC( "0" );
   5620 
   5621 		next->a_desc = ad_numQueries;
   5622 		attr_valadd( next, &bv, NULL, 1 );
   5623 		next = next->a_next;
   5624 
   5625 		next->a_desc = ad_numEntries;
   5626 		attr_valadd( next, &bv, NULL, 1 );
   5627 		next = next->a_next;
   5628 	}
   5629 
   5630 	cb = ch_calloc( sizeof( monitor_callback_t ), 1 );
   5631 	cb->mc_update = pcache_monitor_update;
   5632 	cb->mc_free = pcache_monitor_free;
   5633 	cb->mc_private = (void *)cm;
   5634 
   5635 	/* make sure the database is registered; then add monitor attributes */
   5636 	BER_BVZERO( &cm->monitor_ndn );
   5637 	rc = mbe->register_overlay( be, on, &cm->monitor_ndn );
   5638 	if ( rc == 0 ) {
   5639 		rc = mbe->register_entry_attrs( &cm->monitor_ndn, a, cb,
   5640 			NULL, -1, NULL);
   5641 	}
   5642 
   5643 cleanup:;
   5644 	if ( rc != 0 ) {
   5645 		if ( cb != NULL ) {
   5646 			ch_free( cb );
   5647 			cb = NULL;
   5648 		}
   5649 
   5650 		if ( a != NULL ) {
   5651 			attrs_free( a );
   5652 			a = NULL;
   5653 		}
   5654 	}
   5655 
   5656 	/* store for cleanup */
   5657 	cm->monitor_cb = (void *)cb;
   5658 
   5659 	/* we don't need to keep track of the attributes, because
   5660 	 * mdb_monitor_free() takes care of everything */
   5661 	if ( a != NULL ) {
   5662 		attrs_free( a );
   5663 	}
   5664 
   5665 	return rc;
   5666 }
   5667 
   5668 static int
   5669 pcache_monitor_db_close( BackendDB *be )
   5670 {
   5671 	slap_overinst *on = (slap_overinst *)be->bd_info;
   5672 	cache_manager *cm = on->on_bi.bi_private;
   5673 
   5674 	if ( !BER_BVISNULL( &cm->monitor_ndn )) {
   5675 		BackendInfo		*mi = backend_info( "monitor" );
   5676 		monitor_extra_t		*mbe;
   5677 
   5678 		if ( mi && mi->bi_extra ) {
   5679 			struct berval dummy = BER_BVNULL;
   5680 			mbe = mi->bi_extra;
   5681 			mbe->unregister_entry_callback( &cm->monitor_ndn,
   5682 				(monitor_callback_t *)cm->monitor_cb,
   5683 				&dummy, 0, &dummy );
   5684 		}
   5685 	}
   5686 
   5687 	return 0;
   5688 }
   5689 
   5690 static int
   5691 pcache_monitor_db_destroy( BackendDB *be )
   5692 {
   5693 	return 0;
   5694 }
   5695 
   5696 #endif /* PCACHE_MONITOR */
   5697 
   5698 static slap_overinst pcache;
   5699 
   5700 static char *obsolete_names[] = {
   5701 	"proxycache",
   5702 	NULL
   5703 };
   5704 
   5705 #if SLAPD_OVER_PROXYCACHE == SLAPD_MOD_DYNAMIC
   5706 static
   5707 #endif /* SLAPD_OVER_PROXYCACHE == SLAPD_MOD_DYNAMIC */
   5708 int
   5709 pcache_initialize()
   5710 {
   5711 	int i, code;
   5712 	struct berval debugbv = BER_BVC("pcache");
   5713 	ConfigArgs c;
   5714 	char *argv[ 4 ];
   5715 
   5716         /* olcDatabaseDummy is defined in slapd, and Windows
   5717            will not let us initialize a struct element with a data pointer
   5718            from another library, so we have to initialize this element
   5719            "by hand".  */
   5720         pcocs[1].co_table = olcDatabaseDummy;
   5721 
   5722 
   5723 	code = slap_loglevel_get( &debugbv, &pcache_debug );
   5724 	if ( code ) {
   5725 		return code;
   5726 	}
   5727 
   5728 #ifdef PCACHE_CONTROL_PRIVDB
   5729 	code = register_supported_control( PCACHE_CONTROL_PRIVDB,
   5730 		SLAP_CTRL_BIND|SLAP_CTRL_ACCESS|SLAP_CTRL_HIDE, extops,
   5731 		parse_privdb_ctrl, &privDB_cid );
   5732 	if ( code != LDAP_SUCCESS ) {
   5733 		Debug( LDAP_DEBUG_ANY,
   5734 			"pcache_initialize: failed to register control %s (%d)\n",
   5735 			PCACHE_CONTROL_PRIVDB, code );
   5736 		return code;
   5737 	}
   5738 #endif /* PCACHE_CONTROL_PRIVDB */
   5739 
   5740 #ifdef PCACHE_EXOP_QUERY_DELETE
   5741 	code = load_extop2( (struct berval *)&pcache_exop_QUERY_DELETE,
   5742 		SLAP_EXOP_WRITES|SLAP_EXOP_HIDE, pcache_exop_query_delete,
   5743 		0 );
   5744 	if ( code != LDAP_SUCCESS ) {
   5745 		Debug( LDAP_DEBUG_ANY,
   5746 			"pcache_initialize: unable to register queryDelete exop: %d.\n",
   5747 			code );
   5748 		return code;
   5749 	}
   5750 #endif /* PCACHE_EXOP_QUERY_DELETE */
   5751 
   5752 	argv[ 0 ] = "back-mdb monitor";
   5753 	c.argv = argv;
   5754 	c.argc = 3;
   5755 	c.fname = argv[0];
   5756 
   5757 	for ( i = 0; s_oid[ i ].name; i++ ) {
   5758 		c.lineno = i;
   5759 		argv[ 1 ] = s_oid[ i ].name;
   5760 		argv[ 2 ] = s_oid[ i ].oid;
   5761 
   5762 		if ( parse_oidm( &c, 0, NULL ) != 0 ) {
   5763 			Debug( LDAP_DEBUG_ANY, "pcache_initialize: "
   5764 				"unable to add objectIdentifier \"%s=%s\"\n",
   5765 				s_oid[ i ].name, s_oid[ i ].oid );
   5766 			return 1;
   5767 		}
   5768 	}
   5769 
   5770 	for ( i = 0; s_ad[i].desc != NULL; i++ ) {
   5771 		code = register_at( s_ad[i].desc, s_ad[i].adp, 0 );
   5772 		if ( code ) {
   5773 			Debug( LDAP_DEBUG_ANY,
   5774 				"pcache_initialize: register_at #%d failed\n", i );
   5775 			return code;
   5776 		}
   5777 		(*s_ad[i].adp)->ad_type->sat_flags |= SLAP_AT_HIDE;
   5778 	}
   5779 
   5780 	for ( i = 0; s_oc[i].desc != NULL; i++ ) {
   5781 		code = register_oc( s_oc[i].desc, s_oc[i].ocp, 0 );
   5782 		if ( code ) {
   5783 			Debug( LDAP_DEBUG_ANY,
   5784 				"pcache_initialize: register_oc #%d failed\n", i );
   5785 			return code;
   5786 		}
   5787 		(*s_oc[i].ocp)->soc_flags |= SLAP_OC_HIDE;
   5788 	}
   5789 
   5790 	pcache.on_bi.bi_type = "pcache";
   5791 	pcache.on_bi.bi_obsolete_names = obsolete_names;
   5792 	pcache.on_bi.bi_db_init = pcache_db_init;
   5793 	pcache.on_bi.bi_db_config = pcache_db_config;
   5794 	pcache.on_bi.bi_db_open = pcache_db_open;
   5795 	pcache.on_bi.bi_db_close = pcache_db_close;
   5796 	pcache.on_bi.bi_db_destroy = pcache_db_destroy;
   5797 
   5798 	pcache.on_bi.bi_op_search = pcache_op_search;
   5799 	pcache.on_bi.bi_op_bind = pcache_op_bind;
   5800 #ifdef PCACHE_CONTROL_PRIVDB
   5801 	pcache.on_bi.bi_op_compare = pcache_op_privdb;
   5802 	pcache.on_bi.bi_op_modrdn = pcache_op_privdb;
   5803 	pcache.on_bi.bi_op_modify = pcache_op_privdb;
   5804 	pcache.on_bi.bi_op_add = pcache_op_privdb;
   5805 	pcache.on_bi.bi_op_delete = pcache_op_privdb;
   5806 #endif /* PCACHE_CONTROL_PRIVDB */
   5807 	pcache.on_bi.bi_extended = pcache_op_extended;
   5808 
   5809 	pcache.on_bi.bi_entry_release_rw = pcache_entry_release;
   5810 	pcache.on_bi.bi_chk_controls = pcache_chk_controls;
   5811 
   5812 	pcache.on_bi.bi_cf_ocs = pcocs;
   5813 
   5814 	code = config_register_schema( pccfg, pcocs );
   5815 	if ( code ) return code;
   5816 
   5817 	return overlay_register( &pcache );
   5818 }
   5819 
   5820 #if SLAPD_OVER_PROXYCACHE == SLAPD_MOD_DYNAMIC
   5821 int init_module(int argc, char *argv[]) {
   5822 	return pcache_initialize();
   5823 }
   5824 #endif
   5825 
   5826 #endif	/* defined(SLAPD_OVER_PROXYCACHE) */
   5827