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