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