diff --git a/servers/slapd/overlays/accesslog.c b/servers/slapd/overlays/accesslog.c index 756fd54bca..2742fd0d95 100644 --- a/servers/slapd/overlays/accesslog.c +++ b/servers/slapd/overlays/accesslog.c @@ -76,6 +76,8 @@ typedef struct log_info { struct berval li_uuid; int li_success; log_base *li_bases; + BerVarray li_mincsn; + int *li_sids, li_numcsns; ldap_pvt_thread_mutex_t li_op_rmutex; ldap_pvt_thread_mutex_t li_log_mutex; } log_info; @@ -198,7 +200,8 @@ static AttributeDescription *ad_reqDN, *ad_reqStart, *ad_reqEnd, *ad_reqType, *ad_reqScope, *ad_reqFilter, *ad_reqAttr, *ad_reqEntries, *ad_reqSizeLimit, *ad_reqTimeLimit, *ad_reqAttrsOnly, *ad_reqData, *ad_reqId, *ad_reqMessage, *ad_reqVersion, *ad_reqDerefAliases, - *ad_reqReferral, *ad_reqOld, *ad_auditContext, *ad_reqEntryUUID; + *ad_reqReferral, *ad_reqOld, *ad_auditContext, *ad_reqEntryUUID, + *ad_minCSN; static int logSchemaControlValidate( @@ -411,6 +414,17 @@ static struct { "ORDERING UUIDOrderingMatch " "SYNTAX 1.3.6.1.1.16.1 " "SINGLE-VALUE )", &ad_reqEntryUUID }, + + /* + * ITS#8486 + */ + { "( " LOG_SCHEMA_AT ".32 NAME 'minCSN' " + "DESC 'CSN set that the logs are recorded from' " + "EQUALITY CSNMatch " + "ORDERING CSNOrderingMatch " + "SYNTAX 1.3.6.1.4.1.4203.666.11.2.1{64} " + "NO-USER-MODIFICATION " + "USAGE dSAOperation )", &ad_minCSN }, { NULL, NULL } }; @@ -587,36 +601,49 @@ static slap_callback nullsc; #define PURGE_INCREMENT 100 typedef struct purge_data { + struct log_info *li; int slots; int used; + int mincsn_updated; BerVarray dn; BerVarray ndn; - struct berval csn; /* an arbitrary old CSN */ } purge_data; static int log_old_lookup( Operation *op, SlapReply *rs ) { purge_data *pd = op->o_callback->sc_private; + struct log_info *li = pd->li; Attribute *a; if ( rs->sr_type != REP_SEARCH) return 0; if ( slapd_shutdown ) return 0; - /* Remember max CSN: should always be the last entry - * seen, since log entries are ordered chronologically... - */ + /* Update minCSN */ a = attr_find( rs->sr_entry->e_attrs, slap_schema.si_ad_entryCSN ); if ( a ) { ber_len_t len = a->a_nvals[0].bv_len; + int i, sid; + + /* Find the correct sid */ + sid = slap_parse_csn_sid( &a->a_nvals[0] ); + for ( i=0; i < li->li_numcsns; i++ ) { + if ( sid <= li->li_sids[i] ) break; + } + if ( i >= li->li_numcsns || sid != li->li_sids[i] ) { + Debug( LDAP_DEBUG_ANY, "log_old_lookup: " + "csn=%s with sid not in minCSN set!\n", + a->a_nvals[0].bv_val ); + } + /* Paranoid len check, normalized CSNs are always the same length */ - if ( len > LDAP_PVT_CSNSTR_BUFSIZE ) - len = LDAP_PVT_CSNSTR_BUFSIZE; - if ( memcmp( a->a_nvals[0].bv_val, pd->csn.bv_val, len ) > 0 ) { - AC_MEMCPY( pd->csn.bv_val, a->a_nvals[0].bv_val, len ); - pd->csn.bv_len = len; + if ( len > li->li_mincsn[i].bv_len ) + len = li->li_mincsn[i].bv_len; + if ( ber_bvcmp( &li->li_mincsn[i], &a->a_nvals[0] ) < 0 ) { + pd->mincsn_updated = 1; + AC_MEMCPY( li->li_mincsn[i].bv_val, a->a_nvals[0].bv_val, len ); } } if ( pd->used >= pd->slots ) { @@ -644,7 +671,7 @@ accesslog_purge( void *ctx, void *arg ) slap_callback cb = { NULL, log_old_lookup, NULL, NULL, NULL }; Filter f; AttributeAssertion ava = ATTRIBUTEASSERTION_INIT; - purge_data pd = {0}; + purge_data pd = { .li = li }; char timebuf[LDAP_LUTIL_GENTIME_BUFSIZE]; char csnbuf[LDAP_PVT_CSNSTR_BUFSIZE]; time_t old = slap_get_time(); @@ -679,9 +706,6 @@ accesslog_purge( void *ctx, void *arg ) op->ors_attrs = slap_anlist_no_attrs; op->ors_attrsonly = 1; - pd.csn.bv_len = sizeof( csnbuf ); - pd.csn.bv_val = csnbuf; - csnbuf[0] = '\0'; cb.sc_private = &pd; op->o_bd->be_search( op, &rs ); @@ -690,12 +714,37 @@ accesslog_purge( void *ctx, void *arg ) if ( pd.used ) { int i; - /* delete the expired entries */ - op->o_tag = LDAP_REQ_DELETE; op->o_callback = &nullsc; - op->o_csn = pd.csn; op->o_dont_replicate = 1; + if ( pd.mincsn_updated ) { + Modifications mod; + /* update context's minCSN to reflect oldest CSN */ + mod.sml_numvals = li->li_numcsns; + mod.sml_values = li->li_mincsn; + mod.sml_nvalues = NULL; + mod.sml_desc = ad_minCSN; + mod.sml_op = LDAP_MOD_REPLACE; + mod.sml_flags = SLAP_MOD_INTERNAL; + mod.sml_next = NULL; + + op->o_tag = LDAP_REQ_MODIFY; + op->orm_modlist = &mod; + op->orm_no_opattrs = 1; + op->o_req_dn = li->li_db->be_suffix[0]; + op->o_req_ndn = li->li_db->be_nsuffix[0]; + op->o_no_schema_check = 1; + op->o_managedsait = SLAP_CONTROL_NONCRITICAL; + if ( !slapd_shutdown ) { + Debug( LDAP_DEBUG_SYNC, "accesslog_purge: " + "updating minCSN with %d values\n", + li->li_numcsns ); + op->o_bd->be_modify( op, &rs ); + } + } + + /* delete the expired entries */ + op->o_tag = LDAP_REQ_DELETE; for (i=0; io_req_dn = pd.dn[i]; op->o_req_ndn = pd.ndn[i]; @@ -709,34 +758,6 @@ accesslog_purge( void *ctx, void *arg ) } ch_free( pd.ndn ); ch_free( pd.dn ); - - { - Modifications mod; - struct berval bv[2]; - rs_reinit( &rs, REP_RESULT ); - /* update context's entryCSN to reflect oldest CSN */ - mod.sml_numvals = 1; - mod.sml_values = bv; - bv[0] = pd.csn; - BER_BVZERO(&bv[1]); - mod.sml_nvalues = NULL; - mod.sml_desc = slap_schema.si_ad_entryCSN; - mod.sml_op = LDAP_MOD_REPLACE; - mod.sml_flags = SLAP_MOD_INTERNAL; - mod.sml_next = NULL; - - op->o_tag = LDAP_REQ_MODIFY; - op->orm_modlist = &mod; - op->orm_no_opattrs = 1; - op->o_req_dn = li->li_db->be_suffix[0]; - op->o_req_ndn = li->li_db->be_nsuffix[0]; - op->o_no_schema_check = 1; - op->o_managedsait = SLAP_CONTROL_NONCRITICAL; - op->o_bd->be_modify( op, &rs ); - if ( mod.sml_next ) { - slap_mods_free( mod.sml_next, 1 ); - } - } } ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex ); @@ -1886,6 +1907,59 @@ static int accesslog_response(Operation *op, SlapReply *rs) { if ( e == op2.ora_e ) entry_free( e ); e = NULL; + /* TODO: What to do about minCSN when we have an op without a CSN? */ + if ( !BER_BVISEMPTY( &op->o_csn ) ) { + Modifications mod; + int i, sid = slap_parse_csn_sid( &op->o_csn ); + + for ( i=0; i < li->li_numcsns; i++ ) { + if ( sid <= li->li_sids[i] ) break; + } + if ( i >= li->li_numcsns || sid != li->li_sids[i] ) { + /* SID not in minCSN set, add */ + struct berval bv[2]; + + Debug( LDAP_DEBUG_TRACE, "accesslog_response: " + "adding minCSN %s\n", + op->o_csn.bv_val ); + slap_insert_csn_sids( (struct sync_cookie *)&li->li_mincsn, i, + sid, &op->o_csn ); + + op2.o_tag = LDAP_REQ_MODIFY; + op2.o_req_dn = li->li_db->be_suffix[0]; + op2.o_req_ndn = li->li_db->be_nsuffix[0]; + + bv[0] = op->o_csn; + BER_BVZERO( &bv[1] ); + + mod.sml_numvals = 1; + mod.sml_values = bv; + mod.sml_nvalues = bv; + mod.sml_desc = ad_minCSN; + mod.sml_op = LDAP_MOD_ADD; + mod.sml_flags = SLAP_MOD_INTERNAL; + mod.sml_next = NULL; + + op2.orm_modlist = &mod; + op2.orm_no_opattrs = 1; + + Debug( LDAP_DEBUG_SYNC, "accesslog_response: " + "adding a new csn=%s into minCSN\n", + bv[0].bv_val ); + rs_reinit( &rs2, REP_RESULT ); + op2.o_bd->be_modify( &op2, &rs2 ); + if ( rs2.sr_err != LDAP_SUCCESS ) { + Debug( LDAP_DEBUG_SYNC, "accesslog_response: " + "got result 0x%x adding minCSN %s\n", + rs2.sr_err, op->o_csn.bv_val ); + } + } else if ( ber_bvcmp( &op->o_csn, &li->li_mincsn[i] ) < 0 ) { + Debug( LDAP_DEBUG_ANY, "accesslog_response: " + "csn=%s older than existing minCSN csn=%s for this sid\n", + op->o_csn.bv_val, li->li_mincsn[i].bv_val ); + } + } + done: if ( lo->mask & LOG_OP_WRITES ) ldap_pvt_thread_mutex_unlock( &li->li_log_mutex ); @@ -2162,7 +2236,7 @@ accesslog_db_destroy( return LDAP_SUCCESS; } -/* Create the logdb's root entry if it's missing */ +/* Create the logdb's root entry if it's missing, load mincsn */ static void * accesslog_db_root( void *ctx, @@ -2187,8 +2261,47 @@ accesslog_db_root( rc = be_entry_get_rw( op, li->li_db->be_nsuffix, NULL, NULL, 0, &e ); if ( e ) { - be_entry_release_rw( op, e, 0 ); + Attribute *a = attr_find( e->e_attrs, ad_minCSN ); + if ( !a ) { + /* TODO: find the lowest CSN we are safe to put in */ + a = attr_find( e->e_attrs, slap_schema.si_ad_contextCSN ); + if ( a ) { + SlapReply rs = {REP_RESULT}; + Modifications mod; + BackendDB db = *li->li_db; + op->o_bd = &db; + + mod.sml_numvals = a->a_numvals; + mod.sml_values = a->a_vals; + mod.sml_nvalues = a->a_nvals; + mod.sml_desc = ad_minCSN; + mod.sml_op = LDAP_MOD_REPLACE; + mod.sml_flags = SLAP_MOD_INTERNAL; + mod.sml_next = NULL; + + op->o_tag = LDAP_REQ_MODIFY; + op->o_req_dn = e->e_name; + op->o_req_ndn = e->e_nname; + op->o_callback = &nullsc; + SLAP_DBFLAGS( op->o_bd ) |= SLAP_DBFLAG_NOLASTMOD; + + Debug( LDAP_DEBUG_SYNC, "accesslog_db_root: " + "setting up minCSN with %d values\n", + a->a_numvals ); + + op->orm_modlist = &mod; + op->orm_no_opattrs = 1; + rc = op->o_bd->be_modify( op, &rs ); + } + } + if ( a ) { + ber_bvarray_dup_x( &li->li_mincsn, a->a_vals, NULL ); + li->li_numcsns = a->a_numvals; + li->li_sids = slap_parse_csn_sids( li->li_mincsn, li->li_numcsns, NULL ); + slap_sort_csn_sids( li->li_mincsn, li->li_sids, li->li_numcsns, NULL ); + } + be_entry_release_rw( op, e, 0 ); } else { SlapReply rs = {REP_RESULT}; struct berval rdn, nrdn, attr; @@ -2238,6 +2351,7 @@ accesslog_db_root( attr_merge_one( e, slap_schema.si_ad_entryCSN, &a->a_vals[0], &a->a_nvals[0] ); attr_merge( e, a->a_desc, a->a_vals, a->a_nvals ); + attr_merge( e, ad_minCSN, a->a_vals, a->a_nvals ); } be_entry_release_rw( op, e_ctx, 0 ); }