Back to index

openldap  2.4.31
refint.c
Go to the documentation of this file.
00001 /* refint.c - referential integrity module */
00002 /* $OpenLDAP$ */
00003 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
00004  *
00005  * Copyright 2004-2012 The OpenLDAP Foundation.
00006  * Portions Copyright 2004 Symas Corporation.
00007  * All rights reserved.
00008  *
00009  * Redistribution and use in source and binary forms, with or without
00010  * modification, are permitted only as authorized by the OpenLDAP
00011  * Public License.
00012  *
00013  * A copy of this license is available in the file LICENSE in the
00014  * top-level directory of the distribution or, alternatively, at
00015  * <http://www.OpenLDAP.org/license.html>.
00016  */
00017 /* ACKNOWLEDGEMENTS:
00018  * This work was initially developed by Symas Corp. for inclusion in
00019  * OpenLDAP Software.  This work was sponsored by Hewlett-Packard.
00020  */
00021 
00022 #include "portable.h"
00023 
00024 /* This module maintains referential integrity for a set of
00025  * DN-valued attributes by searching for all references to a given
00026  * DN whenever the DN is changed or its entry is deleted, and making
00027  * the appropriate update.
00028  *
00029  * Updates are performed using the database rootdn in a separate task
00030  * to allow the original operation to complete immediately.
00031  */
00032 
00033 #ifdef SLAPD_OVER_REFINT
00034 
00035 #include <stdio.h>
00036 
00037 #include <ac/string.h>
00038 #include <ac/socket.h>
00039 
00040 #include "slap.h"
00041 #include "config.h"
00042 #include "ldap_rq.h"
00043 
00044 static slap_overinst refint;
00045 
00046 /* The DN to use in the ModifiersName for all refint updates */
00047 static BerValue refint_dn = BER_BVC("cn=Referential Integrity Overlay");
00048 static BerValue refint_ndn = BER_BVC("cn=referential integrity overlay");
00049 
00050 typedef struct refint_attrs_s {
00051        struct refint_attrs_s       *next;
00052        AttributeDescription *attr;
00053        BerVarray            old_vals;
00054        BerVarray            old_nvals;
00055        BerVarray            new_vals;
00056        BerVarray            new_nvals;
00057        int                         ra_numvals;
00058        int                         dont_empty;
00059 } refint_attrs;
00060 
00061 typedef struct dependents_s {
00062        struct dependents_s *next;
00063        BerValue dn;                       /* target dn */
00064        BerValue ndn;
00065        refint_attrs *attrs;
00066 } dependent_data;
00067 
00068 typedef struct refint_q {
00069        struct refint_q *next;
00070        struct refint_data_s *rdata;
00071        dependent_data *attrs;             /* entries and attrs returned from callback */
00072        BackendDB *db;
00073        BerValue olddn;
00074        BerValue oldndn;
00075        BerValue newdn;
00076        BerValue newndn;
00077 } refint_q;
00078 
00079 typedef struct refint_data_s {
00080        struct refint_attrs_s *attrs;      /* list of known attrs */
00081        BerValue dn;                       /* basedn in parent, */
00082        BerValue nothing;                  /* the nothing value, if needed */
00083        BerValue nnothing;                 /* normalized nothingness */
00084        BerValue refint_dn;                /* modifier's name */
00085        BerValue refint_ndn;               /* normalized modifier's name */
00086        struct re_s *qtask;
00087        refint_q *qhead;
00088        refint_q *qtail;
00089        ldap_pvt_thread_mutex_t qmutex;
00090 } refint_data;
00091 
00092 #define       RUNQ_INTERVAL 36000  /* a long time */
00093 
00094 static MatchingRule  *mr_dnSubtreeMatch;
00095 
00096 enum {
00097        REFINT_ATTRS = 1,
00098        REFINT_NOTHING,
00099        REFINT_MODIFIERSNAME
00100 };
00101 
00102 static ConfigDriver refint_cf_gen;
00103 
00104 static ConfigTable refintcfg[] = {
00105        { "refint_attributes", "attribute...", 2, 0, 0,
00106          ARG_MAGIC|REFINT_ATTRS, refint_cf_gen,
00107          "( OLcfgOvAt:11.1 NAME 'olcRefintAttribute' "
00108          "DESC 'Attributes for referential integrity' "
00109          "EQUALITY caseIgnoreMatch "
00110          "SYNTAX OMsDirectoryString )", NULL, NULL },
00111        { "refint_nothing", "string", 2, 2, 0,
00112          ARG_DN|ARG_MAGIC|REFINT_NOTHING, refint_cf_gen,
00113          "( OLcfgOvAt:11.2 NAME 'olcRefintNothing' "
00114          "DESC 'Replacement DN to supply when needed' "
00115          "SYNTAX OMsDN SINGLE-VALUE )", NULL, NULL },
00116        { "refint_modifiersName", "DN", 2, 2, 0,
00117          ARG_DN|ARG_MAGIC|REFINT_MODIFIERSNAME, refint_cf_gen,
00118          "( OLcfgOvAt:11.3 NAME 'olcRefintModifiersName' "
00119          "DESC 'The DN to use as modifiersName' "
00120          "SYNTAX OMsDN SINGLE-VALUE )", NULL, NULL },
00121        { NULL, NULL, 0, 0, 0, ARG_IGNORED }
00122 };
00123 
00124 static ConfigOCs refintocs[] = {
00125        { "( OLcfgOvOc:11.1 "
00126          "NAME 'olcRefintConfig' "
00127          "DESC 'Referential integrity configuration' "
00128          "SUP olcOverlayConfig "
00129          "MAY ( olcRefintAttribute "
00130               "$ olcRefintNothing "
00131               "$ olcRefintModifiersName "
00132          ") )",
00133          Cft_Overlay, refintcfg },
00134        { NULL, 0, NULL }
00135 };
00136 
00137 static int
00138 refint_cf_gen(ConfigArgs *c)
00139 {
00140        slap_overinst *on = (slap_overinst *)c->bi;
00141        refint_data *dd = (refint_data *)on->on_bi.bi_private;
00142        refint_attrs *ip, *pip, **pipp = NULL;
00143        AttributeDescription *ad;
00144        const char *text;
00145        int rc = ARG_BAD_CONF;
00146        int i;
00147 
00148        switch ( c->op ) {
00149        case SLAP_CONFIG_EMIT:
00150               switch ( c->type ) {
00151               case REFINT_ATTRS:
00152                      ip = dd->attrs;
00153                      while ( ip ) {
00154                             value_add_one( &c->rvalue_vals,
00155                                           &ip->attr->ad_cname );
00156                             ip = ip->next;
00157                      }
00158                      rc = 0;
00159                      break;
00160               case REFINT_NOTHING:
00161                      if ( !BER_BVISEMPTY( &dd->nothing )) {
00162                             rc = value_add_one( &c->rvalue_vals,
00163                                               &dd->nothing );
00164                             if ( rc ) return rc;
00165                             rc = value_add_one( &c->rvalue_nvals,
00166                                               &dd->nnothing );
00167                             return rc;
00168                      }
00169                      rc = 0;
00170                      break;
00171               case REFINT_MODIFIERSNAME:
00172                      if ( !BER_BVISEMPTY( &dd->refint_dn )) {
00173                             rc = value_add_one( &c->rvalue_vals,
00174                                               &dd->refint_dn );
00175                             if ( rc ) return rc;
00176                             rc = value_add_one( &c->rvalue_nvals,
00177                                               &dd->refint_ndn );
00178                             return rc;
00179                      }
00180                      rc = 0;
00181                      break;
00182               default:
00183                      abort ();
00184               }
00185               break;
00186        case LDAP_MOD_DELETE:
00187               switch ( c->type ) {
00188               case REFINT_ATTRS:
00189                      pipp = &dd->attrs;
00190                      if ( c->valx < 0 ) {
00191                             ip = *pipp;
00192                             *pipp = NULL;
00193                             while ( ip ) {
00194                                    pip = ip;
00195                                    ip = ip->next;
00196                                    ch_free ( pip );
00197                             }
00198                      } else {
00199                             /* delete from linked list */
00200                             for ( i=0; i < c->valx; ++i ) {
00201                                    pipp = &(*pipp)->next;
00202                             }
00203                             ip = *pipp;
00204                             *pipp = (*pipp)->next;
00205 
00206                             /* AttributeDescriptions are global so
00207                              * shouldn't be freed here... */
00208                             ch_free ( ip );
00209                      }
00210                      rc = 0;
00211                      break;
00212               case REFINT_NOTHING:
00213                      ch_free( dd->nothing.bv_val );
00214                      ch_free( dd->nnothing.bv_val );
00215                      BER_BVZERO( &dd->nothing );
00216                      BER_BVZERO( &dd->nnothing );
00217                      rc = 0;
00218                      break;
00219               case REFINT_MODIFIERSNAME:
00220                      ch_free( dd->refint_dn.bv_val );
00221                      ch_free( dd->refint_ndn.bv_val );
00222                      BER_BVZERO( &dd->refint_dn );
00223                      BER_BVZERO( &dd->refint_ndn );
00224                      rc = 0;
00225                      break;
00226               default:
00227                      abort ();
00228               }
00229               break;
00230        case SLAP_CONFIG_ADD:
00231               /* fallthrough to LDAP_MOD_ADD */
00232        case LDAP_MOD_ADD:
00233               switch ( c->type ) {
00234               case REFINT_ATTRS:
00235                      rc = 0;
00236                      for ( i=1; i < c->argc; ++i ) {
00237                             ad = NULL;
00238                             if ( slap_str2ad ( c->argv[i], &ad, &text )
00239                                  == LDAP_SUCCESS) {
00240                                    ip = ch_malloc (
00241                                           sizeof ( refint_attrs ) );
00242                                    ip->attr = ad;
00243                                    ip->next = dd->attrs;
00244                                    dd->attrs = ip;
00245                             } else {
00246                                    snprintf( c->cr_msg, sizeof( c->cr_msg ),
00247                                           "%s <%s>: %s", c->argv[0], c->argv[i], text );
00248                                    Debug ( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE,
00249                                           "%s: %s\n", c->log, c->cr_msg, 0 );
00250                                    rc = ARG_BAD_CONF;
00251                             }
00252                      }
00253                      break;
00254               case REFINT_NOTHING:
00255                      if ( !BER_BVISNULL( &c->value_ndn )) {
00256                             ch_free ( dd->nothing.bv_val );
00257                             ch_free ( dd->nnothing.bv_val );
00258                             dd->nothing = c->value_dn;
00259                             dd->nnothing = c->value_ndn;
00260                             rc = 0;
00261                      } else {
00262                             rc = ARG_BAD_CONF;
00263                      }
00264                      break;
00265               case REFINT_MODIFIERSNAME:
00266                      if ( !BER_BVISNULL( &c->value_ndn )) {
00267                             ch_free( dd->refint_dn.bv_val );
00268                             ch_free( dd->refint_ndn.bv_val );
00269                             dd->refint_dn = c->value_dn;
00270                             dd->refint_ndn = c->value_ndn;
00271                             rc = 0;
00272                      } else {
00273                             rc = ARG_BAD_CONF;
00274                      }
00275                      break;
00276               default:
00277                      abort ();
00278               }
00279               break;
00280        default:
00281               abort ();
00282        }
00283 
00284        return rc;
00285 }
00286 
00287 /*
00288 ** allocate new refint_data;
00289 ** store in on_bi.bi_private;
00290 **
00291 */
00292 
00293 static int
00294 refint_db_init(
00295        BackendDB     *be,
00296        ConfigReply   *cr
00297 )
00298 {
00299        slap_overinst *on = (slap_overinst *)be->bd_info;
00300        refint_data *id = ch_calloc(1,sizeof(refint_data));
00301 
00302        on->on_bi.bi_private = id;
00303        ldap_pvt_thread_mutex_init( &id->qmutex );
00304        return(0);
00305 }
00306 
00307 static int
00308 refint_db_destroy(
00309        BackendDB     *be,
00310        ConfigReply   *cr
00311 )
00312 {
00313        slap_overinst *on = (slap_overinst *)be->bd_info;
00314 
00315        if ( on->on_bi.bi_private ) {
00316               refint_data *id = on->on_bi.bi_private;
00317               on->on_bi.bi_private = NULL;
00318               ldap_pvt_thread_mutex_destroy( &id->qmutex );
00319               ch_free( id );
00320        }
00321        return(0);
00322 }
00323 
00324 /*
00325 ** initialize, copy basedn if not already set
00326 **
00327 */
00328 
00329 static int
00330 refint_open(
00331        BackendDB *be,
00332        ConfigReply *cr
00333 )
00334 {
00335        slap_overinst *on    = (slap_overinst *)be->bd_info;
00336        refint_data *id      = on->on_bi.bi_private;
00337 
00338        if ( BER_BVISNULL( &id->dn )) {
00339               if ( BER_BVISNULL( &be->be_nsuffix[0] ))
00340                      return -1;
00341               ber_dupbv( &id->dn, &be->be_nsuffix[0] );
00342        }
00343        if ( BER_BVISNULL( &id->refint_dn ) ) {
00344               ber_dupbv( &id->refint_dn, &refint_dn );
00345               ber_dupbv( &id->refint_ndn, &refint_ndn );
00346        }
00347        return(0);
00348 }
00349 
00350 
00351 /*
00352 ** foreach configured attribute:
00353 **     free it;
00354 ** free our basedn;
00355 ** reset on_bi.bi_private;
00356 ** free our config data;
00357 **
00358 */
00359 
00360 static int
00361 refint_close(
00362        BackendDB *be,
00363        ConfigReply *cr
00364 )
00365 {
00366        slap_overinst *on    = (slap_overinst *) be->bd_info;
00367        refint_data *id      = on->on_bi.bi_private;
00368        refint_attrs *ii, *ij;
00369 
00370        for(ii = id->attrs; ii; ii = ij) {
00371               ij = ii->next;
00372               ch_free(ii);
00373        }
00374        id->attrs = NULL;
00375 
00376        ch_free( id->dn.bv_val );
00377        BER_BVZERO( &id->dn );
00378        ch_free( id->nothing.bv_val );
00379        BER_BVZERO( &id->nothing );
00380        ch_free( id->nnothing.bv_val );
00381        BER_BVZERO( &id->nnothing );
00382        ch_free( id->refint_dn.bv_val );
00383        BER_BVZERO( &id->refint_dn );
00384        ch_free( id->refint_ndn.bv_val );
00385        BER_BVZERO( &id->refint_ndn );
00386 
00387        return(0);
00388 }
00389 
00390 /*
00391 ** search callback
00392 ** generates a list of Attributes from search results
00393 */
00394 
00395 static int
00396 refint_search_cb(
00397        Operation *op,
00398        SlapReply *rs
00399 )
00400 {
00401        Attribute *a;
00402        BerVarray b = NULL;
00403        refint_q *rq = op->o_callback->sc_private;
00404        refint_data *dd = rq->rdata;
00405        refint_attrs *ia, *da = dd->attrs, *na;
00406        dependent_data *ip;
00407        int i;
00408 
00409        Debug(LDAP_DEBUG_TRACE, "refint_search_cb <%s>\n",
00410               rs->sr_entry ? rs->sr_entry->e_name.bv_val : "NOTHING", 0, 0);
00411 
00412        if (rs->sr_type != REP_SEARCH || !rs->sr_entry) return(0);
00413 
00414        /*
00415        ** foreach configured attribute type:
00416        **     if this attr exists in the search result,
00417        **     and it has a value matching the target:
00418        **            allocate an attr;
00419        **            save/build DNs of any subordinate matches;
00420        **            handle special case: found exact + subordinate match;
00421        **            handle olcRefintNothing;
00422        **
00423        */
00424 
00425        ip = op->o_tmpalloc(sizeof(dependent_data), op->o_tmpmemctx );
00426        ber_dupbv_x( &ip->dn, &rs->sr_entry->e_name, op->o_tmpmemctx );
00427        ber_dupbv_x( &ip->ndn, &rs->sr_entry->e_nname, op->o_tmpmemctx );
00428        ip->next = rq->attrs;
00429        rq->attrs = ip;
00430        ip->attrs = NULL;
00431        for(ia = da; ia; ia = ia->next) {
00432               if ( (a = attr_find(rs->sr_entry->e_attrs, ia->attr) ) ) {
00433                      int exact = -1, is_exact;
00434 
00435                      na = NULL;
00436 
00437                      for(i = 0, b = a->a_nvals; b[i].bv_val; i++) {
00438                             if(dnIsSuffix(&b[i], &rq->oldndn)) {
00439                                    is_exact = b[i].bv_len == rq->oldndn.bv_len;
00440 
00441                                    /* Paranoia: skip buggy duplicate exact match,
00442                                     * it would break ra_numvals
00443                                     */
00444                                    if ( is_exact && exact >= 0 )
00445                                           continue;
00446 
00447                                    /* first match? create structure */
00448                                    if ( na == NULL ) {
00449                                           na = op->o_tmpcalloc( 1,
00450                                                  sizeof( refint_attrs ),
00451                                                  op->o_tmpmemctx );
00452                                           na->next = ip->attrs;
00453                                           ip->attrs = na;
00454                                           na->attr = ia->attr;
00455                                    }
00456 
00457                                    na->ra_numvals++;
00458 
00459                                    if ( is_exact ) {
00460                                           /* Exact match: refint_repair will deduce the DNs */
00461                                           exact = i;
00462 
00463                                    } else {
00464                                           /* Subordinate match */
00465                                           struct berval newsub, newdn, olddn, oldndn;
00466 
00467                                           /* Save old DN */
00468                                           ber_dupbv_x( &olddn, &a->a_vals[i], op->o_tmpmemctx );
00469                                           ber_bvarray_add_x( &na->old_vals, &olddn, op->o_tmpmemctx );
00470 
00471                                           ber_dupbv_x( &oldndn, &a->a_nvals[i], op->o_tmpmemctx );
00472                                           ber_bvarray_add_x( &na->old_nvals, &oldndn, op->o_tmpmemctx );
00473 
00474                                           if ( BER_BVISEMPTY( &rq->newdn ) )
00475                                                  continue;
00476 
00477                                           /* Rename subordinate match: Build new DN */
00478                                           newsub = a->a_vals[i];
00479                                           newsub.bv_len -= rq->olddn.bv_len + 1;
00480                                           build_new_dn( &newdn, &rq->newdn, &newsub, op->o_tmpmemctx );
00481                                           ber_bvarray_add_x( &na->new_vals, &newdn, op->o_tmpmemctx );
00482 
00483                                           newsub = a->a_nvals[i];
00484                                           newsub.bv_len -= rq->oldndn.bv_len + 1;
00485                                           build_new_dn( &newdn, &rq->newndn, &newsub, op->o_tmpmemctx );
00486                                           ber_bvarray_add_x( &na->new_nvals, &newdn, op->o_tmpmemctx );
00487                                    }
00488                             }
00489                      }
00490 
00491                      /* If we got both subordinate and exact match,
00492                       * refint_repair won't special-case the exact match */
00493                      if ( exact >= 0 && na->old_vals ) {
00494                             struct berval dn;
00495 
00496                             ber_dupbv_x( &dn, &a->a_vals[exact], op->o_tmpmemctx );
00497                             ber_bvarray_add_x( &na->old_vals, &dn, op->o_tmpmemctx );
00498                             ber_dupbv_x( &dn, &a->a_nvals[exact], op->o_tmpmemctx );
00499                             ber_bvarray_add_x( &na->old_nvals, &dn, op->o_tmpmemctx );
00500 
00501                             if ( !BER_BVISEMPTY( &rq->newdn ) ) {
00502                                    ber_dupbv_x( &dn, &rq->newdn, op->o_tmpmemctx );
00503                                    ber_bvarray_add_x( &na->new_vals, &dn, op->o_tmpmemctx );
00504                                    ber_dupbv_x( &dn, &rq->newndn, op->o_tmpmemctx );
00505                                    ber_bvarray_add_x( &na->new_nvals, &dn, op->o_tmpmemctx );
00506                             }
00507                      }
00508 
00509                      /* Deleting/replacing all values and a nothing DN is configured? */
00510                      if ( na && na->ra_numvals == i && !BER_BVISNULL(&dd->nothing) )
00511                             na->dont_empty = 1;
00512 
00513                      Debug( LDAP_DEBUG_TRACE, "refint_search_cb: %s: %s (#%d)\n",
00514                             a->a_desc->ad_cname.bv_val, rq->olddn.bv_val, i );
00515               }
00516        }
00517 
00518        return(0);
00519 }
00520 
00521 static int
00522 refint_repair(
00523        Operation     *op,
00524        refint_data   *id,
00525        refint_q      *rq )
00526 {
00527        dependent_data       *dp;
00528        SlapReply            rs = {REP_RESULT};
00529        Operation            op2;
00530        int           rc;
00531 
00532        op->o_callback->sc_response = refint_search_cb;
00533        op->o_req_dn = op->o_bd->be_suffix[ 0 ];
00534        op->o_req_ndn = op->o_bd->be_nsuffix[ 0 ];
00535        op->o_dn = op->o_bd->be_rootdn;
00536        op->o_ndn = op->o_bd->be_rootndn;
00537 
00538        /* search */
00539        rc = op->o_bd->be_search( op, &rs );
00540 
00541        if ( rc != LDAP_SUCCESS ) {
00542               Debug( LDAP_DEBUG_TRACE,
00543                      "refint_repair: search failed: %d\n",
00544                      rc, 0, 0 );
00545               return 0;
00546        }
00547 
00548        /* safety? paranoid just in case */
00549        if ( op->o_callback->sc_private == NULL ) {
00550               Debug( LDAP_DEBUG_TRACE,
00551                      "refint_repair: callback wiped out sc_private?!\n",
00552                      0, 0, 0 );
00553               return 0;
00554        }
00555 
00556        /* Set up the Modify requests */
00557        op->o_callback->sc_response = &slap_null_cb;
00558 
00559        /*
00560         * [our search callback builds a list of attrs]
00561         * foreach attr:
00562         *     make sure its dn has a backend;
00563         *     build Modification* chain;
00564         *     call the backend modify function;
00565         *
00566         */
00567 
00568        op2 = *op;
00569        for ( dp = rq->attrs; dp; dp = dp->next ) {
00570               SlapReply     rs2 = {REP_RESULT};
00571               refint_attrs  *ra;
00572               Modifications *m;
00573 
00574               if ( dp->attrs == NULL ) continue; /* TODO: Is this needed? */
00575 
00576               op2.o_bd = select_backend( &dp->ndn, 1 );
00577               if ( !op2.o_bd ) {
00578                      Debug( LDAP_DEBUG_TRACE,
00579                             "refint_repair: no backend for DN %s!\n",
00580                             dp->dn.bv_val, 0, 0 );
00581                      continue;
00582               }
00583               op2.o_tag = LDAP_REQ_MODIFY;
00584               op2.orm_modlist = NULL;
00585               op2.o_req_dn  = dp->dn;
00586               op2.o_req_ndn = dp->ndn;
00587               /* Internal ops, never replicate these */
00588               op2.orm_no_opattrs = 1;
00589               op2.o_dont_replicate = 1;
00590 
00591               /* Set our ModifiersName */
00592               if ( SLAP_LASTMOD( op->o_bd ) ) {
00593                             m = op2.o_tmpalloc( sizeof(Modifications) +
00594                                    4*sizeof(BerValue), op2.o_tmpmemctx );
00595                             m->sml_next = op2.orm_modlist;
00596                             op2.orm_modlist = m;
00597                             m->sml_op = LDAP_MOD_REPLACE;
00598                             m->sml_flags = SLAP_MOD_INTERNAL;
00599                             m->sml_desc = slap_schema.si_ad_modifiersName;
00600                             m->sml_type = m->sml_desc->ad_cname;
00601                             m->sml_numvals = 1;
00602                             m->sml_values = (BerVarray)(m+1);
00603                             m->sml_nvalues = m->sml_values+2;
00604                             BER_BVZERO( &m->sml_values[1] );
00605                             BER_BVZERO( &m->sml_nvalues[1] );
00606                             m->sml_values[0] = id->refint_dn;
00607                             m->sml_nvalues[0] = id->refint_ndn;
00608               }
00609 
00610               for ( ra = dp->attrs; ra; ra = ra->next ) {
00611                      size_t len;
00612 
00613                      /* Add values */
00614                      if ( ra->dont_empty || !BER_BVISEMPTY( &rq->newdn ) ) {
00615                             len = sizeof(Modifications);
00616 
00617                             if ( ra->new_vals == NULL ) {
00618                                    len += 4*sizeof(BerValue);
00619                             }
00620 
00621                             m = op2.o_tmpalloc( len, op2.o_tmpmemctx );
00622                             m->sml_next = op2.orm_modlist;
00623                             op2.orm_modlist = m;
00624                             m->sml_op = LDAP_MOD_ADD;
00625                             m->sml_flags = 0;
00626                             m->sml_desc = ra->attr;
00627                             m->sml_type = ra->attr->ad_cname;
00628                             if ( ra->new_vals == NULL ) {
00629                                    m->sml_values = (BerVarray)(m+1);
00630                                    m->sml_nvalues = m->sml_values+2;
00631                                    BER_BVZERO( &m->sml_values[1] );
00632                                    BER_BVZERO( &m->sml_nvalues[1] );
00633                                    m->sml_numvals = 1;
00634                                    if ( BER_BVISEMPTY( &rq->newdn ) ) {
00635                                           m->sml_values[0] = id->nothing;
00636                                           m->sml_nvalues[0] = id->nnothing;
00637                                    } else {
00638                                           m->sml_values[0] = rq->newdn;
00639                                           m->sml_nvalues[0] = rq->newndn;
00640                                    }
00641                             } else {
00642                                    m->sml_values = ra->new_vals;
00643                                    m->sml_nvalues = ra->new_nvals;
00644                                    m->sml_numvals = ra->ra_numvals;
00645                             }
00646                      }
00647 
00648                      /* Delete values */
00649                      len = sizeof(Modifications);
00650                      if ( ra->old_vals == NULL ) {
00651                             len += 4*sizeof(BerValue);
00652                      }
00653                      m = op2.o_tmpalloc( len, op2.o_tmpmemctx );
00654                      m->sml_next = op2.orm_modlist;
00655                      op2.orm_modlist = m;
00656                      m->sml_op = LDAP_MOD_DELETE;
00657                      m->sml_flags = 0;
00658                      m->sml_desc = ra->attr;
00659                      m->sml_type = ra->attr->ad_cname;
00660                      if ( ra->old_vals == NULL ) {
00661                             m->sml_numvals = 1;
00662                             m->sml_values = (BerVarray)(m+1);
00663                             m->sml_nvalues = m->sml_values+2;
00664                             m->sml_values[0] = rq->olddn;
00665                             m->sml_nvalues[0] = rq->oldndn;
00666                             BER_BVZERO( &m->sml_values[1] );
00667                             BER_BVZERO( &m->sml_nvalues[1] );
00668                      } else {
00669                             m->sml_values = ra->old_vals;
00670                             m->sml_nvalues = ra->old_nvals;
00671                             m->sml_numvals = ra->ra_numvals;
00672                      }
00673               }
00674 
00675               op2.o_dn = op2.o_bd->be_rootdn;
00676               op2.o_ndn = op2.o_bd->be_rootndn;
00677               rc = op2.o_bd->be_modify( &op2, &rs2 );
00678               if ( rc != LDAP_SUCCESS ) {
00679                      Debug( LDAP_DEBUG_TRACE,
00680                             "refint_repair: dependent modify failed: %d\n",
00681                             rs2.sr_err, 0, 0 );
00682               }
00683 
00684               while ( ( m = op2.orm_modlist ) ) {
00685                      op2.orm_modlist = m->sml_next;
00686                      op2.o_tmpfree( m, op2.o_tmpmemctx );
00687               }
00688        }
00689 
00690        return 0;
00691 }
00692 
00693 static void *
00694 refint_qtask( void *ctx, void *arg )
00695 {
00696        struct re_s *rtask = arg;
00697        refint_data *id = rtask->arg;
00698        Connection conn = {0};
00699        OperationBuffer opbuf;
00700        Operation *op;
00701        slap_callback cb = { NULL, NULL, NULL, NULL };
00702        Filter ftop, *fptr;
00703        refint_q *rq;
00704        refint_attrs *ip;
00705 
00706        connection_fake_init( &conn, &opbuf, ctx );
00707        op = &opbuf.ob_op;
00708 
00709        /*
00710        ** build a search filter for all configured attributes;
00711        ** populate our Operation;
00712        ** pass our data (attr list, dn) to backend via sc_private;
00713        ** call the backend search function;
00714        ** nb: (|(one=thing)) is valid, but do smart formatting anyway;
00715        ** nb: 16 is arbitrarily a dozen or so extra bytes;
00716        **
00717        */
00718 
00719        ftop.f_choice = LDAP_FILTER_OR;
00720        ftop.f_next = NULL;
00721        ftop.f_or = NULL;
00722        op->ors_filter = &ftop;
00723        for(ip = id->attrs; ip; ip = ip->next) {
00724               fptr = op->o_tmpcalloc( sizeof(Filter) + sizeof(MatchingRuleAssertion),
00725                      1, op->o_tmpmemctx );
00726               /* Use (attr:dnSubtreeMatch:=value) to catch subtree rename
00727                * and subtree delete where supported */
00728               fptr->f_choice = LDAP_FILTER_EXT;
00729               fptr->f_mra = (MatchingRuleAssertion *)(fptr+1);
00730               fptr->f_mr_rule = mr_dnSubtreeMatch;
00731               fptr->f_mr_rule_text = mr_dnSubtreeMatch->smr_bvoid;
00732               fptr->f_mr_desc = ip->attr;
00733               fptr->f_mr_dnattrs = 0;
00734               fptr->f_next = ftop.f_or;
00735               ftop.f_or = fptr;
00736        }
00737 
00738        for (;;) {
00739               dependent_data       *dp, *dp_next;
00740               refint_attrs *ra, *ra_next;
00741 
00742               /* Dequeue an op */
00743               ldap_pvt_thread_mutex_lock( &id->qmutex );
00744               rq = id->qhead;
00745               if ( rq ) {
00746                      id->qhead = rq->next;
00747                      if ( !id->qhead )
00748                             id->qtail = NULL;
00749               }
00750               ldap_pvt_thread_mutex_unlock( &id->qmutex );
00751               if ( !rq )
00752                      break;
00753 
00754               for (fptr = ftop.f_or; fptr; fptr = fptr->f_next )
00755                      fptr->f_mr_value = rq->oldndn;
00756 
00757               filter2bv_x( op, op->ors_filter, &op->ors_filterstr );
00758 
00759               /* callback gets the searched dn instead */
00760               cb.sc_private = rq;
00761               cb.sc_response       = refint_search_cb;
00762               op->o_callback       = &cb;
00763               op->o_tag     = LDAP_REQ_SEARCH;
00764               op->ors_scope = LDAP_SCOPE_SUBTREE;
00765               op->ors_deref = LDAP_DEREF_NEVER;
00766               op->ors_limit   = NULL;
00767               op->ors_slimit       = SLAP_NO_LIMIT;
00768               op->ors_tlimit       = SLAP_NO_LIMIT;
00769 
00770               /* no attrs! */
00771               op->ors_attrs = slap_anlist_no_attrs;
00772 
00773               slap_op_time( &op->o_time, &op->o_tincr );
00774 
00775               if ( rq->db != NULL ) {
00776                      op->o_bd = rq->db;
00777                      refint_repair( op, id, rq );
00778 
00779               } else {
00780                      BackendDB     *be;
00781 
00782                      LDAP_STAILQ_FOREACH( be, &backendDB, be_next ) {
00783                             /* we may want to skip cn=config */
00784                             if ( be == LDAP_STAILQ_FIRST(&backendDB) ) {
00785                                    continue;
00786                             }
00787 
00788                             if ( be->be_search && be->be_modify ) {
00789                                    op->o_bd = be;
00790                                    refint_repair( op, id, rq );
00791                             }
00792                      }
00793               }
00794 
00795               for ( dp = rq->attrs; dp; dp = dp_next ) {
00796                      dp_next = dp->next;
00797                      for ( ra = dp->attrs; ra; ra = ra_next ) {
00798                             ra_next = ra->next;
00799                             ber_bvarray_free_x( ra->new_nvals, op->o_tmpmemctx );
00800                             ber_bvarray_free_x( ra->new_vals, op->o_tmpmemctx );
00801                             ber_bvarray_free_x( ra->old_nvals, op->o_tmpmemctx );
00802                             ber_bvarray_free_x( ra->old_vals, op->o_tmpmemctx );
00803                             op->o_tmpfree( ra, op->o_tmpmemctx );
00804                      }
00805                      op->o_tmpfree( dp->ndn.bv_val, op->o_tmpmemctx );
00806                      op->o_tmpfree( dp->dn.bv_val, op->o_tmpmemctx );
00807                      op->o_tmpfree( dp, op->o_tmpmemctx );
00808               }
00809               op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx );
00810 
00811               if ( !BER_BVISNULL( &rq->newndn )) {
00812                      ch_free( rq->newndn.bv_val );
00813                      ch_free( rq->newdn.bv_val );
00814               }
00815               ch_free( rq->oldndn.bv_val );
00816               ch_free( rq->olddn.bv_val );
00817               ch_free( rq );
00818        }
00819 
00820        /* free filter */
00821        for ( fptr = ftop.f_or; fptr; ) {
00822               Filter *f_next = fptr->f_next;
00823               op->o_tmpfree( fptr, op->o_tmpmemctx );
00824               fptr = f_next;
00825        }
00826 
00827        /* wait until we get explicitly scheduled again */
00828        ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
00829        ldap_pvt_runqueue_stoptask( &slapd_rq, id->qtask );
00830        ldap_pvt_runqueue_resched( &slapd_rq,id->qtask, 1 );
00831        ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
00832 
00833        return NULL;
00834 }
00835 
00836 /*
00837 ** refint_response
00838 ** search for matching records and modify them
00839 */
00840 
00841 static int
00842 refint_response(
00843        Operation *op,
00844        SlapReply *rs
00845 )
00846 {
00847        slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
00848        refint_data *id = on->on_bi.bi_private;
00849        BerValue pdn;
00850        int ac;
00851        refint_q *rq;
00852        BackendDB *db = NULL;
00853        refint_attrs *ip;
00854 
00855        /* If the main op failed or is not a Delete or ModRdn, ignore it */
00856        if (( op->o_tag != LDAP_REQ_DELETE && op->o_tag != LDAP_REQ_MODRDN ) ||
00857               rs->sr_err != LDAP_SUCCESS )
00858               return SLAP_CB_CONTINUE;
00859 
00860        /*
00861        ** validate (and count) the list of attrs;
00862        **
00863        */
00864 
00865        for(ip = id->attrs, ac = 0; ip; ip = ip->next, ac++);
00866        if(!ac) {
00867               Debug( LDAP_DEBUG_TRACE,
00868                      "refint_response called without any attributes\n", 0, 0, 0 );
00869               return SLAP_CB_CONTINUE;
00870        }
00871 
00872        /*
00873        ** find the backend that matches our configured basedn;
00874        ** make sure it exists and has search and modify methods;
00875        **
00876        */
00877 
00878        if ( on->on_info->oi_origdb != frontendDB ) {
00879               db = select_backend(&id->dn, 1);
00880 
00881               if ( db ) {
00882                      if ( !db->be_search || !db->be_modify ) {
00883                             Debug( LDAP_DEBUG_TRACE,
00884                                    "refint_response: backend missing search and/or modify\n",
00885                                    0, 0, 0 );
00886                             return SLAP_CB_CONTINUE;
00887                      }
00888               } else {
00889                      Debug( LDAP_DEBUG_TRACE,
00890                             "refint_response: no backend for our baseDN %s??\n",
00891                             id->dn.bv_val, 0, 0 );
00892                      return SLAP_CB_CONTINUE;
00893               }
00894        }
00895 
00896        rq = ch_calloc( 1, sizeof( refint_q ));
00897        ber_dupbv( &rq->olddn, &op->o_req_dn );
00898        ber_dupbv( &rq->oldndn, &op->o_req_ndn );
00899        rq->db = db;
00900        rq->rdata = id;
00901 
00902        if ( op->o_tag == LDAP_REQ_MODRDN ) {
00903               if ( op->oq_modrdn.rs_newSup ) {
00904                      pdn = *op->oq_modrdn.rs_newSup;
00905               } else {
00906                      dnParent( &op->o_req_dn, &pdn );
00907               }
00908               build_new_dn( &rq->newdn, &pdn, &op->orr_newrdn, NULL );
00909               if ( op->oq_modrdn.rs_nnewSup ) {
00910                      pdn = *op->oq_modrdn.rs_nnewSup;
00911               } else {
00912                      dnParent( &op->o_req_ndn, &pdn );
00913               }
00914               build_new_dn( &rq->newndn, &pdn, &op->orr_nnewrdn, NULL );
00915        }
00916 
00917        ldap_pvt_thread_mutex_lock( &id->qmutex );
00918        if ( id->qtail ) {
00919               id->qtail->next = rq;
00920        } else {
00921               id->qhead = rq;
00922        }
00923        id->qtail = rq;
00924        ldap_pvt_thread_mutex_unlock( &id->qmutex );
00925 
00926        ac = 0;
00927        ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
00928        if ( !id->qtask ) {
00929               id->qtask = ldap_pvt_runqueue_insert( &slapd_rq, RUNQ_INTERVAL,
00930                      refint_qtask, id, "refint_qtask",
00931                      op->o_bd->be_suffix[0].bv_val );
00932               ac = 1;
00933        } else {
00934               if ( !ldap_pvt_runqueue_isrunning( &slapd_rq, id->qtask ) &&
00935                      !id->qtask->next_sched.tv_sec ) {
00936                      id->qtask->interval.tv_sec = 0;
00937                      ldap_pvt_runqueue_resched( &slapd_rq, id->qtask, 0 );
00938                      id->qtask->interval.tv_sec = RUNQ_INTERVAL;
00939                      ac = 1;
00940               }
00941        }
00942        ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
00943        if ( ac )
00944               slap_wake_listener();
00945 
00946        return SLAP_CB_CONTINUE;
00947 }
00948 
00949 /*
00950 ** init_module is last so the symbols resolve "for free" --
00951 ** it expects to be called automagically during dynamic module initialization
00952 */
00953 
00954 int refint_initialize() {
00955        int rc;
00956 
00957        mr_dnSubtreeMatch = mr_find( "dnSubtreeMatch" );
00958        if ( mr_dnSubtreeMatch == NULL ) {
00959               Debug( LDAP_DEBUG_ANY, "refint_initialize: "
00960                      "unable to find MatchingRule 'dnSubtreeMatch'.\n",
00961                      0, 0, 0 );
00962               return 1;
00963        }
00964 
00965        /* statically declared just after the #includes at top */
00966        refint.on_bi.bi_type = "refint";
00967        refint.on_bi.bi_db_init = refint_db_init;
00968        refint.on_bi.bi_db_destroy = refint_db_destroy;
00969        refint.on_bi.bi_db_open = refint_open;
00970        refint.on_bi.bi_db_close = refint_close;
00971        refint.on_response = refint_response;
00972 
00973        refint.on_bi.bi_cf_ocs = refintocs;
00974        rc = config_register_schema ( refintcfg, refintocs );
00975        if ( rc ) return rc;
00976 
00977        return(overlay_register(&refint));
00978 }
00979 
00980 #if SLAPD_OVER_REFINT == SLAPD_MOD_DYNAMIC && defined(PIC)
00981 int init_module(int argc, char *argv[]) {
00982        return refint_initialize();
00983 }
00984 #endif
00985 
00986 #endif /* SLAPD_OVER_REFINT */