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; }
/* 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; }
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; }
/* * FIXME: error return must be handled in a cleaner way ... */ int asyncmeta_back_op_result( a_metaconn_t *mc, Operation *op, SlapReply *rs, int candidate, ber_int_t msgid, time_t timeout, ldap_back_send_t sendok ) { a_metainfo_t *mi = mc->mc_info; const char *save_text = rs->sr_text, *save_matched = rs->sr_matched; BerVarray save_ref = rs->sr_ref; LDAPControl **save_ctrls = rs->sr_ctrls; void *matched_ctx = NULL; char *matched = NULL; char *text = NULL; char **refs = NULL; LDAPControl **ctrls = NULL; assert( mc != NULL ); rs->sr_text = NULL; rs->sr_matched = NULL; rs->sr_ref = NULL; rs->sr_ctrls = NULL; if ( candidate != META_TARGET_NONE ) { a_metatarget_t *mt = mi->mi_targets[ candidate ]; a_metasingleconn_t *msc = &mc->mc_conns[ candidate ]; if ( LDAP_ERR_OK( rs->sr_err ) ) { int rc; struct timeval tv; LDAPMessage *res = NULL; time_t stoptime = (time_t)(-1); int timeout_err = op->o_protocol >= LDAP_VERSION3 ? LDAP_ADMINLIMIT_EXCEEDED : LDAP_OTHER; const char *timeout_text = "Operation timed out"; /* if timeout is not specified, compute and use * the one specific to the ongoing operation */ if ( timeout == (time_t)(-1) ) { slap_op_t opidx = slap_req2op( op->o_tag ); if ( opidx == SLAP_OP_SEARCH ) { if ( op->ors_tlimit <= 0 ) { timeout = 0; } else { timeout = op->ors_tlimit; timeout_err = LDAP_TIMELIMIT_EXCEEDED; timeout_text = NULL; } } else { timeout = mt->mt_timeout[ opidx ]; } } /* better than nothing :) */ if ( timeout == 0 ) { if ( mi->mi_idle_timeout ) { timeout = mi->mi_idle_timeout; } } if ( timeout ) { stoptime = op->o_time + timeout; } LDAP_BACK_TV_SET( &tv ); retry:; rc = ldap_result( msc->msc_ld, msgid, LDAP_MSG_ALL, &tv, &res ); switch ( rc ) { case 0: if ( timeout && slap_get_time() > stoptime ) { (void)asyncmeta_back_cancel( mc, op, msgid, candidate ); rs->sr_err = timeout_err; rs->sr_text = timeout_text; break; } LDAP_BACK_TV_SET( &tv ); ldap_pvt_thread_yield(); goto retry; case -1: ldap_get_option( msc->msc_ld, LDAP_OPT_RESULT_CODE, &rs->sr_err ); break; /* otherwise get the result; if it is not * LDAP_SUCCESS, record it in the reply * structure (this includes * LDAP_COMPARE_{TRUE|FALSE}) */ default: /* only touch when activity actually took place... */ if ( mi->mi_idle_timeout != 0 && msc->msc_time < op->o_time ) { msc->msc_time = op->o_time; } rc = ldap_parse_result( msc->msc_ld, res, &rs->sr_err, &matched, &text, &refs, &ctrls, 1 ); res = NULL; 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_back_op_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_back_op_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; } } assert( res == NULL ); } /* 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 ); } } else { int i, err = rs->sr_err; for ( i = 0; i < mi->mi_ntargets; i++ ) { a_metasingleconn_t *msc = &mc->mc_conns[ i ]; char *xtext = NULL; char *xmatched = NULL; if ( msc->msc_ld == NULL ) { continue; } rs->sr_err = LDAP_SUCCESS; ldap_get_option( msc->msc_ld, LDAP_OPT_RESULT_CODE, &rs->sr_err ); if ( rs->sr_err != LDAP_SUCCESS ) { /* * better check the type of error. In some cases * (search ?) it might be better to return a * success if at least one of the targets gave * positive result ... */ ldap_get_option( msc->msc_ld, LDAP_OPT_DIAGNOSTIC_MESSAGE, &xtext ); if ( xtext != NULL && xtext [ 0 ] == '\0' ) { ldap_memfree( xtext ); xtext = NULL; } ldap_get_option( msc->msc_ld, LDAP_OPT_MATCHED_DN, &xmatched ); if ( xmatched != NULL && xmatched[ 0 ] == '\0' ) { ldap_memfree( xmatched ); xmatched = NULL; } rs->sr_err = slap_map_api2result( rs ); if ( LogTest( LDAP_DEBUG_ANY ) ) { char buf[ SLAP_TEXT_BUFLEN ]; snprintf( buf, sizeof( buf ), "asyncmeta_back_op_result[%d] " "err=%d text=\"%s\" matched=\"%s\"", i, rs->sr_err, ( xtext ? xtext : "" ), ( xmatched ? xmatched : "" ) ); Debug( LDAP_DEBUG_ANY, "%s %s.\n", op->o_log_prefix, buf, 0 ); } /* * FIXME: need to rewrite "match" (need rwinfo) */ switch ( rs->sr_err ) { default: err = rs->sr_err; if ( xtext != NULL ) { if ( text ) { ldap_memfree( text ); } text = xtext; xtext = NULL; } if ( xmatched != NULL ) { if ( matched ) { ldap_memfree( matched ); } matched = xmatched; xmatched = NULL; } break; } if ( xtext ) { ldap_memfree( xtext ); } if ( xmatched ) { ldap_memfree( xmatched ); } } if ( META_BACK_TGT_QUARANTINE( mi->mi_targets[ i ] ) ) { asyncmeta_quarantine( op, mi, rs, i ); } } if ( err != LDAP_SUCCESS ) { rs->sr_err = err; } } 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 ) { if ( !( sendok & LDAP_BACK_RETRYING ) ) { if ( op->o_conn && ( sendok & LDAP_BACK_SENDERR ) ) { if ( rs->sr_text == NULL ) rs->sr_text = "Proxy operation retry failed"; send_ldap_result( op, rs ); } } } else if ( op->o_conn && ( ( ( sendok & LDAP_BACK_SENDOK ) && LDAP_ERR_OK( rs->sr_err ) ) || ( ( sendok & LDAP_BACK_SENDERR ) && !LDAP_ERR_OK( rs->sr_err ) ) ) ) { send_ldap_result( 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; return( LDAP_ERR_OK( rs->sr_err ) ? LDAP_SUCCESS : rs->sr_err ); }