コード例 #1
0
ファイル: operation.c プロジェクト: numberer6/likewise-open-1
void
slap_op_free( Operation *op, void *ctx )
{
    OperationBuffer *opbuf;

    assert( LDAP_STAILQ_NEXT(op, o_next) == NULL );

    if ( op->o_ber != NULL ) {
        ber_free( op->o_ber, 1 );
    }
    if ( !BER_BVISNULL( &op->o_dn ) ) {
        ch_free( op->o_dn.bv_val );
    }
    if ( !BER_BVISNULL( &op->o_ndn ) ) {
        ch_free( op->o_ndn.bv_val );
    }
    if ( !BER_BVISNULL( &op->o_authmech ) ) {
        ch_free( op->o_authmech.bv_val );
    }
    if ( op->o_ctrls != NULL ) {
        slap_free_ctrls( op, op->o_ctrls );
    }

#ifdef LDAP_CONNECTIONLESS
    if ( op->o_res_ber != NULL ) {
        ber_free( op->o_res_ber, 1 );
    }
#endif

    if ( op->o_groups ) {
        slap_op_groups_free( op );
    }

#if defined( LDAP_SLAPI )
    if ( slapi_plugins_used ) {
        slapi_int_free_object_extensions( SLAPI_X_EXT_OPERATION, op );
    }
#endif /* defined( LDAP_SLAPI ) */

    if ( !BER_BVISNULL( &op->o_csn ) ) {
        op->o_tmpfree( op->o_csn.bv_val, op->o_tmpmemctx );
        BER_BVZERO( &op->o_csn );
    }

    opbuf = (OperationBuffer *) op;
    memset( opbuf, 0, sizeof(*opbuf) );
    op->o_hdr = &opbuf->ob_hdr;
    op->o_controls = opbuf->ob_controls;

    if ( ctx ) {
        void *op2 = NULL;
        ldap_pvt_thread_pool_setkey( ctx, (void *)slap_op_free,
                                     op, slap_op_q_destroy, &op2, NULL );
        LDAP_STAILQ_NEXT( op, o_next ) = op2;
    } else {
        ber_memfree_x( op, NULL );
    }
}
コード例 #2
0
ファイル: operation.c プロジェクト: numberer6/likewise-open-1
static void
slap_op_q_destroy( void *key, void *data )
{
    Operation *op, *op2;
    for ( op = data; op; op = op2 ) {
        op2 = LDAP_STAILQ_NEXT( op, o_next );
        ber_memfree_x( op, NULL );
    }
}
コード例 #3
0
void slap_op_destroy(void)
{
	Operation *o;

	while ( (o = LDAP_STAILQ_FIRST( &slap_free_ops )) != NULL) {
		LDAP_STAILQ_REMOVE_HEAD( &slap_free_ops, o_next );
		LDAP_STAILQ_NEXT(o, o_next) = NULL;
		ch_free( o );
	}
	ldap_pvt_thread_mutex_destroy( &slap_op_mutex );
}
コード例 #4
0
void
slap_op_free( Operation *op )
{
	assert( LDAP_STAILQ_NEXT(op, o_next) == NULL );

	if ( op->o_ber != NULL ) {
		ber_free( op->o_ber, 1 );
	}
	if ( op->o_dn.bv_val != NULL ) {
		free( op->o_dn.bv_val );
	}
	if ( op->o_ndn.bv_val != NULL ) {
		free( op->o_ndn.bv_val );
	}
	if ( op->o_authmech.bv_val != NULL ) {
		free( op->o_authmech.bv_val );
	}
	if ( op->o_ctrls != NULL ) {
		ldap_controls_free( op->o_ctrls );
	}

#ifdef LDAP_CONNECTIONLESS
	if ( op->o_res_ber != NULL ) {
		ber_free( op->o_res_ber, 1 );
	}
#endif
#ifdef LDAP_CLIENT_UPDATE
	if ( op->o_clientupdate_state.bv_val != NULL ) {
		free( op->o_clientupdate_state.bv_val );
	}
#endif
#ifdef LDAP_SYNC
	if ( op->o_sync_state.bv_val != NULL ) {
		free( op->o_sync_state.bv_val );
	}
#endif

#if defined( LDAP_SLAPI )
	if ( op->o_pb != NULL ) {
		slapi_pblock_destroy( (Slapi_PBlock *)op->o_pb );
	}
#endif /* defined( LDAP_SLAPI ) */

	memset( op, 0, sizeof(Operation) );
	ldap_pvt_thread_mutex_lock( &slap_op_mutex );
	LDAP_STAILQ_INSERT_HEAD( &slap_free_ops, op, o_next );
	ldap_pvt_thread_mutex_unlock( &slap_op_mutex );
}
コード例 #5
0
ファイル: meta_result.c プロジェクト: openldap/openldap
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;
}
コード例 #6
0
ファイル: operation.c プロジェクト: thespooler/openldap
Operation *
slap_op_alloc(
    BerElement		*ber,
    ber_int_t	msgid,
    ber_tag_t	tag,
    ber_int_t	id,
    void *ctx )
{
    Operation	*op = NULL;

    if ( ctx ) {
        void *otmp = NULL;
        ldap_pvt_thread_pool_getkey( ctx, (void *)slap_op_free, &otmp, NULL );
        if ( otmp ) {
            op = otmp;
            otmp = LDAP_STAILQ_NEXT( op, o_next );
            ldap_pvt_thread_pool_setkey( ctx, (void *)slap_op_free,
                                         otmp, slap_op_q_destroy, NULL, NULL );
            op->o_abandon = 0;
            op->o_cancel = 0;
        }
    }
    if (!op) {
        op = (Operation *) ch_calloc( 1, sizeof(OperationBuffer) );
        op->o_hdr = &((OperationBuffer *) op)->ob_hdr;
        op->o_controls = ((OperationBuffer *) op)->ob_controls;
    }

    op->o_ber = ber;
    op->o_msgid = msgid;
    op->o_tag = tag;

    slap_op_time( &op->o_time, &op->o_tincr );
    op->o_opid = id;

#if defined( LDAP_SLAPI )
    if ( slapi_plugins_used ) {
        slapi_int_create_object_extensions( SLAPI_X_EXT_OPERATION, op );
    }
#endif /* defined( LDAP_SLAPI ) */

    return( op );
}
コード例 #7
0
ファイル: meta_result.c プロジェクト: openldap/openldap
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;
}
コード例 #8
0
ファイル: meta_result.c プロジェクト: openldap/openldap
/* 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;
}
コード例 #9
0
ファイル: starttls.c プロジェクト: RevanthPar/openldap
int
starttls_extop ( Operation *op, SlapReply *rs )
{
	int rc;

	Statslog( LDAP_DEBUG_STATS, "%s STARTTLS\n",
	    op->o_log_prefix, 0, 0, 0, 0 );

	if ( op->ore_reqdata != NULL ) {
		/* no request data should be provided */
		rs->sr_text = "no request data expected";
		return LDAP_PROTOCOL_ERROR;
	}

	/* acquire connection lock */
	ldap_pvt_thread_mutex_lock( &op->o_conn->c_mutex );

	/* can't start TLS if it is already started */
	if (op->o_conn->c_is_tls != 0) {
		rs->sr_text = "TLS already started";
		rc = LDAP_OPERATIONS_ERROR;
		goto done;
	}

	/* can't start TLS if there are other op's around */
	if (( !LDAP_STAILQ_EMPTY(&op->o_conn->c_ops) &&
			(LDAP_STAILQ_FIRST(&op->o_conn->c_ops) != op ||
			LDAP_STAILQ_NEXT(op, o_next) != NULL)) ||
		( !LDAP_STAILQ_EMPTY(&op->o_conn->c_pending_ops) ))
	{
		rs->sr_text = "cannot start TLS when operations are outstanding";
		rc = LDAP_OPERATIONS_ERROR;
		goto done;
	}

	if ( !( global_disallows & SLAP_DISALLOW_TLS_2_ANON ) &&
		( op->o_conn->c_dn.bv_len != 0 ) )
	{
		Statslog( LDAP_DEBUG_STATS,
			"%s AUTHZ anonymous mech=starttls ssf=0\n",
			op->o_log_prefix, 0, 0, 0, 0 );

		/* force to anonymous */
		connection2anonymous( op->o_conn );
	}

	if ( ( global_disallows & SLAP_DISALLOW_TLS_AUTHC ) &&
		( op->o_conn->c_dn.bv_len != 0 ) )
	{
		rs->sr_text = "cannot start TLS after authentication";
		rc = LDAP_OPERATIONS_ERROR;
		goto done;
	}

	/* fail if TLS could not be initialized */
	if ( slap_tls_ctx == NULL ) {
		if (default_referral != NULL) {
			/* caller will put the referral in the result */
			rc = LDAP_REFERRAL;
			goto done;
		}

		rs->sr_text = "Could not initialize TLS";
		rc = LDAP_UNAVAILABLE;
		goto done;
	}

    op->o_conn->c_is_tls = 1;
    op->o_conn->c_needs_tls_accept = 1;

    rc = LDAP_SUCCESS;

done:
	/* give up connection lock */
	ldap_pvt_thread_mutex_unlock( &op->o_conn->c_mutex );

	/* FIXME: RACE CONDITION! we give up lock before sending result
	 * Should be resolved by reworking connection state, not
	 * by moving send here (so as to ensure proper TLS sequencing)
	 */

	return rc;
}
コード例 #10
0
ファイル: operation.c プロジェクト: thespooler/openldap
void
slap_op_free( Operation *op, void *ctx )
{
    OperationBuffer *opbuf;

    assert( LDAP_STAILQ_NEXT(op, o_next) == NULL );

    /* paranoia */
    op->o_abandon = 1;

    if ( op->o_ber != NULL ) {
        ber_free( op->o_ber, 1 );
    }
    if ( !BER_BVISNULL( &op->o_dn ) ) {
        ch_free( op->o_dn.bv_val );
    }
    if ( !BER_BVISNULL( &op->o_ndn ) ) {
        ch_free( op->o_ndn.bv_val );
    }
    if ( !BER_BVISNULL( &op->o_authmech ) ) {
        ch_free( op->o_authmech.bv_val );
    }
    if ( op->o_ctrls != NULL ) {
        slap_free_ctrls( op, op->o_ctrls );
    }

#ifdef LDAP_CONNECTIONLESS
    if ( op->o_res_ber != NULL ) {
        ber_free( op->o_res_ber, 1 );
    }
#endif

    if ( op->o_groups ) {
        slap_op_groups_free( op );
    }

#if defined( LDAP_SLAPI )
    if ( slapi_plugins_used ) {
        slapi_int_free_object_extensions( SLAPI_X_EXT_OPERATION, op );
    }
#endif /* defined( LDAP_SLAPI ) */

    if ( !BER_BVISNULL( &op->o_csn ) ) {
        op->o_tmpfree( op->o_csn.bv_val, op->o_tmpmemctx );
    }

    if ( op->o_pagedresults_state != NULL ) {
        op->o_tmpfree( op->o_pagedresults_state, op->o_tmpmemctx );
    }

    /* Selectively zero out the struct. Ignore fields that will
     * get explicitly initialized later anyway. Keep o_abandon intact.
     */
    opbuf = (OperationBuffer *) op;
    op->o_bd = NULL;
    BER_BVZERO( &op->o_req_dn );
    BER_BVZERO( &op->o_req_ndn );
    memset( op->o_hdr, 0, sizeof( *op->o_hdr ));
    memset( &op->o_request, 0, sizeof( op->o_request ));
    memset( &op->o_do_not_cache, 0, sizeof( Operation ) - offsetof( Operation, o_do_not_cache ));
    memset( opbuf->ob_controls, 0, sizeof( opbuf->ob_controls ));
    op->o_controls = opbuf->ob_controls;

    if ( ctx ) {
        Operation *op2 = NULL;
        ldap_pvt_thread_pool_setkey( ctx, (void *)slap_op_free,
                                     op, slap_op_q_destroy, (void **)&op2, NULL );
        LDAP_STAILQ_NEXT( op, o_next ) = op2;
        if ( op2 ) {
            op->o_tincr = op2->o_tincr + 1;
            /* No more than 10 ops on per-thread free list */
            if ( op->o_tincr > 10 ) {
                ldap_pvt_thread_pool_setkey( ctx, (void *)slap_op_free,
                                             op2, slap_op_q_destroy, NULL, NULL );
                ber_memfree_x( op, NULL );
            }
        } else {
            op->o_tincr = 1;
        }
    } else {
        ber_memfree_x( op, NULL );
    }
}