mirror of
https://git.openldap.org/openldap/openldap.git
synced 2024-12-21 03:10:25 +08:00
a8574a450f
new slapadd options -p : promote : If the ldif file contains syncConsumerSubentries, convert them to a single syncProviderSubentry. Its contextCSN attribute has the largest value of the syncreplCookie attributes of the syncConsumerSubentries. syncProviderSubentry in the ldif file is retained. -p -w : promote : Recalculate contextCSN based on entryCSN of each entry. create Existing syncConsumerSubentries and syncProviderSubentry are ignored and not added to the directory. -r : demote : If the ldif file contains syncProviderSubentry, convert it to a syncConsumerSubentry having the default syncrepl id of 0. syncConsumerSubentries in the ldif file are retained. -r -w : demote : Recalculate syncreplCookie based on entryCSN of each entry. create Existing syncConsumerSubentries and syncProviderSubentry are ignored and not added to the directory. The default syncrepl id of 0 will be used for the new syncConsumerSubentry. -r -w -i %d[,%d]* : Using the comma separated list followed by the -i option, it is possible to create multiple syncConsumerSubentries having the syncrepl ids specified in the list. syncreplCookie values of these sycnConsumerSubentries will have the same value, either from the maximum entryCSN value or from the contextCSN value of the syncProviderSubentry.
1666 lines
40 KiB
C
1666 lines
40 KiB
C
/* $OpenLDAP$ */
|
|
/*
|
|
* Replication Engine which uses the LDAP Sync protocol
|
|
*/
|
|
/* Copyright (c) 2003 by International Business Machines, Inc.
|
|
*
|
|
* International Business Machines, Inc. (hereinafter called IBM) grants
|
|
* permission under its copyrights to use, copy, modify, and distribute this
|
|
* Software with or without fee, provided that the above copyright notice and
|
|
* all paragraphs of this notice appear in all copies, and that the name of IBM
|
|
* not be used in connection with the marketing of any product incorporating
|
|
* the Software or modifications thereof, without specific, written prior
|
|
* permission.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", AND IBM DISCLAIMS ALL WARRANTIES,
|
|
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
|
* PARTICULAR PURPOSE. IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL,
|
|
* DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER ARISING
|
|
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE, EVEN
|
|
* IF IBM IS APPRISED OF THE POSSIBILITY OF SUCH DAMAGES.
|
|
*/
|
|
|
|
#include "portable.h"
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <ac/string.h>
|
|
#include <ac/socket.h>
|
|
#include <db.h>
|
|
|
|
#include "ldap_pvt.h"
|
|
#include "lutil.h"
|
|
#include "slap.h"
|
|
#include "lutil_ldap.h"
|
|
|
|
#include "ldap_rq.h"
|
|
|
|
const struct berval slap_syncrepl_bvc = BER_BVC("syncreplxxx");
|
|
const struct berval slap_syncrepl_cn_bvc = BER_BVC("cn=syncreplxxx");
|
|
|
|
static void
|
|
syncrepl_del_nonpresent( LDAP *, Operation * );
|
|
|
|
/* callback functions */
|
|
static int cookie_callback( struct slap_op *, struct slap_rep * );
|
|
static int dn_callback( struct slap_op *, struct slap_rep * );
|
|
static int nonpresent_callback( struct slap_op *, struct slap_rep * );
|
|
static int null_callback( struct slap_op *, struct slap_rep * );
|
|
static int contextcsn_callback( Operation*, SlapReply* );
|
|
|
|
static AttributeDescription **sync_descs;
|
|
|
|
struct runqueue_s syncrepl_rq;
|
|
|
|
void
|
|
init_syncrepl()
|
|
{
|
|
sync_descs = ch_malloc( 4 * sizeof( AttributeDescription * ));
|
|
sync_descs[0] = slap_schema.si_ad_objectClass;
|
|
sync_descs[1] = slap_schema.si_ad_structuralObjectClass;
|
|
sync_descs[2] = slap_schema.si_ad_entryCSN;
|
|
sync_descs[3] = NULL;
|
|
}
|
|
|
|
int
|
|
ldap_sync_search(
|
|
syncinfo_t *si,
|
|
LDAP *ld,
|
|
LDAPControl **sctrls,
|
|
LDAPControl **cctrls,
|
|
int *msgidp )
|
|
{
|
|
BerElement *ber;
|
|
int timelimit;
|
|
ber_int_t id;
|
|
|
|
int rc;
|
|
BerElement *sync_ber = NULL;
|
|
struct berval *sync_bvalp = NULL;
|
|
LDAPControl c[2];
|
|
LDAPControl **ctrls;
|
|
int err;
|
|
struct timeval timeout;
|
|
|
|
/* setup LDAP SYNC control */
|
|
sync_ber = ber_alloc_t( LBER_USE_DER );
|
|
ber_set_option( sync_ber, LBER_OPT_BER_MEMCTX, NULL );
|
|
|
|
if ( si->syncCookie ) {
|
|
ber_printf( sync_ber, "{eO}", abs(si->type), si->syncCookie );
|
|
} else {
|
|
ber_printf( sync_ber, "{e}", abs(si->type) );
|
|
}
|
|
|
|
if ( ber_flatten( sync_ber, &sync_bvalp ) == LBER_ERROR ) {
|
|
ber_free( sync_ber, 1 );
|
|
return LBER_ERROR;
|
|
}
|
|
ber_free( sync_ber, 1 );
|
|
|
|
ctrls = (LDAPControl**) sl_calloc( 3, sizeof(LDAPControl*), NULL );
|
|
|
|
c[0].ldctl_oid = LDAP_CONTROL_SYNC;
|
|
c[0].ldctl_value = (*sync_bvalp);
|
|
c[0].ldctl_iscritical = si->type < 0;
|
|
ctrls[0] = &c[0];
|
|
|
|
if ( si->authzId ) {
|
|
c[1].ldctl_oid = LDAP_CONTROL_PROXY_AUTHZ;
|
|
c[1].ldctl_value.bv_val = si->authzId;
|
|
c[1].ldctl_value.bv_len = strlen( si->authzId );
|
|
c[1].ldctl_iscritical = 1;
|
|
ctrls[1] = &c[1];
|
|
} else {
|
|
ctrls[1] = NULL;
|
|
}
|
|
|
|
ctrls[2] = NULL;
|
|
|
|
err = ldap_set_option( ld, LDAP_OPT_SERVER_CONTROLS, ctrls );
|
|
|
|
ber_bvfree( sync_bvalp );
|
|
ch_free( ctrls );
|
|
|
|
if ( err != LDAP_OPT_SUCCESS )
|
|
fprintf( stderr, "Could not set controls : %d\n", err );
|
|
|
|
timeout.tv_sec = si->tlimit > 0 ? si->tlimit : 1;
|
|
|
|
rc = ldap_search_ext( ld, si->base, si->scope, si->filterstr,
|
|
si->attrs, si->attrsonly, sctrls, cctrls,
|
|
si->tlimit < 0 ? NULL : &timeout,
|
|
si->slimit, msgidp );
|
|
|
|
return rc;
|
|
}
|
|
|
|
void *
|
|
do_syncrepl(
|
|
void *ctx,
|
|
void *arg )
|
|
{
|
|
struct re_s* rtask = arg;
|
|
syncinfo_t *si = ( syncinfo_t * ) rtask->arg;
|
|
Backend *be = si->be;
|
|
|
|
SlapReply rs = {REP_RESULT};
|
|
|
|
LDAPControl c[2];
|
|
LDAPControl **sctrls = NULL;
|
|
LDAPControl **rctrls = NULL;
|
|
LDAPControl *rctrlp = NULL;
|
|
BerElement *sync_ber = NULL;
|
|
struct berval *sync_bvalp = NULL;
|
|
|
|
BerElement *ctrl_ber = NULL;
|
|
BerElement *res_ber = NULL;
|
|
|
|
LDAP *ld = NULL;
|
|
LDAPMessage *res = NULL;
|
|
LDAPMessage *msg = NULL;
|
|
|
|
ber_int_t msgid;
|
|
|
|
int nresponses, nreferences, nextended, npartial;
|
|
int nresponses_psearch;
|
|
|
|
int cancel_msgid = -1;
|
|
char *retoid = NULL;
|
|
struct berval *retdata = NULL;
|
|
|
|
int sync_info_arrived = 0;
|
|
Entry *entry = NULL;
|
|
|
|
int syncstate;
|
|
struct berval syncUUID = { 0, NULL };
|
|
struct berval syncCookie = { 0, NULL };
|
|
struct berval syncCookie_req = { 0, NULL };
|
|
|
|
int rc;
|
|
int err;
|
|
ber_len_t len;
|
|
int syncinfo_arrived = 0;
|
|
|
|
char **tmp = NULL;
|
|
AttributeDescription** descs = NULL;
|
|
|
|
Connection conn;
|
|
Operation op = {0};
|
|
slap_callback cb;
|
|
|
|
void *memctx = NULL;
|
|
ber_len_t memsiz;
|
|
|
|
int i, j, k, n;
|
|
int rc_efree;
|
|
|
|
struct berval base_bv = { 0, NULL };
|
|
struct berval pbase = { 0, NULL };
|
|
struct berval nbase = { 0, NULL };
|
|
struct berval sub_bv = { 0, NULL };
|
|
struct berval psubrdn = { 0, NULL };
|
|
struct berval nsubrdn = { 0, NULL };
|
|
struct berval psub = { 0, NULL };
|
|
struct berval nsub = { 0, NULL };
|
|
char substr[64];
|
|
Modifications *modlist = NULL;
|
|
Modifications *ml, *mlnext;
|
|
char *def_filter_str = NULL;
|
|
|
|
const char *text;
|
|
int match;
|
|
|
|
struct timeval *tout_p = NULL;
|
|
struct timeval tout = { 10, 0 };
|
|
|
|
#ifdef NEW_LOGGING
|
|
LDAP_LOG ( OPERATION, DETAIL1, "do_syncrepl\n", 0, 0, 0 );
|
|
#else
|
|
Debug( LDAP_DEBUG_TRACE, "=>do_syncrepl\n", 0, 0, 0 );
|
|
#endif
|
|
|
|
if ( si == NULL )
|
|
return NULL;
|
|
|
|
if ( abs(si->type) != LDAP_SYNC_REFRESH_ONLY &&
|
|
abs(si->type) != LDAP_SYNC_REFRESH_AND_PERSIST ) {
|
|
return NULL;
|
|
}
|
|
|
|
si->sync_mode = LDAP_SYNC_STATE_MODE;
|
|
|
|
/* Init connection to master */
|
|
|
|
rc = ldap_initialize( &ld, si->provideruri );
|
|
if ( rc != LDAP_SUCCESS ) {
|
|
#ifdef NEW_LOGGING
|
|
LDAP_LOG( OPERATION, ERR, "do_syncrepl: "
|
|
"ldap_initialize failed (%s)\n",
|
|
si->provideruri, 0, 0 );
|
|
#else
|
|
Debug( LDAP_DEBUG_ANY, "do_syncrepl: "
|
|
"ldap_initialize failed (%s)\n",
|
|
si->provideruri, 0, 0 );
|
|
#endif
|
|
}
|
|
|
|
op.o_protocol = LDAP_VERSION3;
|
|
ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, &op.o_protocol );
|
|
|
|
/* Bind to master */
|
|
|
|
if ( si->tls ) {
|
|
rc = ldap_start_tls_s( ld, NULL, NULL );
|
|
if( rc != LDAP_SUCCESS ) {
|
|
#ifdef NEW_LOGGING
|
|
LDAP_LOG ( OPERATION, ERR, "do_syncrepl: "
|
|
"%s: ldap_start_tls failed (%d)\n",
|
|
si->tls == TLS_CRITICAL ? "Error" : "Warning",
|
|
rc, 0 );
|
|
#else
|
|
Debug( LDAP_DEBUG_ANY,
|
|
"%s: ldap_start_tls failed (%d)\n",
|
|
si->tls == TLS_CRITICAL ? "Error" : "Warning",
|
|
rc, 0 );
|
|
#endif
|
|
if( si->tls == TLS_CRITICAL )
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
if ( si->bindmethod == LDAP_AUTH_SASL ) {
|
|
#ifdef HAVE_CYRUS_SASL
|
|
void *defaults;
|
|
|
|
if ( si->secprops != NULL ) {
|
|
int err = ldap_set_option( ld,
|
|
LDAP_OPT_X_SASL_SECPROPS, si->secprops);
|
|
|
|
if( err != LDAP_OPT_SUCCESS ) {
|
|
#ifdef NEW_LOGGING
|
|
LDAP_LOG ( OPERATION, ERR, "do_bind: Error: "
|
|
"ldap_set_option(%s,SECPROPS,\"%s\") failed!\n",
|
|
si->provideruri, si->secprops, 0 );
|
|
#else
|
|
Debug( LDAP_DEBUG_ANY, "Error: ldap_set_option "
|
|
"(%s,SECPROPS,\"%s\") failed!\n",
|
|
si->provideruri, si->secprops, NULL );
|
|
#endif
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
defaults = lutil_sasl_defaults( ld,
|
|
si->saslmech,
|
|
si->realm,
|
|
si->authcId,
|
|
si->passwd,
|
|
si->authzId );
|
|
|
|
rc = ldap_sasl_interactive_bind_s( ld,
|
|
si->binddn,
|
|
si->saslmech,
|
|
NULL, NULL,
|
|
LDAP_SASL_QUIET,
|
|
lutil_sasl_interact,
|
|
defaults );
|
|
|
|
/* FIXME : different error behaviors according to
|
|
1) return code
|
|
2) on err policy : exit, retry, backoff ...
|
|
*/
|
|
if ( rc != LDAP_SUCCESS ) {
|
|
#ifdef NEW_LOGGING
|
|
LDAP_LOG ( OPERATION, ERR, "do_syncrepl: "
|
|
"ldap_sasl_interactive_bind_s failed (%d)\n",
|
|
rc, 0, 0 );
|
|
#else
|
|
Debug( LDAP_DEBUG_ANY, "do_syncrepl: "
|
|
"ldap_sasl_interactive_bind_s failed (%d)\n",
|
|
rc, 0, 0 );
|
|
#endif
|
|
return NULL;
|
|
}
|
|
#else /* HAVE_CYRUS_SASL */
|
|
fprintf( stderr, "not compiled with SASL support\n" );
|
|
return NULL;
|
|
#endif
|
|
} else {
|
|
rc = ldap_bind_s( ld, si->binddn, si->passwd, si->bindmethod );
|
|
if ( rc != LDAP_SUCCESS ) {
|
|
#ifdef NEW_LOGGING
|
|
LDAP_LOG ( OPERATION, ERR, "do_syncrepl: "
|
|
"ldap_bind_s failed (%d)\n", rc, 0, 0 );
|
|
#else
|
|
Debug( LDAP_DEBUG_ANY, "do_syncrepl: "
|
|
"ldap_bind_s failed (%d)\n", rc, 0, 0 );
|
|
#endif
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
/* set thread context in syncinfo */
|
|
si->ctx = ctx;
|
|
|
|
/* set memory context */
|
|
#define SLAB_SIZE 1048576
|
|
memsiz = SLAB_SIZE;
|
|
memctx = sl_mem_create( memsiz, ctx );
|
|
op.o_tmpmemctx = memctx;
|
|
op.o_tmpmfuncs = &sl_mfuncs;
|
|
|
|
op.o_si = si;
|
|
op.o_tag = LDAP_REQ_SEARCH;
|
|
op.o_dn = si->updatedn;
|
|
op.o_ndn = si->updatedn;
|
|
op.o_callback = &cb;
|
|
op.o_time = slap_get_time();
|
|
op.o_managedsait = 1;
|
|
op.o_threadctx = si->ctx;
|
|
op.o_bd = be;
|
|
op.o_conn = &conn;
|
|
op.o_connid = op.o_conn->c_connid;
|
|
op.ors_scope = LDAP_SCOPE_BASE;
|
|
op.ors_deref = LDAP_DEREF_NEVER;
|
|
op.ors_slimit = 0;
|
|
op.ors_tlimit = 0;
|
|
op.ors_attrsonly = 0;
|
|
op.ors_attrs = NULL;
|
|
op.ors_filter = str2filter_x( &op, def_filter_str = "(objectClass=*)" );
|
|
ber_str2bv( def_filter_str, 0, 0, &op.ors_filterstr );
|
|
|
|
si->conn = &conn;
|
|
conn.c_send_ldap_result = slap_send_ldap_result;
|
|
conn.c_send_search_entry = slap_send_search_entry;
|
|
conn.c_send_search_reference = slap_send_search_reference;
|
|
|
|
/* get syncrepl cookie of shadow replica from subentry */
|
|
ber_str2bv( si->base, 0, 0, &base_bv );
|
|
dnPrettyNormal( 0, &base_bv, &pbase, &nbase, op.o_tmpmemctx );
|
|
|
|
sprintf( substr, "cn=syncrepl%d", si->id );
|
|
ber_str2bv( substr, 0, 0, &sub_bv );
|
|
dnPrettyNormal( 0, &sub_bv, &psubrdn, &nsubrdn, op.o_tmpmemctx );
|
|
|
|
build_new_dn( &op.o_req_dn, &pbase, &psubrdn, op.o_tmpmemctx );
|
|
build_new_dn( &op.o_req_ndn, &nbase, &nsubrdn, op.o_tmpmemctx );
|
|
|
|
/* set callback function */
|
|
cb.sc_response = cookie_callback;
|
|
cb.sc_private = si;
|
|
|
|
/* search subentry to retrieve cookie */
|
|
si->syncCookie = NULL;
|
|
be->be_search( &op, &rs );
|
|
|
|
ber_dupbv( &syncCookie_req, si->syncCookie );
|
|
|
|
psub = be->be_nsuffix[0];
|
|
|
|
for ( n = 0; si->attrs[ n ] != NULL; n++ ) ;
|
|
|
|
if ( n != 0 ) {
|
|
/* Delete Attributes */
|
|
descs = sync_descs;
|
|
for ( i = 0; descs[i] != NULL; i++ ) {
|
|
for ( j = 0; si->attrs[j] != NULL; j++ ) {
|
|
if ( !strcmp( si->attrs[j], descs[i]->ad_cname.bv_val )) {
|
|
ch_free( si->attrs[j] );
|
|
for ( k = j; si->attrs[k] != NULL; k++ ) {
|
|
si->attrs[k] = si->attrs[k+1];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
for ( n = 0; si->attrs[ n ] != NULL; n++ );
|
|
tmp = ( char ** ) ch_realloc( si->attrs, ( n + 4 ) * sizeof( char * ));
|
|
if ( tmp == NULL ) {
|
|
#ifdef NEW_LOGGING
|
|
LDAP_LOG( OPERATION, ERR, "out of memory\n", 0,0,0 );
|
|
#else
|
|
Debug( LDAP_DEBUG_ANY, "out of memory\n", 0,0,0 );
|
|
#endif
|
|
}
|
|
} else {
|
|
tmp = ( char ** ) ch_realloc( si->attrs, 5 * sizeof( char * ));
|
|
if ( tmp == NULL ) {
|
|
#ifdef NEW_LOGGING
|
|
LDAP_LOG( OPERATION, ERR, "out of memory\n", 0,0,0 );
|
|
#else
|
|
Debug( LDAP_DEBUG_ANY, "out of memory\n", 0,0,0 );
|
|
#endif
|
|
}
|
|
tmp[ n++ ] = ch_strdup( "*" );
|
|
}
|
|
|
|
descs = sync_descs;
|
|
si->attrs = tmp;
|
|
|
|
/* Add Attributes */
|
|
|
|
for ( i = 0; descs[ i ] != NULL; i++ ) {
|
|
si->attrs[ n++ ] = ch_strdup ( descs[i]->ad_cname.bv_val );
|
|
si->attrs[ n ] = NULL;
|
|
}
|
|
|
|
rc = ldap_sync_search( si, ld, NULL, NULL, &msgid );
|
|
if( rc != LDAP_SUCCESS ) {
|
|
fprintf( stderr, "syncrepl: ldap_search_ext: %s (%d)\n",
|
|
ldap_err2string( rc ), rc );
|
|
return NULL;
|
|
}
|
|
|
|
if ( abs(si->type) == LDAP_SYNC_REFRESH_AND_PERSIST ){
|
|
tout_p = &tout;
|
|
} else {
|
|
tout_p = NULL;
|
|
}
|
|
|
|
while (( rc = ldap_result( ld, LDAP_RES_ANY, LDAP_MSG_ONE, tout_p, &res )) >= 0 ) {
|
|
|
|
if ( rc == 0 ) {
|
|
if ( slapd_abrupt_shutdown ) {
|
|
break;
|
|
} else {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
for ( msg = ldap_first_message( ld, res );
|
|
msg != NULL;
|
|
msg = ldap_next_message( ld, msg ) )
|
|
{
|
|
syncCookie.bv_len = 0; syncCookie.bv_val = NULL;
|
|
switch( ldap_msgtype( msg ) ) {
|
|
case LDAP_RES_SEARCH_ENTRY:
|
|
entry = syncrepl_message_to_entry( si, ld, &op, msg,
|
|
&modlist, &syncstate, &syncUUID, &syncCookie );
|
|
rc_efree = syncrepl_entry( si, ld, &op, entry, modlist,
|
|
syncstate, &syncUUID, &syncCookie, !syncinfo_arrived );
|
|
if ( syncCookie.bv_len ) {
|
|
syncrepl_updateCookie( si, ld, &op, &psub, &syncCookie );
|
|
}
|
|
if ( modlist ) {
|
|
slap_mods_free( modlist );
|
|
}
|
|
if ( rc_efree ) {
|
|
entry_free( entry );
|
|
}
|
|
break;
|
|
|
|
case LDAP_RES_SEARCH_REFERENCE:
|
|
#ifdef NEW_LOGGING
|
|
LDAP_LOG( OPERATION, ERR,
|
|
"do_syncrepl : reference received\n", 0, 0, 0 );
|
|
#else
|
|
Debug( LDAP_DEBUG_ANY,
|
|
"do_syncrepl : reference received\n", 0, 0, 0 );
|
|
#endif
|
|
break;
|
|
|
|
case LDAP_RES_SEARCH_RESULT:
|
|
ldap_parse_result( ld, msg, &err, NULL, NULL, NULL, &rctrls, 0 );
|
|
if ( rctrls ) {
|
|
rctrlp = *rctrls;
|
|
ctrl_ber = ber_alloc_t( LBER_USE_DER );
|
|
ber_set_option( ctrl_ber, LBER_OPT_BER_MEMCTX, &op.o_tmpmemctx );
|
|
ber_write( ctrl_ber, rctrlp->ldctl_value.bv_val, rctrlp->ldctl_value.bv_len, 0 );
|
|
ber_reset( ctrl_ber, 1 );
|
|
|
|
ber_scanf( ctrl_ber, "{" /*"}"*/);
|
|
if ( ber_peek_tag( ctrl_ber, &len )
|
|
== LDAP_SYNC_TAG_COOKIE ) {
|
|
ber_scanf( ctrl_ber, "o", &syncCookie );
|
|
}
|
|
}
|
|
value_match( &match, slap_schema.si_ad_entryCSN,
|
|
slap_schema.si_ad_entryCSN->ad_type->sat_ordering,
|
|
SLAP_MR_VALUE_OF_ATTRIBUTE_SYNTAX,
|
|
&syncCookie_req, &syncCookie, &text );
|
|
if (si->type == LDAP_SYNC_REFRESH_AND_PERSIST) {
|
|
/* FIXME : different error behaviors according to
|
|
1) err code : LDAP_BUSY ...
|
|
2) on err policy : stop service, stop sync, retry
|
|
*/
|
|
if ( syncCookie.bv_len && match < 0) {
|
|
syncrepl_updateCookie( si, ld, &op, &psub, &syncCookie );
|
|
}
|
|
if ( ctrl_ber )
|
|
ber_free( ctrl_ber, 1 );
|
|
goto done;
|
|
} else {
|
|
/* FIXME : different error behaviors according to
|
|
1) err code : LDAP_BUSY ...
|
|
2) on err policy : stop service, stop sync, retry
|
|
*/
|
|
if ( syncCookie.bv_len && match < 0 ) {
|
|
syncrepl_updateCookie( si, ld, &op, &psub, &syncCookie);
|
|
}
|
|
if ( si->sync_mode == LDAP_SYNC_STATE_MODE && match < 0 ) {
|
|
syncrepl_del_nonpresent( ld, &op );
|
|
}
|
|
if ( ctrl_ber )
|
|
ber_free( ctrl_ber, 1 );
|
|
goto done;
|
|
}
|
|
break;
|
|
|
|
case LDAP_RES_INTERMEDIATE:
|
|
rc = ldap_parse_intermediate( ld, msg,
|
|
&retoid, &retdata, NULL, 0 );
|
|
if ( !rc && !strcmp( retoid, LDAP_SYNC_INFO ) ) {
|
|
sync_info_arrived = 1;
|
|
res_ber = ber_init( retdata );
|
|
ber_scanf( res_ber, "{e" /*"}"*/, &syncstate );
|
|
|
|
if ( ber_peek_tag( res_ber, &len )
|
|
== LDAP_SYNC_TAG_COOKIE ) {
|
|
ber_scanf( res_ber, /*"{"*/ "o}", &syncCookie );
|
|
} else {
|
|
if ( syncstate == LDAP_SYNC_NEW_COOKIE ) {
|
|
#ifdef NEW_LOGGING
|
|
LDAP_LOG( OPERATION, ERR,
|
|
"do_syncrepl : cookie required\n", 0, 0, 0 );
|
|
#else
|
|
Debug( LDAP_DEBUG_ANY,
|
|
"do_syncrepl : cookie required\n", 0, 0, 0 );
|
|
#endif
|
|
}
|
|
}
|
|
|
|
value_match( &match, slap_schema.si_ad_entryCSN,
|
|
slap_schema.si_ad_entryCSN->ad_type->sat_ordering,
|
|
SLAP_MR_VALUE_OF_ATTRIBUTE_SYNTAX,
|
|
&syncCookie_req, &syncCookie, &text );
|
|
|
|
if ( syncCookie.bv_len && match < 0 ) {
|
|
syncrepl_updateCookie( si, ld, &op, &psub, &syncCookie);
|
|
}
|
|
|
|
if ( syncstate == LDAP_SYNC_STATE_MODE_DONE ) {
|
|
if ( match < 0 ) {
|
|
syncrepl_del_nonpresent( ld, &op );
|
|
}
|
|
si->sync_mode = LDAP_SYNC_LOG_MODE;
|
|
} else if ( syncstate == LDAP_SYNC_LOG_MODE_DONE ) {
|
|
si->sync_mode = LDAP_SYNC_PERSIST_MODE;
|
|
} else if ( syncstate == LDAP_SYNC_REFRESH_DONE ) {
|
|
si->sync_mode = LDAP_SYNC_PERSIST_MODE;
|
|
} else if ( syncstate != LDAP_SYNC_NEW_COOKIE ||
|
|
syncstate != LDAP_SYNC_LOG_MODE_DONE ) {
|
|
#ifdef NEW_LOGGING
|
|
LDAP_LOG( OPERATION, ERR,
|
|
"do_syncrepl : unknown sync info\n", 0, 0, 0 );
|
|
#else
|
|
Debug( LDAP_DEBUG_ANY,
|
|
"do_syncrepl : unknown sync info\n", 0, 0, 0 );
|
|
#endif
|
|
}
|
|
|
|
ldap_memfree( retoid );
|
|
ber_bvfree( retdata );
|
|
ber_free( res_ber, 1 );
|
|
break;
|
|
} else {
|
|
#ifdef NEW_LOGGING
|
|
LDAP_LOG( OPERATION, ERR,"do_syncrepl :"
|
|
" unknown intermediate "
|
|
"response\n", 0, 0, 0 );
|
|
#else
|
|
Debug( LDAP_DEBUG_ANY, "do_syncrepl : "
|
|
"unknown intermediate response (%d)\n",
|
|
rc, 0, 0 );
|
|
#endif
|
|
ldap_memfree( retoid );
|
|
ber_bvfree( retdata );
|
|
break;
|
|
}
|
|
break;
|
|
default:
|
|
#ifdef NEW_LOGGING
|
|
LDAP_LOG( OPERATION, ERR, "do_syncrepl : "
|
|
"unknown message\n", 0, 0, 0 );
|
|
#else
|
|
Debug( LDAP_DEBUG_ANY, "do_syncrepl : "
|
|
"unknown message\n", 0, 0, 0 );
|
|
#endif
|
|
break;
|
|
|
|
}
|
|
if ( syncCookie.bv_val )
|
|
ch_free( syncCookie.bv_val );
|
|
if ( syncUUID.bv_val )
|
|
ch_free( syncUUID.bv_val );
|
|
}
|
|
ldap_msgfree( res );
|
|
}
|
|
|
|
if ( rc == -1 ) {
|
|
int errno;
|
|
const char *errstr;
|
|
|
|
ldap_get_option( ld, LDAP_OPT_ERROR_NUMBER, &errno );
|
|
errstr = ldap_err2string( errno );
|
|
|
|
#ifdef NEW_LOGGING
|
|
LDAP_LOG( OPERATION, ERR,
|
|
"do_syncrepl : %s\n", errstr, 0, 0 );
|
|
#else
|
|
Debug( LDAP_DEBUG_ANY,
|
|
"do_syncrepl : %s\n", errstr, 0, 0 );
|
|
#endif
|
|
}
|
|
|
|
done:
|
|
if ( syncCookie.bv_val )
|
|
ch_free( syncCookie.bv_val );
|
|
if ( syncCookie_req.bv_val )
|
|
ch_free( syncCookie_req.bv_val );
|
|
if ( syncUUID.bv_val )
|
|
ch_free( syncUUID.bv_val );
|
|
|
|
if ( res )
|
|
ldap_msgfree( res );
|
|
|
|
ldap_unbind( ld );
|
|
|
|
ldap_pvt_thread_mutex_lock( &syncrepl_rq.rq_mutex );
|
|
ldap_pvt_runqueue_stoptask( &syncrepl_rq, rtask );
|
|
if ( si->type == LDAP_SYNC_REFRESH_ONLY ) {
|
|
ldap_pvt_runqueue_resched( &syncrepl_rq, rtask );
|
|
} else {
|
|
ldap_pvt_runqueue_remove( &syncrepl_rq, rtask );
|
|
}
|
|
ldap_pvt_thread_mutex_unlock( &syncrepl_rq.rq_mutex );
|
|
|
|
return NULL;
|
|
}
|
|
|
|
Entry*
|
|
syncrepl_message_to_entry(
|
|
syncinfo_t *si,
|
|
LDAP *ld,
|
|
Operation *op,
|
|
LDAPMessage *msg,
|
|
Modifications **modlist,
|
|
int *syncstate,
|
|
struct berval *syncUUID,
|
|
struct berval *syncCookie
|
|
)
|
|
{
|
|
Entry *e;
|
|
BerElement *ber = NULL;
|
|
BerElement *tmpber;
|
|
struct berval bv = {0, NULL};
|
|
Modifications tmp;
|
|
Modifications *mod;
|
|
Modifications **modtail = modlist;
|
|
Backend *be = op->o_bd;
|
|
|
|
const char *text;
|
|
char txtbuf[SLAP_TEXT_BUFLEN];
|
|
size_t textlen = sizeof txtbuf;
|
|
|
|
struct berval **bvals = NULL;
|
|
char *dn;
|
|
struct berval bdn = {0, NULL};
|
|
Attribute *attr;
|
|
struct berval empty_bv = { 0, NULL };
|
|
int rc;
|
|
char *a;
|
|
|
|
ber_len_t len;
|
|
LDAPControl* rctrlp;
|
|
LDAPControl** rctrls = NULL;
|
|
BerElement* ctrl_ber;
|
|
|
|
ber_tag_t tag;
|
|
|
|
Modifications *ml = NULL;
|
|
AttributeDescription** descs;
|
|
int i;
|
|
|
|
*modlist = NULL;
|
|
|
|
if ( ldap_msgtype( msg ) != LDAP_RES_SEARCH_ENTRY ) {
|
|
#ifdef NEW_LOGGING
|
|
LDAP_LOG( OPERATION, ERR,
|
|
"Message type should be entry (%d)", ldap_msgtype( msg ), 0, 0 );
|
|
#else
|
|
Debug( LDAP_DEBUG_ANY,
|
|
"Message type should be entry (%d)", ldap_msgtype( msg ), 0, 0 );
|
|
#endif
|
|
return NULL;
|
|
}
|
|
|
|
op->o_tag = LDAP_REQ_ADD;
|
|
|
|
rc = ldap_get_dn_ber( ld, msg, &ber, &bdn );
|
|
|
|
if ( rc != LDAP_SUCCESS ) {
|
|
#ifdef NEW_LOGGING
|
|
LDAP_LOG( OPERATION, ERR,
|
|
"syncrepl_message_to_entry : dn get failed (%d)", rc, 0, 0 );
|
|
#else
|
|
Debug( LDAP_DEBUG_ANY,
|
|
"syncrepl_message_to_entry : dn get failed (%d)", rc, 0, 0 );
|
|
#endif
|
|
return NULL;
|
|
}
|
|
|
|
e = ( Entry * ) ch_calloc( 1, sizeof( Entry ));
|
|
dnPrettyNormal( NULL, &bdn, &e->e_name, &e->e_nname, NULL );
|
|
|
|
e->e_attrs = NULL;
|
|
|
|
while ( ber_remaining( ber ) ) {
|
|
tag = ber_scanf( ber, "{mW}", &tmp.sml_type, &tmp.sml_values );
|
|
|
|
if ( tag == LBER_ERROR ) break;
|
|
if ( tmp.sml_type.bv_val == NULL ) break;
|
|
|
|
mod = (Modifications *) ch_malloc( sizeof( Modifications ));
|
|
|
|
mod->sml_op = LDAP_MOD_REPLACE;
|
|
mod->sml_next = NULL;
|
|
mod->sml_desc = NULL;
|
|
mod->sml_type = tmp.sml_type;
|
|
mod->sml_bvalues = tmp.sml_bvalues;
|
|
mod->sml_nvalues = NULL;
|
|
|
|
*modtail = mod;
|
|
modtail = &mod->sml_next;
|
|
}
|
|
|
|
if ( ber_scanf( ber, "}") == LBER_ERROR ) {
|
|
#ifdef NEW_LOGGING
|
|
LDAP_LOG( OPERATION, ERR,
|
|
"syncrepl_message_to_entry: ber_scanf failed\n", 0, 0, 0 );
|
|
#else
|
|
Debug( LDAP_DEBUG_ANY, "syncrepl_message_to_entry: ber_scanf failed\n",
|
|
0, 0, 0 );
|
|
#endif
|
|
return NULL;
|
|
}
|
|
|
|
ber_free( ber, 0 );
|
|
tmpber = ldap_get_message_ber( msg );
|
|
ber = ber_dup( tmpber );
|
|
|
|
ber_scanf( ber, "{xx" );
|
|
|
|
rc = ldap_int_get_controls( ber, &rctrls );
|
|
|
|
if ( rc != LDAP_SUCCESS ) {
|
|
#ifdef NEW_LOGGING
|
|
LDAP_LOG( OPERATION, ERR,
|
|
"syncrepl_message_to_entry : control get failed (%d)", rc, 0, 0 );
|
|
#else
|
|
Debug( LDAP_DEBUG_ANY,
|
|
"syncrepl_message_to_entry : control get failed (%d)", rc, 0, 0 );
|
|
#endif
|
|
return NULL;
|
|
}
|
|
|
|
if ( rctrls ) {
|
|
rctrlp = *rctrls;
|
|
ctrl_ber = ber_alloc_t( LBER_USE_DER );
|
|
ber_set_option( ctrl_ber, LBER_OPT_BER_MEMCTX, &op->o_tmpmemctx );
|
|
ber_write( ctrl_ber, rctrlp->ldctl_value.bv_val, rctrlp->ldctl_value.bv_len, 0 );
|
|
ber_reset( ctrl_ber, 1 );
|
|
ber_scanf( ctrl_ber, "{eo", syncstate, syncUUID );
|
|
if ( ber_peek_tag( ctrl_ber, &len ) == LDAP_SYNC_TAG_COOKIE ) {
|
|
ber_scanf( ctrl_ber, "o}", syncCookie );
|
|
}
|
|
ber_free( ctrl_ber, 1 );
|
|
ldap_controls_free( rctrls );
|
|
} else {
|
|
#ifdef NEW_LOGGING
|
|
LDAP_LOG( OPERATION, ERR,"syncrepl_message_to_entry : "
|
|
" rctrls absent\n", 0, 0, 0 );
|
|
#else
|
|
Debug( LDAP_DEBUG_ANY, "syncrepl_message_to_entry :"
|
|
" rctrls absent\n", 0, 0, 0 );
|
|
#endif
|
|
}
|
|
|
|
if ( *syncstate == LDAP_SYNC_PRESENT || *syncstate == LDAP_SYNC_DELETE ) {
|
|
goto done;
|
|
}
|
|
|
|
if ( *modlist == NULL ) {
|
|
#ifdef NEW_LOGGING
|
|
LDAP_LOG( OPERATION, ERR,
|
|
"syncrepl_message_to_entry: no attributes\n", 0, 0, 0 );
|
|
#else
|
|
Debug( LDAP_DEBUG_ANY, "syncrepl_message_to_entry: no attributes\n",
|
|
0, 0, 0 );
|
|
#endif
|
|
}
|
|
|
|
ml = *modlist;
|
|
while ( ml != NULL ) {
|
|
AttributeDescription *ad = NULL;
|
|
rc = slap_bv2ad( &ml->sml_type, &ml->sml_desc, &text );
|
|
|
|
if( rc != LDAP_SUCCESS ) {
|
|
e = NULL;
|
|
goto done;
|
|
}
|
|
|
|
ad = ml->sml_desc;
|
|
ml->sml_desc = NULL;
|
|
ml = ml->sml_next;
|
|
}
|
|
|
|
rc = slap_mods_check( *modlist, 1, &text, txtbuf, textlen, NULL );
|
|
|
|
if ( rc != LDAP_SUCCESS ) {
|
|
#ifdef NEW_LOGGING
|
|
LDAP_LOG( OPERATION, ERR,
|
|
"syncrepl_message_to_entry: mods check (%s)\n", text, 0, 0 );
|
|
#else
|
|
Debug( LDAP_DEBUG_ANY, "syncrepl_message_to_entry: mods check (%s)\n",
|
|
text, 0, 0 );
|
|
#endif
|
|
return NULL;
|
|
}
|
|
|
|
rc = slap_mods2entry( *modlist, &e, 1, 1, &text, txtbuf, textlen);
|
|
if( rc != LDAP_SUCCESS ) {
|
|
#ifdef NEW_LOGGING
|
|
LDAP_LOG( OPERATION, ERR,
|
|
"syncrepl_message_to_entry: mods2entry (%s)\n", text, 0, 0 );
|
|
#else
|
|
Debug( LDAP_DEBUG_ANY, "syncrepl_message_to_entry: mods2entry (%s)\n",
|
|
text, 0, 0 );
|
|
#endif
|
|
}
|
|
|
|
done:
|
|
|
|
ber_free ( ber, 0 );
|
|
|
|
return e;
|
|
}
|
|
|
|
int
|
|
syncuuid_cmp( const void* v_uuid1, const void* v_uuid2 )
|
|
{
|
|
const struct berval *uuid1 = v_uuid1;
|
|
const struct berval *uuid2 = v_uuid2;
|
|
int rc = uuid1->bv_len - uuid2->bv_len;
|
|
if ( rc ) return rc;
|
|
return ( strcmp( uuid1->bv_val, uuid2->bv_val ) );
|
|
}
|
|
|
|
int
|
|
syncrepl_entry(
|
|
syncinfo_t* si,
|
|
LDAP *ld,
|
|
Operation *op,
|
|
Entry* e,
|
|
Modifications* modlist,
|
|
int syncstate,
|
|
struct berval* syncUUID,
|
|
struct berval* syncCookie,
|
|
int refresh
|
|
)
|
|
{
|
|
Backend *be = op->o_bd;
|
|
slap_callback cb;
|
|
struct berval csn_bv = {0, NULL};
|
|
struct berval *syncuuid_bv = NULL;
|
|
char csnbuf[ LDAP_LUTIL_CSNSTR_BUFSIZE ];
|
|
|
|
SlapReply rs = {REP_RESULT};
|
|
int rc = LDAP_SUCCESS;
|
|
|
|
struct berval base_bv = {0, NULL};
|
|
|
|
char *filterstr;
|
|
Filter *filter;
|
|
|
|
Attribute *a;
|
|
|
|
if ( refresh &&
|
|
( syncstate == LDAP_SYNC_PRESENT || syncstate == LDAP_SYNC_ADD )) {
|
|
syncuuid_bv = ber_dupbv( NULL, syncUUID );
|
|
avl_insert( &si->presentlist, (caddr_t) syncuuid_bv,
|
|
syncuuid_cmp, avl_dup_error );
|
|
}
|
|
|
|
if ( syncstate == LDAP_SYNC_PRESENT ) {
|
|
if ( e ) {
|
|
return 1;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
filterstr = (char *) sl_malloc( strlen("entryUUID=") + syncUUID->bv_len + 1,
|
|
op->o_tmpmemctx );
|
|
strcpy( filterstr, "entryUUID=" );
|
|
strcat( filterstr, syncUUID->bv_val );
|
|
|
|
si->e = e;
|
|
si->syncUUID_ndn = NULL;
|
|
|
|
filter = str2filter( filterstr );
|
|
ber_str2bv( filterstr, strlen(filterstr), 1, &op->ors_filterstr );
|
|
ch_free( filterstr );
|
|
op->ors_filter = filter;
|
|
op->ors_scope = LDAP_SCOPE_SUBTREE;
|
|
|
|
/* get syncrepl cookie of shadow replica from subentry */
|
|
ber_str2bv( si->base, strlen(si->base), 1, &base_bv );
|
|
dnPrettyNormal( 0, &base_bv, &op->o_req_dn, &op->o_req_ndn, op->o_tmpmemctx );
|
|
ch_free( base_bv.bv_val );
|
|
|
|
/* set callback function */
|
|
op->o_callback = &cb;
|
|
cb.sc_response = dn_callback;
|
|
cb.sc_private = si;
|
|
|
|
si->syncUUID_ndn = NULL;
|
|
|
|
rc = be->be_search( op, &rs );
|
|
|
|
ch_free( op->o_req_dn.bv_val );
|
|
ch_free( op->o_req_ndn.bv_val );
|
|
filter_free( op->ors_filter );
|
|
ch_free( op->ors_filterstr.bv_val );
|
|
|
|
cb.sc_response = null_callback;
|
|
cb.sc_private = si;
|
|
|
|
if ( rc == LDAP_SUCCESS && si->syncUUID_ndn && si->sync_mode != LDAP_SYNC_LOG_MODE ) {
|
|
op->o_req_dn = *si->syncUUID_ndn;
|
|
op->o_req_ndn = *si->syncUUID_ndn;
|
|
op->o_tag = LDAP_REQ_DELETE;
|
|
rc = be->be_delete( op, &rs );
|
|
}
|
|
|
|
if ( si->syncUUID_ndn ) {
|
|
ber_bvfree( si->syncUUID_ndn );
|
|
}
|
|
|
|
switch ( syncstate ) {
|
|
case LDAP_SYNC_ADD :
|
|
case LDAP_SYNC_MODIFY :
|
|
|
|
if ( rc == LDAP_SUCCESS ||
|
|
rc == LDAP_REFERRAL ||
|
|
rc == LDAP_NO_SUCH_OBJECT ) {
|
|
|
|
attr_delete( &e->e_attrs, slap_schema.si_ad_entryUUID );
|
|
attr_merge_normalize_one( e, slap_schema.si_ad_entryUUID, syncUUID, op->o_tmpmemctx );
|
|
|
|
op->o_tag = LDAP_REQ_ADD;
|
|
op->ora_e = e;
|
|
op->o_req_dn = e->e_name;
|
|
op->o_req_ndn = e->e_nname;
|
|
rc = be->be_add( op, &rs );
|
|
|
|
if ( rc != LDAP_SUCCESS ) {
|
|
if ( rc == LDAP_ALREADY_EXISTS ) {
|
|
op->o_tag = LDAP_REQ_MODIFY;
|
|
op->orm_modlist = modlist;
|
|
op->o_req_dn = e->e_name;
|
|
op->o_req_ndn = e->e_nname;
|
|
rc = be->be_modify( op, &rs );
|
|
si->e = NULL;
|
|
if ( rc != LDAP_SUCCESS ) {
|
|
#ifdef NEW_LOGGING
|
|
LDAP_LOG( OPERATION, ERR,
|
|
"syncrepl_entry : be_modify failed (%d)\n",
|
|
rc, 0, 0 );
|
|
#else
|
|
Debug( LDAP_DEBUG_ANY,
|
|
"syncrepl_entry : be_modify failed (%d)\n",
|
|
rc, 0, 0 );
|
|
#endif
|
|
}
|
|
return 1;
|
|
} else if ( rc == LDAP_REFERRAL ||
|
|
rc == LDAP_NO_SUCH_OBJECT ) {
|
|
syncrepl_add_glue( si, ld, op, e,
|
|
modlist, syncstate,
|
|
syncUUID, syncCookie);
|
|
si->e = NULL;
|
|
return 0;
|
|
} else {
|
|
#ifdef NEW_LOGGING
|
|
LDAP_LOG( OPERATION, ERR,
|
|
"syncrepl_entry : be_add failed (%d)\n",
|
|
rc, 0, 0 );
|
|
#else
|
|
Debug( LDAP_DEBUG_ANY,
|
|
"syncrepl_entry : be_add failed (%d)\n",
|
|
rc, 0, 0 );
|
|
#endif
|
|
si->e = NULL;
|
|
return 1;
|
|
}
|
|
} else {
|
|
si->e = NULL;
|
|
be_entry_release_w( op, e );
|
|
return 0;
|
|
}
|
|
} else {
|
|
#ifdef NEW_LOGGING
|
|
LDAP_LOG( OPERATION, ERR,
|
|
"syncrepl_entry : be_search failed (%d)\n", rc, 0, 0 );
|
|
#else
|
|
Debug( LDAP_DEBUG_ANY,
|
|
"syncrepl_entry : be_search failed (%d)\n", rc, 0, 0 );
|
|
#endif
|
|
si->e = NULL;
|
|
return 1;
|
|
}
|
|
|
|
case LDAP_SYNC_DELETE :
|
|
if ( si->sync_mode == LDAP_SYNC_LOG_MODE ) {
|
|
op->o_req_dn = *si->syncUUID_ndn;
|
|
op->o_req_ndn = *si->syncUUID_ndn;
|
|
op->o_tag = LDAP_REQ_DELETE;
|
|
rc = be->be_delete( op, &rs );
|
|
}
|
|
/* Already deleted otherwise */
|
|
return 1;
|
|
|
|
default :
|
|
#ifdef NEW_LOGGING
|
|
LDAP_LOG( OPERATION, ERR,
|
|
"syncrepl_entry : unknown syncstate\n", 0, 0, 0 );
|
|
#else
|
|
Debug( LDAP_DEBUG_ANY,
|
|
"syncrepl_entry : unknown syncstate\n", 0, 0, 0 );
|
|
#endif
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
static void
|
|
syncrepl_del_nonpresent(
|
|
LDAP *ld,
|
|
Operation *op
|
|
)
|
|
{
|
|
Backend* be = op->o_bd;
|
|
syncinfo_t *si = op->o_si;
|
|
slap_callback cb;
|
|
struct berval base_bv = {0, NULL};
|
|
Filter *filter;
|
|
SlapReply rs = {REP_RESULT};
|
|
struct berval filterstr_bv = {0, NULL};
|
|
struct nonpresent_entry *np_list, *np_prev;
|
|
|
|
ber_str2bv( si->base, strlen(si->base), 1, &base_bv );
|
|
dnPrettyNormal(0, &base_bv, &op->o_req_dn, &op->o_req_ndn, op->o_tmpmemctx );
|
|
ch_free( base_bv.bv_val );
|
|
|
|
filter = str2filter( si->filterstr );
|
|
|
|
cb.sc_response = nonpresent_callback;
|
|
cb.sc_private = si;
|
|
|
|
op->o_callback = &cb;
|
|
op->o_tag = LDAP_REQ_SEARCH;
|
|
op->ors_scope = si->scope;
|
|
op->ors_deref = LDAP_DEREF_NEVER;
|
|
op->ors_slimit = 0;
|
|
op->ors_tlimit = 0;
|
|
op->ors_attrsonly = 0;
|
|
op->ors_attrs = NULL;
|
|
op->ors_filter = filter;
|
|
ber_str2bv( si->filterstr, strlen( si->filterstr ), 1, &op->ors_filterstr );
|
|
|
|
op->o_nocaching = 1;
|
|
be->be_search( op, &rs );
|
|
op->o_nocaching = 0;
|
|
|
|
if ( !LDAP_LIST_EMPTY( &si->nonpresentlist ) ) {
|
|
np_list = LDAP_LIST_FIRST( &si->nonpresentlist );
|
|
while ( np_list != NULL ) {
|
|
LDAP_LIST_REMOVE( np_list, np_link );
|
|
np_prev = np_list;
|
|
np_list = LDAP_LIST_NEXT( np_list, np_link );
|
|
op->o_tag = LDAP_REQ_DELETE;
|
|
op->o_callback = &cb;
|
|
cb.sc_response = null_callback;
|
|
cb.sc_private = si;
|
|
op->o_req_dn = *np_prev->dn;
|
|
op->o_req_ndn = *np_prev->ndn;
|
|
op->o_bd->be_delete( op, &rs );
|
|
ber_bvfree( np_prev->dn );
|
|
ber_bvfree( np_prev->ndn );
|
|
op->o_req_dn.bv_val = NULL;
|
|
op->o_req_ndn.bv_val = NULL;
|
|
ch_free( np_prev );
|
|
}
|
|
}
|
|
|
|
if ( op->o_req_dn.bv_val )
|
|
ch_free( op->o_req_dn.bv_val );
|
|
if ( op->o_req_ndn.bv_val )
|
|
ch_free( op->o_req_ndn.bv_val );
|
|
filter_free( op->ors_filter );
|
|
ch_free( op->ors_filterstr.bv_val );
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
void
|
|
syncrepl_add_glue(
|
|
syncinfo_t *si,
|
|
LDAP *ld,
|
|
Operation* op,
|
|
Entry *e,
|
|
Modifications* modlist,
|
|
int syncstate,
|
|
struct berval* syncUUID,
|
|
struct berval* syncCookie
|
|
)
|
|
{
|
|
Backend *be = op->o_bd;
|
|
struct berval uuid_bv = {0, NULL};
|
|
slap_callback cb;
|
|
Attribute *a;
|
|
int rc;
|
|
char uuidbuf[ LDAP_LUTIL_UUIDSTR_BUFSIZE ];
|
|
int levels = 0;
|
|
int i, j, k;
|
|
struct berval dn = {0, NULL};
|
|
struct berval pdn = {0, NULL};
|
|
struct berval ndn = {0, NULL};
|
|
struct berval rdn = {0, NULL};
|
|
Entry *glue;
|
|
SlapReply rs = {REP_RESULT};
|
|
Connection *conn = op->o_conn;
|
|
char* ptr;
|
|
|
|
op->o_tag = LDAP_REQ_ADD;
|
|
op->o_callback = &cb;
|
|
cb.sc_response = null_callback;
|
|
cb.sc_private = si;
|
|
|
|
ber_dupbv( &dn, &e->e_nname );
|
|
ber_dupbv( &pdn, &e->e_nname );
|
|
|
|
ptr = dn.bv_val;
|
|
while ( !be_issuffix ( be, &pdn )) {
|
|
dnParent( &dn, &pdn );
|
|
dn.bv_val = pdn.bv_val;
|
|
dn.bv_len = pdn.bv_len;
|
|
levels++;
|
|
}
|
|
ch_free( ptr );
|
|
|
|
for ( i = 0; i <= levels; i++ ) {
|
|
glue = (Entry*) ch_calloc( 1, sizeof(Entry) );
|
|
ber_dupbv( &dn, &e->e_nname );
|
|
j = levels - i;
|
|
|
|
ptr = dn.bv_val;
|
|
for ( k = 0; k < j; k++ ) {
|
|
dnParent( &dn, &pdn );
|
|
dn.bv_val = pdn.bv_val;
|
|
dn.bv_len = pdn.bv_len;
|
|
}
|
|
|
|
dnPrettyNormal( 0, &dn, &pdn, &ndn, op->o_tmpmemctx );
|
|
ber_dupbv( &glue->e_name, &pdn );
|
|
ber_dupbv( &glue->e_nname, &ndn );
|
|
ch_free( ptr );
|
|
ch_free( pdn.bv_val );
|
|
ch_free( ndn.bv_val );
|
|
|
|
a = ch_calloc( 1, sizeof( Attribute ));
|
|
a->a_desc = slap_schema.si_ad_objectClass;
|
|
|
|
a->a_vals = ch_calloc( 3, sizeof( struct berval ));
|
|
ber_str2bv( "top", strlen("top"), 1, &a->a_vals[0] );
|
|
ber_str2bv( "glue", strlen("glue"), 1, &a->a_vals[1] );
|
|
a->a_vals[2].bv_len = 0;
|
|
a->a_vals[2].bv_val = NULL;
|
|
|
|
a->a_nvals = ch_calloc( 3, sizeof( struct berval ));
|
|
ber_str2bv( "top", strlen("top"), 1, &a->a_nvals[0] );
|
|
ber_str2bv( "glue", strlen("glue"), 1, &a->a_nvals[1] );
|
|
a->a_nvals[2].bv_len = 0;
|
|
a->a_nvals[2].bv_val = NULL;
|
|
|
|
a->a_next = glue->e_attrs;
|
|
glue->e_attrs = a;
|
|
|
|
a = ch_calloc( 1, sizeof( Attribute ));
|
|
a->a_desc = slap_schema.si_ad_structuralObjectClass;
|
|
|
|
a->a_vals = ch_calloc( 2, sizeof( struct berval ));
|
|
ber_str2bv( "glue", strlen("glue"), 1, &a->a_vals[0] );
|
|
a->a_vals[1].bv_len = 0;
|
|
a->a_vals[1].bv_val = NULL;
|
|
|
|
a->a_nvals = ch_calloc( 2, sizeof( struct berval ));
|
|
ber_str2bv( "glue", strlen("glue"), 1, &a->a_nvals[0] );
|
|
a->a_nvals[1].bv_len = 0;
|
|
a->a_nvals[1].bv_val = NULL;
|
|
|
|
a->a_next = glue->e_attrs;
|
|
glue->e_attrs = a;
|
|
|
|
if ( !strcmp( e->e_nname.bv_val, glue->e_nname.bv_val )) {
|
|
op->o_req_dn = e->e_name;
|
|
op->o_req_ndn = e->e_nname;
|
|
op->ora_e = e;
|
|
rc = be->be_add ( op, &rs );
|
|
if ( rc == LDAP_SUCCESS )
|
|
be_entry_release_w( op, e );
|
|
else
|
|
entry_free( e );
|
|
entry_free( glue );
|
|
} else {
|
|
op->o_req_dn = glue->e_name;
|
|
op->o_req_ndn = glue->e_nname;
|
|
op->ora_e = glue;
|
|
rc = be->be_add ( op, &rs );
|
|
if ( rc == LDAP_SUCCESS ) {
|
|
be_entry_release_w( op, glue );
|
|
} else {
|
|
/* incl. ALREADY EXIST */
|
|
entry_free( glue );
|
|
}
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void
|
|
syncrepl_updateCookie(
|
|
syncinfo_t *si,
|
|
LDAP *ld,
|
|
Operation *op,
|
|
struct berval *pdn,
|
|
struct berval *syncCookie
|
|
)
|
|
{
|
|
Backend *be = op->o_bd;
|
|
Modifications *ml;
|
|
Modifications *mlnext;
|
|
Modifications *mod;
|
|
Modifications *modlist = NULL;
|
|
Modifications **modtail = &modlist;
|
|
|
|
struct berval* ocbva = NULL;
|
|
struct berval* cnbva = NULL;
|
|
struct berval* ssbva = NULL;
|
|
struct berval* scbva = NULL;
|
|
|
|
char substr[64];
|
|
char rdnstr[67];
|
|
const char *text;
|
|
char txtbuf[SLAP_TEXT_BUFLEN];
|
|
size_t textlen = sizeof txtbuf;
|
|
|
|
Entry* e = NULL;
|
|
int rc;
|
|
|
|
struct berval sub_bv = { 0, NULL };
|
|
struct berval psubrdn = { 0, NULL };
|
|
|
|
slap_callback cb;
|
|
SlapReply rs = {REP_RESULT};
|
|
|
|
ocbva = ( struct berval * ) ch_calloc( 4, sizeof( struct berval ));
|
|
cnbva = ( struct berval * ) ch_calloc( 2, sizeof( struct berval ));
|
|
ssbva = ( struct berval * ) ch_calloc( 2, sizeof( struct berval ));
|
|
scbva = ( struct berval * ) ch_calloc( 2, sizeof( struct berval ));
|
|
|
|
/* update in memory cookie */
|
|
if ( si->syncCookie != NULL ) {
|
|
ber_bvfree( si->syncCookie );
|
|
}
|
|
si->syncCookie = ber_dupbv( NULL, syncCookie );
|
|
ber_str2bv( "top", strlen("top"), 1, &ocbva[0] );
|
|
ber_str2bv( "subentry", strlen("subentry"), 1, &ocbva[1] );
|
|
ber_str2bv( "syncConsumerSubentry",
|
|
strlen("syncConsumerSubentry"), 1, &ocbva[2] );
|
|
mod = (Modifications *) ch_calloc( 1, sizeof( Modifications ));
|
|
mod->sml_op = LDAP_MOD_REPLACE;
|
|
ber_str2bv( "objectClass", strlen("objectClass"), 1, &mod->sml_type );
|
|
mod->sml_bvalues = ocbva;
|
|
*modtail = mod;
|
|
modtail = &mod->sml_next;
|
|
|
|
sprintf( substr, "syncrepl%d", si->id );
|
|
sprintf( rdnstr, "cn=%s", substr );
|
|
ber_str2bv( substr, strlen( substr ), 1, &cnbva[0] );
|
|
ber_str2bv( rdnstr, strlen( rdnstr ), 1, &psubrdn );
|
|
mod = (Modifications *) ch_calloc( 1, sizeof( Modifications ));
|
|
mod->sml_op = LDAP_MOD_REPLACE;
|
|
ber_str2bv( "cn", strlen("cn"), 1, &mod->sml_type );
|
|
mod->sml_bvalues = cnbva;
|
|
*modtail = mod;
|
|
modtail = &mod->sml_next;
|
|
|
|
ber_dupbv( &scbva[0], si->syncCookie );
|
|
mod = (Modifications *) ch_calloc( 1, sizeof( Modifications ));
|
|
mod->sml_op = LDAP_MOD_REPLACE;
|
|
ber_str2bv( "syncreplCookie", strlen("syncreplCookie"),
|
|
1, &mod->sml_type );
|
|
mod->sml_bvalues = scbva;
|
|
*modtail = mod;
|
|
modtail = &mod->sml_next;
|
|
|
|
ber_str2bv( "{}", strlen("{}"), 1, &ssbva[0] );
|
|
mod = (Modifications *) ch_calloc( 1, sizeof( Modifications ));
|
|
mod->sml_op = LDAP_MOD_REPLACE;
|
|
ber_str2bv( "subtreeSpecification",
|
|
strlen("subtreeSpecification"), 1, &mod->sml_type );
|
|
mod->sml_bvalues = ssbva;
|
|
*modtail = mod;
|
|
modtail = &mod->sml_next;
|
|
|
|
rc = slap_mods_check( modlist, 1, &text, txtbuf, textlen, NULL );
|
|
|
|
if ( rc != LDAP_SUCCESS ) {
|
|
#ifdef NEW_LOGGING
|
|
LDAP_LOG( OPERATION, ERR,
|
|
"syncrepl_updateCookie: mods check (%s)\n", text, 0, 0 );
|
|
#else
|
|
Debug( LDAP_DEBUG_ANY, "syncrepl_updateCookie: mods check (%s)\n",
|
|
text, 0, 0 );
|
|
#endif
|
|
}
|
|
|
|
op->o_tag = LDAP_REQ_ADD;
|
|
rc = slap_mods_opattrs( op, modlist, modtail,
|
|
&text,txtbuf, textlen );
|
|
|
|
for ( ml = modlist; ml != NULL; ml = mlnext ) {
|
|
mlnext = ml->sml_next;
|
|
ml->sml_op = LDAP_MOD_REPLACE;
|
|
}
|
|
|
|
if( rc != LDAP_SUCCESS ) {
|
|
#ifdef NEW_LOGGING
|
|
LDAP_LOG( OPERATION, ERR,
|
|
"syncrepl_updateCookie: mods opattrs (%s)\n", text, 0, 0 );
|
|
#else
|
|
Debug( LDAP_DEBUG_ANY, "syncrepl_updateCookie: mods opattrs (%s)\n",
|
|
text, 0, 0 );
|
|
#endif
|
|
}
|
|
|
|
e = ( Entry * ) ch_calloc( 1, sizeof( Entry ));
|
|
|
|
build_new_dn( &sub_bv, pdn, &psubrdn, NULL );
|
|
dnPrettyNormal( NULL, &sub_bv, &e->e_name, &e->e_nname, NULL );
|
|
ch_free( sub_bv.bv_val );
|
|
ch_free( psubrdn.bv_val );
|
|
|
|
e->e_attrs = NULL;
|
|
|
|
rc = slap_mods2entry( modlist, &e, 1, 1, &text, txtbuf, textlen );
|
|
|
|
if( rc != LDAP_SUCCESS ) {
|
|
#ifdef NEW_LOGGING
|
|
LDAP_LOG( OPERATION, ERR,
|
|
"syncrepl_updateCookie: mods2entry (%s)\n", text, 0, 0 );
|
|
#else
|
|
Debug( LDAP_DEBUG_ANY, "syncrepl_updateCookie: mods2entry (%s)\n",
|
|
text, 0, 0 );
|
|
#endif
|
|
}
|
|
|
|
cb.sc_response = null_callback;
|
|
cb.sc_private = si;
|
|
|
|
op->o_callback = &cb;
|
|
op->o_req_dn = e->e_name;
|
|
op->o_req_ndn = e->e_nname;
|
|
|
|
/* update persistent cookie */
|
|
update_cookie_retry:
|
|
op->o_tag = LDAP_REQ_MODIFY;
|
|
op->orm_modlist = modlist;
|
|
rc = be->be_modify( op, &rs );
|
|
|
|
if ( rc != LDAP_SUCCESS ) {
|
|
if ( rc == LDAP_REFERRAL ||
|
|
rc == LDAP_NO_SUCH_OBJECT ) {
|
|
op->o_tag = LDAP_REQ_ADD;
|
|
op->ora_e = e;
|
|
rc = be->be_add( op, &rs );
|
|
if ( rc != LDAP_SUCCESS ) {
|
|
if ( rc == LDAP_ALREADY_EXISTS ) {
|
|
goto update_cookie_retry;
|
|
} else if ( rc == LDAP_REFERRAL ||
|
|
rc == LDAP_NO_SUCH_OBJECT ) {
|
|
#ifdef NEW_LOGGING
|
|
LDAP_LOG( OPERATION, ERR,
|
|
"cookie will be non-persistent\n",
|
|
0, 0, 0 );
|
|
#else
|
|
Debug( LDAP_DEBUG_ANY,
|
|
"cookie will be non-persistent\n",
|
|
0, 0, 0 );
|
|
#endif
|
|
} else {
|
|
#ifdef NEW_LOGGING
|
|
LDAP_LOG( OPERATION, ERR,
|
|
"be_add failed (%d)\n",
|
|
rc, 0, 0 );
|
|
#else
|
|
Debug( LDAP_DEBUG_ANY,
|
|
"be_add failed (%d)\n",
|
|
rc, 0, 0 );
|
|
#endif
|
|
}
|
|
} else {
|
|
be_entry_release_w( op, e );
|
|
goto done;
|
|
}
|
|
} else {
|
|
#ifdef NEW_LOGGING
|
|
LDAP_LOG( OPERATION, ERR,
|
|
"be_modify failed (%d)\n", rc, 0, 0 );
|
|
#else
|
|
Debug( LDAP_DEBUG_ANY,
|
|
"be_modify failed (%d)\n", rc, 0, 0 );
|
|
#endif
|
|
}
|
|
}
|
|
|
|
if ( e != NULL ) {
|
|
entry_free( e );
|
|
}
|
|
|
|
done :
|
|
|
|
if ( modlist ) {
|
|
slap_mods_free( modlist );
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void
|
|
avl_ber_bvfree( void *bv )
|
|
{
|
|
if( bv == NULL ) {
|
|
return;
|
|
}
|
|
if ( ((struct berval *)bv)->bv_val != NULL ) {
|
|
ch_free ( ((struct berval *)bv)->bv_val );
|
|
}
|
|
ch_free ( (char *) bv );
|
|
}
|
|
|
|
static int
|
|
cookie_callback(
|
|
Operation* op,
|
|
SlapReply* rs
|
|
)
|
|
{
|
|
syncinfo_t *si = op->o_callback->sc_private;
|
|
Attribute *a;
|
|
|
|
if ( rs->sr_type != REP_SEARCH ) return LDAP_SUCCESS;
|
|
|
|
a = attr_find( rs->sr_entry->e_attrs, slap_schema.si_ad_syncreplCookie );
|
|
|
|
if ( a == NULL ) {
|
|
si->syncCookie = NULL;
|
|
} else {
|
|
si->syncCookie = ber_dupbv( NULL, &a->a_vals[0] );
|
|
}
|
|
return LDAP_SUCCESS;
|
|
}
|
|
|
|
static int
|
|
dn_callback(
|
|
Operation* op,
|
|
SlapReply* rs
|
|
)
|
|
{
|
|
syncinfo_t *si = op->o_callback->sc_private;
|
|
|
|
if ( rs->sr_type == REP_SEARCH ) {
|
|
if ( si->syncUUID_ndn != NULL ) {
|
|
#ifdef NEW_LOGGING
|
|
LDAP_LOG( OPERATION, ERR,
|
|
"dn_callback : multiple entries match dn\n", 0, 0, 0 );
|
|
#else
|
|
Debug( LDAP_DEBUG_ANY,
|
|
"dn_callback : multiple entries match dn\n", 0, 0, 0 );
|
|
#endif
|
|
} else {
|
|
if ( rs->sr_entry == NULL ) {
|
|
si->syncUUID_ndn = NULL;
|
|
} else {
|
|
si->syncUUID_ndn = ber_dupbv( NULL, &rs->sr_entry->e_nname );
|
|
}
|
|
}
|
|
}
|
|
|
|
return LDAP_SUCCESS;
|
|
}
|
|
|
|
static int
|
|
nonpresent_callback(
|
|
Operation* op,
|
|
SlapReply* rs
|
|
)
|
|
{
|
|
syncinfo_t *si = op->o_callback->sc_private;
|
|
Attribute *a;
|
|
int count = 0;
|
|
struct berval* present_uuid = NULL;
|
|
slap_callback cb;
|
|
SlapReply rs_cb = {REP_RESULT};
|
|
struct nonpresent_entry *np_entry;
|
|
|
|
if ( rs->sr_type == REP_RESULT ) {
|
|
count = avl_free( si->presentlist, avl_ber_bvfree );
|
|
si->presentlist = NULL;
|
|
return LDAP_SUCCESS;
|
|
} else if ( rs->sr_type == REP_SEARCH ) {
|
|
a = attr_find( rs->sr_entry->e_attrs, slap_schema.si_ad_entryUUID );
|
|
|
|
if ( a == NULL )
|
|
return 0;
|
|
|
|
present_uuid = avl_find( si->presentlist, &a->a_vals[0], syncuuid_cmp );
|
|
|
|
if ( present_uuid == NULL ) {
|
|
np_entry = (struct nonpresent_entry *)
|
|
ch_calloc( 1, sizeof( struct nonpresent_entry ));
|
|
np_entry->dn = ber_dupbv( NULL, &rs->sr_entry->e_name );
|
|
np_entry->ndn = ber_dupbv( NULL, &rs->sr_entry->e_nname );
|
|
LDAP_LIST_INSERT_HEAD( &si->nonpresentlist, np_entry, np_link );
|
|
} else {
|
|
avl_delete( &si->presentlist,
|
|
&a->a_vals[0], syncuuid_cmp );
|
|
ch_free( present_uuid->bv_val );
|
|
ch_free( present_uuid );
|
|
}
|
|
return LDAP_SUCCESS;
|
|
} else {
|
|
return LDAP_SUCCESS;
|
|
}
|
|
|
|
}
|
|
|
|
static int
|
|
null_callback(
|
|
Operation* op,
|
|
SlapReply* rs
|
|
)
|
|
{
|
|
syncinfo_t *si = op->o_callback->sc_private;
|
|
|
|
if ( rs->sr_err != LDAP_SUCCESS &&
|
|
rs->sr_err != LDAP_REFERRAL &&
|
|
rs->sr_err != LDAP_ALREADY_EXISTS &&
|
|
rs->sr_err != LDAP_NO_SUCH_OBJECT ) {
|
|
#ifdef NEW_LOGGING
|
|
LDAP_LOG( OPERATION, ERR,
|
|
"null_callback : error code 0x%x\n",
|
|
rs->sr_err, 0, 0 );
|
|
#else
|
|
Debug( LDAP_DEBUG_ANY,
|
|
"null_callback : error code 0x%x\n",
|
|
rs->sr_err, 0, 0 );
|
|
#endif
|
|
}
|
|
return LDAP_SUCCESS;
|
|
}
|
|
|
|
static struct berval ocbva[] = {
|
|
BER_BVC("top"),
|
|
BER_BVC("subentry"),
|
|
BER_BVC("syncProviderSubentry"),
|
|
{0,NULL}
|
|
};
|
|
|
|
Entry *
|
|
slap_create_syncrepl_entry(
|
|
Backend *be,
|
|
struct berval *context_csn,
|
|
struct berval *rdn,
|
|
struct berval *cn
|
|
)
|
|
{
|
|
Entry* e;
|
|
int rc;
|
|
|
|
struct berval bv;
|
|
|
|
e = ( Entry * ) ch_calloc( 1, sizeof( Entry ));
|
|
|
|
attr_merge( e, slap_schema.si_ad_objectClass, ocbva, NULL );
|
|
|
|
attr_merge_one( e, slap_schema.si_ad_structuralObjectClass, &ocbva[1], NULL );
|
|
|
|
attr_merge_one( e, slap_schema.si_ad_cn, cn, NULL );
|
|
|
|
if ( context_csn ) {
|
|
attr_merge_one( e, slap_schema.si_ad_syncreplCookie,
|
|
context_csn, NULL );
|
|
}
|
|
|
|
bv.bv_val = "{}";
|
|
bv.bv_len = sizeof("{}")-1;
|
|
attr_merge_one( e, slap_schema.si_ad_subtreeSpecification, &bv, NULL );
|
|
|
|
build_new_dn( &e->e_name, &be->be_nsuffix[0], rdn, NULL );
|
|
ber_dupbv( &e->e_nname, &e->e_name );
|
|
|
|
return e;
|
|
}
|