Commit 279760a4 authored by Jong Hyuk Choi's avatar Jong Hyuk Choi
Browse files

1. Session history support

	- memory based session history to minimize sync traffic
	- when client is covered by a session history, then
      [add+delete] mode is used
	- when client cookie is not covered by the history because
      the cookie is too outdated and/or the history is truncated,
	  [add+present] mode is used
2. Sync cookie syntax : comma separated name=value pairs
	- csn=yyyymmddhh:mm:ssZ#0xSSSS#r#ssssr,sid=nnn
parent 9492ed16
......@@ -23,7 +23,7 @@ SRCS = main.c globals.c config.c daemon.c \
oidm.c starttls.c index.c sets.c referral.c root_dse.c \
sasl.c module.c mra.c mods.c sl_malloc.c limits.c \
backglue.c operational.c matchedValues.c cancel.c syncrepl.c \
backover.c ctxcsn.c $(@PLAT@_SRCS)
backover.c ctxcsn.c ldapsync.c sessionlog.c $(@PLAT@_SRCS)
OBJS = main.o globals.o config.o daemon.o \
connection.o search.o filter.o add.o cr.o \
......@@ -37,7 +37,7 @@ OBJS = main.o globals.o config.o daemon.o \
oidm.o starttls.o index.o sets.o referral.o root_dse.o \
sasl.o module.o mra.o mods.o sl_malloc.o limits.o \
backglue.o operational.o matchedValues.o cancel.o syncrepl.o \
backover.o ctxcsn.o $(@PLAT@_OBJS)
backover.o ctxcsn.o ldapsync.o sessionlog.o $(@PLAT@_OBJS)
LDAP_INCDIR= ../../include -I$(srcdir)/slapi
LDAP_LIBDIR= ../../libraries
......
......@@ -528,7 +528,7 @@ retry: /* transaction retry */
return_results:
send_ldap_result( op, rs );
if ( rs->sr_err == LDAP_SUCCESS && !noop ) {
if ( rs->sr_err == LDAP_SUCCESS && !noop && !op->o_no_psearch ) {
LDAP_LIST_FOREACH ( ps_list, &bdb->bi_psearch_list, o_ps_link ) {
bdb_psearch( op, rs, ps_list, op->oq_add.rs_e, LDAP_PSEARCH_BY_ADD );
}
......
......@@ -162,6 +162,8 @@ struct bdb_info {
ID bi_lastid;
ldap_pvt_thread_mutex_t bi_lastid_mutex;
LDAP_LIST_HEAD(pl, slap_op) bi_psearch_list;
ldap_pvt_thread_mutex_t bi_pslist_mutex;
LDAP_LIST_HEAD(se, slap_session_entry) bi_session_list;
#ifdef SLAP_IDL_CACHE
int bi_idl_cache_max_size;
int bi_idl_cache_size;
......
......@@ -127,31 +127,31 @@ bdb_db_config(
if( rc != LDAP_SUCCESS ) return 1;
/* unique key for shared memory regions */
} else if ( strcasecmp( argv[0], "shm_key" ) == 0 ) {
if ( argc < 2 ) {
fprintf( stderr,
"%s: line %d: missing key in \"shm_key <key>\" line\n",
fname, lineno );
return( 1 );
}
bdb->bi_shm_key = atoi( argv[1] );
} else if ( strcasecmp( argv[0], "shm_key" ) == 0 ) {
if ( argc < 2 ) {
fprintf( stderr,
"%s: line %d: missing key in \"shm_key <key>\" line\n",
fname, lineno );
return( 1 );
}
bdb->bi_shm_key = atoi( argv[1] );
/* size of the cache in entries */
} else if ( strcasecmp( argv[0], "cachesize" ) == 0 ) {
if ( argc < 2 ) {
fprintf( stderr,
"%s: line %d: missing size in \"cachesize <size>\" line\n",
fname, lineno );
return( 1 );
}
bdb->bi_cache.c_maxsize = atoi( argv[1] );
} else if ( strcasecmp( argv[0], "cachesize" ) == 0 ) {
if ( argc < 2 ) {
fprintf( stderr,
"%s: line %d: missing size in \"cachesize <size>\" line\n",
fname, lineno );
return( 1 );
}
bdb->bi_cache.c_maxsize = atoi( argv[1] );
/* depth of search stack cache in units of (IDL)s */
} else if ( strcasecmp( argv[0], "searchstack" ) == 0 ) {
} else if ( strcasecmp( argv[0], "searchstack" ) == 0 ) {
if ( argc < 2 ) {
fprintf( stderr,
"%s: line %d: missing depth in \"searchstack <depth>\" line\n",
fname, lineno );
"%s: line %d: missing depth in \"searchstack <depth>\" line\n",
fname, lineno );
return( 1 );
}
bdb->bi_search_stack_depth = atoi( argv[1] );
......@@ -165,17 +165,82 @@ bdb_db_config(
#ifdef SLAP_IDL_CACHE
/* size of the IDL cache in entries */
} else if ( strcasecmp( argv[0], "idlcachesize" ) == 0 ) {
if ( argc < 2 ) {
fprintf( stderr,
"%s: line %d: missing size in \"idlcachesize <size>\" line\n",
fname, lineno );
return( 1 );
}
if ( !( slapMode & SLAP_TOOL_MODE ) )
bdb->bi_idl_cache_max_size = atoi( argv[1] );
} else if ( strcasecmp( argv[0], "idlcachesize" ) == 0 ) {
if ( argc < 2 ) {
fprintf( stderr,
"%s: line %d: missing size in \"idlcachesize <size>\" line\n",
fname, lineno );
return( 1 );
}
if ( !( slapMode & SLAP_TOOL_MODE ) )
bdb->bi_idl_cache_max_size = atoi( argv[1] );
#endif
} else if ( strcasecmp( argv[0], "sessionlog" ) == 0 ) {
int se_id = 0, se_size = 0;
struct slap_session_entry *sent;
if ( argc < 3 ) {
#ifdef NEW_LOGGING
LDAP_LOG( CONFIG, CRIT,
"%s: line %d: missing arguments in \"sessionlog <id> <size>\""
" line.\n", fname, lineno , 0 );
#else
Debug( LDAP_DEBUG_ANY,
"%s: line %d: missing arguments in \"sessionlog <id> <size>\""
" line\n", fname, lineno, 0 );
#endif
return( 1 );
}
se_id = atoi( argv[1] );
se_size = atoi( argv[2] );
if ( se_id < 0 || se_id > 999 ) {
#ifdef NEW_LOGGING
LDAP_LOG( CONFIG, CRIT,
"%s: line %d: session log id %d is out of range [0..999]\n",
fname, lineno , se_id );
#else
Debug( LDAP_DEBUG_ANY,
"%s: line %d: session log id %d is out of range [0..999]\n",
fname, lineno , se_id );
#endif
return( 1 );
}
if ( se_size < 0 || se_size > 999 ) {
#ifdef NEW_LOGGING
LDAP_LOG( CONFIG, CRIT,
"%s: line %d: session log size %d is negative\n",
fname, lineno , se_size );
#else
Debug( LDAP_DEBUG_ANY,
"%s: line %d: session log size %d is negative\n",
fname, lineno , se_size );
#endif
return( 1 );
}
LDAP_LIST_FOREACH( sent, &bdb->bi_session_list, se_link ) {
if ( sent->se_id == se_id ) {
#ifdef NEW_LOGGING
LDAP_LOG( CONFIG, CRIT,
"%s: line %d: session %d already exists\n",
fname, lineno , se_id );
#else
Debug( LDAP_DEBUG_ANY,
"%s: line %d: session %d already exists\n",
fname, lineno , se_id );
#endif
return( 1 );
}
}
sent = (struct slap_session_entry *) ch_calloc( 1,
sizeof( struct slap_session_entry ));
sent->se_id = se_id;
sent->se_size = se_size;
LDAP_LIST_INSERT_HEAD( &bdb->bi_session_list, sent, se_link );
/* anything else */
} else {
fprintf( stderr, "%s: line %d: "
......
......@@ -262,6 +262,7 @@ bdb_get_commit_csn(
int num_retries = 0;
int ctxcsn_added = 0;
int rc;
struct sync_cookie syncCookie = { NULL, -1, NULL};
if ( op->o_sync_mode != SLAP_SYNC_NONE ) {
if ( op->o_bd->be_syncinfo ) {
......@@ -271,7 +272,8 @@ bdb_get_commit_csn(
ber_str2bv( substr, 0, 0, &bv );
build_new_dn( &ctxcsn_ndn, &op->o_bd->be_nsuffix[0], &bv, NULL );
} else {
build_new_dn( &ctxcsn_ndn, &op->o_bd->be_nsuffix[0], (struct berval *)&slap_ldapsync_cn_bv, NULL );
build_new_dn( &ctxcsn_ndn, &op->o_bd->be_nsuffix[0],
(struct berval *)&slap_ldapsync_cn_bv, NULL );
}
ctxcsn_retry :
......@@ -330,7 +332,8 @@ txn_retry:
return rs->sr_err;
}
bdb_cache_add( bdb, suffix_ei, ctxcsn_e, (struct berval *)&slap_ldapsync_cn_bv, locker );
bdb_cache_add( bdb, suffix_ei, ctxcsn_e,
(struct berval *)&slap_ldapsync_cn_bv, locker );
rs->sr_err = TXN_COMMIT( ltid, 0 );
if ( rs->sr_err != 0 ) {
......@@ -361,14 +364,24 @@ txn_retry:
if ( op->o_bd->be_syncinfo ) {
csn_a = attr_find( ctxcsn_e->e_attrs,
slap_schema.si_ad_syncreplCookie );
if ( csn_a ) {
struct berval cookie;
ber_dupbv( &cookie, &csn_a->a_vals[0] );
ber_bvarray_add( &syncCookie.octet_str, &cookie );
slap_parse_sync_cookie( &syncCookie );
*search_context_csn = ber_dupbv( NULL, syncCookie.ctxcsn );
slap_sync_cookie_free( &syncCookie, 0 );
} else {
*search_context_csn = NULL;
}
} else {
csn_a = attr_find( ctxcsn_e->e_attrs,
slap_schema.si_ad_contextCSN );
}
if ( csn_a ) {
*search_context_csn = ber_dupbv( NULL, &csn_a->a_vals[0] );
} else {
*search_context_csn = NULL;
if ( csn_a ) {
*search_context_csn = ber_dupbv( NULL, &csn_a->a_vals[0] );
} else {
*search_context_csn = NULL;
}
}
} else {
*search_context_csn = NULL;
......
......@@ -540,7 +540,7 @@ retry: /* transaction retry */
return_results:
send_ldap_result( op, rs );
if ( rs->sr_err == LDAP_SUCCESS && !noop ) {
if ( rs->sr_err == LDAP_SUCCESS && !noop && !op->o_no_psearch ) {
LDAP_LIST_FOREACH( ps_list, &bdb->bi_psearch_list, o_ps_link ) {
bdb_psearch( op, rs, ps_list, e, LDAP_PSEARCH_BY_DELETE );
}
......
......@@ -467,7 +467,7 @@ retry: /* transaction retry */
goto return_results;
}
if ( rs->sr_err == LDAP_SUCCESS && !op->o_noop ) {
if ( rs->sr_err == LDAP_SUCCESS && !op->o_noop && !op->o_no_psearch ) {
LDAP_LIST_FOREACH ( ps_list, &bdb->bi_psearch_list, o_ps_link ) {
bdb_psearch(op, rs, ps_list, e, LDAP_PSEARCH_BY_PREMODIFY );
}
......
......@@ -864,7 +864,7 @@ retry: /* transaction retry */
goto return_results;
}
if ( rs->sr_err == LDAP_SUCCESS && !op->o_noop ) {
if ( rs->sr_err == LDAP_SUCCESS && !op->o_noop && !op->o_no_psearch ) {
LDAP_LIST_FOREACH ( ps_list, &bdb->bi_psearch_list, o_ps_link ) {
bdb_psearch( op, rs, ps_list, e, LDAP_PSEARCH_BY_PREMODIFY );
}
......
......@@ -548,30 +548,8 @@ int bdb_do_search(
);
#define bdb_psearch(op, rs, sop, e, ps_type) bdb_do_search(op, rs, sop, e, ps_type)
#define bdb_build_sync_state_ctrl BDB_SYMBOL(build_sync_state_ctrl)
#define bdb_build_sync_done_ctrl BDB_SYMBOL(build_sync_done_ctrl)
#define bdb_send_ldap_intermediate BDB_SYMBOL(send_ldap_intermediate)
int
bdb_build_sync_state_ctrl(
Operation *op,
SlapReply *rs,
Entry *e,
int entry_sync_state,
LDAPControl **ctrls,
int num_ctrls,
int send_cookie,
struct berval *csn );
int
bdb_build_sync_done_ctrl(
Operation *op,
SlapReply *rs,
LDAPControl **ctrls,
int num_ctrls,
int send_cookie,
struct berval *latest_entrycsn_bv );
int
bdb_send_ldap_intermediate(
Operation *op,
......
......@@ -378,7 +378,6 @@ bdb_do_search( Operation *op, SlapReply *rs, Operation *sop,
AttributeName *attrs;
Filter contextcsnand, contextcsnle, cookief, csnfnot, csnfeq, csnfand, csnfge;
Filter omitcsnf, omitcsnfle;
AttributeAssertion aa_ge, aa_eq, aa_le;
int entry_count = 0;
struct berval *search_context_csn = NULL;
......@@ -396,19 +395,86 @@ bdb_do_search( Operation *op, SlapReply *rs, Operation *sop,
u_int32_t locker = 0;
DB_LOCK lock;
Operation *ps_list;
int sync_send_present_mode = 1;
int match;
MatchingRule *mr;
const char *text;
int slog_found = 0;
#ifdef NEW_LOGGING
LDAP_LOG( OPERATION, ENTRY, "bdb_back_search\n", 0, 0, 0 );
LDAP_LOG( OPERATION, ENTRY, "bdb_search\n", 0, 0, 0 );
#else
Debug( LDAP_DEBUG_TRACE, "=> bdb_back_search\n",
Debug( LDAP_DEBUG_TRACE, "=> bdb_search\n",
0, 0, 0);
#endif
attrs = sop->oq_search.rs_attrs;
if ( !IS_PSEARCH && sop->o_sync_mode & SLAP_SYNC_REFRESH_AND_PERSIST ) {
struct slap_session_entry *sent;
if ( sop->o_sync_state.sid >= 0 ) {
LDAP_LIST_FOREACH( sent, &bdb->bi_session_list, se_link ) {
if ( sent->se_id == sop->o_sync_state.sid ) {
sop->o_sync_slog_size = sent->se_size;
break;
}
}
}
}
/* psearch needs to be registered before refresh begins */
/* psearch and refresh transmission is serialized in send_ldap_ber() */
if ( !IS_PSEARCH && sop->o_sync_mode & SLAP_SYNC_PERSIST ) {
ldap_pvt_thread_mutex_lock( &bdb->bi_pslist_mutex );
LDAP_LIST_INSERT_HEAD( &bdb->bi_psearch_list, sop, o_ps_link );
ldap_pvt_thread_mutex_unlock( &bdb->bi_pslist_mutex );
} else if ( !IS_PSEARCH && sop->o_sync_mode & SLAP_SYNC_REFRESH_AND_PERSIST
&& sop->o_sync_slog_size >= 0 ) {
ldap_pvt_thread_mutex_lock( &bdb->bi_pslist_mutex );
LDAP_LIST_FOREACH( ps_list, &bdb->bi_psearch_list, o_ps_link ) {
if ( ps_list->o_sync_slog_size >= 0 ) {
if ( ps_list->o_sync_state.sid == sop->o_sync_state.sid ) {
slog_found = 1;
break;
}
}
}
if ( slog_found ) {
if ( ps_list->o_sync_slog_omitcsn.bv_len != 0 ) {
mr = slap_schema.si_ad_entryCSN->ad_type->sat_ordering;
if ( sop->o_sync_state.ctxcsn && sop->o_sync_state.ctxcsn->bv_val != NULL ) {
value_match( &match, slap_schema.si_ad_entryCSN, mr,
SLAP_MR_VALUE_OF_ATTRIBUTE_SYNTAX,
sop->o_sync_state.ctxcsn, &ps_list->o_sync_slog_omitcsn,
&text );
} else {
match = -1;
}
if ( match >= 0 ) {
rs->sr_err = LDAP_SUCCESS;
rs->sr_rspoid = LDAP_SYNC_INFO;
rs->sr_ctrls = NULL;
bdb_send_ldap_intermediate( sop, rs,
LDAP_SYNC_STATE_MODE_DONE, NULL );
sync_send_present_mode = 0;
}
} else {
rs->sr_err = LDAP_SUCCESS;
rs->sr_rspoid = LDAP_SYNC_INFO;
rs->sr_ctrls = NULL;
bdb_send_ldap_intermediate( sop, rs,
LDAP_SYNC_STATE_MODE_DONE, NULL );
sync_send_present_mode = 0;
}
} else if ( sop->o_sync_slog_size >= 0 ) {
LDAP_LIST_INSERT_HEAD( &bdb->bi_psearch_list, sop, o_ps_link );
} else {
sop->o_sync_state.sid = -1;
}
ldap_pvt_thread_mutex_unlock( &bdb->bi_pslist_mutex );
}
null_attr.an_desc = NULL;
null_attr.an_oc = NULL;
null_attr.an_name.bv_len = 0;
......@@ -670,8 +736,9 @@ dn2entry_retry:
goto done;
}
if ( sop->o_sync_state.bv_val && ber_bvcmp( &sop->o_sync_state,
search_context_csn ) == 0 )
if ( sop->o_sync_mode != SLAP_SYNC_NONE && sop->o_sync_state.ctxcsn &&
sop->o_sync_state.ctxcsn->bv_val &&
ber_bvcmp( &sop->o_sync_state.ctxcsn[0], search_context_csn ) == 0 )
{
bdb_cache_entry_db_unlock( bdb->bi_dbenv, &ctxcsn_lock );
goto nochange;
......@@ -777,8 +844,6 @@ dn2entry_retry:
if ( (sop->o_sync_mode & SLAP_SYNC_REFRESH) || IS_PSEARCH )
{
MatchingRule *mr;
const char *text;
int match;
cookief.f_choice = LDAP_FILTER_AND;
......@@ -792,7 +857,11 @@ dn2entry_retry:
csnfeq.f_choice = LDAP_FILTER_EQUALITY;
csnfeq.f_ava = &aa_eq;
csnfeq.f_av_desc = slap_schema.si_ad_entryCSN;
csnfeq.f_av_value = sop->o_sync_state;
if ( sop->o_sync_state.ctxcsn != NULL ) {
csnfeq.f_av_value = *sop->o_sync_state.ctxcsn;
} else {
csnfeq.f_av_value = slap_empty_bv;
}
csnfand.f_choice = LDAP_FILTER_AND;
csnfand.f_and = &csnfge;
......@@ -801,7 +870,11 @@ dn2entry_retry:
csnfge.f_choice = LDAP_FILTER_GE;
csnfge.f_ava = &aa_ge;
csnfge.f_av_desc = slap_schema.si_ad_entryCSN;
csnfge.f_av_value = sop->o_sync_state;
if ( sop->o_sync_state.ctxcsn != NULL ) {
csnfge.f_av_value = *sop->o_sync_state.ctxcsn;
} else {
csnfge.f_av_value = slap_empty_bv;
}
if ( search_context_csn && !IS_PSEARCH ) {
csnfge.f_next = &contextcsnand;
......@@ -817,21 +890,23 @@ dn2entry_retry:
contextcsnle.f_next = sop->oq_search.rs_filter;
mr = slap_schema.si_ad_entryCSN->ad_type->sat_ordering;
if ( sop->o_sync_state.bv_len != 0 ) {
if ( sop->o_sync_state.ctxcsn &&
sop->o_sync_state.ctxcsn->bv_val != NULL ) {
value_match( &match, slap_schema.si_ad_entryCSN, mr,
SLAP_MR_VALUE_OF_ATTRIBUTE_SYNTAX,
&sop->o_sync_state, search_context_csn, &text );
SLAP_MR_VALUE_OF_ATTRIBUTE_SYNTAX,
&sop->o_sync_state.ctxcsn[0], search_context_csn,
&text );
} else {
match = -1;
}
no_sync_state_change = !match;
no_sync_state_change = ( match >= 0 );
} else {
csnfge.f_next = sop->oq_search.rs_filter;
}
}
for ( id = bdb_idl_first( candidates, &cursor );
id != NOID;
id != NOID && !no_sync_state_change;
id = bdb_idl_next( candidates, &cursor ) )
{
int scopeok = 0;
......@@ -1060,8 +1135,7 @@ id2entry_retry:
} else {
if ( sop->o_sync_mode & SLAP_SYNC_REFRESH ) {
rc_sync = test_filter( sop, rs->sr_entry, &cookief );
rs->sr_err = test_filter( sop,
rs->sr_entry, &contextcsnand );
rs->sr_err = test_filter( sop, rs->sr_entry, &contextcsnand );
if ( rs->sr_err == LDAP_COMPARE_TRUE ) {
if ( rc_sync == LDAP_COMPARE_TRUE ) {
if ( no_sync_state_change ) {
......@@ -1091,7 +1165,8 @@ id2entry_retry:
if ( rs->sr_err == LDAP_COMPARE_TRUE ) {
/* check size limit */
if ( --sop->oq_search.rs_slimit == -1 ) {
if ( --sop->oq_search.rs_slimit == -1 &&
sop->o_sync_slog_size == -1 ) {
if (!IS_PSEARCH) {
bdb_cache_return_entry_r( bdb->bi_dbenv,
&bdb->bi_cache, e, &lock );
......@@ -1157,49 +1232,82 @@ id2entry_retry:
} else {
entry_sync_state = LDAP_SYNC_ADD;
}
} else if ( ps_type == LDAP_PSEARCH_BY_SCOPEOUT )
} else if ( ps_type == LDAP_PSEARCH_BY_SCOPEOUT ) {
entry_sync_state = LDAP_SYNC_DELETE;
else {
} else {
rs->sr_err = LDAP_OTHER;
goto done;
}
rs->sr_err = bdb_build_sync_state_ctrl( sop,
rs, e, entry_sync_state, ctrls,
num_ctrls++, 1, search_context_csn );
if ( rs->sr_err != LDAP_SUCCESS ) goto done;
rs->sr_attrs = attrs;
rs->sr_ctrls = ctrls;
result = send_search_entry( sop, rs );
ch_free( ctrls[num_ctrls-1]->ldctl_value.bv_val );
ch_free( ctrls[--num_ctrls] );
ctrls[num_ctrls] = NULL;
rs->sr_ctrls = NULL;
if ( sop->o_sync_slog_size != -1 ) {
if ( entry_sync_state == LDAP_SYNC_DELETE ) {
result = slap_add_session_log( op, sop, e );
} else {
result = 1;
}
} else {
struct berval cookie;
slap_compose_sync_cookie( sop, &cookie,
search_context_csn,
sop->o_sync_state.sid );
rs->sr_err = slap_build_sync_state_ctrl( sop,
rs, e, entry_sync_state, ctrls,
num_ctrls++, 1, &cookie );
if ( rs->sr_err != LDAP_SUCCESS ) goto done;
rs->sr_attrs = attrs;
rs->sr_ctrls = ctrls;
result = send_search_entry( sop, rs );
if ( cookie.bv_val )
ch_free( cookie.bv_val );
ch_free( ctrls[num_ctrls-1]->ldctl_value.bv_val );
ch_free( ctrls[--num_ctrls] );
ctrls[num_ctrls] = NULL;
rs->sr_ctrls = NULL;
}
} else if ( ps_type == LDAP_PSEARCH_BY_PREMODIFY ) {
struct psid_entry* psid_e;
psid_e = (struct psid_entry *) calloc (1,
psid_e = (struct psid_entry *) ch_calloc (1,
sizeof(struct psid_entry));
psid_e->ps_op = sop;
LDAP_LIST_INSERT_HEAD( &op->o_pm_list,
psid_e, ps_link );
} else {
printf("Error !\n");
#ifdef NEW_LOGGING
LDAP_LOG ( OPERATION, RESULTS,
"bdb_search: invalid ps_type (%d) \n",
ps_type, 0, 0);
#else
Debug( LDAP_DEBUG_TRACE,
"bdb_search: invalid ps_type (%d) \n",
ps_type, 0, 0);
#endif
}
} else {
if ( sop->o_sync_mode & SLAP_SYNC_REFRESH ) {
rs->sr_err = bdb_build_sync_state_ctrl( sop,
struct berval cookie;
slap_compose_sync_cookie( sop, &cookie,
search_context_csn,
sop->o_sync_state.sid );
rs->sr_err = slap_build_sync_state_ctrl( sop,
rs, e, entry_sync_state, ctrls,
num_ctrls++, 0, search_context_csn );
num_ctrls++, 0, &cookie );
if ( rs->sr_err != LDAP_SUCCESS ) goto done;
rs->sr_ctrls = ctrls;
if ( rc_sync == LDAP_COMPARE_TRUE ) { /* ADD */
rs->sr_attrs = sop->oq_search.rs_attrs;
result = send_search_entry( sop, rs );
} else { /* PRESENT */
rs->sr_attrs = &null_attr;
if ( sync_send_present_mode ) {
rs->sr_attrs = &null_attr;
result = send_search_entry( sop, rs );
} else {
result = 1;
}
}
result = send_search_entry( sop, rs );
if ( cookie.bv_val )
ch_free( cookie.bv_val );
ch_free( ctrls[num_ctrls-1]->ldctl_value.bv_val );
ch_free( ctrls[--num_ctrls] );
ctrls[num_ctrls] = NULL;
......@@ -1260,19 +1368,54 @@ nochange: