Browse Source

Trac Ticket #260 - 389 DS does not support multiple
paging controls on a single connection

https://fedorahosted.org/389/ticket/260

Fix description:
1. "Connection" object holds the paged results related values.
Now they are packaged in one PagedResults object. And the
array of PagedResults with its length and used count are
placed in PagedResultList, which is stashed in Connection
(slap.h).
2. The pagedresults APIs are extended to take the index of Paged-
Results array. The index is set to the cookie in the Paged-
Results control before returning it to the client. When a
client sends a paged results request, the cookie field in the
first request control is supposed to be empty. The result
control is returned with the search results. The result
control stores the index in the cookie and the client sets
it to the following request control to get the next pages.
When the paged search is done, empty cookie is returned.
If a passed cookie is less than 0 or greater than the array
size and the request control is "critical", the request fails
with LDAP_PROTOCOL_ERROR and the error is logged in the error
log. If the control is not "critical", the paged results
request is disabled.
3. The array grows if multiple simple paged results requests over
a single connection come in. The array does not get
released every time, but it is when the server is shutdown.
4. Simple paged results connection is timed out when it exeeds
the timelimit. If multiple requests are served, it won't
be disconnected until all the requests are timed out.

Noriko Hosoi 13 years ago
parent
commit
add880acca

+ 6 - 5
ldap/servers/slapd/abandon.c

@@ -152,11 +152,12 @@ do_abandon( Slapi_PBlock *pb )
 		    0 );
 	}
 
-	if (pagedresults_cleanup(pb->pb_conn, 0 /* already locked */)) {
-		/* Cleaned up paged result connection */
-		slapi_log_access( LDAP_DEBUG_STATS, "conn=%" NSPRIu64 " op=%d ABANDON"
-			" targetop=Simple Paged Results\n",
-			pb->pb_conn->c_connid, pb->pb_op->o_opid );
+	if ( op_is_pagedresults(o) ) {
+		if ( 0 == pagedresults_free_one_msgid(pb->pb_conn, id) ) {
+			slapi_log_access( LDAP_DEBUG_STATS, "conn=%" NSPRIu64 
+			                  " op=%d ABANDON targetop=Simple Paged Results\n",
+			                  pb->pb_conn->c_connid, pb->pb_op->o_opid );
+		}
 	} else if ( NULL == o ) {
 		slapi_log_access( LDAP_DEBUG_STATS, "conn=%" NSPRIu64 " op=%d ABANDON"
 			" targetop=NOTFOUND msgid=%d\n",

+ 19 - 8
ldap/servers/slapd/back-ldbm/filterindex.c

@@ -223,6 +223,7 @@ ava_candidates(
     int           unindexed = 0;
     Slapi_Attr    sattr;
     back_txn      txn = {NULL};
+    int           pr_idx = -1;
 
     LDAPDebug( LDAP_DEBUG_TRACE, "=> ava_candidates\n", 0, 0, 0 );
 
@@ -232,6 +233,7 @@ ava_candidates(
         return( NULL );
     }
 
+    slapi_pblock_get(pb, SLAPI_PAGED_RESULTS_INDEX, &pr_idx);
     slapi_attr_init(&sattr, type);
 
 #ifdef LDAP_DEBUG
@@ -315,7 +317,7 @@ ava_candidates(
         if ( unindexed ) {
             unsigned int opnote = SLAPI_OP_NOTE_UNINDEXED;
             slapi_pblock_set( pb, SLAPI_OPERATION_NOTES, &opnote );
-            pagedresults_set_unindexed( pb->pb_conn );
+            pagedresults_set_unindexed( pb->pb_conn, pr_idx );
         }
 
         /* We don't use valuearray_free here since the valueset, berval
@@ -347,7 +349,7 @@ ava_candidates(
         if ( unindexed ) {
             unsigned int opnote = SLAPI_OP_NOTE_UNINDEXED;
             slapi_pblock_set( pb, SLAPI_OPERATION_NOTES, &opnote );
-            pagedresults_set_unindexed( pb->pb_conn );
+            pagedresults_set_unindexed( pb->pb_conn, pr_idx );
         }
          valuearray_free( &ivals );
          LDAPDebug( LDAP_DEBUG_TRACE, "<= ava_candidates %lu\n",
@@ -384,9 +386,11 @@ presence_candidates(
                                  NULL, &txn, err, &unindexed, allidslimit );
 
     if ( unindexed ) {
+        int pr_idx = -1;
         unsigned int opnote = SLAPI_OP_NOTE_UNINDEXED;
         slapi_pblock_set( pb, SLAPI_OPERATION_NOTES, &opnote );
-        pagedresults_set_unindexed( pb->pb_conn );
+        slapi_pblock_get(pb, SLAPI_PAGED_RESULTS_INDEX, &pr_idx);
+        pagedresults_set_unindexed( pb->pb_conn, pr_idx );
     }
 
     if (idl != NULL && ALLIDS(idl) && strcasecmp(type, "nscpentrydn") == 0) {
@@ -492,10 +496,15 @@ extensible_candidates(
                                 index_range_read_ext(pb, be, mrTYPE, mrOID, mrOP,
                                                      *key, NULL, 0, &txn, err, allidslimit);
                             if ( unindexed ) {
+                                int pr_idx = -1;
                                 unsigned int opnote = SLAPI_OP_NOTE_UNINDEXED;
                                 slapi_pblock_set( glob_pb,
-                                               SLAPI_OPERATION_NOTES, &opnote );
-                                pagedresults_set_unindexed( glob_pb->pb_conn );
+                                            SLAPI_OPERATION_NOTES, &opnote );
+                                slapi_pblock_get( glob_pb,
+                                                  SLAPI_PAGED_RESULTS_INDEX,
+                                                  &pr_idx );
+                                pagedresults_set_unindexed( glob_pb->pb_conn,
+                                                            pr_idx );
                             }
                             if (idl2 == NULL)
                             {
@@ -839,7 +848,7 @@ list_candidates(
              */
             /* PAGED RESULTS: we strictly limit the idlist size by the allids (aka idlistscan) limit.
              */
-            if (operation->o_flags & OP_FLAG_PAGED_RESULTS) {
+            if (op_is_pagedresults(operation)) {
                 int nids = IDL_NIDS(idl);
                 if ( allidslimit > 0 && nids > allidslimit ) {
                     idl_free( idl );
@@ -887,6 +896,7 @@ substring_candidates(
     unsigned int opnote = SLAPI_OP_NOTE_UNINDEXED;
     Slapi_Attr   sattr;
     back_txn     txn = {NULL};
+    int          pr_idx = -1;
 
     LDAPDebug( LDAP_DEBUG_TRACE, "=> sub_candidates\n", 0, 0, 0 );
 
@@ -903,9 +913,10 @@ substring_candidates(
     slapi_attr_init(&sattr, type);
     slapi_attr_assertion2keys_sub_sv( &sattr, initial, any, final, &ivals );
     attr_done(&sattr);
+    slapi_pblock_get(pb, SLAPI_PAGED_RESULTS_INDEX, &pr_idx);
     if ( ivals == NULL || *ivals == NULL ) {
         slapi_pblock_set( pb, SLAPI_OPERATION_NOTES, &opnote );
-        pagedresults_set_unindexed( pb->pb_conn );
+        pagedresults_set_unindexed( pb->pb_conn, pr_idx );
         LDAPDebug( LDAP_DEBUG_TRACE,
             "<= sub_candidates ALLIDS (no keys)\n", 0, 0, 0 );
         return( idl_allids( be ) );
@@ -919,7 +930,7 @@ substring_candidates(
     idl = keys2idl( be, type, indextype_SUB, ivals, err, &unindexed, &txn, allidslimit );
     if ( unindexed ) {
         slapi_pblock_set( pb, SLAPI_OPERATION_NOTES, &opnote );
-        pagedresults_set_unindexed( pb->pb_conn );
+        pagedresults_set_unindexed( pb->pb_conn, pr_idx );
     }
     valuearray_free( &ivals );
 

+ 18 - 11
ldap/servers/slapd/back-ldbm/ldbm_search.c

@@ -95,7 +95,7 @@ compute_lookthrough_limit( Slapi_PBlock *pb, struct ldbminfo *li )
         }
     }
 
-    if (op && (op->o_flags & OP_FLAG_PAGED_RESULTS)) {
+    if (op_is_pagedresults(op)) {
         if ( slapi_reslimit_get_integer_limit( conn,
                 li->li_reslimit_pagedlookthrough_handle, &limit )
                 != SLAPI_RESLIMIT_STATUS_SUCCESS ) {
@@ -127,7 +127,7 @@ compute_allids_limit( Slapi_PBlock *pb, struct ldbminfo *li )
         limit = li->li_allidsthreshold;
         PR_Unlock(li->li_config_mutex);
     }
-    if (op && (op->o_flags & OP_FLAG_PAGED_RESULTS)) {
+    if (op_is_pagedresults(op)) {
         if ( slapi_reslimit_get_integer_limit( conn,
                 li->li_reslimit_pagedallids_handle, &limit )
              != SLAPI_RESLIMIT_STATUS_SUCCESS ) {
@@ -184,8 +184,10 @@ ldbm_back_search_cleanup(Slapi_PBlock *pb,
         back_search_result_set *sr = NULL;
         slapi_pblock_get( pb, SLAPI_SEARCH_RESULT_SET, &sr );
         if ( (NULL != sr) && (function_result != 0) ) {
+            int pr_idx = -1;
+            slapi_pblock_get( pb, SLAPI_PAGED_RESULTS_INDEX, &pr_idx );
             /* in case paged results, clean up the conn */
-            pagedresults_set_search_result(pb->pb_conn, NULL, 0);
+            pagedresults_set_search_result( pb->pb_conn, NULL, 0, pr_idx );
             slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_SET, NULL );
             slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_SET_SIZE_ESTIMATE, &estimate );
             delete_search_result_set(&sr);
@@ -355,8 +357,8 @@ ldbm_back_search( Slapi_PBlock *pb )
 
     if ( !txn.back_txn_txn ) {
         dblayer_txn_init( li, &txn );
-		slapi_pblock_set( pb, SLAPI_TXN, txn.back_txn_txn );
-	}
+        slapi_pblock_set( pb, SLAPI_TXN, txn.back_txn_txn );
+    }
 
     inst = (ldbm_instance *) be->be_instance_info;
 
@@ -829,6 +831,7 @@ ldbm_back_search( Slapi_PBlock *pb )
     if ( NULL != candidates && ALLIDS( candidates )) {
         unsigned int opnote = SLAPI_OP_NOTE_UNINDEXED;
         int ri = 0;
+        int pr_idx = -1;
 
         /*
          * Return error if nsslapd-require-index is set and
@@ -850,7 +853,8 @@ ldbm_back_search( Slapi_PBlock *pb )
         }
 
         slapi_pblock_set( pb, SLAPI_OPERATION_NOTES, &opnote );
-        pagedresults_set_unindexed( pb->pb_conn );
+        slapi_pblock_get( pb, SLAPI_PAGED_RESULTS_INDEX, &pr_idx );
+        pagedresults_set_unindexed( pb->pb_conn, pr_idx );
     }
 
     sr->sr_candidates = candidates;
@@ -1338,6 +1342,7 @@ ldbm_back_next_search_entry_ext( Slapi_PBlock *pb, int use_extension )
     int                    rc = 0; 
     int                    estimate = 0; /* estimated search result count */
     back_txn               txn = {NULL};
+    int                    pr_idx = -1;
 
     slapi_pblock_get( pb, SLAPI_BACKEND, &be );
     slapi_pblock_get( pb, SLAPI_PLUGIN_PRIVATE, &li );
@@ -1354,6 +1359,7 @@ ldbm_back_next_search_entry_ext( Slapi_PBlock *pb, int use_extension )
     slapi_pblock_get( pb, SLAPI_TARGET_UNIQUEID, &target_uniqueid );
     slapi_pblock_get( pb, SLAPI_SEARCH_RESULT_SET, &sr );
     slapi_pblock_get( pb, SLAPI_TXN, &txn.back_txn_txn );
+    slapi_pblock_get( pb, SLAPI_PAGED_RESULTS_INDEX, &pr_idx );
 
     if ( !txn.back_txn_txn ) {
         dblayer_txn_init( li, &txn );
@@ -1424,7 +1430,7 @@ ldbm_back_next_search_entry_ext( Slapi_PBlock *pb, int use_extension )
         if ( slapi_op_abandoned( pb ) || (NULL == sr) )
         {
             /* in case paged results, clean up the conn */
-            pagedresults_set_search_result(pb->pb_conn, NULL, 0);
+            pagedresults_set_search_result( pb->pb_conn, NULL, 0, pr_idx );
             slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_SET, NULL );
             slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_SET_SIZE_ESTIMATE, &estimate );
             if ( use_extension ) {
@@ -1441,7 +1447,7 @@ ldbm_back_next_search_entry_ext( Slapi_PBlock *pb, int use_extension )
         if ( tlimit != -1 && curtime > stoptime )
         {
             /* in case paged results, clean up the conn */
-            pagedresults_set_search_result(pb->pb_conn, NULL, 0);
+            pagedresults_set_search_result( pb->pb_conn, NULL, 0, pr_idx );
             slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_SET, NULL );
             slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_SET_SIZE_ESTIMATE, &estimate );
             if ( use_extension ) {
@@ -1458,7 +1464,7 @@ ldbm_back_next_search_entry_ext( Slapi_PBlock *pb, int use_extension )
         if ( llimit != -1 && sr->sr_lookthroughcount >= llimit )
         {
             /* in case paged results, clean up the conn */
-            pagedresults_set_search_result(pb->pb_conn, NULL, 0);
+            pagedresults_set_search_result( pb->pb_conn, NULL, 0, pr_idx );
             slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_SET, NULL );
             slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_SET_SIZE_ESTIMATE, &estimate );
             if ( use_extension ) {
@@ -1478,7 +1484,7 @@ ldbm_back_next_search_entry_ext( Slapi_PBlock *pb, int use_extension )
             /* No more entries */
             /* destroy back_search_result_set */
             /* in case paged results, clean up the conn */
-            pagedresults_set_search_result(pb->pb_conn, NULL, 0);
+            pagedresults_set_search_result( pb->pb_conn, NULL, 0, pr_idx );
             slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_SET, NULL );
             slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_SET_SIZE_ESTIMATE, &estimate );
             if ( use_extension ) {
@@ -1621,7 +1627,8 @@ ldbm_back_next_search_entry_ext( Slapi_PBlock *pb, int use_extension )
                      if ( --slimit < 0 ) {
                          CACHE_RETURN( &inst->inst_cache, &e );
                          /* in case paged results, clean up the conn */
-                         pagedresults_set_search_result(pb->pb_conn, NULL, 0);
+                         pagedresults_set_search_result( pb->pb_conn, NULL,
+                                                         0, pr_idx);
                          slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_SET, NULL );
                          slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_SET_SIZE_ESTIMATE, &estimate );
                          delete_search_result_set( &sr );

+ 3 - 1
ldap/servers/slapd/back-ldbm/vlv.c

@@ -1159,9 +1159,11 @@ vlv_search_build_candidate_list(Slapi_PBlock *pb, const Slapi_DN *base, int *vlv
 	slapi_rwlock_rdlock(be->vlvSearchList_lock);
 	if((pi=vlv_find_search(be, base, scope, fstr, sort_control)) == NULL) {
 	    unsigned int opnote = SLAPI_OP_NOTE_UNINDEXED;
+	    int pr_idx = -1;
+	    slapi_pblock_get( pb, SLAPI_PAGED_RESULTS_INDEX, &pr_idx );
 	    slapi_rwlock_unlock(be->vlvSearchList_lock);
 	    slapi_pblock_set( pb, SLAPI_OPERATION_NOTES, &opnote );
-	    pagedresults_set_unindexed( pb->pb_conn );
+	    pagedresults_set_unindexed( pb->pb_conn, pr_idx );
 	    rc = VLV_FIND_SEARCH_FAILED;
 	} else if((*vlv_rc=vlvIndex_accessallowed(pi, pb)) != LDAP_SUCCESS) {
 	    slapi_rwlock_unlock(be->vlvSearchList_lock);

+ 6 - 2
ldap/servers/slapd/connection.c

@@ -117,6 +117,8 @@ connection_done(Connection *conn)
 	{
 		PR_DestroyLock(conn->c_pdumutex);
 	}
+	/* PAGED_RESULTS */
+	pagedresults_cleanup_all(conn, 0);
 }
 
 /*
@@ -2092,7 +2094,7 @@ void connection_enter_leave_turbo(Connection *conn, int current_turbo_flag, int
 	PR_Lock(conn->c_mutex);
 	/* We can already be in turbo mode, or not */
 	current_mode = current_turbo_flag;
-	if (conn->c_search_result_set) {
+	if (pagedresults_in_use(conn)) {
 		/* PAGED_RESULTS does not need turbo mode */
 		new_mode = 0;
 	} else if (conn->c_private->operation_rate == 0) {
@@ -2776,7 +2778,9 @@ disconnect_server_nomutex( Connection *conn, PRUint64 opconnid, int opid, PRErro
 
 	conn->c_gettingber = 0;
 	connection_abandon_operations( conn );
-	conn->c_timelimit = 0; /* needed here to ensure simple paged results timeout properly and don't impact subsequent ops */
+	/* needed here to ensure simple paged results timeout properly and 
+	 * don't impact subsequent ops */
+	pagedresults_reset_timedout(conn);
 
 	if (! config_check_referral_mode()) {
 	    /*

+ 8 - 10
ldap/servers/slapd/daemon.c

@@ -1211,17 +1211,15 @@ setup_pr_read_pds(Connection_Table *ct, PRFileDesc **n_tcps, PRFileDesc **s_tcps
 				{
 					int add_fd = 1;
 					/* check timeout for PAGED RESULTS */
-					if (c->c_current_be && (c->c_timelimit > 0))
+                    if (pagedresults_is_timedout(c))
 					{
-						time_t ctime = current_time();
-						if (ctime > c->c_timelimit)
-						{
-							/* Exceeded the timelimit; disconnect the client */
-							disconnect_server_nomutex(c, c->c_connid, -1,
-													  SLAPD_DISCONNECT_IO_TIMEOUT, 0);
-							connection_table_move_connection_out_of_active_list(ct,c);
-							add_fd = 0; /* do not poll on this fd */
-						}
+						/* Exceeded the timelimit; disconnect the client */
+						disconnect_server_nomutex(c, c->c_connid, -1,
+						                          SLAPD_DISCONNECT_IO_TIMEOUT,
+						                          0);
+						connection_table_move_connection_out_of_active_list(ct,
+						                                                    c);
+						add_fd = 0; /* do not poll on this fd */
 					}
 					if (add_fd)
 					{

+ 49 - 34
ldap/servers/slapd/opshared.c

@@ -268,6 +268,7 @@ op_shared_search (Slapi_PBlock *pb, int send_result)
   Slapi_Backend *pr_be = NULL;
   void *pr_search_result = NULL;
   int pr_reset_processing = 0;
+  int pr_idx = -1;
 
   be_list[0] = NULL;
   referral_list[0] = NULL;
@@ -450,28 +451,38 @@ op_shared_search (Slapi_PBlock *pb, int send_result)
       if ( slapi_control_present (ctrlp, LDAP_CONTROL_PAGEDRESULTS,
                                   &ctl_value, &iscritical) )
       {
-          rc = pagedresults_parse_control_value(ctl_value,
-                                               &pagesize, &curr_search_count);
+          rc = pagedresults_parse_control_value(pb, ctl_value,
+                                                &pagesize, &pr_idx);
+          /* Let's set pr_idx even if it fails; in case, pr_idx == -1. */
+          slapi_pblock_set(pb, SLAPI_PAGED_RESULTS_INDEX, &pr_idx);
           if (LDAP_SUCCESS == rc) {
               unsigned int opnote = SLAPI_OP_NOTE_SIMPLEPAGED;
-              if (pagedresults_check_or_set_processing(pb->pb_conn)) {
+              if (pagedresults_check_or_set_processing(pb->pb_conn, pr_idx)) {
                   send_ldap_result(pb, LDAP_UNWILLING_TO_PERFORM,
-                                   NULL, "Simple Paged Results Search already in progress on this connection", 0, NULL);
+                                   NULL, "Simple Paged Results Search "
+                                   "already in progress on this connection",
+                                   0, NULL);
                   goto free_and_return_nolock;
               }
-              pr_reset_processing = 1; /* need to reset after we are done with this op */
-              operation->o_flags |= OP_FLAG_PAGED_RESULTS;
-              pr_be = pagedresults_get_current_be(pb->pb_conn);
-              pr_search_result = pagedresults_get_search_result(pb->pb_conn);
+              /* need to reset after we are done with this op */
+              pr_reset_processing = 1;
+              op_set_pagedresults(operation);
+              pr_be = pagedresults_get_current_be(pb->pb_conn, pr_idx);
+              pr_search_result = pagedresults_get_search_result(pb->pb_conn,
+                                                                pr_idx);
               estimate = 
-                 pagedresults_get_search_result_set_size_estimate(pb->pb_conn);
-              if (pagedresults_get_unindexed(pb->pb_conn)) {
+                 pagedresults_get_search_result_set_size_estimate(pb->pb_conn,
+                                                                  pr_idx);
+              if (pagedresults_get_unindexed(pb->pb_conn, pr_idx)) {
                   opnote |= SLAPI_OP_NOTE_UNINDEXED;
               }
               slapi_pblock_set( pb, SLAPI_OPERATION_NOTES, &opnote );
           } else {
               /* parse paged-results-control failed */
               if (iscritical) { /* return an error since it's critical */
+                  send_ldap_result(pb, rc, NULL,
+                                   "Simple Paged Results Search failed",
+                                   0, NULL);
                   goto free_and_return;
               }
           }
@@ -567,7 +578,7 @@ op_shared_search (Slapi_PBlock *pb, int send_result)
           }
           if (slapi_sdn_compare(basesdn, sdn)) {
               slapi_sdn_free(&basesdn);
-			  basesdn = operation_get_target_spec(pb->pb_op);
+              basesdn = operation_get_target_spec(pb->pb_op);
               slapi_sdn_free(&basesdn);
               basesdn = slapi_sdn_dup(sdn);
               operation_set_target_spec (pb->pb_op, basesdn);
@@ -601,13 +612,13 @@ op_shared_search (Slapi_PBlock *pb, int send_result)
   }
 
   /* set the timelimit to clean up the too-long-lived-paged results requests */
-  if (operation->o_flags & OP_FLAG_PAGED_RESULTS) {
+  if (op_is_pagedresults(operation)) {
     time_t optime, time_up;
     int tlimit;
     slapi_pblock_get( pb, SLAPI_SEARCH_TIMELIMIT, &tlimit );
     slapi_pblock_get( pb, SLAPI_OPINITIATED_TIME, &optime );
     time_up = (tlimit==-1 ? -1 : optime + tlimit); /* -1: no time limit */
-    pagedresults_set_timelimit(pb->pb_conn, time_up);
+    pagedresults_set_timelimit(pb->pb_conn, time_up, pr_idx);
   }
 
   /* PAR: now filters have been rewritten, we can assign plugins to work on them */
@@ -658,7 +669,7 @@ op_shared_search (Slapi_PBlock *pb, int send_result)
         next_be = NULL;
     }
         
-    if ((operation->o_flags & OP_FLAG_PAGED_RESULTS) && pr_search_result) {
+    if (op_is_pagedresults(operation) && pr_search_result) {
       void *sr = NULL;
       /* PAGED RESULTS and already have the search results from the prev op */
       slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_SET, pr_search_result );
@@ -666,7 +677,7 @@ op_shared_search (Slapi_PBlock *pb, int send_result)
 
       /* search result could be reset in the backend/dse */
       slapi_pblock_get(pb, SLAPI_SEARCH_RESULT_SET, &sr);
-      pagedresults_set_search_result(pb->pb_conn, sr, 0);
+      pagedresults_set_search_result(pb->pb_conn, sr, 0, pr_idx);
 
       if (PAGEDRESULTS_SEARCH_END == pr_stat) {
         /* no more entries to send in the backend */
@@ -676,7 +687,7 @@ op_shared_search (Slapi_PBlock *pb, int send_result)
         } else {
           curr_search_count = pnentries;
           /* no more entries, but at least another backend */
-          if (pagedresults_set_current_be(pb->pb_conn, next_be) < 0) {
+          if (pagedresults_set_current_be(pb->pb_conn, next_be, pr_idx) < 0) {
               goto free_and_return;
           }
         }
@@ -685,14 +696,16 @@ op_shared_search (Slapi_PBlock *pb, int send_result)
         curr_search_count = pnentries;
         estimate -= estimate?curr_search_count:0;
       }
-      pagedresults_set_response_control(pb, 0, estimate, curr_search_count);
-      if (pagedresults_get_with_sort(pb->pb_conn)) {
+      pagedresults_set_response_control(pb, 0, estimate,
+                                        curr_search_count, pr_idx);
+      if (pagedresults_get_with_sort(pb->pb_conn, pr_idx)) {
         sort_make_sort_response_control(pb, CONN_GET_SORT_RESULT_CODE, NULL);
       }
-      pagedresults_set_search_result_set_size_estimate(pb->pb_conn, estimate);
+      pagedresults_set_search_result_set_size_estimate(pb->pb_conn, 
+                                                       estimate, pr_idx);
       next_be = NULL; /* to break the loop */
       if (curr_search_count == -1) {
-        pagedresults_cleanup(pb->pb_conn, 1 /* need to lock */);
+        pagedresults_free_one(pb->pb_conn, pr_idx);
       }
     } else {
       /* be_suffix null means that we are searching the default backend
@@ -803,7 +816,7 @@ op_shared_search (Slapi_PBlock *pb, int send_result)
         rc = send_results_ext (pb, 1, &pnentries, pagesize, &pr_stat);
   
         /* PAGED RESULTS */
-        if (operation->o_flags & OP_FLAG_PAGED_RESULTS) {
+        if (op_is_pagedresults(operation)) {
             void *sr = NULL;
             int with_sort = operation->o_flags & OP_FLAG_SERVER_SIDE_SORTING;
   
@@ -814,7 +827,7 @@ op_shared_search (Slapi_PBlock *pb, int send_result)
                 curr_search_count = -1;
               } else {
                 /* no more entries, but at least another backend */
-                if (pagedresults_set_current_be(pb->pb_conn, next_be) < 0) {
+                if (pagedresults_set_current_be(pb->pb_conn, next_be, pr_idx) < 0) {
                   goto free_and_return;
                 }
               }
@@ -822,22 +835,22 @@ op_shared_search (Slapi_PBlock *pb, int send_result)
               curr_search_count = pnentries;
               slapi_pblock_get(pb, SLAPI_SEARCH_RESULT_SET, &sr);
               slapi_pblock_get(pb, SLAPI_SEARCH_RESULT_SET_SIZE_ESTIMATE, &estimate);
-              if (pagedresults_set_current_be(pb->pb_conn, be) < 0 ||
-                  pagedresults_set_search_result(pb->pb_conn, sr, 0) < 0 ||
+              if (pagedresults_set_current_be(pb->pb_conn, be, pr_idx) < 0 ||
+                  pagedresults_set_search_result(pb->pb_conn, sr, 0, pr_idx) < 0 ||
                   pagedresults_set_search_result_count(pb->pb_conn,
-                                                   curr_search_count) < 0 ||
+                                                   curr_search_count, pr_idx) < 0 ||
                   pagedresults_set_search_result_set_size_estimate(pb->pb_conn,
-                                                   estimate) < 0 ||
-                  pagedresults_set_with_sort(pb->pb_conn, with_sort) < 0) {
+                                                   estimate, pr_idx) < 0 ||
+                  pagedresults_set_with_sort(pb->pb_conn, with_sort, pr_idx) < 0) {
                 goto free_and_return;
               }
             }
-            pagedresults_set_response_control(pb, 0,
-                                              estimate, curr_search_count);
+            pagedresults_set_response_control(pb, 0, estimate, 
+                                              curr_search_count, pr_idx);
             slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_SET, NULL );
             next_be = NULL; /* to break the loop */
             if (curr_search_count == -1) {
-                pagedresults_cleanup(pb->pb_conn, 1 /* need to lock */);
+                pagedresults_free_one(pb->pb_conn, pr_idx);
             }
         }
   
@@ -965,7 +978,7 @@ free_and_return_nolock:
   slapi_ch_free_string(&proxydn);
   slapi_ch_free_string(&proxystr);
   if (pr_reset_processing) {
-    pagedresults_reset_processing(pb->pb_conn);
+    pagedresults_reset_processing(pb->pb_conn, pr_idx);
   }
 }
 
@@ -1204,12 +1217,14 @@ iterate(Slapi_PBlock *pb, Slapi_Backend *be, int send_result,
     Slapi_Entry *e = NULL;
     char **attrs = NULL;
     unsigned int pr_stat = 0;
+    int pr_idx = -1;
 
     if ( NULL == pb ) {
         return rval;
     }
     slapi_pblock_get(pb, SLAPI_SEARCH_ATTRS, &attrs);
     slapi_pblock_get(pb, SLAPI_SEARCH_ATTRSONLY, &attrsonly);
+    slapi_pblock_get(pb, SLAPI_PAGED_RESULTS_INDEX, &pr_idx);
 
     *pnentries = 0;
     
@@ -1231,7 +1246,7 @@ iterate(Slapi_PBlock *pb, Slapi_Backend *be, int send_result,
                 operation_out_of_disk_space();
             }
             pr_stat = PAGEDRESULTS_SEARCH_END;
-            pagedresults_set_timelimit(pb->pb_conn, 0);
+            pagedresults_set_timelimit(pb->pb_conn, 0, pr_idx);
             rval = -1;
             done = 1;
             continue;
@@ -1542,7 +1557,7 @@ compute_limits (Slapi_PBlock *pb)
         }
     }
 
-    if (op && (op->o_flags & OP_FLAG_PAGED_RESULTS)) {
+    if (op_is_pagedresults(op)) {
         if ( slapi_reslimit_get_integer_limit( pb->pb_conn,
                 pagedsizelimit_reslimit_handle, &max_sizelimit )
                 != SLAPI_RESLIMIT_STATUS_SUCCESS ) {
@@ -1633,7 +1648,7 @@ send_results_ext(Slapi_PBlock *pb, int send_result, int *nentries, int pagesize,
                      */
                     break;    
 
-        case 1:        /* everything is ok - don't send the result */
+        case 1:     /* everything is ok - don't send the result */
                     rc = 0;
     }
     

+ 449 - 79
ldap/servers/slapd/pagedresults.c

@@ -50,20 +50,29 @@
  *   -- requested page size from client
  *   -- result set size estimate from server
  *   cookie OCTET STRING
+ *   -- index for the pagedresults array in the connection
  *   }
  *
  * Return an LDAP error code (LDAP_SUCCESS if all goes well).
  */
 int
-pagedresults_parse_control_value( struct berval *psbvp,
-                                  ber_int_t *pagesize, int *curr_search_count )
+pagedresults_parse_control_value( Slapi_PBlock *pb,
+                                  struct berval *psbvp, ber_int_t *pagesize,
+                                  int *index )
 {
     int rc = LDAP_SUCCESS;
     struct berval cookie = {0};
+    Connection *conn = pb->pb_conn;
+    Operation *op = pb->pb_op;
 
-    if ( NULL == pagesize || NULL == curr_search_count ) {
+    LDAPDebug0Args(LDAP_DEBUG_TRACE, "--> pagedresults_parse_control_value\n");
+    if ( NULL == conn || NULL == op || NULL == pagesize || NULL == index ) {
+        LDAPDebug1Arg(LDAP_DEBUG_ANY,
+                      "<-- pagedresults_parse_control_value: %d\n",
+                      LDAP_OPERATIONS_ERROR);
         return LDAP_OPERATIONS_ERROR;
     }
+    *index = -1;
 
     if ( psbvp->bv_len == 0 || psbvp->bv_val == NULL )
     {
@@ -85,19 +94,64 @@ pagedresults_parse_control_value( struct berval *psbvp,
             /* the ber encoding is no longer needed */
             ber_free(ber, 1);
             if ( cookie.bv_len <= 0 ) {
-                *curr_search_count = 0;
+                int i;
+                int maxlen;
+                /* first time? */
+                PR_Lock(conn->c_mutex);
+                maxlen = conn->c_pagedresults.prl_maxlen;
+                if (conn->c_pagedresults.prl_count == maxlen) {
+                    if (0 == maxlen) { /* first time */
+                        conn->c_pagedresults.prl_maxlen = 1;
+                        conn->c_pagedresults.prl_list =
+                            (PagedResults *)slapi_ch_calloc(1,
+                                                        sizeof(PagedResults));
+                    } else {
+                        /* new max length */
+                        conn->c_pagedresults.prl_maxlen *= 2;
+                        conn->c_pagedresults.prl_list =
+                            (PagedResults *)slapi_ch_realloc(
+                                        (char *)conn->c_pagedresults.prl_list,
+                                        sizeof(PagedResults) *
+                                        conn->c_pagedresults.prl_maxlen);
+                        /* initialze newly allocated area */
+                        memset(conn->c_pagedresults.prl_list + maxlen, '\0',
+                                   sizeof(PagedResults) * maxlen);
+                    }
+                    *index = maxlen; /* the first position in the new area */
+                } else {
+                    for (i = 0; i < conn->c_pagedresults.prl_maxlen; i++) {
+                        if (!conn->c_pagedresults.prl_list[i].pr_current_be) {
+                            *index = i;
+                            break;
+                        }
+                    }
+                }
+                conn->c_pagedresults.prl_count++;
+                PR_Unlock(conn->c_mutex);
             } else {
-                /* not an error */
+                /* Repeated paged results request.
+                 * PagedResults is already allocated. */
                 char *ptr = slapi_ch_malloc(cookie.bv_len + 1);
                 memcpy(ptr, cookie.bv_val, cookie.bv_len);
                 *(ptr+cookie.bv_len) = '\0';
-                *curr_search_count = strtol(ptr, NULL, 10);
+                *index = strtol(ptr, NULL, 10);
                 slapi_ch_free_string(&ptr);
             }
             slapi_ch_free((void **)&cookie.bv_val);
         }
     }
+    if ((*index > -1) && (*index < conn->c_pagedresults.prl_maxlen)) {
+        /* Need to keep the latest msgid to prepare for the abandon. */
+        conn->c_pagedresults.prl_list[*index].pr_msgid = op->o_msgid;
+    } else {
+        rc = LDAP_PROTOCOL_ERROR;
+        LDAPDebug1Arg(LDAP_DEBUG_ANY,
+                      "pagedresults_parse_control_value: invalid cookie: %d\n",
+                      *index);
+    }
 
+    LDAPDebug1Arg(LDAP_DEBUG_TRACE,
+                  "<-- pagedresults_parse_control_value: idx %d\n", *index);
     return rc;
 }
 
@@ -114,7 +168,8 @@ pagedresults_parse_control_value( struct berval *psbvp,
  */
 void
 pagedresults_set_response_control( Slapi_PBlock *pb, int iscritical,
-                                   ber_int_t estimate, int curr_search_count )
+                                   ber_int_t estimate, int current_search_count,
+                                   int index )
 {
     LDAPControl **resultctrls = NULL;
     LDAPControl pr_respctrl;
@@ -124,16 +179,19 @@ pagedresults_set_response_control( Slapi_PBlock *pb, int iscritical,
     int found = 0;
     int i;
 
+    LDAPDebug1Arg(LDAP_DEBUG_TRACE,
+                  "--> pagedresults_set_response_control: idx=%d\n", index);
+
     if ( (ber = der_alloc()) == NULL )
     {
         goto bailout;
     }
 
     /* begin sequence, payload, end sequence */
-    if (curr_search_count < 0) {
+    if (current_search_count < 0) {
         cookie_str = slapi_ch_strdup("");
     } else {
-        cookie_str = slapi_ch_smprintf("%d", curr_search_count);
+        cookie_str = slapi_ch_smprintf("%d", index);
     }
     ber_printf ( ber, "{io}", estimate, cookie_str, strlen(cookie_str) );
     if ( ber_flatten ( ber, &berval ) != LDAP_SUCCESS )
@@ -169,202 +227,362 @@ pagedresults_set_response_control( Slapi_PBlock *pb, int iscritical,
 
 bailout:
     slapi_ch_free_string(&cookie_str);
-    ber_free ( ber, 1 );    /* ber_free() checks for NULL param */
+    ber_free ( ber, 1 );      /* ber_free() checks for NULL param */
     ber_bvfree ( berval );    /* ber_bvfree() checks for NULL param */
+
+    LDAPDebug1Arg(LDAP_DEBUG_TRACE,
+                  "<-- pagedresults_set_response_control: idx=%d\n", index);
+}
+
+int 
+pagedresults_free_one( Connection *conn, int index )
+{
+    int rc = -1;
+
+    LDAPDebug1Arg(LDAP_DEBUG_TRACE,
+                  "--> pagedresults_free_one: idx=%d\n", index);
+    if (conn && (index > -1)) {
+        PR_Lock(conn->c_mutex);
+        if (conn->c_pagedresults.prl_count <= 0) {
+            LDAPDebug2Args(LDAP_DEBUG_TRACE, "pagedresults_free_one: "
+                           "conn=%d paged requests list count is %d\n",
+                           conn->c_connid, conn->c_pagedresults.prl_count);
+        } else if (index < conn->c_pagedresults.prl_maxlen) {
+            memset(&conn->c_pagedresults.prl_list[index],
+                   '\0', sizeof(PagedResults));
+            conn->c_pagedresults.prl_count--;
+            rc = 0;
+        }
+        PR_Unlock(conn->c_mutex);
+    }
+
+    LDAPDebug1Arg(LDAP_DEBUG_TRACE, "<-- pagedresults_free_one: %d\n", rc);
+    return rc;
+}
+
+int 
+pagedresults_free_one_msgid( Connection *conn, ber_int_t msgid )
+{
+    int rc = -1;
+    int i;
+
+    LDAPDebug1Arg(LDAP_DEBUG_TRACE,
+                  "--> pagedresults_free_one: idx=%d\n", index);
+    if (conn && (index > -1)) {
+        PR_Lock(conn->c_mutex);
+        if (conn->c_pagedresults.prl_count <= 0) {
+            LDAPDebug2Args(LDAP_DEBUG_TRACE, "pagedresults_free_one_msgid: "
+                           "conn=%d paged requests list count is %d\n",
+                           conn->c_connid, conn->c_pagedresults.prl_count);
+        } else {
+            for (i = 0; i < conn->c_pagedresults.prl_maxlen; i++) {
+                if (conn->c_pagedresults.prl_list[i].pr_msgid == msgid) {
+                    memset(&conn->c_pagedresults.prl_list[i],
+                           '\0', sizeof(PagedResults));
+                    conn->c_pagedresults.prl_count--;
+                    rc = 0;
+                    break;
+                }
+            }
+        }
+        PR_Unlock(conn->c_mutex);
+    }
+
+    LDAPDebug1Arg(LDAP_DEBUG_TRACE, "<-- pagedresults_free_one: %d\n", rc);
+    return rc;
 }
 
 /* setters and getters for the connection */
 Slapi_Backend *
-pagedresults_get_current_be(Connection *conn)
+pagedresults_get_current_be(Connection *conn, int index)
 {
     Slapi_Backend *be = NULL;
-    if (conn) {
+    LDAPDebug1Arg(LDAP_DEBUG_TRACE,
+                  "--> pagedresults_get_current_be: idx=%d\n", index);
+    if (conn && (index > -1)) {
         PR_Lock(conn->c_mutex);
-        be = conn->c_current_be;
+        if (index < conn->c_pagedresults.prl_maxlen) {
+            be = conn->c_pagedresults.prl_list[index].pr_current_be;
+        }
         PR_Unlock(conn->c_mutex);
     }
+    LDAPDebug1Arg(LDAP_DEBUG_TRACE,
+                  "<-- pagedresults_get_current_be: %p\n", be);
     return be;
 }
 
 int
-pagedresults_set_current_be(Connection *conn, Slapi_Backend *be)
+pagedresults_set_current_be(Connection *conn, Slapi_Backend *be, int index)
 {
     int rc = -1;
-    if (conn) {
+    LDAPDebug1Arg(LDAP_DEBUG_TRACE,
+                  "--> pagedresults_set_current_be: idx=%d\n", index);
+    if (conn && (index > -1)) {
         PR_Lock(conn->c_mutex);
-        conn->c_current_be = be;
+        if (index < conn->c_pagedresults.prl_maxlen) {
+            conn->c_pagedresults.prl_list[index].pr_current_be = be;
+        }
         PR_Unlock(conn->c_mutex);
         rc = 0;
     }
+    LDAPDebug1Arg(LDAP_DEBUG_TRACE,
+                  "<-- pagedresults_set_current_be: %d\n", rc);
     return rc;
 }
 
 void *
-pagedresults_get_search_result(Connection *conn)
+pagedresults_get_search_result(Connection *conn, int index)
 {
     void *sr = NULL;
-    if (conn) {
+    LDAPDebug1Arg(LDAP_DEBUG_TRACE,
+                  "--> pagedresults_get_search_result: idx=%d\n", index);
+    if (conn && (index > -1)) {
         PR_Lock(conn->c_mutex);
-        sr = conn->c_search_result_set;
+        if (index < conn->c_pagedresults.prl_maxlen) {
+            sr = conn->c_pagedresults.prl_list[index].pr_search_result_set;
+        }
         PR_Unlock(conn->c_mutex);
     }
+    LDAPDebug1Arg(LDAP_DEBUG_TRACE,
+                  "<-- pagedresults_get_search_result: %p\n", sr);
     return sr;
 }
 
 int
-pagedresults_set_search_result(Connection *conn, void *sr, int locked)
+pagedresults_set_search_result(Connection *conn, void *sr, 
+                               int locked, int index)
 {
     int rc = -1;
-    if (conn) {
+    LDAPDebug2Args(LDAP_DEBUG_TRACE,
+                   "--> pagedresults_set_search_result: idx=%d, sr=%p\n",
+                   index, sr);
+    if (conn && (index > -1)) {
         if (!locked) PR_Lock(conn->c_mutex);
-        conn->c_search_result_set = sr;
+        if (index < conn->c_pagedresults.prl_maxlen) {
+            conn->c_pagedresults.prl_list[index].pr_search_result_set = sr;
+        }
         if (!locked) PR_Unlock(conn->c_mutex);
         rc = 0;
     }
+    LDAPDebug1Arg(LDAP_DEBUG_TRACE,
+                  "<-- pagedresults_set_search_result: %d\n", rc);
     return rc;
 }
 
 int
-pagedresults_get_search_result_count(Connection *conn)
+pagedresults_get_search_result_count(Connection *conn, int index)
 {
     int count = 0;
-    if (conn) {
+    LDAPDebug1Arg(LDAP_DEBUG_TRACE,
+                  "--> pagedresults_get_search_result_count: idx=%d\n", index);
+    if (conn && (index > -1)) {
         PR_Lock(conn->c_mutex);
-        count = conn->c_search_result_count;
+        if (index < conn->c_pagedresults.prl_maxlen) {
+            count = conn->c_pagedresults.prl_list[index].pr_search_result_count;
+        }
         PR_Unlock(conn->c_mutex);
     }
+    LDAPDebug1Arg(LDAP_DEBUG_TRACE,
+                  "<-- pagedresults_get_search_result_count: %d\n", count);
     return count;
 }
 
 int
-pagedresults_set_search_result_count(Connection *conn, int count)
+pagedresults_set_search_result_count(Connection *conn, int count, int index)
 {
     int rc = -1;
-    if (conn) {
+    LDAPDebug1Arg(LDAP_DEBUG_TRACE,
+                  "--> pagedresults_set_search_result_count: idx=%d\n", index);
+    if (conn && (index > -1)) {
         PR_Lock(conn->c_mutex);
-        conn->c_search_result_count = count;
+        if (index < conn->c_pagedresults.prl_maxlen) {
+            conn->c_pagedresults.prl_list[index].pr_search_result_count = count;
+        }
         PR_Unlock(conn->c_mutex);
         rc = 0;
     }
+    LDAPDebug1Arg(LDAP_DEBUG_TRACE,
+                  "<-- pagedresults_set_search_result_count: %d\n", rc);
     return rc;
 }
 
 int
-pagedresults_get_search_result_set_size_estimate(Connection *conn)
+pagedresults_get_search_result_set_size_estimate(Connection *conn, int index)
 {
     int count = 0;
-    if (conn) {
+    LDAPDebug1Arg(LDAP_DEBUG_TRACE,
+                  "--> pagedresults_get_search_result_set_size_estimate: "
+                  "idx=%d\n", index);
+    if (conn && (index > -1)) {
         PR_Lock(conn->c_mutex);
-        count = conn->c_search_result_set_size_estimate;
+        if (index < conn->c_pagedresults.prl_maxlen) {
+            count = conn->c_pagedresults.prl_list[index].pr_search_result_set_size_estimate;
+        }
         PR_Unlock(conn->c_mutex);
     }
+    LDAPDebug1Arg(LDAP_DEBUG_TRACE,
+                  "<-- pagedresults_get_search_result_set_size_estimate: %d\n",
+                  count);
     return count;
 }
 
 int
-pagedresults_set_search_result_set_size_estimate(Connection *conn, int count)
+pagedresults_set_search_result_set_size_estimate(Connection *conn, 
+                                                 int count, int index)
 {
     int rc = -1;
-    if (conn) {
+    LDAPDebug1Arg(LDAP_DEBUG_TRACE,
+                  "--> pagedresults_set_search_result_set_size_estimate: "
+                  "idx=%d\n", index);
+    if (conn && (index > -1)) {
         PR_Lock(conn->c_mutex);
-        conn->c_search_result_set_size_estimate = count;
+        if (index < conn->c_pagedresults.prl_maxlen) {
+            conn->c_pagedresults.prl_list[index].pr_search_result_set_size_estimate = count;
+        }
         PR_Unlock(conn->c_mutex);
         rc = 0;
     }
+    LDAPDebug1Arg(LDAP_DEBUG_TRACE,
+                  "<-- pagedresults_set_search_result_set_size_estimate: %d\n",
+                  rc);
     return rc;
 }
 
 int
-pagedresults_get_with_sort(Connection *conn)
+pagedresults_get_with_sort(Connection *conn, int index)
 {
     int flags = 0;
-    if (conn) {
+    LDAPDebug1Arg(LDAP_DEBUG_TRACE,
+                  "--> pagedresults_get_with_sort: idx=%d\n", index);
+    if (conn && (index > -1)) {
         PR_Lock(conn->c_mutex);
-        flags = conn->c_flags&CONN_FLAG_PAGEDRESULTS_WITH_SORT;
+        if (index < conn->c_pagedresults.prl_maxlen) {
+            flags = conn->c_pagedresults.prl_list[index].pr_flags&CONN_FLAG_PAGEDRESULTS_WITH_SORT;
+        }
         PR_Unlock(conn->c_mutex);
     }
+    LDAPDebug1Arg(LDAP_DEBUG_TRACE,
+                  "<-- pagedresults_get_with_sort: %p\n", flags);
     return flags;
 }
 
 int
-pagedresults_set_with_sort(Connection *conn, int flags)
+pagedresults_set_with_sort(Connection *conn, int flags, int index)
 {
     int rc = -1;
-    if (conn) {
+    LDAPDebug1Arg(LDAP_DEBUG_TRACE,
+                  "--> pagedresults_set_with_sort: idx=%d\n", index);
+    if (conn && (index > -1)) {
         PR_Lock(conn->c_mutex);
-        if (flags & OP_FLAG_SERVER_SIDE_SORTING)
-          conn->c_flags |= CONN_FLAG_PAGEDRESULTS_WITH_SORT;
+        if (index < conn->c_pagedresults.prl_maxlen) {
+            if (flags & OP_FLAG_SERVER_SIDE_SORTING) {
+                conn->c_pagedresults.prl_list[index].pr_flags |=
+                                               CONN_FLAG_PAGEDRESULTS_WITH_SORT;
+            }
+        }
         PR_Unlock(conn->c_mutex);
         rc = 0;
     }
+    LDAPDebug1Arg(LDAP_DEBUG_TRACE, "<-- pagedresults_set_with_sort: %d\n", rc);
     return rc;
 }
 
 int
-pagedresults_get_unindexed(Connection *conn)
+pagedresults_get_unindexed(Connection *conn, int index)
 {
     int flags = 0;
-    if (conn) {
+    LDAPDebug1Arg(LDAP_DEBUG_TRACE,
+                  "--> pagedresults_get_unindexed: idx=%d\n", index);
+    if (conn && (index > -1)) {
         PR_Lock(conn->c_mutex);
-        flags = conn->c_flags&CONN_FLAG_PAGEDRESULTS_UNINDEXED;
+        if (index < conn->c_pagedresults.prl_maxlen) {
+            flags = conn->c_pagedresults.prl_list[index].pr_flags&CONN_FLAG_PAGEDRESULTS_UNINDEXED;
+        }
         PR_Unlock(conn->c_mutex);
     }
+    LDAPDebug1Arg(LDAP_DEBUG_TRACE,
+                  "<-- pagedresults_get_unindexed: %p\n", flags);
     return flags;
 }
 
 int
-pagedresults_set_unindexed(Connection *conn)
+pagedresults_set_unindexed(Connection *conn, int index)
 {
     int rc = -1;
-    if (conn) {
+    LDAPDebug1Arg(LDAP_DEBUG_TRACE,
+                  "--> pagedresults_set_unindexed: idx=%d\n", index);
+    if (conn && (index > -1)) {
         PR_Lock(conn->c_mutex);
-        conn->c_flags |= CONN_FLAG_PAGEDRESULTS_UNINDEXED;
+        if (index < conn->c_pagedresults.prl_maxlen) {
+            conn->c_pagedresults.prl_list[index].pr_flags |=
+                                               CONN_FLAG_PAGEDRESULTS_UNINDEXED;
+        }
         PR_Unlock(conn->c_mutex);
         rc = 0;
     }
+    LDAPDebug1Arg(LDAP_DEBUG_TRACE,
+                  "<-- pagedresults_set_unindexed: %d\n", rc);
     return rc;
 }
 
 int
-pagedresults_get_sort_result_code(Connection *conn)
+pagedresults_get_sort_result_code(Connection *conn, int index)
 {
-    int code = 0;
-    if (conn) {
+    int code = LDAP_OPERATIONS_ERROR;
+    LDAPDebug1Arg(LDAP_DEBUG_TRACE,
+                  "--> pagedresults_get_sort_result_code: idx=%d\n", index);
+    if (conn && (index > -1)) {
         PR_Lock(conn->c_mutex);
-        code = conn->c_sort_result_code;
+        if (index < conn->c_pagedresults.prl_maxlen) {
+            code = conn->c_pagedresults.prl_list[index].pr_sort_result_code;
+        }
         PR_Unlock(conn->c_mutex);
     }
+    LDAPDebug1Arg(LDAP_DEBUG_TRACE,
+                  "<-- pagedresults_get_sort_result_code: %d\n", code);
     return code;
 }
 
 int
-pagedresults_set_sort_result_code(Connection *conn, int code)
+pagedresults_set_sort_result_code(Connection *conn, int code, int index)
 {
     int rc = -1;
-    if (conn) {
+    LDAPDebug1Arg(LDAP_DEBUG_TRACE,
+                  "--> pagedresults_set_sort_result_code: idx=%d\n", index);
+    if (conn && (index > -1)) {
         PR_Lock(conn->c_mutex);
-        conn->c_sort_result_code = code;
+        if (index < conn->c_pagedresults.prl_maxlen) {
+            conn->c_pagedresults.prl_list[index].pr_sort_result_code = code;
+        }
         PR_Unlock(conn->c_mutex);
         rc = 0;
     }
+    LDAPDebug1Arg(LDAP_DEBUG_TRACE,
+                  "<-- pagedresults_set_sort_result_code: %d\n", rc);
     return rc;
 }
 
 int
-pagedresults_set_timelimit(Connection *conn, time_t timelimit)
+pagedresults_set_timelimit(Connection *conn, time_t timelimit, int index)
 {
     int rc = -1;
-    if (conn) {
+    LDAPDebug1Arg(LDAP_DEBUG_TRACE,
+                  "--> pagedresults_set_timelimit: idx=%d\n", index);
+    if (conn && (index > -1)) {
         PR_Lock(conn->c_mutex);
-        conn->c_timelimit = timelimit;
+        if (index < conn->c_pagedresults.prl_maxlen) {
+            conn->c_pagedresults.prl_list[index].pr_timelimit = timelimit;
+        }
         PR_Unlock(conn->c_mutex);
         rc = 0;
     }
+    LDAPDebug1Arg(LDAP_DEBUG_TRACE, "<-- pagedresults_set_timelimit: %d\n", rc);
     return rc;
 }
 
 /*
- * must be called with conn->c_mutex held
+ * pagedresults_cleanup cleans up the pagedresults list;
+ * it does not free the list.
  * return values
  * 0: not a simple paged result connection
  * 1: simple paged result and successfully abandoned
@@ -373,26 +591,76 @@ int
 pagedresults_cleanup(Connection *conn, int needlock)
 {
     int rc = 0;
+    int i;
+    PagedResults *prp = NULL;
+
+    LDAPDebug0Args(LDAP_DEBUG_TRACE, "--> pagedresults_cleanup\n");
+
+    if (NULL == conn) {
+        LDAPDebug0Args(LDAP_DEBUG_TRACE, "<-- pagedresults_cleanup: -\n");
+        return 0;
+    }
 
     if (needlock) {
         PR_Lock(conn->c_mutex);
     }
-    if (conn->c_current_be) {
-        if (conn->c_search_result_set) {
-            if (conn->c_current_be->be_search_results_release) {
-                conn->c_current_be->be_search_results_release(&(conn->c_search_result_set));
-            }
-            conn->c_search_result_set = NULL;
+    for (i = 0; conn->c_pagedresults.prl_list &&
+                i < conn->c_pagedresults.prl_maxlen; i++) {
+        prp = conn->c_pagedresults.prl_list + i;
+        if (prp->pr_current_be && prp->pr_search_result_set &&
+            prp->pr_current_be->be_search_results_release) {
+            prp->pr_current_be->be_search_results_release(&(prp->pr_search_result_set));
+            rc = 1;
+        }
+        memset(prp, '\0', sizeof(PagedResults));
+    }
+    conn->c_pagedresults.prl_count = 0;
+    if (needlock) {
+        PR_Unlock(conn->c_mutex);
+    }
+    LDAPDebug1Arg(LDAP_DEBUG_TRACE, "<-- pagedresults_cleanup: %d\n", rc);
+    return rc;
+}
+
+/*
+ * pagedresults_cleanup_all frees the list.
+ * return values
+ * 0: not a simple paged result connection
+ * 1: simple paged result and successfully abandoned
+ */
+int
+pagedresults_cleanup_all(Connection *conn, int needlock)
+{
+    int rc = 0;
+    int i;
+    PagedResults *prp = NULL;
+
+    LDAPDebug0Args(LDAP_DEBUG_TRACE, "--> pagedresults_cleanup_all\n");
+
+    if (NULL == conn) {
+        LDAPDebug0Args(LDAP_DEBUG_TRACE, "<-- pagedresults_cleanup_all: -\n");
+        return 0;
+    }
+
+    if (needlock) {
+        PR_Lock(conn->c_mutex);
+    }
+    for (i = 0; conn->c_pagedresults.prl_list &&
+                i < conn->c_pagedresults.prl_maxlen; i++) {
+        prp = conn->c_pagedresults.prl_list + i;
+        if (prp->pr_current_be && prp->pr_search_result_set &&
+            prp->pr_current_be->be_search_results_release) {
+            prp->pr_current_be->be_search_results_release(&(prp->pr_search_result_set));
+            rc = 1;
         }
-        conn->c_current_be = 0;
-        rc = 1;
     }
-    conn->c_search_result_count = 0;
-    conn->c_timelimit = 0;
-    conn->c_flags &= ~CONN_FLAG_PAGEDRESULTS_PROCESSING;
+    slapi_ch_free((void **)&conn->c_pagedresults.prl_list);
+    conn->c_pagedresults.prl_maxlen = 0;
+    conn->c_pagedresults.prl_count = 0;
     if (needlock) {
         PR_Unlock(conn->c_mutex);
     }
+    LDAPDebug1Arg(LDAP_DEBUG_TRACE, "<-- pagedresults_cleanup_all: %d\n", rc);
     return rc;
 }
 
@@ -402,16 +670,24 @@ pagedresults_cleanup(Connection *conn, int needlock)
  * mark that it is processing, and return False
  */
 int
-pagedresults_check_or_set_processing(Connection *conn)
+pagedresults_check_or_set_processing(Connection *conn, int index)
 {
     int ret = 0;
-    if (conn) {
+    LDAPDebug1Arg(LDAP_DEBUG_TRACE,
+                  "--> pagedresults_check_or_set_processing\n", index);
+    if (conn && (index > -1)) {
         PR_Lock(conn->c_mutex);
-        ret = conn->c_flags&CONN_FLAG_PAGEDRESULTS_PROCESSING;
-        /* if ret is true, the following doesn't do anything */
-        conn->c_flags |= CONN_FLAG_PAGEDRESULTS_PROCESSING;
+        if (index < conn->c_pagedresults.prl_maxlen) {
+            ret = (conn->c_pagedresults.prl_list[index].pr_flags &
+                   CONN_FLAG_PAGEDRESULTS_PROCESSING);
+            /* if ret is true, the following doesn't do anything */
+            conn->c_pagedresults.prl_list[index].pr_flags |=
+                                              CONN_FLAG_PAGEDRESULTS_PROCESSING;
+        }
         PR_Unlock(conn->c_mutex);
     }
+    LDAPDebug1Arg(LDAP_DEBUG_TRACE,
+                  "<-- pagedresults_check_or_set_processing: %d\n", ret);
     return ret;
 }
 
@@ -421,15 +697,109 @@ pagedresults_check_or_set_processing(Connection *conn)
  * False otherwise
  */
 int
-pagedresults_reset_processing(Connection *conn)
+pagedresults_reset_processing(Connection *conn, int index)
 {
     int ret = 0;
-    if (conn) {
+    LDAPDebug1Arg(LDAP_DEBUG_TRACE,
+                  "--> pagedresults_reset_processing: idx=%d\n", index);
+    if (conn && (index > -1)) {
         PR_Lock(conn->c_mutex);
-        ret = conn->c_flags&CONN_FLAG_PAGEDRESULTS_PROCESSING;
-        /* if ret is false, the following doesn't do anything */
-        conn->c_flags &= ~CONN_FLAG_PAGEDRESULTS_PROCESSING;
+        if (index < conn->c_pagedresults.prl_maxlen) {
+            ret = (conn->c_pagedresults.prl_list[index].pr_flags &
+                   CONN_FLAG_PAGEDRESULTS_PROCESSING);
+            /* if ret is false, the following doesn't do anything */
+            conn->c_pagedresults.prl_list[index].pr_flags &=
+                                             ~CONN_FLAG_PAGEDRESULTS_PROCESSING;
+        }
         PR_Unlock(conn->c_mutex);
     }
+    LDAPDebug1Arg(LDAP_DEBUG_TRACE,
+                  "<-- pagedresults_reset_processing: %d\n", ret);
     return ret;
 }
+
+/* Are all the paged results requests timed out? */
+int
+pagedresults_is_timedout(Connection *conn)
+{
+    int i;
+    PagedResults *prp = NULL;
+    time_t ctime;
+    int rc = 0;
+
+    LDAPDebug0Args(LDAP_DEBUG_TRACE, "--> pagedresults_is_timedout\n");
+
+    if (NULL == conn || 0 == conn->c_pagedresults.prl_count) {
+        LDAPDebug0Args(LDAP_DEBUG_TRACE, "<-- pagedresults_is_timedout: -\n");
+        return rc;
+    }
+
+    ctime = current_time();
+    for (i = 0; i < conn->c_pagedresults.prl_maxlen; i++) {
+        prp = conn->c_pagedresults.prl_list + i;
+        if (prp->pr_current_be && (prp->pr_timelimit > 0)) {
+            if (ctime < prp->pr_timelimit) {
+                LDAPDebug0Args(LDAP_DEBUG_TRACE,
+                               "<-- pagedresults_is_timedout: 0\n");
+                return 0; /* at least, one request is not timed out. */
+            } else {
+                rc = 1;   /* possibly timed out */
+            }
+        }
+    }
+    LDAPDebug0Args(LDAP_DEBUG_TRACE, "<-- pagedresults_is_timedout: 1\n");
+    return rc; /* all requests are timed out. */
+}
+
+/* reset all timeout */
+int
+pagedresults_reset_timedout(Connection *conn)
+{
+    int i;
+    PagedResults *prp = NULL;
+
+    LDAPDebug0Args(LDAP_DEBUG_TRACE, "--> pagedresults_reset_timedout\n");
+    if (NULL == conn) {
+        LDAPDebug0Args(LDAP_DEBUG_TRACE, "<-- pagedresults_reset_timedout: -\n");
+        return 0;
+    }
+
+    for (i = 0; i < conn->c_pagedresults.prl_maxlen; i++) {
+        prp = conn->c_pagedresults.prl_list + i;
+        prp->pr_timelimit = 0;
+    }
+    LDAPDebug0Args(LDAP_DEBUG_TRACE, "<-- pagedresults_reset_timedout: 0\n");
+    return 0;
+}
+
+/* paged results requests are in progress. */
+int
+pagedresults_in_use(Connection *conn)
+{
+    LDAPDebug0Args(LDAP_DEBUG_TRACE, "--> pagedresults_in_use\n");
+    if (NULL == conn) {
+        LDAPDebug0Args(LDAP_DEBUG_TRACE, "<-- pagedresults_in_use: -\n");
+        return 0;
+    }
+    LDAPDebug1Arg(LDAP_DEBUG_TRACE, "<-- pagedresults_in_use: %d\n",
+                  conn->c_pagedresults.prl_count);
+    return conn->c_pagedresults.prl_count;
+}
+
+int
+op_is_pagedresults(Operation *op)
+{
+    if (NULL == op) {
+        return 0;
+    }
+    return op->o_flags & OP_FLAG_PAGED_RESULTS;
+}
+
+void
+op_set_pagedresults(Operation *op)
+{
+    if (NULL == op) {
+        return;
+    }
+    op->o_flags |= OP_FLAG_PAGED_RESULTS;
+}

+ 12 - 0
ldap/servers/slapd/pblock.c

@@ -1925,6 +1925,14 @@ slapi_pblock_get( Slapi_PBlock *pblock, int arg, void *value )
 		(*(void **)value) = pblock->pb_syntax_filter_data;
 		break;
 		
+	case SLAPI_PAGED_RESULTS_INDEX:
+		if (op_is_pagedresults(pblock->pb_op)) {
+			/* search req is simple paged results */
+			(*(int *)value) = pblock->pb_paged_results_index;
+		} else {
+			(*(int *)value) = -1;
+		}
+		break;
 	default:
 		LDAPDebug( LDAP_DEBUG_ANY,
 		    "Unknown parameter block argument %d\n", arg, 0, 0 );
@@ -3459,6 +3467,10 @@ slapi_pblock_set( Slapi_PBlock *pblock, int arg, void *value )
 		pblock->pb_syntax_filter_data = (void *)value;
 		break;
 
+	case SLAPI_PAGED_RESULTS_INDEX:
+		pblock->pb_paged_results_index = *(int *)value;
+		break;
+
 	default:
 		LDAPDebug( LDAP_DEBUG_ANY,
 		    "Unknown parameter block argument %d\n", arg, 0, 0 );

+ 33 - 19
ldap/servers/slapd/proto-slap.h

@@ -1378,26 +1378,40 @@ int slapd_do_all_nss_ssl_init(int slapd_exemode, int importexport_encrypt,
 /*
  * pagedresults.c
  */
-int pagedresults_parse_control_value(struct berval *psbvp, ber_int_t *pagesize, int *curr_search_count);
-void pagedresults_set_response_control(Slapi_PBlock *pb, int iscritical, ber_int_t estimate, int curr_search_count);
-Slapi_Backend *pagedresults_get_current_be(Connection *conn);
-int pagedresults_set_current_be(Connection *conn, Slapi_Backend *be);
-void *pagedresults_get_search_result(Connection *conn);
-int pagedresults_set_search_result(Connection *conn, void *sr, int locked);
-int pagedresults_get_search_result_count(Connection *conn);
-int pagedresults_set_search_result_count(Connection *conn, int cnt);
-int pagedresults_get_search_result_set_size_estimate(Connection *conn);
-int pagedresults_set_search_result_set_size_estimate(Connection *conn, int cnt);
-int pagedresults_get_with_sort(Connection *conn);
-int pagedresults_set_with_sort(Connection *conn, int flags);
-int pagedresults_get_unindexed(Connection *conn);
-int pagedresults_set_unindexed(Connection *conn);
-int pagedresults_get_sort_result_code(Connection *conn);
-int pagedresults_set_sort_result_code(Connection *conn, int code);
-int pagedresults_set_timelimit(Connection *conn, time_t timelimit);
+int pagedresults_parse_control_value(Slapi_PBlock *pb, struct berval *psbvp,
+                                     ber_int_t *pagesize, int *index);
+void pagedresults_set_response_control(Slapi_PBlock *pb, int iscritical, 
+                                       ber_int_t estimate,
+                                       int curr_search_count, int index);
+Slapi_Backend *pagedresults_get_current_be(Connection *conn, int index);
+int pagedresults_set_current_be(Connection *conn, Slapi_Backend *be, int index);
+void *pagedresults_get_search_result(Connection *conn, int index);
+int pagedresults_set_search_result(Connection *conn, void *sr, 
+                                   int locked, int index);
+int pagedresults_get_search_result_count(Connection *conn, int index);
+int pagedresults_set_search_result_count(Connection *conn, int cnt, int index);
+int pagedresults_get_search_result_set_size_estimate(Connection *conn, 
+                                                     int index);
+int pagedresults_set_search_result_set_size_estimate(Connection *conn, int cnt, 
+                                                     int index);
+int pagedresults_get_with_sort(Connection *conn, int index);
+int pagedresults_set_with_sort(Connection *conn, int flags, int index);
+int pagedresults_get_unindexed(Connection *conn, int index);
+int pagedresults_set_unindexed(Connection *conn, int index);
+int pagedresults_get_sort_result_code(Connection *conn, int index);
+int pagedresults_set_sort_result_code(Connection *conn, int code, int index);
+int pagedresults_set_timelimit(Connection *conn, time_t timelimit, int index);
 int pagedresults_cleanup(Connection *conn, int needlock);
-int pagedresults_check_or_set_processing(Connection *conn);
-int pagedresults_reset_processing(Connection *conn);
+int pagedresults_check_or_set_processing(Connection *conn, int index);
+int pagedresults_reset_processing(Connection *conn, int index);
+int pagedresults_is_timedout(Connection *conn);
+int pagedresults_reset_timedout(Connection *conn);
+int pagedresults_in_use(Connection *conn);
+int pagedresults_free_one(Connection *conn, int index);
+int op_is_pagedresults(Operation *op);
+int pagedresults_cleanup_all(Connection *conn, int needlock);
+void op_set_pagedresults(Operation *op);
+
 
 /*
  * sort.c

+ 1 - 2
ldap/servers/slapd/result.c

@@ -1426,8 +1426,7 @@ send_ldap_search_entry_ext(
 	ber = NULL; /* flush_ber will always free the ber */
 
 log_and_return:
-	if ( logit && operation_is_flag_set(operation,
-		OP_FLAG_ACTION_LOG_ACCESS)){
+	if ( logit && operation_is_flag_set(operation, OP_FLAG_ACTION_LOG_ACCESS)) {
 
 	    log_entry( op, e );
 

+ 22 - 8
ldap/servers/slapd/slap.h

@@ -1351,6 +1351,26 @@ typedef struct op {
 												client (or we tried to do
 												so and failed) */
 
+
+/* simple paged structure */
+typedef struct _paged_results {
+    Slapi_Backend *pr_current_be;         /* backend being used */
+    void          *pr_search_result_set;  /* search result set for paging */
+    int           pr_search_result_count; /* search result count */
+    int           pr_search_result_set_size_estimate; /* estimated search result set size */
+    int           pr_sort_result_code;    /* sort result put in response */
+    time_t        pr_timelimit;           /* time limit for this request */
+    int           pr_flags;
+    ber_int_t     pr_msgid;               /* msgid of the request; to abandon */
+} PagedResults;
+
+/* array of simple paged structure stashed in connection */
+typedef struct _paged_results_list {
+    int prl_maxlen;          /* size of the PagedResults array */ 
+    int prl_count;           /* count of the list in use */ 
+    PagedResults *prl_list;  /* pointer to pr_maxlen length PageResults array */
+} PagedResultsList;
+
 /*
  * represents a connection from an ldap client
  */
@@ -1406,14 +1426,7 @@ typedef struct conn {
     int				c_local_valid; /* flag true if the uid/gid are valid */
     uid_t			c_local_uid;  /* uid of connecting process */
     gid_t			c_local_gid;  /* gid of connecting process */
-    /* PAGED_RESULTS */
-    Slapi_Backend   *c_current_be;         /* backend being used */
-    void            *c_search_result_set;  /* search result set for paging */
-    int             c_search_result_count; /* search result count */
-    int             c_search_result_set_size_estimate; /* estimated search result set size */
-    int             c_sort_result_code;    /* sort result put in response */
-    time_t          c_timelimit;           /* time limit for this connection */
-    /* PAGED_RESULTS ENDS */
+    PagedResultsList c_pagedresults; /* PAGED_RESULTS */
     /* IO layer push/pop */
     Conn_IO_Layer_cb c_push_io_layer_cb; /* callback to push an IO layer on the conn->c_prfd */
     Conn_IO_Layer_cb c_pop_io_layer_cb; /* callback to pop an IO layer off of the conn->c_prfd */
@@ -1637,6 +1650,7 @@ typedef struct slapi_pblock {
 	IFP		pb_mr_index_sv_fn; /* values and keys are Slapi_Value ** */
 	int		pb_syntax_filter_normalized; /* the syntax filter types/values are already normalized */
 	void		*pb_syntax_filter_data; /* extra data to pass to a syntax plugin function */
+	int	pb_paged_results_index;    /* stash SLAPI_PAGED_RESULTS_INDEX */
 } slapi_pblock;
 
 /* index if substrlens */

+ 3 - 0
ldap/servers/slapd/slapi-plugin.h

@@ -6674,6 +6674,9 @@ typedef struct slapi_plugindesc {
 /* Size of the database, in kilobytes */
 #define SLAPI_DBSIZE				199
 
+/* Simple paged results index */
+#define SLAPI_PAGED_RESULTS_INDEX   1945
+
 /* convenience macros for checking modify operation types */
 #define SLAPI_IS_MOD_ADD(x) (((x) & ~LDAP_MOD_BVALUES) == LDAP_MOD_ADD)
 #define SLAPI_IS_MOD_DELETE(x) (((x) & ~LDAP_MOD_BVALUES) == LDAP_MOD_DELETE)

+ 6 - 3
ldap/servers/slapd/sort.c

@@ -53,14 +53,17 @@ sort_make_sort_response_control ( Slapi_PBlock *pb, int code, char *error_type)
     struct berval   *bvp = NULL;
     int             rc = -1;
     ber_int_t       control_code;
+    int             pr_idx = -1;
+
+    slapi_pblock_get(pb, SLAPI_PAGED_RESULTS_INDEX, &pr_idx);
 
     if (code == CONN_GET_SORT_RESULT_CODE) {
-        code = pagedresults_get_sort_result_code(pb->pb_conn);
+        code = pagedresults_get_sort_result_code(pb->pb_conn, pr_idx);
     } else {
         Slapi_Operation *operation;
         slapi_pblock_get (pb, SLAPI_OPERATION, &operation);
-        if (operation->o_flags & OP_FLAG_PAGED_RESULTS) {
-            pagedresults_set_sort_result_code(pb->pb_conn, code);
+        if (op_is_pagedresults(operation)) {
+            pagedresults_set_sort_result_code(pb->pb_conn, code, pr_idx);
         }
     }