.BR discover ,
support is detected by reading the remote server's root DSE.
+.TP
+.B timeout [{add|delete|modify|modrdn}=]<val> [...]
+This directive allows to set per-operation timeouts.
+If no operation is specified, it affects all.
+Currently, only write operations are addressed, because searches
+can already be limited by means of the
+.B limits
+directive (see
+.BR slapd.conf (5)
+for details), and other operations are not supposed to incur into the
+need for timeouts.
+Note: if the timelimit is exceeded, the operation is abandoned;
+the protocol does not provide any means to rollback the operation,
+so the client will not know if the operation eventually succeeded or not.
+
.SH BACKWARD COMPATIBILITY
The LDAP backend has been heavily reworked between releases 2.2 and 2.3;
as a side-effect, some of the traditional directives have been
Operation *op,
SlapReply *rs )
{
+ struct ldapinfo *li = (struct ldapinfo *)op->o_bd->be_private;
+
struct ldapconn *lc;
int i = 0,
j = 0;
retry:
rs->sr_err = ldap_add_ext( lc->lc_ld, op->o_req_dn.bv_val, attrs,
ctrls, NULL, &msgid );
- rs->sr_err = ldap_back_op_result( lc, op, rs, msgid, LDAP_BACK_SENDRESULT );
+ rs->sr_err = ldap_back_op_result( lc, op, rs, msgid,
+ li->timeout[ LDAP_BACK_OP_ADD ], LDAP_BACK_SENDRESULT );
if ( rs->sr_err == LDAP_UNAVAILABLE && do_retry ) {
do_retry = 0;
if ( ldap_back_retry( lc, op, rs, LDAP_BACK_SENDERR ) ) {
LDAP_BACK_IDASSERT_OTHERID
};
+/*
+ * operation enumeration for timeouts
+ */
+enum {
+ LDAP_BACK_OP_ADD = 0,
+ LDAP_BACK_OP_DELETE,
+ LDAP_BACK_OP_MODIFY,
+ LDAP_BACK_OP_MODRDN,
+ LDAP_BACK_OP_LAST
+};
+
struct ldapinfo {
char *url;
LDAPURLDesc *lud;
/* FIXME: automatic rwm instantiation removed */
int rwm_started;
#endif
+
+ time_t timeout[ LDAP_BACK_OP_LAST ];
};
typedef enum ldap_back_send_t {
rs->sr_err = ldap_sasl_bind( lc->lc_ld, op->o_req_dn.bv_val,
LDAP_SASL_SIMPLE,
&op->orb_cred, op->o_ctrls, NULL, &msgid );
- rc = ldap_back_op_result( lc, op, rs, msgid, LDAP_BACK_SENDERR );
+ rc = ldap_back_op_result( lc, op, rs, msgid, 0, LDAP_BACK_SENDERR );
if ( rc == LDAP_SUCCESS ) {
/* If defined, proxyAuthz will be used also when
return 0;
}
- rc = ldap_back_op_result( lc, op, rs, msgid, sendok );
+ rc = ldap_back_op_result( lc, op, rs, msgid, 0, sendok );
if ( rc == LDAP_SUCCESS ) {
LDAP_BACK_CONN_ISBOUND_SET( lc );
Operation *op,
SlapReply *rs,
ber_int_t msgid,
+ time_t timeout,
ldap_back_send_t sendok )
{
char *match = NULL;
int rc;
struct timeval tv;
- LDAP_BACK_TV_SET( &tv );
+ if ( timeout ) {
+ tv.tv_sec = timeout;
+ tv.tv_usec = 0;
+
+ } else {
+ LDAP_BACK_TV_SET( &tv );
+ }
retry:;
/* if result parsing fails, note the failure reason */
- switch ( ldap_result( lc->lc_ld, msgid, 1, &tv, &res ) ) {
+ rc = ldap_result( lc->lc_ld, msgid, 1, &tv, &res );
+ switch ( rc ) {
case 0:
+ if ( timeout ) {
+ (void)ldap_abandon_ext( lc->lc_ld, msgid, NULL, NULL );
+ rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
+ rs->sr_text = "Operation timed out";
+ break;
+ }
+
LDAP_BACK_TV_SET( &tv );
ldap_pvt_thread_yield();
goto retry;
ldap_pvt_thread_mutex_lock( &li->conn_mutex );
if ( lc->lc_refcnt == 1 ) {
+ Debug( LDAP_DEBUG_ANY,
+ "%s ldap_back_retry: retrying URI=\"%s\" DN=\"%s\"\n",
+ op->o_log_prefix, li->url,
+ BER_BVISNULL( &lc->lc_bound_ndn ) ?
+ "" : lc->lc_bound_ndn.bv_val );
+
ldap_unbind_ext( lc->lc_ld, NULL, NULL );
lc->lc_ld = NULL;
LDAP_BACK_CONN_ISBOUND_CLEAR( lc );
goto done;
}
- rc = ldap_back_op_result( lc, op, rs, msgid, LDAP_BACK_SENDERR );
+ rc = ldap_back_op_result( lc, op, rs, msgid, 0, LDAP_BACK_SENDERR );
if ( rc == LDAP_SUCCESS ) {
LDAP_BACK_CONN_ISBOUND_SET( lc );
}
op->orc_ava->aa_desc->ad_cname.bv_val,
&op->orc_ava->aa_value,
ctrls, NULL, &msgid );
- rc = ldap_back_op_result( lc, op, rs, msgid, LDAP_BACK_SENDRESULT );
+ rc = ldap_back_op_result( lc, op, rs, msgid, 0, LDAP_BACK_SENDRESULT );
if ( rc == LDAP_UNAVAILABLE && do_retry ) {
do_retry = 0;
if ( ldap_back_retry( lc, op, rs, LDAP_BACK_SENDERR ) ) {
LDAP_BACK_CFG_CHASE,
LDAP_BACK_CFG_T_F,
LDAP_BACK_CFG_WHOAMI,
+ LDAP_BACK_CFG_TIMEOUT,
LDAP_BACK_CFG_REWRITE
};
"SYNTAX OMsDirectoryString "
"SINGLE-VALUE )",
NULL, NULL },
+ { "timeout", "timeout", 0, 2, 0,
+ ARG_MAGIC|LDAP_BACK_CFG_TIMEOUT,
+ ldap_back_cf_gen, "( OLcfgDbAt:3.14 "
+ "NAME 'olcDbTimeout' "
+ "DESC 'Per-operation timeouts' "
+ "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 },
"$ olcDbChaseReferrals "
"$ olcDbTFSupport "
"$ olcDbProxyWhoAmI "
+ "$ olcDbTimeout "
") )",
Cft_Database, ldapcfg},
{ NULL, 0, NULL }
{ BER_BVNULL, 0 }
};
+static slap_cf_aux_table timeout_table[] = {
+ { BER_BVC("add="), 0 * sizeof( time_t ), 'u', 0, NULL },
+ { BER_BVC("delete="), 1 * sizeof( time_t ), 'u', 0, NULL },
+ { BER_BVC("modify="), 2 * sizeof( time_t ), 'u', 0, NULL },
+ { BER_BVC("modrdn="), 3 * sizeof( time_t ), 'u', 0, NULL },
+ { BER_BVNULL, 0, 0, 0, NULL }
+};
+
static int
ldap_back_cf_gen( ConfigArgs *c )
{
}
break;
+ case LDAP_BACK_CFG_TIMEOUT:
+ BER_BVZERO( &bv );
+
+ slap_cf_aux_table_unparse( li->timeout, &bv, timeout_table );
+
+ if ( !BER_BVISNULL( &bv ) ) {
+ for ( i = 0; isspace( bv.bv_val[ i ] ); i++ )
+ /* count spaces */ ;
+
+ if ( i ) {
+ bv.bv_len -= i;
+ AC_MEMCPY( bv.bv_val, &bv.bv_val[ i ],
+ bv.bv_len + 1 );
+ }
+
+ ber_bvarray_add( &c->rvalue_vals, &bv );
+ }
+ break;
+
default:
/* FIXME: we need to handle all... */
assert( 0 );
rc = 1;
break;
+ case LDAP_BACK_CFG_TIMEOUT:
+ for ( i = 0; i < LDAP_BACK_OP_LAST; i++ ) {
+ li->timeout[ i ] = 0;
+ }
+ break;
+
default:
/* FIXME: we need to handle all... */
assert( 0 );
} else {
li->flags &= ~LDAP_BACK_F_SAVECRED;
}
- break;
- }
+ } break;
case LDAP_BACK_CFG_CHASE: {
int dochase = 0;
} else {
li->flags &= ~LDAP_BACK_F_CHASE_REFERRALS;
}
- break;
- }
+ } break;
case LDAP_BACK_CFG_T_F:
i = verb_to_mask( c->argv[1], t_f_mode );
} else {
li->flags &= ~LDAP_BACK_F_PROXY_WHOAMI;
}
+ } break;
+
+ case LDAP_BACK_CFG_TIMEOUT:
+ if ( c->argc < 2 ) {
+ return 1;
+ }
+
+ for ( i = 1; i < c->argc; i++ ) {
+ if ( isdigit( c->argv[ i ][ 0 ] ) ) {
+ char *next;
+ int j;
+ unsigned u;
+
+ u = strtoul( c->argv[ i ], &next, 0 );
+ if ( next == c->argv[ i ] || next[ 0 ] != '\0' ) {
+ return 1;
+ }
+
+ for ( j = 0; j < LDAP_BACK_OP_LAST; j++ ) {
+ li->timeout[ j ] = u;
+ }
+
+ continue;
+ }
+
+ if ( slap_cf_aux_table_parse( c->argv[ i ], li->timeout, timeout_table, "slapd-ldap timeout" ) ) {
+ return 1;
+ }
+ }
break;
- }
case LDAP_BACK_CFG_REWRITE:
fprintf( stderr, "%s: line %d: "
Operation *op,
SlapReply *rs )
{
+ struct ldapinfo *li = (struct ldapinfo *)op->o_bd->be_private;
+
struct ldapconn *lc;
ber_int_t msgid;
LDAPControl **ctrls = NULL;
retry:
rs->sr_err = ldap_delete_ext( lc->lc_ld, op->o_req_ndn.bv_val,
ctrls, NULL, &msgid );
- rc = ldap_back_op_result( lc, op, rs, msgid, LDAP_BACK_SENDRESULT );
+ rc = ldap_back_op_result( lc, op, rs, msgid,
+ li->timeout[ LDAP_BACK_OP_DELETE], LDAP_BACK_SENDRESULT );
if ( rs->sr_err == LDAP_SERVER_DOWN && do_retry ) {
do_retry = 0;
if ( ldap_back_retry( lc, op, rs, LDAP_BACK_SENDERR ) ) {
Operation *op,
SlapReply *rs )
{
+ struct ldapinfo *li = (struct ldapinfo *)op->o_bd->be_private;
+
struct ldapconn *lc;
LDAPMod **modv = NULL,
*mods = NULL;
retry:
rs->sr_err = ldap_modify_ext( lc->lc_ld, op->o_req_ndn.bv_val, modv,
ctrls, NULL, &msgid );
- rc = ldap_back_op_result( lc, op, rs, msgid, LDAP_BACK_SENDRESULT );
+ rc = ldap_back_op_result( lc, op, rs, msgid,
+ li->timeout[ LDAP_BACK_OP_MODIFY], LDAP_BACK_SENDRESULT );
if ( rs->sr_err == LDAP_UNAVAILABLE && do_retry ) {
do_retry = 0;
if ( ldap_back_retry( lc, op, rs, LDAP_BACK_SENDERR ) ) {
Operation *op,
SlapReply *rs )
{
+ struct ldapinfo *li = (struct ldapinfo *)op->o_bd->be_private;
+
struct ldapconn *lc;
ber_int_t msgid;
LDAPControl **ctrls = NULL;
rs->sr_err = ldap_rename( lc->lc_ld, op->o_req_ndn.bv_val,
op->orr_newrdn.bv_val, newSup,
op->orr_deleteoldrdn, ctrls, NULL, &msgid );
- rc = ldap_back_op_result( lc, op, rs, msgid, LDAP_BACK_SENDRESULT );
+ rc = ldap_back_op_result( lc, op, rs, msgid,
+ li->timeout[ LDAP_BACK_OP_MODRDN ], LDAP_BACK_SENDRESULT );
if ( rs->sr_err == LDAP_SERVER_DOWN && do_retry ) {
do_retry = 0;
if ( ldap_back_retry( lc, op, rs, LDAP_BACK_SENDERR ) ) {
int ldap_back_retry(struct ldapconn *lc, Operation *op, SlapReply *rs, ldap_back_send_t sendok);
int ldap_back_map_result(SlapReply *rs);
int ldap_back_op_result(struct ldapconn *lc, Operation *op, SlapReply *rs,
- ber_int_t msgid, ldap_back_send_t sendok);
+ ber_int_t msgid, time_t timeout, ldap_back_send_t sendok);
int back_ldap_LTX_init_module(int argc, char *argv[]);
int ldap_back_init_cf( BackendInfo *bi );
{
struct ldapconn *lc;
struct timeval tv;
- time_t stoptime;
+ time_t stoptime = (time_t)-1;
LDAPMessage *res,
*e;
int rc = 0,
goto retry;
}
}
- rc = ldap_back_op_result( lc, op, rs, msgid, LDAP_BACK_DONTSEND );
+ rc = ldap_back_op_result( lc, op, rs, msgid, 0, LDAP_BACK_DONTSEND );
ldap_back_freeconn( op, lc );
lc = NULL;
goto finish;
/* cleanup */
if ( references ) {
- ldap_value_free( references );
+ ber_memvfree( (void **)references );
ch_free( rs->sr_ref );
rs->sr_ref = NULL;
}
/* cleanup */
if ( references ) {
- ldap_value_free( references );
+ ber_memvfree( (void **)references );
}
rc = 0;
LDAPMessage *res = NULL;
int rc;
- if ( mi->mi_targets[ candidate ].mt_timeout[ META_OP_ADD ] != 0 ) {
- tv.tv_sec = mi->mi_targets[ candidate ].mt_timeout[ META_OP_ADD ];
+ if ( mi->mi_targets[ candidate ].mt_timeout[ LDAP_BACK_OP_ADD ] != 0 ) {
+ tv.tv_sec = mi->mi_targets[ candidate ].mt_timeout[ LDAP_BACK_OP_ADD ];
tv.tv_usec = 0;
tvp = &tv;
}
* in one block with the metaconn_t structure */
} metaconn_t;
-enum {
- META_OP_ADD = 0,
- META_OP_DELETE,
- META_OP_MODIFY,
- META_OP_MODRDN,
- META_OP_LAST
-};
-
typedef struct metatarget_t {
char *mt_uri;
int mt_scope;
unsigned mt_flags;
int mt_version;
- time_t mt_timeout[ META_OP_LAST ];
+ time_t mt_timeout[ LDAP_BACK_OP_LAST ];
} metatarget_t;
typedef struct metadncache_t {
#define META_BACK_DEFER_ROOTDN_BIND(mi) ( (mi)->flags & META_BACK_F_DEFER_ROOTDN_BIND )
int mi_version;
- time_t mi_timeout[ META_OP_LAST ];
+ time_t mi_timeout[ LDAP_BACK_OP_LAST ];
} metainfo_t;
typedef enum meta_op_type {
mi->mi_targets[ i ].mt_flags = mi->flags;
mi->mi_targets[ i ].mt_version = mi->mi_version;
- for ( c = 0; c < META_OP_LAST; c++ ) {
+ for ( c = 0; c < LDAP_BACK_OP_LAST; c++ ) {
mi->mi_targets[ i ].mt_timeout[ c ] = mi->mi_timeout[ c ];
}
size_t len = sep - argv[ c ];
if ( strncasecmp( argv[ c ], "add", len ) == 0 ) {
- t = &tv[ META_OP_ADD ];
+ t = &tv[ LDAP_BACK_OP_ADD ];
} else if ( strncasecmp( argv[ c ], "delete", len ) == 0 ) {
- t = &tv[ META_OP_DELETE ];
+ t = &tv[ LDAP_BACK_OP_DELETE ];
} else if ( strncasecmp( argv[ c ], "modify", len ) == 0 ) {
- t = &tv[ META_OP_MODIFY ];
+ t = &tv[ LDAP_BACK_OP_MODIFY ];
} else if ( strncasecmp( argv[ c ], "modrdn", len ) == 0 ) {
- t = &tv[ META_OP_MODRDN ];
+ t = &tv[ LDAP_BACK_OP_MODRDN ];
} else {
fprintf( stderr,
"%s: line %d: unknown operation \"%s\" for timeout #%d.\n",
} else {
int i;
- for ( i = 0; i < META_OP_LAST; i++ ) {
+ for ( i = 0; i < LDAP_BACK_OP_LAST; i++ ) {
tv[ i ] = val;
}
}
goto retry_lock;
}
- Debug( LDAP_DEBUG_ANY, "%s meta_back_retry: retrying uri=\"%s\" DN=\"%s\"\n",
+ Debug( LDAP_DEBUG_ANY,
+ "%s meta_back_retry: retrying URI=\"%s\" DN=\"%s\"\n",
op->o_log_prefix, mt->mt_uri,
- BER_BVISNULL( &msc->msc_bound_ndn ) ? "" : msc->msc_bound_ndn.bv_val );
+ BER_BVISNULL( &msc->msc_bound_ndn ) ?
+ "" : msc->msc_bound_ndn.bv_val );
meta_clear_one_candidate( msc );
LDAP_BACK_CONN_ISBOUND_CLEAR( msc );
LDAPMessage *res = NULL;
int rc;
- if ( mi->mi_targets[ candidate ].mt_timeout[ META_OP_DELETE ] != 0 ) {
- tv.tv_sec = mi->mi_targets[ candidate ].mt_timeout[ META_OP_DELETE ];
+ if ( mi->mi_targets[ candidate ].mt_timeout[ LDAP_BACK_OP_DELETE ] != 0 ) {
+ tv.tv_sec = mi->mi_targets[ candidate ].mt_timeout[ LDAP_BACK_OP_DELETE ];
tv.tv_usec = 0;
tvp = &tv;
}
struct timeval tv, *tvp = NULL;
LDAPMessage *res = NULL;
- if ( mi->mi_targets[ candidate ].mt_timeout[ META_OP_MODIFY ] != 0 ) {
- tv.tv_sec = mi->mi_targets[ candidate ].mt_timeout[ META_OP_MODIFY ];
+ if ( mi->mi_targets[ candidate ].mt_timeout[ LDAP_BACK_OP_MODIFY ] != 0 ) {
+ tv.tv_sec = mi->mi_targets[ candidate ].mt_timeout[ LDAP_BACK_OP_MODIFY ];
tv.tv_usec = 0;
tvp = &tv;
}
LDAPMessage *res = NULL;
int rc;
- if ( mi->mi_targets[ candidate ].mt_timeout[ META_OP_MODRDN ] != 0 ) {
- tv.tv_sec = mi->mi_targets[ candidate ].mt_timeout[ META_OP_MODRDN ];
+ if ( mi->mi_targets[ candidate ].mt_timeout[ LDAP_BACK_OP_MODRDN ] != 0 ) {
+ tv.tv_sec = mi->mi_targets[ candidate ].mt_timeout[ LDAP_BACK_OP_MODRDN ];
tv.tv_usec = 0;
tvp = &tv;
}
metainfo_t *mi = ( metainfo_t * )op->o_bd->be_private;
metaconn_t *mc;
struct timeval tv = { 0, 0 };
- time_t stoptime;
+ time_t stoptime = (time_t)-1;
LDAPMessage *res = NULL, *e;
int rc = 0, sres = LDAP_SUCCESS;
char *matched = NULL;