X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=servers%2Fslapd%2Foverlays%2Fpcache.c;h=6875b19fa7a62a1cbc0ab2244e1cbd58083c947d;hb=ee3f05000055619ec6d4eee48b57fa5cb6085127;hp=8c3209abb1718200ebe698074baf9d3a786e5618;hpb=a688867cae2da213bc187419dc8578750a1dda79;p=openldap diff --git a/servers/slapd/overlays/pcache.c b/servers/slapd/overlays/pcache.c index 8c3209abb1..6875b19fa7 100644 --- a/servers/slapd/overlays/pcache.c +++ b/servers/slapd/overlays/pcache.c @@ -1,7 +1,7 @@ /* $OpenLDAP$ */ /* This work is part of OpenLDAP Software . * - * Copyright 2003-2009 The OpenLDAP Foundation. + * Copyright 2003-2011 The OpenLDAP Foundation. * Portions Copyright 2003 IBM Corporation. * Portions Copyright 2003-2009 Symas Corporation. * All rights reserved. @@ -84,6 +84,7 @@ typedef struct cached_query_s { time_t expiry_time; /* time till the query is considered invalid */ time_t refresh_time; /* time till the query is refreshed */ time_t bindref_time; /* time till the bind is refreshed */ + int bind_refcnt; /* number of bind operation referencing this query */ unsigned long answerable_cnt; /* how many times it was answerable */ int refcnt; /* references since last refresh */ ldap_pvt_thread_mutex_t answerable_cnt_mutex; @@ -251,6 +252,7 @@ static int privDB_cid; static AttributeDescription *ad_queryId, *ad_cachedQueryURL; #ifdef PCACHE_MONITOR +static AttributeDescription *ad_numQueries, *ad_numEntries; static ObjectClass *oc_olmPCache; #endif /* PCACHE_MONITOR */ @@ -285,6 +287,24 @@ static struct { "NO-USER-MODIFICATION " "USAGE directoryOperation )", &ad_cachedQueryURL }, +#ifdef PCACHE_MONITOR + { "( PCacheAttributes:3 " + "NAME 'pcacheNumQueries' " + "DESC 'Number of cached queries' " + "EQUALITY integerMatch " + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 " + "NO-USER-MODIFICATION " + "USAGE directoryOperation )", + &ad_numQueries }, + { "( PCacheAttributes:4 " + "NAME 'pcacheNumEntries' " + "DESC 'Number of cached entries' " + "EQUALITY integerMatch " + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 " + "NO-USER-MODIFICATION " + "USAGE directoryOperation )", + &ad_numEntries }, +#endif /* PCACHE_MONITOR */ { NULL } }; @@ -299,8 +319,10 @@ static struct { "NAME ( 'olmPCache' ) " "SUP top AUXILIARY " "MAY ( " - "pcacheQueryURL" - ") )", + "pcacheQueryURL " + "$ pcacheNumQueries " + "$ pcacheNumEntries " + " ) )", &oc_olmPCache }, #endif /* PCACHE_MONITOR */ @@ -325,7 +347,6 @@ add_query( static int remove_query_data( Operation *op, - SlapReply *rs, struct berval *query_uuid ); /* @@ -425,7 +446,7 @@ ftemp_attrs( struct berval *ftemp, struct berval *template, AttributeDescription **descs = NULL; char *temp2; - temp2 = ch_malloc( ftemp->bv_len ); + temp2 = ch_malloc( ftemp->bv_len + 1 ); p1 = ftemp->bv_val; t1 = temp2; @@ -494,7 +515,7 @@ template_attrs( char *template, struct attr_set *set, AttributeName **ret, *ret = NULL; - attrs = ch_malloc(( set->count+1 )*sizeof(AttributeName)); + attrs = ch_calloc( set->count + 1, sizeof(AttributeName) ); for ( i=0; i < set->count; i++ ) attrs[i] = set->attrs[i]; attr_cnt = i; @@ -578,7 +599,7 @@ url2query( LDAPURLDesc *lud = NULL; struct berval base, tempstr = BER_BVNULL, - uuid; + uuid = BER_BVNULL; int attrset; time_t expiry_time; time_t refresh_time; @@ -654,9 +675,11 @@ url2query( } ber_str2bv( &lud->lud_exts[ i ][ STRLENOF( "x-uuid=" ) ], 0, 0, &tmpUUID ); - rc = syn_UUID->ssyn_pretty( syn_UUID, &tmpUUID, &uuid, NULL ); - if ( rc != LDAP_SUCCESS ) { - goto error; + if ( !BER_BVISEMPTY( &tmpUUID ) ) { + rc = syn_UUID->ssyn_pretty( syn_UUID, &tmpUUID, &uuid, NULL ); + if ( rc != LDAP_SUCCESS ) { + goto error; + } } got |= GOT_UUID; @@ -731,11 +754,10 @@ url2query( /* ignore expired queries */ if ( expiry_time <= slap_get_time()) { Operation op2 = *op; - SlapReply rs2 = { 0 }; memset( &op2.oq_search, 0, sizeof( op2.oq_search ) ); - (void)remove_query_data( &op2, &rs2, &uuid ); + (void)remove_query_data( &op2, &uuid ); rc = 0; @@ -747,6 +769,10 @@ url2query( } query.scope = lud->lud_scope; query.filter = str2filter( lud->lud_filter ); + if ( query.filter == NULL ) { + rc = -1; + goto error; + } tempstr.bv_val = ch_malloc( strlen( lud->lud_filter ) + 1 ); tempstr.bv_len = 0; @@ -802,6 +828,7 @@ static int merge_entry( Operation *op, Entry *e, + int dup, struct berval* query_uuid ) { int rc; @@ -815,6 +842,8 @@ merge_entry( slap_callback cb = { NULL, slap_null_cb, NULL, NULL }; + if ( dup ) + e = entry_dup( e ); attr = e->e_attrs; e->e_attrs = NULL; @@ -837,10 +866,12 @@ merge_entry( if ( rc != LDAP_SUCCESS ) { if ( rc == LDAP_ALREADY_EXISTS ) { + rs_reinit( &sreply, REP_RESULT ); slap_entry2mods( e, &modlist, &text, textbuf, textlen ); modlist->sml_op = LDAP_MOD_ADD; op->o_tag = LDAP_REQ_MODIFY; op->orm_modlist = modlist; + op->o_managedsait = SLAP_CONTROL_CRITICAL; op->o_bd->be_modify( op, &sreply ); slap_mods_free( modlist, 1 ); } else if ( rc == LDAP_REFERRAL || @@ -855,7 +886,7 @@ merge_entry( } } else { if ( op->ora_e == e ) - be_entry_release_w( op, e ); + entry_free( e ); rc = 1; } @@ -891,38 +922,49 @@ static int pcache_filter_cmp( Filter *f1, Filter *f2 ) int rc, weight1, weight2; switch( f1->f_choice ) { - case LDAP_FILTER_PRESENT: + case LDAP_FILTER_AND: + case LDAP_FILTER_OR: weight1 = 0; break; + case LDAP_FILTER_PRESENT: + weight1 = 1; + break; case LDAP_FILTER_EQUALITY: case LDAP_FILTER_GE: case LDAP_FILTER_LE: - weight1 = 1; + weight1 = 2; break; default: - weight1 = 2; + weight1 = 3; } switch( f2->f_choice ) { - case LDAP_FILTER_PRESENT: + case LDAP_FILTER_AND: + case LDAP_FILTER_OR: weight2 = 0; break; + case LDAP_FILTER_PRESENT: + weight2 = 1; + break; case LDAP_FILTER_EQUALITY: case LDAP_FILTER_GE: case LDAP_FILTER_LE: - weight2 = 1; + weight2 = 2; break; default: - weight2 = 2; + weight2 = 3; } rc = weight1 - weight2; if ( !rc ) { switch( weight1 ) { case 0: + rc = pcache_filter_cmp( f1->f_and, f2->f_and ); break; case 1: - rc = lex_bvcmp( &f1->f_av_value, &f2->f_av_value ); break; case 2: + rc = lex_bvcmp( &f1->f_av_value, &f2->f_av_value ); + break; + case 3: if ( f1->f_choice == LDAP_FILTER_SUBSTRINGS ) { rc = 0; if ( !BER_BVISNULL( &f1->f_sub_initial )) { @@ -963,7 +1005,7 @@ static int pcache_filter_cmp( Filter *f1, Filter *f2 ) } break; } - if ( !rc ) { + while ( !rc ) { f1 = f1->f_next; f2 = f2->f_next; if ( f1 || f2 ) { @@ -972,12 +1014,10 @@ static int pcache_filter_cmp( Filter *f1, Filter *f2 ) else if ( !f2 ) rc = 1; else { - while ( f1->f_choice == LDAP_FILTER_AND || f1->f_choice == LDAP_FILTER_OR ) - f1 = f1->f_and; - while ( f2->f_choice == LDAP_FILTER_AND || f2->f_choice == LDAP_FILTER_OR ) - f2 = f2->f_and; rc = pcache_filter_cmp( f1, f2 ); } + } else { + break; } } } @@ -988,7 +1028,7 @@ static int pcache_filter_cmp( Filter *f1, Filter *f2 ) static int pcache_query_cmp( const void *v1, const void *v2 ) { const CachedQuery *q1 = v1, *q2 =v2; - return pcache_filter_cmp( q1->first, q2->first ); + return pcache_filter_cmp( q1->filter, q2->filter ); } /* add query on top of LRU list */ @@ -1220,6 +1260,11 @@ filter_first( Filter *f ) return f; } +typedef struct fstack { + struct fstack *fs_next; + Filter *fs_fs; + Filter *fs_fi; +} fstack; static CachedQuery * find_filter( Operation *op, Avlnode *root, Filter *inputf, Filter *first ) @@ -1231,6 +1276,7 @@ find_filter( Operation *op, Avlnode *root, Filter *inputf, Filter *first ) int ret, rc, dir; Avlnode *ptr; CachedQuery cq, *qc; + fstack *stack = NULL, *fsp; cq.filter = inputf; cq.first = first; @@ -1306,6 +1352,14 @@ nextpass: eqpass = 1; switch (fs->f_choice) { case LDAP_FILTER_OR: case LDAP_FILTER_AND: + if ( fs->f_next ) { + /* save our stack position */ + fsp = op->o_tmpalloc(sizeof(fstack), op->o_tmpmemctx); + fsp->fs_next = stack; + fsp->fs_fs = fs->f_next; + fsp->fs_fi = fi->f_next; + stack = fsp; + } fs = fs->f_and; fi = fi->f_and; res=1; @@ -1355,6 +1409,14 @@ nextpass: eqpass = 1; default: break; } + if (!fs && !fi && stack) { + /* pop the stack */ + fsp = stack; + stack = fsp->fs_next; + fs = fsp->fs_fs; + fi = fsp->fs_fi; + op->o_tmpfree(fsp, op->o_tmpmemctx); + } } while((res) && (fi != NULL) && (fs != NULL)); if ( res ) @@ -1522,7 +1584,9 @@ add_query( } new_cached_query->expiry_time = now + ttl; new_cached_query->refresh_time = ttr; + new_cached_query->bindref_time = 0; + new_cached_query->bind_refcnt = 0; new_cached_query->answerable_cnt = 0; new_cached_query->refcnt = 1; ldap_pvt_thread_mutex_init(&new_cached_query->answerable_cnt_mutex); @@ -1570,6 +1634,10 @@ add_query( templ->query = new_cached_query; templ->no_of_queries++; } else { + ldap_pvt_thread_mutex_destroy(&new_cached_query->answerable_cnt_mutex); + if (wlock) + ldap_pvt_thread_rdwr_wunlock(&new_cached_query->rwlock); + ldap_pvt_thread_rdwr_destroy( &new_cached_query->rwlock ); ch_free( new_cached_query ); new_cached_query = find_filter( op, qbase->scopes[query->scope], query->filter, first ); @@ -1579,16 +1647,16 @@ add_query( Debug( pcache_debug, "TEMPLATE %p QUERIES++ %d\n", (void *) templ, templ->no_of_queries, 0 ); - Debug( pcache_debug, "Unlock AQ index = %p \n", - (void *) templ, 0, 0 ); - ldap_pvt_thread_rdwr_wunlock(&templ->t_rwlock); - /* Adding on top of LRU list */ if ( rc == 0 ) { ldap_pvt_thread_mutex_lock(&qm->lru_mutex); add_query_on_top(qm, new_cached_query); ldap_pvt_thread_mutex_unlock(&qm->lru_mutex); } + Debug( pcache_debug, "Unlock AQ index = %p \n", + (void *) templ, 0, 0 ); + ldap_pvt_thread_rdwr_wunlock(&templ->t_rwlock); + return rc == 0 ? new_cached_query : NULL; } @@ -1717,7 +1785,6 @@ remove_func ( static int remove_query_data( Operation *op, - SlapReply *rs, struct berval *query_uuid ) { struct query_info *qi, *qnext; @@ -1728,8 +1795,6 @@ remove_query_data( slap_callback cb = { NULL, remove_func, NULL, NULL }; int deleted = 0; - sreply.sr_entry = NULL; - sreply.sr_nentries = 0; op->ors_filterstr.bv_len = snprintf(filter_str, sizeof(filter_str), "(%s=%s)", ad_queryId->ad_cname.bv_val, query_uuid->bv_val); filter.f_ava = &ava; @@ -1762,6 +1827,7 @@ remove_query_data( op->o_req_dn = qi->xdn; op->o_req_ndn = qi->xdn; + rs_reinit( &sreply, REP_RESULT ); if ( qi->del ) { Debug( pcache_debug, "DELETING ENTRY TEMPLATE=%s\n", @@ -1940,7 +2006,6 @@ struct search_info { static void remove_query_and_data( Operation *op, - SlapReply *rs, cache_manager *cm, struct berval *uuid ) { @@ -1953,7 +2018,7 @@ remove_query_and_data( Debug( pcache_debug, "Removing query UUID %s\n", uuid->bv_val, 0, 0 ); - return_val = remove_query_data( op, rs, uuid ); + return_val = remove_query_data( op, uuid ); Debug( pcache_debug, "QUERY REMOVED, SIZE=%d\n", return_val, 0, 0); @@ -2003,11 +2068,7 @@ fetch_queryId_cb( Operation *op, SlapReply *rs ) } /* clear entry if required */ - if ( rs->sr_flags & REP_ENTRY_MUSTBEFREED ) { - entry_free( rs->sr_entry ); - rs->sr_entry = NULL; - rs->sr_flags ^= REP_ENTRY_MUSTBEFREED; - } + rs_flush_entry( op, rs, (slap_overinst *) op->o_bd->bd_info ); return rc; } @@ -2026,7 +2087,6 @@ pcache_remove_entries_from_cache( OperationBuffer opbuf; Operation op2; slap_callback sc = { 0 }; - SlapReply rs = { REP_RESULT }; Filter f = { 0 }; char filtbuf[ LDAP_LUTIL_UUIDSTR_BUFSIZE + STRLENOF( "(entryUUID=)" ) ]; AttributeAssertion ava = ATTRIBUTEASSERTION_INIT; @@ -2073,6 +2133,7 @@ pcache_remove_entries_from_cache( for ( s = 0; !BER_BVISNULL( &entryUUIDs[ s ] ); s++ ) { BerVarray vals = NULL; + SlapReply rs = { REP_RESULT }; op->ors_filterstr.bv_len = snprintf( filtbuf, sizeof( filtbuf ), "(entryUUID=%s)", entryUUIDs[ s ].bv_val ); @@ -2091,7 +2152,7 @@ pcache_remove_entries_from_cache( for ( i = 0; !BER_BVISNULL( &vals[ i ] ); i++ ) { struct berval val = vals[ i ]; - remove_query_and_data( op, &rs, cm, &val ); + remove_query_and_data( op, cm, &val ); if ( !BER_BVISNULL( &val ) && val.bv_val != vals[ i ].bv_val ) { ch_free( val.bv_val ); @@ -2116,12 +2177,11 @@ pcache_remove_query_from_cache( struct berval *queryid ) { Operation op2 = *op; - SlapReply rs2 = { 0 }; op2.o_bd = &cm->db; /* remove the selected query */ - remove_query_and_data( &op2, &rs2, cm, queryid ); + remove_query_and_data( &op2, cm, queryid ); return LDAP_SUCCESS; } @@ -2212,7 +2272,7 @@ pcache_remove_entry_queries_from_cache( for ( i = 0; !BER_BVISNULL( &vals[ i ] ); i++ ) { struct berval val = vals[ i ]; - remove_query_and_data( op, &rs, cm, &val ); + remove_query_and_data( op, cm, &val ); if ( !BER_BVISNULL( &val ) && val.bv_val != vals[ i ].bv_val ) { ch_free( val.bv_val ); @@ -2228,7 +2288,6 @@ pcache_remove_entry_queries_from_cache( static int cache_entries( Operation *op, - SlapReply *rs, struct berval *query_uuid ) { struct search_info *si = op->o_callback->sc_private; @@ -2260,10 +2319,10 @@ cache_entries( e->e_private = NULL; while ( cm->cur_entries > (cm->max_entries) ) { BER_BVZERO( &crp_uuid ); - remove_query_and_data( op_tmp, rs, cm, &crp_uuid ); + remove_query_and_data( op_tmp, cm, &crp_uuid ); } - return_val = merge_entry(op_tmp, e, query_uuid); + return_val = merge_entry(op_tmp, e, 0, query_uuid); ldap_pvt_thread_mutex_lock(&cm->cache_mutex); cm->cur_entries += return_val; Debug( pcache_debug, @@ -2360,8 +2419,7 @@ over:; entry_free(si->head); } } - op->o_callback = op->o_callback->sc_next; - op->o_tmpfree( cb, op->o_tmpmemctx ); + } else if ( si->caching_reason != PC_IGNORE ) { CachedQuery *qc = qm->addfunc(op, qm, &si->query, si->qtemp, si->caching_reason, 1 ); @@ -2369,9 +2427,11 @@ over:; if ( qc != NULL ) { switch ( si->caching_reason ) { case PC_POSITIVE: - cache_entries( op, rs, &qc->q_uuid ); - if ( si->pbi ) + cache_entries( op, &qc->q_uuid ); + if ( si->pbi ) { + qc->bind_refcnt++; si->pbi->bi_cq = qc; + } break; case PC_SIZELIMIT: @@ -2417,6 +2477,9 @@ over:; } else { filter_free( si->query.filter ); } + + op->o_callback = op->o_callback->sc_next; + op->o_tmpfree( cb, op->o_tmpmemctx ); } return SLAP_CB_CONTINUE; @@ -2449,7 +2512,14 @@ pcache_response( } else if ( rs->sr_err == LDAP_SIZELIMIT_EXCEEDED && si->qtemp->limitttl ) { + Entry *e; + si->caching_reason = PC_SIZELIMIT; + for (;si->head; si->head=e) { + e = si->head->e_private; + si->head->e_private = NULL; + entry_free(si->head); + } } } else if ( si->qtemp->negttl && !si->count && !si->over && @@ -2503,11 +2573,58 @@ pcache_chk_controls( return rs->sr_err; } +static int +pc_setpw( Operation *op, struct berval *pwd, cache_manager *cm ) +{ + struct berval vals[2]; + + { + const char *text = NULL; + BER_BVZERO( &vals[0] ); + slap_passwd_hash( pwd, &vals[0], &text ); + if ( BER_BVISEMPTY( &vals[0] )) { + Debug( pcache_debug, "pc_setpw: hash failed %s\n", + text, 0, 0 ); + return LDAP_OTHER; + } + } + + BER_BVZERO( &vals[1] ); + + { + Modifications mod; + SlapReply sr = { REP_RESULT }; + slap_callback cb = { 0, slap_null_cb, 0, 0 }; + int rc; + + mod.sml_op = LDAP_MOD_REPLACE; + mod.sml_flags = 0; + mod.sml_desc = slap_schema.si_ad_userPassword; + mod.sml_type = mod.sml_desc->ad_cname; + mod.sml_values = vals; + mod.sml_nvalues = NULL; + mod.sml_numvals = 1; + mod.sml_next = NULL; + + op->o_tag = LDAP_REQ_MODIFY; + op->orm_modlist = &mod; + op->o_bd = &cm->db; + op->o_dn = op->o_bd->be_rootdn; + op->o_ndn = op->o_bd->be_rootndn; + op->o_callback = &cb; + Debug( pcache_debug, "pc_setpw: CACHING BIND for %s\n", + op->o_req_dn.bv_val, 0, 0 ); + rc = op->o_bd->be_modify( op, &sr ); + ch_free( vals[0].bv_val ); + return rc; + } +} + typedef struct bindcacheinfo { slap_overinst *on; CachedQuery *qc; } bindcacheinfo; - + static int pc_bind_save( Operation *op, SlapReply *rs ) { @@ -2515,42 +2632,20 @@ pc_bind_save( Operation *op, SlapReply *rs ) bindcacheinfo *bci = op->o_callback->sc_private; slap_overinst *on = bci->on; cache_manager *cm = on->on_bi.bi_private; - struct berval vals[2]; - const char *text = NULL; + CachedQuery *qc = bci->qc; + int delete = 0; - BER_BVZERO( &vals[0] ); - BER_BVZERO( &vals[1] ); - slap_passwd_hash( &op->orb_cred, &vals[0], &text ); - if ( BER_BVISEMPTY( &vals[0] )) { - Debug( pcache_debug, "pcache_bind_save: hash failed %s\n", - text, 0, 0 ); - } else { + ldap_pvt_thread_rdwr_wlock( &qc->rwlock ); + if ( qc->bind_refcnt-- ) { Operation op2 = *op; - Modifications mod; - SlapReply sr = { REP_RESULT }; - slap_callback cb = { 0, slap_null_cb, 0, 0 }; - - mod.sml_op = LDAP_MOD_REPLACE; - mod.sml_flags = 0; - mod.sml_desc = slap_schema.si_ad_userPassword; - mod.sml_type = mod.sml_desc->ad_cname; - mod.sml_values = vals; - mod.sml_nvalues = NULL; - mod.sml_numvals = 1; - mod.sml_next = NULL; - - op2.o_tag = LDAP_REQ_MODIFY; - op2.orm_modlist = &mod; - op2.o_bd = &cm->db; - op2.o_dn = op2.o_bd->be_rootdn; - op2.o_ndn = op2.o_bd->be_rootndn; - op2.o_callback = &cb; - Debug( pcache_debug, "pcache_bind_save: caching bind for %s\n", - op->o_req_dn.bv_val, 0, 0 ); - if ( op2.o_bd->be_modify( &op2, &sr ) == LDAP_SUCCESS ) + if ( pc_setpw( &op2, &op->orb_cred, cm ) == LDAP_SUCCESS ) bci->qc->bindref_time = op->o_time + bci->qc->qtemp->bindttr; - ch_free( vals[0].bv_val ); + } else { + bci->qc = NULL; + delete = 1; } + ldap_pvt_thread_rdwr_wunlock( &qc->rwlock ); + if ( delete ) free_query(qc); } return SLAP_CB_CONTINUE; } @@ -2561,7 +2656,7 @@ pc_bind_attrs( Operation *op, Entry *e, QueryTemplate *temp, { int i, len = 0; struct berval *vals, pres = BER_BVC("*"); - char *p1, *p2, *t1; + char *p1, *p2; Attribute *a; vals = op->o_tmpalloc( temp->bindnattrs * sizeof( struct berval ), @@ -2593,7 +2688,14 @@ pc_bind_attrs( Operation *op, Entry *e, QueryTemplate *temp, } *p2 = '\0'; op->o_tmpfree( vals, op->o_tmpmemctx ); - return str2filter_x( op, fbv->bv_val ); + + /* FIXME: are we sure str2filter_x can't fail? + * caller needs to check */ + { + Filter *f = str2filter_x( op, fbv->bv_val ); + assert( f != NULL ); + return f; + } } /* Check if the requested entry is from the cache and has a valid @@ -2657,78 +2759,97 @@ pc_bind_resp( Operation *op, SlapReply *rs ) return SLAP_CB_CONTINUE; } +#ifdef PCACHE_CONTROL_PRIVDB static int -pcache_op_bind( +pcache_op_privdb( Operation *op, SlapReply *rs ) { slap_overinst *on = (slap_overinst *)op->o_bd->bd_info; cache_manager *cm = on->on_bi.bi_private; - QueryTemplate *temp; - Entry *e; - slap_callback cb = { 0 }, *sc; - bindinfo bi; - bindcacheinfo *bci; - Operation op2; - int is_priv = 0, rc; - -#ifdef PCACHE_CONTROL_PRIVDB - if ( op->o_ctrlflag[ privDB_cid ] == SLAP_CONTROL_CRITICAL ) - is_priv = 1; -#endif + slap_callback *save_cb; + slap_op_t type; - /* Skip if we're not configured for Binds, and no priv control */ - if ( !cm->cache_binds && !is_priv ) + /* skip if control is unset */ + if ( op->o_ctrlflag[ privDB_cid ] != SLAP_CONTROL_CRITICAL ) { return SLAP_CB_CONTINUE; + } /* The cache DB isn't open yet */ if ( cm->defer_db_open ) { - if ( !is_priv ) { - /* Just passthru for now */ - return SLAP_CB_CONTINUE; - } else { - send_ldap_error( op, rs, LDAP_UNAVAILABLE, - "pcachePrivDB: cacheDB not available" ); - return rs->sr_err; - } + send_ldap_error( op, rs, LDAP_UNAVAILABLE, + "pcachePrivDB: cacheDB not available" ); + return rs->sr_err; } - if ( is_priv ) { - int rc; - slap_callback *save_cb; + /* FIXME: might be a little bit exaggerated... */ + if ( !be_isroot( op ) ) { + save_cb = op->o_callback; + op->o_callback = NULL; + send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM, + "pcachePrivDB: operation not allowed" ); + op->o_callback = save_cb; - /* FIXME: might be a little bit exaggerated... */ - if ( !be_isroot( op ) ) { - save_cb = op->o_callback; - op->o_callback = NULL; - send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM, - "pcachePrivDB: operation not allowed" ); - op->o_callback = save_cb; + return rs->sr_err; + } - return rs->sr_err; - } + /* map tag to operation */ + type = slap_req2op( op->o_tag ); + if ( type != SLAP_OP_LAST ) { + BI_op_func **func; + int rc; /* execute, if possible */ - if ( cm->db.be_bind != NULL ) { - op2 = *op; + func = &cm->db.be_bind; + if ( func[ type ] != NULL ) { + Operation op2 = *op; + op2.o_bd = &cm->db; - rc = op2.o_bd->be_bind( &op2, rs ); - if ( rc == LDAP_SUCCESS ) { + rc = func[ type ]( &op2, rs ); + if ( type == SLAP_OP_BIND && rc == LDAP_SUCCESS ) { op->o_conn->c_authz_cookie = cm->db.be_private; } - } else { - /* otherwise fall back to error */ - save_cb = op->o_callback; - op->o_callback = NULL; - send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM, - "operation not supported with pcachePrivDB control" ); - op->o_callback = save_cb; - } - return rs->sr_err; + return rs->sr_err; + } } + /* otherwise fall back to error */ + save_cb = op->o_callback; + op->o_callback = NULL; + send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM, + "operation not supported with pcachePrivDB control" ); + op->o_callback = save_cb; + + return rs->sr_err; +} +#endif /* PCACHE_CONTROL_PRIVDB */ + +static int +pcache_op_bind( + Operation *op, + SlapReply *rs ) +{ + slap_overinst *on = (slap_overinst *)op->o_bd->bd_info; + cache_manager *cm = on->on_bi.bi_private; + QueryTemplate *temp; + Entry *e; + slap_callback cb = { 0 }, *sc; + bindinfo bi; + bindcacheinfo *bci; + Operation op2; + int rc; + +#ifdef PCACHE_CONTROL_PRIVDB + if ( op->o_ctrlflag[ privDB_cid ] == SLAP_CONTROL_CRITICAL ) + return pcache_op_privdb( op, rs ); +#endif /* PCACHE_CONTROL_PRIVDB */ + + /* Skip if we're not configured for Binds, or cache DB isn't open yet */ + if ( !cm->cache_binds || cm->defer_db_open ) + return SLAP_CB_CONTINUE; + /* First find a matching template with Bind info */ for ( temp=cm->qm->templates; temp; temp=temp->qmnext ) { if ( temp->bindttr && dnIsSuffix( &op->o_req_ndn, &temp->bindbase )) @@ -2751,9 +2872,6 @@ pcache_op_bind( e = NULL; rc = be_entry_get_rw( &op2, &op->o_req_ndn, NULL, NULL, 0, &e ); if ( rc == LDAP_SUCCESS && e ) { - int i; - Attribute *a; - bi.bi_flags |= BI_LOOKUP; op2.ors_filter = pc_bind_attrs( op, e, temp, &op2.ors_filterstr ); be_entry_release_r( &op2, e ); @@ -2792,11 +2910,21 @@ pcache_op_bind( if ( bi.bi_flags & BI_HASHED ) { BackendDB *be = op->o_bd; op->o_bd = &cm->db; + int delete = 0; + + Debug( pcache_debug, "pcache_op_bind: CACHED BIND for %s\n", + op->o_req_dn.bv_val, 0, 0 ); if ( op->o_bd->be_bind( op, rs ) == LDAP_SUCCESS ) { op->o_conn->c_authz_cookie = cm->db.be_private; } op->o_bd = be; + ldap_pvt_thread_rdwr_wlock( &bi.bi_cq->rwlock ); + if ( !bi.bi_cq->bind_refcnt-- ) { + delete = 1; + } + ldap_pvt_thread_rdwr_wunlock( &bi.bi_cq->rwlock ); + if ( delete ) free_query( bi.bi_cq ); return rs->sr_err; } @@ -2816,72 +2944,7 @@ pcache_op_bind( return SLAP_CB_CONTINUE; } -#ifdef PCACHE_CONTROL_PRIVDB -static int -pcache_op_privdb( - Operation *op, - SlapReply *rs ) -{ - slap_overinst *on = (slap_overinst *)op->o_bd->bd_info; - cache_manager *cm = on->on_bi.bi_private; - slap_callback *save_cb; - slap_op_t type; - - /* skip if control is unset */ - if ( op->o_ctrlflag[ privDB_cid ] != SLAP_CONTROL_CRITICAL ) { - return SLAP_CB_CONTINUE; - } - - /* The cache DB isn't open yet */ - if ( cm->defer_db_open ) { - send_ldap_error( op, rs, LDAP_UNAVAILABLE, - "pcachePrivDB: cacheDB not available" ); - return rs->sr_err; - } - - /* FIXME: might be a little bit exaggerated... */ - if ( !be_isroot( op ) ) { - save_cb = op->o_callback; - op->o_callback = NULL; - send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM, - "pcachePrivDB: operation not allowed" ); - op->o_callback = save_cb; - - return rs->sr_err; - } - - /* map tag to operation */ - type = slap_req2op( op->o_tag ); - if ( type != SLAP_OP_LAST ) { - BI_op_func **func; - int rc; - - /* execute, if possible */ - func = &cm->db.be_bind; - if ( func[ type ] != NULL ) { - Operation op2 = *op; - - op2.o_bd = &cm->db; - - rc = func[ type ]( &op2, rs ); - if ( type == SLAP_OP_BIND && rc == LDAP_SUCCESS ) { - op->o_conn->c_authz_cookie = cm->db.be_private; - } - - return rs->sr_err; - } - } - - /* otherwise fall back to error */ - save_cb = op->o_callback; - op->o_callback = NULL; - send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM, - "operation not supported with pcachePrivDB control" ); - op->o_callback = save_cb; - - return rs->sr_err; -} -#endif /* PCACHE_CONTROL_PRIVDB */ +static slap_response refresh_merge; static int pcache_op_search( @@ -2921,7 +2984,8 @@ pcache_op_search( cm->db.be_acl = op->o_bd->be_acl; { - /* See if we're processing a Bind request */ + /* See if we're processing a Bind request + * or a cache refresh */ slap_callback *cb = op->o_callback; for ( ; cb; cb=cb->sc_next ) { @@ -2929,6 +2993,10 @@ pcache_op_search( pbi = cb->sc_private; break; } + if ( cb->sc_response == refresh_merge ) { + /* This is a refresh, do not search the cache */ + return SLAP_CB_CONTINUE; + } } } @@ -2997,28 +3065,40 @@ pcache_op_search( answerable->answerable_cnt, 0, 0 ); ldap_pvt_thread_mutex_unlock( &answerable->answerable_cnt_mutex ); - ldap_pvt_thread_rdwr_rlock(&answerable->rwlock); + ldap_pvt_thread_rdwr_wlock(&answerable->rwlock); if ( BER_BVISNULL( &answerable->q_uuid )) { /* No entries cached, just an empty result set */ i = rs->sr_err = 0; send_ldap_result( op, rs ); } else { /* Let Bind know we used a cached query */ - if ( pbi ) + if ( pbi ) { + answerable->bind_refcnt++; pbi->bi_cq = answerable; + } op->o_bd = &cm->db; -#if 0 if ( cm->response_cb == PCACHE_RESPONSE_CB_TAIL ) { + slap_callback cb; /* The cached entry was already processed by any * other overlays, so don't let it get processed again. + * + * This loop removes over_back_response from the stack. */ - op->o_callback = NULL; + if ( overlay_callback_after_backover( op, &cb, 0) == 0 ) { + slap_callback **scp; + for ( scp = &op->o_callback; *scp != NULL; + scp = &(*scp)->sc_next ) { + if ( (*scp)->sc_next == &cb ) { + *scp = cb.sc_next; + break; + } + } + } } -#endif i = cm->db.bd_info->bi_op_search( op, rs ); } - ldap_pvt_thread_rdwr_runlock(&answerable->rwlock); + ldap_pvt_thread_rdwr_wunlock(&answerable->rwlock); /* locked by qtemp->qcfunc (query_containment) */ ldap_pvt_thread_rdwr_runlock(&qtemp->t_rwlock); op->o_bd = save_bd; @@ -3100,19 +3180,30 @@ get_attr_set( query_manager* qm, int num ) { - int i; + int i = 0; int count = 0; if ( attrs ) { - for ( ; attrs[count].an_name.bv_val; count++ ); + for ( ; attrs[i].an_name.bv_val; i++ ) { + /* only count valid attribute names + * (searches ignore others, this overlay does the same) */ + if ( attrs[i].an_desc ) { + count++; + } + } } - /* recognize a single "*" or a "1.1" */ - if ( count == 0 ) { + /* recognize default or explicit single "*" */ + if ( ! attrs || + ( i == 1 && bvmatch( &attrs[0].an_name, slap_bv_all_user_attrs ) ) ) + { count = 1; attrs = slap_anlist_all_user_attributes; - } else if ( count == 1 && bvmatch( &attrs[0].an_name, slap_bv_no_attrs ) ) { + /* recognize implicit (no valid attributes) or explicit single "1.1" */ + } else if ( count == 0 || + ( i == 1 && bvmatch( &attrs[0].an_name, slap_bv_no_attrs ) ) ) + { count = 0; attrs = NULL; } @@ -3133,6 +3224,8 @@ get_attr_set( } for ( a2 = attrs; a2->an_name.bv_val; a2++ ) { + if ( !a2->an_desc && !bvmatch( &a2->an_name, slap_bv_all_user_attrs ) ) continue; + if ( !an_find( qm->attr_sets[i].attrs, &a2->an_name ) ) { found = 0; break; @@ -3187,7 +3280,6 @@ refresh_merge( Operation *op, SlapReply *rs ) { if ( rs->sr_type == REP_SEARCH ) { refresh_info *ri = op->o_callback->sc_private; - BackendDB *be = op->o_bd; Entry *e; dnlist *dnl; slap_callback *ocb; @@ -3201,7 +3293,7 @@ refresh_merge( Operation *op, SlapReply *rs ) /* No local entry, just add it. FIXME: we are not checking * the cache entry limit here */ - merge_entry( op, rs->sr_entry, &ri->ri_q->q_uuid ); + merge_entry( op, rs->sr_entry, 1, &ri->ri_q->q_uuid ); } else { /* Entry exists, update it */ Entry ne; @@ -3234,6 +3326,7 @@ refresh_merge( Operation *op, SlapReply *rs ) slap_mods_free( modlist, 1 ); /* mods is NULL if there are no changes */ if ( mods ) { + SlapReply rs2 = { REP_RESULT }; struct berval dn = op->o_req_dn; struct berval ndn = op->o_req_ndn; op->o_tag = LDAP_REQ_MODIFY; @@ -3241,7 +3334,9 @@ refresh_merge( Operation *op, SlapReply *rs ) op->o_req_dn = rs->sr_entry->e_name; op->o_req_ndn = rs->sr_entry->e_nname; op->o_callback = &cb; - op->o_bd->be_modify( op, rs ); + op->o_bd->be_modify( op, &rs2 ); + rs->sr_err = rs2.sr_err; + rs_assert_done( &rs2 ); slap_mods_free( mods, 1 ); op->o_req_dn = dn; op->o_req_ndn = ndn; @@ -3295,9 +3390,9 @@ refresh_purge( Operation *op, SlapReply *rs ) } static int -refresh_query( Operation *op, SlapReply *rs, CachedQuery *query, - slap_overinst *on ) +refresh_query( Operation *op, CachedQuery *query, slap_overinst *on ) { + SlapReply rs = {REP_RESULT}; slap_callback cb = { 0 }; refresh_info ri = { 0 }; char filter_str[ LDAP_LUTIL_UUIDSTR_BUFSIZE + STRLENOF( "(pcacheQueryID=)" ) ]; @@ -3335,7 +3430,7 @@ refresh_query( Operation *op, SlapReply *rs, CachedQuery *query, op->ors_attrsonly = 0; op->o_bd = on->on_info->oi_origdb; - rc = op->o_bd->be_search( op, rs ); + rc = op->o_bd->be_search( op, &rs ); if ( rc ) { op->o_bd = ri.ri_be; goto leave; @@ -3358,17 +3453,17 @@ refresh_query( Operation *op, SlapReply *rs, CachedQuery *query, attrs[ 0 ].an_name = ad_queryId->ad_cname; op->ors_attrs = attrs; op->ors_attrsonly = 0; - rs->sr_entry = NULL; - rs->sr_nentries = 0; - rc = op->o_bd->be_search( op, rs ); + rs_reinit( &rs, REP_RESULT ); + rc = op->o_bd->be_search( op, &rs ); if ( rc ) goto leave; while (( dn = ri.ri_dels )) { op->o_req_dn = dn->dn; op->o_req_ndn = dn->dn; + rs_reinit( &rs, REP_RESULT ); if ( dn->delete ) { op->o_tag = LDAP_REQ_DELETE; - op->o_bd->be_delete( op, rs ); + op->o_bd->be_delete( op, &rs ); } else { Modifications mod; struct berval vals[2]; @@ -3386,7 +3481,7 @@ refresh_query( Operation *op, SlapReply *rs, CachedQuery *query, op->o_tag = LDAP_REQ_MODIFY; op->orm_modlist = &mod; - op->o_bd->be_modify( op, rs ); + op->o_bd->be_modify( op, &rs ); } ri.ri_dels = dn->next; op->o_tmpfree( dn, op->o_tmpmemctx ); @@ -3411,7 +3506,6 @@ consistency_check( OperationBuffer opbuf; Operation *op; - SlapReply rs = {REP_RESULT}; CachedQuery *query, *qprev; int return_val, pause = PCACHE_CC_PAUSED; QueryTemplate *templ; @@ -3457,7 +3551,7 @@ consistency_check( if ( query->refcnt ) query->expiry_time = op->o_time + templ->ttl; if ( query->expiry_time > op->o_time ) { - refresh_query( op, &rs, query, on ); + refresh_query( op, query, on ); continue; } } @@ -3475,8 +3569,8 @@ consistency_check( Debug( pcache_debug, "Unlock CR index = %p\n", (void *) templ, 0, 0 ); } - ldap_pvt_thread_rdwr_wunlock(&templ->t_rwlock); if ( !rem ) { + ldap_pvt_thread_rdwr_wunlock(&templ->t_rwlock); continue; } ldap_pvt_thread_mutex_lock(&qm->lru_mutex); @@ -3485,7 +3579,7 @@ consistency_check( if ( BER_BVISNULL( &query->q_uuid )) return_val = 0; else - return_val = remove_query_data(op, &rs, &query->q_uuid); + return_val = remove_query_data(op, &query->q_uuid); Debug( pcache_debug, "STALE QUERY REMOVED, SIZE=%d\n", return_val, 0, 0 ); ldap_pvt_thread_mutex_lock(&cm->cache_mutex); @@ -3498,7 +3592,15 @@ consistency_check( "STALE QUERY REMOVED, CACHE =" "%d entries\n", cm->cur_entries, 0, 0 ); - free_query(query); + ldap_pvt_thread_rdwr_wlock( &query->rwlock ); + if ( query->bind_refcnt-- ) { + rem = 0; + } else { + rem = 1; + } + ldap_pvt_thread_rdwr_wunlock( &query->rwlock ); + if ( rem ) free_query(query); + ldap_pvt_thread_rdwr_wunlock(&templ->t_rwlock); } else if ( !templ->ttr && query->expiry_time > ttl ) { /* We don't need to check for refreshes, and this * query's expiry is too new, and all subsequent queries @@ -3733,13 +3835,23 @@ pc_cf_gen( ConfigArgs *c ) /* count the attr length */ for ( attr_name = qm->attr_sets[i].attrs; attr_name->an_name.bv_val; attr_name++ ) + { bv.bv_len += attr_name->an_name.bv_len + 1; + if ( attr_name->an_desc && + ( attr_name->an_desc->ad_flags & SLAP_DESC_TEMPORARY ) ) { + bv.bv_len += STRLENOF("undef:"); + } + } bv.bv_val = ch_malloc( bv.bv_len+1 ); ptr = lutil_strcopy( bv.bv_val, c->cr_msg ); for ( attr_name = qm->attr_sets[i].attrs; attr_name->an_name.bv_val; attr_name++ ) { *ptr++ = ' '; + if ( attr_name->an_desc && + ( attr_name->an_desc->ad_flags & SLAP_DESC_TEMPORARY ) ) { + ptr = lutil_strcopy( ptr, "undef:" ); + } ptr = lutil_strcopy( ptr, attr_name->an_name.bv_val ); } ber_bvarray_add( &c->rvalue_vals, &bv ); @@ -3778,8 +3890,8 @@ pc_cf_gen( ConfigArgs *c ) temp->attr_set_index, temp->bindttr, ldap_pvt_scope2str( temp->bindscope )); - bv.bv_len += temp->bindbase.bv_len + temp->bindftemp.bv_len + 3; - bv.bv_val = ch_malloc( bv.bv_len+1 ); + bv.bv_len += temp->bindbase.bv_len + temp->bindftemp.bv_len + 4; + bv.bv_val = ch_malloc( bv.bv_len + 1 ); ptr = bv.bv_val; *ptr++ = '"'; ptr = lutil_strcopy( ptr, temp->bindftemp.bv_val ); @@ -3995,7 +4107,12 @@ pc_cf_gen( ConfigArgs *c ) all_op = 1; BER_BVSTR( &attr_name->an_name, LDAP_ALL_OPERATIONAL_ATTRIBUTES ); } else { - if ( slap_str2ad( c->argv[i], &attr_name->an_desc, &text ) ) { + if ( strncasecmp( c->argv[i], "undef:", STRLENOF("undef:") ) == 0 ) { + struct berval bv; + ber_str2bv( c->argv[i] + STRLENOF("undef:"), 0, 0, &bv ); + attr_name->an_desc = slap_bv2tmp_ad( &bv, NULL ); + + } else if ( slap_str2ad( c->argv[i], &attr_name->an_desc, &text ) ) { strcpy( c->cr_msg, text ); Debug( LDAP_DEBUG_CONFIG, "%s: %s.\n", c->log, c->cr_msg, 0 ); ch_free( qm->attr_sets[num].attrs ); @@ -4216,7 +4333,7 @@ pc_bind_fail: i = 0; while ((eq = strchr(eq, '=' ))) { eq++; - if ( eq[1] == ')' ) + if ( eq[0] == ')' ) i++; } bv.bv_len = temp->bindftemp.bv_len + i; @@ -4388,9 +4505,13 @@ pcache_db_init( cm->check_cacheability = 0; cm->response_cb = PCACHE_RESPONSE_CB_TAIL; cm->defer_db_open = 1; + cm->cache_binds = 0; cm->cc_period = 1000; cm->cc_paused = 0; cm->cc_arg = NULL; +#ifdef PCACHE_MONITOR + cm->monitor_cb = NULL; +#endif /* PCACHE_MONITOR */ qm->attr_sets = NULL; qm->templates = NULL; @@ -4490,13 +4611,13 @@ pcache_db_open2( OperationBuffer opbuf; Operation *op; slap_callback cb = { 0 }; - SlapReply rs = { 0 }; + SlapReply rs = { REP_RESULT }; BerVarray vals = NULL; Filter f = { 0 }, f2 = { 0 }; AttributeAssertion ava = ATTRIBUTEASSERTION_INIT; AttributeName attrs[ 2 ] = {{{ 0 }}}; - connection_fake_init( &conn, &opbuf, thrctx ); + connection_fake_init2( &conn, &opbuf, thrctx, 0 ); op = &opbuf.ob_op; op->o_bd = &cm->db; @@ -4520,7 +4641,7 @@ pcache_db_open2( op->ors_slimit = 1; op->ors_tlimit = SLAP_NO_LIMIT; op->ors_limit = NULL; - ber_str2bv( "(cachedQueryURL=*)", 0, 0, &op->ors_filterstr ); + ber_str2bv( "(pcacheQueryURL=*)", 0, 0, &op->ors_filterstr ); f.f_choice = LDAP_FILTER_PRESENT; f.f_desc = ad_cachedQueryURL; op->ors_filter = &f; @@ -4555,8 +4676,8 @@ pcache_db_open2( op->ors_scope = LDAP_SCOPE_SUBTREE; op->ors_attrs = slap_anlist_no_attrs; + rs_reinit( &rs, REP_RESULT ); op->o_callback->sc_response = pcache_cachedquery_count_cb; - rs.sr_nentries = 0; op->o_callback->sc_private = &rs.sr_nentries; rc = op->o_bd->be_search( op, &rs ); @@ -4682,9 +4803,10 @@ pcache_db_close( thrctx = ldap_pvt_thread_pool_context(); - connection_fake_init( &conn, &opbuf, thrctx ); + connection_fake_init2( &conn, &opbuf, thrctx, 0 ); op = &opbuf.ob_op; + mod.sml_numvals = 0; if ( qm->templates != NULL ) { for ( tm = qm->templates; tm != NULL; tm = tm->qmnext ) { for ( qc = tm->query; qc; qc = qc->next ) { @@ -4692,6 +4814,7 @@ pcache_db_close( if ( query2url( op, qc, &bv, 0 ) == 0 ) { ber_bvarray_add_x( &vals, &bv, op->o_tmpmemctx ); + mod.sml_numvals++; } } } @@ -4718,7 +4841,6 @@ pcache_db_close( mod.sml_type = ad_cachedQueryURL->ad_cname; mod.sml_values = vals; mod.sml_nvalues = NULL; - mod.sml_numvals = 1; mod.sml_next = NULL; Debug( pcache_debug, "%sSETTING CACHED QUERY URLS\n", @@ -4758,7 +4880,19 @@ pcache_db_close( free( tm ); } - for ( i=0; inumattrsets; i++ ) { + for ( i = 0; i < cm->numattrsets; i++ ) { + int j; + + /* Account of LDAP_NO_ATTRS */ + if ( !qm->attr_sets[i].count ) continue; + + for ( j = 0; !BER_BVISNULL( &qm->attr_sets[i].attrs[j].an_name ); j++ ) { + if ( qm->attr_sets[i].attrs[j].an_desc && + ( qm->attr_sets[i].attrs[j].an_desc->ad_flags & + SLAP_DESC_TEMPORARY ) ) { + slap_sl_mfuncs.bmf_free( qm->attr_sets[i].attrs[j].an_desc, NULL ); + } + } free( qm->attr_sets[i].attrs ); } free( qm->attr_sets ); @@ -4842,6 +4976,7 @@ static char *extops[] = { }; #endif /* PCACHE_CONTROL_PRIVDB */ +static struct berval pcache_exop_MODIFY_PASSWD = BER_BVC( LDAP_EXOP_MODIFY_PASSWD ); #ifdef PCACHE_EXOP_QUERY_DELETE static struct berval pcache_exop_QUERY_DELETE = BER_BVC( PCACHE_EXOP_QUERY_DELETE ); @@ -5086,6 +5221,10 @@ pcache_exop_query_delete( op->o_req_dn = op->o_req_ndn; op->o_bd = select_backend( &op->o_req_ndn, 0 ); + if ( op->o_bd == NULL ) { + send_ldap_error( op, rs, LDAP_NO_SUCH_OBJECT, + "no global superior knowledge" ); + } rs->sr_err = backend_check_restrictions( op, rs, (struct berval *)&pcache_exop_QUERY_DELETE ); if ( rs->sr_err != LDAP_SUCCESS ) { @@ -5115,6 +5254,7 @@ done:; return rs->sr_err; } +#endif /* PCACHE_EXOP_QUERY_DELETE */ static int pcache_op_extended( Operation *op, SlapReply *rs ) @@ -5128,6 +5268,7 @@ pcache_op_extended( Operation *op, SlapReply *rs ) } #endif /* PCACHE_CONTROL_PRIVDB */ +#ifdef PCACHE_EXOP_QUERY_DELETE if ( bvmatch( &op->ore_reqoid, &pcache_exop_QUERY_DELETE ) ) { struct berval uuid = BER_BVNULL; ber_tag_t tag = LBER_DEFAULT; @@ -5155,11 +5296,66 @@ pcache_op_extended( Operation *op, SlapReply *rs ) } op->o_tmpfree( uuid.bv_val, op->o_tmpmemctx ); + return rs->sr_err; } +#endif /* PCACHE_EXOP_QUERY_DELETE */ - return rs->sr_err; + /* We only care if we're configured for Bind caching */ + if ( bvmatch( &op->ore_reqoid, &pcache_exop_MODIFY_PASSWD ) && + cm->cache_binds ) { + /* See if the local entry exists and has a password. + * It's too much work to find the matching query, so + * we just see if there's a hashed password to update. + */ + Operation op2 = *op; + Entry *e = NULL; + int rc; + int doit = 0; + + op2.o_bd = &cm->db; + op2.o_dn = op->o_bd->be_rootdn; + op2.o_ndn = op->o_bd->be_rootndn; + rc = be_entry_get_rw( &op2, &op->o_req_ndn, NULL, + slap_schema.si_ad_userPassword, 0, &e ); + if ( rc == LDAP_SUCCESS && e ) { + /* See if a recognized password is hashed here */ + Attribute *a = attr_find( e->e_attrs, + slap_schema.si_ad_userPassword ); + if ( a && a->a_vals[0].bv_val[0] == '{' && + lutil_passwd_scheme( a->a_vals[0].bv_val )) { + doit = 1; + } + be_entry_release_r( &op2, e ); + } + + if ( doit ) { + rc = overlay_op_walk( op, rs, op_extended, on->on_info, + on->on_next ); + if ( rc == LDAP_SUCCESS ) { + req_pwdexop_s *qpw = &op->oq_pwdexop; + + /* We don't care if it succeeds or not */ + pc_setpw( &op2, &qpw->rs_new, cm ); + } + return rc; + } + } + return SLAP_CB_CONTINUE; +} + +static int +pcache_entry_release( Operation *op, Entry *e, int rw ) +{ + slap_overinst *on = (slap_overinst *)op->o_bd->bd_info; + cache_manager *cm = on->on_bi.bi_private; + BackendDB *db = op->o_bd; + int rc; + + op->o_bd = &cm->db; + rc = be_entry_release_rw( op, e, rw ); + op->o_bd = db; + return rc; } -#endif /* PCACHE_EXOP_QUERY_DELETE */ #ifdef PCACHE_MONITOR @@ -5176,8 +5372,6 @@ pcache_monitor_update( CachedQuery *qc; BerVarray vals = NULL; - assert( ad_cachedQueryURL != NULL ); - attr_delete( &e->e_attrs, ad_cachedQueryURL ); if ( ( SLAP_OPATTRS( rs->sr_attr_flags ) || ad_inlist( ad_cachedQueryURL, rs->sr_attrs ) ) && qm->templates != NULL ) @@ -5201,6 +5395,36 @@ pcache_monitor_update( } } + { + Attribute *a; + char buf[ SLAP_TEXT_BUFLEN ]; + struct berval bv; + + /* number of cached queries */ + a = attr_find( e->e_attrs, ad_numQueries ); + assert( a != NULL ); + + bv.bv_val = buf; + bv.bv_len = snprintf( buf, sizeof( buf ), "%lu", cm->num_cached_queries ); + + if ( a->a_nvals != a->a_vals ) { + ber_bvreplace( &a->a_nvals[ 0 ], &bv ); + } + ber_bvreplace( &a->a_vals[ 0 ], &bv ); + + /* number of cached entries */ + a = attr_find( e->e_attrs, ad_numEntries ); + assert( a != NULL ); + + bv.bv_val = buf; + bv.bv_len = snprintf( buf, sizeof( buf ), "%d", cm->cur_entries ); + + if ( a->a_nvals != a->a_vals ) { + ber_bvreplace( &a->a_nvals[ 0 ], &bv ); + } + ber_bvreplace( &a->a_vals[ 0 ], &bv ); + } + return SLAP_CB_CONTINUE; } @@ -5240,6 +5464,22 @@ pcache_monitor_free( textbuf, sizeof( textbuf ) ); /* don't care too much about return code... */ + /* remove attrs */ + mod.sm_values = NULL; + mod.sm_desc = ad_numQueries; + mod.sm_numvals = 0; + rc = modify_delete_values( e, &mod, 1, &text, + textbuf, sizeof( textbuf ) ); + /* don't care too much about return code... */ + + /* remove attrs */ + mod.sm_values = NULL; + mod.sm_desc = ad_numEntries; + mod.sm_numvals = 0; + rc = modify_delete_values( e, &mod, 1, &text, + textbuf, sizeof( textbuf ) ); + /* don't care too much about return code... */ + return SLAP_CB_CONTINUE; } @@ -5310,7 +5550,7 @@ pcache_monitor_db_open( BackendDB *be ) } /* alloc as many as required (plus 1 for objectClass) */ - a = attrs_alloc( 1 + 0 ); + a = attrs_alloc( 1 + 2 ); if ( a == NULL ) { rc = 1; goto cleanup; @@ -5320,6 +5560,18 @@ pcache_monitor_db_open( BackendDB *be ) attr_valadd( a, &oc_olmPCache->soc_cname, NULL, 1 ); next = a->a_next; + { + struct berval bv = BER_BVC( "0" ); + + next->a_desc = ad_numQueries; + attr_valadd( next, &bv, NULL, 1 ); + next = next->a_next; + + next->a_desc = ad_numEntries; + attr_valadd( next, &bv, NULL, 1 ); + next = next->a_next; + } + cb = ch_calloc( sizeof( monitor_callback_t ), 1 ); cb->mc_update = pcache_monitor_update; cb->mc_free = pcache_monitor_free; @@ -5370,7 +5622,7 @@ pcache_monitor_db_close( BackendDB *be ) if ( mi && &mi->bi_extra ) { mbe = mi->bi_extra; - mbe->unregister_entry_callback( NULL, + mbe->unregister_entry_callback( &cm->monitor_ndn, (monitor_callback_t *)cm->monitor_cb, NULL, 0, NULL ); } @@ -5489,12 +5741,9 @@ pcache_initialize() pcache.on_bi.bi_op_add = pcache_op_privdb; pcache.on_bi.bi_op_delete = pcache_op_privdb; #endif /* PCACHE_CONTROL_PRIVDB */ -#ifdef PCACHE_EXOP_QUERY_DELETE pcache.on_bi.bi_extended = pcache_op_extended; -#elif defined( PCACHE_CONTROL_PRIVDB ) - pcache.on_bi.bi_extended = pcache_op_privdb; -#endif + pcache.on_bi.bi_entry_release_rw = pcache_entry_release; pcache.on_bi.bi_chk_controls = pcache_chk_controls; pcache.on_bi.bi_cf_ocs = pcocs;