1 /* $OpenLDAP: pkg/ldap/servers/slapd/overlays/pcache.c,v 1.88.2.17 2008/07/08 21:09:37 quanah Exp $ */
2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4 * Copyright 2003-2008 The OpenLDAP Foundation.
5 * Portions Copyright 2003 IBM Corporation.
6 * Portions Copyright 2003 Symas Corporation.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted only as authorized by the OpenLDAP
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>.
18 * This work was initially developed by Apurva Kumar for inclusion
19 * in OpenLDAP Software and subsequently rewritten by Howard Chu.
24 #ifdef SLAPD_OVER_PROXYCACHE
28 #include <ac/string.h>
40 * Control that allows to access the private DB
41 * instead of the public one
43 #define PCACHE_CONTROL_PRIVDB "1.3.6.1.4.1.4203.666.11.9.5.1"
46 * Extended Operation that allows to remove a query from the cache
48 #define PCACHE_EXOP_QUERY_DELETE "1.3.6.1.4.1.4203.666.11.9.6.1"
51 /* query cache structs */
54 typedef struct Query_s
{
55 Filter
* filter
; /* Search Filter */
56 struct berval base
; /* Search Base */
57 int scope
; /* Search scope */
60 struct query_template_s
;
62 typedef struct Qbase_s
{
63 Avlnode
*scopes
[4]; /* threaded AVL trees of cached queries */
68 /* struct representing a cached query */
69 typedef struct cached_query_s
{
74 struct berval q_uuid
; /* query identifier */
76 struct query_template_s
*qtemp
; /* template of the query */
77 time_t expiry_time
; /* time till the query is considered valid */
78 struct cached_query_s
*next
; /* next query in the template */
79 struct cached_query_s
*prev
; /* previous query in the template */
80 struct cached_query_s
*lru_up
; /* previous query in the LRU list */
81 struct cached_query_s
*lru_down
; /* next query in the LRU list */
82 ldap_pvt_thread_rdwr_t rwlock
;
88 * ldap:///<base>??<scope>?<filter>?x-uuid=<uid>,x-template=<template>,x-attrset=<attrset>,x-expiry=<expiry>
90 * <base> ::= CachedQuery.qbase->base
91 * <scope> ::= CachedQuery.scope
92 * <filter> ::= filter2bv(CachedQuery.filter)
93 * <uuid> ::= CachedQuery.q_uuid
94 * <attrset> ::= CachedQuery.qtemp->attr_set_index
95 * <expiry> ::= CachedQuery.expiry_time
97 * quick hack: parse URI, call add_query() and then fix
98 * CachedQuery.expiry_time and CachedQuery.q_uuid
102 * Represents a set of projected attributes.
106 struct query_template_s
*templates
;
107 AttributeName
* attrs
; /* specifies the set */
109 #define PC_CONFIGURED (0x1)
110 #define PC_REFERENCED (0x2)
111 #define PC_GOT_OC (0x4)
112 int count
; /* number of attributes */
115 /* struct representing a query template
116 * e.g. template string = &(cn=)(mail=)
118 typedef struct query_template_s
{
119 struct query_template_s
*qtnext
;
120 struct query_template_s
*qmnext
;
123 CachedQuery
* query
; /* most recent query cached for the template */
124 CachedQuery
* query_last
; /* oldest query cached for the template */
125 ldap_pvt_thread_rdwr_t t_rwlock
; /* Rd/wr lock for accessing queries in the template */
126 struct berval querystr
; /* Filter string corresponding to the QT */
128 int attr_set_index
; /* determines the projected attributes */
129 int no_of_queries
; /* Total number of queries in the template */
130 time_t ttl
; /* TTL for the queries of this template */
131 time_t negttl
; /* TTL for negative results */
132 time_t limitttl
; /* TTL for sizelimit exceeding results */
133 struct attr_set t_attrs
; /* filter attrs + attr_set */
141 } pc_caching_reason_t
;
143 static const char *pc_caching_reason_str
[] = {
152 struct query_manager_s
;
154 /* prototypes for functions for 1) query containment
155 * 2) query addition, 3) cache replacement
157 typedef CachedQuery
*(QCfunc
)(Operation
*op
, struct query_manager_s
*,
158 Query
*, QueryTemplate
*);
159 typedef CachedQuery
*(AddQueryfunc
)(Operation
*op
, struct query_manager_s
*,
160 Query
*, QueryTemplate
*, pc_caching_reason_t
, int wlock
);
161 typedef void (CRfunc
)(struct query_manager_s
*, struct berval
*);
163 /* LDAP query cache */
164 typedef struct query_manager_s
{
165 struct attr_set
* attr_sets
; /* possible sets of projected attributes */
166 QueryTemplate
* templates
; /* cacheable templates */
168 CachedQuery
* lru_top
; /* top and bottom of LRU list */
169 CachedQuery
* lru_bottom
;
171 ldap_pvt_thread_mutex_t lru_mutex
; /* mutex for accessing LRU list */
173 /* Query cache methods */
174 QCfunc
*qcfunc
; /* Query containment*/
175 CRfunc
*crfunc
; /* cache replacement */
176 AddQueryfunc
*addfunc
; /* add query */
179 /* LDAP query cache manager */
180 typedef struct cache_manager_s
{
181 BackendDB db
; /* underlying database */
182 unsigned long num_cached_queries
; /* total number of cached queries */
183 unsigned long max_queries
; /* upper bound on # of cached queries */
184 int save_queries
; /* save cached queries across restarts */
185 int numattrsets
; /* number of attribute sets */
186 int cur_entries
; /* current number of entries cached */
187 int max_entries
; /* max number of entries cached */
188 int num_entries_limit
; /* max # of entries in a cacheable query */
190 char response_cb
; /* install the response callback
191 * at the tail of the callback list */
192 #define PCACHE_RESPONSE_CB_HEAD 0
193 #define PCACHE_RESPONSE_CB_TAIL 1
194 char defer_db_open
; /* defer open for online add */
196 time_t cc_period
; /* interval between successive consistency checks (sec) */
200 ldap_pvt_thread_mutex_t cache_mutex
;
202 query_manager
* qm
; /* query cache managed by the cache manager */
205 static int pcache_debug
;
207 #ifdef PCACHE_CONTROL_PRIVDB
208 static int privDB_cid
;
209 #endif /* PCACHE_CONTROL_PRIVDB */
211 static AttributeDescription
*ad_queryId
, *ad_cachedQueryURL
;
214 AttributeDescription
**adp
;
216 { "( 1.3.6.1.4.1.4203.666.11.9.1.1 "
218 "DESC 'ID of query the entry belongs to, formatted as a UUID' "
219 "EQUALITY octetStringMatch "
220 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.40{64} "
221 "NO-USER-MODIFICATION "
222 "USAGE directoryOperation )",
224 { "( 1.3.6.1.4.1.4203.666.11.9.1.2 "
225 "NAME 'cachedQueryURL' "
226 "DESC 'URI describing a cached query' "
227 "EQUALITY caseExactMatch "
228 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 "
229 "NO-USER-MODIFICATION "
230 "USAGE directoryOperation )",
231 &ad_cachedQueryURL
},
240 AttributeName
** filter_attrs
,
242 int* filter_got_oc
);
249 QueryTemplate
*templ
,
250 pc_caching_reason_t why
,
257 struct berval
*query_uuid
);
260 * Turn a cached query into its URL representation
263 query2url( Operation
*op
, CachedQuery
*q
, struct berval
*urlbv
)
265 struct berval bv_scope
,
267 char attrset_buf
[ 32 ],
270 ber_len_t attrset_len
,
273 ldap_pvt_scope2bv( q
->scope
, &bv_scope
);
274 filter2bv_x( op
, q
->filter
, &bv_filter
);
275 attrset_len
= snprintf( attrset_buf
, sizeof( attrset_buf
),
276 "%lu", (unsigned long)q
->qtemp
->attr_set_index
);
277 expiry_len
= snprintf( expiry_buf
, sizeof( expiry_buf
),
278 "%lu", (unsigned long)q
->expiry_time
);
280 urlbv
->bv_len
= STRLENOF( "ldap:///" )
281 + q
->qbase
->base
.bv_len
286 + STRLENOF( "?x-uuid=" )
288 + STRLENOF( ",x-attrset=" )
290 + STRLENOF( ",x-expiry=" )
292 ptr
= urlbv
->bv_val
= ber_memalloc_x( urlbv
->bv_len
+ 1, op
->o_tmpmemctx
);
293 ptr
= lutil_strcopy( ptr
, "ldap:///" );
294 ptr
= lutil_strcopy( ptr
, q
->qbase
->base
.bv_val
);
295 ptr
= lutil_strcopy( ptr
, "??" );
296 ptr
= lutil_strcopy( ptr
, bv_scope
.bv_val
);
297 ptr
= lutil_strcopy( ptr
, "?" );
298 ptr
= lutil_strcopy( ptr
, bv_filter
.bv_val
);
299 ptr
= lutil_strcopy( ptr
, "?x-uuid=" );
300 ptr
= lutil_strcopy( ptr
, q
->q_uuid
.bv_val
);
301 ptr
= lutil_strcopy( ptr
, ",x-attrset=" );
302 ptr
= lutil_strcopy( ptr
, attrset_buf
);
303 ptr
= lutil_strcopy( ptr
, ",x-expiry=" );
304 ptr
= lutil_strcopy( ptr
, expiry_buf
);
306 ber_memfree_x( bv_filter
.bv_val
, op
->o_tmpmemctx
);
312 * Turn an URL representing a formerly cached query into a cached query,
313 * and try to cache it
324 LDAPURLDesc
*lud
= NULL
;
326 tempstr
= BER_BVNULL
,
336 rc
= ldap_url_parse( url
, &lud
);
337 if ( rc
!= LDAP_URL_SUCCESS
) {
341 /* non-allowed fields */
342 if ( lud
->lud_host
!= NULL
) {
347 if ( lud
->lud_attrs
!= NULL
) {
353 if ( strcmp( lud
->lud_scheme
, "ldap" ) != 0 ) {
358 /* required fields */
359 if ( lud
->lud_dn
== NULL
|| lud
->lud_dn
[ 0 ] == '\0' ) {
364 switch ( lud
->lud_scope
) {
365 case LDAP_SCOPE_BASE
:
366 case LDAP_SCOPE_ONELEVEL
:
367 case LDAP_SCOPE_SUBTREE
:
368 case LDAP_SCOPE_SUBORDINATE
:
376 if ( lud
->lud_filter
== NULL
|| lud
->lud_filter
[ 0 ] == '\0' ) {
381 if ( lud
->lud_exts
== NULL
) {
386 for ( i
= 0; lud
->lud_exts
[ i
] != NULL
; i
++ ) {
387 if ( strncmp( lud
->lud_exts
[ i
], "x-uuid=", STRLENOF( "x-uuid=" ) ) == 0 ) {
388 struct berval tmpUUID
;
389 Syntax
*syn_UUID
= slap_schema
.si_ad_entryUUID
->ad_type
->sat_syntax
;
391 ber_str2bv( &lud
->lud_exts
[ i
][ STRLENOF( "x-uuid=" ) ], 0, 0, &tmpUUID
);
392 rc
= syn_UUID
->ssyn_pretty( syn_UUID
, &tmpUUID
, &uuid
, NULL
);
393 if ( rc
!= LDAP_SUCCESS
) {
398 } else if ( strncmp( lud
->lud_exts
[ i
], "x-attrset=", STRLENOF( "x-attrset=" ) ) == 0 ) {
399 rc
= lutil_atoi( &attrset
, &lud
->lud_exts
[ i
][ STRLENOF( "x-attrset=" ) ] );
405 } else if ( strncmp( lud
->lud_exts
[ i
], "x-expiry=", STRLENOF( "x-expiry=" ) ) == 0 ) {
408 rc
= lutil_atoul( &l
, &lud
->lud_exts
[ i
][ STRLENOF( "x-expiry=" ) ] );
412 expiry_time
= (time_t)l
;
426 if ( !got_attrset
) {
436 /* ignore expired queries */
437 if ( expiry_time
<= slap_get_time()) {
439 SlapReply rs2
= { 0 };
441 memset( &op2
.oq_search
, 0, sizeof( op2
.oq_search
) );
443 (void)remove_query_data( &op2
, &rs2
, &uuid
);
448 ber_str2bv( lud
->lud_dn
, 0, 0, &base
);
449 rc
= dnNormalize( 0, NULL
, NULL
, &base
, &query
.base
, NULL
);
450 if ( rc
!= LDAP_SUCCESS
) {
453 query
.scope
= lud
->lud_scope
;
454 query
.filter
= str2filter( lud
->lud_filter
);
456 tempstr
.bv_val
= ch_malloc( strlen( lud
->lud_filter
) + 1 );
458 if ( filter2template( op
, query
.filter
, &tempstr
, NULL
, NULL
, NULL
) ) {
459 ch_free( tempstr
.bv_val
);
464 /* check for query containment */
465 qt
= qm
->attr_sets
[attrset
].templates
;
466 for ( ; qt
; qt
= qt
->qtnext
) {
467 /* find if template i can potentially answer tempstr */
468 if ( bvmatch( &qt
->querystr
, &tempstr
) ) {
478 cq
= add_query( op
, qm
, &query
, qt
, PC_POSITIVE
, 0 );
480 cq
->expiry_time
= expiry_time
;
483 /* it's now into cq->filter */
493 if ( query
.filter
!= NULL
) filter_free( query
.filter
);
494 if ( !BER_BVISNULL( &tempstr
) ) ch_free( tempstr
.bv_val
);
495 if ( !BER_BVISNULL( &query
.base
) ) ch_free( query
.base
.bv_val
);
496 if ( !BER_BVISNULL( &uuid
) ) ch_free( uuid
.bv_val
);
497 if ( lud
!= NULL
) ldap_free_urldesc( lud
);
502 /* Return 1 for an added entry, else 0 */
507 struct berval
* query_uuid
)
510 Modifications
* modlist
= NULL
;
511 const char* text
= NULL
;
513 char textbuf
[SLAP_TEXT_BUFLEN
];
514 size_t textlen
= sizeof(textbuf
);
516 SlapReply sreply
= {REP_RESULT
};
518 slap_callback cb
= { NULL
, slap_null_cb
, NULL
, NULL
};
523 /* add queryId attribute */
524 attr_merge_one( e
, ad_queryId
, query_uuid
, NULL
);
526 /* append the attribute list from the fetched entry */
527 e
->e_attrs
->a_next
= attr
;
529 op
->o_tag
= LDAP_REQ_ADD
;
530 op
->o_protocol
= LDAP_VERSION3
;
531 op
->o_callback
= &cb
;
532 op
->o_time
= slap_get_time();
533 op
->o_do_not_cache
= 1;
536 op
->o_req_dn
= e
->e_name
;
537 op
->o_req_ndn
= e
->e_nname
;
538 rc
= op
->o_bd
->be_add( op
, &sreply
);
540 if ( rc
!= LDAP_SUCCESS
) {
541 if ( rc
== LDAP_ALREADY_EXISTS
) {
542 slap_entry2mods( e
, &modlist
, &text
, textbuf
, textlen
);
543 modlist
->sml_op
= LDAP_MOD_ADD
;
544 op
->o_tag
= LDAP_REQ_MODIFY
;
545 op
->orm_modlist
= modlist
;
546 op
->o_bd
->be_modify( op
, &sreply
);
547 slap_mods_free( modlist
, 1 );
548 } else if ( rc
== LDAP_REFERRAL
||
549 rc
== LDAP_NO_SUCH_OBJECT
) {
550 syncrepl_add_glue( op
, e
);
559 if ( op
->ora_e
== e
)
560 be_entry_release_w( op
, e
);
567 /* Length-ordered sort on normalized DNs */
568 static int pcache_dn_cmp( const void *v1
, const void *v2
)
570 const Qbase
*q1
= v1
, *q2
= v2
;
572 int rc
= q1
->base
.bv_len
- q2
->base
.bv_len
;
574 rc
= strncmp( q1
->base
.bv_val
, q2
->base
.bv_val
, q1
->base
.bv_len
);
578 static int lex_bvcmp( struct berval
*bv1
, struct berval
*bv2
)
581 dif
= bv1
->bv_len
- bv2
->bv_len
;
583 if ( dif
> 0 ) len
-= dif
;
584 len
= memcmp( bv1
->bv_val
, bv2
->bv_val
, len
);
590 /* compare the first value in each filter */
591 static int pcache_filter_cmp( const void *v1
, const void *v2
)
593 const CachedQuery
*q1
= v1
, *q2
=v2
;
594 int rc
, weight1
, weight2
;
596 switch( q1
->first
->f_choice
) {
597 case LDAP_FILTER_PRESENT
:
600 case LDAP_FILTER_EQUALITY
:
608 switch( q2
->first
->f_choice
) {
609 case LDAP_FILTER_PRESENT
:
612 case LDAP_FILTER_EQUALITY
:
620 rc
= weight1
- weight2
;
625 rc
= lex_bvcmp( &q1
->first
->f_av_value
, &q2
->first
->f_av_value
);
628 if ( q1
->first
->f_choice
== LDAP_FILTER_SUBSTRINGS
) {
630 if ( !BER_BVISNULL( &q1
->first
->f_sub_initial
)) {
631 if ( !BER_BVISNULL( &q2
->first
->f_sub_initial
)) {
632 rc
= lex_bvcmp( &q1
->first
->f_sub_initial
,
633 &q2
->first
->f_sub_initial
);
637 } else if ( !BER_BVISNULL( &q2
->first
->f_sub_initial
)) {
641 if ( q1
->first
->f_sub_any
) {
642 if ( q2
->first
->f_sub_any
) {
643 rc
= lex_bvcmp( q1
->first
->f_sub_any
,
644 q2
->first
->f_sub_any
);
648 } else if ( q2
->first
->f_sub_any
) {
652 if ( !BER_BVISNULL( &q1
->first
->f_sub_final
)) {
653 if ( !BER_BVISNULL( &q2
->first
->f_sub_final
)) {
654 rc
= lex_bvcmp( &q1
->first
->f_sub_final
,
655 &q2
->first
->f_sub_final
);
659 } else if ( !BER_BVISNULL( &q2
->first
->f_sub_final
)) {
663 rc
= lex_bvcmp( &q1
->first
->f_mr_value
,
664 &q2
->first
->f_mr_value
);
673 /* add query on top of LRU list */
675 add_query_on_top (query_manager
* qm
, CachedQuery
* qc
)
677 CachedQuery
* top
= qm
->lru_top
;
688 Debug( pcache_debug
, "Base of added query = %s\n",
689 qc
->qbase
->base
.bv_val
, 0, 0 );
692 /* remove_query from LRU list */
695 remove_query (query_manager
* qm
, CachedQuery
* qc
)
718 qc
->lru_up
= qc
->lru_down
= NULL
;
721 /* find and remove string2 from string1
722 * from start if position = 1,
723 * from end if position = 3,
724 * from anywhere if position = 2
725 * string1 is overwritten if position = 2.
729 find_and_remove(struct berval
* ber1
, struct berval
* ber2
, int position
)
740 if ( ber1
->bv_len
>= ber2
->bv_len
&& !memcmp( ber1
->bv_val
,
741 ber2
->bv_val
, ber2
->bv_len
)) {
743 ber1
->bv_val
+= ber2
->bv_len
;
744 ber1
->bv_len
-= ber2
->bv_len
;
749 ber1
->bv_val
[ber1
->bv_len
] = '\0';
750 temp
= strstr( ber1
->bv_val
, ber2
->bv_val
);
752 strcpy( temp
, temp
+ber2
->bv_len
);
753 ber1
->bv_len
-= ber2
->bv_len
;
759 if ( ber1
->bv_len
>= ber2
->bv_len
&&
760 !memcmp( ber1
->bv_val
+ber1
->bv_len
-ber2
->bv_len
, ber2
->bv_val
,
763 ber1
->bv_len
-= ber2
->bv_len
;
771 static struct berval
*
772 merge_init_final(Operation
*op
, struct berval
* init
, struct berval
* any
,
773 struct berval
* final
)
775 struct berval
* merged
, *temp
;
776 int i
, any_count
, count
;
778 for (any_count
=0; any
&& any
[any_count
].bv_val
; any_count
++)
788 merged
= (struct berval
*)op
->o_tmpalloc( (count
+1)*sizeof(struct berval
),
793 ber_dupbv_x( temp
, init
, op
->o_tmpmemctx
);
797 for (i
=0; i
<any_count
; i
++) {
798 ber_dupbv_x( temp
, any
, op
->o_tmpmemctx
);
803 ber_dupbv_x( temp
, final
, op
->o_tmpmemctx
);
810 /* Each element in stored must be found in incoming. Incoming is overwritten.
813 strings_containment(struct berval
* stored
, struct berval
* incoming
)
815 struct berval
* element
;
819 for ( element
=stored
; element
->bv_val
!= NULL
; element
++ ) {
820 for (j
= k
; incoming
[j
].bv_val
!= NULL
; j
++) {
821 if (find_and_remove(&(incoming
[j
]), element
, 2)) {
838 substr_containment_substr(Operation
*op
, Filter
* stored
, Filter
* incoming
)
842 struct berval init_incoming
;
843 struct berval final_incoming
;
844 struct berval
*remaining_incoming
= NULL
;
846 if ((!(incoming
->f_sub_initial
.bv_val
) && (stored
->f_sub_initial
.bv_val
))
847 || (!(incoming
->f_sub_final
.bv_val
) && (stored
->f_sub_final
.bv_val
)))
850 init_incoming
= incoming
->f_sub_initial
;
851 final_incoming
= incoming
->f_sub_final
;
853 if (find_and_remove(&init_incoming
,
854 &(stored
->f_sub_initial
), 1) && find_and_remove(&final_incoming
,
855 &(stored
->f_sub_final
), 3))
857 if (stored
->f_sub_any
== NULL
) {
861 remaining_incoming
= merge_init_final(op
, &init_incoming
,
862 incoming
->f_sub_any
, &final_incoming
);
863 rc
= strings_containment(stored
->f_sub_any
, remaining_incoming
);
864 ber_bvarray_free_x( remaining_incoming
, op
->o_tmpmemctx
);
871 substr_containment_equality(Operation
*op
, Filter
* stored
, Filter
* incoming
)
873 struct berval incoming_val
[2];
876 incoming_val
[1] = incoming
->f_av_value
;
878 if (find_and_remove(incoming_val
+1,
879 &(stored
->f_sub_initial
), 1) && find_and_remove(incoming_val
+1,
880 &(stored
->f_sub_final
), 3)) {
881 if (stored
->f_sub_any
== NULL
){
885 ber_dupbv_x( incoming_val
, incoming_val
+1, op
->o_tmpmemctx
);
886 BER_BVZERO( incoming_val
+1 );
887 rc
= strings_containment(stored
->f_sub_any
, incoming_val
);
888 op
->o_tmpfree( incoming_val
[0].bv_val
, op
->o_tmpmemctx
);
895 filter_first( Filter
*f
)
897 while ( f
->f_choice
== LDAP_FILTER_OR
|| f
->f_choice
== LDAP_FILTER_AND
)
904 find_filter( Operation
*op
, Avlnode
*root
, Filter
*inputf
, Filter
*first
)
908 MatchingRule
* mrule
= NULL
;
909 int res
=0, eqpass
= 0;
917 /* substring matches sort to the end, and we just have to
918 * walk the entire list.
920 if ( first
->f_choice
== LDAP_FILTER_SUBSTRINGS
) {
921 ptr
= tavl_end( root
, 1 );
924 ptr
= tavl_find3( root
, &cq
, pcache_filter_cmp
, &ret
);
925 dir
= (first
->f_choice
== LDAP_FILTER_GE
) ? TAVL_DIR_LEFT
:
934 /* an incoming substr query can only be satisfied by a cached
937 if ( first
->f_choice
== LDAP_FILTER_SUBSTRINGS
&&
938 qc
->first
->f_choice
!= LDAP_FILTER_SUBSTRINGS
)
941 /* an incoming eq query can be satisfied by a cached eq or substr
944 if ( first
->f_choice
== LDAP_FILTER_EQUALITY
) {
946 if ( qc
->first
->f_choice
!= LDAP_FILTER_EQUALITY
) {
947 nextpass
: eqpass
= 1;
948 ptr
= tavl_end( root
, 1 );
953 if ( qc
->first
->f_choice
!= LDAP_FILTER_SUBSTRINGS
)
959 switch (fs
->f_choice
) {
960 case LDAP_FILTER_EQUALITY
:
961 if (fi
->f_choice
== LDAP_FILTER_EQUALITY
)
962 mrule
= fs
->f_ava
->aa_desc
->ad_type
->sat_equality
;
968 mrule
= fs
->f_ava
->aa_desc
->ad_type
->sat_ordering
;
975 rc
= value_match(&ret
, fs
->f_ava
->aa_desc
, mrule
,
976 SLAP_MR_VALUE_OF_ASSERTION_SYNTAX
,
977 &(fi
->f_ava
->aa_value
),
978 &(fs
->f_ava
->aa_value
), &text
);
979 if (rc
!= LDAP_SUCCESS
) {
982 if ( fi
==first
&& fi
->f_choice
==LDAP_FILTER_EQUALITY
&& ret
)
985 switch (fs
->f_choice
) {
987 case LDAP_FILTER_AND
:
992 case LDAP_FILTER_SUBSTRINGS
:
993 /* check if the equality query can be
994 * answered with cached substring query */
995 if ((fi
->f_choice
== LDAP_FILTER_EQUALITY
)
996 && substr_containment_equality( op
,
999 /* check if the substring query can be
1000 * answered with cached substring query */
1001 if ((fi
->f_choice
==LDAP_FILTER_SUBSTRINGS
1002 ) && substr_containment_substr( op
,
1008 case LDAP_FILTER_PRESENT
:
1013 case LDAP_FILTER_EQUALITY
:
1019 case LDAP_FILTER_GE
:
1020 if (mrule
&& ret
>= 0)
1025 case LDAP_FILTER_LE
:
1026 if (mrule
&& ret
<= 0)
1031 case LDAP_FILTER_NOT
:
1037 } while((res
) && (fi
!= NULL
) && (fs
!= NULL
));
1041 ptr
= tavl_next( ptr
, dir
);
1046 /* check whether query is contained in any of
1047 * the cached queries in template
1049 static CachedQuery
*
1050 query_containment(Operation
*op
, query_manager
*qm
,
1052 QueryTemplate
*templa
)
1055 int depth
= 0, tscope
;
1056 Qbase qbase
, *qbptr
= NULL
;
1059 if (query
->filter
!= NULL
) {
1062 Debug( pcache_debug
, "Lock QC index = %p\n",
1063 (void *) templa
, 0, 0 );
1064 qbase
.base
= query
->base
;
1066 first
= filter_first( query
->filter
);
1068 ldap_pvt_thread_rdwr_rlock(&templa
->t_rwlock
);
1071 qbptr
= avl_find( templa
->qbase
, &qbase
, pcache_dn_cmp
);
1073 tscope
= query
->scope
;
1074 /* Find a matching scope:
1075 * match at depth 0 OK
1078 * subord at depth > 0 OK
1079 * subtree at any depth OK
1081 * subtree or subord at any depth OK
1083 * subtree or subord at any depth OK
1085 * subord at depth > 0 OK
1086 * subtree at any depth OK
1088 for ( tscope
= 0 ; tscope
<= LDAP_SCOPE_CHILDREN
; tscope
++ ) {
1089 switch ( query
->scope
) {
1090 case LDAP_SCOPE_BASE
:
1091 if ( tscope
== LDAP_SCOPE_BASE
&& depth
) continue;
1092 if ( tscope
== LDAP_SCOPE_ONE
&& depth
!= 1) continue;
1093 if ( tscope
== LDAP_SCOPE_CHILDREN
&& !depth
) continue;
1095 case LDAP_SCOPE_ONE
:
1096 if ( tscope
== LDAP_SCOPE_BASE
)
1097 tscope
= LDAP_SCOPE_ONE
;
1098 if ( tscope
== LDAP_SCOPE_ONE
&& depth
) continue;
1099 if ( !depth
) break;
1100 if ( tscope
< LDAP_SCOPE_SUBTREE
)
1101 tscope
= LDAP_SCOPE_SUBTREE
;
1103 case LDAP_SCOPE_SUBTREE
:
1104 if ( tscope
< LDAP_SCOPE_SUBTREE
)
1105 tscope
= LDAP_SCOPE_SUBTREE
;
1106 if ( tscope
== LDAP_SCOPE_CHILDREN
&& !depth
) continue;
1108 case LDAP_SCOPE_CHILDREN
:
1109 if ( tscope
< LDAP_SCOPE_SUBTREE
)
1110 tscope
= LDAP_SCOPE_SUBTREE
;
1113 if ( !qbptr
->scopes
[tscope
] ) continue;
1116 qc
= find_filter( op
, qbptr
->scopes
[tscope
],
1117 query
->filter
, first
);
1119 if ( qc
->q_sizelimit
) {
1120 ldap_pvt_thread_rdwr_runlock(&templa
->t_rwlock
);
1123 ldap_pvt_thread_mutex_lock(&qm
->lru_mutex
);
1124 if (qm
->lru_top
!= qc
) {
1125 remove_query(qm
, qc
);
1126 add_query_on_top(qm
, qc
);
1128 ldap_pvt_thread_mutex_unlock(&qm
->lru_mutex
);
1133 if ( be_issuffix( op
->o_bd
, &qbase
.base
))
1136 dnParent( &qbase
.base
, &pdn
);
1141 Debug( pcache_debug
,
1142 "Not answerable: Unlock QC index=%p\n",
1143 (void *) templa
, 0, 0 );
1144 ldap_pvt_thread_rdwr_runlock(&templa
->t_rwlock
);
1150 free_query (CachedQuery
* qc
)
1152 free(qc
->q_uuid
.bv_val
);
1153 filter_free(qc
->filter
);
1158 /* Add query to query cache, the returned Query is locked for writing */
1159 static CachedQuery
*
1164 QueryTemplate
*templ
,
1165 pc_caching_reason_t why
,
1168 CachedQuery
* new_cached_query
= (CachedQuery
*) ch_malloc(sizeof(CachedQuery
));
1174 new_cached_query
->qtemp
= templ
;
1175 BER_BVZERO( &new_cached_query
->q_uuid
);
1176 new_cached_query
->q_sizelimit
= 0;
1184 ttl
= templ
->negttl
;
1188 ttl
= templ
->limitttl
;
1195 new_cached_query
->expiry_time
= slap_get_time() + ttl
;
1196 new_cached_query
->lru_up
= NULL
;
1197 new_cached_query
->lru_down
= NULL
;
1198 Debug( pcache_debug
, "Added query expires at %ld (%s)\n",
1199 (long) new_cached_query
->expiry_time
,
1200 pc_caching_reason_str
[ why
], 0 );
1202 new_cached_query
->scope
= query
->scope
;
1203 new_cached_query
->filter
= query
->filter
;
1204 new_cached_query
->first
= first
= filter_first( query
->filter
);
1206 ldap_pvt_thread_rdwr_init(&new_cached_query
->rwlock
);
1208 ldap_pvt_thread_rdwr_wlock(&new_cached_query
->rwlock
);
1210 qb
.base
= query
->base
;
1212 /* Adding a query */
1213 Debug( pcache_debug
, "Lock AQ index = %p\n",
1214 (void *) templ
, 0, 0 );
1215 ldap_pvt_thread_rdwr_wlock(&templ
->t_rwlock
);
1216 qbase
= avl_find( templ
->qbase
, &qb
, pcache_dn_cmp
);
1218 qbase
= ch_calloc( 1, sizeof(Qbase
) + qb
.base
.bv_len
+ 1 );
1219 qbase
->base
.bv_len
= qb
.base
.bv_len
;
1220 qbase
->base
.bv_val
= (char *)(qbase
+1);
1221 memcpy( qbase
->base
.bv_val
, qb
.base
.bv_val
, qb
.base
.bv_len
);
1222 qbase
->base
.bv_val
[qbase
->base
.bv_len
] = '\0';
1223 avl_insert( &templ
->qbase
, qbase
, pcache_dn_cmp
, avl_dup_error
);
1225 new_cached_query
->next
= templ
->query
;
1226 new_cached_query
->prev
= NULL
;
1227 new_cached_query
->qbase
= qbase
;
1228 rc
= tavl_insert( &qbase
->scopes
[query
->scope
], new_cached_query
,
1229 pcache_filter_cmp
, avl_dup_error
);
1232 if (templ
->query
== NULL
)
1233 templ
->query_last
= new_cached_query
;
1235 templ
->query
->prev
= new_cached_query
;
1236 templ
->query
= new_cached_query
;
1237 templ
->no_of_queries
++;
1239 ch_free( new_cached_query
);
1240 new_cached_query
= find_filter( op
, qbase
->scopes
[query
->scope
],
1241 query
->filter
, first
);
1242 filter_free( query
->filter
);
1244 Debug( pcache_debug
, "TEMPLATE %p QUERIES++ %d\n",
1245 (void *) templ
, templ
->no_of_queries
, 0 );
1247 Debug( pcache_debug
, "Unlock AQ index = %p \n",
1248 (void *) templ
, 0, 0 );
1249 ldap_pvt_thread_rdwr_wunlock(&templ
->t_rwlock
);
1251 /* Adding on top of LRU list */
1253 ldap_pvt_thread_mutex_lock(&qm
->lru_mutex
);
1254 add_query_on_top(qm
, new_cached_query
);
1255 ldap_pvt_thread_mutex_unlock(&qm
->lru_mutex
);
1257 return rc
== 0 ? new_cached_query
: NULL
;
1261 remove_from_template (CachedQuery
* qc
, QueryTemplate
* template)
1263 if (!qc
->prev
&& !qc
->next
) {
1264 template->query_last
= template->query
= NULL
;
1265 } else if (qc
->prev
== NULL
) {
1266 qc
->next
->prev
= NULL
;
1267 template->query
= qc
->next
;
1268 } else if (qc
->next
== NULL
) {
1269 qc
->prev
->next
= NULL
;
1270 template->query_last
= qc
->prev
;
1272 qc
->next
->prev
= qc
->prev
;
1273 qc
->prev
->next
= qc
->next
;
1275 tavl_delete( &qc
->qbase
->scopes
[qc
->scope
], qc
, pcache_filter_cmp
);
1276 qc
->qbase
->queries
--;
1277 if ( qc
->qbase
->queries
== 0 ) {
1278 avl_delete( &template->qbase
, qc
->qbase
, pcache_dn_cmp
);
1279 ch_free( qc
->qbase
);
1283 template->no_of_queries
--;
1286 /* remove bottom query of LRU list from the query cache */
1288 * NOTE: slight change in functionality.
1290 * - if result->bv_val is NULL, the query at the bottom of the LRU
1292 * - otherwise, the query whose UUID is *result is removed
1293 * - if not found, result->bv_val is zeroed
1296 cache_replacement(query_manager
* qm
, struct berval
*result
)
1298 CachedQuery
* bottom
;
1299 QueryTemplate
*temp
;
1301 ldap_pvt_thread_mutex_lock(&qm
->lru_mutex
);
1302 if ( BER_BVISNULL( result
) ) {
1303 bottom
= qm
->lru_bottom
;
1306 Debug ( pcache_debug
,
1307 "Cache replacement invoked without "
1308 "any query in LRU list\n", 0, 0, 0 );
1309 ldap_pvt_thread_mutex_unlock(&qm
->lru_mutex
);
1314 for ( bottom
= qm
->lru_bottom
;
1316 bottom
= bottom
->lru_up
)
1318 if ( bvmatch( result
, &bottom
->q_uuid
) ) {
1324 Debug ( pcache_debug
,
1325 "Could not find query with uuid=\"%s\""
1326 "in LRU list\n", result
->bv_val
, 0, 0 );
1327 ldap_pvt_thread_mutex_unlock(&qm
->lru_mutex
);
1328 BER_BVZERO( result
);
1333 temp
= bottom
->qtemp
;
1334 remove_query(qm
, bottom
);
1335 ldap_pvt_thread_mutex_unlock(&qm
->lru_mutex
);
1337 *result
= bottom
->q_uuid
;
1338 BER_BVZERO( &bottom
->q_uuid
);
1340 Debug( pcache_debug
, "Lock CR index = %p\n", (void *) temp
, 0, 0 );
1341 ldap_pvt_thread_rdwr_wlock(&temp
->t_rwlock
);
1342 remove_from_template(bottom
, temp
);
1343 Debug( pcache_debug
, "TEMPLATE %p QUERIES-- %d\n",
1344 (void *) temp
, temp
->no_of_queries
, 0 );
1345 Debug( pcache_debug
, "Unlock CR index = %p\n", (void *) temp
, 0, 0 );
1346 ldap_pvt_thread_rdwr_wunlock(&temp
->t_rwlock
);
1351 struct query_info
*next
;
1363 struct query_info
*qi
;
1366 if ( rs
->sr_type
!= REP_SEARCH
) return 0;
1368 attr
= attr_find( rs
->sr_entry
->e_attrs
, ad_queryId
);
1369 if ( attr
== NULL
) return 0;
1371 count
= attr
->a_numvals
;
1372 assert( count
> 0 );
1373 qi
= op
->o_tmpalloc( sizeof( struct query_info
), op
->o_tmpmemctx
);
1374 qi
->next
= op
->o_callback
->sc_private
;
1375 op
->o_callback
->sc_private
= qi
;
1376 ber_dupbv_x( &qi
->xdn
, &rs
->sr_entry
->e_nname
, op
->o_tmpmemctx
);
1377 qi
->del
= ( count
== 1 );
1386 struct berval
*query_uuid
)
1388 struct query_info
*qi
, *qnext
;
1389 char filter_str
[ LDAP_LUTIL_UUIDSTR_BUFSIZE
+ STRLENOF( "(queryId=)" ) ];
1390 AttributeAssertion ava
= ATTRIBUTEASSERTION_INIT
;
1391 Filter filter
= {LDAP_FILTER_EQUALITY
};
1392 SlapReply sreply
= {REP_RESULT
};
1393 slap_callback cb
= { NULL
, remove_func
, NULL
, NULL
};
1396 sreply
.sr_entry
= NULL
;
1397 sreply
.sr_nentries
= 0;
1398 op
->ors_filterstr
.bv_len
= snprintf(filter_str
, sizeof(filter_str
),
1399 "(%s=%s)", ad_queryId
->ad_cname
.bv_val
, query_uuid
->bv_val
);
1400 filter
.f_ava
= &ava
;
1401 filter
.f_av_desc
= ad_queryId
;
1402 filter
.f_av_value
= *query_uuid
;
1404 op
->o_tag
= LDAP_REQ_SEARCH
;
1405 op
->o_protocol
= LDAP_VERSION3
;
1406 op
->o_callback
= &cb
;
1407 op
->o_time
= slap_get_time();
1408 op
->o_do_not_cache
= 1;
1410 op
->o_req_dn
= op
->o_bd
->be_suffix
[0];
1411 op
->o_req_ndn
= op
->o_bd
->be_nsuffix
[0];
1412 op
->ors_scope
= LDAP_SCOPE_SUBTREE
;
1413 op
->ors_deref
= LDAP_DEREF_NEVER
;
1414 op
->ors_slimit
= SLAP_NO_LIMIT
;
1415 op
->ors_tlimit
= SLAP_NO_LIMIT
;
1416 op
->ors_filter
= &filter
;
1417 op
->ors_filterstr
.bv_val
= filter_str
;
1418 op
->ors_filterstr
.bv_len
= strlen(filter_str
);
1419 op
->ors_attrs
= NULL
;
1420 op
->ors_attrsonly
= 0;
1422 op
->o_bd
->be_search( op
, &sreply
);
1424 for ( qi
=cb
.sc_private
; qi
; qi
=qnext
) {
1427 op
->o_req_dn
= qi
->xdn
;
1428 op
->o_req_ndn
= qi
->xdn
;
1431 Debug( pcache_debug
, "DELETING ENTRY TEMPLATE=%s\n",
1432 query_uuid
->bv_val
, 0, 0 );
1434 op
->o_tag
= LDAP_REQ_DELETE
;
1436 if (op
->o_bd
->be_delete(op
, &sreply
) == LDAP_SUCCESS
) {
1442 struct berval vals
[2];
1444 vals
[0] = *query_uuid
;
1445 vals
[1].bv_val
= NULL
;
1447 mod
.sml_op
= LDAP_MOD_DELETE
;
1449 mod
.sml_desc
= ad_queryId
;
1450 mod
.sml_type
= ad_queryId
->ad_cname
;
1451 mod
.sml_values
= vals
;
1452 mod
.sml_nvalues
= NULL
;
1453 mod
.sml_numvals
= 1;
1454 mod
.sml_next
= NULL
;
1455 Debug( pcache_debug
,
1456 "REMOVING TEMP ATTR : TEMPLATE=%s\n",
1457 query_uuid
->bv_val
, 0, 0 );
1459 op
->orm_modlist
= &mod
;
1461 op
->o_bd
->be_modify( op
, &sreply
);
1463 op
->o_tmpfree( qi
->xdn
.bv_val
, op
->o_tmpmemctx
);
1464 op
->o_tmpfree( qi
, op
->o_tmpmemctx
);
1471 AttributeName
* attrs
,
1480 struct berval
*fstr
,
1481 AttributeName
** filter_attrs
,
1483 int* filter_got_oc
)
1485 AttributeDescription
*ad
;
1488 switch ( f
->f_choice
) {
1489 case LDAP_FILTER_EQUALITY
:
1491 len
= STRLENOF( "(=)" ) + ad
->ad_cname
.bv_len
;
1492 ret
= snprintf( fstr
->bv_val
+fstr
->bv_len
, len
+ 1, "(%s=)", ad
->ad_cname
.bv_val
);
1493 assert( ret
== len
);
1494 fstr
->bv_len
+= len
;
1497 case LDAP_FILTER_GE
:
1499 len
= STRLENOF( "(>=)" ) + ad
->ad_cname
.bv_len
;
1500 ret
= snprintf( fstr
->bv_val
+fstr
->bv_len
, len
+ 1, "(%s>=)", ad
->ad_cname
.bv_val
);
1501 assert( ret
== len
);
1502 fstr
->bv_len
+= len
;
1505 case LDAP_FILTER_LE
:
1507 len
= STRLENOF( "(<=)" ) + ad
->ad_cname
.bv_len
;
1508 ret
= snprintf( fstr
->bv_val
+fstr
->bv_len
, len
+ 1, "(%s<=)", ad
->ad_cname
.bv_val
);
1509 assert( ret
== len
);
1510 fstr
->bv_len
+= len
;
1513 case LDAP_FILTER_APPROX
:
1515 len
= STRLENOF( "(~=)" ) + ad
->ad_cname
.bv_len
;
1516 ret
= snprintf( fstr
->bv_val
+fstr
->bv_len
, len
+ 1, "(%s~=)", ad
->ad_cname
.bv_val
);
1517 assert( ret
== len
);
1518 fstr
->bv_len
+= len
;
1521 case LDAP_FILTER_SUBSTRINGS
:
1523 len
= STRLENOF( "(=)" ) + ad
->ad_cname
.bv_len
;
1524 ret
= snprintf( fstr
->bv_val
+fstr
->bv_len
, len
+ 1, "(%s=)", ad
->ad_cname
.bv_val
);
1525 assert( ret
== len
);
1526 fstr
->bv_len
+= len
;
1529 case LDAP_FILTER_PRESENT
:
1531 len
= STRLENOF( "(=*)" ) + ad
->ad_cname
.bv_len
;
1532 ret
= snprintf( fstr
->bv_val
+fstr
->bv_len
, len
+ 1, "(%s=*)", ad
->ad_cname
.bv_val
);
1533 assert( ret
== len
);
1534 fstr
->bv_len
+= len
;
1537 case LDAP_FILTER_AND
:
1538 case LDAP_FILTER_OR
:
1539 case LDAP_FILTER_NOT
: {
1541 fstr
->bv_val
[fstr
->bv_len
++] = '(';
1542 switch ( f
->f_choice
) {
1543 case LDAP_FILTER_AND
:
1544 fstr
->bv_val
[fstr
->bv_len
] = '&';
1546 case LDAP_FILTER_OR
:
1547 fstr
->bv_val
[fstr
->bv_len
] = '|';
1549 case LDAP_FILTER_NOT
:
1550 fstr
->bv_val
[fstr
->bv_len
] = '!';
1555 for ( f
= f
->f_list
; f
!= NULL
; f
= f
->f_next
) {
1556 rc
= filter2template( op
, f
, fstr
, filter_attrs
, filter_cnt
,
1560 fstr
->bv_val
[fstr
->bv_len
++] = ')';
1561 fstr
->bv_val
[fstr
->bv_len
] = '\0';
1567 /* a filter should at least have room for "()",
1568 * an "=" and for a 1-char attr */
1569 strcpy( fstr
->bv_val
, "(?=)" );
1570 fstr
->bv_len
+= STRLENOF("(?=)");
1574 if ( filter_attrs
!= NULL
) {
1575 *filter_attrs
= (AttributeName
*)op
->o_tmprealloc(*filter_attrs
,
1576 (*filter_cnt
+ 2)*sizeof(AttributeName
), op
->o_tmpmemctx
);
1578 (*filter_attrs
)[*filter_cnt
].an_desc
= ad
;
1579 (*filter_attrs
)[*filter_cnt
].an_name
= ad
->ad_cname
;
1580 (*filter_attrs
)[*filter_cnt
].an_oc
= NULL
;
1581 (*filter_attrs
)[*filter_cnt
].an_oc_exclude
= 0;
1582 BER_BVZERO( &(*filter_attrs
)[*filter_cnt
+1].an_name
);
1584 if ( ad
== slap_schema
.si_ad_objectClass
)
1591 struct search_info
{
1594 QueryTemplate
*qtemp
;
1595 AttributeName
* save_attrs
; /* original attributes, saved for response */
1600 int slimit_exceeded
;
1601 pc_caching_reason_t caching_reason
;
1606 remove_query_and_data(
1610 struct berval
*uuid
)
1612 query_manager
* qm
= cm
->qm
;
1614 qm
->crfunc( qm
, uuid
);
1615 if ( !BER_BVISNULL( uuid
) ) {
1618 Debug( pcache_debug
,
1619 "Removing query UUID %s\n",
1620 uuid
->bv_val
, 0, 0 );
1621 return_val
= remove_query_data( op
, rs
, uuid
);
1622 Debug( pcache_debug
,
1623 "QUERY REMOVED, SIZE=%d\n",
1625 ldap_pvt_thread_mutex_lock( &cm
->cache_mutex
);
1626 cm
->cur_entries
-= return_val
;
1627 cm
->num_cached_queries
--;
1628 Debug( pcache_debug
,
1629 "STORED QUERIES = %lu\n",
1630 cm
->num_cached_queries
, 0, 0 );
1631 ldap_pvt_thread_mutex_unlock( &cm
->cache_mutex
);
1632 Debug( pcache_debug
,
1633 "QUERY REMOVED, CACHE ="
1635 cm
->cur_entries
, 0, 0 );
1640 * Callback used to fetch queryId values based on entryUUID;
1641 * used by pcache_remove_entries_from_cache()
1644 fetch_queryId_cb( Operation
*op
, SlapReply
*rs
)
1648 /* only care about searchEntry responses */
1649 if ( rs
->sr_type
!= REP_SEARCH
) {
1653 /* allow only one response per entryUUID */
1654 if ( op
->o_callback
->sc_private
!= NULL
) {
1660 /* copy all queryId values into callback's private data */
1661 a
= attr_find( rs
->sr_entry
->e_attrs
, ad_queryId
);
1663 BerVarray vals
= NULL
;
1665 ber_bvarray_dup_x( &vals
, a
->a_nvals
, op
->o_tmpmemctx
);
1666 op
->o_callback
->sc_private
= (void *)vals
;
1670 /* clear entry if required */
1671 if ( rs
->sr_flags
& REP_ENTRY_MUSTBEFREED
) {
1672 entry_free( rs
->sr_entry
);
1673 rs
->sr_entry
= NULL
;
1674 rs
->sr_flags
^= REP_ENTRY_MUSTBEFREED
;
1681 * Call that allows to remove a set of entries from the cache,
1682 * by forcing the removal of all the related queries.
1685 pcache_remove_entries_from_cache(
1688 BerVarray entryUUIDs
)
1690 Connection conn
= { 0 };
1691 OperationBuffer opbuf
;
1693 slap_callback sc
= { 0 };
1694 SlapReply rs
= { REP_RESULT
};
1696 char filtbuf
[ LDAP_LUTIL_UUIDSTR_BUFSIZE
+ STRLENOF( "(entryUUID=)" ) ];
1697 AttributeAssertion ava
= ATTRIBUTEASSERTION_INIT
;
1698 AttributeName attrs
[ 2 ] = { 0 };
1702 void *thrctx
= ldap_pvt_thread_pool_context();
1704 connection_fake_init( &conn
, &opbuf
, thrctx
);
1712 memset( &op
->oq_search
, 0, sizeof( op
->oq_search
) );
1713 op
->ors_scope
= LDAP_SCOPE_SUBTREE
;
1714 op
->ors_deref
= LDAP_DEREF_NEVER
;
1715 f
.f_choice
= LDAP_FILTER_EQUALITY
;
1717 ava
.aa_desc
= slap_schema
.si_ad_entryUUID
;
1718 op
->ors_filter
= &f
;
1720 op
->ors_tlimit
= SLAP_NO_LIMIT
;
1721 attrs
[ 0 ].an_desc
= ad_queryId
;
1722 attrs
[ 0 ].an_name
= ad_queryId
->ad_cname
;
1723 op
->ors_attrs
= attrs
;
1724 op
->ors_attrsonly
= 0;
1726 op
->o_req_dn
= cm
->db
.be_suffix
[ 0 ];
1727 op
->o_req_ndn
= cm
->db
.be_nsuffix
[ 0 ];
1729 op
->o_tag
= LDAP_REQ_SEARCH
;
1730 op
->o_protocol
= LDAP_VERSION3
;
1731 op
->o_managedsait
= SLAP_CONTROL_CRITICAL
;
1733 op
->o_dn
= op
->o_bd
->be_rootdn
;
1734 op
->o_ndn
= op
->o_bd
->be_rootndn
;
1735 sc
.sc_response
= fetch_queryId_cb
;
1736 op
->o_callback
= &sc
;
1738 for ( s
= 0; !BER_BVISNULL( &entryUUIDs
[ s
] ); s
++ ) {
1739 BerVarray vals
= NULL
;
1741 op
->ors_filterstr
.bv_len
= snprintf( filtbuf
, sizeof( filtbuf
),
1742 "(entryUUID=%s)", entryUUIDs
[ s
].bv_val
);
1743 op
->ors_filterstr
.bv_val
= filtbuf
;
1744 ava
.aa_value
= entryUUIDs
[ s
];
1746 rc
= op
->o_bd
->be_search( op
, &rs
);
1747 if ( rc
!= LDAP_SUCCESS
) {
1751 vals
= (BerVarray
)op
->o_callback
->sc_private
;
1752 if ( vals
!= NULL
) {
1755 for ( i
= 0; !BER_BVISNULL( &vals
[ i
] ); i
++ ) {
1756 struct berval val
= vals
[ i
];
1758 remove_query_and_data( op
, &rs
, cm
, &val
);
1760 if ( !BER_BVISNULL( &val
) && val
.bv_val
!= vals
[ i
].bv_val
) {
1761 ch_free( val
.bv_val
);
1765 ber_bvarray_free_x( vals
, op
->o_tmpmemctx
);
1766 op
->o_callback
->sc_private
= NULL
;
1774 * Call that allows to remove a query from the cache.
1777 pcache_remove_query_from_cache(
1780 struct berval
*queryid
)
1782 Operation op2
= *op
;
1783 SlapReply rs2
= { 0 };
1787 /* remove the selected query */
1788 remove_query_and_data( &op2
, &rs2
, cm
, queryid
);
1790 return LDAP_SUCCESS
;
1794 * Call that allows to remove a set of queries related to an entry
1795 * from the cache; if queryid is not null, the entry must belong to
1796 * the query indicated by queryid.
1799 pcache_remove_entry_queries_from_cache(
1803 struct berval
*queryid
)
1805 Connection conn
= { 0 };
1806 OperationBuffer opbuf
;
1808 slap_callback sc
= { 0 };
1809 SlapReply rs
= { REP_RESULT
};
1811 char filter_str
[ LDAP_LUTIL_UUIDSTR_BUFSIZE
+ STRLENOF( "(queryId=)" ) ];
1812 AttributeAssertion ava
= ATTRIBUTEASSERTION_INIT
;
1813 AttributeName attrs
[ 2 ] = { 0 };
1816 BerVarray vals
= NULL
;
1819 void *thrctx
= ldap_pvt_thread_pool_context();
1821 connection_fake_init( &conn
, &opbuf
, thrctx
);
1829 memset( &op
->oq_search
, 0, sizeof( op
->oq_search
) );
1830 op
->ors_scope
= LDAP_SCOPE_BASE
;
1831 op
->ors_deref
= LDAP_DEREF_NEVER
;
1832 if ( queryid
== NULL
|| BER_BVISNULL( queryid
) ) {
1833 BER_BVSTR( &op
->ors_filterstr
, "(objectClass=*)" );
1834 f
.f_choice
= LDAP_FILTER_PRESENT
;
1835 f
.f_desc
= slap_schema
.si_ad_objectClass
;
1838 op
->ors_filterstr
.bv_len
= snprintf( filter_str
,
1839 sizeof( filter_str
), "(%s=%s)",
1840 ad_queryId
->ad_cname
.bv_val
, queryid
->bv_val
);
1841 f
.f_choice
= LDAP_FILTER_EQUALITY
;
1843 f
.f_av_desc
= ad_queryId
;
1844 f
.f_av_value
= *queryid
;
1846 op
->ors_filter
= &f
;
1848 op
->ors_tlimit
= SLAP_NO_LIMIT
;
1849 attrs
[ 0 ].an_desc
= ad_queryId
;
1850 attrs
[ 0 ].an_name
= ad_queryId
->ad_cname
;
1851 op
->ors_attrs
= attrs
;
1852 op
->ors_attrsonly
= 0;
1854 op
->o_req_dn
= *ndn
;
1855 op
->o_req_ndn
= *ndn
;
1857 op
->o_tag
= LDAP_REQ_SEARCH
;
1858 op
->o_protocol
= LDAP_VERSION3
;
1859 op
->o_managedsait
= SLAP_CONTROL_CRITICAL
;
1861 op
->o_dn
= op
->o_bd
->be_rootdn
;
1862 op
->o_ndn
= op
->o_bd
->be_rootndn
;
1863 sc
.sc_response
= fetch_queryId_cb
;
1864 op
->o_callback
= &sc
;
1866 rc
= op
->o_bd
->be_search( op
, &rs
);
1867 if ( rc
!= LDAP_SUCCESS
) {
1871 vals
= (BerVarray
)op
->o_callback
->sc_private
;
1872 if ( vals
!= NULL
) {
1875 for ( i
= 0; !BER_BVISNULL( &vals
[ i
] ); i
++ ) {
1876 struct berval val
= vals
[ i
];
1878 remove_query_and_data( op
, &rs
, cm
, &val
);
1880 if ( !BER_BVISNULL( &val
) && val
.bv_val
!= vals
[ i
].bv_val
) {
1881 ch_free( val
.bv_val
);
1885 ber_bvarray_free_x( vals
, op
->o_tmpmemctx
);
1888 return LDAP_SUCCESS
;
1895 struct berval
*query_uuid
)
1897 struct search_info
*si
= op
->o_callback
->sc_private
;
1898 slap_overinst
*on
= si
->on
;
1899 cache_manager
*cm
= on
->on_bi
.bi_private
;
1902 struct berval crp_uuid
;
1903 char uuidbuf
[ LDAP_LUTIL_UUIDSTR_BUFSIZE
];
1905 Connection conn
= {0};
1906 OperationBuffer opbuf
;
1907 void *thrctx
= ldap_pvt_thread_pool_context();
1909 query_uuid
->bv_len
= lutil_uuidstr(uuidbuf
, sizeof(uuidbuf
));
1910 ber_str2bv(uuidbuf
, query_uuid
->bv_len
, 1, query_uuid
);
1912 connection_fake_init2( &conn
, &opbuf
, thrctx
, 0 );
1913 op_tmp
= &opbuf
.ob_op
;
1914 op_tmp
->o_bd
= &cm
->db
;
1915 op_tmp
->o_dn
= cm
->db
.be_rootdn
;
1916 op_tmp
->o_ndn
= cm
->db
.be_rootndn
;
1918 Debug( pcache_debug
, "UUID for query being added = %s\n",
1921 for ( e
=si
->head
; e
; e
=si
->head
) {
1922 si
->head
= e
->e_private
;
1923 e
->e_private
= NULL
;
1924 while ( cm
->cur_entries
> (cm
->max_entries
) ) {
1925 BER_BVZERO( &crp_uuid
);
1926 remove_query_and_data( op_tmp
, rs
, cm
, &crp_uuid
);
1929 return_val
= merge_entry(op_tmp
, e
, query_uuid
);
1930 ldap_pvt_thread_mutex_lock(&cm
->cache_mutex
);
1931 cm
->cur_entries
+= return_val
;
1932 Debug( pcache_debug
,
1933 "ENTRY ADDED/MERGED, CACHED ENTRIES=%d\n",
1934 cm
->cur_entries
, 0, 0 );
1936 ldap_pvt_thread_mutex_unlock(&cm
->cache_mutex
);
1943 pcache_op_cleanup( Operation
*op
, SlapReply
*rs
) {
1944 slap_callback
*cb
= op
->o_callback
;
1945 struct search_info
*si
= cb
->sc_private
;
1946 slap_overinst
*on
= si
->on
;
1947 cache_manager
*cm
= on
->on_bi
.bi_private
;
1948 query_manager
* qm
= cm
->qm
;
1950 if ( rs
->sr_type
== REP_SEARCH
) {
1953 /* don't return more entries than requested by the client */
1954 if ( si
->slimit
&& rs
->sr_nentries
>= si
->slimit
) {
1955 si
->slimit_exceeded
= 1;
1958 /* If we haven't exceeded the limit for this query,
1959 * build a chain of answers to store. If we hit the
1960 * limit, empty the chain and ignore the rest.
1963 if ( si
->count
< si
->max
) {
1965 e
= entry_dup( rs
->sr_entry
);
1966 if ( !si
->head
) si
->head
= e
;
1967 if ( si
->tail
) si
->tail
->e_private
= e
;
1973 for (;si
->head
; si
->head
=e
) {
1974 e
= si
->head
->e_private
;
1975 si
->head
->e_private
= NULL
;
1976 entry_free(si
->head
);
1984 if ( rs
->sr_type
== REP_RESULT
||
1985 op
->o_abandon
|| rs
->sr_err
== SLAPD_ABANDON
)
1987 if ( si
->save_attrs
!= NULL
) {
1988 rs
->sr_attrs
= si
->save_attrs
;
1989 op
->ors_attrs
= si
->save_attrs
;
1991 if ( (op
->o_abandon
|| rs
->sr_err
== SLAPD_ABANDON
) &&
1992 si
->caching_reason
== PC_IGNORE
) {
1993 filter_free( si
->query
.filter
);
1995 /* duplicate query, free it */
1997 for (;si
->head
; si
->head
=e
) {
1998 e
= si
->head
->e_private
;
1999 si
->head
->e_private
= NULL
;
2000 entry_free(si
->head
);
2003 op
->o_callback
= op
->o_callback
->sc_next
;
2004 op
->o_tmpfree( cb
, op
->o_tmpmemctx
);
2005 } else if ( si
->caching_reason
!= PC_IGNORE
) {
2006 CachedQuery
*qc
= qm
->addfunc(op
, qm
, &si
->query
,
2007 si
->qtemp
, si
->caching_reason
, 1 );
2010 switch ( si
->caching_reason
) {
2012 cache_entries( op
, rs
, &qc
->q_uuid
);
2016 qc
->q_sizelimit
= rs
->sr_nentries
;
2026 ldap_pvt_thread_rdwr_wunlock(&qc
->rwlock
);
2027 ldap_pvt_thread_mutex_lock(&cm
->cache_mutex
);
2028 cm
->num_cached_queries
++;
2029 Debug( pcache_debug
, "STORED QUERIES = %lu\n",
2030 cm
->num_cached_queries
, 0, 0 );
2031 ldap_pvt_thread_mutex_unlock(&cm
->cache_mutex
);
2033 /* If the consistency checker suspended itself,
2036 if ( cm
->cc_paused
) {
2037 ldap_pvt_thread_mutex_lock( &slapd_rq
.rq_mutex
);
2038 if ( cm
->cc_paused
) {
2040 ldap_pvt_runqueue_resched( &slapd_rq
, cm
->cc_arg
, 0 );
2042 ldap_pvt_thread_mutex_unlock( &slapd_rq
.rq_mutex
);
2045 } else if ( si
->count
) {
2046 /* duplicate query, free it */
2048 for (;si
->head
; si
->head
=e
) {
2049 e
= si
->head
->e_private
;
2050 si
->head
->e_private
= NULL
;
2051 entry_free(si
->head
);
2056 filter_free( si
->query
.filter
);
2060 return SLAP_CB_CONTINUE
;
2068 struct search_info
*si
= op
->o_callback
->sc_private
;
2070 if ( si
->save_attrs
!= NULL
) {
2071 rs
->sr_attrs
= si
->save_attrs
;
2072 op
->ors_attrs
= si
->save_attrs
;
2075 if ( rs
->sr_type
== REP_SEARCH
) {
2076 /* don't return more entries than requested by the client */
2077 if ( si
->slimit_exceeded
) {
2081 } else if ( rs
->sr_type
== REP_RESULT
) {
2084 if ( rs
->sr_err
== LDAP_SUCCESS
) {
2085 si
->caching_reason
= PC_POSITIVE
;
2087 } else if ( rs
->sr_err
== LDAP_SIZELIMIT_EXCEEDED
2088 && si
->qtemp
->limitttl
)
2090 si
->caching_reason
= PC_SIZELIMIT
;
2093 } else if ( si
->qtemp
->negttl
&& !si
->count
&& !si
->over
&&
2094 rs
->sr_err
== LDAP_SUCCESS
)
2096 si
->caching_reason
= PC_NEGATIVE
;
2100 if ( si
->slimit_exceeded
) {
2101 rs
->sr_err
= LDAP_SIZELIMIT_EXCEEDED
;
2105 return SLAP_CB_CONTINUE
;
2111 AttributeName
** new_attrs
,
2112 struct attr_set
*attrs
,
2113 AttributeName
* filter_attrs
,
2123 /* duplicate attrs */
2124 count
= attrs
->count
+ fattr_cnt
;
2125 if ( !fattr_got_oc
&& !(attrs
->flags
& PC_GOT_OC
)) {
2130 *new_attrs
= (AttributeName
*)ch_calloc( count
+ 1,
2131 sizeof(AttributeName
) );
2132 for (i
=0; i
<attrs
->count
; i
++) {
2133 (*new_attrs
)[i
].an_name
= attrs
->attrs
[i
].an_name
;
2134 (*new_attrs
)[i
].an_desc
= attrs
->attrs
[i
].an_desc
;
2136 BER_BVZERO( &(*new_attrs
)[i
].an_name
);
2137 alluser
= an_find(*new_attrs
, &AllUser
);
2138 allop
= an_find(*new_attrs
, &AllOper
);
2141 for ( i
=0; i
<fattr_cnt
; i
++ ) {
2142 if ( an_find(*new_attrs
, &filter_attrs
[i
].an_name
) ) {
2145 if ( is_at_operational(filter_attrs
[i
].an_desc
->ad_type
) ) {
2149 } else if ( alluser
) {
2152 (*new_attrs
)[j
].an_name
= filter_attrs
[i
].an_name
;
2153 (*new_attrs
)[j
].an_desc
= filter_attrs
[i
].an_desc
;
2154 (*new_attrs
)[j
].an_oc
= NULL
;
2155 (*new_attrs
)[j
].an_oc_exclude
= 0;
2159 (*new_attrs
)[j
].an_name
= slap_schema
.si_ad_objectClass
->ad_cname
;
2160 (*new_attrs
)[j
].an_desc
= slap_schema
.si_ad_objectClass
;
2161 (*new_attrs
)[j
].an_oc
= NULL
;
2162 (*new_attrs
)[j
].an_oc_exclude
= 0;
2165 BER_BVZERO( &(*new_attrs
)[j
].an_name
);
2170 /* NOTE: this is a quick workaround to let pcache minimally interact
2171 * with pagedResults. A more articulated solutions would be to
2172 * perform the remote query without control and cache all results,
2173 * performing the pagedResults search only within the client
2174 * and the proxy. This requires pcache to understand pagedResults. */
2176 pcache_chk_controls(
2180 const char *non
= "";
2181 const char *stripped
= "";
2183 switch( op
->o_pagedresults
) {
2184 case SLAP_CONTROL_NONCRITICAL
:
2186 stripped
= "; stripped";
2189 case SLAP_CONTROL_CRITICAL
:
2190 Debug( pcache_debug
, "%s: "
2191 "%scritical pagedResults control "
2192 "disabled with proxy cache%s.\n",
2193 op
->o_log_prefix
, non
, stripped
);
2195 slap_remove_control( op
, rs
, slap_cids
.sc_pagedResults
, NULL
);
2199 rs
->sr_err
= SLAP_CB_CONTINUE
;
2206 #ifdef PCACHE_CONTROL_PRIVDB
2212 slap_overinst
*on
= (slap_overinst
*)op
->o_bd
->bd_info
;
2213 cache_manager
*cm
= on
->on_bi
.bi_private
;
2214 slap_callback
*save_cb
;
2217 /* skip if control is unset */
2218 if ( op
->o_ctrlflag
[ privDB_cid
] != SLAP_CONTROL_CRITICAL
) {
2219 return SLAP_CB_CONTINUE
;
2222 /* The cache DB isn't open yet */
2223 if ( cm
->defer_db_open
) {
2224 send_ldap_error( op
, rs
, LDAP_UNAVAILABLE
,
2225 "pcachePrivDB: cacheDB not available" );
2229 /* FIXME: might be a little bit exaggerated... */
2230 if ( !be_isroot( op
) ) {
2231 save_cb
= op
->o_callback
;
2232 op
->o_callback
= NULL
;
2233 send_ldap_error( op
, rs
, LDAP_UNWILLING_TO_PERFORM
,
2234 "pcachePrivDB: operation not allowed" );
2235 op
->o_callback
= save_cb
;
2240 /* map tag to operation */
2241 type
= slap_req2op( op
->o_tag
);
2242 if ( type
!= SLAP_OP_LAST
) {
2246 /* execute, if possible */
2247 func
= &cm
->db
.be_bind
;
2248 if ( func
[ type
] != NULL
) {
2249 Operation op2
= *op
;
2253 rc
= func
[ type
]( &op2
, rs
);
2254 if ( type
== SLAP_OP_BIND
&& rc
== LDAP_SUCCESS
) {
2255 op
->o_conn
->c_authz_cookie
= cm
->db
.be_private
;
2260 /* otherwise fall back to error */
2261 save_cb
= op
->o_callback
;
2262 op
->o_callback
= NULL
;
2263 send_ldap_error( op
, rs
, LDAP_UNWILLING_TO_PERFORM
,
2264 "operation not supported with pcachePrivDB control" );
2265 op
->o_callback
= save_cb
;
2269 #endif /* PCACHE_CONTROL_PRIVDB */
2276 slap_overinst
*on
= (slap_overinst
*)op
->o_bd
->bd_info
;
2277 cache_manager
*cm
= on
->on_bi
.bi_private
;
2278 query_manager
* qm
= cm
->qm
;
2282 AttributeName
*filter_attrs
= NULL
;
2285 QueryTemplate
*qtemp
= NULL
;
2288 CachedQuery
*answerable
= NULL
;
2291 int fattr_got_oc
= 0;
2293 struct berval tempstr
;
2295 #ifdef PCACHE_CONTROL_PRIVDB
2296 if ( op
->o_ctrlflag
[ privDB_cid
] == SLAP_CONTROL_CRITICAL
) {
2297 return pcache_op_privdb( op
, rs
);
2299 #endif /* PCACHE_CONTROL_PRIVDB */
2301 /* The cache DB isn't open yet */
2302 if ( cm
->defer_db_open
) {
2303 send_ldap_error( op
, rs
, LDAP_UNAVAILABLE
,
2304 "pcachePrivDB: cacheDB not available" );
2308 tempstr
.bv_val
= op
->o_tmpalloc( op
->ors_filterstr
.bv_len
+1, op
->o_tmpmemctx
);
2310 if ( filter2template( op
, op
->ors_filter
, &tempstr
, &filter_attrs
,
2311 &fattr_cnt
, &fattr_got_oc
)) {
2312 op
->o_tmpfree( tempstr
.bv_val
, op
->o_tmpmemctx
);
2313 return SLAP_CB_CONTINUE
;
2316 Debug( pcache_debug
, "query template of incoming query = %s\n",
2317 tempstr
.bv_val
, 0, 0 );
2319 /* FIXME: cannot cache/answer requests with pagedResults control */
2322 attr_set
= get_attr_set(op
->ors_attrs
, qm
, cm
->numattrsets
);
2324 query
.filter
= op
->ors_filter
;
2325 query
.base
= op
->o_req_ndn
;
2326 query
.scope
= op
->ors_scope
;
2328 /* check for query containment */
2329 if (attr_set
> -1) {
2330 QueryTemplate
*qt
= qm
->attr_sets
[attr_set
].templates
;
2331 for (; qt
; qt
= qt
->qtnext
) {
2332 /* find if template i can potentially answer tempstr */
2333 if (qt
->querystr
.bv_len
!= tempstr
.bv_len
||
2334 strcasecmp( qt
->querystr
.bv_val
, tempstr
.bv_val
))
2338 Debug( pcache_debug
, "Entering QC, querystr = %s\n",
2339 op
->ors_filterstr
.bv_val
, 0, 0 );
2340 answerable
= (*(qm
->qcfunc
))(op
, qm
, &query
, qt
);
2346 op
->o_tmpfree( tempstr
.bv_val
, op
->o_tmpmemctx
);
2349 /* Need to clear the callbacks of the original operation,
2350 * in case there are other overlays */
2351 BackendDB
*save_bd
= op
->o_bd
;
2352 slap_callback
*save_cb
= op
->o_callback
;
2354 Debug( pcache_debug
, "QUERY ANSWERABLE\n", 0, 0, 0 );
2355 op
->o_tmpfree( filter_attrs
, op
->o_tmpmemctx
);
2356 ldap_pvt_thread_rdwr_rlock(&answerable
->rwlock
);
2357 if ( BER_BVISNULL( &answerable
->q_uuid
)) {
2358 /* No entries cached, just an empty result set */
2360 send_ldap_result( op
, rs
);
2363 op
->o_callback
= NULL
;
2364 i
= cm
->db
.bd_info
->bi_op_search( op
, rs
);
2366 ldap_pvt_thread_rdwr_runlock(&answerable
->rwlock
);
2367 ldap_pvt_thread_rdwr_runlock(&qtemp
->t_rwlock
);
2369 op
->o_callback
= save_cb
;
2373 Debug( pcache_debug
, "QUERY NOT ANSWERABLE\n", 0, 0, 0 );
2375 ldap_pvt_thread_mutex_lock(&cm
->cache_mutex
);
2376 if (cm
->num_cached_queries
>= cm
->max_queries
) {
2379 ldap_pvt_thread_mutex_unlock(&cm
->cache_mutex
);
2381 if (op
->ors_attrsonly
)
2386 struct search_info
*si
;
2388 Debug( pcache_debug
, "QUERY CACHEABLE\n", 0, 0, 0 );
2389 query
.filter
= filter_dup(op
->ors_filter
, NULL
);
2390 ldap_pvt_thread_rdwr_wlock(&qtemp
->t_rwlock
);
2391 if ( !qtemp
->t_attrs
.count
) {
2392 qtemp
->t_attrs
.count
= add_filter_attrs(op
,
2393 &qtemp
->t_attrs
.attrs
,
2394 &qm
->attr_sets
[attr_set
],
2395 filter_attrs
, fattr_cnt
, fattr_got_oc
);
2397 ldap_pvt_thread_rdwr_wunlock(&qtemp
->t_rwlock
);
2399 cb
= op
->o_tmpalloc( sizeof(*cb
) + sizeof(*si
), op
->o_tmpmemctx
);
2400 cb
->sc_response
= pcache_response
;
2401 cb
->sc_cleanup
= pcache_op_cleanup
;
2402 cb
->sc_private
= (cb
+1);
2403 si
= cb
->sc_private
;
2407 si
->max
= cm
->num_entries_limit
;
2411 si
->slimit_exceeded
= 0;
2412 si
->caching_reason
= PC_IGNORE
;
2413 if ( op
->ors_slimit
&& op
->ors_slimit
< cm
->num_entries_limit
) {
2414 si
->slimit
= op
->ors_slimit
;
2415 op
->ors_slimit
= cm
->num_entries_limit
;
2419 si
->save_attrs
= op
->ors_attrs
;
2421 op
->ors_attrs
= qtemp
->t_attrs
.attrs
;
2423 if ( cm
->response_cb
== PCACHE_RESPONSE_CB_HEAD
) {
2424 cb
->sc_next
= op
->o_callback
;
2425 op
->o_callback
= cb
;
2428 slap_callback
**pcb
;
2430 /* need to move the callback at the end, in case other
2431 * overlays are present, so that the final entry is
2432 * actually cached */
2434 for ( pcb
= &op
->o_callback
; *pcb
; pcb
= &(*pcb
)->sc_next
);
2439 Debug( pcache_debug
, "QUERY NOT CACHEABLE\n",
2443 op
->o_tmpfree( filter_attrs
, op
->o_tmpmemctx
);
2445 return SLAP_CB_CONTINUE
;
2450 AttributeName
* attrs
,
2458 for ( ; attrs
[count
].an_name
.bv_val
; count
++ );
2461 /* recognize a single "*" or a "1.1" */
2464 attrs
= slap_anlist_all_user_attributes
;
2466 } else if ( count
== 1 && strcmp( attrs
[0].an_name
.bv_val
, LDAP_NO_ATTRS
) == 0 ) {
2471 for ( i
= 0; i
< num
; i
++ ) {
2475 if ( count
> qm
->attr_sets
[i
].count
) {
2480 if ( !qm
->attr_sets
[i
].count
) {
2486 for ( a2
= attrs
; a2
->an_name
.bv_val
; a2
++ ) {
2487 if ( !an_find( qm
->attr_sets
[i
].attrs
, &a2
->an_name
) ) {
2510 struct re_s
*rtask
= arg
;
2511 slap_overinst
*on
= rtask
->arg
;
2512 cache_manager
*cm
= on
->on_bi
.bi_private
;
2513 query_manager
*qm
= cm
->qm
;
2514 Connection conn
= {0};
2515 OperationBuffer opbuf
;
2518 SlapReply rs
= {REP_RESULT
};
2520 int return_val
, pause
= 1;
2521 QueryTemplate
* templ
;
2523 connection_fake_init( &conn
, &opbuf
, ctx
);
2527 op
->o_dn
= cm
->db
.be_rootdn
;
2528 op
->o_ndn
= cm
->db
.be_rootndn
;
2532 for (templ
= qm
->templates
; templ
; templ
=templ
->qmnext
) {
2533 query
= templ
->query_last
;
2534 if ( query
) pause
= 0;
2535 op
->o_time
= slap_get_time();
2536 while (query
&& (query
->expiry_time
< op
->o_time
)) {
2538 Debug( pcache_debug
, "Lock CR index = %p\n",
2539 (void *) templ
, 0, 0 );
2540 ldap_pvt_thread_rdwr_wlock(&templ
->t_rwlock
);
2541 if ( query
== templ
->query_last
) {
2543 remove_from_template(query
, templ
);
2544 Debug( pcache_debug
, "TEMPLATE %p QUERIES-- %d\n",
2545 (void *) templ
, templ
->no_of_queries
, 0 );
2546 Debug( pcache_debug
, "Unlock CR index = %p\n",
2547 (void *) templ
, 0, 0 );
2549 ldap_pvt_thread_rdwr_wunlock(&templ
->t_rwlock
);
2551 query
= templ
->query_last
;
2554 ldap_pvt_thread_mutex_lock(&qm
->lru_mutex
);
2555 remove_query(qm
, query
);
2556 ldap_pvt_thread_mutex_unlock(&qm
->lru_mutex
);
2557 if ( BER_BVISNULL( &query
->q_uuid
))
2560 return_val
= remove_query_data(op
, &rs
, &query
->q_uuid
);
2561 Debug( pcache_debug
, "STALE QUERY REMOVED, SIZE=%d\n",
2563 ldap_pvt_thread_mutex_lock(&cm
->cache_mutex
);
2564 cm
->cur_entries
-= return_val
;
2565 cm
->num_cached_queries
--;
2566 Debug( pcache_debug
, "STORED QUERIES = %lu\n",
2567 cm
->num_cached_queries
, 0, 0 );
2568 ldap_pvt_thread_mutex_unlock(&cm
->cache_mutex
);
2569 Debug( pcache_debug
,
2570 "STALE QUERY REMOVED, CACHE ="
2572 cm
->cur_entries
, 0, 0 );
2574 query
= templ
->query_last
;
2577 ldap_pvt_thread_mutex_lock( &slapd_rq
.rq_mutex
);
2578 if ( ldap_pvt_runqueue_isrunning( &slapd_rq
, rtask
)) {
2579 ldap_pvt_runqueue_stoptask( &slapd_rq
, rtask
);
2581 /* If there were no queries, defer processing for a while */
2582 cm
->cc_paused
= pause
;
2583 ldap_pvt_runqueue_resched( &slapd_rq
, rtask
, pause
);
2585 ldap_pvt_thread_mutex_unlock( &slapd_rq
.rq_mutex
);
2590 #define MAX_ATTR_SETS 500
2600 static ConfigDriver pc_cf_gen
;
2601 static ConfigLDAPadd pc_ldadd
;
2602 static ConfigCfAdd pc_cfadd
;
2604 static ConfigTable pccfg
[] = {
2605 { "proxycache", "backend> <max_entries> <numattrsets> <entry limit> "
2607 6, 6, 0, ARG_MAGIC
|ARG_NO_DELETE
|PC_MAIN
, pc_cf_gen
,
2608 "( OLcfgOvAt:2.1 NAME 'olcProxyCache' "
2609 "DESC 'ProxyCache basic parameters' "
2610 "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL
, NULL
},
2611 { "proxyattrset", "index> <attributes...",
2612 2, 0, 0, ARG_MAGIC
|PC_ATTR
, pc_cf_gen
,
2613 "( OLcfgOvAt:2.2 NAME 'olcProxyAttrset' "
2614 "DESC 'A set of attributes to cache' "
2615 "SYNTAX OMsDirectoryString )", NULL
, NULL
},
2616 { "proxytemplate", "filter> <attrset-index> <TTL> <negTTL",
2617 4, 6, 0, ARG_MAGIC
|PC_TEMP
, pc_cf_gen
,
2618 "( OLcfgOvAt:2.3 NAME 'olcProxyTemplate' "
2619 "DESC 'Filter template, attrset, cache TTL, "
2620 "optional negative TTL, optional sizelimit TTL' "
2621 "SYNTAX OMsDirectoryString )", NULL
, NULL
},
2622 { "response-callback", "head|tail(default)",
2623 2, 2, 0, ARG_MAGIC
|PC_RESP
, pc_cf_gen
,
2624 "( OLcfgOvAt:2.4 NAME 'olcProxyResponseCB' "
2625 "DESC 'Response callback position in overlay stack' "
2626 "SYNTAX OMsDirectoryString )", NULL
, NULL
},
2627 { "proxyCacheQueries", "queries",
2628 2, 2, 0, ARG_INT
|ARG_MAGIC
|PC_QUERIES
, pc_cf_gen
,
2629 "( OLcfgOvAt:2.5 NAME 'olcProxyCacheQueries' "
2630 "DESC 'Maximum number of queries to cache' "
2631 "SYNTAX OMsInteger )", NULL
, NULL
},
2632 { "proxySaveQueries", "TRUE|FALSE",
2633 2, 2, 0, ARG_ON_OFF
|ARG_OFFSET
, (void *)offsetof(cache_manager
, save_queries
),
2634 "( OLcfgOvAt:2.6 NAME 'olcProxySaveQueries' "
2635 "DESC 'Save cached queries for hot restart' "
2636 "SYNTAX OMsBoolean )", NULL
, NULL
},
2638 { NULL
, NULL
, 0, 0, 0, ARG_IGNORED
}
2641 /* Need to no-op this keyword for dynamic config */
2642 static ConfigTable pcdummy
[] = {
2643 { "", "", 0, 0, 0, ARG_IGNORED
,
2644 NULL
, "( OLcfgGlAt:13 NAME 'olcDatabase' "
2645 "DESC 'The backend type for a database instance' "
2646 "SUP olcBackend SINGLE-VALUE X-ORDERED 'SIBLINGS' )", NULL
, NULL
},
2647 { NULL
, NULL
, 0, 0, 0, ARG_IGNORED
}
2650 static ConfigOCs pcocs
[] = {
2651 { "( OLcfgOvOc:2.1 "
2652 "NAME 'olcPcacheConfig' "
2653 "DESC 'ProxyCache configuration' "
2654 "SUP olcOverlayConfig "
2655 "MUST ( olcProxyCache $ olcProxyAttrset $ olcProxyTemplate ) "
2656 "MAY ( olcProxyResponseCB $ olcProxyCacheQueries $ olcProxySaveQueries ) )",
2657 Cft_Overlay
, pccfg
, NULL
, pc_cfadd
},
2658 { "( OLcfgOvOc:2.2 "
2659 "NAME 'olcPcacheDatabase' "
2660 "DESC 'Cache database configuration' "
2661 "AUXILIARY )", Cft_Misc
, pcdummy
, pc_ldadd
},
2665 static int pcache_db_open2( slap_overinst
*on
, ConfigReply
*cr
);
2668 pc_ldadd_cleanup( ConfigArgs
*c
)
2670 slap_overinst
*on
= c
->ca_private
;
2671 return pcache_db_open2( on
, &c
->reply
);
2675 pc_ldadd( CfEntryInfo
*p
, Entry
*e
, ConfigArgs
*ca
)
2680 if ( p
->ce_type
!= Cft_Overlay
|| !p
->ce_bi
||
2681 p
->ce_bi
->bi_cf_ocs
!= pcocs
)
2682 return LDAP_CONSTRAINT_VIOLATION
;
2684 on
= (slap_overinst
*)p
->ce_bi
;
2685 cm
= on
->on_bi
.bi_private
;
2687 /* Defer open if this is an LDAPadd */
2688 if ( CONFIG_ONLINE_ADD( ca
))
2689 ca
->cleanup
= pc_ldadd_cleanup
;
2691 cm
->defer_db_open
= 0;
2692 ca
->ca_private
= on
;
2693 return LDAP_SUCCESS
;
2697 pc_cfadd( Operation
*op
, SlapReply
*rs
, Entry
*p
, ConfigArgs
*ca
)
2699 CfEntryInfo
*pe
= p
->e_private
;
2700 slap_overinst
*on
= (slap_overinst
*)pe
->ce_bi
;
2701 cache_manager
*cm
= on
->on_bi
.bi_private
;
2704 /* FIXME: should not hardcode "olcDatabase" here */
2705 bv
.bv_len
= snprintf( ca
->cr_msg
, sizeof( ca
->cr_msg
),
2706 "olcDatabase=%s", cm
->db
.bd_info
->bi_type
);
2707 if ( bv
.bv_len
< 0 || bv
.bv_len
>= sizeof( ca
->cr_msg
) ) {
2710 bv
.bv_val
= ca
->cr_msg
;
2712 cm
->defer_db_open
= 0;
2714 /* We can only create this entry if the database is table-driven
2716 if ( cm
->db
.bd_info
->bi_cf_ocs
)
2717 config_build_entry( op
, rs
, pe
, ca
, &bv
, cm
->db
.bd_info
->bi_cf_ocs
,
2724 pc_cf_gen( ConfigArgs
*c
)
2726 slap_overinst
*on
= (slap_overinst
*)c
->bi
;
2727 cache_manager
* cm
= on
->on_bi
.bi_private
;
2728 query_manager
* qm
= cm
->qm
;
2729 QueryTemplate
* temp
;
2730 AttributeName
* attr_name
;
2731 AttributeName
* attrarray
;
2732 const char* text
=NULL
;
2737 if ( c
->op
== SLAP_CONFIG_EMIT
) {
2741 bv
.bv_len
= snprintf( c
->cr_msg
, sizeof( c
->cr_msg
), "%s %d %d %d %ld",
2742 cm
->db
.bd_info
->bi_type
, cm
->max_entries
, cm
->numattrsets
,
2743 cm
->num_entries_limit
, cm
->cc_period
);
2744 bv
.bv_val
= c
->cr_msg
;
2745 value_add_one( &c
->rvalue_vals
, &bv
);
2748 for (i
=0; i
<cm
->numattrsets
; i
++) {
2749 if ( !qm
->attr_sets
[i
].count
) continue;
2751 bv
.bv_len
= snprintf( c
->cr_msg
, sizeof( c
->cr_msg
), "%d", i
);
2753 /* count the attr length */
2754 for ( attr_name
= qm
->attr_sets
[i
].attrs
;
2755 attr_name
->an_name
.bv_val
; attr_name
++ )
2756 bv
.bv_len
+= attr_name
->an_name
.bv_len
+ 1;
2758 bv
.bv_val
= ch_malloc( bv
.bv_len
+1 );
2759 ptr
= lutil_strcopy( bv
.bv_val
, c
->cr_msg
);
2760 for ( attr_name
= qm
->attr_sets
[i
].attrs
;
2761 attr_name
->an_name
.bv_val
; attr_name
++ ) {
2763 ptr
= lutil_strcopy( ptr
, attr_name
->an_name
.bv_val
);
2765 ber_bvarray_add( &c
->rvalue_vals
, &bv
);
2767 if ( !c
->rvalue_vals
)
2771 for (temp
=qm
->templates
; temp
; temp
=temp
->qmnext
) {
2772 /* HEADS-UP: always print all;
2773 * if optional == 0, ignore */
2774 bv
.bv_len
= snprintf( c
->cr_msg
, sizeof( c
->cr_msg
),
2776 temp
->attr_set_index
,
2780 bv
.bv_len
+= temp
->querystr
.bv_len
+ 2;
2781 bv
.bv_val
= ch_malloc( bv
.bv_len
+1 );
2784 ptr
= lutil_strcopy( ptr
, temp
->querystr
.bv_val
);
2786 strcpy( ptr
, c
->cr_msg
);
2787 ber_bvarray_add( &c
->rvalue_vals
, &bv
);
2789 if ( !c
->rvalue_vals
)
2793 if ( cm
->response_cb
== PCACHE_RESPONSE_CB_HEAD
) {
2794 BER_BVSTR( &bv
, "head" );
2796 BER_BVSTR( &bv
, "tail" );
2798 value_add_one( &c
->rvalue_vals
, &bv
);
2801 c
->value_int
= cm
->max_queries
;
2805 } else if ( c
->op
== LDAP_MOD_DELETE
) {
2806 return 1; /* FIXME */
2818 if ( cm
->numattrsets
> 0 ) {
2819 snprintf( c
->cr_msg
, sizeof( c
->cr_msg
), "\"proxycache\" directive already provided" );
2820 Debug( LDAP_DEBUG_CONFIG
, "%s: %s.\n", c
->log
, c
->cr_msg
, 0 );
2824 if ( lutil_atoi( &cm
->numattrsets
, c
->argv
[3] ) != 0 ) {
2825 snprintf( c
->cr_msg
, sizeof( c
->cr_msg
), "unable to parse num attrsets=\"%s\" (arg #3)",
2827 Debug( LDAP_DEBUG_CONFIG
, "%s: %s.\n", c
->log
, c
->cr_msg
, 0 );
2830 if ( cm
->numattrsets
<= 0 ) {
2831 snprintf( c
->cr_msg
, sizeof( c
->cr_msg
), "numattrsets (arg #3) must be positive" );
2832 Debug( LDAP_DEBUG_CONFIG
, "%s: %s.\n", c
->log
, c
->cr_msg
, 0 );
2835 if ( cm
->numattrsets
> MAX_ATTR_SETS
) {
2836 snprintf( c
->cr_msg
, sizeof( c
->cr_msg
), "numattrsets (arg #3) must be <= %d", MAX_ATTR_SETS
);
2837 Debug( LDAP_DEBUG_CONFIG
, "%s: %s.\n", c
->log
, c
->cr_msg
, 0 );
2841 if ( !backend_db_init( c
->argv
[1], &cm
->db
, -1, NULL
)) {
2842 snprintf( c
->cr_msg
, sizeof( c
->cr_msg
), "unknown backend type (arg #1)" );
2843 Debug( LDAP_DEBUG_CONFIG
, "%s: %s.\n", c
->log
, c
->cr_msg
, 0 );
2847 if ( lutil_atoi( &cm
->max_entries
, c
->argv
[2] ) != 0 ) {
2848 snprintf( c
->cr_msg
, sizeof( c
->cr_msg
), "unable to parse max entries=\"%s\" (arg #2)",
2850 Debug( LDAP_DEBUG_CONFIG
, "%s: %s.\n", c
->log
, c
->cr_msg
, 0 );
2853 if ( cm
->max_entries
<= 0 ) {
2854 snprintf( c
->cr_msg
, sizeof( c
->cr_msg
), "max entries (arg #2) must be positive.\n" );
2855 Debug( LDAP_DEBUG_CONFIG
, "%s: %s\n", c
->log
, c
->cr_msg
, 0 );
2859 if ( lutil_atoi( &cm
->num_entries_limit
, c
->argv
[4] ) != 0 ) {
2860 snprintf( c
->cr_msg
, sizeof( c
->cr_msg
), "unable to parse entry limit=\"%s\" (arg #4)",
2862 Debug( LDAP_DEBUG_CONFIG
, "%s: %s.\n", c
->log
, c
->cr_msg
, 0 );
2865 if ( cm
->num_entries_limit
<= 0 ) {
2866 snprintf( c
->cr_msg
, sizeof( c
->cr_msg
), "entry limit (arg #4) must be positive" );
2867 Debug( LDAP_DEBUG_CONFIG
, "%s: %s.\n", c
->log
, c
->cr_msg
, 0 );
2870 if ( cm
->num_entries_limit
> cm
->max_entries
) {
2871 snprintf( c
->cr_msg
, sizeof( c
->cr_msg
), "entry limit (arg #4) must be less than max entries %d (arg #2)", cm
->max_entries
);
2872 Debug( LDAP_DEBUG_CONFIG
, "%s: %s.\n", c
->log
, c
->cr_msg
, 0 );
2876 if ( lutil_parse_time( c
->argv
[5], &t
) != 0 ) {
2877 snprintf( c
->cr_msg
, sizeof( c
->cr_msg
), "unable to parse period=\"%s\" (arg #5)",
2879 Debug( LDAP_DEBUG_CONFIG
, "%s: %s.\n", c
->log
, c
->cr_msg
, 0 );
2883 cm
->cc_period
= (time_t)t
;
2884 Debug( pcache_debug
,
2885 "Total # of attribute sets to be cached = %d.\n",
2886 cm
->numattrsets
, 0, 0 );
2887 qm
->attr_sets
= ( struct attr_set
* )ch_calloc( cm
->numattrsets
,
2888 sizeof( struct attr_set
) );
2891 if ( cm
->numattrsets
== 0 ) {
2892 snprintf( c
->cr_msg
, sizeof( c
->cr_msg
), "\"proxycache\" directive not provided yet" );
2893 Debug( LDAP_DEBUG_CONFIG
, "%s: %s.\n", c
->log
, c
->cr_msg
, 0 );
2896 if ( lutil_atoi( &num
, c
->argv
[1] ) != 0 ) {
2897 snprintf( c
->cr_msg
, sizeof( c
->cr_msg
), "unable to parse attrset #=\"%s\"",
2899 Debug( LDAP_DEBUG_CONFIG
, "%s: %s.\n", c
->log
, c
->cr_msg
, 0 );
2903 if ( num
< 0 || num
>= cm
->numattrsets
) {
2904 snprintf( c
->cr_msg
, sizeof( c
->cr_msg
), "attrset index %d out of bounds (must be %s%d)",
2905 num
, cm
->numattrsets
> 1 ? "0->" : "", cm
->numattrsets
- 1 );
2906 Debug( LDAP_DEBUG_CONFIG
, "%s: %s.\n", c
->log
, c
->cr_msg
, 0 );
2909 qm
->attr_sets
[num
].flags
|= PC_CONFIGURED
;
2910 if ( c
->argc
== 2 ) {
2912 snprintf( c
->cr_msg
, sizeof( c
->cr_msg
),
2913 "need an explicit attr in attrlist; use \"*\" to indicate all attrs" );
2914 Debug( LDAP_DEBUG_CONFIG
, "%s: %s.\n", c
->log
, c
->cr_msg
, 0 );
2917 } else if ( c
->argc
== 3 ) {
2918 if ( strcmp( c
->argv
[2], LDAP_ALL_USER_ATTRIBUTES
) == 0 ) {
2919 qm
->attr_sets
[num
].count
= 1;
2920 qm
->attr_sets
[num
].attrs
= (AttributeName
*)ch_calloc( 2,
2921 sizeof( AttributeName
) );
2922 BER_BVSTR( &qm
->attr_sets
[num
].attrs
[0].an_name
, LDAP_ALL_USER_ATTRIBUTES
);
2925 } else if ( strcmp( c
->argv
[2], LDAP_ALL_OPERATIONAL_ATTRIBUTES
) == 0 ) {
2926 qm
->attr_sets
[num
].count
= 1;
2927 qm
->attr_sets
[num
].attrs
= (AttributeName
*)ch_calloc( 2,
2928 sizeof( AttributeName
) );
2929 BER_BVSTR( &qm
->attr_sets
[num
].attrs
[0].an_name
, LDAP_ALL_OPERATIONAL_ATTRIBUTES
);
2932 } else if ( strcmp( c
->argv
[2], LDAP_NO_ATTRS
) == 0 ) {
2935 /* else: fallthru */
2937 } else if ( c
->argc
== 4 ) {
2938 if ( ( strcmp( c
->argv
[2], LDAP_ALL_USER_ATTRIBUTES
) == 0 && strcmp( c
->argv
[3], LDAP_ALL_OPERATIONAL_ATTRIBUTES
) == 0 )
2939 || ( strcmp( c
->argv
[2], LDAP_ALL_OPERATIONAL_ATTRIBUTES
) == 0 && strcmp( c
->argv
[3], LDAP_ALL_USER_ATTRIBUTES
) == 0 ) )
2941 qm
->attr_sets
[num
].count
= 2;
2942 qm
->attr_sets
[num
].attrs
= (AttributeName
*)ch_calloc( 3,
2943 sizeof( AttributeName
) );
2944 BER_BVSTR( &qm
->attr_sets
[num
].attrs
[0].an_name
, LDAP_ALL_USER_ATTRIBUTES
);
2945 BER_BVSTR( &qm
->attr_sets
[num
].attrs
[1].an_name
, LDAP_ALL_OPERATIONAL_ATTRIBUTES
);
2948 /* else: fallthru */
2951 if ( c
->argc
> 2 ) {
2952 int all_user
= 0, all_op
= 0;
2954 qm
->attr_sets
[num
].count
= c
->argc
- 2;
2955 qm
->attr_sets
[num
].attrs
= (AttributeName
*)ch_calloc( c
->argc
- 1,
2956 sizeof( AttributeName
) );
2957 attr_name
= qm
->attr_sets
[num
].attrs
;
2958 for ( i
= 2; i
< c
->argc
; i
++ ) {
2959 attr_name
->an_desc
= NULL
;
2960 if ( strcmp( c
->argv
[i
], LDAP_NO_ATTRS
) == 0 ) {
2961 snprintf( c
->cr_msg
, sizeof( c
->cr_msg
),
2962 "invalid attr #%d \"%s\" in attrlist",
2963 i
- 2, c
->argv
[i
] );
2964 Debug( LDAP_DEBUG_CONFIG
, "%s: %s.\n", c
->log
, c
->cr_msg
, 0 );
2965 ch_free( qm
->attr_sets
[num
].attrs
);
2966 qm
->attr_sets
[num
].attrs
= NULL
;
2967 qm
->attr_sets
[num
].count
= 0;
2970 if ( strcmp( c
->argv
[i
], LDAP_ALL_USER_ATTRIBUTES
) == 0 ) {
2972 BER_BVSTR( &attr_name
->an_name
, LDAP_ALL_USER_ATTRIBUTES
);
2973 } else if ( strcmp( c
->argv
[i
], LDAP_ALL_OPERATIONAL_ATTRIBUTES
) == 0 ) {
2975 BER_BVSTR( &attr_name
->an_name
, LDAP_ALL_OPERATIONAL_ATTRIBUTES
);
2977 if ( slap_str2ad( c
->argv
[i
], &attr_name
->an_desc
, &text
) ) {
2978 strcpy( c
->cr_msg
, text
);
2979 Debug( LDAP_DEBUG_CONFIG
, "%s: %s.\n", c
->log
, c
->cr_msg
, 0 );
2980 ch_free( qm
->attr_sets
[num
].attrs
);
2981 qm
->attr_sets
[num
].attrs
= NULL
;
2982 qm
->attr_sets
[num
].count
= 0;
2985 attr_name
->an_name
= attr_name
->an_desc
->ad_cname
;
2987 attr_name
->an_oc
= NULL
;
2988 attr_name
->an_oc_exclude
= 0;
2989 if ( attr_name
->an_desc
== slap_schema
.si_ad_objectClass
)
2990 qm
->attr_sets
[num
].flags
|= PC_GOT_OC
;
2992 BER_BVZERO( &attr_name
->an_name
);
2995 /* warn if list contains both "*" and "+" */
2996 if ( i
> 4 && all_user
&& all_op
) {
2997 snprintf( c
->cr_msg
, sizeof( c
->cr_msg
),
2998 "warning: attribute list contains \"*\" and \"+\"" );
2999 Debug( LDAP_DEBUG_CONFIG
, "%s: %s.\n", c
->log
, c
->cr_msg
, 0 );
3004 if ( cm
->numattrsets
== 0 ) {
3005 snprintf( c
->cr_msg
, sizeof( c
->cr_msg
), "\"proxycache\" directive not provided yet" );
3006 Debug( LDAP_DEBUG_CONFIG
, "%s: %s.\n", c
->log
, c
->cr_msg
, 0 );
3009 if ( lutil_atoi( &i
, c
->argv
[2] ) != 0 ) {
3010 snprintf( c
->cr_msg
, sizeof( c
->cr_msg
), "unable to parse template #=\"%s\"",
3012 Debug( LDAP_DEBUG_CONFIG
, "%s: %s.\n", c
->log
, c
->cr_msg
, 0 );
3016 if ( i
< 0 || i
>= cm
->numattrsets
||
3017 !(qm
->attr_sets
[i
].flags
& PC_CONFIGURED
)) {
3018 snprintf( c
->cr_msg
, sizeof( c
->cr_msg
), "template index %d invalid (%s%d)",
3019 i
, cm
->numattrsets
> 1 ? "0->" : "", cm
->numattrsets
- 1 );
3020 Debug( LDAP_DEBUG_CONFIG
, "%s: %s.\n", c
->log
, c
->cr_msg
, 0 );
3023 temp
= ch_calloc( 1, sizeof( QueryTemplate
));
3024 temp
->qmnext
= qm
->templates
;
3025 qm
->templates
= temp
;
3026 ldap_pvt_thread_rdwr_init( &temp
->t_rwlock
);
3027 temp
->query
= temp
->query_last
= NULL
;
3028 if ( lutil_parse_time( c
->argv
[3], &t
) != 0 ) {
3029 snprintf( c
->cr_msg
, sizeof( c
->cr_msg
),
3030 "unable to parse template ttl=\"%s\"",
3032 Debug( LDAP_DEBUG_CONFIG
, "%s: %s.\n", c
->log
, c
->cr_msg
, 0 );
3035 temp
->ttl
= (time_t)t
;
3036 temp
->negttl
= (time_t)0;
3037 temp
->limitttl
= (time_t)0;
3038 switch ( c
->argc
) {
3040 if ( lutil_parse_time( c
->argv
[5], &t
) != 0 ) {
3041 snprintf( c
->cr_msg
, sizeof( c
->cr_msg
),
3042 "unable to parse template sizelimit ttl=\"%s\"",
3044 Debug( LDAP_DEBUG_CONFIG
, "%s: %s.\n", c
->log
, c
->cr_msg
, 0 );
3047 temp
->limitttl
= (time_t)t
;
3051 if ( lutil_parse_time( c
->argv
[4], &t
) != 0 ) {
3052 snprintf( c
->cr_msg
, sizeof( c
->cr_msg
),
3053 "unable to parse template negative ttl=\"%s\"",
3055 Debug( LDAP_DEBUG_CONFIG
, "%s: %s.\n", c
->log
, c
->cr_msg
, 0 );
3058 temp
->negttl
= (time_t)t
;
3062 temp
->no_of_queries
= 0;
3064 ber_str2bv( c
->argv
[1], 0, 1, &temp
->querystr
);
3065 Debug( pcache_debug
, "Template:\n", 0, 0, 0 );
3066 Debug( pcache_debug
, " query template: %s\n",
3067 temp
->querystr
.bv_val
, 0, 0 );
3068 temp
->attr_set_index
= i
;
3069 qm
->attr_sets
[i
].flags
|= PC_REFERENCED
;
3070 temp
->qtnext
= qm
->attr_sets
[i
].templates
;
3071 qm
->attr_sets
[i
].templates
= temp
;
3072 Debug( pcache_debug
, " attributes: \n", 0, 0, 0 );
3073 if ( ( attrarray
= qm
->attr_sets
[i
].attrs
) != NULL
) {
3074 for ( i
=0; attrarray
[i
].an_name
.bv_val
; i
++ )
3075 Debug( pcache_debug
, "\t%s\n",
3076 attrarray
[i
].an_name
.bv_val
, 0, 0 );
3080 if ( strcasecmp( c
->argv
[1], "head" ) == 0 ) {
3081 cm
->response_cb
= PCACHE_RESPONSE_CB_HEAD
;
3083 } else if ( strcasecmp( c
->argv
[1], "tail" ) == 0 ) {
3084 cm
->response_cb
= PCACHE_RESPONSE_CB_TAIL
;
3087 snprintf( c
->cr_msg
, sizeof( c
->cr_msg
), "unknown specifier" );
3088 Debug( LDAP_DEBUG_CONFIG
, "%s: %s.\n", c
->log
, c
->cr_msg
, 0 );
3093 if ( c
->value_int
<= 0 ) {
3094 snprintf( c
->cr_msg
, sizeof( c
->cr_msg
), "max queries must be positive" );
3095 Debug( LDAP_DEBUG_CONFIG
, "%s: %s.\n", c
->log
, c
->cr_msg
, 0 );
3098 cm
->max_queries
= c
->value_int
;
3113 slap_overinst
*on
= (slap_overinst
*)be
->bd_info
;
3114 cache_manager
* cm
= on
->on_bi
.bi_private
;
3116 /* Something for the cache database? */
3117 if ( cm
->db
.bd_info
&& cm
->db
.bd_info
->bi_db_config
)
3118 return cm
->db
.bd_info
->bi_db_config( &cm
->db
, fname
, lineno
,
3120 return SLAP_CONF_UNKNOWN
;
3128 slap_overinst
*on
= (slap_overinst
*)be
->bd_info
;
3132 cm
= (cache_manager
*)ch_malloc(sizeof(cache_manager
));
3133 on
->on_bi
.bi_private
= cm
;
3135 qm
= (query_manager
*)ch_malloc(sizeof(query_manager
));
3138 SLAP_DBFLAGS(&cm
->db
) |= SLAP_DBFLAG_NO_SCHEMA_CHECK
;
3139 cm
->db
.be_private
= NULL
;
3140 cm
->db
.be_pcl_mutexp
= &cm
->db
.be_pcl_mutex
;
3142 cm
->numattrsets
= 0;
3143 cm
->num_entries_limit
= 5;
3144 cm
->num_cached_queries
= 0;
3145 cm
->max_entries
= 0;
3146 cm
->cur_entries
= 0;
3147 cm
->max_queries
= 10000;
3148 cm
->save_queries
= 0;
3149 cm
->response_cb
= PCACHE_RESPONSE_CB_TAIL
;
3150 cm
->defer_db_open
= 1;
3151 cm
->cc_period
= 1000;
3155 qm
->attr_sets
= NULL
;
3156 qm
->templates
= NULL
;
3158 qm
->lru_bottom
= NULL
;
3160 qm
->qcfunc
= query_containment
;
3161 qm
->crfunc
= cache_replacement
;
3162 qm
->addfunc
= add_query
;
3163 ldap_pvt_thread_mutex_init(&qm
->lru_mutex
);
3165 ldap_pvt_thread_mutex_init(&cm
->cache_mutex
);
3170 pcache_cachedquery_open_cb( Operation
*op
, SlapReply
*rs
)
3172 assert( op
->o_tag
== LDAP_REQ_SEARCH
);
3174 if ( rs
->sr_type
== REP_SEARCH
) {
3177 a
= attr_find( rs
->sr_entry
->e_attrs
, ad_cachedQueryURL
);
3181 assert( a
->a_nvals
!= NULL
);
3183 valsp
= op
->o_callback
->sc_private
;
3184 assert( *valsp
== NULL
);
3186 ber_bvarray_dup_x( valsp
, a
->a_nvals
, op
->o_tmpmemctx
);
3194 pcache_cachedquery_count_cb( Operation
*op
, SlapReply
*rs
)
3196 assert( op
->o_tag
== LDAP_REQ_SEARCH
);
3198 if ( rs
->sr_type
== REP_SEARCH
) {
3199 int *countp
= (int *)op
->o_callback
->sc_private
;
3212 cache_manager
*cm
= on
->on_bi
.bi_private
;
3213 query_manager
* qm
= cm
->qm
;
3216 rc
= backend_startup_one( &cm
->db
, cr
);
3218 cm
->defer_db_open
= 0;
3221 /* There is no runqueue in TOOL mode */
3222 if (( slapMode
& SLAP_SERVER_MODE
) && rc
== 0 ) {
3223 ldap_pvt_thread_mutex_lock( &slapd_rq
.rq_mutex
);
3224 ldap_pvt_runqueue_insert( &slapd_rq
, cm
->cc_period
,
3225 consistency_check
, on
,
3226 "pcache_consistency", cm
->db
.be_suffix
[0].bv_val
);
3227 ldap_pvt_thread_mutex_unlock( &slapd_rq
.rq_mutex
);
3229 /* Cached database must have the rootdn */
3230 if ( BER_BVISNULL( &cm
->db
.be_rootndn
)
3231 || BER_BVISEMPTY( &cm
->db
.be_rootndn
) )
3233 Debug( LDAP_DEBUG_ANY
, "pcache_db_open(): "
3234 "underlying database of type \"%s\"\n"
3235 " serving naming context \"%s\"\n"
3236 " has no \"rootdn\", required by \"proxycache\".\n",
3237 on
->on_info
->oi_orig
->bi_type
,
3238 cm
->db
.be_suffix
[0].bv_val
, 0 );
3242 if ( cm
->save_queries
) {
3243 void *thrctx
= ldap_pvt_thread_pool_context();
3244 Connection conn
= { 0 };
3245 OperationBuffer opbuf
;
3247 slap_callback cb
= { 0 };
3248 SlapReply rs
= { 0 };
3249 BerVarray vals
= NULL
;
3250 Filter f
= { 0 }, f2
= { 0 };
3251 AttributeAssertion ava
= ATTRIBUTEASSERTION_INIT
;
3252 AttributeName attrs
[ 2 ] = { 0 };
3254 connection_fake_init( &conn
, &opbuf
, thrctx
);
3259 op
->o_tag
= LDAP_REQ_SEARCH
;
3260 op
->o_protocol
= LDAP_VERSION3
;
3261 cb
.sc_response
= pcache_cachedquery_open_cb
;
3262 cb
.sc_private
= &vals
;
3263 op
->o_callback
= &cb
;
3264 op
->o_time
= slap_get_time();
3265 op
->o_do_not_cache
= 1;
3266 op
->o_managedsait
= SLAP_CONTROL_CRITICAL
;
3268 op
->o_dn
= cm
->db
.be_rootdn
;
3269 op
->o_ndn
= cm
->db
.be_rootndn
;
3270 op
->o_req_dn
= cm
->db
.be_suffix
[ 0 ];
3271 op
->o_req_ndn
= cm
->db
.be_nsuffix
[ 0 ];
3273 op
->ors_scope
= LDAP_SCOPE_BASE
;
3274 op
->ors_deref
= LDAP_DEREF_NEVER
;
3276 op
->ors_tlimit
= SLAP_NO_LIMIT
;
3277 ber_str2bv( "(cachedQueryURL=*)", 0, 0, &op
->ors_filterstr
);
3278 f
.f_choice
= LDAP_FILTER_PRESENT
;
3279 f
.f_desc
= ad_cachedQueryURL
;
3280 op
->ors_filter
= &f
;
3281 attrs
[ 0 ].an_desc
= ad_cachedQueryURL
;
3282 attrs
[ 0 ].an_name
= ad_cachedQueryURL
->ad_cname
;
3283 op
->ors_attrs
= attrs
;
3284 op
->ors_attrsonly
= 0;
3286 rc
= op
->o_bd
->be_search( op
, &rs
);
3287 if ( rc
== LDAP_SUCCESS
&& vals
!= NULL
) {
3290 for ( i
= 0; !BER_BVISNULL( &vals
[ i
] ); i
++ ) {
3291 if ( url2query( vals
[ i
].bv_val
, op
, qm
) == 0 ) {
3292 cm
->num_cached_queries
++;
3296 ber_bvarray_free_x( vals
, op
->o_tmpmemctx
);
3299 /* count cached entries */
3300 f
.f_choice
= LDAP_FILTER_NOT
;
3302 f2
.f_choice
= LDAP_FILTER_EQUALITY
;
3304 f2
.f_av_desc
= slap_schema
.si_ad_objectClass
;
3305 BER_BVSTR( &f2
.f_av_value
, "glue" );
3306 ber_str2bv( "(!(objectClass=glue))", 0, 0, &op
->ors_filterstr
);
3308 op
->ors_slimit
= SLAP_NO_LIMIT
;
3309 op
->ors_scope
= LDAP_SCOPE_SUBTREE
;
3310 op
->ors_attrs
= slap_anlist_no_attrs
;
3312 op
->o_callback
->sc_response
= pcache_cachedquery_count_cb
;
3314 op
->o_callback
->sc_private
= &rs
.sr_nentries
;
3316 rc
= op
->o_bd
->be_search( op
, &rs
);
3318 cm
->cur_entries
= rs
.sr_nentries
;
3332 slap_overinst
*on
= (slap_overinst
*)be
->bd_info
;
3333 cache_manager
*cm
= on
->on_bi
.bi_private
;
3334 query_manager
* qm
= cm
->qm
;
3335 int i
, ncf
= 0, rf
= 0, nrf
= 0, rc
= 0;
3337 /* check attr sets */
3338 for ( i
= 0; i
< cm
->numattrsets
; i
++) {
3339 if ( !( qm
->attr_sets
[i
].flags
& PC_CONFIGURED
) ) {
3340 if ( qm
->attr_sets
[i
].flags
& PC_REFERENCED
) {
3341 Debug( LDAP_DEBUG_CONFIG
, "pcache: attr set #%d not configured but referenced.\n", i
, 0, 0 );
3345 Debug( LDAP_DEBUG_CONFIG
, "pcache: warning, attr set #%d not configured.\n", i
, 0, 0 );
3349 } else if ( !( qm
->attr_sets
[i
].flags
& PC_REFERENCED
) ) {
3350 Debug( LDAP_DEBUG_CONFIG
, "pcache: attr set #%d configured but not referenced.\n", i
, 0, 0 );
3355 if ( ncf
|| rf
|| nrf
) {
3356 Debug( LDAP_DEBUG_CONFIG
, "pcache: warning, %d attr sets configured but not referenced.\n", nrf
, 0, 0 );
3357 Debug( LDAP_DEBUG_CONFIG
, "pcache: warning, %d attr sets not configured.\n", ncf
, 0, 0 );
3358 Debug( LDAP_DEBUG_CONFIG
, "pcache: %d attr sets not configured but referenced.\n", rf
, 0, 0 );
3365 /* need to inherit something from the original database... */
3366 cm
->db
.be_def_limit
= be
->be_def_limit
;
3367 cm
->db
.be_limits
= be
->be_limits
;
3368 cm
->db
.be_acl
= be
->be_acl
;
3369 cm
->db
.be_dfltaccess
= be
->be_dfltaccess
;
3371 if ( SLAP_DBMONITORING( be
) ) {
3372 SLAP_DBFLAGS( &cm
->db
) |= SLAP_DBFLAG_MONITORING
;
3375 SLAP_DBFLAGS( &cm
->db
) &= ~SLAP_DBFLAG_MONITORING
;
3378 if ( !cm
->defer_db_open
)
3379 rc
= pcache_db_open2( on
, cr
);
3385 pcache_free_qbase( void *v
)
3391 tavl_free( qb
->scopes
[i
], NULL
);
3401 slap_overinst
*on
= (slap_overinst
*)be
->bd_info
;
3402 cache_manager
*cm
= on
->on_bi
.bi_private
;
3403 query_manager
*qm
= cm
->qm
;
3407 if ( cm
->save_queries
) {
3409 BerVarray vals
= NULL
;
3412 Connection conn
= { 0 };
3413 OperationBuffer opbuf
;
3415 slap_callback cb
= { 0 };
3417 SlapReply rs
= { REP_RESULT
};
3418 Modifications mod
= { 0 };
3420 thrctx
= ldap_pvt_thread_pool_context();
3422 connection_fake_init( &conn
, &opbuf
, thrctx
);
3425 if ( qm
->templates
!= NULL
) {
3426 for ( tm
= qm
->templates
; tm
!= NULL
; tm
= tm
->qmnext
) {
3427 for ( qc
= tm
->query
; qc
; qc
= qc
->next
) {
3430 if ( query2url( op
, qc
, &bv
) == 0 ) {
3431 ber_bvarray_add_x( &vals
, &bv
, op
->o_tmpmemctx
);
3438 op
->o_dn
= cm
->db
.be_rootdn
;
3439 op
->o_ndn
= cm
->db
.be_rootndn
;
3441 op
->o_tag
= LDAP_REQ_MODIFY
;
3442 op
->o_protocol
= LDAP_VERSION3
;
3443 cb
.sc_response
= slap_null_cb
;
3444 op
->o_callback
= &cb
;
3445 op
->o_time
= slap_get_time();
3446 op
->o_do_not_cache
= 1;
3447 op
->o_managedsait
= SLAP_CONTROL_CRITICAL
;
3449 op
->o_req_dn
= op
->o_bd
->be_suffix
[0];
3450 op
->o_req_ndn
= op
->o_bd
->be_nsuffix
[0];
3452 mod
.sml_op
= LDAP_MOD_REPLACE
;
3454 mod
.sml_desc
= ad_cachedQueryURL
;
3455 mod
.sml_type
= ad_cachedQueryURL
->ad_cname
;
3456 mod
.sml_values
= vals
;
3457 mod
.sml_nvalues
= NULL
;
3458 mod
.sml_numvals
= 1;
3459 mod
.sml_next
= NULL
;
3460 Debug( pcache_debug
,
3461 "%sSETTING CACHED QUERY URLS\n",
3462 vals
== NULL
? "RE" : "", 0, 0 );
3464 op
->orm_modlist
= &mod
;
3466 op
->o_bd
->be_modify( op
, &rs
);
3468 ber_bvarray_free_x( vals
, op
->o_tmpmemctx
);
3471 /* cleanup stuff inherited from the original database... */
3472 cm
->db
.be_limits
= NULL
;
3473 cm
->db
.be_acl
= NULL
;
3475 /* stop the thread ... */
3477 ldap_pvt_thread_mutex_lock( &slapd_rq
.rq_mutex
);
3478 if ( ldap_pvt_runqueue_isrunning( &slapd_rq
, cm
->cc_arg
) ) {
3479 ldap_pvt_runqueue_stoptask( &slapd_rq
, cm
->cc_arg
);
3481 ldap_pvt_runqueue_remove( &slapd_rq
, cm
->cc_arg
);
3482 ldap_pvt_thread_mutex_unlock( &slapd_rq
.rq_mutex
);
3485 if ( cm
->db
.bd_info
->bi_db_close
) {
3486 rc
= cm
->db
.bd_info
->bi_db_close( &cm
->db
, NULL
);
3488 while ( (tm
= qm
->templates
) != NULL
) {
3489 CachedQuery
*qc
, *qn
;
3490 qm
->templates
= tm
->qmnext
;
3491 for ( qc
= tm
->query
; qc
; qc
= qn
) {
3495 avl_free( tm
->qbase
, pcache_free_qbase
);
3496 free( tm
->querystr
.bv_val
);
3497 ldap_pvt_thread_rdwr_destroy( &tm
->t_rwlock
);
3498 free( tm
->t_attrs
.attrs
);
3502 for ( i
=0; i
<cm
->numattrsets
; i
++ ) {
3503 free( qm
->attr_sets
[i
].attrs
);
3505 free( qm
->attr_sets
);
3506 qm
->attr_sets
= NULL
;
3517 slap_overinst
*on
= (slap_overinst
*)be
->bd_info
;
3518 cache_manager
*cm
= on
->on_bi
.bi_private
;
3519 query_manager
*qm
= cm
->qm
;
3521 if ( cm
->db
.be_private
!= NULL
) {
3522 backend_stopdown_one( &cm
->db
);
3525 ldap_pvt_thread_mutex_destroy( &qm
->lru_mutex
);
3526 ldap_pvt_thread_mutex_destroy( &cm
->cache_mutex
);
3533 #ifdef PCACHE_CONTROL_PRIVDB
3535 Control ::= SEQUENCE {
3536 controlType LDAPOID,
3537 criticality BOOLEAN DEFAULT FALSE,
3538 controlValue OCTET STRING OPTIONAL }
3540 controlType ::= 1.3.6.1.4.1.4203.666.11.9.5.1
3542 * criticality must be TRUE; controlValue must be absent.
3550 if ( op
->o_ctrlflag
[ privDB_cid
] != SLAP_CONTROL_NONE
) {
3551 rs
->sr_text
= "privateDB control specified multiple times";
3552 return LDAP_PROTOCOL_ERROR
;
3555 if ( !BER_BVISNULL( &ctrl
->ldctl_value
) ) {
3556 rs
->sr_text
= "privateDB control value not absent";
3557 return LDAP_PROTOCOL_ERROR
;
3560 if ( !ctrl
->ldctl_iscritical
) {
3561 rs
->sr_text
= "privateDB control criticality required";
3562 return LDAP_PROTOCOL_ERROR
;
3565 op
->o_ctrlflag
[ privDB_cid
] = SLAP_CONTROL_CRITICAL
;
3567 return LDAP_SUCCESS
;
3570 static char *extops
[] = {
3571 LDAP_EXOP_MODIFY_PASSWD
,
3574 #endif /* PCACHE_CONTROL_PRIVDB */
3576 #ifdef PCACHE_EXOP_QUERY_DELETE
3577 static struct berval pcache_exop_QUERY_DELETE
= BER_BVC( PCACHE_EXOP_QUERY_DELETE
);
3579 #define LDAP_TAG_EXOP_QUERY_DELETE_BASE ((LBER_CLASS_CONTEXT|LBER_CONSTRUCTED) + 0)
3580 #define LDAP_TAG_EXOP_QUERY_DELETE_DN ((LBER_CLASS_CONTEXT|LBER_CONSTRUCTED) + 1)
3581 #define LDAP_TAG_EXOP_QUERY_DELETE_UUID ((LBER_CLASS_CONTEXT|LBER_CONSTRUCTED) + 2)
3584 ExtendedRequest ::= [APPLICATION 23] SEQUENCE {
3585 requestName [0] LDAPOID,
3586 requestValue [1] OCTET STRING OPTIONAL }
3588 requestName ::= 1.3.6.1.4.1.4203.666.11.9.6.1
3590 requestValue ::= SEQUENCE { CHOICE {
3592 entryDN [1] LDAPDN },
3593 queryID [2] OCTET STRING (SIZE(16))
3594 -- constrained to UUID }
3596 * Either baseDN or entryDN must be present, to allow database selection.
3598 * 1. if baseDN and queryID are present, then the query corresponding
3599 * to queryID is deleted;
3600 * 2. if baseDN is present and queryID is absent, then all queries
3602 * 3. if entryDN is present and queryID is absent, then all queries
3603 * corresponding to the queryID values present in entryDN are deleted;
3604 * 4. if entryDN and queryID are present, then all queries
3605 * corresponding to the queryID values present in entryDN are deleted,
3606 * but only if the value of queryID is contained in the entry;
3608 * Currently, only 1, 3 and 4 are implemented. 2 can be obtained by either
3609 * recursively deleting the database (ldapdelete -r) with PRIVDB control,
3610 * or by removing the database files.
3612 ExtendedResponse ::= [APPLICATION 24] SEQUENCE {
3613 COMPONENTS OF LDAPResult,
3614 responseName [10] LDAPOID OPTIONAL,
3615 responseValue [11] OCTET STRING OPTIONAL }
3617 * responseName and responseValue must be absent.
3621 * - on success, *tagp is either LDAP_TAG_EXOP_QUERY_DELETE_BASE
3622 * or LDAP_TAG_EXOP_QUERY_DELETE_DN.
3623 * - if ndn != NULL, it is set to the normalized DN in the request
3624 * corresponding to either the baseDN or the entryDN, according
3625 * to *tagp; memory is malloc'ed on the Operation's slab, and must
3626 * be freed by the caller.
3627 * - if uuid != NULL, it is set to point to the normalized UUID;
3628 * memory is malloc'ed on the Operation's slab, and must
3629 * be freed by the caller.
3632 pcache_parse_query_delete(
3636 struct berval
*uuid
,
3640 int rc
= LDAP_SUCCESS
;
3643 BerElementBuffer berbuf
;
3644 BerElement
*ber
= (BerElement
*)&berbuf
;
3645 struct berval reqdata
= BER_BVNULL
;
3657 if ( in
== NULL
|| in
->bv_len
== 0 ) {
3658 *text
= "empty request data field in queryDelete exop";
3659 return LDAP_PROTOCOL_ERROR
;
3662 ber_dupbv_x( &reqdata
, in
, ctx
);
3664 /* ber_init2 uses reqdata directly, doesn't allocate new buffers */
3665 ber_init2( ber
, &reqdata
, 0 );
3667 tag
= ber_scanf( ber
, "{" /*}*/ );
3669 if ( tag
== LBER_ERROR
) {
3670 Debug( LDAP_DEBUG_TRACE
,
3671 "pcache_parse_query_delete: decoding error.\n",
3673 goto decoding_error
;
3676 tag
= ber_peek_tag( ber
, &len
);
3677 if ( tag
== LDAP_TAG_EXOP_QUERY_DELETE_BASE
3678 || tag
== LDAP_TAG_EXOP_QUERY_DELETE_DN
)
3682 if ( ndn
!= NULL
) {
3685 tag
= ber_scanf( ber
, "m", &dn
);
3686 if ( tag
== LBER_ERROR
) {
3687 Debug( LDAP_DEBUG_TRACE
,
3688 "pcache_parse_query_delete: DN parse failed.\n",
3690 goto decoding_error
;
3693 rc
= dnNormalize( 0, NULL
, NULL
, &dn
, ndn
, ctx
);
3694 if ( rc
!= LDAP_SUCCESS
) {
3695 *text
= "invalid DN in queryDelete exop request data";
3700 tag
= ber_scanf( ber
, "x" /* "m" */ );
3701 if ( tag
== LBER_DEFAULT
) {
3702 goto decoding_error
;
3706 tag
= ber_peek_tag( ber
, &len
);
3709 if ( tag
== LDAP_TAG_EXOP_QUERY_DELETE_UUID
) {
3710 if ( uuid
!= NULL
) {
3712 char uuidbuf
[ LDAP_LUTIL_UUIDSTR_BUFSIZE
];
3714 tag
= ber_scanf( ber
, "m", &bv
);
3715 if ( tag
== LBER_ERROR
) {
3716 Debug( LDAP_DEBUG_TRACE
,
3717 "pcache_parse_query_delete: UUID parse failed.\n",
3719 goto decoding_error
;
3722 if ( bv
.bv_len
!= 16 ) {
3723 Debug( LDAP_DEBUG_TRACE
,
3724 "pcache_parse_query_delete: invalid UUID length %lu.\n",
3725 (unsigned long)bv
.bv_len
, 0, 0 );
3726 goto decoding_error
;
3729 rc
= lutil_uuidstr_from_normalized(
3730 bv
.bv_val
, bv
.bv_len
,
3731 uuidbuf
, sizeof( uuidbuf
) );
3733 goto decoding_error
;
3735 ber_str2bv( uuidbuf
, rc
, 1, uuid
);
3739 tag
= ber_skip_tag( ber
, &len
);
3740 if ( tag
== LBER_DEFAULT
) {
3741 goto decoding_error
;
3745 Debug( LDAP_DEBUG_TRACE
,
3746 "pcache_parse_query_delete: invalid UUID length %lu.\n",
3747 (unsigned long)len
, 0, 0 );
3748 goto decoding_error
;
3752 tag
= ber_peek_tag( ber
, &len
);
3755 if ( tag
!= LBER_DEFAULT
|| len
!= 0 ) {
3757 Debug( LDAP_DEBUG_TRACE
,
3758 "pcache_parse_query_delete: decoding error\n",
3760 rc
= LDAP_PROTOCOL_ERROR
;
3761 *text
= "queryDelete data decoding error";
3764 if ( ndn
&& !BER_BVISNULL( ndn
) ) {
3765 slap_sl_free( ndn
->bv_val
, ctx
);
3769 if ( uuid
&& !BER_BVISNULL( uuid
) ) {
3770 slap_sl_free( uuid
->bv_val
, ctx
);
3775 if ( !BER_BVISNULL( &reqdata
) ) {
3776 ber_memfree_x( reqdata
.bv_val
, ctx
);
3783 pcache_exop_query_delete(
3787 BackendDB
*bd
= op
->o_bd
;
3789 struct berval uuid
= BER_BVNULL
,
3791 char buf
[ SLAP_TEXT_BUFLEN
] = { '\0' };
3793 ber_tag_t tag
= LBER_DEFAULT
;
3795 if ( LogTest( LDAP_DEBUG_STATS
) ) {
3799 rs
->sr_err
= pcache_parse_query_delete( op
->ore_reqdata
,
3800 &tag
, &op
->o_req_ndn
, uuidp
,
3801 &rs
->sr_text
, op
->o_tmpmemctx
);
3802 if ( rs
->sr_err
!= LDAP_SUCCESS
) {
3806 if ( LogTest( LDAP_DEBUG_STATS
) ) {
3807 assert( !BER_BVISNULL( &op
->o_req_ndn
) );
3808 len
= snprintf( buf
, sizeof( buf
), " dn=\"%s\"", op
->o_req_ndn
.bv_val
);
3810 if ( !BER_BVISNULL( &uuid
) ) {
3811 snprintf( &buf
[ len
], sizeof( buf
) - len
, " queryId=\"%s\"", uuid
.bv_val
);
3814 Debug( LDAP_DEBUG_STATS
, "%s QUERY DELETE%s\n",
3815 op
->o_log_prefix
, buf
, 0 );
3817 op
->o_req_dn
= op
->o_req_ndn
;
3819 op
->o_bd
= select_backend( &op
->o_req_ndn
, 0 );
3820 rs
->sr_err
= backend_check_restrictions( op
, rs
,
3821 (struct berval
*)&pcache_exop_QUERY_DELETE
);
3822 if ( rs
->sr_err
!= LDAP_SUCCESS
) {
3826 if ( op
->o_bd
->be_extended
== NULL
) {
3827 send_ldap_error( op
, rs
, LDAP_UNAVAILABLE_CRITICAL_EXTENSION
,
3828 "backend does not support extended operations" );
3832 op
->o_bd
->be_extended( op
, rs
);
3835 if ( !BER_BVISNULL( &op
->o_req_ndn
) ) {
3836 op
->o_tmpfree( op
->o_req_ndn
.bv_val
, op
->o_tmpmemctx
);
3837 BER_BVZERO( &op
->o_req_ndn
);
3838 BER_BVZERO( &op
->o_req_dn
);
3841 if ( !BER_BVISNULL( &uuid
) ) {
3842 op
->o_tmpfree( uuid
.bv_val
, op
->o_tmpmemctx
);
3851 pcache_op_extended( Operation
*op
, SlapReply
*rs
)
3853 slap_overinst
*on
= (slap_overinst
*)op
->o_bd
->bd_info
;
3854 cache_manager
*cm
= on
->on_bi
.bi_private
;
3856 #ifdef PCACHE_CONTROL_PRIVDB
3857 if ( op
->o_ctrlflag
[ privDB_cid
] == SLAP_CONTROL_CRITICAL
) {
3858 return pcache_op_privdb( op
, rs
);
3860 #endif /* PCACHE_CONTROL_PRIVDB */
3862 if ( bvmatch( &op
->ore_reqoid
, &pcache_exop_QUERY_DELETE
) ) {
3863 struct berval uuid
= BER_BVNULL
;
3864 ber_tag_t tag
= LBER_DEFAULT
;
3866 rs
->sr_err
= pcache_parse_query_delete( op
->ore_reqdata
,
3867 &tag
, NULL
, &uuid
, &rs
->sr_text
, op
->o_tmpmemctx
);
3868 assert( rs
->sr_err
== LDAP_SUCCESS
);
3870 if ( tag
== LDAP_TAG_EXOP_QUERY_DELETE_DN
) {
3871 /* remove all queries related to the selected entry */
3872 rs
->sr_err
= pcache_remove_entry_queries_from_cache( op
,
3873 cm
, &op
->o_req_ndn
, &uuid
);
3875 } else if ( tag
== LDAP_TAG_EXOP_QUERY_DELETE_BASE
) {
3876 if ( !BER_BVISNULL( &uuid
) ) {
3877 /* remove the selected query */
3878 rs
->sr_err
= pcache_remove_query_from_cache( op
,
3882 /* TODO: remove all queries */
3883 rs
->sr_err
= LDAP_UNWILLING_TO_PERFORM
;
3884 rs
->sr_text
= "deletion of all queries not implemented";
3888 op
->o_tmpfree( uuid
.bv_val
, op
->o_tmpmemctx
);
3893 #endif /* PCACHE_EXOP_QUERY_DELETE */
3895 static slap_overinst pcache
;
3897 static char *obsolete_names
[] = {
3902 #if SLAPD_OVER_PROXYCACHE == SLAPD_MOD_DYNAMIC
3904 #endif /* SLAPD_OVER_PROXYCACHE == SLAPD_MOD_DYNAMIC */
3909 struct berval debugbv
= BER_BVC("pcache");
3911 code
= slap_loglevel_get( &debugbv
, &pcache_debug
);
3916 #ifdef PCACHE_CONTROL_PRIVDB
3917 code
= register_supported_control( PCACHE_CONTROL_PRIVDB
,
3918 SLAP_CTRL_BIND
|SLAP_CTRL_ACCESS
|SLAP_CTRL_HIDE
, extops
,
3919 parse_privdb_ctrl
, &privDB_cid
);
3920 if ( code
!= LDAP_SUCCESS
) {
3921 Debug( LDAP_DEBUG_ANY
,
3922 "pcache_initialize: failed to register control %s (%d)\n",
3923 PCACHE_CONTROL_PRIVDB
, code
, 0 );
3926 #endif /* PCACHE_CONTROL_PRIVDB */
3928 #ifdef PCACHE_EXOP_QUERY_DELETE
3929 code
= load_extop2( (struct berval
*)&pcache_exop_QUERY_DELETE
,
3930 SLAP_EXOP_WRITES
|SLAP_EXOP_HIDE
, pcache_exop_query_delete
,
3932 if ( code
!= LDAP_SUCCESS
) {
3933 Debug( LDAP_DEBUG_ANY
,
3934 "pcache_initialize: unable to register queryDelete exop: %d.\n",
3938 #endif /* PCACHE_EXOP_QUERY_DELETE */
3940 for ( i
= 0; as
[i
].desc
!= NULL
; i
++ ) {
3941 code
= register_at( as
[i
].desc
, as
[i
].adp
, 0 );
3943 Debug( LDAP_DEBUG_ANY
,
3944 "pcache_initialize: register_at #%d failed\n", i
, 0, 0 );
3947 (*as
[i
].adp
)->ad_type
->sat_flags
|= SLAP_AT_HIDE
;
3950 pcache
.on_bi
.bi_type
= "pcache";
3951 pcache
.on_bi
.bi_obsolete_names
= obsolete_names
;
3952 pcache
.on_bi
.bi_db_init
= pcache_db_init
;
3953 pcache
.on_bi
.bi_db_config
= pcache_db_config
;
3954 pcache
.on_bi
.bi_db_open
= pcache_db_open
;
3955 pcache
.on_bi
.bi_db_close
= pcache_db_close
;
3956 pcache
.on_bi
.bi_db_destroy
= pcache_db_destroy
;
3958 pcache
.on_bi
.bi_op_search
= pcache_op_search
;
3959 #ifdef PCACHE_CONTROL_PRIVDB
3960 pcache
.on_bi
.bi_op_bind
= pcache_op_privdb
;
3961 pcache
.on_bi
.bi_op_compare
= pcache_op_privdb
;
3962 pcache
.on_bi
.bi_op_modrdn
= pcache_op_privdb
;
3963 pcache
.on_bi
.bi_op_modify
= pcache_op_privdb
;
3964 pcache
.on_bi
.bi_op_add
= pcache_op_privdb
;
3965 pcache
.on_bi
.bi_op_delete
= pcache_op_privdb
;
3966 #endif /* PCACHE_CONTROL_PRIVDB */
3967 #ifdef PCACHE_EXOP_QUERY_DELETE
3968 pcache
.on_bi
.bi_extended
= pcache_op_extended
;
3969 #elif defined( PCACHE_CONTROL_PRIVDB )
3970 pcache
.on_bi
.bi_extended
= pcache_op_privdb
;
3973 pcache
.on_bi
.bi_chk_controls
= pcache_chk_controls
;
3975 pcache
.on_bi
.bi_cf_ocs
= pcocs
;
3977 code
= config_register_schema( pccfg
, pcocs
);
3978 if ( code
) return code
;
3982 code
= slap_str2ad( "olcDatabase", &pcdummy
[0].ad
, &text
);
3983 if ( code
) return code
;
3985 return overlay_register( &pcache
);
3988 #if SLAPD_OVER_PROXYCACHE == SLAPD_MOD_DYNAMIC
3989 int init_module(int argc
, char *argv
[]) {
3990 return pcache_initialize();
3994 #endif /* defined(SLAPD_OVER_PROXYCACHE) */