The proxy returns \fIunwillingToPerform\fP if an operation that is
incompatible with the requested protocol is attempted.
-.TP
-.B single\-conn {NO|yes}
-Discards current cached connection when the client rebinds.
-
.TP
.B proxy\-whoami {NO|yes}
Turns on proxying of the WhoAmI extended operation. If this option is
be handled by the local slapd, as before. This option is mainly useful
in conjunction with Proxy Authorization.
+.TP
+.B quarantine <interval>,<num>[;<interval>,<num>[...]]
+Turns on quarantine of URIs that returned
+.IR LDAP_UNAVAILABLE ,
+so that an attempt to reconnect only occurs at given intervals instead
+of any time a client requests an operation.
+The pattern is: retry only after at least
+.I interval
+seconds elapsed since last attempt, for exactly
+.I num
+times; then use the next pattern.
+If
+.I num
+for the last pattern is "\fB+\fP", it retries forever; otherwise,
+no more retries occur.
+The process can be restarted by resetting the \fIolcDbQuarantine\fP
+attribute of the database entry in the configuration backend.
+
.TP
.B rebind-as-user {NO|yes}
If this option is given, the client's bind credentials are remembered
for rebinds when chasing referrals. Useful when
\fBchase-referrals\fP is set to \fByes\fP, useless otherwise.
+.TP
+.B single\-conn {NO|yes}
+Discards current cached connection when the client rebinds.
+
.TP
.B t-f-support {NO|yes|discover}
enable if the remote server supports absolute filters
deprecated and should be no longer used, as they might disappear
in future releases.
-.TP
-.B server <hostname[:port]>
-this directive is no longer supported. Use the
-.B uri
-directive as described above.
-
.TP
.B acl-authcDN "<administrative DN for access control purposes>"
DN which is used to query the target server for acl checking; it
.BR idassert-bind ,
and will be dismissed in the future.
+.TP
+.B server <hostname[:port]>
+this directive is no longer supported. Use the
+.B uri
+directive as described above.
+
.TP
.B suffixmassage, map, rewrite*
These directives are no longer supported by back-ldap; their
causes the authentication to the remote servers with the pseudo-root
identity to be deferred until actually needed by subsequent operations.
+.TP
+.B quarantine <interval>,<num>[;<interval>,<num>[...]]
+Turns on quarantine of URIs that returned
+.IR LDAP_UNAVAILABLE ,
+so that an attempt to reconnect only occurs at given intervals instead
+of any time a client requests an operation.
+The pattern is: retry only after at least
+.I interval
+seconds elapsed since last attempt, for exactly
+.I num
+times; then use the next pattern.
+If
+.I num
+for the last pattern is "\fB+\fP", it retries forever; otherwise,
+no more retries occur.
+This directive must appear before any target specification;
+it affects all targets with the same pattern.
+
.TP
.B rebind-as-user {NO|yes}
If this option is given, the client's bind credentials are remembered
Avlnode *lai_tree;
} ldap_avl_info_t;
+typedef struct slap_retry_info_t {
+ time_t *ri_interval;
+ int *ri_num;
+ int ri_idx;
+ int ri_count;
+ time_t ri_last;
+
+#define SLAP_RETRYNUM_FOREVER (-1) /* retry forever */
+#define SLAP_RETRYNUM_TAIL (-2) /* end of retrynum array */
+#define SLAP_RETRYNUM_VALID(n) ((n) >= SLAP_RETRYNUM_FOREVER) /* valid retrynum */
+#define SLAP_RETRYNUM_FINITE(n) ((n) > SLAP_RETRYNUM_FOREVER) /* not forever */
+} slap_retry_info_t;
+
typedef struct ldapinfo_t {
/* li_uri: the string that goes into ldap_initialize()
* TODO: use li_acl.sb_uri instead */
- char *li_uri;
+ char *li_uri;
/* li_bvuri: an array of each single URI that is equivalent;
* to be checked for the presence of a certain item */
- BerVarray li_bvuri;
+ BerVarray li_bvuri;
ldap_pvt_thread_mutex_t li_uri_mutex;
LDAP_REBIND_PROC *li_rebind_f;
ldap_avl_info_t li_conninfo;
+ slap_retry_info_t li_quarantine;
+ /* NOTE: quarantine uses the connection mutex */
+ sig_atomic_t li_isquarantined;
+#define LDAP_BACK_FQ_NO (0)
+#define LDAP_BACK_FQ_YES (1)
+#define LDAP_BACK_FQ_RETRYING (2)
+
+#define LDAP_BACK_QUARANTINE(li) ( (li)->li_quarantine.ri_num != NULL )
+
time_t li_network_timeout;
time_t li_conn_ttl;
time_t li_idle_timeout;
lc_curr = { 0 };
int refcnt = 1, binding = 1;
+ /* if the server is quarantined, and
+ * - the current interval did not expire yet, or
+ * - no more retries should occur,
+ * don't return the connection */
+ if ( li->li_isquarantined ) {
+ slap_retry_info_t *ri = &li->li_quarantine;
+ int dont_retry = 1;
+
+ ldap_pvt_thread_mutex_lock( &li->li_conninfo.lai_mutex );
+ if ( li->li_isquarantined == LDAP_BACK_FQ_YES ) {
+ dont_retry = ( ri->ri_num[ ri->ri_idx ] == SLAP_RETRYNUM_TAIL
+ || slap_get_time() < ri->ri_last + ri->ri_interval[ ri->ri_idx ] );
+ if ( !dont_retry ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s: ldap_back_getconn quarantine "
+ "retry block #%d try #%d.\n",
+ op->o_log_prefix, ri->ri_idx, ri->ri_count );
+ li->li_isquarantined = LDAP_BACK_FQ_RETRYING;
+ }
+ }
+ ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
+
+ if ( dont_retry ) {
+ rs->sr_err = LDAP_UNAVAILABLE;
+ if ( op->o_conn && ( sendok & LDAP_BACK_SENDERR ) ) {
+ send_ldap_result( op, rs );
+ }
+ return NULL;
+ }
+ }
+
/* Internal searches are privileged and shared. So is root. */
if ( op->o_do_not_cache || be_isroot( op ) ) {
LDAP_BACK_CONN_ISPRIV_SET( &lc_curr );
ldap_pvt_thread_yield();
goto retry_lock;
}
+
refcnt = ++lc->lc_refcnt;
binding = ++lc->lc_binding;
}
}
} else {
- char buf[ SLAP_TEXT_BUFLEN ];
int expiring = 0;
if ( ( li->li_idle_timeout != 0 && op->o_time > lc->lc_time + li->li_idle_timeout )
}
if ( LogTest( LDAP_DEBUG_TRACE ) ) {
+ char buf[ SLAP_TEXT_BUFLEN ];
+
snprintf( buf, sizeof( buf ),
"conn %p fetched refcnt=%u binding=%u%s",
(void *)lc, refcnt, binding, expiring ? " expiring" : "" );
Debug( LDAP_DEBUG_TRACE,
"=>ldap_back_getconn: %s.\n", buf, 0, 0 );
}
-
}
#ifdef HAVE_TLS
}
}
+void
+ldap_back_quarantine(
+ Operation *op,
+ SlapReply *rs,
+ int dolock )
+{
+ ldapinfo_t *li = (ldapinfo_t *)op->o_bd->be_private;
+
+ slap_retry_info_t *ri = &li->li_quarantine;
+
+ if ( dolock ) {
+ ldap_pvt_thread_mutex_lock( &li->li_conninfo.lai_mutex );
+ }
+
+ if ( rs->sr_err == LDAP_UNAVAILABLE ) {
+ switch ( li->li_isquarantined ) {
+ case LDAP_BACK_FQ_NO:
+ Debug( LDAP_DEBUG_ANY,
+ "%s: ldap_back_quarantine enter.\n",
+ op->o_log_prefix, 0, 0 );
+
+ ri->ri_idx = 0;
+ ri->ri_count = 0;
+ break;
+
+ case LDAP_BACK_FQ_RETRYING:
+ Debug( LDAP_DEBUG_ANY,
+ "%s: ldap_back_quarantine block #%d try #%d failed.\n",
+ op->o_log_prefix, ri->ri_idx, ri->ri_count );
+
+ ++ri->ri_count;
+ if ( ri->ri_num[ ri->ri_idx ] != SLAP_RETRYNUM_FOREVER
+ && ri->ri_count == ri->ri_num[ ri->ri_idx ] )
+ {
+ ri->ri_count = 0;
+ ++ri->ri_idx;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ li->li_isquarantined = LDAP_BACK_FQ_YES;
+ ri->ri_last = slap_get_time();
+
+ } else if ( li->li_isquarantined != LDAP_BACK_FQ_NO ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s: ldap_back_quarantine exit.\n",
+ op->o_log_prefix, ri->ri_idx, ri->ri_count );
+
+ ri->ri_count = 0;
+ ri->ri_idx = 0;
+ li->li_isquarantined = LDAP_BACK_FQ_NO;
+ }
+
+ if ( dolock ) {
+ ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
+ }
+}
+
/*
* ldap_back_dobind
*
if ( li->li_acl_secprops != NULL ) {
rc = ldap_set_option( lc->lc_ld,
- LDAP_OPT_X_SASL_SECPROPS, li->li_acl_secprops);
+ LDAP_OPT_X_SASL_SECPROPS, li->li_acl_secprops );
if ( rc != LDAP_OPT_SUCCESS ) {
Debug( LDAP_DEBUG_ANY, "Error: ldap_set_option "
}
}
+ if ( LDAP_BACK_QUARANTINE( li ) ) {
+ ldap_back_quarantine( op, rs, dolock );
+ }
+
+ /* FIXME: one binding-- too many? */
lc->lc_binding--;
ldap_back_freeconn( op, lc, dolock );
rs->sr_err = slap_map_api2result( rs );
}
done:;
+ if ( LDAP_BACK_QUARANTINE( li ) ) {
+ ldap_back_quarantine( op, rs, dolock );
+ }
+
lc->lc_binding--;
LDAP_BACK_CONN_BINDING_CLEAR( lc );
rc = LDAP_BACK_CONN_ISBOUND( lc );
time_t timeout,
ldap_back_send_t sendok )
{
+ ldapinfo_t *li = (ldapinfo_t *)op->o_bd->be_private;
+
char *match = NULL;
LDAPMessage *res = NULL;
char *text = NULL;
rs->sr_matched = match;
}
}
+ if ( LDAP_BACK_QUARANTINE( li ) ) {
+ ldap_back_quarantine( op, rs, 1 );
+ }
if ( op->o_conn &&
( ( sendok & LDAP_BACK_SENDOK )
|| ( ( sendok & LDAP_BACK_SENDERR ) && rs->sr_err != LDAP_SUCCESS ) ) )
rc = ldap_back_prepare_conn( lcp, op, rs, sendok );
if ( rc != LDAP_SUCCESS ) {
rc = 0;
+ /* freeit, because lc_refcnt == 1 */
+ (void)ldap_back_conn_free( *lcp );
*lcp = NULL;
} else {
rc = ldap_back_dobind_int( *lcp, op, rs, sendok, 0, 0 );
- if ( rc == 0 ) {
+ if ( rc == 0 && *lcp != NULL ) {
+ /* freeit, because lc_refcnt == 1 */
+ (void)ldap_back_conn_free( *lcp );
*lcp = NULL;
}
}
LDAP_BACK_CFG_VERSION,
LDAP_BACK_CFG_SINGLECONN,
LDAP_BACK_CFG_CANCEL,
+ LDAP_BACK_CFG_QUARANTINE,
LDAP_BACK_CFG_REWRITE,
LDAP_BACK_CFG_LAST
"SYNTAX OMsDirectoryString "
"SINGLE-VALUE )",
NULL, NULL },
+ { "quarantine", "retrylist", 2, 0, 0,
+ ARG_MAGIC|LDAP_BACK_CFG_QUARANTINE,
+ ldap_back_cf_gen, "( OLcfgDbAt:3.21 "
+ "NAME 'olcDbQuarantine' "
+ "DESC 'Quarantine database if connection fails and retry according to rule' "
+ "SYNTAX OMsDirectoryString "
+ "SINGLE-VALUE )",
+ NULL, NULL },
{ "suffixmassage", "[virtual]> <real", 2, 3, 0,
ARG_STRING|ARG_MAGIC|LDAP_BACK_CFG_REWRITE,
ldap_back_cf_gen, NULL, NULL, NULL },
"$ olcDbIdleTimeout "
"$ olcDbSingleConn "
"$ olcDbCancel "
+ "$ olcDbQuarantine "
") )",
Cft_Database, ldapcfg},
{ NULL, 0, NULL }
{ BER_BVNULL, 0, 0, 0, NULL }
};
+int
+slap_retry_info_parse(
+ char *in,
+ slap_retry_info_t *ri,
+ char *buf,
+ ber_len_t buflen )
+{
+ char **retrylist = NULL;
+ int rc = 0;
+ int i;
+
+ slap_str2clist( &retrylist, in, " ;" );
+ if ( retrylist == NULL ) {
+ return 1;
+ }
+
+ for ( i = 0; retrylist[ i ] != NULL; i++ )
+ /* count */ ;
+
+ ri->ri_interval = ch_calloc( sizeof( time_t ), i + 1 );
+ ri->ri_num = ch_calloc( sizeof( int ), i + 1 );
+
+ for ( i = 0; retrylist[ i ] != NULL; i++ ) {
+ char *sep = strchr( retrylist[ i ], ',' );
+
+ if ( sep == NULL ) {
+ snprintf( buf, buflen,
+ "missing comma in retry pattern #%d \"%s\"",
+ i, retrylist[ i ] );
+ rc = 1;
+ goto done;
+ }
+
+ *sep++ = '\0';
+
+ if ( lutil_atol( &ri->ri_interval[ i ], retrylist[ i ] ) ) {
+ snprintf( buf, buflen,
+ "unable to parse interval #%d \"%s\"",
+ i, retrylist[ i ] );
+ rc = 1;
+ goto done;
+ }
+
+ if ( strcmp( sep, "+" ) == 0 ) {
+ if ( retrylist[ i + 1 ] != NULL ) {
+ snprintf( buf, buflen,
+ "extra cruft after retry pattern "
+ "#%d \"%s,+\" with \"forever\" mark",
+ i, retrylist[ i ] );
+ rc = 1;
+ goto done;
+ }
+ ri->ri_num[ i ] = SLAP_RETRYNUM_FOREVER;
+
+ } else if ( lutil_atoi( &ri->ri_num[ i ], sep ) ) {
+ snprintf( buf, buflen,
+ "unable to parse retry num #%d \"%s\"",
+ i, sep );
+ rc = 1;
+ goto done;
+ }
+ }
+
+ ri->ri_num[ i ] = SLAP_RETRYNUM_TAIL;
+
+ ri->ri_idx = 0;
+ ri->ri_count = 0;
+ ri->ri_last = (time_t)(-1);
+
+done:;
+ ldap_charray_free( retrylist );
+
+ if ( rc ) {
+ slap_retry_info_destroy( ri );
+ }
+
+ return rc;
+}
+
+int
+slap_retry_info_unparse(
+ slap_retry_info_t *ri,
+ struct berval *bvout )
+{
+ int i;
+ char buf[ BUFSIZ * 2 ],
+ *ptr = buf;
+ struct berval bv = BER_BVNULL;
+
+ assert( ri != NULL );
+ assert( bvout != NULL );
+
+ BER_BVZERO( bvout );
+
+#define WHATSLEFT ( sizeof( buf ) - ( ptr - buf ) )
+
+ for ( i = 0; ri->ri_num[ i ] != SLAP_RETRYNUM_TAIL; i++ ) {
+ if ( i > 0 ) {
+ if ( WHATSLEFT <= 1 ) {
+ return 1;
+ }
+ *ptr++ = ';';
+ }
+
+ ptr += snprintf( ptr, WHATSLEFT, "%ld,", (long)ri->ri_interval[i] );
+ if ( WHATSLEFT <= 0 ) {
+ return 1;
+ }
+
+ if ( ri->ri_num[i] == SLAP_RETRYNUM_FOREVER ) {
+ if ( WHATSLEFT <= 1 ) {
+ return 1;
+ }
+ *ptr++ = '+';
+
+ } else {
+ ptr += snprintf( ptr, WHATSLEFT, "%d", ri->ri_num[i] );
+ if ( WHATSLEFT <= 0 ) {
+ return 1;
+ }
+ }
+ }
+
+ bv.bv_val = buf;
+ bv.bv_len = ptr - buf;
+
+ ber_dupbv( bvout, &bv );
+
+ return 0;
+}
+
+void
+slap_retry_info_destroy(
+ slap_retry_info_t *ri )
+{
+ assert( ri != NULL );
+
+ assert( ri->ri_interval != NULL );
+ ch_free( ri->ri_interval );
+ ri->ri_interval = NULL;
+
+ assert( ri->ri_num != NULL );
+ ch_free( ri->ri_num );
+ ri->ri_num = NULL;
+}
+
static int
ldap_back_cf_gen( ConfigArgs *c )
{
ldapinfo_t *li = ( ldapinfo_t * )c->be->be_private;
- int rc;
+ int rc = 0;
int i;
if ( c->op == SLAP_CONFIG_EMIT ) {
struct berval bv = BER_BVNULL;
- rc = 0;
if ( li == NULL ) {
return 1;
}
} break;
+ case LDAP_BACK_CFG_QUARANTINE:
+ if ( !LDAP_BACK_QUARANTINE( li ) ) {
+ rc = 1;
+ break;
+ }
+
+ rc = slap_retry_info_unparse( &li->li_quarantine, &bv );
+ if ( rc == 0 ) {
+ ber_bvarray_add( &c->rvalue_vals, &bv );
+ }
+ break;
+
default:
/* FIXME: we need to handle all... */
assert( 0 );
return rc;
} else if ( c->op == LDAP_MOD_DELETE ) {
- rc = 0;
switch( c->type ) {
case LDAP_BACK_CFG_URI:
if ( li->li_uri != NULL ) {
li->li_flags &= ~LDAP_BACK_F_SINGLECONN;
break;
+ case LDAP_BACK_CFG_QUARANTINE:
+ if ( !LDAP_BACK_QUARANTINE( li ) ) {
+ break;
+ }
+
+ slap_retry_info_destroy( &li->li_quarantine );
+ li->li_isquarantined = 0;
+ break;
+
default:
/* FIXME: we need to handle all... */
assert( 0 );
li->li_flags |= mask;
} break;
+ case LDAP_BACK_CFG_QUARANTINE:
+ if ( LDAP_BACK_QUARANTINE( li ) ) {
+ snprintf( c->msg, sizeof( c->msg ),
+ "quarantine already defined" );
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->msg, 0 );
+ return 1;
+ }
+ rc = slap_retry_info_parse( c->argv[1], &li->li_quarantine,
+ c->msg, sizeof( c->msg ) );
+ if ( rc ) {
+ Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->msg, 0 );
+ }
+ /* give it a chance to retry if the pattern gets reset
+ * via back-config */
+ li->li_isquarantined = 0;
+ break;
+
case LDAP_BACK_CFG_REWRITE:
snprintf( c->msg, sizeof( c->msg ),
"rewrite/remap capabilities have been moved "
break;
}
- return 0;
+ return rc;
}
int
Operation *op,
SlapReply *rs )
{
+ ldapinfo_t *li = (ldapinfo_t *) op->o_bd->be_private;
+
ldapconn_t *lc;
req_pwdexop_s *qpw = &op->oq_pwdexop;
LDAPMessage *res;
goto retry;
}
}
+
+ if ( LDAP_BACK_QUARANTINE( li ) ) {
+ ldap_back_quarantine( op, rs, 1 );
+ }
+
if ( text ) rs->sr_text = text;
send_ldap_extended( op, rs );
/* otherwise frontend resends result */
rc = rs->sr_err = SLAPD_ABANDON;
+
+ } else if ( LDAP_BACK_QUARANTINE( li ) ) {
+ ldap_back_quarantine( op, rs, 1 );
}
/* these have to be freed anyway... */
Operation *op,
SlapReply *rs )
{
+ ldapinfo_t *li = (ldapinfo_t *) op->o_bd->be_private;
+
ldapconn_t *lc;
LDAPMessage *res;
ber_int_t msgid;
goto retry;
}
}
+
+ if ( LDAP_BACK_QUARANTINE( li ) ) {
+ ldap_back_quarantine( op, rs, 1 );
+ }
+
if ( text ) rs->sr_text = text;
send_ldap_extended( op, rs );
/* otherwise frontend resends result */
rc = rs->sr_err = SLAPD_ABANDON;
+
+ } else if ( LDAP_BACK_QUARANTINE( li ) ) {
+ ldap_back_quarantine( op, rs, 1 );
}
/* these have to be freed anyway... */
if ( li->li_conninfo.lai_tree ) {
avl_free( li->li_conninfo.lai_tree, ldap_back_conn_free );
}
+ if ( LDAP_BACK_QUARANTINE( li ) ) {
+ slap_retry_info_destroy( &li->li_quarantine );
+ }
ldap_pvt_thread_mutex_unlock( &li->li_conninfo.lai_mutex );
ldap_pvt_thread_mutex_destroy( &li->li_conninfo.lai_mutex );
Operation *op,
LDAPControl ***pctrls );
+extern void
+ldap_back_quarantine(
+ Operation *op,
+ SlapReply *rs,
+ int dolock );
+
+extern void slap_retry_info_destroy( slap_retry_info_t *ri );
+extern int slap_retry_info_parse( char *in, slap_retry_info_t *ri,
+ char *buf, ber_len_t buflen );
+extern int slap_retry_info_unparse( slap_retry_info_t *ri, struct berval *bvout );
+
+
extern int chain_initialize( void );
#ifdef LDAP_DEVEL
extern int distproc_initialize( void );
Operation *op,
SlapReply *rs )
{
+ ldapinfo_t *li = (ldapinfo_t *) op->o_bd->be_private;
+
ldapconn_t *lc;
struct timeval tv;
time_t stoptime = (time_t)-1;
}
finish:;
+ if ( LDAP_BACK_QUARANTINE( li ) ) {
+ ldap_back_quarantine( op, rs, 1 );
+ }
+
if ( rc != SLAPD_ABANDON ) {
send_ldap_result( op, rs );
}
} else {
send_ldap_result( op, rs );
+
+ if ( META_BACK_QUARANTINE( mi ) ) {
+ meta_back_quarantine( op, rs, candidate, 1 );
+ }
}
cleanup:;
struct ldaprwmap mt_rwmap;
+ sig_atomic_t mt_isquarantined;
+ slap_retry_info_t mt_quarantine;
+
unsigned mt_flags;
#define META_BACK_TGT_ISSET(mt,f) ( ( (mt)->mt_flags & (f) ) == (f) )
#define META_BACK_TGT_ISMASK(mt,m,f) ( ( (mt)->mt_flags & (m) ) == (f) )
ldap_avl_info_t mi_conninfo;
+ /* NOTE: quarantine uses the connection mutex */
+ slap_retry_info_t mi_quarantine;
+
+#define META_BACK_QUARANTINE(mi) ( (mi)->mi_quarantine.ri_num != NULL )
+
unsigned mi_flags;
#define li_flags mi_flags
/* uses flags as defined in <back-ldap/back-ldap.h> */
meta_back_init_one_conn(
Operation *op,
SlapReply *rs,
- metatarget_t *mt,
metaconn_t *mc,
int candidate,
int ispriv,
ldap_back_send_t sendok );
+extern void
+meta_back_quarantine(
+ Operation *op,
+ SlapReply *rs,
+ int candidate,
+ int dolock );
+
extern int
meta_back_single_bind(
Operation *op,
}
if ( mi->mi_cache.ttl != META_DNCACHE_DISABLED
- && op->o_req_ndn.bv_len != 0 )
+ && !BER_BVISEMPTY( &op->o_req_ndn ) )
{
( void )meta_dncache_update_entry( &mi->mi_cache,
&op->o_req_ndn, candidate );
free( mdn.bv_val );
}
+ if ( META_BACK_QUARANTINE( mi ) ) {
+ meta_back_quarantine( op, rs, candidate, 1 );
+ }
+
return rs->sr_err;
}
/* mc here must be the regular mc,
* reset and ready for init */
rc = meta_back_init_one_conn( op, rs,
- mt, mc, candidate,
+ mc, candidate,
LDAP_BACK_CONN_ISPRIV( mc ),
LDAP_BACK_DONTSEND );
if ( rc == LDAP_SUCCESS ) {
}
}
+ if ( META_BACK_QUARANTINE( mi ) ) {
+ meta_back_quarantine( op, rs, candidate, dolock );
+ }
+
return rc;
}
( rmatch ? rmatch : "" ) );
}
+ if ( META_BACK_QUARANTINE( mi ) ) {
+ meta_back_quarantine( op, rs, candidate, 1 );
+ }
+
} else {
for ( i = 0; i < mi->mi_ntargets; i++ ) {
metasingleconn_t *msc = &mc->mc_conns[ i ];
ldap_memfree( match );
}
}
+
+ if ( META_BACK_QUARANTINE( mi ) ) {
+ meta_back_quarantine( op, rs, i, 1 );
+ }
}
}
mt->mt_urllist_p = mt;
mt->mt_nretries = mi->mi_nretries;
+ mt->mt_quarantine = mi->mi_quarantine;
mt->mt_flags = mi->mi_flags;
mt->mt_version = mi->mi_version;
mt->mt_network_timeout = mi->mi_network_timeout;
return 1;
}
ber_str2bv( argv[ 1 ], 0L, 1, &mi->mi_targets[ i ]->mt_pseudorootpw );
+
+ /* quarantine */
+ } else if ( strcasecmp( argv[ 0 ], "quarantine" ) == 0 ) {
+ char buf[ SLAP_TEXT_BUFLEN ] = { '\0' };
+ slap_retry_info_t *ri = mi->mi_ntargets ?
+ &mi->mi_targets[ mi->mi_ntargets - 1 ]->mt_quarantine
+ : &mi->mi_quarantine;
+
+ if ( META_BACK_QUARANTINE( mi ) ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s: line %d: quarantine already defined.\n",
+ fname, lineno, 0 );
+ return 1;
+ }
+
+ switch ( argc ) {
+ case 2:
+ break;
+
+ case 1:
+ Debug( LDAP_DEBUG_ANY,
+ "%s: line %d: missing arg in \"quarantine <pattern list>\".\n",
+ fname, lineno, 0 );
+ return 1;
+
+ default:
+ Debug( LDAP_DEBUG_ANY,
+ "%s: line %d: extra cruft after \"quarantine <pattern list>\".\n",
+ fname, lineno, 0 );
+ return 1;
+ }
+
+ if ( ri != &mi->mi_quarantine ) {
+ ri->ri_interval = NULL;
+ ri->ri_num = NULL;
+ }
+
+ if ( slap_retry_info_parse( argv[ 1 ], ri, buf, sizeof( buf ) ) ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s line %d: %s.\n",
+ fname, lineno, buf );
+ return 1;
+ }
/* dn massaging */
} else if ( strcasecmp( argv[ 0 ], "suffixmassage" ) == 0 ) {
meta_back_init_one_conn(
Operation *op,
SlapReply *rs,
- metatarget_t *mt,
metaconn_t *mc,
int candidate,
int ispriv,
ldap_back_send_t sendok )
{
metainfo_t *mi = ( metainfo_t * )op->o_bd->be_private;
+ metatarget_t *mt = mi->mi_targets[ candidate ];
metasingleconn_t *msc = &mc->mc_conns[ candidate ];
int version;
dncookie dc;
int is_ldaps = 0;
#endif /* HAVE_TLS */
+ /* if the server is quarantined, and
+ * - the current interval did not expire yet, or
+ * - no more retries should occur,
+ * don't return the connection */
+ if ( mt->mt_isquarantined ) {
+ slap_retry_info_t *ri = &mt->mt_quarantine;
+ int dont_retry = 1;
+
+ if ( mt->mt_isquarantined == LDAP_BACK_FQ_YES ) {
+ dont_retry = ( ri->ri_num[ ri->ri_idx ] == SLAP_RETRYNUM_TAIL
+ || slap_get_time() < ri->ri_last + ri->ri_interval[ ri->ri_idx ] );
+ if ( !dont_retry ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s: meta_back_init_one_conn quarantine "
+ "retry block #%d try #%d.\n",
+ op->o_log_prefix, ri->ri_idx, ri->ri_count );
+ mt->mt_isquarantined = LDAP_BACK_FQ_RETRYING;
+ }
+ }
+
+ if ( dont_retry ) {
+ rs->sr_err = LDAP_UNAVAILABLE;
+ if ( op->o_conn && ( sendok & LDAP_BACK_SENDERR ) ) {
+ send_ldap_result( op, rs );
+ }
+ return rs->sr_err;
+ }
+ }
+
/*
* Already init'ed
*/
( void )rewrite_session_delete( mt->mt_rwmap.rwm_rw, op->o_conn );
/* mc here must be the regular mc, reset and ready for init */
- rc = meta_back_init_one_conn( op, rs, mt, mc, candidate,
+ rc = meta_back_init_one_conn( op, rs, mc, candidate,
LDAP_BACK_CONN_ISPRIV( mc ), sendok );
if ( binding ) {
LDAP_BACK_CONN_BINDING_SET( msc );
}
}
+ if ( META_BACK_QUARANTINE( mi ) ) {
+ meta_back_quarantine( op, rs, candidate, 0 );
+ }
+
ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
return rc == LDAP_SUCCESS ? 1 : 0;
}
for ( i = 0; i < mi->mi_ntargets; i++ ) {
- metatarget_t *mt = mi->mi_targets[ i ];
-
/*
* The target is activated; if needed, it is
* also init'd
*/
candidates[ i ].sr_err = meta_back_init_one_conn( op,
- rs, mt, mc, i,
- LDAP_BACK_CONN_ISPRIV( &mc_curr ), sendok );
+ rs, mc, i, LDAP_BACK_CONN_ISPRIV( &mc_curr ),
+ sendok );
if ( candidates[ i ].sr_err == LDAP_SUCCESS ) {
candidates[ i ].sr_tag = META_CANDIDATE;
ncandidates++;
* also init'd. In case of error, meta_back_init_one_conn
* sends the appropriate result.
*/
- err = meta_back_init_one_conn( op, rs, mt, mc, i,
+ err = meta_back_init_one_conn( op, rs, mc, i,
LDAP_BACK_CONN_ISPRIV( &mc_curr ), sendok );
if ( err != LDAP_SUCCESS ) {
/*
* The target is activated; if needed, it is
* also init'd
*/
- int lerr = meta_back_init_one_conn( op, rs,
- mt, mc, i,
- LDAP_BACK_CONN_ISPRIV( &mc_curr ),
- sendok );
+ int lerr = meta_back_init_one_conn( op, rs, mc, i,
+ LDAP_BACK_CONN_ISPRIV( &mc_curr ), LDAP_BACK_DONTSEND );
if ( lerr == LDAP_SUCCESS ) {
candidates[ i ].sr_tag = META_CANDIDATE;
candidates[ i ].sr_err = LDAP_SUCCESS;
Debug( LDAP_DEBUG_ANY, "%s: meta_back_getconn[%d] failed: %d\n",
op->o_log_prefix, i, lerr );
+ if ( META_BACK_ONERR_STOP( mi ) ) {
+ if ( sendok & LDAP_BACK_SENDERR ) {
+ send_ldap_result( op, rs );
+ rs->sr_text = NULL;
+ }
+ if ( new_conn ) {
+ meta_back_freeconn( op, mc );
+
+ } else {
+ meta_back_release_conn( op, mc );
+ }
+
+ return NULL;
+ }
+
+ rs->sr_text = NULL;
continue;
}
ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
}
}
+
+void
+meta_back_quarantine(
+ Operation *op,
+ SlapReply *rs,
+ int candidate,
+ int dolock )
+{
+ metainfo_t *mi = (metainfo_t *)op->o_bd->be_private;
+ metatarget_t *mt = mi->mi_targets[ candidate ];
+
+ slap_retry_info_t *ri = &mt->mt_quarantine;
+
+ if ( dolock ) {
+ ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
+ }
+
+ if ( rs->sr_err == LDAP_UNAVAILABLE ) {
+ switch ( mt->mt_isquarantined ) {
+ case LDAP_BACK_FQ_NO:
+ Debug( LDAP_DEBUG_ANY,
+ "%s: meta_back_quarantine enter.\n",
+ op->o_log_prefix, 0, 0 );
+
+ ri->ri_idx = 0;
+ ri->ri_count = 0;
+ break;
+
+ case LDAP_BACK_FQ_RETRYING:
+ Debug( LDAP_DEBUG_ANY,
+ "%s: meta_back_quarantine block #%d try #%d failed.\n",
+ op->o_log_prefix, ri->ri_idx, ri->ri_count );
+
+ ++ri->ri_count;
+ if ( ri->ri_num[ ri->ri_idx ] != SLAP_RETRYNUM_FOREVER
+ && ri->ri_count == ri->ri_num[ ri->ri_idx ] )
+ {
+ ri->ri_count = 0;
+ ++ri->ri_idx;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ mt->mt_isquarantined = LDAP_BACK_FQ_YES;
+ ri->ri_last = slap_get_time();
+
+ } else if ( mt->mt_isquarantined != LDAP_BACK_FQ_NO ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s: meta_back_quarantine exit.\n",
+ op->o_log_prefix, ri->ri_idx, ri->ri_count );
+
+ ri->ri_count = 0;
+ ri->ri_idx = 0;
+ mt->mt_isquarantined = LDAP_BACK_FQ_NO;
+ }
+
+ if ( dolock ) {
+ ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
+ }
+}
+
} else {
send_ldap_result( op, rs );
+
+ if ( META_BACK_QUARANTINE( mi ) ) {
+ meta_back_quarantine( op, rs, candidate, 1 );
+ }
}
cleanup:;
*/
if ( mi->mi_targets != NULL ) {
for ( i = 0; i < mi->mi_ntargets; i++ ) {
+ if ( META_BACK_QUARANTINE( mi )
+ && mi->mi_targets[ i ]->mt_quarantine.ri_num != mi->mi_quarantine.ri_num )
+ {
+ slap_retry_info_destroy( &mi->mi_targets[ i ]->mt_quarantine );
+ }
+
target_free( mi->mi_targets[ i ] );
}
if ( mi->mi_candidates != NULL ) {
ber_memfree_x( mi->mi_candidates, NULL );
}
+
+ if ( META_BACK_QUARANTINE( mi ) ) {
+ slap_retry_info_destroy( &mi->mi_quarantine );
+ }
}
free( be->be_private );
} else {
send_ldap_result( op, rs );
+
+ if ( META_BACK_QUARANTINE( mi ) ) {
+ meta_back_quarantine( op, rs, candidate, 1 );
+ }
}
done:;
} else {
send_ldap_result( op, rs );
+
+ if ( META_BACK_QUARANTINE( mi ) ) {
+ meta_back_quarantine( op, rs, candidate, 1 );
+ }
}
done:;
*/
candidates[ i ].sr_msgid = META_MSGID_IGNORE;
--ncandidates;
- rs->sr_err = candidates[ i ].sr_err = LDAP_OTHER;
- rs->sr_text = "remote server unavailable";
+ rs->sr_err = candidates[ i ].sr_err;
} else if ( rc == LDAP_RES_SEARCH_ENTRY ) {
if ( candidates[ i ].sr_type == REP_INTERMEDIATE ) {
ldap_controls_free( candidates[ i ].sr_ctrls );
candidates[ i ].sr_ctrls = NULL;
}
+
+ if ( META_BACK_QUARANTINE( mi ) ) {
+ meta_back_quarantine( op, &candidates[ i ], i, 1 );
+ }
}
if ( mc ) {