xref: /netbsd-src/external/bsd/openldap/dist/servers/slapd/overlays/sssvlv.c (revision 413d532bcc3f62d122e56d92e13ac64825a40baf)
1 /*	$NetBSD: sssvlv.c,v 1.1.1.2 2010/12/12 15:23:44 adam Exp $	*/
2 
3 /* sssvlv.c - server side sort / virtual list view */
4 /* OpenLDAP: pkg/ldap/servers/slapd/overlays/sssvlv.c,v 1.9.2.9 2010/06/10 17:37:40 quanah Exp */
5 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
6  *
7  * Copyright 2009-2010 The OpenLDAP Foundation.
8  * Portions copyright 2009 Symas Corporation.
9  * All rights reserved.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted only as authorized by the OpenLDAP
13  * Public License.
14  *
15  * A copy of this license is available in the file LICENSE in the
16  * top-level directory of the distribution or, alternatively, at
17  * <http://www.OpenLDAP.org/license.html>.
18  */
19 /* ACKNOWLEDGEMENTS:
20  * This work was initially developed by Howard Chu for inclusion in
21  * OpenLDAP Software.
22  */
23 
24 #include "portable.h"
25 
26 #ifdef SLAPD_OVER_SSSVLV
27 
28 #include <stdio.h>
29 
30 #include <ac/string.h>
31 #include <ac/ctype.h>
32 
33 #include <avl.h>
34 
35 #include "slap.h"
36 #include "lutil.h"
37 #include "config.h"
38 
39 #include "../../../libraries/liblber/lber-int.h"	/* ber_rewind */
40 
41 /* RFC2891: Server Side Sorting
42  * RFC2696: Paged Results
43  */
44 #ifndef LDAP_MATCHRULE_IDENTIFIER
45 #define LDAP_MATCHRULE_IDENTIFIER      0x80L
46 #define LDAP_REVERSEORDER_IDENTIFIER   0x81L
47 #define LDAP_ATTRTYPES_IDENTIFIER      0x80L
48 #endif
49 
50 /* draft-ietf-ldapext-ldapv3-vlv-09.txt: Virtual List Views
51  */
52 #ifndef LDAP_VLVBYINDEX_IDENTIFIER
53 #define LDAP_VLVBYINDEX_IDENTIFIER	   0xa0L
54 #define LDAP_VLVBYVALUE_IDENTIFIER     0x81L
55 #define LDAP_VLVCONTEXT_IDENTIFIER     0x04L
56 
57 #define LDAP_VLV_SSS_MISSING	0x4C
58 #define LDAP_VLV_RANGE_ERROR	0x4D
59 #endif
60 
61 #define SAFESTR(macro_str, macro_def) ((macro_str) ? (macro_str) : (macro_def))
62 
63 #define SSSVLV_DEFAULT_MAX_KEYS	5
64 
65 typedef struct vlv_ctrl {
66 	int vc_before;
67 	int vc_after;
68 	int	vc_offset;
69 	int vc_count;
70 	struct berval vc_value;
71 	unsigned long vc_context;
72 } vlv_ctrl;
73 
74 typedef struct sort_key
75 {
76 	AttributeDescription	*sk_ad;
77 	MatchingRule			*sk_ordering;
78 	int						sk_direction;	/* 1=normal, -1=reverse */
79 } sort_key;
80 
81 typedef struct sort_ctrl {
82 	int sc_nkeys;
83 	sort_key sc_keys[1];
84 } sort_ctrl;
85 
86 
87 typedef struct sort_node
88 {
89 	int sn_conn;
90 	struct berval sn_dn;
91 	struct berval *sn_vals;
92 } sort_node;
93 
94 typedef struct sssvlv_info
95 {
96 	int svi_max;	/* max concurrent sorts */
97 	int svi_num;	/* current # sorts */
98 	int svi_max_keys;	/* max sort keys per request */
99 } sssvlv_info;
100 
101 typedef struct sort_op
102 {
103 	Avlnode	*so_tree;
104 	sort_ctrl *so_ctrl;
105 	sssvlv_info *so_info;
106 	int so_paged;
107 	int so_page_size;
108 	int so_nentries;
109 	int so_vlv;
110 	int so_vlv_rc;
111 	int so_vlv_target;
112 	unsigned long so_vcontext;
113 } sort_op;
114 
115 /* There is only one conn table for all overlay instances */
116 static sort_op **sort_conns;
117 static ldap_pvt_thread_mutex_t sort_conns_mutex;
118 static int ov_count;
119 static const char *debug_header = "sssvlv";
120 
121 static int sss_cid;
122 static int vlv_cid;
123 
124 /* RFC 2981 Section 2.2
125  * If a sort key is a multi-valued attribute, and an entry happens to
126  * have multiple values for that attribute and no other controls are
127  * present that affect the sorting order, then the server SHOULD use the
128  * least value (according to the ORDERING rule for that attribute).
129  */
130 static struct berval* select_value(
131 	Attribute		*attr,
132 	sort_key			*key )
133 {
134 	struct berval* ber1, *ber2;
135 	MatchingRule *mr = key->sk_ordering;
136 	int i, cmp;
137 
138 	ber1 = &(attr->a_nvals[0]);
139 	ber2 = ber1+1;
140 	for ( i = 1; i < attr->a_numvals; i++,ber2++ ) {
141 		mr->smr_match( &cmp, 0, mr->smr_syntax, mr, ber1, ber2 );
142 		if ( cmp > 0 ) {
143 			ber1 = ber2;
144 		}
145 	}
146 
147 	Debug(LDAP_DEBUG_TRACE, "%s: value selected for compare: %s\n",
148 		debug_header,
149 		SAFESTR(ber1->bv_val, "<Empty>"),
150 		0);
151 
152 	return ber1;
153 }
154 
155 static int node_cmp( const void* val1, const void* val2 )
156 {
157 	sort_node *sn1 = (sort_node *)val1;
158 	sort_node *sn2 = (sort_node *)val2;
159 	sort_ctrl *sc = sort_conns[sn1->sn_conn]->so_ctrl;
160 	MatchingRule *mr;
161 	int i, cmp = 0;
162 
163 	for ( i=0; cmp == 0 && i<sc->sc_nkeys; i++ ) {
164 		if ( BER_BVISNULL( &sn1->sn_vals[i] )) {
165 			if ( BER_BVISNULL( &sn2->sn_vals[i] ))
166 				cmp = 0;
167 			else
168 				cmp = sc->sc_keys[i].sk_direction;
169 		} else if ( BER_BVISNULL( &sn2->sn_vals[i] )) {
170 			cmp = sc->sc_keys[i].sk_direction * -1;
171 		} else {
172 			mr = sc->sc_keys[i].sk_ordering;
173 			mr->smr_match( &cmp, 0, mr->smr_syntax, mr,
174 				&sn1->sn_vals[i], &sn2->sn_vals[i] );
175 			if ( cmp )
176 				cmp *= sc->sc_keys[i].sk_direction;
177 		}
178 	}
179 	return cmp;
180 }
181 
182 static int node_insert( const void *val1, const void *val2 )
183 {
184 	/* Never return equal so that new entries are always inserted */
185 	return node_cmp( val1, val2 ) < 0 ? -1 : 1;
186 }
187 
188 static int pack_vlv_response_control(
189 	Operation		*op,
190 	SlapReply		*rs,
191 	sort_op			*so,
192 	LDAPControl	**ctrlsp )
193 {
194 	LDAPControl			*ctrl;
195 	BerElementBuffer	berbuf;
196 	BerElement			*ber		= (BerElement *)&berbuf;
197 	struct berval		cookie, bv;
198 	int					rc;
199 
200 	ber_init2( ber, NULL, LBER_USE_DER );
201 	ber_set_option( ber, LBER_OPT_BER_MEMCTX, &op->o_tmpmemctx );
202 
203 	rc = ber_printf( ber, "{iii", so->so_vlv_target, so->so_nentries,
204 		so->so_vlv_rc );
205 
206 	if ( rc != -1 && so->so_vcontext ) {
207 		cookie.bv_val = (char *)&so->so_vcontext;
208 		cookie.bv_len = sizeof(so->so_vcontext);
209 		rc = ber_printf( ber, "tO", LDAP_VLVCONTEXT_IDENTIFIER, &cookie );
210 	}
211 
212 	if ( rc != -1 ) {
213 		rc = ber_printf( ber, "}" );
214 	}
215 
216 	if ( rc != -1 ) {
217 		rc = ber_flatten2( ber, &bv, 0 );
218 	}
219 
220 	if ( rc != -1 ) {
221 		ctrl = (LDAPControl *)op->o_tmpalloc( sizeof(LDAPControl)+
222 			bv.bv_len, op->o_tmpmemctx );
223 		ctrl->ldctl_oid			= LDAP_CONTROL_VLVRESPONSE;
224 		ctrl->ldctl_iscritical	= 0;
225 		ctrl->ldctl_value.bv_val = (char *)(ctrl+1);
226 		ctrl->ldctl_value.bv_len = bv.bv_len;
227 		AC_MEMCPY( ctrl->ldctl_value.bv_val, bv.bv_val, bv.bv_len );
228 		ctrlsp[0] = ctrl;
229 	} else {
230 		ctrlsp[0] = NULL;
231 		rs->sr_err = LDAP_OTHER;
232 	}
233 
234 	ber_free_buf( ber );
235 
236 	return rs->sr_err;
237 }
238 
239 static int pack_pagedresult_response_control(
240 	Operation		*op,
241 	SlapReply		*rs,
242 	sort_op			*so,
243 	LDAPControl	**ctrlsp )
244 {
245 	LDAPControl			*ctrl;
246 	BerElementBuffer	berbuf;
247 	BerElement			*ber		= (BerElement *)&berbuf;
248 	PagedResultsCookie	resp_cookie;
249 	struct berval		cookie, bv;
250 	int					rc;
251 
252 	ber_init2( ber, NULL, LBER_USE_DER );
253 	ber_set_option( ber, LBER_OPT_BER_MEMCTX, &op->o_tmpmemctx );
254 
255 	if ( so->so_nentries > 0 ) {
256 		resp_cookie		= ( PagedResultsCookie )so->so_tree;
257 		cookie.bv_len	= sizeof( PagedResultsCookie );
258 		cookie.bv_val	= (char *)&resp_cookie;
259 	} else {
260 		resp_cookie		= ( PagedResultsCookie )0;
261 		BER_BVZERO( &cookie );
262 	}
263 
264 	op->o_conn->c_pagedresults_state.ps_cookie = resp_cookie;
265 	op->o_conn->c_pagedresults_state.ps_count
266 		= ((PagedResultsState *)op->o_pagedresults_state)->ps_count
267 		  + rs->sr_nentries;
268 
269 	rc = ber_printf( ber, "{iO}", so->so_nentries, &cookie );
270 	if ( rc != -1 ) {
271 		rc = ber_flatten2( ber, &bv, 0 );
272 	}
273 
274 	if ( rc != -1 ) {
275 		ctrl = (LDAPControl *)op->o_tmpalloc( sizeof(LDAPControl)+
276 			bv.bv_len, op->o_tmpmemctx );
277 		ctrl->ldctl_oid			= LDAP_CONTROL_PAGEDRESULTS;
278 		ctrl->ldctl_iscritical	= 0;
279 		ctrl->ldctl_value.bv_val = (char *)(ctrl+1);
280 		ctrl->ldctl_value.bv_len = bv.bv_len;
281 		AC_MEMCPY( ctrl->ldctl_value.bv_val, bv.bv_val, bv.bv_len );
282 		ctrlsp[0] = ctrl;
283 	} else {
284 		ctrlsp[0] = NULL;
285 		rs->sr_err = LDAP_OTHER;
286 	}
287 
288 	ber_free_buf( ber );
289 
290 	return rs->sr_err;
291 }
292 
293 static int pack_sss_response_control(
294 	Operation		*op,
295 	SlapReply		*rs,
296 	LDAPControl	**ctrlsp )
297 {
298 	LDAPControl			*ctrl;
299 	BerElementBuffer	berbuf;
300 	BerElement			*ber		= (BerElement *)&berbuf;
301 	struct berval		bv;
302 	int					rc;
303 
304 	ber_init2( ber, NULL, LBER_USE_DER );
305 	ber_set_option( ber, LBER_OPT_BER_MEMCTX, &op->o_tmpmemctx );
306 
307 	/* Pack error code */
308 	rc = ber_printf(ber, "{e}", rs->sr_err);
309 
310 	if ( rc != -1)
311 		rc = ber_flatten2( ber, &bv, 0 );
312 
313 	if ( rc != -1 ) {
314 		ctrl = (LDAPControl *)op->o_tmpalloc( sizeof(LDAPControl)+
315 			bv.bv_len, op->o_tmpmemctx );
316 		ctrl->ldctl_oid			= LDAP_CONTROL_SORTRESPONSE;
317 		ctrl->ldctl_iscritical	= 0;
318 		ctrl->ldctl_value.bv_val = (char *)(ctrl+1);
319 		ctrl->ldctl_value.bv_len = bv.bv_len;
320 		AC_MEMCPY( ctrl->ldctl_value.bv_val, bv.bv_val, bv.bv_len );
321 		ctrlsp[0] = ctrl;
322 	} else {
323 		ctrlsp[0] = NULL;
324 		rs->sr_err = LDAP_OTHER;
325 	}
326 
327 	ber_free_buf( ber );
328 
329 	return rs->sr_err;
330 }
331 
332 static void free_sort_op( Connection *conn, sort_op *so )
333 {
334 	if ( so->so_tree ) {
335 		tavl_free( so->so_tree, ch_free );
336 		so->so_tree = NULL;
337 	}
338 
339 	ldap_pvt_thread_mutex_lock( &sort_conns_mutex );
340 	sort_conns[conn->c_conn_idx] = NULL;
341 	so->so_info->svi_num--;
342 	ldap_pvt_thread_mutex_unlock( &sort_conns_mutex );
343 
344 	ch_free( so );
345 }
346 
347 static void send_list(
348 	Operation		*op,
349 	SlapReply		*rs,
350 	sort_op			*so)
351 {
352 	Avlnode	*cur_node, *tmp_node;
353 	vlv_ctrl *vc = op->o_controls[vlv_cid];
354 	int i, j, dir, rc;
355 	BackendDB *be;
356 	Entry *e;
357 	LDAPControl *ctrls[2];
358 
359 	/* FIXME: it may be better to just flatten the tree into
360 	 * an array before doing all of this...
361 	 */
362 
363 	/* Are we just counting an offset? */
364 	if ( BER_BVISNULL( &vc->vc_value )) {
365 		if ( vc->vc_offset == vc->vc_count ) {
366 			/* wants the last entry in the list */
367 			cur_node = tavl_end(so->so_tree, TAVL_DIR_RIGHT);
368 			so->so_vlv_target = so->so_nentries;
369 		} else if ( vc->vc_offset == 1 ) {
370 			/* wants the first entry in the list */
371 			cur_node = tavl_end(so->so_tree, TAVL_DIR_LEFT);
372 			so->so_vlv_target = 1;
373 		} else {
374 			int target;
375 			/* Just iterate to the right spot */
376 			if ( vc->vc_count && vc->vc_count != so->so_nentries ) {
377 				if ( vc->vc_offset > vc->vc_count )
378 					goto range_err;
379 				target = so->so_nentries * vc->vc_offset / vc->vc_count;
380 			} else {
381 				if ( vc->vc_offset > so->so_nentries ) {
382 range_err:
383 					so->so_vlv_rc = LDAP_VLV_RANGE_ERROR;
384 					pack_vlv_response_control( op, rs, so, ctrls );
385 					ctrls[1] = NULL;
386 					slap_add_ctrls( op, rs, ctrls );
387 					rs->sr_err = LDAP_VLV_ERROR;
388 					return;
389 				}
390 				target = vc->vc_offset;
391 			}
392 			so->so_vlv_target = target;
393 			/* Start at left and go right, or start at right and go left? */
394 			if ( target < so->so_nentries / 2 ) {
395 				cur_node = tavl_end(so->so_tree, TAVL_DIR_LEFT);
396 				dir = TAVL_DIR_RIGHT;
397 			} else {
398 				cur_node = tavl_end(so->so_tree, TAVL_DIR_RIGHT);
399 				dir = TAVL_DIR_LEFT;
400 				target = so->so_nentries - target + 1;
401 			}
402 			for ( i=1; i<target; i++ )
403 				cur_node = tavl_next( cur_node, dir );
404 		}
405 	} else {
406 	/* we're looking for a specific value */
407 		sort_ctrl *sc = so->so_ctrl;
408 		MatchingRule *mr = sc->sc_keys[0].sk_ordering;
409 		sort_node *sn;
410 		struct berval bv;
411 
412 		if ( mr->smr_normalize ) {
413 			rc = mr->smr_normalize( SLAP_MR_VALUE_OF_SYNTAX,
414 				mr->smr_syntax, mr, &vc->vc_value, &bv, op->o_tmpmemctx );
415 			if ( rc ) {
416 				so->so_vlv_rc = LDAP_INAPPROPRIATE_MATCHING;
417 				pack_vlv_response_control( op, rs, so, ctrls );
418 				ctrls[1] = NULL;
419 				slap_add_ctrls( op, rs, ctrls );
420 				rs->sr_err = LDAP_VLV_ERROR;
421 				return;
422 			}
423 		} else {
424 			bv = vc->vc_value;
425 		}
426 
427 		sn = op->o_tmpalloc( sizeof(sort_node) +
428 			sc->sc_nkeys * sizeof(struct berval), op->o_tmpmemctx );
429 		sn->sn_vals = (struct berval *)(sn+1);
430 		sn->sn_conn = op->o_conn->c_conn_idx;
431 		sn->sn_vals[0] = bv;
432 		for (i=1; i<sc->sc_nkeys; i++) {
433 			BER_BVZERO( &sn->sn_vals[i] );
434 		}
435 		cur_node = tavl_find3( so->so_tree, sn, node_cmp, &j );
436 		/* didn't find >= match */
437 		if ( j > 0 )
438 			cur_node = NULL;
439 		op->o_tmpfree( sn, op->o_tmpmemctx );
440 
441 		if ( !cur_node ) {
442 			so->so_vlv_target = so->so_nentries + 1;
443 		} else {
444 			sort_node *sn = so->so_tree->avl_data;
445 			/* start from the left or the right side? */
446 			mr->smr_match( &i, 0, mr->smr_syntax, mr, &bv, &sn->sn_vals[0] );
447 			if ( i > 0 ) {
448 				tmp_node = tavl_end(so->so_tree, TAVL_DIR_RIGHT);
449 				dir = TAVL_DIR_LEFT;
450 			} else {
451 				tmp_node = tavl_end(so->so_tree, TAVL_DIR_LEFT);
452 				dir = TAVL_DIR_RIGHT;
453 			}
454 			for (i=0; tmp_node != cur_node;
455 				tmp_node = tavl_next( tmp_node, dir ), i++);
456 			so->so_vlv_target = i;
457 		}
458 		if ( bv.bv_val != vc->vc_value.bv_val )
459 			op->o_tmpfree( bv.bv_val, op->o_tmpmemctx );
460 	}
461 	if ( !cur_node ) {
462 		i = 1;
463 		cur_node = tavl_end(so->so_tree, TAVL_DIR_RIGHT);
464 	} else {
465 		i = 0;
466 	}
467 	for ( ; i<vc->vc_before; i++ ) {
468 		tmp_node = tavl_next( cur_node, TAVL_DIR_LEFT );
469 		if ( !tmp_node ) break;
470 		cur_node = tmp_node;
471 	}
472 	j = i + vc->vc_after + 1;
473 	be = op->o_bd;
474 	for ( i=0; i<j; i++ ) {
475 		sort_node *sn = cur_node->avl_data;
476 
477 		if ( slapd_shutdown ) break;
478 
479 		op->o_bd = select_backend( &sn->sn_dn, 0 );
480 		e = NULL;
481 		rc = be_entry_get_rw( op, &sn->sn_dn, NULL, NULL, 0, &e );
482 
483 		if ( e && rc == LDAP_SUCCESS ) {
484 			rs->sr_entry = e;
485 			rs->sr_flags = REP_ENTRY_MUSTRELEASE;
486 			rs->sr_err = send_search_entry( op, rs );
487 			if ( rs->sr_err == LDAP_UNAVAILABLE )
488 				break;
489 		}
490 		cur_node = tavl_next( cur_node, TAVL_DIR_RIGHT );
491 		if ( !cur_node ) break;
492 	}
493 	so->so_vlv_rc = LDAP_SUCCESS;
494 
495 	op->o_bd = be;
496 }
497 
498 static void send_page( Operation *op, SlapReply *rs, sort_op *so )
499 {
500 	Avlnode		*cur_node		= so->so_tree;
501 	Avlnode		*next_node		= NULL;
502 	BackendDB *be = op->o_bd;
503 	sort_node *sn;
504 	Entry *e;
505 	int rc;
506 
507 	while ( cur_node && rs->sr_nentries < so->so_page_size ) {
508 		sort_node *sn = cur_node->avl_data;
509 
510 		if ( slapd_shutdown ) break;
511 
512 		next_node = tavl_next( cur_node, TAVL_DIR_RIGHT );
513 
514 		op->o_bd = select_backend( &sn->sn_dn, 0 );
515 		e = NULL;
516 		rc = be_entry_get_rw( op, &sn->sn_dn, NULL, NULL, 0, &e );
517 
518 		ch_free( cur_node->avl_data );
519 		ber_memfree( cur_node );
520 
521 		cur_node = next_node;
522 		so->so_nentries--;
523 
524 		if ( e && rc == LDAP_SUCCESS ) {
525 			rs->sr_entry = e;
526 			rs->sr_flags = REP_ENTRY_MUSTRELEASE;
527 			rs->sr_err = send_search_entry( op, rs );
528 			if ( rs->sr_err == LDAP_UNAVAILABLE )
529 				break;
530 		}
531 	}
532 
533 	/* Set the first entry to send for the next page */
534 	so->so_tree = next_node;
535 
536 	op->o_bd = be;
537 }
538 
539 static void send_entry(
540 	Operation		*op,
541 	SlapReply		*rs,
542 	sort_op			*so)
543 {
544 	Debug(LDAP_DEBUG_TRACE,
545 		"%s: response control: status=%d, text=%s\n",
546 		debug_header, rs->sr_err, SAFESTR(rs->sr_text, "<None>"));
547 
548 	if ( !so->so_tree )
549 		return;
550 
551 	/* RFC 2891: If critical then send the entries iff they were
552 	 * succesfully sorted.  If non-critical send all entries
553 	 * whether they were sorted or not.
554 	 */
555 	if ( (op->o_ctrlflag[sss_cid] != SLAP_CONTROL_CRITICAL) ||
556 		 (rs->sr_err == LDAP_SUCCESS) )
557 	{
558 		if ( so->so_vlv > SLAP_CONTROL_IGNORED ) {
559 			send_list( op, rs, so );
560 		} else {
561 			/* Get the first node to send */
562 			Avlnode *start_node = tavl_end(so->so_tree, TAVL_DIR_LEFT);
563 			so->so_tree = start_node;
564 
565 			if ( so->so_paged <= SLAP_CONTROL_IGNORED ) {
566 				/* Not paged result search.  Send all entries.
567 				 * Set the page size to the number of entries
568 				 * so that send_page() will send all entries.
569 				 */
570 				so->so_page_size = so->so_nentries;
571 			}
572 
573 			send_page( op, rs, so );
574 		}
575 	}
576 }
577 
578 static void send_result(
579 	Operation		*op,
580 	SlapReply		*rs,
581 	sort_op			*so)
582 {
583 	LDAPControl *ctrls[3];
584 	int rc, i = 0;
585 
586 	rc = pack_sss_response_control( op, rs, ctrls );
587 	if ( rc == LDAP_SUCCESS ) {
588 		i++;
589 		rc = -1;
590 		if ( so->so_paged > SLAP_CONTROL_IGNORED ) {
591 			rc = pack_pagedresult_response_control( op, rs, so, ctrls+1 );
592 		} else if ( so->so_vlv > SLAP_CONTROL_IGNORED ) {
593 			rc = pack_vlv_response_control( op, rs, so, ctrls+1 );
594 		}
595 		if ( rc == LDAP_SUCCESS )
596 			i++;
597 	}
598 	ctrls[i] = NULL;
599 
600 	if ( ctrls[0] != NULL )
601 		slap_add_ctrls( op, rs, ctrls );
602 	send_ldap_result( op, rs );
603 
604 	if ( so->so_tree == NULL ) {
605 		/* Search finished, so clean up */
606 		free_sort_op( op->o_conn, so );
607 	}
608 }
609 
610 static int sssvlv_op_response(
611 	Operation	*op,
612 	SlapReply	*rs )
613 {
614 	sort_ctrl *sc = op->o_controls[sss_cid];
615 	sort_op *so = op->o_callback->sc_private;
616 
617 	if ( rs->sr_type == REP_SEARCH ) {
618 		int i;
619 		size_t len;
620 		sort_node *sn, *sn2;
621 		struct berval *bv;
622 		char *ptr;
623 
624 		len = sizeof(sort_node) + sc->sc_nkeys * sizeof(struct berval) +
625 			rs->sr_entry->e_nname.bv_len + 1;
626 		sn = op->o_tmpalloc( len, op->o_tmpmemctx );
627 		sn->sn_vals = (struct berval *)(sn+1);
628 
629 		/* Build tmp list of key values */
630 		for ( i=0; i<sc->sc_nkeys; i++ ) {
631 			Attribute *a = attr_find( rs->sr_entry->e_attrs,
632 				sc->sc_keys[i].sk_ad );
633 			if ( a ) {
634 				if ( a->a_numvals > 1 ) {
635 					bv = select_value( a, &sc->sc_keys[i] );
636 				} else {
637 					bv = a->a_nvals;
638 				}
639 				sn->sn_vals[i] = *bv;
640 				len += bv->bv_len + 1;
641 			} else {
642 				BER_BVZERO( &sn->sn_vals[i] );
643 			}
644 		}
645 
646 		/* Now dup into regular memory */
647 		sn2 = ch_malloc( len );
648 		sn2->sn_vals = (struct berval *)(sn2+1);
649 		AC_MEMCPY( sn2->sn_vals, sn->sn_vals,
650 				sc->sc_nkeys * sizeof(struct berval));
651 
652 		ptr = (char *)(sn2->sn_vals + sc->sc_nkeys);
653 		sn2->sn_dn.bv_val = ptr;
654 		sn2->sn_dn.bv_len = rs->sr_entry->e_nname.bv_len;
655 		AC_MEMCPY( ptr, rs->sr_entry->e_nname.bv_val,
656 			rs->sr_entry->e_nname.bv_len );
657 		ptr += rs->sr_entry->e_nname.bv_len;
658 		*ptr++ = '\0';
659 		for ( i=0; i<sc->sc_nkeys; i++ ) {
660 			if ( !BER_BVISNULL( &sn2->sn_vals[i] )) {
661 				AC_MEMCPY(ptr, sn2->sn_vals[i].bv_val, sn2->sn_vals[i].bv_len);
662 				sn2->sn_vals[i].bv_val = ptr;
663 				ptr += sn2->sn_vals[i].bv_len;
664 				*ptr++ = '\0';
665 			}
666 		}
667 		op->o_tmpfree( sn, op->o_tmpmemctx );
668 		sn = sn2;
669 		sn->sn_conn = op->o_conn->c_conn_idx;
670 
671 		/* Insert into the AVL tree */
672 		tavl_insert(&(so->so_tree), sn, node_insert, avl_dup_error);
673 
674 		so->so_nentries++;
675 
676 		/* Collected the keys so that they can be sorted.  Thus, stop
677 		 * the entry from propagating.
678 		 */
679 		rs->sr_err = LDAP_SUCCESS;
680 	}
681 	else if ( rs->sr_type == REP_RESULT ) {
682 		/* Remove serversort response callback.
683 		 * We don't want the entries that we are about to send to be
684 		 * processed by serversort response again.
685 		 */
686 		if ( op->o_callback->sc_response == sssvlv_op_response ) {
687 			op->o_callback = op->o_callback->sc_next;
688 		}
689 
690 		send_entry( op, rs, so );
691 		send_result( op, rs, so );
692 	}
693 
694 	return rs->sr_err;
695 }
696 
697 static int sssvlv_op_search(
698 	Operation		*op,
699 	SlapReply		*rs)
700 {
701 	slap_overinst			*on			= (slap_overinst *)op->o_bd->bd_info;
702 	sssvlv_info				*si			= on->on_bi.bi_private;
703 	int						rc			= SLAP_CB_CONTINUE;
704 	int	ok;
705 	sort_op *so, so2;
706 	sort_ctrl *sc;
707 	PagedResultsState *ps;
708 	vlv_ctrl *vc;
709 
710 	if ( op->o_ctrlflag[sss_cid] <= SLAP_CONTROL_IGNORED ) {
711 		if ( op->o_ctrlflag[vlv_cid] > SLAP_CONTROL_IGNORED ) {
712 			LDAPControl *ctrls[2];
713 			so2.so_vcontext = 0;
714 			so2.so_vlv_target = 0;
715 			so2.so_nentries = 0;
716 			so2.so_vlv_rc = LDAP_VLV_SSS_MISSING;
717 			rc = pack_vlv_response_control( op, rs, &so2, ctrls );
718 			if ( rc == LDAP_SUCCESS ) {
719 				ctrls[1] = NULL;
720 				slap_add_ctrls( op, rs, ctrls );
721 			}
722 			rs->sr_err = LDAP_VLV_ERROR;
723 			rs->sr_text = "Sort control is required with VLV";
724 			goto leave;
725 		}
726 		/* Not server side sort so just continue */
727 		return SLAP_CB_CONTINUE;
728 	}
729 
730 	Debug(LDAP_DEBUG_TRACE,
731 		"==> sssvlv_search: <%s> %s, control flag: %d\n",
732 		op->o_req_dn.bv_val, op->ors_filterstr.bv_val,
733 		op->o_ctrlflag[sss_cid]);
734 
735 	sc = op->o_controls[sss_cid];
736 	if ( sc->sc_nkeys > si->svi_max_keys ) {
737 		rs->sr_text = "Too many sort keys";
738 		rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
739 		goto leave;
740 	}
741 
742 	ps = ( op->o_pagedresults > SLAP_CONTROL_IGNORED ) ?
743 		(PagedResultsState*)(op->o_pagedresults_state) : NULL;
744 	vc = op->o_ctrlflag[vlv_cid] > SLAP_CONTROL_IGNORED ?
745 		op->o_controls[vlv_cid] : NULL;
746 
747 	if ( ps && vc ) {
748 		rs->sr_text = "VLV incompatible with PagedResults";
749 		rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
750 		goto leave;
751 	}
752 
753 	ok = 1;
754 	ldap_pvt_thread_mutex_lock( &sort_conns_mutex );
755 	so = sort_conns[op->o_conn->c_conn_idx];
756 	/* Is there already a sort running on this conn? */
757 	if ( so ) {
758 		/* Is it a continuation of a VLV search? */
759 		if ( !vc || so->so_vlv <= SLAP_CONTROL_IGNORED ||
760 			vc->vc_context != so->so_vcontext ) {
761 			/* Is it a continuation of a paged search? */
762 			if ( !ps || so->so_paged <= SLAP_CONTROL_IGNORED ||
763 				op->o_conn->c_pagedresults_state.ps_cookie != ps->ps_cookie ) {
764 				ok = 0;
765 			} else if ( !ps->ps_size ) {
766 			/* Abandoning current request */
767 				ok = 0;
768 				so->so_nentries = 0;
769 				rs->sr_err = LDAP_SUCCESS;
770 			}
771 		}
772 		if (( vc && so->so_paged > SLAP_CONTROL_IGNORED ) ||
773 			( ps && so->so_vlv > SLAP_CONTROL_IGNORED )) {
774 			/* changed from paged to vlv or vice versa, abandon */
775 			ok = 0;
776 			so->so_nentries = 0;
777 			rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
778 		}
779 	/* Are there too many running overall? */
780 	} else if ( si->svi_num >= si->svi_max ) {
781 		ok = 0;
782 	} else {
783 		/* OK, this connection now has a sort running */
784 		si->svi_num++;
785 		sort_conns[op->o_conn->c_conn_idx] = &so2;
786 	}
787 	ldap_pvt_thread_mutex_unlock( &sort_conns_mutex );
788 	if ( ok ) {
789 		/* are we continuing a VLV search? */
790 		if ( vc && vc->vc_context ) {
791 			so->so_ctrl = sc;
792 			send_list( op, rs, so );
793 			send_result( op, rs, so );
794 			rc = LDAP_SUCCESS;
795 		/* are we continuing a paged search? */
796 		} else if ( ps && ps->ps_cookie ) {
797 			so->so_ctrl = sc;
798 			send_page( op, rs, so );
799 			send_result( op, rs, so );
800 			rc = LDAP_SUCCESS;
801 		} else {
802 			slap_callback *cb = op->o_tmpalloc( sizeof(slap_callback),
803 				op->o_tmpmemctx );
804 			/* Install serversort response callback to handle a new search */
805 			if ( ps || vc ) {
806 				so = ch_malloc( sizeof(sort_op));
807 			} else {
808 				so = op->o_tmpalloc( sizeof(sort_op), op->o_tmpmemctx );
809 			}
810 			sort_conns[op->o_conn->c_conn_idx] = so;
811 
812 			cb->sc_cleanup		= NULL;
813 			cb->sc_response		= sssvlv_op_response;
814 			cb->sc_next			= op->o_callback;
815 			cb->sc_private		= so;
816 
817 			so->so_tree = NULL;
818 			so->so_ctrl = sc;
819 			so->so_info = si;
820 			if ( ps ) {
821 				so->so_paged = op->o_pagedresults;
822 				so->so_page_size = ps->ps_size;
823 				op->o_pagedresults = SLAP_CONTROL_IGNORED;
824 			} else {
825 				so->so_paged = 0;
826 				so->so_page_size = 0;
827 				if ( vc ) {
828 					so->so_vlv = op->o_ctrlflag[vlv_cid];
829 					so->so_vlv_target = 0;
830 					so->so_vlv_rc = 0;
831 				} else {
832 					so->so_vlv = SLAP_CONTROL_NONE;
833 				}
834 			}
835 			so->so_vcontext = (unsigned long)so;
836 			so->so_nentries = 0;
837 
838 			op->o_callback		= cb;
839 		}
840 	} else {
841 		if ( so && !so->so_nentries ) {
842 			free_sort_op( op->o_conn, so );
843 		} else {
844 			rs->sr_text = "Other sort requests already in progress";
845 			rs->sr_err = LDAP_BUSY;
846 		}
847 leave:
848 		rc = rs->sr_err;
849 		send_ldap_result( op, rs );
850 	}
851 
852 	return rc;
853 }
854 
855 static int get_ordering_rule(
856 	AttributeDescription	*ad,
857 	struct berval			*matchrule,
858 	SlapReply				*rs,
859 	MatchingRule			**ordering )
860 {
861 	MatchingRule* mr;
862 
863 	if ( matchrule && matchrule->bv_val ) {
864 		mr = mr_find( matchrule->bv_val );
865 		if ( mr == NULL ) {
866 			rs->sr_err = LDAP_INAPPROPRIATE_MATCHING;
867 			rs->sr_text = "serverSort control: No ordering rule";
868 			Debug(LDAP_DEBUG_TRACE, "%s: no ordering rule function for %s\n",
869 				debug_header, matchrule->bv_val, 0);
870 		}
871 	}
872 	else {
873 		mr = ad->ad_type->sat_ordering;
874 		if ( mr == NULL ) {
875 			rs->sr_err = LDAP_INAPPROPRIATE_MATCHING;
876 			rs->sr_text = "serverSort control: No ordering rule";
877 			Debug(LDAP_DEBUG_TRACE,
878 				"%s: no ordering rule specified and no default ordering rule for attribute %s\n",
879 				debug_header, ad->ad_cname.bv_val, 0);
880 		}
881 	}
882 
883 	*ordering = mr;
884 	return rs->sr_err;
885 }
886 
887 static int count_key(BerElement *ber)
888 {
889 	char *end;
890 	ber_len_t len;
891 	ber_tag_t tag;
892 	int count = 0;
893 
894 	/* Server Side Sort Control is a SEQUENCE of SEQUENCE */
895 	for ( tag = ber_first_element( ber, &len, &end );
896 		  tag == LBER_SEQUENCE;
897 		  tag = ber_next_element( ber, &len, end ))
898 	{
899 		tag = ber_skip_tag( ber, &len );
900 		ber_skip_data( ber, len );
901 		++count;
902 	}
903 	ber_rewind( ber );
904 
905 	return count;
906 }
907 
908 static int build_key(
909 	BerElement		*ber,
910 	SlapReply		*rs,
911 	sort_key			*key )
912 {
913 	struct berval attr;
914 	struct berval matchrule = BER_BVNULL;
915 	ber_int_t reverse = 0;
916 	ber_tag_t tag;
917 	ber_len_t len;
918 	MatchingRule *ordering = NULL;
919 	AttributeDescription *ad = NULL;
920 	const char *text;
921 
922 	if (( tag = ber_scanf( ber, "{" )) == LBER_ERROR ) {
923 		rs->sr_text = "serverSort control: decoding error";
924 		rs->sr_err = LDAP_PROTOCOL_ERROR;
925 		return rs->sr_err;
926 	}
927 
928 	if (( tag = ber_scanf( ber, "m", &attr )) == LBER_ERROR ) {
929 		rs->sr_text = "serverSort control: attribute decoding error";
930 		rs->sr_err = LDAP_PROTOCOL_ERROR;
931 		return rs->sr_err;
932 	}
933 
934 	tag = ber_peek_tag( ber, &len );
935 	if ( tag == LDAP_MATCHRULE_IDENTIFIER ) {
936 		if (( tag = ber_scanf( ber, "m", &matchrule )) == LBER_ERROR ) {
937 			rs->sr_text = "serverSort control: matchrule decoding error";
938 			rs->sr_err = LDAP_PROTOCOL_ERROR;
939 			return rs->sr_err;
940 		}
941 		tag = ber_peek_tag( ber, &len );
942 	}
943 
944 	if ( tag == LDAP_REVERSEORDER_IDENTIFIER ) {
945 		if (( tag = ber_scanf( ber, "b", &reverse )) == LBER_ERROR ) {
946 			rs->sr_text = "serverSort control: reverse decoding error";
947 			rs->sr_err = LDAP_PROTOCOL_ERROR;
948 			return rs->sr_err;
949 		}
950 	}
951 
952 	if (( tag = ber_scanf( ber, "}" )) == LBER_ERROR ) {
953 		rs->sr_text = "serverSort control: decoding error";
954 		rs->sr_err = LDAP_PROTOCOL_ERROR;
955 		return rs->sr_err;
956 	}
957 
958 	if ( slap_bv2ad( &attr, &ad, &text ) != LDAP_SUCCESS ) {
959 		rs->sr_text =
960 			"serverSort control: Unrecognized attribute type in sort key";
961 		Debug(LDAP_DEBUG_TRACE,
962 			"%s: Unrecognized attribute type in sort key: %s\n",
963 			debug_header, SAFESTR(attr.bv_val, "<None>"), 0);
964 		rs->sr_err = LDAP_NO_SUCH_ATTRIBUTE;
965 		return rs->sr_err;
966 	}
967 
968 	/* get_ordering_rule will set sr_err and sr_text */
969 	get_ordering_rule( ad, &matchrule, rs, &ordering );
970 	if ( rs->sr_err != LDAP_SUCCESS ) {
971 		return rs->sr_err;
972 	}
973 
974 	key->sk_ad = ad;
975 	key->sk_ordering = ordering;
976 	key->sk_direction = reverse ? -1 : 1;
977 
978 	return rs->sr_err;
979 }
980 
981 static int sss_parseCtrl(
982 	Operation		*op,
983 	SlapReply		*rs,
984 	LDAPControl		*ctrl )
985 {
986 	BerElementBuffer	berbuf;
987 	BerElement			*ber;
988 	ber_tag_t		tag;
989 	ber_len_t		len;
990 	int					i;
991 	sort_ctrl	*sc;
992 
993 	rs->sr_err = LDAP_PROTOCOL_ERROR;
994 
995 	if ( op->o_ctrlflag[sss_cid] > SLAP_CONTROL_IGNORED ) {
996 		rs->sr_text = "sorted results control specified multiple times";
997 	} else if ( BER_BVISNULL( &ctrl->ldctl_value ) ) {
998 		rs->sr_text = "sorted results control value is absent";
999 	} else if ( BER_BVISEMPTY( &ctrl->ldctl_value ) ) {
1000 		rs->sr_text = "sorted results control value is empty";
1001 	} else {
1002 		rs->sr_err = LDAP_SUCCESS;
1003 	}
1004 	if ( rs->sr_err != LDAP_SUCCESS )
1005 		return rs->sr_err;
1006 
1007 	op->o_ctrlflag[sss_cid] = ctrl->ldctl_iscritical ?
1008 		SLAP_CONTROL_CRITICAL : SLAP_CONTROL_NONCRITICAL;
1009 
1010 	ber = (BerElement *)&berbuf;
1011 	ber_init2( ber, &ctrl->ldctl_value, 0 );
1012 	i = count_key( ber );
1013 
1014 	sc = op->o_tmpalloc( sizeof(sort_ctrl) +
1015 		(i-1) * sizeof(sort_key), op->o_tmpmemctx );
1016 	sc->sc_nkeys = i;
1017 	op->o_controls[sss_cid] = sc;
1018 
1019 	/* peel off initial sequence */
1020 	ber_scanf( ber, "{" );
1021 
1022 	i = 0;
1023 	do {
1024 		if ( build_key( ber, rs, &sc->sc_keys[i] ) != LDAP_SUCCESS )
1025 			break;
1026 		i++;
1027 		tag = ber_peek_tag( ber, &len );
1028 	} while ( tag != LBER_DEFAULT );
1029 
1030 	return rs->sr_err;
1031 }
1032 
1033 static int vlv_parseCtrl(
1034 	Operation		*op,
1035 	SlapReply		*rs,
1036 	LDAPControl		*ctrl )
1037 {
1038 	BerElementBuffer	berbuf;
1039 	BerElement			*ber;
1040 	ber_tag_t		tag;
1041 	ber_len_t		len;
1042 	int					i;
1043 	vlv_ctrl	*vc, vc2;
1044 
1045 	rs->sr_err = LDAP_PROTOCOL_ERROR;
1046 	rs->sr_text = NULL;
1047 
1048 	if ( op->o_ctrlflag[vlv_cid] > SLAP_CONTROL_IGNORED ) {
1049 		rs->sr_text = "vlv control specified multiple times";
1050 	} else if ( BER_BVISNULL( &ctrl->ldctl_value ) ) {
1051 		rs->sr_text = "vlv control value is absent";
1052 	} else if ( BER_BVISEMPTY( &ctrl->ldctl_value ) ) {
1053 		rs->sr_text = "vlv control value is empty";
1054 	}
1055 	if ( rs->sr_text != NULL )
1056 		return rs->sr_err;
1057 
1058 	op->o_ctrlflag[vlv_cid] = ctrl->ldctl_iscritical ?
1059 		SLAP_CONTROL_CRITICAL : SLAP_CONTROL_NONCRITICAL;
1060 
1061 	ber = (BerElement *)&berbuf;
1062 	ber_init2( ber, &ctrl->ldctl_value, 0 );
1063 
1064 	rs->sr_err = LDAP_PROTOCOL_ERROR;
1065 
1066 	tag = ber_scanf( ber, "{ii", &vc2.vc_before, &vc2.vc_after );
1067 	if ( tag == LBER_ERROR ) {
1068 		return rs->sr_err;
1069 	}
1070 
1071 	tag = ber_peek_tag( ber, &len );
1072 	if ( tag == LDAP_VLVBYINDEX_IDENTIFIER ) {
1073 		tag = ber_scanf( ber, "{ii}", &vc2.vc_offset, &vc2.vc_count );
1074 		if ( tag == LBER_ERROR )
1075 			return rs->sr_err;
1076 		BER_BVZERO( &vc2.vc_value );
1077 	} else if ( tag == LDAP_VLVBYVALUE_IDENTIFIER ) {
1078 		tag = ber_scanf( ber, "m", &vc2.vc_value );
1079 		if ( tag == LBER_ERROR || BER_BVISNULL( &vc2.vc_value ))
1080 			return rs->sr_err;
1081 	} else {
1082 		return rs->sr_err;
1083 	}
1084 	tag = ber_peek_tag( ber, &len );
1085 	if ( tag == LDAP_VLVCONTEXT_IDENTIFIER ) {
1086 		struct berval bv;
1087 		tag = ber_scanf( ber, "m", &bv );
1088 		if ( tag == LBER_ERROR || bv.bv_len != sizeof(vc2.vc_context))
1089 			return rs->sr_err;
1090 		AC_MEMCPY( &vc2.vc_context, bv.bv_val, bv.bv_len );
1091 	} else {
1092 		vc2.vc_context = 0;
1093 	}
1094 
1095 	vc = op->o_tmpalloc( sizeof(vlv_ctrl), op->o_tmpmemctx );
1096 	*vc = vc2;
1097 	op->o_controls[vlv_cid] = vc;
1098 	rs->sr_err = LDAP_SUCCESS;
1099 
1100 	return rs->sr_err;
1101 }
1102 
1103 static int sssvlv_connection_destroy( BackendDB *be, Connection *conn )
1104 {
1105 	slap_overinst	*on		= (slap_overinst *)be->bd_info;
1106 
1107 	if ( sort_conns[conn->c_conn_idx] )
1108 		free_sort_op( conn, sort_conns[conn->c_conn_idx] );
1109 
1110 	return LDAP_SUCCESS;
1111 }
1112 
1113 static int sssvlv_db_open(
1114 	BackendDB		*be,
1115 	ConfigReply		*cr )
1116 {
1117 	slap_overinst	*on = (slap_overinst *)be->bd_info;
1118 	sssvlv_info *si = on->on_bi.bi_private;
1119 	int rc;
1120 
1121 	/* If not set, default to 1/2 of available threads */
1122 	if ( !si->svi_max )
1123 		si->svi_max = connection_pool_max / 2;
1124 
1125 	rc = overlay_register_control( be, LDAP_CONTROL_SORTREQUEST );
1126 	if ( rc == LDAP_SUCCESS )
1127 		rc = overlay_register_control( be, LDAP_CONTROL_VLVREQUEST );
1128 	return rc;
1129 }
1130 
1131 static ConfigTable sssvlv_cfg[] = {
1132 	{ "sssvlv-max", "num",
1133 		2, 2, 0, ARG_INT|ARG_OFFSET,
1134 			(void *)offsetof(sssvlv_info, svi_max),
1135 		"( OLcfgOvAt:21.1 NAME 'olcSssVlvMax' "
1136 			"DESC 'Maximum number of concurrent Sort requests' "
1137 			"SYNTAX OMsInteger SINGLE-VALUE )", NULL, NULL },
1138 	{ "sssvlv-maxkeys", "num",
1139 		2, 2, 0, ARG_INT|ARG_OFFSET,
1140 			(void *)offsetof(sssvlv_info, svi_max_keys),
1141 		"( OLcfgOvAt:21.2 NAME 'olcSssVlvMaxKeys' "
1142 			"DESC 'Maximum number of Keys in a Sort request' "
1143 			"SYNTAX OMsInteger SINGLE-VALUE )", NULL, NULL },
1144 	{ NULL, NULL, 0, 0, 0, ARG_IGNORED }
1145 };
1146 
1147 static ConfigOCs sssvlv_ocs[] = {
1148 	{ "( OLcfgOvOc:21.1 "
1149 		"NAME 'olcSssVlvConfig' "
1150 		"DESC 'SSS VLV configuration' "
1151 		"SUP olcOverlayConfig "
1152 		"MAY ( olcSssVlvMax $ olcSssVlvMaxKeys ) )",
1153 		Cft_Overlay, sssvlv_cfg, NULL, NULL },
1154 	{ NULL, 0, NULL }
1155 };
1156 
1157 static int sssvlv_db_init(
1158 	BackendDB		*be,
1159 	ConfigReply		*cr)
1160 {
1161 	slap_overinst	*on = (slap_overinst *)be->bd_info;
1162 	sssvlv_info *si;
1163 
1164 	si = (sssvlv_info *)ch_malloc(sizeof(sssvlv_info));
1165 	on->on_bi.bi_private = si;
1166 
1167 	si->svi_max = 0;
1168 	si->svi_num = 0;
1169 	si->svi_max_keys = SSSVLV_DEFAULT_MAX_KEYS;
1170 
1171 	if ( dtblsize && !sort_conns ) {
1172 		ldap_pvt_thread_mutex_init( &sort_conns_mutex );
1173 		/* accommodate for c_conn_idx == -1 */
1174 		sort_conns = ch_calloc( sizeof(sort_op *), dtblsize + 1 );
1175 		sort_conns++;
1176 	}
1177 	ov_count++;
1178 
1179 	return LDAP_SUCCESS;
1180 }
1181 
1182 static int sssvlv_db_destroy(
1183 	BackendDB		*be,
1184 	ConfigReply		*cr )
1185 {
1186 	slap_overinst	*on = (slap_overinst *)be->bd_info;
1187 	sssvlv_info *si = (sssvlv_info *)on->on_bi.bi_private;
1188 
1189 	ov_count--;
1190 	if ( !ov_count && sort_conns) {
1191 		sort_conns--;
1192 		ch_free(sort_conns);
1193 		ldap_pvt_thread_mutex_destroy( &sort_conns_mutex );
1194 	}
1195 
1196 	if ( si ) {
1197 		ch_free( si );
1198 		on->on_bi.bi_private = NULL;
1199 	}
1200 	return LDAP_SUCCESS;
1201 }
1202 
1203 static slap_overinst sssvlv;
1204 
1205 int sssvlv_initialize()
1206 {
1207 	int rc;
1208 
1209 	sssvlv.on_bi.bi_type				= "sssvlv";
1210 	sssvlv.on_bi.bi_db_init				= sssvlv_db_init;
1211 	sssvlv.on_bi.bi_db_destroy			= sssvlv_db_destroy;
1212 	sssvlv.on_bi.bi_db_open				= sssvlv_db_open;
1213 	sssvlv.on_bi.bi_connection_destroy	= sssvlv_connection_destroy;
1214 	sssvlv.on_bi.bi_op_search			= sssvlv_op_search;
1215 
1216 	sssvlv.on_bi.bi_cf_ocs = sssvlv_ocs;
1217 
1218 	rc = config_register_schema( sssvlv_cfg, sssvlv_ocs );
1219 	if ( rc )
1220 		return rc;
1221 
1222 	rc = register_supported_control2( LDAP_CONTROL_SORTREQUEST,
1223 			SLAP_CTRL_SEARCH,
1224 			NULL,
1225 			sss_parseCtrl,
1226 			1 /* replace */,
1227 			&sss_cid );
1228 
1229 	if ( rc == LDAP_SUCCESS ) {
1230 		rc = register_supported_control2( LDAP_CONTROL_VLVREQUEST,
1231 			SLAP_CTRL_SEARCH,
1232 			NULL,
1233 			vlv_parseCtrl,
1234 			1 /* replace */,
1235 			&vlv_cid );
1236 	}
1237 
1238 	if ( rc == LDAP_SUCCESS ) {
1239 		rc = overlay_register( &sssvlv );
1240 		if ( rc != LDAP_SUCCESS ) {
1241 			Debug( LDAP_DEBUG_ANY, "Failed to register server side sort overlay\n", 0, 0, 0 );
1242 		}
1243 	}
1244 	else {
1245 		Debug( LDAP_DEBUG_ANY, "Failed to register control %d\n", rc, 0, 0 );
1246 	}
1247 
1248 	return rc;
1249 }
1250 
1251 #if SLAPD_OVER_SSSVLV == SLAPD_MOD_DYNAMIC
1252 int init_module( int argc, char *argv[])
1253 {
1254 	return sssvlv_initialize();
1255 }
1256 #endif
1257 
1258 #endif /* SLAPD_OVER_SSSVLV */
1259