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