Esempio n. 1
0
static void
asyncmeta_handle_onerr_stop(Operation *op,
			    SlapReply *rs,
			    a_metaconn_t *mc,
			    bm_context_t *bc,
			    int candidate,
			    slap_callback *cb)
{
	a_metainfo_t *mi = mc->mc_info;
	int j;
	ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
	if (bc->bc_active > 0) {
		ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
		return;
	}
	bc->bc_active = 1;
	asyncmeta_drop_bc(mc, bc);
	ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);

	for (j=0; j<mi->mi_ntargets; j++) {
		if (j != candidate && bc->candidates[j].sr_msgid >= 0
		    && mc->mc_conns[j].msc_ld != NULL) {
			asyncmeta_back_abandon_candidate( mc, op,
						bc->candidates[ j ].sr_msgid, j );
		}
	}
	if (cb != NULL) {
		op->o_callback = cb;
	}
	send_ldap_result(op, rs);
	asyncmeta_clear_bm_context(bc);
}
Esempio n. 2
0
meta_search_candidate_t
asyncmeta_send_all_pending_ops(a_metaconn_t *mc, int candidate, void *ctx, int dolock)
{
	a_metainfo_t	*mi = mc->mc_info;
	bm_context_t *bc, *onext;
	a_metasingleconn_t *msc = &mc->mc_conns[candidate];

	if ( dolock )
		ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex );

	msc->msc_active++;
	for (bc = LDAP_STAILQ_FIRST(&mc->mc_om_list); bc; bc = onext) {
		meta_search_candidate_t ret;
		onext = LDAP_STAILQ_NEXT(bc, bc_next);
		if (bc->candidates[candidate].sr_msgid != META_MSGID_NEED_BIND || bc->bc_active > 0 || bc->op->o_abandon > 0) {
			continue;
		}
		bc->op->o_threadctx = ctx;
		bc->op->o_tid = ldap_pvt_thread_pool_tid( ctx );
		slap_sl_mem_setctx(ctx, bc->op->o_tmpmemctx);
		bc->bc_active++;
		ret = asyncmeta_send_pending_op(bc, candidate);
		if (ret != META_SEARCH_CANDIDATE) {
			bc->candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
			bc->candidates[ candidate ].sr_type = REP_RESULT;
			bc->candidates[ candidate ].sr_err = bc->rs.sr_err;
			if (bc->op->o_tag != LDAP_REQ_SEARCH || (META_BACK_ONERR_STOP( mi )) ||
			    (asyncmeta_is_last_result(mc, bc, candidate) == 0)) {
				LDAP_STAILQ_REMOVE(&mc->mc_om_list, bc, bm_context_t, bc_next);
				mc->pending_ops--;
				asyncmeta_send_ldap_result(bc, bc->op, &bc->rs);
				asyncmeta_clear_bm_context(bc);
			}
		} else {
			bc->bc_active--;
		}
	}
	msc->msc_active--;

	if ( dolock )
		ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );

	return META_SEARCH_CANDIDATE;
}
Esempio n. 3
0
int
asyncmeta_back_compare( Operation *op, SlapReply *rs )
{
	a_metainfo_t	*mi = ( a_metainfo_t * )op->o_bd->be_private;
	a_metatarget_t	*mt;
	a_metaconn_t	*mc;
	int		rc, candidate = -1;
	OperationBuffer opbuf;
	bm_context_t *bc;
	SlapReply *candidates;
	slap_callback *cb = op->o_callback;

	Debug(LDAP_DEBUG_ARGS, "==> asyncmeta_back_compare: %s\n",
	      op->o_req_dn.bv_val, 0, 0 );

	asyncmeta_new_bm_context(op, rs, &bc, mi->mi_ntargets );
	if (bc == NULL) {
		rs->sr_err = LDAP_OTHER;
		asyncmeta_sender_error(op, rs, cb);
		return rs->sr_err;
	}

	candidates = bc->candidates;
	mc = asyncmeta_getconn( op, rs, candidates, &candidate, LDAP_BACK_DONTSEND, 0);
	if ( !mc || rs->sr_err != LDAP_SUCCESS) {
		asyncmeta_sender_error(op, rs, cb);
		asyncmeta_clear_bm_context(bc);
		return rs->sr_err;
	}

	mt = mi->mi_targets[ candidate ];
	bc->timeout = mt->mt_timeout[ SLAP_OP_COMPARE ];
	bc->retrying = LDAP_BACK_RETRYING;
	bc->sendok = ( LDAP_BACK_SENDRESULT | bc->retrying );
	bc->stoptime = op->o_time + bc->timeout;

	ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
	rc = asyncmeta_add_message_queue(mc, bc);
	ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);

	if (rc != LDAP_SUCCESS) {
		rs->sr_err = LDAP_BUSY;
		rs->sr_text = "Maximum pending ops limit exceeded";
		asyncmeta_clear_bm_context(bc);
		asyncmeta_sender_error(op, rs, cb);
		goto finish;
	}

	rc = asyncmeta_dobind_init_with_retry(op, rs, bc, mc, candidate);
	switch (rc)
	{
	case META_SEARCH_CANDIDATE:
		/* target is already bound, just send the request */
		Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_compare:  "
		       "cnd=\"%ld\"\n", op->o_log_prefix, candidate , 0);

		rc = asyncmeta_back_compare_start( op, rs, mc, bc, candidate);
		if (rc == META_SEARCH_ERR) {
			ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
			asyncmeta_drop_bc(mc, bc);
			ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
			asyncmeta_sender_error(op, rs, cb);
			asyncmeta_clear_bm_context(bc);
			goto finish;

		}
			break;
	case META_SEARCH_NOT_CANDIDATE:
		Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_compare: NOT_CANDIDATE "
		       "cnd=\"%ld\"\n", op->o_log_prefix, candidate , 0);
		candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
		ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
		asyncmeta_drop_bc(mc, bc);
		ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
		asyncmeta_sender_error(op, rs, cb);
		asyncmeta_clear_bm_context(bc);
		goto finish;

	case META_SEARCH_NEED_BIND:
	case META_SEARCH_CONNECTING:
		Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_compare: NEED_BIND "
		       "cnd=\"%ld\" %p\n", op->o_log_prefix, candidate , &mc->mc_conns[candidate]);
		rc = asyncmeta_dobind_init(op, rs, bc, mc, candidate);
		if (rc == META_SEARCH_ERR) {
			ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
			asyncmeta_drop_bc(mc, bc);
			ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
			asyncmeta_sender_error(op, rs, cb);
			asyncmeta_clear_bm_context(bc);
			goto finish;
		}
		break;
	case META_SEARCH_BINDING:
			Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_compare: BINDING "
			       "cnd=\"%ld\" %p\n", op->o_log_prefix, candidate , &mc->mc_conns[candidate]);
			/* Todo add the context to the message queue but do not send the request
			   the receiver must send this when we are done binding */
			/* question - how would do receiver know to which targets??? */
			break;

	case META_SEARCH_ERR:
			Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_compare: ERR "
			       "cnd=\"%ldd\"\n", op->o_log_prefix, candidate , 0);
			candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
			candidates[ candidate ].sr_type = REP_RESULT;
			ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
			asyncmeta_drop_bc(mc, bc);
			ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
			asyncmeta_sender_error(op, rs, cb);
			asyncmeta_clear_bm_context(bc);
			goto finish;
		default:
			assert( 0 );
			break;
		}
	ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
	asyncmeta_start_one_listener(mc, candidates, bc, candidate);
	ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
finish:
	return rs->sr_err;

}
Esempio n. 4
0
int
asyncmeta_handle_search_msg(LDAPMessage *res, a_metaconn_t *mc, bm_context_t *bc, int candidate)
{
	a_metainfo_t	*mi;
	a_metatarget_t	*mt;
	a_metasingleconn_t *msc;
	Operation *op = bc->op;
	SlapReply *rs;
	int	   i, rc = LDAP_SUCCESS, sres;
	SlapReply *candidates;
	char		**references = NULL;
	LDAPControl	**ctrls = NULL;
	a_dncookie dc;
	LDAPMessage *msg;
	ber_int_t id;

	rs = &bc->rs;
	mi = mc->mc_info;
	mt = mi->mi_targets[ candidate ];
	msc = &mc->mc_conns[ candidate ];
	dc.op = op;
	dc.target = mt;
	dc.to_from = MASSAGE_REP;
	id = ldap_msgid(res);


	candidates = bc->candidates;
	i = candidate;

	while (res && !META_BACK_CONN_INVALID(msc)) {
	for (msg = ldap_first_message(msc->msc_ldr, res); msg; msg = ldap_next_message(msc->msc_ldr, msg)) {
		switch(ldap_msgtype(msg)) {
		case LDAP_RES_SEARCH_ENTRY:
			Debug( LDAP_DEBUG_TRACE,
				"%s asyncmeta_handle_search_msg: msc %p entry\n",
				op->o_log_prefix, msc );
			if ( candidates[ i ].sr_type == REP_INTERMEDIATE ) {
				/* don't retry any more... */
				candidates[ i ].sr_type = REP_RESULT;
			}
			/* count entries returned by target */
			candidates[ i ].sr_nentries++;
			if (bc->c_peer_name.bv_val == op->o_conn->c_peer_name.bv_val && !op->o_abandon) {
				rs->sr_err = asyncmeta_send_entry( &bc->copy_op, rs, mc, i, msg );
			} else {
				goto err_cleanup;
			}
			switch ( rs->sr_err ) {
			case LDAP_SIZELIMIT_EXCEEDED:
				asyncmeta_send_ldap_result(bc, op, rs);
				rs->sr_err = LDAP_SUCCESS;
				goto err_cleanup;
			case LDAP_UNAVAILABLE:
				rs->sr_err = LDAP_OTHER;
				break;
			default:
				break;
			}
			bc->is_ok++;
			break;

		case LDAP_RES_SEARCH_REFERENCE:
			if ( META_BACK_TGT_NOREFS( mt ) ) {
				rs->sr_err = LDAP_OTHER;
				asyncmeta_send_ldap_result(bc, op, rs);
				goto err_cleanup;
			}
			if ( candidates[ i ].sr_type == REP_INTERMEDIATE ) {
				/* don't retry any more... */
				candidates[ i ].sr_type = REP_RESULT;
			}
			bc->is_ok++;
			rc = ldap_parse_reference( msc->msc_ldr, msg,
				   &references, &rs->sr_ctrls, 0 );

			if ( rc != LDAP_SUCCESS || references == NULL ) {
				rs->sr_err = LDAP_OTHER;
				asyncmeta_send_ldap_result(bc, op, rs);
				goto err_cleanup;
			}

			/* FIXME: merge all and return at the end */

			{
				int cnt;
				for ( cnt = 0; references[ cnt ]; cnt++ )
					;

				rs->sr_ref = ber_memalloc_x( sizeof( struct berval ) * ( cnt + 1 ),
								 op->o_tmpmemctx );

				for ( cnt = 0; references[ cnt ]; cnt++ ) {
					ber_str2bv_x( references[ cnt ], 0, 1, &rs->sr_ref[ cnt ],
							  op->o_tmpmemctx );
				}
				BER_BVZERO( &rs->sr_ref[ cnt ] );
			}

			{
				dc.memctx = op->o_tmpmemctx;
				( void )asyncmeta_referral_result_rewrite( &dc, rs->sr_ref );
			}

			if ( rs->sr_ref != NULL ) {
				if (!BER_BVISNULL( &rs->sr_ref[ 0 ] ) ) {
					/* ignore return value by now */
					( void )send_search_reference( op, rs );
				}

				ber_bvarray_free_x( rs->sr_ref, op->o_tmpmemctx );
				rs->sr_ref = NULL;
			}

			/* cleanup */
			if ( references ) {
				ber_memvfree( (void **)references );
			}

			if ( rs->sr_ctrls ) {
				ldap_controls_free( rs->sr_ctrls );
				rs->sr_ctrls = NULL;
			}
			break;

		case LDAP_RES_INTERMEDIATE:
			if ( candidates[ i ].sr_type == REP_INTERMEDIATE ) {
				/* don't retry any more... */
				candidates[ i ].sr_type = REP_RESULT;
			}
			bc->is_ok++;

			/* FIXME: response controls
			 * are passed without checks */
			rs->sr_err = ldap_parse_intermediate( msc->msc_ldr,
								  msg,
								  (char **)&rs->sr_rspoid,
								  &rs->sr_rspdata,
								  &rs->sr_ctrls,
								  0 );
			if ( rs->sr_err != LDAP_SUCCESS ) {
				candidates[ i ].sr_type = REP_RESULT;
				rs->sr_err = LDAP_OTHER;
				asyncmeta_send_ldap_result(bc, op, rs);
				goto err_cleanup;
			}

			slap_send_ldap_intermediate( op, rs );

			if ( rs->sr_rspoid != NULL ) {
				ber_memfree( (char *)rs->sr_rspoid );
				rs->sr_rspoid = NULL;
			}

			if ( rs->sr_rspdata != NULL ) {
				ber_bvfree( rs->sr_rspdata );
				rs->sr_rspdata = NULL;
			}

			if ( rs->sr_ctrls != NULL ) {
				ldap_controls_free( rs->sr_ctrls );
				rs->sr_ctrls = NULL;
			}
			break;

		case LDAP_RES_SEARCH_RESULT:
			if ( mi->mi_idle_timeout != 0 ) {
				asyncmeta_set_msc_time(msc);
			}
			Debug( LDAP_DEBUG_TRACE,
			       "%s asyncmeta_handle_search_msg: msc %p result\n",
			       op->o_log_prefix, msc );
			candidates[ i ].sr_type = REP_RESULT;
			candidates[ i ].sr_msgid = META_MSGID_IGNORE;
			/* NOTE: ignores response controls
			 * (and intermediate response controls
			 * as well, except for those with search
			 * references); this may not be correct,
			 * but if they're not ignored then
			 * back-meta would need to merge them
			 * consistently (think of pagedResults...)
			 */
			/* FIXME: response controls? */
			rs->sr_err = ldap_parse_result( msc->msc_ldr,
							msg,
							&candidates[ i ].sr_err,
								(char **)&candidates[ i ].sr_matched,
							(char **)&candidates[ i ].sr_text,
							&references,
							&ctrls /* &candidates[ i ].sr_ctrls (unused) */ ,
							0 );
			if ( rs->sr_err != LDAP_SUCCESS ) {
				candidates[ i ].sr_err = rs->sr_err;
				sres = slap_map_api2result( &candidates[ i ] );
				candidates[ i ].sr_type = REP_RESULT;
				goto finish;
			}

			rs->sr_err = candidates[ i ].sr_err;

			/* massage matchedDN if need be */
			if ( candidates[ i ].sr_matched != NULL ) {
				struct berval	match, mmatch;

				ber_str2bv( candidates[ i ].sr_matched,
						0, 0, &match );
				candidates[ i ].sr_matched = NULL;

				dc.memctx = NULL;
				asyncmeta_dn_massage( &dc, &match, &mmatch );
				if ( mmatch.bv_val == match.bv_val ) {
					candidates[ i ].sr_matched
						= ch_strdup( mmatch.bv_val );

				} else {
					candidates[ i ].sr_matched = mmatch.bv_val;
				}

				bc->candidate_match++;
				ldap_memfree( match.bv_val );
			}

			/* add references to array */
			/* RFC 4511: referrals can only appear
			 * if result code is LDAP_REFERRAL */
			if ( references != NULL
				 && references[ 0 ] != NULL
				 && references[ 0 ][ 0 ] != '\0' )
			{
				if ( rs->sr_err != LDAP_REFERRAL ) {
					Debug( LDAP_DEBUG_ANY,
						   "%s asncmeta_search_result[%d]: "
						   "got referrals with err=%d\n",
						   op->o_log_prefix,
						   i, rs->sr_err );

				} else {
					BerVarray	sr_ref;
					int		cnt;

					for ( cnt = 0; references[ cnt ]; cnt++ )
						;

					sr_ref = ber_memalloc_x( sizeof( struct berval ) * ( cnt + 1 ),
								 op->o_tmpmemctx );

					for ( cnt = 0; references[ cnt ]; cnt++ ) {
						ber_str2bv_x( references[ cnt ], 0, 1, &sr_ref[ cnt ],
								  op->o_tmpmemctx );
					}
					BER_BVZERO( &sr_ref[ cnt ] );

					dc.memctx = op->o_tmpmemctx;
					( void )asyncmeta_referral_result_rewrite( &dc, sr_ref );

					if ( rs->sr_v2ref == NULL ) {
						rs->sr_v2ref = sr_ref;

					} else {
						for ( cnt = 0; !BER_BVISNULL( &sr_ref[ cnt ] ); cnt++ ) {
							ber_bvarray_add_x( &rs->sr_v2ref, &sr_ref[ cnt ],
									   op->o_tmpmemctx );
						}
						ber_memfree_x( sr_ref, op->o_tmpmemctx );
					}
				}

			} else if ( rs->sr_err == LDAP_REFERRAL ) {
				Debug( LDAP_DEBUG_TRACE,
					   "%s asyncmeta_search_result[%d]: "
					   "got err=%d with null "
					   "or empty referrals\n",
					   op->o_log_prefix,
					   i, rs->sr_err );

				rs->sr_err = LDAP_NO_SUCH_OBJECT;
			}

			/* cleanup */
			ber_memvfree( (void **)references );

			sres = slap_map_api2result( rs );

			if ( candidates[ i ].sr_err == LDAP_SUCCESS ) {
				Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_search_result[%d] "
				       "match=\"%s\" err=%ld",
				       op->o_log_prefix, i,
				       candidates[ i ].sr_matched ? candidates[ i ].sr_matched : "",
				       (long) candidates[ i ].sr_err );
			} else {
					Debug( LDAP_DEBUG_ANY,  "%s asyncmeta_search_result[%d] "
				       "match=\"%s\" err=%ld (%s)",
				       op->o_log_prefix, i,
				       candidates[ i ].sr_matched ? candidates[ i ].sr_matched : "",
					       (long) candidates[ i ].sr_err, ldap_err2string( candidates[ i ].sr_err ) );
			}

			switch ( sres ) {
			case LDAP_NO_SUCH_OBJECT:
				/* is_ok is touched any time a valid
				 * (even intermediate) result is
				 * returned; as a consequence, if
				 * a candidate returns noSuchObject
				 * it is ignored and the candidate
				 * is simply demoted. */
				if ( bc->is_ok ) {
					sres = LDAP_SUCCESS;
				}
				break;

			case LDAP_SUCCESS:
				if ( ctrls != NULL && ctrls[0] != NULL ) {
#ifdef SLAPD_META_CLIENT_PR
					LDAPControl *pr_c;

					pr_c = ldap_control_find( LDAP_CONTROL_PAGEDRESULTS, ctrls, NULL );
					if ( pr_c != NULL ) {
						BerElementBuffer berbuf;
						BerElement *ber = (BerElement *)&berbuf;
						ber_tag_t tag;
						ber_int_t prsize;
						struct berval prcookie;

						/* unsolicited, do not accept */
						if ( mt->mt_ps == 0 ) {
							rs->sr_err = LDAP_OTHER;
							goto err_pr;
						}

						ber_init2( ber, &pr_c->ldctl_value, LBER_USE_DER );

						tag = ber_scanf( ber, "{im}", &prsize, &prcookie );
						if ( tag == LBER_ERROR ) {
							rs->sr_err = LDAP_OTHER;
							goto err_pr;
						}

						/* more pages? new search request */
						if ( !BER_BVISNULL( &prcookie ) && !BER_BVISEMPTY( &prcookie ) ) {
							if ( mt->mt_ps > 0 ) {
								/* ignore size if specified */
								prsize = 0;

							} else if ( prsize == 0 ) {
								/* guess the page size from the entries returned so far */
								prsize = candidates[ i ].sr_nentries;
							}

							candidates[ i ].sr_nentries = 0;
							candidates[ i ].sr_msgid = META_MSGID_IGNORE;
							candidates[ i ].sr_type = REP_INTERMEDIATE;

							assert( candidates[ i ].sr_matched == NULL );
							assert( candidates[ i ].sr_text == NULL );
							assert( candidates[ i ].sr_ref == NULL );

							switch ( asyncmeta_back_search_start( &bc->copy_op, rs, mc, bc, i, &prcookie, prsize, 1 ) )
							{
							case META_SEARCH_CANDIDATE:
								assert( candidates[ i ].sr_msgid >= 0 );
								ldap_controls_free( ctrls );
								//	goto free_message;

							case META_SEARCH_ERR:
							case META_SEARCH_NEED_BIND:
err_pr:;
								candidates[ i ].sr_err = rs->sr_err;
								candidates[ i ].sr_type = REP_RESULT;
								if ( META_BACK_ONERR_STOP( mi ) ) {
									asyncmeta_send_ldap_result(bc, op, rs);
									ldap_controls_free( ctrls );
									goto err_cleanup;
								}
								/* fallthru */

							case META_SEARCH_NOT_CANDIDATE:
								/* means that asyncmeta_back_search_start()
								 * failed but onerr == continue */
								candidates[ i ].sr_msgid = META_MSGID_IGNORE;
								candidates[ i ].sr_type = REP_RESULT;
								break;

							default:
								/* impossible */
								assert( 0 );
								break;
							}
							break;
						}
					}
#endif /* SLAPD_META_CLIENT_PR */

					ldap_controls_free( ctrls );
				}
				/* fallthru */

			case LDAP_REFERRAL:
				bc->is_ok++;
				break;

			case LDAP_SIZELIMIT_EXCEEDED:
				/* if a target returned sizelimitExceeded
				 * and the entry count is equal to the
				 * proxy's limit, the target would have
				 * returned more, and the error must be
				 * propagated to the client; otherwise,
				 * the target enforced a limit lower
				 * than what requested by the proxy;
				 * ignore it */
				candidates[ i ].sr_err = rs->sr_err;
				if ( rs->sr_nentries == op->ors_slimit
					 || META_BACK_ONERR_STOP( mi ) )
				{
					const char *save_text;
got_err:
					save_text = rs->sr_text;
					rs->sr_text = candidates[ i ].sr_text;
					asyncmeta_send_ldap_result(bc, op, rs);
					if (candidates[ i ].sr_text != NULL) {
						ch_free( (char *)candidates[ i ].sr_text );
						candidates[ i ].sr_text = NULL;
					}
					rs->sr_text = save_text;
					ldap_controls_free( ctrls );
					goto err_cleanup;
				}
				break;

			default:
				candidates[ i ].sr_err = rs->sr_err;
				if ( META_BACK_ONERR_STOP( mi ) ) {
					goto got_err;
				}
				break;
			}
			/* if this is the last result we will ever receive, send it back  */
			rc = rs->sr_err;
			if (asyncmeta_is_last_result(mc, bc, i) == 0) {
				Debug( LDAP_DEBUG_TRACE,
					"%s asyncmeta_handle_search_msg: msc %p last result\n",
					op->o_log_prefix, msc );
				asyncmeta_search_last_result(mc, bc, i, sres);
err_cleanup:
				rc = rs->sr_err;
				ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex );
				asyncmeta_drop_bc( mc, bc);
				asyncmeta_clear_bm_context(bc);
				ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
				ldap_msgfree(res);
				return rc;
			}
finish:
			break;

		default:
			continue;
		}
	}
		ldap_msgfree(res);
		res = NULL;
		if (candidates[ i ].sr_type != REP_RESULT) {
			struct timeval	tv = {0};
			rc = ldap_result( msc->msc_ldr, id, LDAP_MSG_RECEIVED, &tv, &res );
			if (res != NULL) {
				msc->msc_result_time = slap_get_time();
			}
		}
	}
	ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex );
	bc->bc_active--;
	ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );

	return rc;
}
Esempio n. 5
0
void* asyncmeta_timeout_loop(void *ctx, void *arg)
{
	struct re_s* rtask = arg;
	a_metainfo_t *mi = rtask->arg;
	bm_context_t *bc, *onext;
	time_t current_time = slap_get_time();
	int i, j;
	LDAP_STAILQ_HEAD(BCList, bm_context_t) timeout_list;
	LDAP_STAILQ_INIT( &timeout_list );

	Debug( asyncmeta_debug, "asyncmeta_timeout_loop[%p] start at [%ld] \n", rtask, current_time );
	void *oldctx = slap_sl_mem_create(SLAP_SLAB_SIZE, SLAP_SLAB_STACK, ctx, 0);
	for (i=0; i<mi->mi_num_conns; i++) {
		a_metaconn_t * mc= &mi->mi_conns[i];
		ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex );
		for (bc = LDAP_STAILQ_FIRST(&mc->mc_om_list); bc; bc = onext) {
			onext = LDAP_STAILQ_NEXT(bc, bc_next);
			if (bc->bc_active > 0) {
				continue;
			}

			if (bc->op->o_abandon ) {
					/* set our memctx */
				bc->op->o_threadctx = ctx;
				bc->op->o_tid = ldap_pvt_thread_pool_tid( ctx );
				slap_sl_mem_setctx(ctx, bc->op->o_tmpmemctx);
				Operation *op = bc->op;

				LDAP_STAILQ_REMOVE(&mc->mc_om_list, bc, bm_context_t, bc_next);
				mc->pending_ops--;
				for (j=0; j<mi->mi_ntargets; j++) {
					if (bc->candidates[j].sr_msgid >= 0) {
						a_metasingleconn_t *msc = &mc->mc_conns[j];
						if ( op->o_tag == LDAP_REQ_SEARCH ) {
							msc->msc_active++;
							asyncmeta_back_cancel( mc, op,
									       bc->candidates[ j ].sr_msgid, j );
							msc->msc_active--;
						}
					}
				}
				asyncmeta_clear_bm_context(bc);
				continue;
			}
			if (bc->bc_invalid) {
				LDAP_STAILQ_REMOVE(&mc->mc_om_list, bc, bm_context_t, bc_next);
				mc->pending_ops--;
				LDAP_STAILQ_INSERT_TAIL( &timeout_list, bc, bc_next);
				continue;
			}

			if (bc->timeout && bc->stoptime < current_time) {
				Operation *op = bc->op;
				LDAP_STAILQ_REMOVE(&mc->mc_om_list, bc, bm_context_t, bc_next);
				mc->pending_ops--;
				LDAP_STAILQ_INSERT_TAIL( &timeout_list, bc, bc_next);
				for (j=0; j<mi->mi_ntargets; j++) {
					if (bc->candidates[j].sr_msgid >= 0) {
						a_metasingleconn_t *msc = &mc->mc_conns[j];
						asyncmeta_set_msc_time(msc);
						if ( op->o_tag == LDAP_REQ_SEARCH ) {
							msc->msc_active++;
							asyncmeta_back_cancel( mc, op,
									       bc->candidates[ j ].sr_msgid, j );
							msc->msc_active--;
						}
					}
				}
			}
		}
		ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );

		for (bc = LDAP_STAILQ_FIRST(&timeout_list); bc; bc = onext) {
			Operation *op = bc->op;
			SlapReply *rs = &bc->rs;
			int		timeout_err;
			const char *timeout_text;

			onext = LDAP_STAILQ_NEXT(bc, bc_next);
			LDAP_STAILQ_REMOVE(&timeout_list, bc, bm_context_t, bc_next);
			/* set our memctx */
			bc->op->o_threadctx = ctx;
			bc->op->o_tid = ldap_pvt_thread_pool_tid( ctx );
			slap_sl_mem_setctx(ctx, bc->op->o_tmpmemctx);

			if (bc->searchtime) {
				timeout_err = LDAP_TIMELIMIT_EXCEEDED;
			} else {
				timeout_err = op->o_protocol >= LDAP_VERSION3 ?
					LDAP_ADMINLIMIT_EXCEEDED : LDAP_OTHER;
			}

			if ( bc->bc_invalid ) {
				timeout_text = "Operation is invalid - target connection has been reset";
			} else {
				a_metasingleconn_t *log_msc =  &mc->mc_conns[0];
				Debug( asyncmeta_debug,
				       "asyncmeta_timeout_loop:Timeout op %s loop[%p], "
				       "current_time:%ld, op->o_time:%ld msc: %p, "
				       "msc->msc_binding_time: %x, msc->msc_flags:%x \n",
				       bc->op->o_log_prefix, rtask, current_time, bc->op->o_time,
				       log_msc, (unsigned int)log_msc->msc_binding_time, log_msc->msc_mscflags );

				if (bc->searchtime) {
					timeout_text = NULL;
				} else {
					timeout_text = "Operation timed out";
				}

				for (j=0; j<mi->mi_ntargets; j++) {
					if (bc->candidates[j].sr_msgid >= 0) {
						a_metatarget_t     *mt = mi->mi_targets[j];
						if (!META_BACK_TGT_QUARANTINE( mt ) ||
						    bc->candidates[j].sr_type == REP_RESULT) {
							continue;
						}

						if (mt->mt_isquarantined > LDAP_BACK_FQ_NO) {
							timeout_err = LDAP_UNAVAILABLE;
						} else {
							mt->mt_timeout_ops++;
							if ((mi->mi_max_timeout_ops > 0) &&
							    (mt->mt_timeout_ops > mi->mi_max_timeout_ops)) {
								timeout_err = LDAP_UNAVAILABLE;
								rs->sr_err = timeout_err;
								if (mt->mt_isquarantined == LDAP_BACK_FQ_NO)
									asyncmeta_quarantine(op, mi, rs, j);
							}
						}
					}
				}
			}
			rs->sr_err = timeout_err;
			rs->sr_text = timeout_text;
			if (!bc->op->o_abandon ) {
				asyncmeta_send_ldap_result( bc, bc->op, &bc->rs );
			}
			asyncmeta_clear_bm_context(bc);
		}

		ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex );
		if (mi->mi_idle_timeout) {
			for (j=0; j<mi->mi_ntargets; j++) {
				a_metasingleconn_t *msc = &mc->mc_conns[j];
				if ( msc->msc_active > 0 ) {
					continue;
				}
				if (mc->pending_ops > 0) {
					continue;
				}
				current_time = slap_get_time();
				if (msc->msc_ld && msc->msc_time > 0 && msc->msc_time + mi->mi_idle_timeout < current_time) {
					asyncmeta_clear_one_msc(NULL, mc, j, 1, __FUNCTION__);
				}
			}
		}
		ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
	}

	slap_sl_mem_setctx(ctx, oldctx);
	current_time = slap_get_time();
	Debug( asyncmeta_debug, "asyncmeta_timeout_loop[%p] stop at [%ld] \n", rtask, current_time );
	ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
	if ( ldap_pvt_runqueue_isrunning( &slapd_rq, rtask )) {
		ldap_pvt_runqueue_stoptask( &slapd_rq, rtask );
	}
	rtask->interval.tv_sec = 1;
	rtask->interval.tv_usec = 0;
	ldap_pvt_runqueue_resched(&slapd_rq, rtask, 0);
	ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
	return NULL;
}
Esempio n. 6
0
void *
asyncmeta_op_handle_result(void *ctx, void *arg)
{
	a_metaconn_t *mc = arg;
	int		i, j, rc, ntargets;
	struct timeval	tv = {0};
	LDAPMessage     *msg;
	a_metasingleconn_t *msc;
	bm_context_t *bc;
	void *oldctx;

	ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex );
	rc = ++mc->mc_active;
	ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
	if (rc > 1)
		return NULL;

	ntargets = mc->mc_info->mi_ntargets;
	i = ntargets;
	oldctx = slap_sl_mem_create(SLAP_SLAB_SIZE, SLAP_SLAB_STACK, ctx, 0);	/* get existing memctx */

again:
	for (j=0; j<ntargets; j++) {
		i++;
		if (i >= ntargets) i = 0;
		msc = &mc->mc_conns[i];
		ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex );
		if (!mc->mc_conns[i].msc_ldr ||
		    META_BACK_CONN_CREATING( &mc->mc_conns[i] ) ||
		    META_BACK_CONN_INVALID(&mc->mc_conns[i])) {
			ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
			continue;
		}

		msc->msc_active++;
		ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );

		rc = ldap_result( mc->mc_conns[i].msc_ldr, LDAP_RES_ANY, LDAP_MSG_RECEIVED, &tv, &msg );
		if (rc < 1) {
			if (rc < 0) {
				ldap_get_option( mc->mc_conns[i].msc_ldr, LDAP_OPT_ERROR_NUMBER, &rc);
				META_BACK_CONN_INVALID_SET(&mc->mc_conns[i]);
				asyncmeta_op_read_error(mc, i, rc, ctx);
			}
			ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex );
			msc->msc_active--;
			ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
			continue;
		}
		rc = ldap_msgtype( msg );
		if (rc == LDAP_RES_BIND) {
			if ( LogTest( asyncmeta_debug ) ) {
				char	time_buf[ SLAP_TEXT_BUFLEN ];
				asyncmeta_get_timestamp(time_buf);
				Debug( asyncmeta_debug, "[%s] asyncmeta_op_handle_result received bind msgid=%d msc: %p\n",
				      time_buf, ldap_msgid(msg), msc );
			}
			asyncmeta_handle_bind_result(msg, mc, i, ctx);
			mc->mc_info->mi_targets[i]->mt_timeout_ops = 0;
			ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex );
			msc->msc_result_time = slap_get_time();
			msc->msc_active--;
			ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
			if (msg)
				ldap_msgfree(msg);

			continue;
		}
retry_bc:
		ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex );
		bc = asyncmeta_find_message(ldap_msgid(msg), mc, i);
/* The sender might not be yet done with the context. On error it might also remove it
 * so it's best to try and find it again after a wait */
		if (bc && bc->bc_active > 0) {
			ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
			ldap_pvt_thread_yield();
			goto retry_bc;
		}
		if (bc) {
			bc->bc_active++;
		}

		msc->msc_result_time = slap_get_time();
		msc->msc_active--;
		ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
		if (!bc) {
			Debug( asyncmeta_debug,
				"asyncmeta_op_handle_result: Unable to find bc for msguid %d, msc: %p\n", ldap_msgid(msg), msc );
			ldap_msgfree(msg);
			continue;
		}

		/* set our memctx */
		bc->op->o_threadctx = ctx;
		bc->op->o_tid = ldap_pvt_thread_pool_tid( ctx );
		slap_sl_mem_setctx(ctx, bc->op->o_tmpmemctx);
		if (bc->op->o_abandon) {
			ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex );
			asyncmeta_drop_bc( mc, bc);
			if ( bc->op->o_tag == LDAP_REQ_SEARCH ) {
				int j;
				for (j=0; j<ntargets; j++) {
					if (bc->candidates[j].sr_msgid >= 0) {
						a_metasingleconn_t *tmp_msc = &mc->mc_conns[j];
						tmp_msc->msc_active++;
						asyncmeta_back_cancel( mc, bc->op,
								       bc->candidates[ j ].sr_msgid, j );
						tmp_msc->msc_active--;
					}
				}
			}
			asyncmeta_clear_bm_context(bc);
			ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
			if (msg)
				ldap_msgfree(msg);
			continue;
		}

		switch (rc) {
		case LDAP_RES_SEARCH_ENTRY:
		case LDAP_RES_SEARCH_REFERENCE:
		case LDAP_RES_SEARCH_RESULT:
		case LDAP_RES_INTERMEDIATE:
			asyncmeta_handle_search_msg(msg, mc, bc, i);
			mc->mc_info->mi_targets[i]->mt_timeout_ops = 0;
			msg = NULL;
			break;
		case LDAP_RES_ADD:
		case LDAP_RES_DELETE:
		case LDAP_RES_MODDN:
		case LDAP_RES_COMPARE:
		case LDAP_RES_MODIFY:
			rc = asyncmeta_handle_common_result(msg, mc, bc, i);
			mc->mc_info->mi_targets[i]->mt_timeout_ops = 0;
			break;
		default:
			{
			Debug( asyncmeta_debug,
				   "asyncmeta_op_handle_result: "
				   "unrecognized response message tag=%d\n",
				   rc );

			}
		}
		if (msg)
			ldap_msgfree(msg);
	}

	ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex );
	rc = --mc->mc_active;
	ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
	if (rc) {
		i++;
		goto again;
	}
	slap_sl_mem_setctx(ctx, oldctx);
	if (mc->mc_conns) {
		ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex );
		for (i=0; i<ntargets; i++) {
			if (!slapd_shutdown && !META_BACK_CONN_INVALID(msc)
			    && mc->mc_conns[i].msc_ldr && mc->mc_conns[i].conn) {
				connection_client_enable(mc->mc_conns[i].conn);
			}
		}
		ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
	}
	return NULL;
}
Esempio n. 7
0
/* This takes care to clean out the outbound queue in case we have a read error
 * sending back responses to the client */
int
asyncmeta_op_read_error(a_metaconn_t *mc, int candidate, int error, void* ctx)
{
	bm_context_t *bc, *onext;
	int cleanup;
	Operation *op;
	SlapReply *rs;
	SlapReply *candidates;
	/* no outstanding ops, nothing to do but log */
	Debug( LDAP_DEBUG_TRACE,
	       "asyncmeta_op_read_error: ldr=%p\n",
	       mc->mc_conns[candidate].msc_ldr );

	ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex );
	/*someone may be trying to write */
	if (mc->mc_conns[candidate].msc_active <= 1) {
		asyncmeta_clear_one_msc(NULL, mc, candidate, 0, __FUNCTION__);
	} else {
		META_BACK_CONN_INVALID_SET(&mc->mc_conns[candidate]);
	}

	if (mc->pending_ops <= 0) {
		ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
		return LDAP_SUCCESS;
	}

	for (bc = LDAP_STAILQ_FIRST(&mc->mc_om_list); bc; bc = onext) {
		onext = LDAP_STAILQ_NEXT(bc, bc_next);
		cleanup = 0;
		candidates = bc->candidates;
		/* was this op affected? */
		if ( !META_IS_CANDIDATE( &candidates[ candidate ] ) )
			continue;

		if (bc->op->o_abandon) {
			bc->bc_invalid = 1;
			continue;
		}

		if (bc->bc_active > 0) {
			continue;
		}

		bc->op->o_threadctx = ctx;
		bc->op->o_tid = ldap_pvt_thread_pool_tid( ctx );
		slap_sl_mem_setctx(ctx, bc->op->o_tmpmemctx);

		op = bc->op;
		rs = &bc->rs;
		switch (op->o_tag) {
		case LDAP_REQ_ADD:
		case LDAP_REQ_MODIFY:
		case LDAP_REQ_MODRDN:
		case LDAP_REQ_COMPARE:
		case LDAP_REQ_DELETE:
			rs->sr_err = LDAP_UNAVAILABLE;
			rs->sr_text = "Read error on connection to target";
			asyncmeta_send_ldap_result( bc, op, rs );
			cleanup = 1;
			break;
		case LDAP_REQ_SEARCH:
		{
			a_metainfo_t *mi = mc->mc_info;
			rs->sr_err = LDAP_UNAVAILABLE;
			rs->sr_text = "Read error on connection to target";
			candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
			candidates[ candidate ].sr_type = REP_RESULT;
			if ( (META_BACK_ONERR_STOP( mi ) ||
			      asyncmeta_is_last_result(mc, bc, candidate)) && op->o_conn) {
				asyncmeta_send_ldap_result( bc, op, rs );
				cleanup = 1;
			}
		}
			break;
		default:
			break;
		}

		if (cleanup) {
			int j;
			a_metainfo_t *mi = mc->mc_info;
			for (j=0; j<mi->mi_ntargets; j++) {
				if (j != candidate && bc->candidates[j].sr_msgid >= 0
				    && mc->mc_conns[j].msc_ld != NULL) {
					asyncmeta_back_cancel( mc, op,
							       bc->candidates[ j ].sr_msgid, j );
				}
			}
			LDAP_STAILQ_REMOVE(&mc->mc_om_list, bc, bm_context_t, bc_next);
			mc->pending_ops--;
			asyncmeta_clear_bm_context(bc);
		}
	}
	ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
	return LDAP_SUCCESS;
}
Esempio n. 8
0
int asyncmeta_handle_common_result(LDAPMessage *msg, a_metaconn_t *mc, bm_context_t *bc, int candidate)
{
	a_metainfo_t	*mi;
	a_metatarget_t	*mt;
	a_metasingleconn_t *msc;
	const char	*save_text = NULL,
		*save_matched = NULL;
	BerVarray	save_ref = NULL;
	LDAPControl	**save_ctrls = NULL;
	void		*matched_ctx = NULL;

	char		*matched = NULL;
	char		*text = NULL;
	char		**refs = NULL;
	LDAPControl	**ctrls = NULL;
	Operation *op;
	SlapReply *rs;
	int		rc;

	mi = mc->mc_info;
	mt = mi->mi_targets[ candidate ];
	msc = &mc->mc_conns[ candidate ];

	op = bc->op;
	rs = &bc->rs;
	save_text = rs->sr_text,
	save_matched = rs->sr_matched;
	save_ref = rs->sr_ref;
	save_ctrls = rs->sr_ctrls;
	rs->sr_text = NULL;
	rs->sr_matched = NULL;
	rs->sr_ref = NULL;
	rs->sr_ctrls = NULL;

	/* only touch when activity actually took place... */
	if ( mi->mi_idle_timeout != 0 ) {
		asyncmeta_set_msc_time(msc);
	}

	rc = ldap_parse_result( msc->msc_ldr, msg, &rs->sr_err,
				&matched, &text, &refs, &ctrls, 0 );

	if ( rc == LDAP_SUCCESS ) {
		rs->sr_text = text;
	} else {
		rs->sr_err = rc;
	}
	rs->sr_err = slap_map_api2result( rs );

	/* RFC 4511: referrals can only appear
	 * if result code is LDAP_REFERRAL */
	if ( refs != NULL
	     && refs[ 0 ] != NULL
	     && refs[ 0 ][ 0 ] != '\0' )
	{
		if ( rs->sr_err != LDAP_REFERRAL ) {
			Debug( LDAP_DEBUG_ANY,
			       "%s asyncmeta_handle_common_result[%d]: "
			       "got referrals with err=%d\n",
			       op->o_log_prefix,
			       candidate, rs->sr_err );

		} else {
			int	i;

			for ( i = 0; refs[ i ] != NULL; i++ )
				/* count */ ;
			rs->sr_ref = op->o_tmpalloc( sizeof( struct berval ) * ( i + 1 ),
						     op->o_tmpmemctx );
			for ( i = 0; refs[ i ] != NULL; i++ ) {
				ber_str2bv( refs[ i ], 0, 0, &rs->sr_ref[ i ] );
			}
			BER_BVZERO( &rs->sr_ref[ i ] );
		}

	} else if ( rs->sr_err == LDAP_REFERRAL ) {
		Debug( LDAP_DEBUG_ANY,
		       "%s asyncmeta_handle_common_result[%d]: "
		       "got err=%d with null "
		       "or empty referrals\n",
		       op->o_log_prefix,
		       candidate, rs->sr_err );

		rs->sr_err = LDAP_NO_SUCH_OBJECT;
	}

	if ( ctrls != NULL ) {
		rs->sr_ctrls = ctrls;
	}

	/* if the error in the reply structure is not
	 * LDAP_SUCCESS, try to map it from client
	 * to server error */
	if ( !LDAP_ERR_OK( rs->sr_err ) ) {
		rs->sr_err = slap_map_api2result( rs );

		/* internal ops ( op->o_conn == NULL )
		 * must not reply to client */
		if ( op->o_conn && !op->o_do_not_cache && matched ) {

			/* record the (massaged) matched
			 * DN into the reply structure */
			rs->sr_matched = matched;
		}
	}

	if ( META_BACK_TGT_QUARANTINE( mt ) ) {
		asyncmeta_quarantine( op, mi, rs, candidate );
	}

	if ( matched != NULL ) {
		struct berval	dn, pdn;

		ber_str2bv( matched, 0, 0, &dn );
		if ( dnPretty( NULL, &dn, &pdn, op->o_tmpmemctx ) == LDAP_SUCCESS ) {
			ldap_memfree( matched );
			matched_ctx = op->o_tmpmemctx;
			matched = pdn.bv_val;
		}
		rs->sr_matched = matched;
	}

	if ( rs->sr_err == LDAP_UNAVAILABLE || rs->sr_err == LDAP_SERVER_DOWN ) {
		if ( rs->sr_text == NULL ) {
				rs->sr_text = "Target is unavailable";
			}
	}

	ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex );
	asyncmeta_drop_bc( mc, bc);
	ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );

	if ( op->o_conn ) {
		asyncmeta_send_ldap_result(bc, op, rs);
	}

	if ( matched ) {
		op->o_tmpfree( (char *)rs->sr_matched, matched_ctx );
	}
	if ( text ) {
		ldap_memfree( text );
	}
	if ( rs->sr_ref ) {
		op->o_tmpfree( rs->sr_ref, op->o_tmpmemctx );
		rs->sr_ref = NULL;
	}
	if ( refs ) {
		ber_memvfree( (void **)refs );
	}
	if ( ctrls ) {
		assert( rs->sr_ctrls != NULL );
		ldap_controls_free( ctrls );
	}

	rs->sr_text = save_text;
	rs->sr_matched = save_matched;
	rs->sr_ref = save_ref;
	rs->sr_ctrls = save_ctrls;
	rc = (LDAP_ERR_OK( rs->sr_err ) ? LDAP_SUCCESS : rs->sr_err);
	ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex );
	asyncmeta_clear_bm_context(bc);
	ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
	return rc;
}
Esempio n. 9
0
int
asyncmeta_back_search( Operation *op, SlapReply *rs )
{
	a_metainfo_t	*mi = ( a_metainfo_t * )op->o_bd->be_private;
	struct timeval	save_tv = { 0, 0 },
			tv;
	time_t		stoptime = (time_t)(-1),
			lastres_time = slap_get_time(),
			timeout = 0;
	int		rc = 0, sres = LDAP_SUCCESS;
	char		*matched = NULL;
	int		last = 0, ncandidates = 0,
			initial_candidates = 0, candidate_match = 0,
			needbind = 0;
	ldap_back_send_t	sendok = LDAP_BACK_SENDERR;
	long		i,j;
	int		is_ok = 0;
	void		*savepriv;
	SlapReply	*candidates = NULL;
	int		do_taint = 0;
	bm_context_t *bc;
	a_metaconn_t *mc;
	slap_callback *cb = op->o_callback;

	rs_assert_ready( rs );
	rs->sr_flags &= ~REP_ENTRY_MASK; /* paranoia, we can set rs = non-entry */

	/*
	 * controls are set in ldap_back_dobind()
	 *
	 * FIXME: in case of values return filter, we might want
	 * to map attrs and maybe rewrite value
	 */

	asyncmeta_new_bm_context(op, rs, &bc, mi->mi_ntargets );
	if (bc == NULL) {
		rs->sr_err = LDAP_OTHER;
		send_ldap_result(op, rs);
		return rs->sr_err;
	}

	candidates = bc->candidates;
	mc = asyncmeta_getconn( op, rs, candidates, NULL, LDAP_BACK_DONTSEND, 0);
	if ( !mc || rs->sr_err != LDAP_SUCCESS) {
		op->o_callback = cb;
		send_ldap_result(op, rs);
		asyncmeta_clear_bm_context(bc);
		return rs->sr_err;
	}

	/*
	 * Inits searches
	 */

	for ( i = 0; i < mi->mi_ntargets; i++ ) {
		/* reset sr_msgid; it is used in most loops
		 * to check if that target is still to be considered */
		candidates[i].sr_msgid = META_MSGID_UNDEFINED;
		/* a target is marked as candidate by asyncmeta_getconn();
		 * if for any reason (an error, it's over or so) it is
		 * no longer active, sr_msgid is set to META_MSGID_IGNORE
		 * but it remains candidate, which means it has been active
		 * at some point during the operation.  This allows to
		 * use its response code and more to compute the final
		 * response */
		if ( !META_IS_CANDIDATE( &candidates[ i ] ) ) {
			continue;
		}

		candidates[ i ].sr_matched = NULL;
		candidates[ i ].sr_text = NULL;
		candidates[ i ].sr_ref = NULL;
		candidates[ i ].sr_ctrls = NULL;
		candidates[ i ].sr_nentries = 0;
		candidates[ i ].sr_type = -1;

		/* get largest timeout among candidates */
		if ( mi->mi_targets[ i ]->mt_timeout[ SLAP_OP_SEARCH ]
			&& mi->mi_targets[ i ]->mt_timeout[ SLAP_OP_SEARCH ] > timeout )
		{
			timeout = mi->mi_targets[ i ]->mt_timeout[ SLAP_OP_SEARCH ];
		}
	}

	bc->timeout = timeout;
	bc->stoptime = op->o_time + bc->timeout;

	if ( op->ors_tlimit != SLAP_NO_LIMIT ) {
		stoptime = op->o_time + op->ors_tlimit;
		if (stoptime < bc->stoptime) {
			bc->stoptime = stoptime;
			bc->searchtime = 1;
			bc->timeout = op->ors_tlimit;
		}
	}

	ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
	rc = asyncmeta_add_message_queue(mc, bc);
	ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);

	if (rc != LDAP_SUCCESS) {
		rs->sr_err = LDAP_BUSY;
		rs->sr_text = "Maximum pending ops limit exceeded";
		asyncmeta_clear_bm_context(bc);
		op->o_callback = cb;
		send_ldap_result(op, rs);
		goto finish;
	}

	for ( i = 0; i < mi->mi_ntargets; i++ ) {
		if ( !META_IS_CANDIDATE( &candidates[ i ] )
			|| candidates[ i ].sr_err != LDAP_SUCCESS )
		{
			continue;
		}

		rc = asyncmeta_dobind_init_with_retry(op, rs, bc, mc, i);
		switch (rc)
		{
		case META_SEARCH_CANDIDATE:
			/* target is already bound, just send the search request */
			ncandidates++;
			Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_search: IS_CANDIDATE "
			       "cnd=\"%ld\"\n", op->o_log_prefix, i , 0);

			rc = asyncmeta_back_search_start( op, rs, mc, bc, i,  NULL, 0 );
			if (rc == META_SEARCH_ERR) {
				META_CANDIDATE_CLEAR(&candidates[i]);
				candidates[ i ].sr_msgid = META_MSGID_IGNORE;
				if ( META_BACK_ONERR_STOP( mi ) ) {
					asyncmeta_handle_onerr_stop(op,rs,mc,bc,i,cb);
					goto finish;
				}
				else {
					continue;
				}
			}
			break;
		case META_SEARCH_NOT_CANDIDATE:
			Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_search: NOT_CANDIDATE "
			       "cnd=\"%ld\"\n", op->o_log_prefix, i , 0);
			candidates[ i ].sr_msgid = META_MSGID_IGNORE;
			break;

		case META_SEARCH_NEED_BIND:
		case META_SEARCH_CONNECTING:
			Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_search: NEED_BIND "
			       "cnd=\"%ld\" %p\n", op->o_log_prefix, i , &mc->mc_conns[i]);
			ncandidates++;
			rc = asyncmeta_dobind_init(op, rs, bc, mc, i);
			if (rc == META_SEARCH_ERR) {
				candidates[ i ].sr_msgid = META_MSGID_IGNORE;
				if ( META_BACK_ONERR_STOP( mi ) ) {
					asyncmeta_handle_onerr_stop(op,rs,mc,bc,i,cb);
					goto finish;
				}
				else {
					continue;
				}
			}
			break;
		case META_SEARCH_BINDING:
			Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_search: BINDING "
			       "cnd=\"%ld\" %p\n", op->o_log_prefix, i , &mc->mc_conns[i]);
			ncandidates++;
			/* Todo add the context to the message queue but do not send the request
			 the receiver must send this when we are done binding */
			/* question - how would do receiver know to which targets??? */
			break;

		case META_SEARCH_ERR:
			Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_search: SEARCH_ERR "
			       "cnd=\"%ldd\"\n", op->o_log_prefix, i , 0);
			candidates[ i ].sr_msgid = META_MSGID_IGNORE;
			candidates[ i ].sr_type = REP_RESULT;

			if ( META_BACK_ONERR_STOP( mi ) ) {
				asyncmeta_handle_onerr_stop(op,rs,mc,bc,i,cb);
				goto finish;
			}
			else {
				continue;
			}
			break;

		default:
			assert( 0 );
			break;
		}
	}

	initial_candidates = ncandidates;

	if ( LogTest( LDAP_DEBUG_TRACE ) ) {
		char	cnd[ SLAP_TEXT_BUFLEN ];
		int	c;

		for ( c = 0; c < mi->mi_ntargets; c++ ) {
			if ( META_IS_CANDIDATE( &candidates[ c ] ) ) {
				cnd[ c ] = '*';
			} else {
				cnd[ c ] = ' ';
			}
		}
		cnd[ c ] = '\0';

		Debug( LDAP_DEBUG_TRACE, "%s asyncmeta_back_search: ncandidates=%d "
			"cnd=\"%s\"\n", op->o_log_prefix, ncandidates, cnd );
	}

	if ( initial_candidates == 0 ) {
		/* NOTE: here we are not sending any matchedDN;
		 * this is intended, because if the back-meta
		 * is serving this search request, but no valid
		 * candidate could be looked up, it means that
		 * there is a hole in the mapping of the targets
		 * and thus no knowledge of any remote superior
		 * is available */
		Debug( LDAP_DEBUG_ANY, "%s asyncmeta_back_search: "
			"base=\"%s\" scope=%d: "
			"no candidate could be selected\n",
			op->o_log_prefix, op->o_req_dn.bv_val,
			op->ors_scope );

		/* FIXME: we're sending the first error we encounter;
		 * maybe we should pick the worst... */
		rc = LDAP_NO_SUCH_OBJECT;
		for ( i = 0; i < mi->mi_ntargets; i++ ) {
			if ( META_IS_CANDIDATE( &candidates[ i ] )
				&& candidates[ i ].sr_err != LDAP_SUCCESS )
			{
				rc = candidates[ i ].sr_err;
				break;
			}
		}
		rs->sr_err = rc;
		ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
		asyncmeta_drop_bc(mc, bc);
		ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
		op->o_callback = cb;
		send_ldap_result(op, rs);
		asyncmeta_clear_bm_context(bc);
		goto finish;
	}
	ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex);
	asyncmeta_start_listeners(mc, candidates, bc);
	ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex);
finish:
	return rs->sr_err;
}