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