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