From 39af2e9e98c895c5145090865d5ab7cde6cc12fd Mon Sep 17 00:00:00 2001 From: Mark Reynolds Date: Dec 10 2013 19:38:07 +0000 Subject: Ticket 47620 - 389-ds rejects nsds5ReplicaProtocolTimeout attribute Bug Description: Attempting to add/modify/delete nsds5ReplicaProtocolTimeout results in an error 53 (unwilling to perform). Fix Description: Allow nsds5ReplicaProtocolTimeout to be updated in agreements and the replica configuration. Also, made the config timeout setting dynamic. https://fedorahosted.org/389/ticket/47620 Reviewed by: rmeggins(Thanks!) (cherry picked from commit 58fca2c4e4f2120cb6e5fb249008be8f551e944c) (cherry picked from commit 490360fd96121d06fa8813e182b44d045257be98) --- diff --git a/ldap/servers/plugins/replication/repl5.h b/ldap/servers/plugins/replication/repl5.h index 92a9229..321a285 100644 --- a/ldap/servers/plugins/replication/repl5.h +++ b/ldap/servers/plugins/replication/repl5.h @@ -386,9 +386,15 @@ char **agmt_get_attrs_to_strip(Repl_Agmt *ra); int agmt_set_attrs_to_strip(Repl_Agmt *ra, Slapi_Entry *e); int agmt_set_timeout(Repl_Agmt *ra, long timeout); void agmt_update_done(Repl_Agmt *ra, int is_total); -int agmt_get_protocol_timeout(Repl_Agmt *agmt); typedef struct replica Replica; +PRUint64 agmt_get_protocol_timeout(Repl_Agmt *agmt); +void agmt_set_protocol_timeout(Repl_Agmt *agmt, PRUint64 timeout); +void agmt_update_maxcsn(Replica *r, Slapi_DN *sdn, int op, LDAPMod **mods, CSN *csn); +void add_agmt_maxcsns(Slapi_Entry *e, Replica *r); +void agmt_set_maxcsn(Repl_Agmt *ra); +void agmt_remove_maxcsn(Repl_Agmt *ra); +int agmt_maxcsn_to_smod (Replica *r, Slapi_Mod *smod); /* In repl5_agmtlist.c */ int agmtlist_config_init(); @@ -494,7 +500,6 @@ void prot_notify_window_opened (Repl_Protocol *rp); void prot_notify_window_closed (Repl_Protocol *rp); Object *prot_get_replica_object(Repl_Protocol *rp); void prot_replicate_now(Repl_Protocol *rp); -int prot_get_timeout(Repl_Protocol *rp); Repl_Protocol *agmt_get_protocol(Repl_Agmt *ra); @@ -591,7 +596,8 @@ char *replica_get_dn(Replica *r); void replica_check_for_tasks(Replica*r, Slapi_Entry *e); void replica_update_state (time_t when, void *arg); void replica_reset_csn_pl(Replica *r); -int replica_get_protocol_timeout(Replica *r); +PRUint64 replica_get_protocol_timeout(Replica *r); +void replica_set_protocol_timeout(Replica *r, PRUint64 timeout); int replica_get_backoff_min(Replica *r); int replica_get_backoff_max(Replica *r); void replica_set_backoff_min(Replica *r, int min); diff --git a/ldap/servers/plugins/replication/repl5_agmt.c b/ldap/servers/plugins/replication/repl5_agmt.c index 90d94f8..b0da172 100644 --- a/ldap/servers/plugins/replication/repl5_agmt.c +++ b/ldap/servers/plugins/replication/repl5_agmt.c @@ -142,7 +142,9 @@ typedef struct repl5agmt { char **attrs_to_strip; /* for fractional replication, if a "mod" is empty, strip out these attributes: * modifiersname, modifytimestamp, internalModifiersname, internalModifyTimestamp, etc */ int agreement_type; - PRUint64 protocol_timeout; + Slapi_Counter *protocol_timeout; + char *maxcsn; /* agmt max csn */ + Slapi_RWLock *attr_lock; /* RW lock for all the stripped attrs */ } repl5agmt; /* Forward declarations */ @@ -265,6 +267,14 @@ agmt_new_from_entry(Slapi_Entry *e) slapi_entry_get_dn_const(e)); goto loser; } + if ((ra->attr_lock = slapi_new_rwlock()) == NULL) + { + slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "Unable to create new attr lock " + "for replication agreement \"%s\" - agreement ignored.\n", + slapi_entry_get_dn_const(e)); + goto loser; + } + ra->protocol_timeout = slapi_counter_new(); /* Find all the stuff we need for the agreement */ @@ -338,19 +348,14 @@ agmt_new_from_entry(Slapi_Entry *e) tmpstr = slapi_entry_attr_get_charptr(e, type_nsds5ReplicaRoot); if (NULL != tmpstr) { + PRUint64 ptimeout = 0; + ra->replarea = slapi_sdn_new_dn_passin(tmpstr); /* If this agmt has its own timeout, grab it, otherwise use the replica's protocol timeout */ - ra->protocol_timeout = slapi_entry_attr_get_int(e, type_replicaProtocolTimeout); - if(ra->protocol_timeout == 0){ - /* grab the replica protocol timeout */ - Object *replobj = replica_get_replica_from_dn(ra->replarea); - if(replobj){ - Replica *replica =(Replica*)object_get_data (replobj); - ra->protocol_timeout = replica_get_protocol_timeout(replica); - } else { - ra->protocol_timeout = DEFAULT_PROTOCOL_TIMEOUT; - } + ptimeout = slapi_entry_attr_get_int(e, type_replicaProtocolTimeout); + if(ptimeout){ + slapi_counter_set_value(ra->protocol_timeout, ptimeout); } } @@ -613,6 +618,17 @@ agmt_delete(void **rap) if(ra->attrs_to_strip){ slapi_ch_array_free(ra->attrs_to_strip); } + if(ra->maxcsn){ + slapi_ch_free_string(&ra->maxcsn); + } + schedule_destroy(ra->schedule); + slapi_ch_free_string(&ra->long_name); + + slapi_counter_destroy(&ra->protocol_timeout); + + /* free the locks */ + PR_DestroyLock(ra->lock); + slapi_destroy_rwlock(ra->attr_lock); schedule_destroy(ra->schedule); slapi_ch_free((void **)&ra->long_name); @@ -2663,9 +2679,21 @@ agmt_update_done(Repl_Agmt *agmt, int is_total) windows_update_done(agmt, is_total); } -int +PRUint64 agmt_get_protocol_timeout(Repl_Agmt *agmt) { - return (int)agmt->protocol_timeout; + if(agmt){ + return slapi_counter_get_value(agmt->protocol_timeout); + } else { + return 0; + } +} + +void +agmt_set_protocol_timeout(Repl_Agmt *agmt, PRUint64 timeout) +{ + if(agmt){ + slapi_counter_set_value(agmt->protocol_timeout, timeout); + } } diff --git a/ldap/servers/plugins/replication/repl5_agmtlist.c b/ldap/servers/plugins/replication/repl5_agmtlist.c index 1167b0c..04891b7 100644 --- a/ldap/servers/plugins/replication/repl5_agmtlist.c +++ b/ldap/servers/plugins/replication/repl5_agmtlist.c @@ -209,6 +209,7 @@ agmtlist_modify_callback(Slapi_PBlock *pb, Slapi_Entry *entryBefore, Slapi_Entry LDAPMod **mods; char buff [SLAPI_DSE_RETURNTEXT_SIZE]; char *errortext = returntext ? returntext : buff; + char *val = NULL; int rc = SLAPI_DSE_CALLBACK_OK; Slapi_Operation *op; void *identity; @@ -243,16 +244,21 @@ agmtlist_modify_callback(Slapi_PBlock *pb, Slapi_Entry *entryBefore, Slapi_Entry slapi_pblock_get(pb, SLAPI_MODIFY_MODS, &mods); for (i = 0; NULL != mods && NULL != mods[i]; i++) { + slapi_ch_free_string(&val); if (slapi_attr_types_equivalent(mods[i]->mod_type, type_nsds5ReplicaInitialize)) { /* we don't allow delete attribute operations unless it was issued by the replication plugin - handled above */ if (mods[i]->mod_op & LDAP_MOD_DELETE) { - if(strcasecmp (mods[i]->mod_type, type_nsds5ReplicaCleanRUVnotified) == 0){ + if(strcasecmp (mods[i]->mod_type, type_nsds5ReplicaCleanRUVnotified) == 0 ){ /* allow the deletion of cleanallruv agmt attr */ continue; } + if(strcasecmp (mods[i]->mod_type, type_replicaProtocolTimeout) == 0){ + agmt_set_protocol_timeout(agmt, 0); + continue; + } slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name, "agmtlist_modify_callback: " "deletion of %s attribute is not allowed\n", type_nsds5ReplicaInitialize); @@ -262,8 +268,6 @@ agmtlist_modify_callback(Slapi_PBlock *pb, Slapi_Entry *entryBefore, Slapi_Entry } else { - char *val; - if (mods[i]->mod_bvalues && mods[i]->mod_bvalues[0]) val = slapi_berval_get_string_copy (mods[i]->mod_bvalues[0]); else @@ -304,7 +308,6 @@ agmtlist_modify_callback(Slapi_PBlock *pb, Slapi_Entry *entryBefore, Slapi_Entry val, mods[i]->mod_type); slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name, "agmtlist_modify_callback: %s\n", errortext); } - slapi_ch_free ((void**)&val); } } else if (slapi_attr_types_equivalent(mods[i]->mod_type, @@ -511,6 +514,21 @@ agmtlist_modify_callback(Slapi_PBlock *pb, Slapi_Entry *entryBefore, Slapi_Entry rc = SLAPI_DSE_CALLBACK_ERROR; } } + else if (slapi_attr_types_equivalent(mods[i]->mod_type, type_replicaProtocolTimeout)){ + if (val){ + long ptimeout = atol(val); + + if(ptimeout <= 0){ + *returncode = LDAP_UNWILLING_TO_PERFORM; + slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "attribute %s value (%s) is invalid, " + "must be a number greater than zero.\n", + type_replicaProtocolTimeout, val); + rc = SLAPI_DSE_CALLBACK_ERROR; + break; + } + agmt_set_protocol_timeout(agmt, ptimeout); + } + } else if (0 == windows_handle_modify_agreement(agmt, mods[i]->mod_type, e)) { slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name, "agmtlist_modify_callback: " @@ -561,6 +579,7 @@ done: { agmtlist_release_agmt(agmt); } + slapi_ch_free_string(&val); return rc; } diff --git a/ldap/servers/plugins/replication/repl5_inc_protocol.c b/ldap/servers/plugins/replication/repl5_inc_protocol.c index 612fe46..05074b0 100644 --- a/ldap/servers/plugins/replication/repl5_inc_protocol.c +++ b/ldap/servers/plugins/replication/repl5_inc_protocol.c @@ -1921,10 +1921,24 @@ send_updates(Private_Repl_Protocol *prp, RUV *remote_update_vector, PRUint32 *nu static int repl5_inc_stop(Private_Repl_Protocol *prp) { - int return_value; PRIntervalTime start, maxwait, now; + Replica *replica = NULL; + PRUint64 timeout; + int return_value; + + if((timeout = agmt_get_protocol_timeout(prp->agmt)) == 0){ + timeout = DEFAULT_PROTOCOL_TIMEOUT; + if(prp->replica_object){ + object_acquire(prp->replica_object); + replica = object_get_data(prp->replica_object); + if((timeout = replica_get_protocol_timeout(replica)) == 0){ + timeout = DEFAULT_PROTOCOL_TIMEOUT; + } + object_release(prp->replica_object); + } + } - maxwait = PR_SecondsToInterval(prp->timeout); + maxwait = PR_SecondsToInterval(timeout); prp->terminate = 1; event_notify(prp, EVENT_PROTOCOL_SHUTDOWN); start = PR_IntervalNow(); @@ -1939,8 +1953,8 @@ repl5_inc_stop(Private_Repl_Protocol *prp) /* Isn't listening. Do something drastic. */ return_value = -1; slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name, - "%s: repl5_inc_stop: protocol does not stop after %d seconds\n", - agmt_get_long_name(prp->agmt), (int)prp->timeout); + "%s: repl5_inc_stop: protocol does not stop after %llu seconds\n", + agmt_get_long_name(prp->agmt), (long long unsigned int)timeout); } else { @@ -2044,7 +2058,6 @@ Repl_5_Inc_Protocol_new(Repl_Protocol *rp) prp->notify_window_closed = repl5_inc_notify_window_closed; prp->update_now = repl5_inc_update_now; prp->replica_object = prot_get_replica_object(rp); - prp->timeout = prot_get_timeout(rp); if ((prp->lock = PR_NewLock()) == NULL) { goto loser; diff --git a/ldap/servers/plugins/replication/repl5_prot_private.h b/ldap/servers/plugins/replication/repl5_prot_private.h index 37072ee..586e1eb 100644 --- a/ldap/servers/plugins/replication/repl5_prot_private.h +++ b/ldap/servers/plugins/replication/repl5_prot_private.h @@ -75,7 +75,6 @@ typedef struct private_repl_protocol int repl50consumer; /* Flag to tell us if this is a 5.0-style consumer we're talking to */ int repl71consumer; /* Flag to tell us if this is a 7.1-style consumer we're talking to */ int repl90consumer; /* Flag to tell us if this is a 9.0-style consumer we're talking to */ - PRUint64 timeout; } Private_Repl_Protocol; extern Private_Repl_Protocol *Repl_5_Inc_Protocol_new(); diff --git a/ldap/servers/plugins/replication/repl5_protocol.c b/ldap/servers/plugins/replication/repl5_protocol.c index 34fe8a0..0e9668d 100644 --- a/ldap/servers/plugins/replication/repl5_protocol.c +++ b/ldap/servers/plugins/replication/repl5_protocol.c @@ -71,8 +71,7 @@ typedef struct repl_protocol Object *replica_object; /* Local replica. If non-NULL, replica object is acquired */ int state; int next_state; - PRUint64 protocol_timeout; - PRThread *agmt_thread; + PRThread *agmt_thread; PRLock *lock; } repl_protocol; @@ -134,16 +133,17 @@ prot_new(Repl_Agmt *agmt, int protocol_state) rp->prp_total = private_protocol_factory(rp, PROTOCOL_WINDOWS_TOTAL); rp->delete_conn = windows_conn_delete; } - rp->protocol_timeout = agmt_get_protocol_timeout(agmt); - /* XXXggood register callback handlers for entries updated, and schedule window enter/leave. */ goto done; + loser: prot_delete(&rp); + done: slapi_sdn_free(&replarea_sdn); + return rp; } @@ -593,8 +593,3 @@ private_protocol_factory(Repl_Protocol *rp, int type) return prp; } -int -prot_get_timeout(Repl_Protocol *rp) -{ - return (int)rp->protocol_timeout; -} diff --git a/ldap/servers/plugins/replication/repl5_replica.c b/ldap/servers/plugins/replication/repl5_replica.c index 8a1c590..02d4e74 100644 --- a/ldap/servers/plugins/replication/repl5_replica.c +++ b/ldap/servers/plugins/replication/repl5_replica.c @@ -87,7 +87,7 @@ struct replica { PRBool state_update_inprogress; /* replica state is being updated */ PRLock *agmt_lock; /* protects agreement creation, start and stop */ char *locking_purl; /* supplier who has exclusive access */ - PRUint64 protocol_timeout; /* protocol shutdown timeout */ + Slapi_Counter *protocol_timeout; /* protocol shutdown timeout */ PRUint64 backoff_min; /* backoff retry minimum */ PRUint64 backoff_max; /* backoff retry maximum */ }; @@ -164,26 +164,26 @@ replica_new(const Slapi_DN *root) Replica * replica_new_from_entry (Slapi_Entry *e, char *errortext, PRBool is_add_operation) { - int rc = 0; - Replica *r; + int rc = 0; + Replica *r; char *repl_name = NULL; - if (e == NULL) - { - if (NULL != errortext) + if (e == NULL) + { + if (NULL != errortext) { - PR_snprintf(errortext, SLAPI_DSE_RETURNTEXT_SIZE, "NULL entry"); + PR_snprintf(errortext, SLAPI_DSE_RETURNTEXT_SIZE, "NULL entry"); } - return NULL; - } + return NULL; + } - r = (Replica *)slapi_ch_calloc(1, sizeof(Replica)); + r = (Replica *)slapi_ch_calloc(1, sizeof(Replica)); - if (!r) + if (!r) { - if (NULL != errortext) + if (NULL != errortext) { - PR_snprintf(errortext, SLAPI_DSE_RETURNTEXT_SIZE, "Out of memory"); + PR_snprintf(errortext, SLAPI_DSE_RETURNTEXT_SIZE, "Out of memory"); } rc = -1; goto done; @@ -208,6 +208,7 @@ replica_new_from_entry (Slapi_Entry *e, char *errortext, PRBool is_add_operation rc = -1; goto done; } + r->protocol_timeout = slapi_counter_new(); /* read parameters from the replica config entry */ rc = _replica_init_from_config (r, e, errortext); @@ -403,6 +404,8 @@ replica_destroy(void **arg) csnplFree(&r->min_csn_pl);; } + slapi_counter_destroy(&r->protocol_timeout); + slapi_ch_free((void **)arg); } @@ -796,10 +799,22 @@ replica_get_type (const Replica *r) return r->repl_type; } -int +PRUint64 replica_get_protocol_timeout(Replica *r) { - return (int)r->protocol_timeout; + if(r){ + return slapi_counter_get_value(r->protocol_timeout); + } else { + return 0; + } +} + +void +replica_set_protocol_timeout(Replica *r, PRUint64 timeout) +{ + if(r){ + slapi_counter_set_value(r->protocol_timeout, timeout); + } } /* @@ -1659,6 +1674,7 @@ _replica_init_from_config (Replica *r, Slapi_Entry *e, char *errortext) char *val; int backoff_min; int backoff_max; + int ptimeout = 0; int rc; PR_ASSERT (r && e); @@ -1731,9 +1747,11 @@ _replica_init_from_config (Replica *r, Slapi_Entry *e, char *errortext) } /* get the protocol timeout */ - r->protocol_timeout = slapi_entry_attr_get_int(e, type_replicaProtocolTimeout); - if(r->protocol_timeout == 0){ - r->protocol_timeout = DEFAULT_PROTOCOL_TIMEOUT; + ptimeout = slapi_entry_attr_get_int(e, type_replicaProtocolTimeout); + if(ptimeout <= 0){ + slapi_counter_set_value(r->protocol_timeout, DEFAULT_PROTOCOL_TIMEOUT); + } else { + slapi_counter_set_value(r->protocol_timeout, ptimeout); } /* get replica flags */ diff --git a/ldap/servers/plugins/replication/repl5_replica_config.c b/ldap/servers/plugins/replication/repl5_replica_config.c index 94c23c0..9452d51 100644 --- a/ldap/servers/plugins/replication/repl5_replica_config.c +++ b/ldap/servers/plugins/replication/repl5_replica_config.c @@ -396,9 +396,16 @@ replica_config_modify (Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* else if (strcasecmp (config_attr, type_replicaCleanRUV) == 0 || strcasecmp (config_attr, type_replicaAbortCleanRUV) == 0) { - /* only allow the deletion of the cleanAllRUV config attributes */ + /* + * Only allow the deletion of the cleanAllRUV config attributes, and the + * protocol timeout. + */ continue; } + else if (strcasecmp (config_attr, type_replicaProtocolTimeout) == 0 ) + { + replica_set_protocol_timeout(r, DEFAULT_PROTOCOL_TIMEOUT); + } else { *returncode = LDAP_UNWILLING_TO_PERFORM; @@ -487,6 +494,22 @@ replica_config_modify (Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* { *returncode = LDAP_SUCCESS; } + else if (strcasecmp (config_attr, type_replicaProtocolTimeout) == 0 ){ + if (apply_mods && config_attr_value && config_attr_value[0]) + { + long ptimeout = atol(config_attr_value); + + if(ptimeout <= 0){ + *returncode = LDAP_UNWILLING_TO_PERFORM; + PR_snprintf (errortext, SLAPI_DSE_RETURNTEXT_SIZE, + "attribute %s value (%s) is invalid, must be a number greater than zero.\n", + config_attr, config_attr_value); + slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "replica_config_modify: %s\n", errortext); + } else { + replica_set_protocol_timeout(r, ptimeout); + } + } + } else { *returncode = LDAP_UNWILLING_TO_PERFORM; diff --git a/ldap/servers/plugins/replication/repl5_tot_protocol.c b/ldap/servers/plugins/replication/repl5_tot_protocol.c index 5bb203a..a241128 100644 --- a/ldap/servers/plugins/replication/repl5_tot_protocol.c +++ b/ldap/servers/plugins/replication/repl5_tot_protocol.c @@ -505,11 +505,22 @@ static int repl5_tot_stop(Private_Repl_Protocol *prp) { int return_value; - int seconds = 600; PRIntervalTime start, maxwait, now; + PRUint64 timeout = DEFAULT_PROTOCOL_TIMEOUT; + Replica *replica = NULL; + + if((timeout = agmt_get_protocol_timeout(prp->agmt)) == 0){ + timeout = DEFAULT_PROTOCOL_TIMEOUT; + if(prp->replica_object){ + replica = object_get_data(prp->replica_object); + if((timeout = replica_get_protocol_timeout(replica)) == 0){ + timeout = DEFAULT_PROTOCOL_TIMEOUT; + } + } + } prp->terminate = 1; - maxwait = PR_SecondsToInterval(seconds); + maxwait = PR_SecondsToInterval(timeout); start = PR_IntervalNow(); now = start; while (!prp->stopped && ((now - start) < maxwait)) @@ -567,7 +578,6 @@ Repl_5_Tot_Protocol_new(Repl_Protocol *rp) prp->notify_window_opened = repl5_tot_noop; prp->notify_window_closed = repl5_tot_noop; prp->update_now = repl5_tot_noop; - prp->timeout = DEFAULT_PROTOCOL_TIMEOUT; if ((prp->lock = PR_NewLock()) == NULL) { goto loser; @@ -588,6 +598,7 @@ Repl_5_Tot_Protocol_new(Repl_Protocol *rp) prp->repl50consumer = 0; prp->repl71consumer = 0; prp->repl90consumer = 0; + prp->replica_object = prot_get_replica_object(rp); return prp; loser: repl5_tot_delete(&prp);