refint.c 22.7 KB
Newer Older
1
2
3
4
/* refint.c - referential integrity module */
/* $OpenLDAP$ */
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
 *
Kurt Zeilenga's avatar
Kurt Zeilenga committed
5
 * Copyright 2004-2008 The OpenLDAP Foundation.
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
 * Portions Copyright 2004 Symas Corporation.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted only as authorized by the OpenLDAP
 * Public License.
 *
 * A copy of this license is available in the file LICENSE in the
 * top-level directory of the distribution or, alternatively, at
 * <http://www.OpenLDAP.org/license.html>.
 */
/* ACKNOWLEDGEMENTS:
 * This work was initially developed by Symas Corp. for inclusion in
 * OpenLDAP Software.  This work was sponsored by Hewlett-Packard.
 */

#include "portable.h"

/* This module maintains referential integrity for a set of
 * DN-valued attributes by searching for all references to a given
 * DN whenever the DN is changed or its entry is deleted, and making
 * the appropriate update.
 *
29
30
 * Updates are performed using the database rootdn in a separate task
 * to allow the original operation to complete immediately.
31
32
33
34
35
36
37
38
39
40
 */

#ifdef SLAPD_OVER_REFINT

#include <stdio.h>

#include <ac/string.h>
#include <ac/socket.h>

#include "slap.h"
41
#include "config.h"
42
#include "ldap_rq.h"
43
44
45

static slap_overinst refint;

46
47
48
49
/* The DN to use in the ModifiersName for all refint updates */
static BerValue refint_dn = BER_BVC("cn=Referential Integrity Overlay");
static BerValue refint_ndn = BER_BVC("cn=referential integrity overlay");

50
typedef struct refint_attrs_s {
51
52
53
54
55
56
	struct refint_attrs_s	*next;
	AttributeDescription	*attr;
	BerVarray		old_vals;
	BerVarray		old_nvals;
	BerVarray		new_vals;
	BerVarray		new_nvals;
57
	int				ra_numvals;
58
59
60
61
62
} refint_attrs;

typedef struct dependents_s {
	struct dependents_s *next;
	BerValue dn;				/* target dn */
63
64
	BerValue ndn;
	refint_attrs *attrs;
65
66
} dependent_data;

67
68
69
70
71
72
73
74
75
76
77
typedef struct refint_q {
	struct refint_q *next;
	struct refint_data_s *rdata;
	dependent_data *attrs;		/* entries and attrs returned from callback */
	BackendDB *db;
	BerValue olddn;
	BerValue oldndn;
	BerValue newdn;
	BerValue newndn;
} refint_q;

78
79
80
typedef struct refint_data_s {
	const char *message;			/* breadcrumbs */
	struct refint_attrs_s *attrs;	/* list of known attrs */
81
	BerValue dn;				/* basedn in parent, */
82
83
	BerValue nothing;			/* the nothing value, if needed */
	BerValue nnothing;			/* normalized nothingness */
84
85
86
87
	struct re_s *qtask;
	refint_q *qhead;
	refint_q *qtail;
	ldap_pvt_thread_mutex_t qmutex;
88
89
} refint_data;

90
91
#define	RUNQ_INTERVAL	36000	/* a long time */

92
93
static MatchingRule	*mr_dnSubtreeMatch;

94
95
96
97
98
99
100
101
102
103
104
105
enum {
	REFINT_ATTRS = 1,
	REFINT_NOTHING
};

static ConfigDriver refint_cf_gen;

static ConfigTable refintcfg[] = {
	{ "refint_attributes", "attribute...", 2, 0, 0,
	  ARG_MAGIC|REFINT_ATTRS, refint_cf_gen,
	  "( OLcfgOvAt:11.1 NAME 'olcRefintAttribute' "
	  "DESC 'Attributes for referential integrity' "
106
	  "EQUALITY caseIgnoreMatch "
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
	  "SYNTAX OMsDirectoryString )", NULL, NULL },
	{ "refint_nothing", "string", 2, 2, 0,
	  ARG_DN|ARG_MAGIC|REFINT_NOTHING, refint_cf_gen,
	  "( OLcfgOvAt:11.2 NAME 'olcRefintNothing' "
	  "DESC 'Replacement DN to supply when needed' "
	  "SYNTAX OMsDN SINGLE-VALUE )", NULL, NULL },
	{ NULL, NULL, 0, 0, 0, ARG_IGNORED }
};

static ConfigOCs refintocs[] = {
	{ "( OLcfgOvOc:11.1 "
	  "NAME 'olcRefintConfig' "
	  "DESC 'Referential integrity configuration' "
	  "SUP olcOverlayConfig "
	  "MAY ( olcRefintAttribute $ olcRefintNothing ) )",
	  Cft_Overlay, refintcfg },
	{ NULL, 0, NULL }
};

static int
refint_cf_gen(ConfigArgs *c)
{
	slap_overinst *on = (slap_overinst *)c->bi;
	refint_data *dd = (refint_data *)on->on_bi.bi_private;
	refint_attrs *ip, *pip, **pipp = NULL;
	AttributeDescription *ad;
	const char *text;
	int rc = ARG_BAD_CONF;
	int i;

	switch ( c->op ) {
	case SLAP_CONFIG_EMIT:
		switch ( c->type ) {
		case REFINT_ATTRS:
			ip = dd->attrs;
			while ( ip ) {
				value_add_one( &c->rvalue_vals,
					       &ip->attr->ad_cname );
				ip = ip->next;
			}
			rc = 0;
			break;
		case REFINT_NOTHING:
			if ( !BER_BVISEMPTY( &dd->nothing )) {
				rc = value_add_one( &c->rvalue_vals,
						    &dd->nothing );
				if ( rc ) return rc;
				rc = value_add_one( &c->rvalue_nvals,
						    &dd->nnothing );
				return rc;
			}
			rc = 0;
			break;
		default:
			abort ();
		}
		break;
	case LDAP_MOD_DELETE:
		switch ( c->type ) {
		case REFINT_ATTRS:
			pipp = &dd->attrs;
			if ( c->valx < 0 ) {
				ip = *pipp;
				*pipp = NULL;
				while ( ip ) {
					pip = ip;
					ip = ip->next;
					ch_free ( pip );
				}
			} else {
				/* delete from linked list */
				for ( i=0; i < c->valx; ++i ) {
					pipp = &(*pipp)->next;
				}
				ip = *pipp;
				*pipp = (*pipp)->next;

				/* AttributeDescriptions are global so
				 * shouldn't be freed here... */
				ch_free ( ip );
			}
			rc = 0;
			break;
		case REFINT_NOTHING:
			if ( dd->nothing.bv_val )
				ber_memfree ( dd->nothing.bv_val );
			if ( dd->nnothing.bv_val )
				ber_memfree ( dd->nnothing.bv_val );
			dd->nothing.bv_len = 0;
			dd->nnothing.bv_len = 0;
			rc = 0;
			break;
		default:
			abort ();
		}
		break;
	case SLAP_CONFIG_ADD:
		/* fallthrough to LDAP_MOD_ADD */
	case LDAP_MOD_ADD:
		switch ( c->type ) {
		case REFINT_ATTRS:
			rc = 0;
			for ( i=1; i < c->argc; ++i ) {
				ad = NULL;
				if ( slap_str2ad ( c->argv[i], &ad, &text )
				     == LDAP_SUCCESS) {
					ip = ch_malloc (
						sizeof ( refint_attrs ) );
					ip->attr = ad;
					ip->next = dd->attrs;
					dd->attrs = ip;
				} else {
219
					snprintf( c->cr_msg, sizeof( c->cr_msg ),
220
221
						"%s <%s>: %s", c->argv[0], c->argv[i], text );
					Debug ( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE,
222
						"%s: %s\n", c->log, c->cr_msg, 0 );
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
					rc = ARG_BAD_CONF;
				}
			}
			break;
		case REFINT_NOTHING:
			if ( dd->nothing.bv_val )
				ber_memfree ( dd->nothing.bv_val );
			if ( dd->nnothing.bv_val )
				ber_memfree ( dd->nnothing.bv_val );
			dd->nothing = c->value_dn;
			dd->nnothing = c->value_ndn;
			rc = 0;
			break;
		default:
			abort ();
		}
		break;
	default:
		abort ();
	}

	return rc;
}

247
248
249
250
251
252
253
254
/*
** allocate new refint_data;
** store in on_bi.bi_private;
**
*/

static int
refint_db_init(
255
256
	BackendDB	*be,
	ConfigReply	*cr
257
258
259
)
{
	slap_overinst *on = (slap_overinst *)be->bd_info;
260
	refint_data *id = ch_calloc(1,sizeof(refint_data));
261

262
263
	id->message = "_init";
	on->on_bi.bi_private = id;
264
	ldap_pvt_thread_mutex_init( &id->qmutex );
265
266
267
268
	return(0);
}

static int
269
refint_db_destroy(
270
271
	BackendDB	*be,
	ConfigReply	*cr
272
273
)
{
274
	slap_overinst *on = (slap_overinst *)be->bd_info;
275

276
	if ( on->on_bi.bi_private ) {
277
		refint_data *id = on->on_bi.bi_private;
278
		on->on_bi.bi_private = NULL;
279
280
		ldap_pvt_thread_mutex_destroy( &id->qmutex );
		ch_free( id );
281
282
283
284
285
	}
	return(0);
}

/*
286
** initialize, copy basedn if not already set
287
288
289
290
291
**
*/

static int
refint_open(
292
293
	BackendDB *be,
	ConfigReply *cr
294
295
296
297
298
)
{
	slap_overinst *on	= (slap_overinst *)be->bd_info;
	refint_data *id	= on->on_bi.bi_private;
	id->message		= "_open";
299
300
301
302
303
304

	if ( BER_BVISNULL( &id->dn )) {
		if ( BER_BVISNULL( &be->be_nsuffix[0] ))
			return -1;
		ber_dupbv( &id->dn, &be->be_nsuffix[0] );
	}
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
	return(0);
}


/*
** foreach configured attribute:
**	free it;
** free our basedn;
** (do not) free id->message;
** reset on_bi.bi_private;
** free our config data;
**
*/

static int
refint_close(
321
322
	BackendDB *be,
	ConfigReply *cr
323
324
325
326
327
328
329
330
331
332
333
)
{
	slap_overinst *on	= (slap_overinst *) be->bd_info;
	refint_data *id	= on->on_bi.bi_private;
	refint_attrs *ii, *ij;
	id->message		= "_close";

	for(ii = id->attrs; ii; ii = ij) {
		ij = ii->next;
		ch_free(ii);
	}
Pierangelo Masarati's avatar
Pierangelo Masarati committed
334
335
336
337
338
339
340
341
	id->attrs = NULL;

	ch_free( id->dn.bv_val );
	BER_BVZERO( &id->dn );
	ch_free( id->nothing.bv_val );
	BER_BVZERO( &id->nothing );
	ch_free( id->nnothing.bv_val );
	BER_BVZERO( &id->nnothing );
342
343
344
345
346

	return(0);
}

/*
347
348
** search callback
** generates a list of Attributes from search results
349
350
351
*/

static int
352
refint_search_cb(
353
354
355
356
357
358
	Operation *op,
	SlapReply *rs
)
{
	Attribute *a;
	BerVarray b = NULL;
359
360
361
	refint_q *rq = op->o_callback->sc_private;
	refint_data *dd = rq->rdata;
	refint_attrs *ia, *da = dd->attrs, *na;
362
	dependent_data *ip;
363
364
	int i;

365
	Debug(LDAP_DEBUG_TRACE, "refint_search_cb <%s>\n",
366
367
368
369
370
371
372
373
		rs->sr_entry ? rs->sr_entry->e_name.bv_val : "NOTHING", 0, 0);

	if (rs->sr_type != REP_SEARCH || !rs->sr_entry) return(0);

	/*
	** foreach configured attribute type:
	**	if this attr exists in the search result,
	**	and it has a value matching the target:
374
375
376
	**		allocate an attr;
	**		if this is a delete and there's only one value:
	**			allocate the same attr again;
377
378
379
	**
	*/

380
381
382
383
384
385
	ip = op->o_tmpalloc(sizeof(dependent_data), op->o_tmpmemctx );
	ber_dupbv_x( &ip->dn, &rs->sr_entry->e_name, op->o_tmpmemctx );
	ber_dupbv_x( &ip->ndn, &rs->sr_entry->e_nname, op->o_tmpmemctx );
	ip->next = rq->attrs;
	rq->attrs = ip;
	ip->attrs = NULL;
386
	for(ia = da; ia; ia = ia->next) {
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
		if ( (a = attr_find(rs->sr_entry->e_attrs, ia->attr) ) ) {
			int		first = -1, count = 0, deleted = 0;

			na = NULL;

			for(i = 0, b = a->a_nvals; b[i].bv_val; i++) {
				count++;

				if(dnIsSuffix(&b[i], &rq->oldndn)) {
					/* first match? create structure */
					if ( na == NULL ) {
						na = op->o_tmpcalloc( 1,
							sizeof( refint_attrs ),
							op->o_tmpmemctx );
						na->next = ip->attrs;
						ip->attrs = na;
						na->attr = ia->attr;

						/* delete, or exact match? note it's first match */
						if ( BER_BVISEMPTY( &rq->newdn ) &&
							b[i].bv_len == rq->oldndn.bv_len )
						{
							first = i;
						}
					}

					/* if it's a rename, or a subordinate match,
					 * save old and build new dn */
					if ( !BER_BVISEMPTY( &rq->newdn ) &&
						b[i].bv_len != rq->oldndn.bv_len )
					{
						struct berval	newsub, newdn, olddn, oldndn;

						/* if not first, save first as well */
						if ( first != -1 ) {

							ber_dupbv_x( &olddn, &a->a_vals[first], op->o_tmpmemctx );
							ber_bvarray_add_x( &na->old_vals, &olddn, op->o_tmpmemctx );
							ber_dupbv_x( &oldndn, &a->a_nvals[first], op->o_tmpmemctx );
							ber_bvarray_add_x( &na->old_nvals, &oldndn, op->o_tmpmemctx );
427
							na->ra_numvals++;
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449

							newsub = a->a_vals[first];
							newsub.bv_len -= rq->olddn.bv_len + 1;

							build_new_dn( &newdn, &rq->newdn, &newsub, op->o_tmpmemctx );

							ber_bvarray_add_x( &na->new_vals, &newdn, op->o_tmpmemctx );

							newsub = a->a_nvals[first];
							newsub.bv_len -= rq->oldndn.bv_len + 1;

							build_new_dn( &newdn, &rq->newndn, &newsub, op->o_tmpmemctx );

							ber_bvarray_add_x( &na->new_nvals, &newdn, op->o_tmpmemctx );
							
							first = -1;
						}

						ber_dupbv_x( &olddn, &a->a_vals[i], op->o_tmpmemctx );
						ber_bvarray_add_x( &na->old_vals, &olddn, op->o_tmpmemctx );
						ber_dupbv_x( &oldndn, &a->a_nvals[i], op->o_tmpmemctx );
						ber_bvarray_add_x( &na->old_nvals, &oldndn, op->o_tmpmemctx );
450
						na->ra_numvals++;
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466

						newsub = a->a_vals[i];
						newsub.bv_len -= rq->olddn.bv_len + 1;

						build_new_dn( &newdn, &rq->newdn, &newsub, op->o_tmpmemctx );

						ber_bvarray_add_x( &na->new_vals, &newdn, op->o_tmpmemctx );

						newsub = a->a_nvals[i];
						newsub.bv_len -= rq->oldndn.bv_len + 1;

						build_new_dn( &newdn, &rq->newndn, &newsub, op->o_tmpmemctx );

						ber_bvarray_add_x( &na->new_nvals, &newdn, op->o_tmpmemctx );
					}

467
					/* count deletes */
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
					if ( BER_BVISEMPTY( &rq->newdn ) ) {
						deleted++;
					}
				}

				/* If this is a delete and no value would be left, and
				 * we have a nothing DN configured, allocate the attr again.
				 */
				if ( count == deleted && !BER_BVISNULL(&dd->nothing) )
				{
					na = op->o_tmpcalloc( 1,
						sizeof( refint_attrs ),
						op->o_tmpmemctx );
					na->next = ip->attrs;
					ip->attrs = na;
					na->attr = ia->attr;
				}

				Debug( LDAP_DEBUG_TRACE, "refint_search_cb: %s: %s (#%d)\n",
					a->a_desc->ad_cname.bv_val, rq->olddn.bv_val, count );
488
			}
489
		}
490
	}
491

492
493
494
	return(0);
}

495
496
static void *
refint_qtask( void *ctx, void *arg )
497
{
498
499
500
501
502
503
504
505
506
	struct re_s *rtask = arg;
	refint_data *id = rtask->arg;
	Connection conn = {0};
	OperationBuffer opbuf;
	Operation *op;
	SlapReply rs = {REP_RESULT};
	slap_callback cb = { NULL, NULL, NULL, NULL };
	Filter ftop, *fptr;
	refint_q *rq;
507
	dependent_data *dp, *dp_next;
508
509
	refint_attrs *ra, *ip;
	int rc;
510

511
512
	connection_fake_init( &conn, &opbuf, ctx );
	op = &opbuf.ob_op;
513
514

	/*
515
516
517
518
519
520
	** build a search filter for all configured attributes;
	** populate our Operation;
	** pass our data (attr list, dn) to backend via sc_private;
	** call the backend search function;
	** nb: (|(one=thing)) is valid, but do smart formatting anyway;
	** nb: 16 is arbitrarily a dozen or so extra bytes;
521
522
523
	**
	*/

524
525
526
527
528
	ftop.f_choice = LDAP_FILTER_OR;
	ftop.f_next = NULL;
	ftop.f_or = NULL;
	op->ors_filter = &ftop;
	for(ip = id->attrs; ip; ip = ip->next) {
529
530
531
532
533
534
535
		fptr = op->o_tmpcalloc( sizeof(Filter) + sizeof(MatchingRuleAssertion),
			1, op->o_tmpmemctx );
		/* Use (attr:dnSubtreeMatch:=value) to catch subtree rename
		 * and subtree delete where supported */
		fptr->f_choice = LDAP_FILTER_EXT;
		fptr->f_mra = (MatchingRuleAssertion *)(fptr+1);
		fptr->f_mr_rule = mr_dnSubtreeMatch;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
536
		fptr->f_mr_rule_text = mr_dnSubtreeMatch->smr_bvoid;
537
538
		fptr->f_mr_desc = ip->attr;
		fptr->f_mr_dnattrs = 0;
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
		fptr->f_next = ftop.f_or;
		ftop.f_or = fptr;
	}

	for (;;) {
		/* Dequeue an op */
		ldap_pvt_thread_mutex_lock( &id->qmutex );
		rq = id->qhead;
		if ( rq ) {
			id->qhead = rq->next;
			if ( !id->qhead )
				id->qtail = NULL;
		}
		ldap_pvt_thread_mutex_unlock( &id->qmutex );
		if ( !rq )
			break;

		for (fptr = ftop.f_or; fptr; fptr=fptr->f_next )
557
			fptr->f_mr_value = rq->oldndn;
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577

		filter2bv_x( op, op->ors_filter, &op->ors_filterstr );

		/* callback gets the searched dn instead */
		cb.sc_private	= rq;
		cb.sc_response	= refint_search_cb;
		op->o_callback	= &cb;
		op->o_tag	= LDAP_REQ_SEARCH;
		op->ors_scope	= LDAP_SCOPE_SUBTREE;
		op->ors_deref	= LDAP_DEREF_NEVER;
		op->ors_limit   = NULL;
		op->ors_slimit	= SLAP_NO_LIMIT;
		op->ors_tlimit	= SLAP_NO_LIMIT;

		/* no attrs! */
		op->ors_attrs = slap_anlist_no_attrs;

		op->o_req_ndn = id->dn;
		op->o_req_dn = id->dn;
		op->o_bd = rq->db;
578
579
580
		op->o_dn = op->o_bd->be_rootdn;
		op->o_ndn = op->o_bd->be_rootndn;
		slap_op_time( &op->o_time, &op->o_tincr );
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614

		/* search */
		rc = op->o_bd->be_search(op, &rs);

		op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx );

		if(rc != LDAP_SUCCESS) {
			Debug( LDAP_DEBUG_TRACE,
				"refint_response: search failed: %d\n",
				rc, 0, 0 );
			continue;
		}

		/* safety? paranoid just in case */
		if(!cb.sc_private) {
			Debug( LDAP_DEBUG_TRACE,
				"refint_response: callback wiped out sc_private?!\n",
				0, 0, 0 );
			continue;
		}

		/* Set up the Modify requests */
		cb.sc_response	= &slap_null_cb;
		op->o_tag	= LDAP_REQ_MODIFY;

		/*
		** [our search callback builds a list of attrs]
		** foreach attr:
		**	make sure its dn has a backend;
		**	build Modification* chain;
		**	call the backend modify function;
		**
		*/

615
		for(dp = rq->attrs; dp; dp = dp_next) {
616
617
			Modifications *m, *first = NULL;

618
619
			dp_next = dp->next;

620
621
			op->orm_modlist = NULL;

622
623
			op->o_req_dn	= dp->dn;
			op->o_req_ndn	= dp->ndn;
624
			op->o_bd = select_backend(&dp->ndn, 1);
625
626
627
628
629
			if(!op->o_bd) {
				Debug( LDAP_DEBUG_TRACE,
					"refint_response: no backend for DN %s!\n",
					dp->dn.bv_val, 0, 0 );
				goto done;
630
			}
631
632
			rs.sr_type	= REP_RESULT;
			for (ra = dp->attrs; ra; ra = dp->attrs) {
633
634
				size_t	len;

635
				dp->attrs = ra->next;
636
637
638
639
640
641
642
643
644
645
646
647
				/* Set our ModifiersName */
				if ( SLAP_LASTMOD( op->o_bd )) {
					m = op->o_tmpalloc( sizeof(Modifications) +
						4*sizeof(BerValue), op->o_tmpmemctx );
					m->sml_next = op->orm_modlist;
					if ( !first )
						first = m;
					op->orm_modlist = m;
					m->sml_op = LDAP_MOD_REPLACE;
					m->sml_flags = SLAP_MOD_INTERNAL;
					m->sml_desc = slap_schema.si_ad_modifiersName;
					m->sml_type = m->sml_desc->ad_cname;
648
					m->sml_numvals = 1;
649
650
651
652
653
654
655
					m->sml_values = (BerVarray)(m+1);
					m->sml_nvalues = m->sml_values+2;
					BER_BVZERO( &m->sml_values[1] );
					BER_BVZERO( &m->sml_nvalues[1] );
					m->sml_values[0] = refint_dn;
					m->sml_nvalues[0] = refint_ndn;
				}
656
				if ( !BER_BVISEMPTY( &rq->newdn ) || ( ra->next &&
657
658
659
660
661
662
663
664
665
					ra->attr == ra->next->attr ))
				{
					len = sizeof(Modifications);

					if ( ra->new_vals == NULL ) {
						len += 4*sizeof(BerValue);
					}

					m = op->o_tmpalloc( len, op->o_tmpmemctx );
666
667
668
669
670
671
672
673
					m->sml_next = op->orm_modlist;
					if ( !first )
						first = m;
					op->orm_modlist = m;
					m->sml_op = LDAP_MOD_ADD;
					m->sml_flags = 0;
					m->sml_desc = ra->attr;
					m->sml_type = ra->attr->ad_cname;
674
675
676
677
678
					if ( ra->new_vals == NULL ) {
						m->sml_values = (BerVarray)(m+1);
						m->sml_nvalues = m->sml_values+2;
						BER_BVZERO( &m->sml_values[1] );
						BER_BVZERO( &m->sml_nvalues[1] );
679
						m->sml_numvals = 1;
680
681
682
683
684
685
686
687
688
689
						if ( BER_BVISEMPTY( &rq->newdn )) {
							op->o_tmpfree( ra, op->o_tmpmemctx );
							ra = dp->attrs;
							dp->attrs = ra->next;
							m->sml_values[0] = id->nothing;
							m->sml_nvalues[0] = id->nnothing;
						} else {
							m->sml_values[0] = rq->newdn;
							m->sml_nvalues[0] = rq->newndn;
						}
690
					} else {
691
692
						m->sml_values = ra->new_vals;
						m->sml_nvalues = ra->new_nvals;
693
						m->sml_numvals = ra->ra_numvals;
694
695
					}
				}
696
697
698
699
700
701
702

				len = sizeof(Modifications);
				if ( ra->old_vals == NULL ) {
					len += 4*sizeof(BerValue);
				}

				m = op->o_tmpalloc( len, op->o_tmpmemctx );
703
704
705
706
707
708
709
710
				m->sml_next = op->orm_modlist;
				op->orm_modlist = m;
				if ( !first )
					first = m;
				m->sml_op = LDAP_MOD_DELETE;
				m->sml_flags = 0;
				m->sml_desc = ra->attr;
				m->sml_type = ra->attr->ad_cname;
711
				if ( ra->old_vals == NULL ) {
712
					m->sml_numvals = 1;
713
714
715
716
717
718
719
720
721
					m->sml_values = (BerVarray)(m+1);
					m->sml_nvalues = m->sml_values+2;
					m->sml_values[0] = rq->olddn;
					m->sml_nvalues[0] = rq->oldndn;
					BER_BVZERO( &m->sml_values[1] );
					BER_BVZERO( &m->sml_nvalues[1] );
				} else {
					m->sml_values = ra->old_vals;
					m->sml_nvalues = ra->old_nvals;
722
					m->sml_numvals = ra->ra_numvals;
723
				}
724
725
726
727
728
				op->o_tmpfree( ra, op->o_tmpmemctx );
			}

			op->o_dn = op->o_bd->be_rootdn;
			op->o_ndn = op->o_bd->be_rootndn;
729
			slap_op_time( &op->o_time, &op->o_tincr );
730
731
732
733
734
735
736
737
			if((rc = op->o_bd->be_modify(op, &rs)) != LDAP_SUCCESS) {
				Debug( LDAP_DEBUG_TRACE,
					"refint_response: dependent modify failed: %d\n",
					rs.sr_err, 0, 0 );
			}

			while (( m = op->orm_modlist )) {
				op->orm_modlist = m->sml_next;
738
739
740
741
				if ( m->sml_values && m->sml_values != (BerVarray)(m+1) ) {
					ber_bvarray_free_x( m->sml_values, op->o_tmpmemctx );
					ber_bvarray_free_x( m->sml_nvalues, op->o_tmpmemctx );
				}
742
743
744
745
746
747
748
				op->o_tmpfree( m, op->o_tmpmemctx );
				if ( m == first ) break;
			}
			slap_mods_free( op->orm_modlist, 1 );
			op->o_tmpfree( dp->ndn.bv_val, op->o_tmpmemctx );
			op->o_tmpfree( dp->dn.bv_val, op->o_tmpmemctx );
			op->o_tmpfree( dp, op->o_tmpmemctx );
749
		}
750
751
752
753
754
755
756
757
done:
		if ( !BER_BVISNULL( &rq->newndn )) {
			ch_free( rq->newndn.bv_val );
			ch_free( rq->newdn.bv_val );
		}
		ch_free( rq->oldndn.bv_val );
		ch_free( rq->olddn.bv_val );
		ch_free( rq );
758
759
	}

760
761
762
763
764
765
766
	/* free filter */
	for ( fptr = ftop.f_or; fptr; ) {
		Filter *f_next = fptr->f_next;
		op->o_tmpfree( fptr, op->o_tmpmemctx );
		fptr = f_next;
	}

767
768
769
770
771
	/* wait until we get explicitly scheduled again */
	ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
	ldap_pvt_runqueue_stoptask( &slapd_rq, id->qtask );
	ldap_pvt_runqueue_resched( &slapd_rq,id->qtask, 1 );
	ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
772

773
774
	return NULL;
}
775
776
777
778
779
780
781
782
783
784
785
786
787
788

/*
** refint_response
** search for matching records and modify them
*/

static int
refint_response(
	Operation *op,
	SlapReply *rs
)
{
	slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
	refint_data *id = on->on_bi.bi_private;
789
	BerValue pdn;
Hallvard Furuseth's avatar
Hallvard Furuseth committed
790
	int ac;
791
792
793
	refint_q *rq;
	BackendDB *db;
	refint_attrs *ip;
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808

	id->message = "_refint_response";

	/* If the main op failed or is not a Delete or ModRdn, ignore it */
	if (( op->o_tag != LDAP_REQ_DELETE && op->o_tag != LDAP_REQ_MODRDN ) ||
		rs->sr_err != LDAP_SUCCESS )
		return SLAP_CB_CONTINUE;

	/*
	** validate (and count) the list of attrs;
	**
	*/

	for(ip = id->attrs, ac = 0; ip; ip = ip->next, ac++);
	if(!ac) {
809
810
		Debug( LDAP_DEBUG_TRACE,
			"refint_response called without any attributes\n", 0, 0, 0 );
811
812
813
814
815
816
817
818
819
		return SLAP_CB_CONTINUE;
	}

	/*
	** find the backend that matches our configured basedn;
	** make sure it exists and has search and modify methods;
	**
	*/

820
	db = select_backend(&id->dn, 1);
821

822
823
	if(db) {
		if (!db->be_search || !db->be_modify) {
824
825
826
			Debug( LDAP_DEBUG_TRACE,
				"refint_response: backend missing search and/or modify\n",
				0, 0, 0 );
827
828
829
			return SLAP_CB_CONTINUE;
		}
	} else {
830
831
832
		Debug( LDAP_DEBUG_TRACE,
			"refint_response: no backend for our baseDN %s??\n",
			id->dn.bv_val, 0, 0 );
833
834
835
		return SLAP_CB_CONTINUE;
	}

836
837
838
839
840
	rq = ch_calloc( 1, sizeof( refint_q ));
	ber_dupbv( &rq->olddn, &op->o_req_dn );
	ber_dupbv( &rq->oldndn, &op->o_req_ndn );
	rq->db = db;
	rq->rdata = id;
841

842
	if(op->o_tag == LDAP_REQ_MODRDN) {
843
844
845
846
847
		if ( op->oq_modrdn.rs_newSup ) {
			pdn = *op->oq_modrdn.rs_newSup;
		} else {
			dnParent( &op->o_req_dn, &pdn );
		}
848
		build_new_dn( &rq->newdn, &pdn, &op->orr_newrdn, NULL );
849
850
851
852
853
		if ( op->oq_modrdn.rs_nnewSup ) {
			pdn = *op->oq_modrdn.rs_nnewSup;
		} else {
			dnParent( &op->o_req_ndn, &pdn );
		}
854
		build_new_dn( &rq->newndn, &pdn, &op->orr_nnewrdn, NULL );
855
856
	}

857
858
859
860
861
	ldap_pvt_thread_mutex_lock( &id->qmutex );
	if ( id->qtail ) {
		id->qtail->next = rq;
	} else {
		id->qhead = rq;
862
	}
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
	id->qtail = rq;
	ldap_pvt_thread_mutex_unlock( &id->qmutex );

	ac = 0;
	ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
	if ( !id->qtask ) {
		id->qtask = ldap_pvt_runqueue_insert( &slapd_rq, RUNQ_INTERVAL,
			refint_qtask, id, "refint_qtask",
			op->o_bd->be_suffix[0].bv_val );
		ac = 1;
	} else {
		if ( !ldap_pvt_runqueue_isrunning( &slapd_rq, id->qtask ) &&
			!id->qtask->next_sched.tv_sec ) {
			id->qtask->interval.tv_sec = 0;
			ldap_pvt_runqueue_resched( &slapd_rq, id->qtask, 0 );
			id->qtask->interval.tv_sec = RUNQ_INTERVAL;
			ac = 1;
880
881
		}
	}
882
883
884
	ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
	if ( ac )
		slap_wake_listener();
885

886
	return SLAP_CB_CONTINUE;
887
888
889
890
891
892
893
}

/*
** init_module is last so the symbols resolve "for free" --
** it expects to be called automagically during dynamic module initialization
*/

894
int refint_initialize() {
895
	int rc;
896

897
898
899
900
901
902
903
904
	mr_dnSubtreeMatch = mr_find( "dnSubtreeMatch" );
	if ( mr_dnSubtreeMatch == NULL ) {
		Debug( LDAP_DEBUG_ANY, "refint_initialize: "
			"unable to find MatchingRule 'dnSubtreeMatch'.\n",
			0, 0, 0 );
		return 1;
	}

905
906
907
	/* statically declared just after the #includes at top */
	refint.on_bi.bi_type = "refint";
	refint.on_bi.bi_db_init = refint_db_init;
908
	refint.on_bi.bi_db_destroy = refint_db_destroy;
909
910
911
912
	refint.on_bi.bi_db_open = refint_open;
	refint.on_bi.bi_db_close = refint_close;
	refint.on_response = refint_response;

913
914
915
916
	refint.on_bi.bi_cf_ocs = refintocs;
	rc = config_register_schema ( refintcfg, refintocs );
	if ( rc ) return rc;

917
918
919
920
921
	return(overlay_register(&refint));
}

#if SLAPD_OVER_REFINT == SLAPD_MOD_DYNAMIC && defined(PIC)
int init_module(int argc, char *argv[]) {
922
	return refint_initialize();
923
924
925
926
}
#endif

#endif /* SLAPD_OVER_REFINT */