/* $OpenLDAP$ */
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
*
- * Copyright 2003-2009 The OpenLDAP Foundation.
+ * Copyright 2003-2010 The OpenLDAP Foundation.
* Portions Copyright 2003 by IBM Corporation.
* Portions Copyright 2003-2008 by Howard Chu, Symas Corporation.
* All rights reserved.
int cs_ref;
struct berval *cs_vals;
int *cs_sids;
-} cookie_state;
+ /* pending changes, not yet committed */
+ ldap_pvt_thread_mutex_t cs_pmutex;
+ int cs_pnum;
+ struct berval *cs_pvals;
+ int *cs_psids;
+} cookie_state;
+
#define SYNCDATA_DEFAULT 0 /* entries are plain LDAP entries */
#define SYNCDATA_ACCESSLOG 1 /* entries are accesslog format */
#define SYNCDATA_CHANGELOG 2 /* entries are changelog format */
struct berval si_base;
struct berval si_logbase;
struct berval si_filterstr;
+ Filter *si_filter;
struct berval si_logfilterstr;
struct berval si_contextdn;
int si_scope;
struct sync_cookie * );
static struct berval * slap_uuidstr_from_normalized(
struct berval *, struct berval *, void * );
+static int syncrepl_add_glue_ancestors(
+ Operation* op, Entry *e );
/* callback functions */
static int dn_callback( Operation *, SlapReply * );
{
ber_len_t ssf; /* ITS#5403, 3864 LDAP_OPT_X_SASL_SSF probably ought
to use sasl_ssf_t but currently uses ber_len_t */
- ldap_get_option( si->si_ld, LDAP_OPT_X_SASL_SSF, &ssf );
- op->o_sasl_ssf = ssf;
+ if ( ldap_get_option( si->si_ld, LDAP_OPT_X_SASL_SSF, &ssf )
+ == LDAP_SUCCESS )
+ op->o_sasl_ssf = ssf;
}
op->o_ssf = ( op->o_sasl_ssf > op->o_tls_ssf )
? op->o_sasl_ssf : op->o_tls_ssf;
si->si_syncCookie.ctxcsn, si->si_syncCookie.rid,
si->si_syncCookie.sid );
} else {
+ /* ITS#6367: recreate the cookie so it has our SID, not our peer's */
+ ch_free( si->si_syncCookie.octet_str.bv_val );
+ slap_compose_sync_cookie( NULL, &si->si_syncCookie.octet_str,
+ si->si_syncCookie.ctxcsn, si->si_syncCookie.rid,
+ si->si_syncCookie.sid );
/* Look for contextCSN from syncprov overlay. */
check_syncprov( op, si );
}
Modifications *modlist = NULL;
- int match, m;
+ int match, m, punlock = -1;
struct timeval *tout_p = NULL;
struct timeval tout = { 0, 0 };
ldap_get_entry_controls( si->si_ld, msg, &rctrls );
/* we can't work without the control */
if ( rctrls ) {
- LDAPControl **next;
+ LDAPControl **next = NULL;
/* NOTE: make sure we use the right one;
* a better approach would be to run thru
* the whole list and take care of all */
if ( ber_peek_tag( ber, &len ) == LDAP_TAG_SYNC_COOKIE ) {
ber_scanf( ber, /*"{"*/ "m}", &cookie );
- Debug( LDAP_DEBUG_SYNC, "do_syncrep2: cookie=%s\n",
- BER_BVISNULL( &cookie ) ? "" : cookie.bv_val, 0, 0 );
+ Debug( LDAP_DEBUG_SYNC, "do_syncrep2: %s cookie=%s\n",
+ si->si_ridtxt,
+ BER_BVISNULL( &cookie ) ? "" : cookie.bv_val, 0 );
if ( !BER_BVISNULL( &cookie ) ) {
ch_free( syncCookie.octet_str.bv_val );
slap_parse_sync_cookie( &syncCookie, NULL );
if ( syncCookie.ctxcsn ) {
int i, sid = slap_parse_csn_sid( syncCookie.ctxcsn );
+ check_syncprov( op, si );
for ( i =0; i<si->si_cookieState->cs_num; i++ ) {
- if ( si->si_cookieState->cs_sids[i] == sid &&
- ber_bvcmp( syncCookie.ctxcsn, &si->si_cookieState->cs_vals[i] ) <= 0 ) {
- Debug( LDAP_DEBUG_SYNC, "do_syncrep2: %s CSN too old, ignoring %s\n",
- si->si_ridtxt, syncCookie.ctxcsn->bv_val, 0 );
- ldap_controls_free( rctrls );
- rc = 0;
+ if ( si->si_cookieState->cs_sids[i] == sid ) {
+ if ( ber_bvcmp( syncCookie.ctxcsn, &si->si_cookieState->cs_vals[i] ) <= 0 ) {
+ Debug( LDAP_DEBUG_SYNC, "do_syncrep2: %s CSN too old, ignoring %s\n",
+ si->si_ridtxt, syncCookie.ctxcsn->bv_val, 0 );
+ ldap_controls_free( rctrls );
+ rc = 0;
+ goto done;
+ }
+ break;
+ }
+ }
+ /* check pending CSNs too */
+ while ( ldap_pvt_thread_mutex_trylock( &si->si_cookieState->cs_pmutex )) {
+ if ( slapd_shutdown ) {
+ rc = -2;
goto done;
}
+ if ( !ldap_pvt_thread_pool_pausecheck( &connection_pool ))
+ ldap_pvt_thread_yield();
+ }
+ for ( i =0; i<si->si_cookieState->cs_pnum; i++ ) {
+ if ( si->si_cookieState->cs_psids[i] == sid ) {
+ if ( ber_bvcmp( syncCookie.ctxcsn, &si->si_cookieState->cs_pvals[i] ) <= 0 ) {
+ Debug( LDAP_DEBUG_SYNC, "do_syncrep2: %s CSN pending, ignoring %s\n",
+ si->si_ridtxt, syncCookie.ctxcsn->bv_val, 0 );
+ ldap_controls_free( rctrls );
+ rc = 0;
+ ldap_pvt_thread_mutex_unlock( &si->si_cookieState->cs_pmutex );
+ goto done;
+ }
+ ber_bvreplace( &si->si_cookieState->cs_pvals[i],
+ syncCookie.ctxcsn );
+ break;
+ }
}
+ /* new SID, add it */
+ if ( i == si->si_cookieState->cs_pnum ) {
+ value_add( &si->si_cookieState->cs_pvals, syncCookie.ctxcsn );
+ si->si_cookieState->cs_pnum++;
+ si->si_cookieState->cs_psids = ch_realloc( si->si_cookieState->cs_psids, si->si_cookieState->cs_pnum * sizeof(int));
+ si->si_cookieState->cs_psids[i] = sid;
+ }
+ punlock = i;
}
op->o_controls[slap_cids.sc_LDAPsync] = &syncCookie;
}
rc = syncrepl_updateCookie( si, op, &syncCookie );
}
}
+ if ( punlock >= 0 ) {
+ /* on failure, revert pending CSN */
+ if ( rc != LDAP_SUCCESS ) {
+ int i;
+ for ( i = 0; i<si->si_cookieState->cs_num; i++ ) {
+ if ( si->si_cookieState->cs_sids[i] == si->si_cookieState->cs_psids[punlock] ) {
+ ber_bvreplace( &si->si_cookieState->cs_pvals[punlock],
+ &si->si_cookieState->cs_vals[i] );
+ break;
+ }
+ }
+ if ( i == si->si_cookieState->cs_num )
+ si->si_cookieState->cs_pvals[punlock].bv_val[0] = '\0';
+ }
+ ldap_pvt_thread_mutex_unlock( &si->si_cookieState->cs_pmutex );
+ }
ldap_controls_free( rctrls );
if ( modlist ) {
slap_mods_free( modlist, 1 );
si->si_ridtxt, err, ldap_err2string( err ) );
}
if ( rctrls ) {
- LDAPControl **next;
+ LDAPControl **next = NULL;
/* NOTE: make sure we use the right one;
* a better approach would be to run thru
* the whole list and take care of all */
if ( ber_peek_tag( ber, &len ) == LDAP_TAG_SYNC_COOKIE ) {
ber_scanf( ber, "m", &cookie );
- Debug( LDAP_DEBUG_SYNC, "do_syncrep2: cookie=%s\n",
- BER_BVISNULL( &cookie ) ? "" : cookie.bv_val, 0, 0 );
+ Debug( LDAP_DEBUG_SYNC, "do_syncrep2: %s cookie=%s\n",
+ si->si_ridtxt,
+ BER_BVISNULL( &cookie ) ? "" : cookie.bv_val, 0 );
if ( !BER_BVISNULL( &cookie ) ) {
ch_free( syncCookie.octet_str.bv_val );
{
ber_scanf( ber, "m", &cookie );
- Debug( LDAP_DEBUG_SYNC, "do_syncrep2: cookie=%s\n",
- BER_BVISNULL( &cookie ) ? "" : cookie.bv_val, 0, 0 );
+ Debug( LDAP_DEBUG_SYNC, "do_syncrep2: %s cookie=%s\n",
+ si->si_ridtxt,
+ BER_BVISNULL( &cookie ) ? "" : cookie.bv_val, 0 );
if ( !BER_BVISNULL( &cookie ) ) {
ch_free( syncCookie.octet_str.bv_val );
{
ber_scanf( ber, "m", &cookie );
- Debug( LDAP_DEBUG_SYNC, "do_syncrep2: cookie=%s\n",
- BER_BVISNULL( &cookie ) ? "" : cookie.bv_val, 0, 0 );
+ Debug( LDAP_DEBUG_SYNC, "do_syncrep2: %s cookie=%s\n",
+ si->si_ridtxt,
+ BER_BVISNULL( &cookie ) ? "" : cookie.bv_val, 0 );
if ( !BER_BVISNULL( &cookie ) ) {
ch_free( syncCookie.octet_str.bv_val );
connection_fake_init( &conn, &opbuf, ctx );
op = &opbuf.ob_op;
+ /* o_connids must be unique for slap_graduate_commit_csn */
+ op->o_connid = SLAPD_SYNC_RID2SYNCCONN(si->si_rid);
op->o_managedsait = SLAP_CONTROL_NONCRITICAL;
be = si->si_be;
if ( SLAP_GLUE_SUBORDINATE( be ) && !overlay_is_inst( be, "syncprov" )) {
BackendDB * top_be = select_backend( &be->be_nsuffix[0], 1 );
if ( overlay_is_inst( top_be, "syncprov" ))
- si->si_wbe = select_backend( &be->be_nsuffix[0], 1 );
+ si->si_wbe = top_be;
else
si->si_wbe = be;
} else {
si->si_refreshDelete = 0;
si->si_refreshPresent = 0;
+ if ( si->si_presentlist ) {
+ avl_free( si->si_presentlist, ch_free );
+ si->si_presentlist = NULL;
+ }
+
/* use main DB when retrieving contextCSN */
op->o_bd = si->si_wbe;
op->o_dn = op->o_bd->be_rootdn;
{ BER_BVNULL, 0 }
};
-static Modifications *
+static int
syncrepl_accesslog_mods(
syncinfo_t *si,
- struct berval *vals
+ struct berval *vals,
+ struct Modifications **modres
)
{
char *colon;
struct berval bv, bv2;
short op;
Modifications *mod = NULL, *modlist = NULL, **modtail;
- int i;
+ int i, rc = 0;
modtail = &modlist;
bv.bv_len = colon - bv.bv_val;
if ( slap_bv2ad( &bv, &ad, &text ) ) {
/* Invalid */
- continue;
+ Debug( LDAP_DEBUG_ANY, "syncrepl_accesslog_mods: %s "
+ "Invalid attribute %s, %s\n",
+ si->si_ridtxt, bv.bv_val, text );
+ slap_mods_free( modlist, 1 );
+ modlist = NULL;
+ rc = -1;
+ break;
}
/* Ignore dynamically generated attrs */
mod->sml_numvals++;
}
}
- return modlist;
+ *modres = modlist;
+ return rc;
}
-static Modifications *
+static int
syncrepl_changelog_mods(
syncinfo_t *si,
- struct berval *vals
+ struct berval *vals,
+ struct Modifications **modres
)
{
- return NULL; /* FIXME */
+ return -1; /* FIXME */
}
static int
} else if ( !ber_bvstrcasecmp( &bv, &ls->ls_mod ) ) {
/* Parse attribute into modlist */
if ( si->si_syncdata == SYNCDATA_ACCESSLOG ) {
- modlist = syncrepl_accesslog_mods( si, bvals );
+ rc = syncrepl_accesslog_mods( si, bvals, &modlist );
} else {
- modlist = syncrepl_changelog_mods( si, bvals );
+ rc = syncrepl_changelog_mods( si, bvals, &modlist );
}
+ if ( rc ) goto done;
} else if ( !ber_bvstrcasecmp( &bv, &ls->ls_newRdn ) ) {
rdn = bvals[0];
} else if ( !ber_bvstrcasecmp( &bv, &ls->ls_delRdn ) ) {
mod->sml_next = m2;
}
op->o_bd = si->si_wbe;
+retry_modrdn:;
rc = op->o_bd->be_modrdn( op, &rs_modify );
+
+ /* NOTE: noSuchObject should result because the new superior
+ * has not been added yet (ITS#6472) */
+ if ( rc == LDAP_NO_SUCH_OBJECT && !BER_BVISNULL( op->orr_nnewSup )) {
+ Operation op2 = *op;
+ rc = syncrepl_add_glue_ancestors( &op2, entry );
+ if ( rc == LDAP_SUCCESS ) {
+ goto retry_modrdn;
+ }
+ }
+
op->o_tmpfree( op->orr_nnewrdn.bv_val, op->o_tmpmemctx );
op->o_tmpfree( op->orr_newrdn.bv_val, op->o_tmpmemctx );
op->ors_tlimit = SLAP_NO_LIMIT;
op->ors_limit = NULL;
op->ors_attrsonly = 0;
- op->ors_filter = str2filter_x( op, si->si_filterstr.bv_val );
+ op->ors_filter = filter_dup( si->si_filter, op->o_tmpmemctx );
/* In multimaster, updates can continue to arrive while
* we're searching. Limit the search result to entries
* older than our newest cookie CSN.
return;
}
-int
-syncrepl_add_glue(
+static int
+syncrepl_add_glue_ancestors(
Operation* op,
Entry *e )
{
Backend *be = op->o_bd;
slap_callback cb = { NULL };
Attribute *a;
- int rc;
+ int rc = LDAP_SUCCESS;
int suffrdns;
int i;
struct berval dn = BER_BVNULL;
ndn.bv_len = e->e_nname.bv_len - (ndn.bv_val - e->e_nname.bv_val);
}
+ return rc;
+}
+
+int
+syncrepl_add_glue(
+ Operation* op,
+ Entry *e )
+{
+ slap_callback cb = { NULL };
+ int rc;
+ Backend *be = op->o_bd;
+ SlapReply rs_add = {REP_RESULT};
+
+ rc = syncrepl_add_glue_ancestors( op, e );
+ switch ( rc ) {
+ case LDAP_SUCCESS:
+ case LDAP_ALREADY_EXISTS:
+ break;
+
+ default:
+ return rc;
+ }
+
+ op->o_tag = LDAP_REQ_ADD;
+ op->o_callback = &cb;
+ cb.sc_response = null_callback;
+ cb.sc_private = NULL;
+
op->o_req_dn = e->e_name;
op->o_req_ndn = e->e_nname;
op->ora_e = e;
Syntax *syn = slap_schema.si_ad_contextCSN->ad_type->sat_syntax;
#endif
- int rc, i, j;
+ int rc, i, j, changed = 0;
ber_len_t len;
slap_callback cb = { NULL };
if ( memcmp( syncCookie->ctxcsn[i].bv_val,
si->si_cookieState->cs_vals[j].bv_val, len ) > 0 ) {
mod.sml_values[j] = syncCookie->ctxcsn[i];
+ changed = 1;
if ( BER_BVISNULL( &first ) ) {
first = syncCookie->ctxcsn[i];
{
first = syncCookie->ctxcsn[i];
}
+ changed = 1;
}
}
/* Should never happen, ITS#5065 */
- if ( BER_BVISNULL( &first )) {
+ if ( BER_BVISNULL( &first ) || !changed ) {
ldap_pvt_thread_mutex_unlock( &si->si_cookieState->cs_mutex );
op->o_tmpfree( mod.sml_values, op->o_tmpmemctx );
return 0;
* Also use replace op if attr has no equality matching rule.
* (ITS#5781)
*/
- if ( nn && no < o &&
+ if ( ( nn || ( no > 0 && no < o ) ) &&
( old->a_desc == slap_schema.si_ad_objectClass ||
- !old->a_desc->ad_type->sat_equality ))
+ !old->a_desc->ad_type->sat_equality ) )
+ {
no = o;
+ }
i = j;
/* all old values were deleted, just use the replace op */
if ( sie->si_filterstr.bv_val ) {
ch_free( sie->si_filterstr.bv_val );
}
+ if ( sie->si_filter ) {
+ filter_free( sie->si_filter );
+ }
if ( sie->si_logfilterstr.bv_val ) {
ch_free( sie->si_logfilterstr.bv_val );
}
if ( sie->si_logbase.bv_val ) {
ch_free( sie->si_logbase.bv_val );
}
- if ( SLAP_SYNC_SUBENTRY( sie->si_be )) {
+ if ( sie->si_be && SLAP_SYNC_SUBENTRY( sie->si_be )) {
ch_free( sie->si_contextdn.bv_val );
}
if ( sie->si_attrs ) {
ch_free( sie->si_cookieState->cs_sids );
ber_bvarray_free( sie->si_cookieState->cs_vals );
ldap_pvt_thread_mutex_destroy( &sie->si_cookieState->cs_mutex );
+ ch_free( sie->si_cookieState->cs_psids );
+ ber_bvarray_free( sie->si_cookieState->cs_pvals );
+ ldap_pvt_thread_mutex_destroy( &sie->si_cookieState->cs_pmutex );
ch_free( sie->si_cookieState );
}
}
}
}
}
+ if ( j < 1 || si->si_retrynum_init[j-1] != RETRYNUM_FOREVER ) {
+ Debug( LDAP_DEBUG_CONFIG,
+ "%s: syncrepl will eventually stop retrying; the \"retry\" parameter should end with a '+'.\n",
+ c->log, 0, 0 );
+ }
+
si->si_retrynum_init[j] = RETRYNUM_TAIL;
si->si_retrynum[j] = RETRYNUM_TAIL;
si->si_retryinterval[j] = 0;
Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
return -1;
}
- if ( tmp > SLAP_SYNC_SID_MAX || tmp < 0 ) {
+ if ( tmp > SLAP_SYNC_RID_MAX || tmp < 0 ) {
snprintf( c->cr_msg, sizeof( c->cr_msg ),
"Error: parse_syncrepl_line: "
- "syncrepl id %d is out of range [0..4095]", tmp );
+ "syncrepl id %d is out of range [0..%d]", tmp, SLAP_SYNC_RID_MAX );
Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
return -1;
}
{
val = c->argv[ i ] + STRLENOF( PROVIDERSTR "=" );
ber_str2bv( val, 0, 1, &si->si_bindconf.sb_uri );
+#ifdef HAVE_TLS
+ if ( ldap_is_ldaps_url( val ))
+ si->si_bindconf.sb_tls_do_init = 1;
+#endif
si->si_got |= GOT_PROVIDER;
} else if ( !strncasecmp( c->argv[ i ], SCHEMASTR "=",
STRLENOF( SCHEMASTR "=" ) ) )
}
}
+ si->si_filter = str2filter( si->si_filterstr.bv_val );
+ if ( si->si_filter == NULL ) {
+ Debug( LDAP_DEBUG_ANY, "syncrepl %s " SEARCHBASESTR "=\"%s\": unable to parse filter=\"%s\"\n",
+ si->si_ridtxt, c->be->be_suffix ? c->be->be_suffix[ 0 ].bv_val : "(null)", si->si_filterstr.bv_val );
+ return 1;
+ }
+
return 0;
}
return 1;
} else {
Debug( LDAP_DEBUG_CONFIG,
- "Config: ** successfully added syncrepl \"%s\"\n",
+ "Config: ** successfully added syncrepl %s \"%s\"\n",
+ si->si_ridtxt,
BER_BVISNULL( &si->si_bindconf.sb_uri ) ?
- "(null)" : si->si_bindconf.sb_uri.bv_val, 0, 0 );
+ "(null)" : si->si_bindconf.sb_uri.bv_val, 0 );
if ( c->be->be_syncinfo ) {
syncinfo_t *sip;
} else {
si->si_cookieState = ch_calloc( 1, sizeof( cookie_state ));
ldap_pvt_thread_mutex_init( &si->si_cookieState->cs_mutex );
+ ldap_pvt_thread_mutex_init( &si->si_cookieState->cs_pmutex );
c->be->be_syncinfo = si;
}
si->si_bindconf.sb_version = LDAP_VERSION3;
ptr = buf;
- assert( si->si_rid >= 0 && si->si_rid <= SLAP_SYNC_SID_MAX );
+ assert( si->si_rid >= 0 && si->si_rid <= SLAP_SYNC_RID_MAX );
len = snprintf( ptr, WHATSLEFT, IDSTR "=%03d " PROVIDERSTR "=%s",
si->si_rid, si->si_bindconf.sb_uri.bv_val );
if ( len >= sizeof( buf ) ) return;
si = *sip;
if ( c->valx == -1 || i == c->valx ) {
*sip = si->si_next;
+ si->si_ctype = -1;
+ si->si_next = NULL;
/* If the task is currently active, we have to leave
* it running. It will exit on its own. This will only
* happen when running on the cn=config DB.
if ( ldap_pvt_thread_mutex_trylock( &si->si_mutex )) {
isrunning = 1;
} else {
+ /* There is no active thread, but we must still
+ * ensure that no thread is (or will be) queued
+ * while we removes the task.
+ */
+ struct re_s *re = si->si_re;
+ si->si_re = NULL;
+
if ( si->si_conn ) {
- /* If there's a persistent connection, it may
- * already have a thread queued. We know it's
- * not active, so it must be pending and we
- * can simply cancel it now.
- */
- ldap_pvt_thread_pool_retract( &connection_pool,
- si->si_re->routine, si->si_re );
+ connection_client_stop( si->si_conn );
+ si->si_conn = NULL;
}
+
+ ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
+ if ( ldap_pvt_runqueue_isrunning( &slapd_rq, re ) ) {
+ ldap_pvt_runqueue_stoptask( &slapd_rq, re );
+ isrunning = 1;
+ }
+ ldap_pvt_runqueue_remove( &slapd_rq, re );
+ ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
+
+ if ( ldap_pvt_thread_pool_retract( &connection_pool,
+ re->routine, re ) > 0 )
+ isrunning = 0;
+
ldap_pvt_thread_mutex_unlock( &si->si_mutex );
}
}
- if ( isrunning ) {
- si->si_ctype = -1;
- si->si_next = NULL;
- } else {
+ if ( !isrunning ) {
syncinfo_free( si, 0 );
}
if ( i == c->valx )