]> git.sur5r.net Git - openldap/blob - servers/slapd/overlays/refint.c
support subtree rename (ITS#5097)
[openldap] / servers / slapd / overlays / refint.c
1 /* refint.c - referential integrity module */
2 /* $OpenLDAP$ */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4  *
5  * Copyright 2004-2007 The OpenLDAP Foundation.
6  * Portions Copyright 2004 Symas Corporation.
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted only as authorized by the OpenLDAP
11  * Public License.
12  *
13  * A copy of this license is available in the file LICENSE in the
14  * top-level directory of the distribution or, alternatively, at
15  * <http://www.OpenLDAP.org/license.html>.
16  */
17 /* ACKNOWLEDGEMENTS:
18  * This work was initially developed by Symas Corp. for inclusion in
19  * OpenLDAP Software.  This work was sponsored by Hewlett-Packard.
20  */
21
22 #include "portable.h"
23
24 /* This module maintains referential integrity for a set of
25  * DN-valued attributes by searching for all references to a given
26  * DN whenever the DN is changed or its entry is deleted, and making
27  * the appropriate update.
28  *
29  * Updates are performed using the database rootdn in a separate task
30  * to allow the original operation to complete immediately.
31  */
32
33 #ifdef SLAPD_OVER_REFINT
34
35 #include <stdio.h>
36
37 #include <ac/string.h>
38 #include <ac/socket.h>
39
40 #include "slap.h"
41 #include "config.h"
42 #include "ldap_rq.h"
43
44 static slap_overinst refint;
45
46 /* The DN to use in the ModifiersName for all refint updates */
47 static BerValue refint_dn = BER_BVC("cn=Referential Integrity Overlay");
48 static BerValue refint_ndn = BER_BVC("cn=referential integrity overlay");
49
50 typedef struct refint_attrs_s {
51         struct refint_attrs_s   *next;
52         AttributeDescription    *attr;
53         BerVarray               old_vals;
54         BerVarray               old_nvals;
55         BerVarray               new_vals;
56         BerVarray               new_nvals;
57 } refint_attrs;
58
59 typedef struct dependents_s {
60         struct dependents_s *next;
61         BerValue dn;                            /* target dn */
62         BerValue ndn;
63         refint_attrs *attrs;
64 } dependent_data;
65
66 typedef struct refint_q {
67         struct refint_q *next;
68         struct refint_data_s *rdata;
69         dependent_data *attrs;          /* entries and attrs returned from callback */
70         BackendDB *db;
71         BerValue olddn;
72         BerValue oldndn;
73         BerValue newdn;
74         BerValue newndn;
75 } refint_q;
76
77 typedef struct refint_data_s {
78         const char *message;                    /* breadcrumbs */
79         struct refint_attrs_s *attrs;   /* list of known attrs */
80         BerValue dn;                            /* basedn in parent, */
81         BerValue nothing;                       /* the nothing value, if needed */
82         BerValue nnothing;                      /* normalized nothingness */
83         struct re_s *qtask;
84         refint_q *qhead;
85         refint_q *qtail;
86         ldap_pvt_thread_mutex_t qmutex;
87 } refint_data;
88
89 #define RUNQ_INTERVAL   36000   /* a long time */
90
91 static MatchingRule     *mr_dnSubtreeMatch;
92
93 enum {
94         REFINT_ATTRS = 1,
95         REFINT_NOTHING
96 };
97
98 static ConfigDriver refint_cf_gen;
99
100 static ConfigTable refintcfg[] = {
101         { "refint_attributes", "attribute...", 2, 0, 0,
102           ARG_MAGIC|REFINT_ATTRS, refint_cf_gen,
103           "( OLcfgOvAt:11.1 NAME 'olcRefintAttribute' "
104           "DESC 'Attributes for referential integrity' "
105           "EQUALITY caseIgnoreMatch "
106           "SYNTAX OMsDirectoryString )", NULL, NULL },
107         { "refint_nothing", "string", 2, 2, 0,
108           ARG_DN|ARG_MAGIC|REFINT_NOTHING, refint_cf_gen,
109           "( OLcfgOvAt:11.2 NAME 'olcRefintNothing' "
110           "DESC 'Replacement DN to supply when needed' "
111           "SYNTAX OMsDN SINGLE-VALUE )", NULL, NULL },
112         { NULL, NULL, 0, 0, 0, ARG_IGNORED }
113 };
114
115 static ConfigOCs refintocs[] = {
116         { "( OLcfgOvOc:11.1 "
117           "NAME 'olcRefintConfig' "
118           "DESC 'Referential integrity configuration' "
119           "SUP olcOverlayConfig "
120           "MAY ( olcRefintAttribute $ olcRefintNothing ) )",
121           Cft_Overlay, refintcfg },
122         { NULL, 0, NULL }
123 };
124
125 static int
126 refint_cf_gen(ConfigArgs *c)
127 {
128         slap_overinst *on = (slap_overinst *)c->bi;
129         refint_data *dd = (refint_data *)on->on_bi.bi_private;
130         refint_attrs *ip, *pip, **pipp = NULL;
131         AttributeDescription *ad;
132         const char *text;
133         int rc = ARG_BAD_CONF;
134         int i;
135
136         switch ( c->op ) {
137         case SLAP_CONFIG_EMIT:
138                 switch ( c->type ) {
139                 case REFINT_ATTRS:
140                         ip = dd->attrs;
141                         while ( ip ) {
142                                 value_add_one( &c->rvalue_vals,
143                                                &ip->attr->ad_cname );
144                                 ip = ip->next;
145                         }
146                         rc = 0;
147                         break;
148                 case REFINT_NOTHING:
149                         if ( !BER_BVISEMPTY( &dd->nothing )) {
150                                 rc = value_add_one( &c->rvalue_vals,
151                                                     &dd->nothing );
152                                 if ( rc ) return rc;
153                                 rc = value_add_one( &c->rvalue_nvals,
154                                                     &dd->nnothing );
155                                 return rc;
156                         }
157                         rc = 0;
158                         break;
159                 default:
160                         abort ();
161                 }
162                 break;
163         case LDAP_MOD_DELETE:
164                 switch ( c->type ) {
165                 case REFINT_ATTRS:
166                         pipp = &dd->attrs;
167                         if ( c->valx < 0 ) {
168                                 ip = *pipp;
169                                 *pipp = NULL;
170                                 while ( ip ) {
171                                         pip = ip;
172                                         ip = ip->next;
173                                         ch_free ( pip );
174                                 }
175                         } else {
176                                 /* delete from linked list */
177                                 for ( i=0; i < c->valx; ++i ) {
178                                         pipp = &(*pipp)->next;
179                                 }
180                                 ip = *pipp;
181                                 *pipp = (*pipp)->next;
182
183                                 /* AttributeDescriptions are global so
184                                  * shouldn't be freed here... */
185                                 ch_free ( ip );
186                         }
187                         rc = 0;
188                         break;
189                 case REFINT_NOTHING:
190                         if ( dd->nothing.bv_val )
191                                 ber_memfree ( dd->nothing.bv_val );
192                         if ( dd->nnothing.bv_val )
193                                 ber_memfree ( dd->nnothing.bv_val );
194                         dd->nothing.bv_len = 0;
195                         dd->nnothing.bv_len = 0;
196                         rc = 0;
197                         break;
198                 default:
199                         abort ();
200                 }
201                 break;
202         case SLAP_CONFIG_ADD:
203                 /* fallthrough to LDAP_MOD_ADD */
204         case LDAP_MOD_ADD:
205                 switch ( c->type ) {
206                 case REFINT_ATTRS:
207                         rc = 0;
208                         for ( i=1; i < c->argc; ++i ) {
209                                 ad = NULL;
210                                 if ( slap_str2ad ( c->argv[i], &ad, &text )
211                                      == LDAP_SUCCESS) {
212                                         ip = ch_malloc (
213                                                 sizeof ( refint_attrs ) );
214                                         ip->attr = ad;
215                                         ip->next = dd->attrs;
216                                         dd->attrs = ip;
217                                 } else {
218                                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
219                                                 "%s <%s>: %s", c->argv[0], c->argv[i], text );
220                                         Debug ( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE,
221                                                 "%s: %s\n", c->log, c->cr_msg, 0 );
222                                         rc = ARG_BAD_CONF;
223                                 }
224                         }
225                         break;
226                 case REFINT_NOTHING:
227                         if ( dd->nothing.bv_val )
228                                 ber_memfree ( dd->nothing.bv_val );
229                         if ( dd->nnothing.bv_val )
230                                 ber_memfree ( dd->nnothing.bv_val );
231                         dd->nothing = c->value_dn;
232                         dd->nnothing = c->value_ndn;
233                         rc = 0;
234                         break;
235                 default:
236                         abort ();
237                 }
238                 break;
239         default:
240                 abort ();
241         }
242
243         return rc;
244 }
245
246 /*
247 ** allocate new refint_data;
248 ** store in on_bi.bi_private;
249 **
250 */
251
252 static int
253 refint_db_init(
254         BackendDB       *be,
255         ConfigReply     *cr
256 )
257 {
258         slap_overinst *on = (slap_overinst *)be->bd_info;
259         refint_data *id = ch_calloc(1,sizeof(refint_data));
260
261         id->message = "_init";
262         on->on_bi.bi_private = id;
263         ldap_pvt_thread_mutex_init( &id->qmutex );
264         return(0);
265 }
266
267 static int
268 refint_db_destroy(
269         BackendDB       *be,
270         ConfigReply     *cr
271 )
272 {
273         slap_overinst *on = (slap_overinst *)be->bd_info;
274
275         if ( on->on_bi.bi_private ) {
276                 refint_data *id = on->on_bi.bi_private;
277                 on->on_bi.bi_private = NULL;
278                 ldap_pvt_thread_mutex_destroy( &id->qmutex );
279                 ch_free( id );
280         }
281         return(0);
282 }
283
284 /*
285 ** initialize, copy basedn if not already set
286 **
287 */
288
289 static int
290 refint_open(
291         BackendDB *be,
292         ConfigReply *cr
293 )
294 {
295         slap_overinst *on       = (slap_overinst *)be->bd_info;
296         refint_data *id = on->on_bi.bi_private;
297         id->message             = "_open";
298
299         if ( BER_BVISNULL( &id->dn )) {
300                 if ( BER_BVISNULL( &be->be_nsuffix[0] ))
301                         return -1;
302                 ber_dupbv( &id->dn, &be->be_nsuffix[0] );
303         }
304         return(0);
305 }
306
307
308 /*
309 ** foreach configured attribute:
310 **      free it;
311 ** free our basedn;
312 ** (do not) free id->message;
313 ** reset on_bi.bi_private;
314 ** free our config data;
315 **
316 */
317
318 static int
319 refint_close(
320         BackendDB *be,
321         ConfigReply *cr
322 )
323 {
324         slap_overinst *on       = (slap_overinst *) be->bd_info;
325         refint_data *id = on->on_bi.bi_private;
326         refint_attrs *ii, *ij;
327         id->message             = "_close";
328
329         for(ii = id->attrs; ii; ii = ij) {
330                 ij = ii->next;
331                 ch_free(ii);
332         }
333         id->attrs = NULL;
334
335         ch_free( id->dn.bv_val );
336         BER_BVZERO( &id->dn );
337         ch_free( id->nothing.bv_val );
338         BER_BVZERO( &id->nothing );
339         ch_free( id->nnothing.bv_val );
340         BER_BVZERO( &id->nnothing );
341
342         return(0);
343 }
344
345 /*
346 ** search callback
347 ** generates a list of Attributes from search results
348 */
349
350 static int
351 refint_search_cb(
352         Operation *op,
353         SlapReply *rs
354 )
355 {
356         Attribute *a;
357         BerVarray b = NULL;
358         refint_q *rq = op->o_callback->sc_private;
359         refint_data *dd = rq->rdata;
360         refint_attrs *ia, *da = dd->attrs, *na;
361         dependent_data *ip;
362         int i;
363
364         Debug(LDAP_DEBUG_TRACE, "refint_search_cb <%s>\n",
365                 rs->sr_entry ? rs->sr_entry->e_name.bv_val : "NOTHING", 0, 0);
366
367         if (rs->sr_type != REP_SEARCH || !rs->sr_entry) return(0);
368
369         /*
370         ** foreach configured attribute type:
371         **      if this attr exists in the search result,
372         **      and it has a value matching the target:
373         **              allocate an attr;
374         **              if this is a delete and there's only one value:
375         **                      allocate the same attr again;
376         **
377         */
378
379         ip = op->o_tmpalloc(sizeof(dependent_data), op->o_tmpmemctx );
380         ber_dupbv_x( &ip->dn, &rs->sr_entry->e_name, op->o_tmpmemctx );
381         ber_dupbv_x( &ip->ndn, &rs->sr_entry->e_nname, op->o_tmpmemctx );
382         ip->next = rq->attrs;
383         rq->attrs = ip;
384         ip->attrs = NULL;
385         for(ia = da; ia; ia = ia->next) {
386                 if ( (a = attr_find(rs->sr_entry->e_attrs, ia->attr) ) ) {
387                         int             first = -1, count = 0, deleted = 0;
388
389                         na = NULL;
390
391                         for(i = 0, b = a->a_nvals; b[i].bv_val; i++) {
392                                 count++;
393
394                                 if(dnIsSuffix(&b[i], &rq->oldndn)) {
395                                         /* first match? create structure */
396                                         if ( na == NULL ) {
397                                                 na = op->o_tmpcalloc( 1,
398                                                         sizeof( refint_attrs ),
399                                                         op->o_tmpmemctx );
400                                                 na->next = ip->attrs;
401                                                 ip->attrs = na;
402                                                 na->attr = ia->attr;
403
404                                                 /* delete, or exact match? note it's first match */
405                                                 if ( BER_BVISEMPTY( &rq->newdn ) &&
406                                                         b[i].bv_len == rq->oldndn.bv_len )
407                                                 {
408                                                         first = i;
409                                                 }
410                                         }
411
412                                         /* if it's a rename, or a subordinate match,
413                                          * save old and build new dn */
414                                         if ( !BER_BVISEMPTY( &rq->newdn ) &&
415                                                 b[i].bv_len != rq->oldndn.bv_len )
416                                         {
417                                                 struct berval   newsub, newdn;
418
419                                                 /* if not first, save first as well */
420                                                 if ( first != -1 ) {
421                                                         ber_bvarray_add_x( &na->old_vals, &a->a_vals[first], op->o_tmpmemctx );
422                                                         ber_bvarray_add_x( &na->old_nvals, &a->a_nvals[first], op->o_tmpmemctx );
423
424                                                         newsub = a->a_vals[first];
425                                                         newsub.bv_len -= rq->olddn.bv_len + 1;
426
427                                                         build_new_dn( &newdn, &rq->newdn, &newsub, op->o_tmpmemctx );
428
429                                                         ber_bvarray_add_x( &na->new_vals, &newdn, op->o_tmpmemctx );
430
431                                                         newsub = a->a_nvals[first];
432                                                         newsub.bv_len -= rq->oldndn.bv_len + 1;
433
434                                                         build_new_dn( &newdn, &rq->newndn, &newsub, op->o_tmpmemctx );
435
436                                                         ber_bvarray_add_x( &na->new_nvals, &newdn, op->o_tmpmemctx );
437                                                         
438                                                         first = -1;
439                                                 }
440
441                                                 ber_bvarray_add_x( &na->old_vals, &a->a_vals[i], op->o_tmpmemctx );
442                                                 ber_bvarray_add_x( &na->old_nvals, &a->a_nvals[i], op->o_tmpmemctx );
443
444                                                 newsub = a->a_vals[i];
445                                                 newsub.bv_len -= rq->olddn.bv_len + 1;
446
447                                                 build_new_dn( &newdn, &rq->newdn, &newsub, op->o_tmpmemctx );
448
449                                                 ber_bvarray_add_x( &na->new_vals, &newdn, op->o_tmpmemctx );
450
451                                                 newsub = a->a_nvals[i];
452                                                 newsub.bv_len -= rq->oldndn.bv_len + 1;
453
454                                                 build_new_dn( &newdn, &rq->newndn, &newsub, op->o_tmpmemctx );
455
456                                                 ber_bvarray_add_x( &na->new_nvals, &newdn, op->o_tmpmemctx );
457                                         }
458
459                                         /* count deteles */
460                                         if ( BER_BVISEMPTY( &rq->newdn ) ) {
461                                                 deleted++;
462                                         }
463                                 }
464
465                                 /* If this is a delete and no value would be left, and
466                                  * we have a nothing DN configured, allocate the attr again.
467                                  */
468                                 if ( count == deleted && !BER_BVISNULL(&dd->nothing) )
469                                 {
470                                         na = op->o_tmpcalloc( 1,
471                                                 sizeof( refint_attrs ),
472                                                 op->o_tmpmemctx );
473                                         na->next = ip->attrs;
474                                         ip->attrs = na;
475                                         na->attr = ia->attr;
476                                 }
477
478                                 Debug( LDAP_DEBUG_TRACE, "refint_search_cb: %s: %s (#%d)\n",
479                                         a->a_desc->ad_cname.bv_val, rq->olddn.bv_val, count );
480                         }
481                 }
482         }
483
484         return(0);
485 }
486
487 static void *
488 refint_qtask( void *ctx, void *arg )
489 {
490         struct re_s *rtask = arg;
491         refint_data *id = rtask->arg;
492         Connection conn = {0};
493         OperationBuffer opbuf;
494         Operation *op;
495         SlapReply rs = {REP_RESULT};
496         slap_callback cb = { NULL, NULL, NULL, NULL };
497         Filter ftop, *fptr;
498         refint_q *rq;
499         dependent_data *dp, *dp_next;
500         refint_attrs *ra, *ip;
501         int rc;
502
503         connection_fake_init( &conn, &opbuf, ctx );
504         op = &opbuf.ob_op;
505
506         /*
507         ** build a search filter for all configured attributes;
508         ** populate our Operation;
509         ** pass our data (attr list, dn) to backend via sc_private;
510         ** call the backend search function;
511         ** nb: (|(one=thing)) is valid, but do smart formatting anyway;
512         ** nb: 16 is arbitrarily a dozen or so extra bytes;
513         **
514         */
515
516         ftop.f_choice = LDAP_FILTER_OR;
517         ftop.f_next = NULL;
518         ftop.f_or = NULL;
519         op->ors_filter = &ftop;
520         for(ip = id->attrs; ip; ip = ip->next) {
521                 fptr = op->o_tmpcalloc( sizeof(Filter) + sizeof(MatchingRuleAssertion),
522                         1, op->o_tmpmemctx );
523                 /* Use (attr:dnSubtreeMatch:=value) to catch subtree rename
524                  * and subtree delete where supported */
525                 fptr->f_choice = LDAP_FILTER_EXT;
526                 fptr->f_mra = (MatchingRuleAssertion *)(fptr+1);
527                 fptr->f_mr_rule = mr_dnSubtreeMatch;
528                 fptr->f_mr_rule_text = mr_dnSubtreeMatch->smr_str;
529                 fptr->f_mr_desc = ip->attr;
530                 fptr->f_mr_dnattrs = 0;
531                 fptr->f_next = ftop.f_or;
532                 ftop.f_or = fptr;
533         }
534
535         for (;;) {
536                 /* Dequeue an op */
537                 ldap_pvt_thread_mutex_lock( &id->qmutex );
538                 rq = id->qhead;
539                 if ( rq ) {
540                         id->qhead = rq->next;
541                         if ( !id->qhead )
542                                 id->qtail = NULL;
543                 }
544                 ldap_pvt_thread_mutex_unlock( &id->qmutex );
545                 if ( !rq )
546                         break;
547
548                 for (fptr = ftop.f_or; fptr; fptr=fptr->f_next )
549                         fptr->f_mr_value = rq->oldndn;
550
551                 filter2bv_x( op, op->ors_filter, &op->ors_filterstr );
552
553                 /* callback gets the searched dn instead */
554                 cb.sc_private   = rq;
555                 cb.sc_response  = refint_search_cb;
556                 op->o_callback  = &cb;
557                 op->o_tag       = LDAP_REQ_SEARCH;
558                 op->ors_scope   = LDAP_SCOPE_SUBTREE;
559                 op->ors_deref   = LDAP_DEREF_NEVER;
560                 op->ors_limit   = NULL;
561                 op->ors_slimit  = SLAP_NO_LIMIT;
562                 op->ors_tlimit  = SLAP_NO_LIMIT;
563
564                 /* no attrs! */
565                 op->ors_attrs = slap_anlist_no_attrs;
566
567                 op->o_req_ndn = id->dn;
568                 op->o_req_dn = id->dn;
569                 op->o_bd = rq->db;
570                 op->o_dn = op->o_bd->be_rootdn;
571                 op->o_ndn = op->o_bd->be_rootndn;
572                 slap_op_time( &op->o_time, &op->o_tincr );
573
574                 /* search */
575                 rc = op->o_bd->be_search(op, &rs);
576
577                 op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx );
578
579                 if(rc != LDAP_SUCCESS) {
580                         Debug( LDAP_DEBUG_TRACE,
581                                 "refint_response: search failed: %d\n",
582                                 rc, 0, 0 );
583                         continue;
584                 }
585
586                 /* safety? paranoid just in case */
587                 if(!cb.sc_private) {
588                         Debug( LDAP_DEBUG_TRACE,
589                                 "refint_response: callback wiped out sc_private?!\n",
590                                 0, 0, 0 );
591                         continue;
592                 }
593
594                 /* Set up the Modify requests */
595                 cb.sc_response  = &slap_null_cb;
596                 op->o_tag       = LDAP_REQ_MODIFY;
597
598                 /*
599                 ** [our search callback builds a list of attrs]
600                 ** foreach attr:
601                 **      make sure its dn has a backend;
602                 **      build Modification* chain;
603                 **      call the backend modify function;
604                 **
605                 */
606
607                 for(dp = rq->attrs; dp; dp = dp_next) {
608                         Modifications *m, *first = NULL;
609
610                         dp_next = dp->next;
611
612                         op->orm_modlist = NULL;
613
614                         op->o_req_dn    = dp->dn;
615                         op->o_req_ndn   = dp->ndn;
616                         op->o_bd = select_backend(&dp->ndn, 1);
617                         if(!op->o_bd) {
618                                 Debug( LDAP_DEBUG_TRACE,
619                                         "refint_response: no backend for DN %s!\n",
620                                         dp->dn.bv_val, 0, 0 );
621                                 goto done;
622                         }
623                         rs.sr_type      = REP_RESULT;
624                         for (ra = dp->attrs; ra; ra = dp->attrs) {
625                                 size_t  len;
626
627                                 dp->attrs = ra->next;
628                                 /* Set our ModifiersName */
629                                 if ( SLAP_LASTMOD( op->o_bd )) {
630                                         m = op->o_tmpalloc( sizeof(Modifications) +
631                                                 4*sizeof(BerValue), op->o_tmpmemctx );
632                                         m->sml_next = op->orm_modlist;
633                                         if ( !first )
634                                                 first = m;
635                                         op->orm_modlist = m;
636                                         m->sml_op = LDAP_MOD_REPLACE;
637                                         m->sml_flags = SLAP_MOD_INTERNAL;
638                                         m->sml_desc = slap_schema.si_ad_modifiersName;
639                                         m->sml_type = m->sml_desc->ad_cname;
640                                         m->sml_values = (BerVarray)(m+1);
641                                         m->sml_nvalues = m->sml_values+2;
642                                         BER_BVZERO( &m->sml_values[1] );
643                                         BER_BVZERO( &m->sml_nvalues[1] );
644                                         m->sml_values[0] = refint_dn;
645                                         m->sml_nvalues[0] = refint_ndn;
646                                 }
647                                 if ( !BER_BVISEMPTY( &rq->newdn ) || ( ra->next &&
648                                         ra->attr == ra->next->attr ))
649                                 {
650                                         len = sizeof(Modifications);
651
652                                         if ( ra->new_vals == NULL ) {
653                                                 len += 4*sizeof(BerValue);
654                                         }
655
656                                         m = op->o_tmpalloc( len, op->o_tmpmemctx );
657                                         m->sml_next = op->orm_modlist;
658                                         if ( !first )
659                                                 first = m;
660                                         op->orm_modlist = m;
661                                         m->sml_op = LDAP_MOD_ADD;
662                                         m->sml_flags = 0;
663                                         m->sml_desc = ra->attr;
664                                         m->sml_type = ra->attr->ad_cname;
665                                         if ( ra->new_vals == NULL ) {
666                                                 m->sml_values = (BerVarray)(m+1);
667                                                 m->sml_nvalues = m->sml_values+2;
668                                                 BER_BVZERO( &m->sml_values[1] );
669                                                 BER_BVZERO( &m->sml_nvalues[1] );
670                                                 if ( BER_BVISEMPTY( &rq->newdn )) {
671                                                         op->o_tmpfree( ra, op->o_tmpmemctx );
672                                                         ra = dp->attrs;
673                                                         dp->attrs = ra->next;
674                                                         m->sml_values[0] = id->nothing;
675                                                         m->sml_nvalues[0] = id->nnothing;
676                                                 } else {
677                                                         m->sml_values[0] = rq->newdn;
678                                                         m->sml_nvalues[0] = rq->newndn;
679                                                 }
680                                         } else {
681                                                 m->sml_values = ra->new_vals;
682                                                 m->sml_nvalues = ra->new_nvals;
683                                         }
684                                 }
685
686                                 len = sizeof(Modifications);
687                                 if ( ra->old_vals == NULL ) {
688                                         len += 4*sizeof(BerValue);
689                                 }
690
691                                 m = op->o_tmpalloc( len, op->o_tmpmemctx );
692                                 m->sml_next = op->orm_modlist;
693                                 op->orm_modlist = m;
694                                 if ( !first )
695                                         first = m;
696                                 m->sml_op = LDAP_MOD_DELETE;
697                                 m->sml_flags = 0;
698                                 m->sml_desc = ra->attr;
699                                 m->sml_type = ra->attr->ad_cname;
700                                 if ( ra->old_vals == NULL ) {
701                                         m->sml_values = (BerVarray)(m+1);
702                                         m->sml_nvalues = m->sml_values+2;
703                                         m->sml_values[0] = rq->olddn;
704                                         m->sml_nvalues[0] = rq->oldndn;
705                                         BER_BVZERO( &m->sml_values[1] );
706                                         BER_BVZERO( &m->sml_nvalues[1] );
707                                 } else {
708                                         m->sml_values = ra->old_vals;
709                                         m->sml_nvalues = ra->old_nvals;
710                                 }
711                                 op->o_tmpfree( ra, op->o_tmpmemctx );
712                         }
713
714                         op->o_dn = op->o_bd->be_rootdn;
715                         op->o_ndn = op->o_bd->be_rootndn;
716                         slap_op_time( &op->o_time, &op->o_tincr );
717                         if((rc = op->o_bd->be_modify(op, &rs)) != LDAP_SUCCESS) {
718                                 Debug( LDAP_DEBUG_TRACE,
719                                         "refint_response: dependent modify failed: %d\n",
720                                         rs.sr_err, 0, 0 );
721                         }
722
723                         while (( m = op->orm_modlist )) {
724                                 op->orm_modlist = m->sml_next;
725                                 if ( m->sml_values && m->sml_values != (BerVarray)(m+1) ) {
726                                         ber_bvarray_free_x( m->sml_values, op->o_tmpmemctx );
727                                         ber_bvarray_free_x( m->sml_nvalues, op->o_tmpmemctx );
728                                 }
729                                 op->o_tmpfree( m, op->o_tmpmemctx );
730                                 if ( m == first ) break;
731                         }
732                         slap_mods_free( op->orm_modlist, 1 );
733                         op->o_tmpfree( dp->ndn.bv_val, op->o_tmpmemctx );
734                         op->o_tmpfree( dp->dn.bv_val, op->o_tmpmemctx );
735                         op->o_tmpfree( dp, op->o_tmpmemctx );
736                 }
737 done:
738                 if ( !BER_BVISNULL( &rq->newndn )) {
739                         ch_free( rq->newndn.bv_val );
740                         ch_free( rq->newdn.bv_val );
741                 }
742                 ch_free( rq->oldndn.bv_val );
743                 ch_free( rq->olddn.bv_val );
744                 ch_free( rq );
745         }
746
747         /* free filter */
748         for ( fptr = ftop.f_or; fptr; ) {
749                 Filter *f_next = fptr->f_next;
750                 op->o_tmpfree( fptr, op->o_tmpmemctx );
751                 fptr = f_next;
752         }
753
754         /* wait until we get explicitly scheduled again */
755         ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
756         ldap_pvt_runqueue_stoptask( &slapd_rq, id->qtask );
757         ldap_pvt_runqueue_resched( &slapd_rq,id->qtask, 1 );
758         ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
759
760         return NULL;
761 }
762
763 /*
764 ** refint_response
765 ** search for matching records and modify them
766 */
767
768 static int
769 refint_response(
770         Operation *op,
771         SlapReply *rs
772 )
773 {
774         slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
775         refint_data *id = on->on_bi.bi_private;
776         BerValue pdn;
777         int ac;
778         refint_q *rq;
779         BackendDB *db;
780         refint_attrs *ip;
781
782         id->message = "_refint_response";
783
784         /* If the main op failed or is not a Delete or ModRdn, ignore it */
785         if (( op->o_tag != LDAP_REQ_DELETE && op->o_tag != LDAP_REQ_MODRDN ) ||
786                 rs->sr_err != LDAP_SUCCESS )
787                 return SLAP_CB_CONTINUE;
788
789         /*
790         ** validate (and count) the list of attrs;
791         **
792         */
793
794         for(ip = id->attrs, ac = 0; ip; ip = ip->next, ac++);
795         if(!ac) {
796                 Debug( LDAP_DEBUG_TRACE,
797                         "refint_response called without any attributes\n", 0, 0, 0 );
798                 return SLAP_CB_CONTINUE;
799         }
800
801         /*
802         ** find the backend that matches our configured basedn;
803         ** make sure it exists and has search and modify methods;
804         **
805         */
806
807         db = select_backend(&id->dn, 1);
808
809         if(db) {
810                 if (!db->be_search || !db->be_modify) {
811                         Debug( LDAP_DEBUG_TRACE,
812                                 "refint_response: backend missing search and/or modify\n",
813                                 0, 0, 0 );
814                         return SLAP_CB_CONTINUE;
815                 }
816         } else {
817                 Debug( LDAP_DEBUG_TRACE,
818                         "refint_response: no backend for our baseDN %s??\n",
819                         id->dn.bv_val, 0, 0 );
820                 return SLAP_CB_CONTINUE;
821         }
822
823         rq = ch_calloc( 1, sizeof( refint_q ));
824         ber_dupbv( &rq->olddn, &op->o_req_dn );
825         ber_dupbv( &rq->oldndn, &op->o_req_ndn );
826         rq->db = db;
827         rq->rdata = id;
828
829         if(op->o_tag == LDAP_REQ_MODRDN) {
830                 if ( op->oq_modrdn.rs_newSup ) {
831                         pdn = *op->oq_modrdn.rs_newSup;
832                 } else {
833                         dnParent( &op->o_req_dn, &pdn );
834                 }
835                 build_new_dn( &rq->newdn, &pdn, &op->orr_newrdn, NULL );
836                 if ( op->oq_modrdn.rs_nnewSup ) {
837                         pdn = *op->oq_modrdn.rs_nnewSup;
838                 } else {
839                         dnParent( &op->o_req_ndn, &pdn );
840                 }
841                 build_new_dn( &rq->newndn, &pdn, &op->orr_nnewrdn, NULL );
842         }
843
844         ldap_pvt_thread_mutex_lock( &id->qmutex );
845         if ( id->qtail ) {
846                 id->qtail->next = rq;
847         } else {
848                 id->qhead = rq;
849         }
850         id->qtail = rq;
851         ldap_pvt_thread_mutex_unlock( &id->qmutex );
852
853         ac = 0;
854         ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
855         if ( !id->qtask ) {
856                 id->qtask = ldap_pvt_runqueue_insert( &slapd_rq, RUNQ_INTERVAL,
857                         refint_qtask, id, "refint_qtask",
858                         op->o_bd->be_suffix[0].bv_val );
859                 ac = 1;
860         } else {
861                 if ( !ldap_pvt_runqueue_isrunning( &slapd_rq, id->qtask ) &&
862                         !id->qtask->next_sched.tv_sec ) {
863                         id->qtask->interval.tv_sec = 0;
864                         ldap_pvt_runqueue_resched( &slapd_rq, id->qtask, 0 );
865                         id->qtask->interval.tv_sec = RUNQ_INTERVAL;
866                         ac = 1;
867                 }
868         }
869         ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
870         if ( ac )
871                 slap_wake_listener();
872
873         return SLAP_CB_CONTINUE;
874 }
875
876 /*
877 ** init_module is last so the symbols resolve "for free" --
878 ** it expects to be called automagically during dynamic module initialization
879 */
880
881 int refint_initialize() {
882         int rc;
883
884         mr_dnSubtreeMatch = mr_find( "dnSubtreeMatch" );
885         if ( mr_dnSubtreeMatch == NULL ) {
886                 Debug( LDAP_DEBUG_ANY, "refint_initialize: "
887                         "unable to find MatchingRule 'dnSubtreeMatch'.\n",
888                         0, 0, 0 );
889                 return 1;
890         }
891
892         /* statically declared just after the #includes at top */
893         refint.on_bi.bi_type = "refint";
894         refint.on_bi.bi_db_init = refint_db_init;
895         refint.on_bi.bi_db_destroy = refint_db_destroy;
896         refint.on_bi.bi_db_open = refint_open;
897         refint.on_bi.bi_db_close = refint_close;
898         refint.on_response = refint_response;
899
900         refint.on_bi.bi_cf_ocs = refintocs;
901         rc = config_register_schema ( refintcfg, refintocs );
902         if ( rc ) return rc;
903
904         return(overlay_register(&refint));
905 }
906
907 #if SLAPD_OVER_REFINT == SLAPD_MOD_DYNAMIC && defined(PIC)
908 int init_module(int argc, char *argv[]) {
909         return refint_initialize();
910 }
911 #endif
912
913 #endif /* SLAPD_OVER_REFINT */