1 /* $NetBSD: backglue.c,v 1.4 2025/09/05 21:16:25 christos Exp $ */ 2 3 /* backglue.c - backend glue */ 4 /* $OpenLDAP$ */ 5 /* This work is part of OpenLDAP Software <http://www.openldap.org/>. 6 * 7 * Copyright 2001-2024 The OpenLDAP Foundation. 8 * All rights reserved. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted only as authorized by the OpenLDAP 12 * Public License. 13 * 14 * A copy of this license is available in the file LICENSE in the 15 * top-level directory of the distribution or, alternatively, at 16 * <http://www.OpenLDAP.org/license.html>. 17 */ 18 19 /* 20 * Functions to glue a bunch of other backends into a single tree. 21 * All of the glued backends must share a common suffix. E.g., you 22 * can glue o=foo and ou=bar,o=foo but you can't glue o=foo and o=bar. 23 * 24 * The purpose of these functions is to allow you to split a single database 25 * into pieces (for load balancing purposes, whatever) but still be able 26 * to treat it as a single database after it's been split. As such, each 27 * of the glued backends should have identical rootdn. 28 * -- Howard Chu 29 */ 30 31 #include <sys/cdefs.h> 32 __RCSID("$NetBSD: backglue.c,v 1.4 2025/09/05 21:16:25 christos Exp $"); 33 34 #include "portable.h" 35 36 #include <stdio.h> 37 38 #include <ac/string.h> 39 #include <ac/socket.h> 40 41 #define SLAPD_TOOLS 42 #include "slap.h" 43 #include "lutil.h" 44 #include "slap-config.h" 45 46 typedef struct gluenode { 47 BackendDB *gn_be; 48 struct berval gn_pdn; 49 } gluenode; 50 51 typedef struct glueinfo { 52 int gi_nodes; 53 struct berval gi_pdn; 54 gluenode gi_n[1]; 55 } glueinfo; 56 57 static slap_overinst glue; 58 59 static int glueMode; 60 static BackendDB *glueBack; 61 static BackendDB glueBackDone; 62 #define GLUEBACK_DONE (&glueBackDone) 63 64 static slap_overinst * glue_tool_inst( BackendInfo *bi); 65 66 static slap_response glue_op_response; 67 68 /* Just like select_backend, but only for our backends */ 69 static BackendDB * 70 glue_back_select ( 71 BackendDB *be, 72 struct berval *dn 73 ) 74 { 75 slap_overinst *on = (slap_overinst *)be->bd_info; 76 glueinfo *gi = (glueinfo *)on->on_bi.bi_private; 77 int i; 78 79 for (i = gi->gi_nodes-1; i >= 0; i--) { 80 assert( gi->gi_n[i].gn_be->be_nsuffix != NULL ); 81 82 if (dnIsSuffix(dn, &gi->gi_n[i].gn_be->be_nsuffix[0])) { 83 return gi->gi_n[i].gn_be; 84 } 85 } 86 be->bd_info = on->on_info->oi_orig; 87 return be; 88 } 89 90 91 typedef struct glue_state { 92 char *matched; 93 BerVarray refs; 94 LDAPControl **ctrls; 95 int err; 96 int matchlen; 97 int nrefs; 98 int nctrls; 99 } glue_state; 100 101 typedef struct glue_entry { 102 BackendDB *ge_be; 103 BackendInfo *ge_bi; 104 void *ge_orig; 105 } glue_entry; 106 107 static int 108 glue_op_cleanup( Operation *op, SlapReply *rs ) 109 { 110 /* This is not a final result */ 111 if (rs->sr_type == REP_RESULT ) 112 rs->sr_type = REP_GLUE_RESULT; 113 return SLAP_CB_CONTINUE; 114 } 115 116 static int 117 glue_op_response ( Operation *op, SlapReply *rs ) 118 { 119 glue_state *gs = op->o_callback->sc_private; 120 121 switch(rs->sr_type) { 122 case REP_SEARCH: 123 case REP_SEARCHREF: 124 case REP_INTERMEDIATE: 125 return SLAP_CB_CONTINUE; 126 127 default: 128 if (rs->sr_err == LDAP_SUCCESS || 129 rs->sr_err == LDAP_SIZELIMIT_EXCEEDED || 130 rs->sr_err == LDAP_TIMELIMIT_EXCEEDED || 131 rs->sr_err == LDAP_ADMINLIMIT_EXCEEDED || 132 rs->sr_err == LDAP_NO_SUCH_OBJECT || 133 gs->err != LDAP_SUCCESS) 134 gs->err = rs->sr_err; 135 if (gs->err == LDAP_SUCCESS && gs->matched) { 136 ch_free (gs->matched); 137 gs->matched = NULL; 138 gs->matchlen = 0; 139 } 140 if (gs->err != LDAP_SUCCESS && rs->sr_matched) { 141 int len; 142 len = strlen (rs->sr_matched); 143 if (len > gs->matchlen) { 144 if (gs->matched) 145 ch_free (gs->matched); 146 gs->matched = ch_strdup (rs->sr_matched); 147 gs->matchlen = len; 148 } 149 } 150 if (rs->sr_ref) { 151 int i, j, k; 152 BerVarray new; 153 154 for (i=0; rs->sr_ref[i].bv_val; i++); 155 156 j = gs->nrefs; 157 if (!j) { 158 new = ch_malloc ((i+1)*sizeof(struct berval)); 159 } else { 160 new = ch_realloc(gs->refs, 161 (j+i+1)*sizeof(struct berval)); 162 } 163 for (k=0; k<i; j++,k++) { 164 ber_dupbv( &new[j], &rs->sr_ref[k] ); 165 } 166 new[j].bv_val = NULL; 167 gs->nrefs = j; 168 gs->refs = new; 169 } 170 if (rs->sr_ctrls) { 171 int i, j, k; 172 LDAPControl **newctrls; 173 174 for (i=0; rs->sr_ctrls[i]; i++); 175 176 j = gs->nctrls; 177 if (!j) { 178 newctrls = op->o_tmpalloc((i+1)*sizeof(LDAPControl *), 179 op->o_tmpmemctx); 180 } else { 181 /* Forget old pagedResults response if we're sending 182 * a new one now 183 */ 184 if ( get_pagedresults( op ) > SLAP_CONTROL_IGNORED ) { 185 int newpage = 0; 186 for ( k=0; k<i; k++ ) { 187 if ( !strcmp(rs->sr_ctrls[k]->ldctl_oid, 188 LDAP_CONTROL_PAGEDRESULTS )) { 189 newpage = 1; 190 break; 191 } 192 } 193 if ( newpage ) { 194 for ( k=0; k<j; k++ ) { 195 if ( !strcmp(gs->ctrls[k]->ldctl_oid, 196 LDAP_CONTROL_PAGEDRESULTS )) 197 { 198 op->o_tmpfree(gs->ctrls[k], op->o_tmpmemctx); 199 gs->ctrls[k] = gs->ctrls[--j]; 200 gs->ctrls[j] = NULL; 201 break; 202 } 203 } 204 } 205 } 206 newctrls = op->o_tmprealloc(gs->ctrls, 207 (j+i+1)*sizeof(LDAPControl *), op->o_tmpmemctx); 208 } 209 for (k=0; k<i; j++,k++) { 210 ber_len_t oidlen = strlen( rs->sr_ctrls[k]->ldctl_oid ); 211 newctrls[j] = op->o_tmpalloc(sizeof(LDAPControl) + oidlen + 1 + rs->sr_ctrls[k]->ldctl_value.bv_len + 1, 212 op->o_tmpmemctx); 213 newctrls[j]->ldctl_iscritical = rs->sr_ctrls[k]->ldctl_iscritical; 214 newctrls[j]->ldctl_oid = (char *)&newctrls[j][1]; 215 lutil_strcopy( newctrls[j]->ldctl_oid, rs->sr_ctrls[k]->ldctl_oid ); 216 if ( !BER_BVISNULL( &rs->sr_ctrls[k]->ldctl_value ) ) { 217 newctrls[j]->ldctl_value.bv_val = &newctrls[j]->ldctl_oid[oidlen + 1]; 218 newctrls[j]->ldctl_value.bv_len = rs->sr_ctrls[k]->ldctl_value.bv_len; 219 lutil_memcopy( newctrls[j]->ldctl_value.bv_val, 220 rs->sr_ctrls[k]->ldctl_value.bv_val, 221 rs->sr_ctrls[k]->ldctl_value.bv_len + 1 ); 222 } else { 223 BER_BVZERO( &newctrls[j]->ldctl_value ); 224 } 225 } 226 newctrls[j] = NULL; 227 gs->nctrls = j; 228 gs->ctrls = newctrls; 229 } 230 } 231 return 0; 232 } 233 234 static int 235 glue_op_func ( Operation *op, SlapReply *rs ) 236 { 237 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info; 238 BackendDB *b0 = op->o_bd; 239 BackendInfo *bi0 = op->o_bd->bd_info, *bi1; 240 slap_operation_t which = op_bind; 241 int rc; 242 243 op->o_bd = glue_back_select (b0, &op->o_req_ndn); 244 245 /* If we're on the primary backend, let overlay framework handle it */ 246 if ( op->o_bd == b0 ) 247 return SLAP_CB_CONTINUE; 248 249 b0->bd_info = on->on_info->oi_orig; 250 251 switch(op->o_tag) { 252 case LDAP_REQ_ADD: which = op_add; break; 253 case LDAP_REQ_DELETE: which = op_delete; break; 254 case LDAP_REQ_MODIFY: which = op_modify; break; 255 case LDAP_REQ_MODRDN: which = op_modrdn; break; 256 case LDAP_REQ_EXTENDED: which = op_extended; break; 257 default: assert( 0 ); break; 258 } 259 260 bi1 = op->o_bd->bd_info; 261 rc = (&bi1->bi_op_bind)[ which ] ? 262 (&bi1->bi_op_bind)[ which ]( op, rs ) : SLAP_CB_BYPASS; 263 264 op->o_bd = b0; 265 op->o_bd->bd_info = bi0; 266 return rc; 267 } 268 269 static int 270 glue_op_abandon( Operation *op, SlapReply *rs ) 271 { 272 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info; 273 glueinfo *gi = (glueinfo *)on->on_bi.bi_private; 274 BackendDB *b0 = op->o_bd; 275 BackendInfo *bi0 = op->o_bd->bd_info; 276 int i; 277 278 b0->bd_info = on->on_info->oi_orig; 279 280 for (i = gi->gi_nodes-1; i >= 0; i--) { 281 assert( gi->gi_n[i].gn_be->be_nsuffix != NULL ); 282 op->o_bd = gi->gi_n[i].gn_be; 283 if ( op->o_bd == b0 ) 284 continue; 285 if ( op->o_bd->bd_info->bi_op_abandon ) 286 op->o_bd->bd_info->bi_op_abandon( op, rs ); 287 } 288 op->o_bd = b0; 289 op->o_bd->bd_info = bi0; 290 return SLAP_CB_CONTINUE; 291 } 292 293 static int 294 glue_response ( Operation *op, SlapReply *rs ) 295 { 296 BackendDB *be = op->o_bd; 297 be = glue_back_select (op->o_bd, &op->o_req_ndn); 298 299 /* If we're on the primary backend, let overlay framework handle it. 300 * Otherwise, bail out. 301 */ 302 return ( op->o_bd == be ) ? SLAP_CB_CONTINUE : SLAP_CB_BYPASS; 303 } 304 305 static int 306 glue_chk_referrals ( Operation *op, SlapReply *rs ) 307 { 308 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info; 309 BackendDB *b0 = op->o_bd; 310 BackendInfo *bi0 = op->o_bd->bd_info; 311 int rc; 312 313 op->o_bd = glue_back_select (b0, &op->o_req_ndn); 314 if ( op->o_bd == b0 ) 315 return SLAP_CB_CONTINUE; 316 317 b0->bd_info = on->on_info->oi_orig; 318 319 if ( op->o_bd->bd_info->bi_chk_referrals ) 320 rc = ( *op->o_bd->bd_info->bi_chk_referrals )( op, rs ); 321 else 322 rc = SLAP_CB_CONTINUE; 323 324 op->o_bd = b0; 325 op->o_bd->bd_info = bi0; 326 return rc; 327 } 328 329 static int 330 glue_chk_controls ( Operation *op, SlapReply *rs ) 331 { 332 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info; 333 BackendDB *b0 = op->o_bd; 334 BackendInfo *bi0 = op->o_bd->bd_info; 335 int rc = SLAP_CB_CONTINUE; 336 337 op->o_bd = glue_back_select (b0, &op->o_req_ndn); 338 if ( op->o_bd == b0 ) 339 return SLAP_CB_CONTINUE; 340 341 b0->bd_info = on->on_info->oi_orig; 342 343 /* if the subordinate database has overlays, the bi_chk_controls() 344 * hook is actually over_aux_chk_controls(); in case it actually 345 * wraps a missing hok, we need to mimic the behavior 346 * of the frontend applied to that database */ 347 if ( op->o_bd->bd_info->bi_chk_controls ) { 348 rc = ( *op->o_bd->bd_info->bi_chk_controls )( op, rs ); 349 } 350 351 352 if ( rc == SLAP_CB_CONTINUE ) { 353 rc = backend_check_controls( op, rs ); 354 } 355 356 op->o_bd = b0; 357 op->o_bd->bd_info = bi0; 358 return rc; 359 } 360 361 /* ITS#4615 - overlays configured above the glue overlay should be 362 * invoked for the entire glued tree. Overlays configured below the 363 * glue overlay should only be invoked on the primary backend. 364 * So, if we're searching on any subordinates, we need to force the 365 * current overlay chain to stop processing, without stopping the 366 * overall callback flow. 367 */ 368 static int 369 glue_sub_search( Operation *op, SlapReply *rs, BackendDB *b0, 370 slap_overinst *on ) 371 { 372 /* Process any overlays on the primary backend */ 373 if ( op->o_bd == b0 && on->on_next ) { 374 BackendInfo *bi = op->o_bd->bd_info; 375 int rc = SLAP_CB_CONTINUE; 376 for ( on=on->on_next; on; on=on->on_next ) { 377 if ( on->on_bi.bi_flags & SLAPO_BFLAG_DISABLED ) 378 continue; 379 op->o_bd->bd_info = (BackendInfo *)on; 380 if ( on->on_bi.bi_op_search ) { 381 rc = on->on_bi.bi_op_search( op, rs ); 382 if ( rc != SLAP_CB_CONTINUE ) 383 break; 384 } 385 } 386 op->o_bd->bd_info = bi; 387 if ( rc != SLAP_CB_CONTINUE ) 388 return rc; 389 } 390 return op->o_bd->be_search( op, rs ); 391 } 392 393 static const ID glueID = NOID; 394 static const struct berval gluecookie = { sizeof( glueID ), (char *)&glueID }; 395 396 static int 397 glue_op_search ( Operation *op, SlapReply *rs ) 398 { 399 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info; 400 glueinfo *gi = (glueinfo *)on->on_bi.bi_private; 401 BackendDB *b0 = op->o_bd; 402 BackendDB *b1 = NULL, *btmp; 403 BackendInfo *bi0 = op->o_bd->bd_info; 404 int i; 405 long stoptime = 0, starttime; 406 glue_state gs = {NULL, NULL, NULL, 0, 0, 0, 0}; 407 slap_callback cb = { NULL, glue_op_response, glue_op_cleanup, NULL }; 408 int scope0, tlimit0; 409 struct berval dn, ndn, *pdn; 410 411 cb.sc_private = &gs; 412 413 cb.sc_next = op->o_callback; 414 415 starttime = op->o_time; 416 stoptime = slap_get_time () + op->ors_tlimit; 417 418 /* reset dummy cookie used to keep paged results going across databases */ 419 if ( get_pagedresults( op ) > SLAP_CONTROL_IGNORED 420 && bvmatch( &((PagedResultsState *)op->o_pagedresults_state)->ps_cookieval, &gluecookie ) ) 421 { 422 PagedResultsState *ps = op->o_pagedresults_state; 423 BerElementBuffer berbuf; 424 BerElement *ber = (BerElement *)&berbuf; 425 struct berval cookie = BER_BVC(""), value; 426 int c; 427 428 for (c = 0; op->o_ctrls[c] != NULL; c++) { 429 if (strcmp(op->o_ctrls[c]->ldctl_oid, LDAP_CONTROL_PAGEDRESULTS) == 0) 430 break; 431 } 432 433 assert( op->o_ctrls[c] != NULL ); 434 435 ber_init2( ber, NULL, LBER_USE_DER ); 436 ber_printf( ber, "{iO}", ps->ps_size, &cookie ); 437 ber_flatten2( ber, &value, 0 ); 438 assert( op->o_ctrls[c]->ldctl_value.bv_len >= value.bv_len ); 439 op->o_ctrls[c]->ldctl_value.bv_len = value.bv_len; 440 lutil_memcopy( op->o_ctrls[c]->ldctl_value.bv_val, 441 value.bv_val, value.bv_len ); 442 ber_free_buf( ber ); 443 444 ps->ps_cookie = (PagedResultsCookie)0; 445 BER_BVZERO( &ps->ps_cookieval ); 446 } 447 448 op->o_bd = glue_back_select (b0, &op->o_req_ndn); 449 b0->bd_info = on->on_info->oi_orig; 450 451 switch (op->ors_scope) { 452 case LDAP_SCOPE_BASE: 453 if ( op->o_bd == b0 ) 454 return SLAP_CB_CONTINUE; 455 456 if (op->o_bd && op->o_bd->be_search) { 457 rs->sr_err = op->o_bd->be_search( op, rs ); 458 } else { 459 rs->sr_err = LDAP_UNWILLING_TO_PERFORM; 460 } 461 return rs->sr_err; 462 463 case LDAP_SCOPE_ONELEVEL: 464 case LDAP_SCOPE_SUBTREE: 465 case LDAP_SCOPE_SUBORDINATE: /* FIXME */ 466 op->o_callback = &cb; 467 rs->sr_err = gs.err = LDAP_UNWILLING_TO_PERFORM; 468 scope0 = op->ors_scope; 469 tlimit0 = op->ors_tlimit; 470 dn = op->o_req_dn; 471 ndn = op->o_req_ndn; 472 b1 = op->o_bd; 473 474 /* 475 * Execute in reverse order, most specific first 476 */ 477 for (i = gi->gi_nodes; i >= 0; i--) { 478 if ( i == gi->gi_nodes ) { 479 btmp = b0; 480 pdn = &gi->gi_pdn; 481 } else { 482 btmp = gi->gi_n[i].gn_be; 483 pdn = &gi->gi_n[i].gn_pdn; 484 } 485 if (!btmp || !btmp->be_search) 486 continue; 487 if (!dnIsSuffix(&btmp->be_nsuffix[0], &b1->be_nsuffix[0])) 488 continue; 489 if (get_no_subordinate_glue(op) && btmp != b1) 490 continue; 491 /* If we remembered which backend we were on before, 492 * skip down to it now 493 */ 494 if ( get_pagedresults( op ) > SLAP_CONTROL_IGNORED && 495 op->o_conn->c_pagedresults_state.ps_be && 496 op->o_conn->c_pagedresults_state.ps_be != btmp ) 497 continue; 498 499 if (tlimit0 != SLAP_NO_LIMIT) { 500 op->o_time = slap_get_time(); 501 op->ors_tlimit = stoptime - op->o_time; 502 if (op->ors_tlimit <= 0) { 503 rs->sr_err = gs.err = LDAP_TIMELIMIT_EXCEEDED; 504 break; 505 } 506 } 507 rs->sr_err = 0; 508 /* 509 * check for abandon 510 */ 511 if (op->o_abandon) { 512 goto end_of_loop; 513 } 514 op->o_bd = btmp; 515 516 assert( op->o_bd->be_suffix != NULL ); 517 assert( op->o_bd->be_nsuffix != NULL ); 518 519 if (scope0 == LDAP_SCOPE_ONELEVEL && 520 dn_match(pdn, &ndn)) 521 { 522 struct berval mdn, mndn; 523 op->ors_scope = LDAP_SCOPE_BASE; 524 mdn = op->o_req_dn = op->o_bd->be_suffix[0]; 525 mndn = op->o_req_ndn = op->o_bd->be_nsuffix[0]; 526 rs->sr_err = op->o_bd->be_search(op, rs); 527 if ( rs->sr_err == LDAP_NO_SUCH_OBJECT ) { 528 gs.err = LDAP_SUCCESS; 529 } 530 op->ors_scope = LDAP_SCOPE_ONELEVEL; 531 if ( op->o_req_dn.bv_val == mdn.bv_val ) 532 op->o_req_dn = dn; 533 if ( op->o_req_ndn.bv_val == mndn.bv_val ) 534 op->o_req_ndn = ndn; 535 536 } else if (scope0 == LDAP_SCOPE_SUBTREE && 537 dn_match(&op->o_bd->be_nsuffix[0], &ndn)) 538 { 539 rs->sr_err = glue_sub_search( op, rs, b0, on ); 540 541 } else if (scope0 == LDAP_SCOPE_SUBTREE && 542 dnIsSuffix(&op->o_bd->be_nsuffix[0], &ndn)) 543 { 544 struct berval mdn, mndn; 545 mdn = op->o_req_dn = op->o_bd->be_suffix[0]; 546 mndn = op->o_req_ndn = op->o_bd->be_nsuffix[0]; 547 rs->sr_err = glue_sub_search( op, rs, b0, on ); 548 if ( rs->sr_err == LDAP_NO_SUCH_OBJECT ) { 549 gs.err = LDAP_SUCCESS; 550 } 551 if ( op->o_req_dn.bv_val == mdn.bv_val ) 552 op->o_req_dn = dn; 553 if ( op->o_req_ndn.bv_val == mndn.bv_val ) 554 op->o_req_ndn = ndn; 555 556 } else if (dnIsSuffix(&ndn, &op->o_bd->be_nsuffix[0])) { 557 rs->sr_err = glue_sub_search( op, rs, b0, on ); 558 } 559 560 if ( rs->sr_err == SLAPD_NO_REPLY ) { 561 gs.err = rs->sr_err; 562 break; 563 } 564 565 switch ( gs.err ) { 566 567 /* 568 * Add errors that should result in dropping 569 * the search 570 */ 571 case LDAP_SIZELIMIT_EXCEEDED: 572 case LDAP_TIMELIMIT_EXCEEDED: 573 case LDAP_ADMINLIMIT_EXCEEDED: 574 case LDAP_NO_SUCH_OBJECT: 575 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR 576 case LDAP_X_CANNOT_CHAIN: 577 #endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */ 578 goto end_of_loop; 579 580 case LDAP_SUCCESS: 581 if ( get_pagedresults( op ) > SLAP_CONTROL_IGNORED ) { 582 PagedResultsState *ps = op->o_pagedresults_state; 583 584 /* Assume this backend can be forgotten now */ 585 op->o_conn->c_pagedresults_state.ps_be = NULL; 586 587 /* If we have a full page, exit the loop. We may 588 * need to remember this backend so we can continue 589 * from here on a subsequent request. 590 */ 591 if ( rs->sr_nentries >= ps->ps_size ) { 592 PagedResultsState *cps = &op->o_conn->c_pagedresults_state; 593 594 /* Don't bother to remember the first backend. 595 * Only remember the last one if there's more state left. 596 */ 597 if ( op->o_bd != b0 && 598 ( cps->ps_cookie != NOID 599 || !BER_BVISNULL( &cps->ps_cookieval ) 600 || op->o_bd != gi->gi_n[0].gn_be ) ) 601 { 602 op->o_conn->c_pagedresults_state.ps_be = op->o_bd; 603 } 604 605 /* Check whether the cookie is empty, 606 * and give remaining databases a chance 607 */ 608 if ( op->o_bd != gi->gi_n[0].gn_be || cps->ps_cookie == NOID ) { 609 int c; 610 611 for ( c = 0; gs.ctrls[c] != NULL; c++ ) { 612 if ( strcmp( gs.ctrls[c]->ldctl_oid, LDAP_CONTROL_PAGEDRESULTS ) == 0 ) { 613 break; 614 } 615 } 616 617 if ( gs.ctrls[c] != NULL ) { 618 BerElementBuffer berbuf; 619 BerElement *ber = (BerElement *)&berbuf; 620 ber_tag_t tag; 621 ber_int_t size; 622 struct berval cookie, value; 623 624 ber_init2( ber, &gs.ctrls[c]->ldctl_value, LBER_USE_DER ); 625 626 tag = ber_scanf( ber, "{im}", &size, &cookie ); 627 assert( tag != LBER_ERROR ); 628 629 if ( BER_BVISEMPTY( &cookie ) && op->o_bd != gi->gi_n[0].gn_be ) { 630 /* delete old, create new cookie with NOID */ 631 PagedResultsCookie respcookie = (PagedResultsCookie)NOID; 632 ber_len_t oidlen = strlen( gs.ctrls[c]->ldctl_oid ); 633 LDAPControl *newctrl; 634 635 /* it's next database's turn */ 636 if ( btmp == b0 ) { 637 op->o_conn->c_pagedresults_state.ps_be = gi->gi_n[gi->gi_nodes - 1].gn_be; 638 639 } else { 640 op->o_conn->c_pagedresults_state.ps_be = gi->gi_n[(i > 0 ? i - 1: 0)].gn_be; 641 } 642 643 cookie.bv_val = (char *)&respcookie; 644 cookie.bv_len = sizeof( PagedResultsCookie ); 645 646 ber_init2( ber, NULL, LBER_USE_DER ); 647 ber_printf( ber, "{iO}", 0, &cookie ); 648 ber_flatten2( ber, &value, 0 ); 649 650 newctrl = op->o_tmprealloc( gs.ctrls[c], 651 sizeof(LDAPControl) + oidlen + 1 + value.bv_len + 1, 652 op->o_tmpmemctx); 653 newctrl->ldctl_iscritical = gs.ctrls[c]->ldctl_iscritical; 654 newctrl->ldctl_oid = (char *)&newctrl[1]; 655 lutil_strcopy( newctrl->ldctl_oid, gs.ctrls[c]->ldctl_oid ); 656 newctrl->ldctl_value.bv_len = value.bv_len; 657 lutil_memcopy( newctrl->ldctl_value.bv_val, 658 value.bv_val, value.bv_len ); 659 660 gs.ctrls[c] = newctrl; 661 662 ber_free_buf( ber ); 663 664 } else if ( !BER_BVISEMPTY( &cookie ) && op->o_bd != b0 ) { 665 /* if cookie not empty, it's again this database's turn */ 666 op->o_conn->c_pagedresults_state.ps_be = op->o_bd; 667 } 668 } 669 } 670 671 goto end_of_loop; 672 } 673 674 /* This backend has run out of entries, but more responses 675 * can fit in the page. Fake a reset of the state so the 676 * next backend will start up properly. Only back-[bh]db 677 * and back-sql look at this state info. 678 */ 679 ps->ps_cookie = (PagedResultsCookie)0; 680 BER_BVZERO( &ps->ps_cookieval ); 681 682 { 683 /* change the size of the page in the request 684 * that will be propagated, and reset the cookie */ 685 BerElementBuffer berbuf; 686 BerElement *ber = (BerElement *)&berbuf; 687 int size = ps->ps_size - rs->sr_nentries; 688 struct berval cookie = BER_BVC(""), value; 689 int c; 690 691 for (c = 0; op->o_ctrls[c] != NULL; c++) { 692 if (strcmp(op->o_ctrls[c]->ldctl_oid, LDAP_CONTROL_PAGEDRESULTS) == 0) 693 break; 694 } 695 696 assert( op->o_ctrls[c] != NULL ); 697 698 ber_init2( ber, NULL, LBER_USE_DER ); 699 ber_printf( ber, "{iO}", size, &cookie ); 700 ber_flatten2( ber, &value, 0 ); 701 assert( op->o_ctrls[c]->ldctl_value.bv_len >= value.bv_len ); 702 op->o_ctrls[c]->ldctl_value.bv_len = value.bv_len; 703 lutil_memcopy( op->o_ctrls[c]->ldctl_value.bv_val, 704 value.bv_val, value.bv_len ); 705 ber_free_buf( ber ); 706 } 707 } 708 709 default: 710 break; 711 } 712 } 713 end_of_loop:; 714 op->ors_scope = scope0; 715 op->ors_tlimit = tlimit0; 716 op->o_time = starttime; 717 718 break; 719 } 720 721 op->o_callback = cb.sc_next; 722 if ( op->o_abandon ) { 723 rs->sr_err = SLAPD_ABANDON; 724 } else { 725 rs->sr_err = gs.err; 726 rs->sr_matched = gs.matched; 727 rs->sr_ref = gs.refs; 728 } 729 rs->sr_ctrls = gs.ctrls; 730 731 if ( rs->sr_err != SLAPD_NO_REPLY ) 732 send_ldap_result( op, rs ); 733 734 op->o_bd = b0; 735 op->o_bd->bd_info = bi0; 736 if (gs.matched) 737 free (gs.matched); 738 if (gs.refs) 739 ber_bvarray_free(gs.refs); 740 if (gs.ctrls) { 741 for (i = gs.nctrls; --i >= 0; ) { 742 op->o_tmpfree(gs.ctrls[i], op->o_tmpmemctx); 743 } 744 op->o_tmpfree(gs.ctrls, op->o_tmpmemctx); 745 } 746 return rs->sr_err; 747 } 748 749 static BackendDB toolDB; 750 751 static int 752 glue_tool_entry_open ( 753 BackendDB *b0, 754 int mode 755 ) 756 { 757 slap_overinfo *oi = (slap_overinfo *)b0->bd_info; 758 759 /* We don't know which backend to talk to yet, so just 760 * remember the mode and move on... 761 */ 762 763 glueMode = mode; 764 glueBack = NULL; 765 toolDB = *b0; 766 toolDB.bd_info = oi->oi_orig; 767 768 /* Sanity checks */ 769 { 770 slap_overinst *on = glue_tool_inst( b0->bd_info ); 771 glueinfo *gi = on->on_bi.bi_private; 772 773 int i; 774 for (i = 0; i < gi->gi_nodes; i++) { 775 BackendDB *bd; 776 struct berval pdn; 777 778 dnParent( &gi->gi_n[i].gn_be->be_nsuffix[0], &pdn ); 779 bd = select_backend( &pdn, 0 ); 780 if ( bd ) { 781 ID id; 782 BackendDB db; 783 784 if ( overlay_is_over( bd ) ) { 785 slap_overinfo *oi = (slap_overinfo *)bd->bd_info; 786 db = *bd; 787 db.bd_info = oi->oi_orig; 788 bd = &db; 789 } 790 791 if ( !bd->bd_info->bi_tool_dn2id_get 792 || !bd->bd_info->bi_tool_entry_open 793 || !bd->bd_info->bi_tool_entry_close ) 794 { 795 continue; 796 } 797 798 bd->bd_info->bi_tool_entry_open( bd, 0 ); 799 id = bd->bd_info->bi_tool_dn2id_get( bd, &gi->gi_n[i].gn_be->be_nsuffix[0] ); 800 bd->bd_info->bi_tool_entry_close( bd ); 801 if ( id != NOID ) { 802 Debug( LDAP_DEBUG_ANY, 803 "glue_tool_entry_open: subordinate database suffix entry DN=\"%s\" also present in superior database rooted at DN=\"%s\"\n", 804 gi->gi_n[i].gn_be->be_suffix[0].bv_val, bd->be_suffix[0].bv_val ); 805 return LDAP_OTHER; 806 } 807 } 808 } 809 } 810 811 return 0; 812 } 813 814 static int 815 glue_tool_entry_close ( 816 BackendDB *b0 817 ) 818 { 819 int rc = 0; 820 821 if (glueBack && glueBack != GLUEBACK_DONE) { 822 if (!glueBack->be_entry_close) 823 return 0; 824 rc = glueBack->be_entry_close (glueBack); 825 } 826 return rc; 827 } 828 829 static slap_overinst * 830 glue_tool_inst( 831 BackendInfo *bi 832 ) 833 { 834 slap_overinfo *oi = (slap_overinfo *)bi; 835 slap_overinst *on; 836 837 for ( on = oi->oi_list; on; on=on->on_next ) { 838 if ( !strcmp( on->on_bi.bi_type, glue.on_bi.bi_type )) 839 return on; 840 } 841 return NULL; 842 } 843 844 /* This function will only be called in tool mode */ 845 static int 846 glue_open ( 847 BackendInfo *bi 848 ) 849 { 850 slap_overinst *on = glue_tool_inst( bi ); 851 glueinfo *gi = on->on_bi.bi_private; 852 static int glueOpened = 0; 853 int i, j, same, bsame = 0, rc = 0; 854 ConfigReply cr = {0}; 855 856 if (glueOpened) return 0; 857 858 glueOpened = 1; 859 860 /* If we were invoked in tool mode, open all the underlying backends */ 861 if (slapMode & SLAP_TOOL_MODE) { 862 for (i = 0; i<gi->gi_nodes; i++) { 863 same = 0; 864 /* Same bi_open as our main backend? */ 865 if ( gi->gi_n[i].gn_be->bd_info->bi_open == 866 on->on_info->oi_orig->bi_open ) 867 bsame = 1; 868 869 /* Loop thru the bd_info's and make sure we only 870 * invoke their bi_open functions once each. 871 */ 872 for ( j = 0; j<i; j++ ) { 873 if ( gi->gi_n[i].gn_be->bd_info->bi_open == 874 gi->gi_n[j].gn_be->bd_info->bi_open ) { 875 same = 1; 876 break; 877 } 878 } 879 /* OK, it's unique and non-NULL, call it. */ 880 if ( !same && gi->gi_n[i].gn_be->bd_info->bi_open ) 881 rc = gi->gi_n[i].gn_be->bd_info->bi_open( 882 gi->gi_n[i].gn_be->bd_info ); 883 /* Let backend.c take care of the rest of startup */ 884 if ( !rc ) 885 rc = backend_startup_one( gi->gi_n[i].gn_be, &cr ); 886 if ( rc ) break; 887 } 888 if ( !rc && !bsame && on->on_info->oi_orig->bi_open ) 889 rc = on->on_info->oi_orig->bi_open( on->on_info->oi_orig ); 890 891 } /* other case is impossible */ 892 return rc; 893 } 894 895 /* This function will only be called in tool mode */ 896 static int 897 glue_close ( 898 BackendInfo *bi 899 ) 900 { 901 static int glueClosed = 0; 902 int rc = 0; 903 904 if (glueClosed) return 0; 905 906 glueClosed = 1; 907 908 if (slapMode & SLAP_TOOL_MODE) { 909 rc = backend_shutdown( NULL ); 910 } 911 return rc; 912 } 913 914 static int 915 glue_entry_get_rw ( 916 Operation *op, 917 struct berval *dn, 918 ObjectClass *oc, 919 AttributeDescription *ad, 920 int rw, 921 Entry **e ) 922 { 923 int rc; 924 BackendDB *b0 = op->o_bd; 925 op->o_bd = glue_back_select( b0, dn ); 926 927 if ( op->o_bd->be_fetch ) { 928 rc = op->o_bd->be_fetch( op, dn, oc, ad, rw, e ); 929 if ( rc == LDAP_SUCCESS && *e ) { 930 glue_entry *ge = op->o_tmpcalloc( 1, sizeof(glue_entry), 931 op->o_tmpmemctx ); 932 933 if ( !ge ) { 934 if ( op->o_bd->be_release ) { 935 op->o_bd->be_release( op, *e, rw ); 936 } else { 937 entry_free( *e ); 938 } 939 rc = LDAP_OTHER; 940 goto out; 941 } 942 943 /* b0 is on overlay_entry_get_ov's stack, we'll be passed a fresh 944 * one at release time */ 945 if ( op->o_bd != b0 ) { 946 ge->ge_be = op->o_bd; 947 } 948 ge->ge_bi = op->o_bd->bd_info; 949 ge->ge_orig = (*e)->e_private; 950 (*e)->e_private = ge; 951 } 952 } else { 953 rc = LDAP_UNWILLING_TO_PERFORM; 954 } 955 956 out: 957 op->o_bd = b0; 958 return rc; 959 } 960 961 static int 962 glue_entry_release_rw ( 963 Operation *op, 964 Entry *e, 965 int rw 966 ) 967 { 968 glue_entry *ge = e->e_private; 969 BackendDB *b0 = op->o_bd; 970 int rc = -1; 971 972 if ( glueBack ) { 973 op->o_bd = glueBack; 974 } else if ( ge ) { 975 assert( ge->ge_bi != NULL ); 976 if ( ge->ge_be ) 977 op->o_bd = ge->ge_be; 978 op->o_bd->bd_info = ge->ge_bi; 979 e->e_private = ge->ge_orig; 980 op->o_tmpfree( ge, op->o_tmpmemctx ); 981 } else { 982 op->o_bd = glue_back_select( b0, &e->e_nname ); 983 } 984 985 if ( op->o_bd->be_release ) { 986 rc = op->o_bd->be_release( op, e, rw ); 987 988 } else { 989 /* FIXME: mimic be_entry_release_rw 990 * when no be_release() available */ 991 /* free entry */ 992 entry_free( e ); 993 rc = 0; 994 } 995 op->o_bd = b0; 996 return rc; 997 } 998 999 static struct berval *glue_base; 1000 static int glue_scope; 1001 static Filter *glue_filter; 1002 1003 static ID 1004 glue_tool_entry_first ( 1005 BackendDB *b0 1006 ) 1007 { 1008 slap_overinst *on = glue_tool_inst( b0->bd_info ); 1009 glueinfo *gi = on->on_bi.bi_private; 1010 int i; 1011 ID rc; 1012 1013 /* If we're starting from scratch, start at the most general */ 1014 if (!glueBack) { 1015 if ( toolDB.be_entry_open && toolDB.be_entry_first ) { 1016 glueBack = &toolDB; 1017 } else { 1018 for (i = gi->gi_nodes-1; i >= 0; i--) { 1019 if (gi->gi_n[i].gn_be->be_entry_open && 1020 gi->gi_n[i].gn_be->be_entry_first) { 1021 glueBack = gi->gi_n[i].gn_be; 1022 break; 1023 } 1024 } 1025 } 1026 } 1027 if (!glueBack || !glueBack->be_entry_open || !glueBack->be_entry_first || 1028 glueBack->be_entry_open (glueBack, glueMode) != 0) 1029 return NOID; 1030 1031 rc = glueBack->be_entry_first (glueBack); 1032 while ( rc == NOID ) { 1033 if ( glueBack && glueBack->be_entry_close ) 1034 glueBack->be_entry_close (glueBack); 1035 for (i=0; i<gi->gi_nodes; i++) { 1036 if (gi->gi_n[i].gn_be == glueBack) 1037 break; 1038 } 1039 if (i == 0) { 1040 glueBack = GLUEBACK_DONE; 1041 break; 1042 } else { 1043 glueBack = gi->gi_n[i-1].gn_be; 1044 rc = glue_tool_entry_first (b0); 1045 if ( glueBack == GLUEBACK_DONE ) { 1046 break; 1047 } 1048 } 1049 } 1050 return rc; 1051 } 1052 1053 static ID 1054 glue_tool_entry_first_x ( 1055 BackendDB *b0, 1056 struct berval *base, 1057 int scope, 1058 Filter *f 1059 ) 1060 { 1061 slap_overinst *on = glue_tool_inst( b0->bd_info ); 1062 glueinfo *gi = on->on_bi.bi_private; 1063 int i; 1064 ID rc; 1065 1066 glue_base = base; 1067 glue_scope = scope; 1068 glue_filter = f; 1069 1070 /* If we're starting from scratch, start at the most general */ 1071 if (!glueBack) { 1072 if ( toolDB.be_entry_open && toolDB.be_entry_first_x ) { 1073 glueBack = &toolDB; 1074 } else { 1075 for (i = gi->gi_nodes-1; i >= 0; i--) { 1076 if (gi->gi_n[i].gn_be->be_entry_open && 1077 gi->gi_n[i].gn_be->be_entry_first_x) 1078 { 1079 glueBack = gi->gi_n[i].gn_be; 1080 break; 1081 } 1082 } 1083 } 1084 } 1085 if (!glueBack || !glueBack->be_entry_open || !glueBack->be_entry_first_x || 1086 glueBack->be_entry_open (glueBack, glueMode) != 0) 1087 return NOID; 1088 1089 rc = glueBack->be_entry_first_x (glueBack, 1090 glue_base, glue_scope, glue_filter); 1091 while ( rc == NOID ) { 1092 if ( glueBack && glueBack->be_entry_close ) 1093 glueBack->be_entry_close (glueBack); 1094 for (i=0; i<gi->gi_nodes; i++) { 1095 if (gi->gi_n[i].gn_be == glueBack) 1096 break; 1097 } 1098 if (i == 0) { 1099 glueBack = GLUEBACK_DONE; 1100 break; 1101 } else { 1102 glueBack = gi->gi_n[i-1].gn_be; 1103 rc = glue_tool_entry_first_x (b0, 1104 glue_base, glue_scope, glue_filter); 1105 if ( glueBack == GLUEBACK_DONE ) { 1106 break; 1107 } 1108 } 1109 } 1110 return rc; 1111 } 1112 1113 static ID 1114 glue_tool_entry_next ( 1115 BackendDB *b0 1116 ) 1117 { 1118 slap_overinst *on = glue_tool_inst( b0->bd_info ); 1119 glueinfo *gi = on->on_bi.bi_private; 1120 int i; 1121 ID rc; 1122 1123 if (!glueBack || !glueBack->be_entry_next) 1124 return NOID; 1125 1126 rc = glueBack->be_entry_next (glueBack); 1127 1128 /* If we ran out of entries in one database, move on to the next */ 1129 while (rc == NOID) { 1130 if ( glueBack && glueBack->be_entry_close ) 1131 glueBack->be_entry_close (glueBack); 1132 for (i=0; i<gi->gi_nodes; i++) { 1133 if (gi->gi_n[i].gn_be == glueBack) 1134 break; 1135 } 1136 if (i == 0) { 1137 glueBack = GLUEBACK_DONE; 1138 break; 1139 } else { 1140 glueBack = gi->gi_n[i-1].gn_be; 1141 if ( glue_base || glue_filter ) { 1142 /* using entry_first_x() */ 1143 rc = glue_tool_entry_first_x (b0, 1144 glue_base, glue_scope, glue_filter); 1145 1146 } else { 1147 /* using entry_first() */ 1148 rc = glue_tool_entry_first (b0); 1149 } 1150 if ( glueBack == GLUEBACK_DONE ) { 1151 break; 1152 } 1153 } 1154 } 1155 return rc; 1156 } 1157 1158 static ID 1159 glue_tool_dn2id_get ( 1160 BackendDB *b0, 1161 struct berval *dn 1162 ) 1163 { 1164 BackendDB *be, b2; 1165 int rc = -1; 1166 1167 b2 = *b0; 1168 b2.bd_info = (BackendInfo *)glue_tool_inst( b0->bd_info ); 1169 be = glue_back_select (&b2, dn); 1170 if ( be == &b2 ) be = &toolDB; 1171 1172 if (!be->be_dn2id_get) 1173 return NOID; 1174 1175 if (!glueBack) { 1176 if ( be->be_entry_open ) { 1177 rc = be->be_entry_open (be, glueMode); 1178 } 1179 if (rc != 0) { 1180 return NOID; 1181 } 1182 } else if (be != glueBack) { 1183 /* If this entry belongs in a different branch than the 1184 * previous one, close the current database and open the 1185 * new one. 1186 */ 1187 if ( glueBack->be_entry_close ) { 1188 glueBack->be_entry_close (glueBack); 1189 } 1190 if ( be->be_entry_open ) { 1191 rc = be->be_entry_open (be, glueMode); 1192 } 1193 if (rc != 0) { 1194 return NOID; 1195 } 1196 } 1197 glueBack = be; 1198 return be->be_dn2id_get (be, dn); 1199 } 1200 1201 static Entry * 1202 glue_tool_entry_get ( 1203 BackendDB *b0, 1204 ID id 1205 ) 1206 { 1207 if (!glueBack || !glueBack->be_entry_get) 1208 return NULL; 1209 1210 return glueBack->be_entry_get (glueBack, id); 1211 } 1212 1213 static ID 1214 glue_tool_entry_put ( 1215 BackendDB *b0, 1216 Entry *e, 1217 struct berval *text 1218 ) 1219 { 1220 BackendDB *be, b2; 1221 int rc = -1; 1222 1223 b2 = *b0; 1224 b2.bd_info = (BackendInfo *)glue_tool_inst( b0->bd_info ); 1225 be = glue_back_select (&b2, &e->e_nname); 1226 if ( be == &b2 ) be = &toolDB; 1227 1228 if (!be->be_entry_put) 1229 return NOID; 1230 1231 if (!glueBack) { 1232 if ( be->be_entry_open ) { 1233 rc = be->be_entry_open (be, glueMode); 1234 } 1235 if (rc != 0) { 1236 return NOID; 1237 } 1238 } else if (be != glueBack) { 1239 /* If this entry belongs in a different branch than the 1240 * previous one, close the current database and open the 1241 * new one. 1242 */ 1243 if ( glueBack->be_entry_close ) { 1244 glueBack->be_entry_close (glueBack); 1245 } 1246 if ( be->be_entry_open ) { 1247 rc = be->be_entry_open (be, glueMode); 1248 } 1249 if (rc != 0) { 1250 return NOID; 1251 } 1252 } 1253 glueBack = be; 1254 return be->be_entry_put (be, e, text); 1255 } 1256 1257 static ID 1258 glue_tool_entry_modify ( 1259 BackendDB *b0, 1260 Entry *e, 1261 struct berval *text 1262 ) 1263 { 1264 if (!glueBack || !glueBack->be_entry_modify) 1265 return NOID; 1266 1267 return glueBack->be_entry_modify (glueBack, e, text); 1268 } 1269 1270 static int 1271 glue_tool_entry_reindex ( 1272 BackendDB *b0, 1273 ID id, 1274 AttributeDescription **adv 1275 ) 1276 { 1277 if (!glueBack || !glueBack->be_entry_reindex) 1278 return -1; 1279 1280 return glueBack->be_entry_reindex (glueBack, id, adv); 1281 } 1282 1283 static int 1284 glue_tool_sync ( 1285 BackendDB *b0 1286 ) 1287 { 1288 slap_overinst *on = glue_tool_inst( b0->bd_info ); 1289 glueinfo *gi = on->on_bi.bi_private; 1290 BackendInfo *bi = b0->bd_info; 1291 int i; 1292 1293 /* just sync everyone */ 1294 for (i = 0; i<gi->gi_nodes; i++) 1295 if (gi->gi_n[i].gn_be->be_sync) 1296 gi->gi_n[i].gn_be->be_sync (gi->gi_n[i].gn_be); 1297 b0->bd_info = on->on_info->oi_orig; 1298 if ( b0->be_sync ) 1299 b0->be_sync( b0 ); 1300 b0->bd_info = bi; 1301 return 0; 1302 } 1303 1304 typedef struct glue_Addrec { 1305 struct glue_Addrec *ga_next; 1306 BackendDB *ga_be; 1307 } glue_Addrec; 1308 1309 /* List of added subordinates */ 1310 static glue_Addrec *ga_list; 1311 static int ga_adding; 1312 1313 static int 1314 glue_db_init( 1315 BackendDB *be, 1316 ConfigReply *cr 1317 ) 1318 { 1319 slap_overinst *on = (slap_overinst *)be->bd_info; 1320 slap_overinfo *oi = on->on_info; 1321 BackendInfo *bi = oi->oi_orig; 1322 glueinfo *gi; 1323 1324 if ( SLAP_GLUE_SUBORDINATE( be )) { 1325 Debug( LDAP_DEBUG_ANY, "glue: backend %s is already subordinate, " 1326 "cannot have glue overlay!\n", 1327 be->be_suffix[0].bv_val ); 1328 return LDAP_OTHER; 1329 } 1330 1331 gi = ch_calloc( 1, sizeof(glueinfo)); 1332 on->on_bi.bi_private = gi; 1333 dnParent( be->be_nsuffix, &gi->gi_pdn ); 1334 1335 /* Currently the overlay framework doesn't handle these entry points 1336 * but we need them.... 1337 */ 1338 oi->oi_bi.bi_open = glue_open; 1339 oi->oi_bi.bi_close = glue_close; 1340 1341 /* Only advertise these if the root DB supports them */ 1342 if ( bi->bi_tool_entry_open ) 1343 oi->oi_bi.bi_tool_entry_open = glue_tool_entry_open; 1344 if ( bi->bi_tool_entry_close ) 1345 oi->oi_bi.bi_tool_entry_close = glue_tool_entry_close; 1346 if ( bi->bi_tool_entry_first ) 1347 oi->oi_bi.bi_tool_entry_first = glue_tool_entry_first; 1348 /* FIXME: check whether all support bi_tool_entry_first_x() ? */ 1349 if ( bi->bi_tool_entry_first_x ) 1350 oi->oi_bi.bi_tool_entry_first_x = glue_tool_entry_first_x; 1351 if ( bi->bi_tool_entry_next ) 1352 oi->oi_bi.bi_tool_entry_next = glue_tool_entry_next; 1353 if ( bi->bi_tool_entry_get ) 1354 oi->oi_bi.bi_tool_entry_get = glue_tool_entry_get; 1355 if ( bi->bi_tool_dn2id_get ) 1356 oi->oi_bi.bi_tool_dn2id_get = glue_tool_dn2id_get; 1357 if ( bi->bi_tool_entry_put ) 1358 oi->oi_bi.bi_tool_entry_put = glue_tool_entry_put; 1359 if ( bi->bi_tool_entry_reindex ) 1360 oi->oi_bi.bi_tool_entry_reindex = glue_tool_entry_reindex; 1361 if ( bi->bi_tool_entry_modify ) 1362 oi->oi_bi.bi_tool_entry_modify = glue_tool_entry_modify; 1363 if ( bi->bi_tool_sync ) 1364 oi->oi_bi.bi_tool_sync = glue_tool_sync; 1365 1366 SLAP_DBFLAGS( be ) |= SLAP_DBFLAG_GLUE_INSTANCE; 1367 1368 if ( ga_list && ( slapMode & SLAP_SERVER_MODE ) ) { 1369 be->bd_info = (BackendInfo *)oi; 1370 glue_sub_attach( 1 ); 1371 } 1372 1373 return 0; 1374 } 1375 1376 static int 1377 glue_db_destroy ( 1378 BackendDB *be, 1379 ConfigReply *cr 1380 ) 1381 { 1382 slap_overinst *on = (slap_overinst *)be->bd_info; 1383 glueinfo *gi = (glueinfo *)on->on_bi.bi_private; 1384 1385 free (gi); 1386 return 0; 1387 } 1388 1389 static int 1390 glue_db_close( 1391 BackendDB *be, 1392 ConfigReply *cr 1393 ) 1394 { 1395 slap_overinst *on = (slap_overinst *)be->bd_info; 1396 1397 on->on_info->oi_bi.bi_db_close = 0; 1398 return 0; 1399 } 1400 1401 int 1402 glue_sub_del( BackendDB *b0 ) 1403 { 1404 BackendDB *be; 1405 int rc = 0; 1406 1407 /* Find the top backend for this subordinate */ 1408 be = b0; 1409 while ( (be=LDAP_STAILQ_NEXT( be, be_next )) != NULL ) { 1410 slap_overinfo *oi; 1411 slap_overinst *on; 1412 glueinfo *gi; 1413 int i; 1414 1415 if ( SLAP_GLUE_SUBORDINATE( be )) 1416 continue; 1417 if ( !SLAP_GLUE_INSTANCE( be )) 1418 continue; 1419 if ( !dnIsSuffix( &b0->be_nsuffix[0], &be->be_nsuffix[0] )) 1420 continue; 1421 1422 /* OK, got the right backend, find the overlay */ 1423 oi = (slap_overinfo *)be->bd_info; 1424 for ( on=oi->oi_list; on; on=on->on_next ) { 1425 if ( on->on_bi.bi_type == glue.on_bi.bi_type ) 1426 break; 1427 } 1428 assert( on != NULL ); 1429 gi = on->on_bi.bi_private; 1430 for ( i=0; i < gi->gi_nodes; i++ ) { 1431 if ( gi->gi_n[i].gn_be == b0 ) { 1432 int j; 1433 1434 for (j=i+1; j < gi->gi_nodes; j++) 1435 gi->gi_n[j-1] = gi->gi_n[j]; 1436 1437 gi->gi_nodes--; 1438 } 1439 } 1440 /* Mark as no longer linked/sub */ 1441 b0->be_flags &= ~(SLAP_DBFLAG_GLUE_SUBORDINATE|SLAP_DBFLAG_GLUE_LINKED| 1442 SLAP_DBFLAG_GLUE_ADVERTISE); 1443 b0->be_pcsn_p = &b0->be_pcsn_st; 1444 break; 1445 } 1446 if ( be == NULL ) 1447 rc = LDAP_NO_SUCH_OBJECT; 1448 1449 return rc; 1450 } 1451 1452 1453 /* Attach all the subordinate backends to their superior */ 1454 int 1455 glue_sub_attach( int online ) 1456 { 1457 glue_Addrec *ga, *gnext = NULL; 1458 int rc = 0; 1459 1460 if ( ga_adding ) 1461 return 0; 1462 1463 ga_adding = 1; 1464 1465 /* For all the subordinate backends */ 1466 for ( ga=ga_list; ga != NULL; ga = gnext ) { 1467 BackendDB *be; 1468 1469 gnext = ga->ga_next; 1470 1471 /* Find the top backend for this subordinate */ 1472 be = ga->ga_be; 1473 while ( (be=LDAP_STAILQ_NEXT( be, be_next )) != NULL ) { 1474 slap_overinfo *oi; 1475 slap_overinst *on; 1476 glueinfo *gi; 1477 1478 if ( SLAP_GLUE_SUBORDINATE( be )) 1479 continue; 1480 if ( !dnIsSuffix( &ga->ga_be->be_nsuffix[0], &be->be_nsuffix[0] )) 1481 continue; 1482 1483 /* If it's not already configured, set up the overlay */ 1484 if ( !SLAP_GLUE_INSTANCE( be )) { 1485 rc = overlay_config( be, glue.on_bi.bi_type, -1, NULL, NULL); 1486 if ( rc ) 1487 break; 1488 } 1489 /* Find the overlay instance */ 1490 oi = (slap_overinfo *)be->bd_info; 1491 for ( on=oi->oi_list; on; on=on->on_next ) { 1492 if ( on->on_bi.bi_type == glue.on_bi.bi_type ) 1493 break; 1494 } 1495 assert( on != NULL ); 1496 gi = on->on_bi.bi_private; 1497 gi = (glueinfo *)ch_realloc( gi, sizeof(glueinfo) + 1498 gi->gi_nodes * sizeof(gluenode)); 1499 gi->gi_n[gi->gi_nodes].gn_be = ga->ga_be; 1500 dnParent( &ga->ga_be->be_nsuffix[0], 1501 &gi->gi_n[gi->gi_nodes].gn_pdn ); 1502 gi->gi_nodes++; 1503 on->on_bi.bi_private = gi; 1504 ga->ga_be->be_pcsn_p = be->be_pcsn_p; 1505 ga->ga_be->be_flags |= SLAP_DBFLAG_GLUE_LINKED; 1506 break; 1507 } 1508 if ( !be ) { 1509 Debug( LDAP_DEBUG_ANY, "glue: no superior found for sub %s!\n", 1510 ga->ga_be->be_suffix[0].bv_val ); 1511 /* allow this for now, assume a superior will 1512 * be added later 1513 */ 1514 if ( online ) { 1515 rc = 0; 1516 gnext = ga_list; 1517 break; 1518 } 1519 rc = LDAP_NO_SUCH_OBJECT; 1520 } 1521 ch_free( ga ); 1522 if ( rc ) break; 1523 } 1524 1525 ga_list = gnext; 1526 1527 ga_adding = 0; 1528 1529 return rc; 1530 } 1531 1532 int 1533 glue_sub_add( BackendDB *be, int advert, int online ) 1534 { 1535 glue_Addrec *ga; 1536 int rc = 0; 1537 1538 if ( overlay_is_inst( be, "glue" )) { 1539 Debug( LDAP_DEBUG_ANY, "glue: backend %s already has glue overlay, " 1540 "cannot be a subordinate!\n", 1541 be->be_suffix[0].bv_val ); 1542 return LDAP_OTHER; 1543 } 1544 SLAP_DBFLAGS( be ) |= SLAP_DBFLAG_GLUE_SUBORDINATE; 1545 if ( advert ) 1546 SLAP_DBFLAGS( be ) |= SLAP_DBFLAG_GLUE_ADVERTISE; 1547 1548 ga = ch_malloc( sizeof( glue_Addrec )); 1549 ga->ga_next = ga_list; 1550 ga->ga_be = be; 1551 ga_list = ga; 1552 1553 if ( online ) 1554 rc = glue_sub_attach( online ); 1555 1556 return rc; 1557 } 1558 1559 static int 1560 glue_access_allowed( 1561 Operation *op, 1562 Entry *e, 1563 AttributeDescription *desc, 1564 struct berval *val, 1565 slap_access_t access, 1566 AccessControlState *state, 1567 slap_mask_t *maskp ) 1568 { 1569 BackendDB *b0, *be = glue_back_select( op->o_bd, &e->e_nname ); 1570 int rc; 1571 1572 if ( be == NULL || be == op->o_bd || be->bd_info->bi_access_allowed == NULL ) 1573 return SLAP_CB_CONTINUE; 1574 1575 b0 = op->o_bd; 1576 op->o_bd = be; 1577 rc = be->bd_info->bi_access_allowed ( op, e, desc, val, access, state, maskp ); 1578 op->o_bd = b0; 1579 return rc; 1580 } 1581 1582 int 1583 glue_sub_init() 1584 { 1585 glue.on_bi.bi_type = "glue"; 1586 1587 glue.on_bi.bi_db_init = glue_db_init; 1588 glue.on_bi.bi_db_close = glue_db_close; 1589 glue.on_bi.bi_db_destroy = glue_db_destroy; 1590 1591 glue.on_bi.bi_op_search = glue_op_search; 1592 glue.on_bi.bi_op_modify = glue_op_func; 1593 glue.on_bi.bi_op_modrdn = glue_op_func; 1594 glue.on_bi.bi_op_add = glue_op_func; 1595 glue.on_bi.bi_op_delete = glue_op_func; 1596 glue.on_bi.bi_op_abandon = glue_op_abandon; 1597 glue.on_bi.bi_extended = glue_op_func; 1598 1599 glue.on_bi.bi_chk_referrals = glue_chk_referrals; 1600 glue.on_bi.bi_chk_controls = glue_chk_controls; 1601 glue.on_bi.bi_entry_get_rw = glue_entry_get_rw; 1602 glue.on_bi.bi_entry_release_rw = glue_entry_release_rw; 1603 glue.on_bi.bi_access_allowed = glue_access_allowed; 1604 1605 glue.on_response = glue_response; 1606 1607 return overlay_register( &glue ); 1608 } 1609