xref: /netbsd-src/external/bsd/openldap/dist/servers/slapd/overlays/dds.c (revision b7b7574d3bf8eeb51a1fa3977b59142ec6434a55)
1 /*	$NetBSD: dds.c,v 1.1.1.4 2014/05/28 09:58:52 tron Exp $	*/
2 
3 /* $OpenLDAP$ */
4 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5  *
6  * Copyright 2005-2014 The OpenLDAP Foundation.
7  * Portions Copyright 2005-2006 SysNet s.n.c.
8  * All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted only as authorized by the OpenLDAP
12  * Public License.
13  *
14  * A copy of this license is available in the file LICENSE in the
15  * top-level directory of the distribution or, alternatively, at
16  * <http://www.OpenLDAP.org/license.html>.
17  */
18 /* ACKNOWLEDGEMENTS:
19  * This work was initially developed by Pierangelo Masarati for inclusion
20  * in OpenLDAP Software, sponsored by SysNet s.n.c.
21  */
22 
23 #include "portable.h"
24 
25 #ifdef SLAPD_OVER_DDS
26 
27 #include <stdio.h>
28 
29 #include <ac/string.h>
30 #include <ac/time.h>
31 
32 #include "slap.h"
33 #include "lutil.h"
34 #include "ldap_rq.h"
35 
36 #include "config.h"
37 
38 #define	DDS_RF2589_MAX_TTL		(31557600)	/* 1 year + 6 hours */
39 #define	DDS_RF2589_DEFAULT_TTL		(86400)		/* 1 day */
40 #define	DDS_DEFAULT_INTERVAL		(3600)		/* 1 hour */
41 
42 typedef struct dds_info_t {
43 	unsigned		di_flags;
44 #define	DDS_FOFF		(0x1U)		/* is this really needed? */
45 #define	DDS_SET(di, f)		( (di)->di_flags & (f) )
46 
47 #define DDS_OFF(di)		DDS_SET( (di), DDS_FOFF )
48 
49 	time_t			di_max_ttl;
50 	time_t			di_min_ttl;
51 	time_t			di_default_ttl;
52 #define	DDS_DEFAULT_TTL(di)	\
53 	( (di)->di_default_ttl ? (di)->di_default_ttl : (di)->di_max_ttl )
54 
55 	time_t			di_tolerance;
56 
57 	/* expire check interval and task */
58 	time_t			di_interval;
59 #define	DDS_INTERVAL(di)	\
60 	( (di)->di_interval ? (di)->di_interval : DDS_DEFAULT_INTERVAL )
61 	struct re_s		*di_expire_task;
62 
63 	/* allows to limit the maximum number of dynamic objects */
64 	ldap_pvt_thread_mutex_t	di_mutex;
65 	int			di_num_dynamicObjects;
66 	int			di_max_dynamicObjects;
67 
68 	/* used to advertize the dynamicSubtrees in the root DSE,
69 	 * and to select the database in the expiration task */
70 	BerVarray		di_suffix;
71 	BerVarray		di_nsuffix;
72 } dds_info_t;
73 
74 static struct berval slap_EXOP_REFRESH = BER_BVC( LDAP_EXOP_REFRESH );
75 static AttributeDescription	*ad_entryExpireTimestamp;
76 
77 /* list of expired DNs */
78 typedef struct dds_expire_t {
79 	struct berval		de_ndn;
80 	struct dds_expire_t	*de_next;
81 } dds_expire_t;
82 
83 typedef struct dds_cb_t {
84 	dds_expire_t	*dc_ndnlist;
85 } dds_cb_t;
86 
87 static int
88 dds_expire_cb( Operation *op, SlapReply *rs )
89 {
90 	dds_cb_t	*dc = (dds_cb_t *)op->o_callback->sc_private;
91 	dds_expire_t	*de;
92 	int		rc;
93 
94 	switch ( rs->sr_type ) {
95 	case REP_SEARCH:
96 		/* alloc list and buffer for berval all in one */
97 		de = op->o_tmpalloc( sizeof( dds_expire_t ) + rs->sr_entry->e_nname.bv_len + 1,
98 			op->o_tmpmemctx );
99 
100 		de->de_next = dc->dc_ndnlist;
101 		dc->dc_ndnlist = de;
102 
103 		de->de_ndn.bv_len = rs->sr_entry->e_nname.bv_len;
104 		de->de_ndn.bv_val = (char *)&de[ 1 ];
105 		AC_MEMCPY( de->de_ndn.bv_val, rs->sr_entry->e_nname.bv_val,
106 			rs->sr_entry->e_nname.bv_len + 1 );
107 		rc = 0;
108 		break;
109 
110 	case REP_SEARCHREF:
111 	case REP_RESULT:
112 		rc = rs->sr_err;
113 		break;
114 
115 	default:
116 		assert( 0 );
117 	}
118 
119 	return rc;
120 }
121 
122 static int
123 dds_expire( void *ctx, dds_info_t *di )
124 {
125 	Connection	conn = { 0 };
126 	OperationBuffer opbuf;
127 	Operation	*op;
128 	slap_callback	sc = { 0 };
129 	dds_cb_t	dc = { 0 };
130 	dds_expire_t	*de = NULL, **dep;
131 	SlapReply	rs = { REP_RESULT };
132 
133 	time_t		expire;
134 	char		tsbuf[ LDAP_LUTIL_GENTIME_BUFSIZE ];
135 	struct berval	ts;
136 
137 	int		ndeletes, ntotdeletes;
138 
139 	int		rc;
140 	char		*extra = "";
141 
142 	connection_fake_init2( &conn, &opbuf, ctx, 0 );
143 	op = &opbuf.ob_op;
144 
145 	op->o_tag = LDAP_REQ_SEARCH;
146 	memset( &op->oq_search, 0, sizeof( op->oq_search ) );
147 
148 	op->o_bd = select_backend( &di->di_nsuffix[ 0 ], 0 );
149 
150 	op->o_req_dn = op->o_bd->be_suffix[ 0 ];
151 	op->o_req_ndn = op->o_bd->be_nsuffix[ 0 ];
152 
153 	op->o_dn = op->o_bd->be_rootdn;
154 	op->o_ndn = op->o_bd->be_rootndn;
155 
156 	op->ors_scope = LDAP_SCOPE_SUBTREE;
157 	op->ors_tlimit = DDS_INTERVAL( di )/2 + 1;
158 	op->ors_slimit = SLAP_NO_LIMIT;
159 	op->ors_attrs = slap_anlist_no_attrs;
160 
161 	expire = slap_get_time() - di->di_tolerance;
162 	ts.bv_val = tsbuf;
163 	ts.bv_len = sizeof( tsbuf );
164 	slap_timestamp( &expire, &ts );
165 
166 	op->ors_filterstr.bv_len = STRLENOF( "(&(objectClass=" ")(" "<=" "))" )
167 		+ slap_schema.si_oc_dynamicObject->soc_cname.bv_len
168 		+ ad_entryExpireTimestamp->ad_cname.bv_len
169 		+ ts.bv_len;
170 	op->ors_filterstr.bv_val = op->o_tmpalloc( op->ors_filterstr.bv_len + 1, op->o_tmpmemctx );
171 	snprintf( op->ors_filterstr.bv_val, op->ors_filterstr.bv_len + 1,
172 		"(&(objectClass=%s)(%s<=%s))",
173 		slap_schema.si_oc_dynamicObject->soc_cname.bv_val,
174 		ad_entryExpireTimestamp->ad_cname.bv_val, ts.bv_val );
175 
176 	op->ors_filter = str2filter_x( op, op->ors_filterstr.bv_val );
177 	if ( op->ors_filter == NULL ) {
178 		rs.sr_err = LDAP_OTHER;
179 		goto done_search;
180 	}
181 
182 	op->o_callback = &sc;
183 	sc.sc_response = dds_expire_cb;
184 	sc.sc_private = &dc;
185 
186 	(void)op->o_bd->bd_info->bi_op_search( op, &rs );
187 
188 done_search:;
189 	op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx );
190 	filter_free_x( op, op->ors_filter, 1 );
191 
192 	rc = rs.sr_err;
193 	switch ( rs.sr_err ) {
194 	case LDAP_SUCCESS:
195 		break;
196 
197 	case LDAP_NO_SUCH_OBJECT:
198 		/* (ITS#5267) database not created yet? */
199 		rs.sr_err = LDAP_SUCCESS;
200 		extra = " (ignored)";
201 		/* fallthru */
202 
203 	default:
204 		Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
205 			"DDS expired objects lookup failed err=%d%s\n",
206 			rc, extra );
207 		goto done;
208 	}
209 
210 	op->o_tag = LDAP_REQ_DELETE;
211 	op->o_callback = &sc;
212 	sc.sc_response = slap_null_cb;
213 	sc.sc_private = NULL;
214 
215 	for ( ntotdeletes = 0, ndeletes = 1; dc.dc_ndnlist != NULL  && ndeletes > 0; ) {
216 		ndeletes = 0;
217 
218 		for ( dep = &dc.dc_ndnlist; *dep != NULL; ) {
219 			de = *dep;
220 
221 			op->o_req_dn = de->de_ndn;
222 			op->o_req_ndn = de->de_ndn;
223 			(void)op->o_bd->bd_info->bi_op_delete( op, &rs );
224 			switch ( rs.sr_err ) {
225 			case LDAP_SUCCESS:
226 				Log1( LDAP_DEBUG_STATS, LDAP_LEVEL_INFO,
227 					"DDS dn=\"%s\" expired.\n",
228 					de->de_ndn.bv_val );
229 				ndeletes++;
230 				break;
231 
232 			case LDAP_NOT_ALLOWED_ON_NONLEAF:
233 				Log1( LDAP_DEBUG_ANY, LDAP_LEVEL_NOTICE,
234 					"DDS dn=\"%s\" is non-leaf; "
235 					"deferring.\n",
236 					de->de_ndn.bv_val );
237 				dep = &de->de_next;
238 				de = NULL;
239 				break;
240 
241 			default:
242 				Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_NOTICE,
243 					"DDS dn=\"%s\" err=%d; "
244 					"deferring.\n",
245 					de->de_ndn.bv_val, rs.sr_err );
246 				break;
247 			}
248 
249 			if ( de != NULL ) {
250 				*dep = de->de_next;
251 				op->o_tmpfree( de, op->o_tmpmemctx );
252 			}
253 		}
254 
255 		ntotdeletes += ndeletes;
256 	}
257 
258 	rs.sr_err = LDAP_SUCCESS;
259 
260 	Log1( LDAP_DEBUG_STATS, LDAP_LEVEL_INFO,
261 		"DDS expired=%d\n", ntotdeletes );
262 
263 done:;
264 	return rs.sr_err;
265 }
266 
267 static void *
268 dds_expire_fn( void *ctx, void *arg )
269 {
270 	struct re_s     *rtask = arg;
271 	dds_info_t	*di = rtask->arg;
272 
273 	assert( di->di_expire_task == rtask );
274 
275 	(void)dds_expire( ctx, di );
276 
277 	ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
278 	if ( ldap_pvt_runqueue_isrunning( &slapd_rq, rtask )) {
279 		ldap_pvt_runqueue_stoptask( &slapd_rq, rtask );
280 	}
281 	ldap_pvt_runqueue_resched( &slapd_rq, rtask, 0 );
282 	ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
283 
284 	return NULL;
285 }
286 
287 /* frees the callback */
288 static int
289 dds_freeit_cb( Operation *op, SlapReply *rs )
290 {
291 	op->o_tmpfree( op->o_callback, op->o_tmpmemctx );
292 	op->o_callback = NULL;
293 
294 	return SLAP_CB_CONTINUE;
295 }
296 
297 /* updates counter - installed on add/delete only if required */
298 static int
299 dds_counter_cb( Operation *op, SlapReply *rs )
300 {
301 	assert( rs->sr_type == REP_RESULT );
302 
303 	if ( rs->sr_err == LDAP_SUCCESS ) {
304 		dds_info_t	*di = op->o_callback->sc_private;
305 
306 		ldap_pvt_thread_mutex_lock( &di->di_mutex );
307 		switch ( op->o_tag ) {
308 		case LDAP_REQ_DELETE:
309 			assert( di->di_num_dynamicObjects > 0 );
310 			di->di_num_dynamicObjects--;
311 			break;
312 
313 		case LDAP_REQ_ADD:
314 			assert( di->di_num_dynamicObjects < di->di_max_dynamicObjects );
315 			di->di_num_dynamicObjects++;
316 			break;
317 
318 		default:
319 			assert( 0 );
320 		}
321 		ldap_pvt_thread_mutex_unlock( &di->di_mutex );
322 	}
323 
324 	return dds_freeit_cb( op, rs );
325 }
326 
327 static int
328 dds_op_add( Operation *op, SlapReply *rs )
329 {
330 	slap_overinst	*on = (slap_overinst *)op->o_bd->bd_info;
331 	dds_info_t	*di = on->on_bi.bi_private;
332 	int		is_dynamicObject;
333 
334 	if ( DDS_OFF( di ) ) {
335 		return SLAP_CB_CONTINUE;
336 	}
337 
338 	is_dynamicObject = is_entry_dynamicObject( op->ora_e );
339 
340 	/* FIXME: do not allow this right now, pending clarification */
341 	if ( is_dynamicObject ) {
342 		rs->sr_err = LDAP_SUCCESS;
343 
344 		if ( is_entry_referral( op->ora_e ) ) {
345 			rs->sr_err = LDAP_OBJECT_CLASS_VIOLATION;
346 			rs->sr_text = "a referral cannot be a dynamicObject";
347 
348 		} else if ( is_entry_alias( op->ora_e ) ) {
349 			rs->sr_err = LDAP_OBJECT_CLASS_VIOLATION;
350 			rs->sr_text = "an alias cannot be a dynamicObject";
351 		}
352 
353 		if ( rs->sr_err != LDAP_SUCCESS ) {
354 			op->o_bd->bd_info = (BackendInfo *)on->on_info;
355 			send_ldap_result( op, rs );
356 			return rs->sr_err;
357 		}
358 	}
359 
360 	/* we don't allow dynamicObjects to have static subordinates */
361 	if ( !dn_match( &op->o_req_ndn, &op->o_bd->be_nsuffix[ 0 ] ) ) {
362 		struct berval	p_ndn;
363 		Entry		*e = NULL;
364 		int		rc;
365 		BackendInfo	*bi = op->o_bd->bd_info;
366 
367 		dnParent( &op->o_req_ndn, &p_ndn );
368 		op->o_bd->bd_info = (BackendInfo *)on->on_info;
369 		rc = be_entry_get_rw( op, &p_ndn,
370 			slap_schema.si_oc_dynamicObject, NULL, 0, &e );
371 		if ( rc == LDAP_SUCCESS && e != NULL ) {
372 			if ( !is_dynamicObject ) {
373 				/* return referral only if "disclose"
374 				 * is granted on the object */
375 				if ( ! access_allowed( op, e,
376 						slap_schema.si_ad_entry,
377 						NULL, ACL_DISCLOSE, NULL ) )
378 				{
379 					rc = rs->sr_err = LDAP_NO_SUCH_OBJECT;
380 					send_ldap_result( op, rs );
381 
382 				} else {
383 					rc = rs->sr_err = LDAP_CONSTRAINT_VIOLATION;
384 					send_ldap_error( op, rs, rc, "no static subordinate entries allowed for dynamicObject" );
385 				}
386 			}
387 
388 			be_entry_release_r( op, e );
389 			if ( rc != LDAP_SUCCESS ) {
390 				return rc;
391 			}
392 		}
393 		op->o_bd->bd_info = bi;
394 	}
395 
396 	/* handle dynamic object operational attr(s) */
397 	if ( is_dynamicObject ) {
398 		time_t		ttl, expire;
399 		char		ttlbuf[STRLENOF("31557600") + 1];
400 		char		tsbuf[ LDAP_LUTIL_GENTIME_BUFSIZE ];
401 		struct berval	bv;
402 
403 		if ( !be_isroot_dn( op->o_bd, &op->o_req_ndn ) ) {
404 			ldap_pvt_thread_mutex_lock( &di->di_mutex );
405 			rs->sr_err = ( di->di_max_dynamicObjects &&
406 				di->di_num_dynamicObjects >= di->di_max_dynamicObjects );
407 			ldap_pvt_thread_mutex_unlock( &di->di_mutex );
408 			if ( rs->sr_err ) {
409 				op->o_bd->bd_info = (BackendInfo *)on->on_info;
410 				send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM,
411 					"too many dynamicObjects in context" );
412 				return rs->sr_err;
413 			}
414 		}
415 
416 		ttl = DDS_DEFAULT_TTL( di );
417 
418 		/* assert because should be checked at configure */
419 		assert( ttl <= DDS_RF2589_MAX_TTL );
420 
421 		bv.bv_val = ttlbuf;
422 		bv.bv_len = snprintf( ttlbuf, sizeof( ttlbuf ), "%ld", ttl );
423 		assert( bv.bv_len < sizeof( ttlbuf ) );
424 
425 		/* FIXME: apparently, values in op->ora_e are malloc'ed
426 		 * on the thread's slab; works fine by chance,
427 		 * only because the attribute doesn't exist yet. */
428 		assert( attr_find( op->ora_e->e_attrs, slap_schema.si_ad_entryTtl ) == NULL );
429 		attr_merge_one( op->ora_e, slap_schema.si_ad_entryTtl, &bv, &bv );
430 
431 		expire = slap_get_time() + ttl;
432 		bv.bv_val = tsbuf;
433 		bv.bv_len = sizeof( tsbuf );
434 		slap_timestamp( &expire, &bv );
435 		assert( attr_find( op->ora_e->e_attrs, ad_entryExpireTimestamp ) == NULL );
436 		attr_merge_one( op->ora_e, ad_entryExpireTimestamp, &bv, &bv );
437 
438 		/* if required, install counter callback */
439 		if ( di->di_max_dynamicObjects > 0) {
440 			slap_callback	*sc;
441 
442 			sc = op->o_tmpalloc( sizeof( slap_callback ), op->o_tmpmemctx );
443 			sc->sc_cleanup = dds_freeit_cb;
444 			sc->sc_response = dds_counter_cb;
445 			sc->sc_private = di;
446 			sc->sc_next = op->o_callback;
447 
448 			op->o_callback = sc;
449 		}
450 	}
451 
452 	return SLAP_CB_CONTINUE;
453 }
454 
455 static int
456 dds_op_delete( Operation *op, SlapReply *rs )
457 {
458 	slap_overinst	*on = (slap_overinst *)op->o_bd->bd_info;
459 	dds_info_t	*di = on->on_bi.bi_private;
460 
461 	/* if required, install counter callback */
462 	if ( !DDS_OFF( di ) && di->di_max_dynamicObjects > 0 ) {
463 		Entry		*e = NULL;
464 		BackendInfo	*bi = op->o_bd->bd_info;
465 
466 		op->o_bd->bd_info = (BackendInfo *)on->on_info;
467 		rs->sr_err = be_entry_get_rw( op, &op->o_req_ndn,
468 			slap_schema.si_oc_dynamicObject, NULL, 0, &e );
469 
470 		/* FIXME: couldn't the entry be added before deletion? */
471 		if ( rs->sr_err == LDAP_SUCCESS && e != NULL ) {
472 			slap_callback	*sc;
473 
474 			be_entry_release_r( op, e );
475 			e = NULL;
476 
477 			sc = op->o_tmpalloc( sizeof( slap_callback ), op->o_tmpmemctx );
478 			sc->sc_cleanup = dds_freeit_cb;
479 			sc->sc_response = dds_counter_cb;
480 			sc->sc_private = di;
481 			sc->sc_next = op->o_callback;
482 
483 			op->o_callback = sc;
484 		}
485 		op->o_bd->bd_info = bi;
486 	}
487 
488 	return SLAP_CB_CONTINUE;
489 }
490 
491 static int
492 dds_op_modify( Operation *op, SlapReply *rs )
493 {
494 	slap_overinst	*on = (slap_overinst *)op->o_bd->bd_info;
495 	dds_info_t	*di = (dds_info_t *)on->on_bi.bi_private;
496 	Modifications	*mod;
497 	Entry		*e = NULL;
498 	BackendInfo	*bi = op->o_bd->bd_info;
499 	int		was_dynamicObject = 0,
500 			is_dynamicObject = 0;
501 	struct berval	bv_entryTtl = BER_BVNULL;
502 	time_t		entryTtl = 0;
503 	char		textbuf[ SLAP_TEXT_BUFLEN ];
504 
505 	if ( DDS_OFF( di ) ) {
506 		return SLAP_CB_CONTINUE;
507 	}
508 
509 	/* bv_entryTtl stores the string representation of the entryTtl
510 	 * across modifies for consistency checks of the final value;
511 	 * the bv_val points to a static buffer; the bv_len is zero when
512 	 * the attribute is deleted.
513 	 * entryTtl stores the integer representation of the entryTtl;
514 	 * its value is -1 when the attribute is deleted; it is 0 only
515 	 * if no modifications of the entryTtl occurred, as an entryTtl
516 	 * of 0 is invalid. */
517 	bv_entryTtl.bv_val = textbuf;
518 
519 	op->o_bd->bd_info = (BackendInfo *)on->on_info;
520 	rs->sr_err = be_entry_get_rw( op, &op->o_req_ndn,
521 		slap_schema.si_oc_dynamicObject, slap_schema.si_ad_entryTtl, 0, &e );
522 	if ( rs->sr_err == LDAP_SUCCESS && e != NULL ) {
523 		Attribute	*a = attr_find( e->e_attrs, slap_schema.si_ad_entryTtl );
524 
525 		/* the value of the entryTtl is saved for later checks */
526 		if ( a != NULL ) {
527 			unsigned long	ttl;
528 			int		rc;
529 
530 			bv_entryTtl.bv_len = a->a_nvals[ 0 ].bv_len;
531 			AC_MEMCPY( bv_entryTtl.bv_val, a->a_nvals[ 0 ].bv_val, bv_entryTtl.bv_len );
532 			bv_entryTtl.bv_val[ bv_entryTtl.bv_len ] = '\0';
533 			rc = lutil_atoul( &ttl, bv_entryTtl.bv_val );
534 			assert( rc == 0 );
535 			entryTtl = (time_t)ttl;
536 		}
537 
538 		be_entry_release_r( op, e );
539 		e = NULL;
540 		was_dynamicObject = is_dynamicObject = 1;
541 	}
542 	op->o_bd->bd_info = bi;
543 
544 	rs->sr_err = LDAP_SUCCESS;
545 	for ( mod = op->orm_modlist; mod; mod = mod->sml_next ) {
546 		if ( mod->sml_desc == slap_schema.si_ad_objectClass ) {
547 			int		i;
548 			ObjectClass	*oc;
549 
550 			switch ( mod->sml_op ) {
551 			case LDAP_MOD_DELETE:
552 				if ( mod->sml_values == NULL ) {
553 					is_dynamicObject = 0;
554 					break;
555 				}
556 
557 				for ( i = 0; !BER_BVISNULL( &mod->sml_values[ i ] ); i++ ) {
558 					oc = oc_bvfind( &mod->sml_values[ i ] );
559 					if ( oc == slap_schema.si_oc_dynamicObject ) {
560 						is_dynamicObject = 0;
561 						break;
562 					}
563 				}
564 
565 				break;
566 
567 			case LDAP_MOD_REPLACE:
568 				if ( mod->sml_values == NULL ) {
569 					is_dynamicObject = 0;
570 					break;
571 				}
572 				/* fallthru */
573 
574 			case LDAP_MOD_ADD:
575 				for ( i = 0; !BER_BVISNULL( &mod->sml_values[ i ] ); i++ ) {
576 					oc = oc_bvfind( &mod->sml_values[ i ] );
577 					if ( oc == slap_schema.si_oc_dynamicObject ) {
578 						is_dynamicObject = 1;
579 						break;
580 					}
581 				}
582 				break;
583 			}
584 
585 		} else if ( mod->sml_desc == slap_schema.si_ad_entryTtl ) {
586 			unsigned long	uttl;
587 			time_t		ttl;
588 			int		rc;
589 
590 			switch ( mod->sml_op ) {
591 			case LDAP_MOD_DELETE:
592 			case SLAP_MOD_SOFTDEL: /* FIXME? */
593 				if ( mod->sml_values != NULL ) {
594 					if ( BER_BVISEMPTY( &bv_entryTtl )
595 						|| !bvmatch( &bv_entryTtl, &mod->sml_values[ 0 ] ) )
596 					{
597 						rs->sr_err = backend_attribute( op, NULL, &op->o_req_ndn,
598 							slap_schema.si_ad_entry, NULL, ACL_DISCLOSE );
599 						if ( rs->sr_err == LDAP_INSUFFICIENT_ACCESS ) {
600 							rs->sr_err = LDAP_NO_SUCH_OBJECT;
601 
602 						} else {
603 							rs->sr_err = LDAP_NO_SUCH_ATTRIBUTE;
604 						}
605 						goto done;
606 					}
607 				}
608 				bv_entryTtl.bv_len = 0;
609 				entryTtl = -1;
610 				break;
611 
612 			case LDAP_MOD_REPLACE:
613 				bv_entryTtl.bv_len = 0;
614 				entryTtl = -1;
615 				/* fallthru */
616 
617 			case LDAP_MOD_ADD:
618 			case SLAP_MOD_SOFTADD: /* FIXME? */
619 			case SLAP_MOD_ADD_IF_NOT_PRESENT: /* FIXME? */
620 				assert( mod->sml_values != NULL );
621 				assert( BER_BVISNULL( &mod->sml_values[ 1 ] ) );
622 
623 				if ( !BER_BVISEMPTY( &bv_entryTtl ) ) {
624 					rs->sr_err = backend_attribute( op, NULL, &op->o_req_ndn,
625 						slap_schema.si_ad_entry, NULL, ACL_DISCLOSE );
626 					if ( rs->sr_err == LDAP_INSUFFICIENT_ACCESS ) {
627 						rs->sr_err = LDAP_NO_SUCH_OBJECT;
628 
629 					} else {
630 						rs->sr_text = "attribute 'entryTtl' cannot have multiple values";
631 						rs->sr_err = LDAP_CONSTRAINT_VIOLATION;
632 					}
633 					goto done;
634 				}
635 
636 				rc = lutil_atoul( &uttl, mod->sml_values[ 0 ].bv_val );
637 				ttl = (time_t)uttl;
638 				assert( rc == 0 );
639 				if ( ttl > DDS_RF2589_MAX_TTL ) {
640 					rs->sr_err = LDAP_PROTOCOL_ERROR;
641 					rs->sr_text = "invalid time-to-live for dynamicObject";
642 					goto done;
643 				}
644 
645 				if ( ttl <= 0 || ttl > di->di_max_ttl ) {
646 					/* FIXME: I don't understand if this has to be an error,
647 					 * or an indication that the requested Ttl has been
648 					 * shortened to di->di_max_ttl >= 1 day */
649 					rs->sr_err = LDAP_SIZELIMIT_EXCEEDED;
650 					rs->sr_text = "time-to-live for dynamicObject exceeds administrative limit";
651 					goto done;
652 				}
653 
654 				entryTtl = ttl;
655 				bv_entryTtl.bv_len = mod->sml_values[ 0 ].bv_len;
656 				AC_MEMCPY( bv_entryTtl.bv_val, mod->sml_values[ 0 ].bv_val, bv_entryTtl.bv_len );
657 				bv_entryTtl.bv_val[ bv_entryTtl.bv_len ] = '\0';
658 				break;
659 
660 			case LDAP_MOD_INCREMENT:
661 				if ( BER_BVISEMPTY( &bv_entryTtl ) ) {
662 					rs->sr_err = backend_attribute( op, NULL, &op->o_req_ndn,
663 						slap_schema.si_ad_entry, NULL, ACL_DISCLOSE );
664 					if ( rs->sr_err == LDAP_INSUFFICIENT_ACCESS ) {
665 						rs->sr_err = LDAP_NO_SUCH_OBJECT;
666 
667 					} else {
668 						rs->sr_err = LDAP_NO_SUCH_ATTRIBUTE;
669 						rs->sr_text = "modify/increment: entryTtl: no such attribute";
670 					}
671 					goto done;
672 				}
673 
674 				entryTtl++;
675 				if ( entryTtl > DDS_RF2589_MAX_TTL ) {
676 					rs->sr_err = LDAP_PROTOCOL_ERROR;
677 					rs->sr_text = "invalid time-to-live for dynamicObject";
678 
679 				} else if ( entryTtl <= 0 || entryTtl > di->di_max_ttl ) {
680 					/* FIXME: I don't understand if this has to be an error,
681 					 * or an indication that the requested Ttl has been
682 					 * shortened to di->di_max_ttl >= 1 day */
683 					rs->sr_err = LDAP_SIZELIMIT_EXCEEDED;
684 					rs->sr_text = "time-to-live for dynamicObject exceeds administrative limit";
685 				}
686 
687 				if ( rs->sr_err != LDAP_SUCCESS ) {
688 					rc = backend_attribute( op, NULL, &op->o_req_ndn,
689 						slap_schema.si_ad_entry, NULL, ACL_DISCLOSE );
690 					if ( rc == LDAP_INSUFFICIENT_ACCESS ) {
691 						rs->sr_text = NULL;
692 						rs->sr_err = LDAP_NO_SUCH_OBJECT;
693 
694 					}
695 					goto done;
696 				}
697 
698 				bv_entryTtl.bv_len = snprintf( textbuf, sizeof( textbuf ), "%ld", entryTtl );
699 				break;
700 
701 			default:
702 				assert( 0 );
703 				break;
704 			}
705 
706 		} else if ( mod->sml_desc == ad_entryExpireTimestamp ) {
707 			/* should have been trapped earlier */
708 			assert( mod->sml_flags & SLAP_MOD_INTERNAL );
709 		}
710 	}
711 
712 done:;
713 	if ( rs->sr_err == LDAP_SUCCESS ) {
714 		int	rc;
715 
716 		/* FIXME: this could be allowed when the Relax control is used...
717 		 * in that case:
718 		 *
719 		 * TODO
720 		 *
721 		 *	static => dynamic:
722 		 *		entryTtl must be provided; add
723 		 *		entryExpireTimestamp accordingly
724 		 *
725 		 *	dynamic => static:
726 		 *		entryTtl must be removed; remove
727 		 *		entryTimestamp accordingly
728 		 *
729 		 * ... but we need to make sure that there are no subordinate
730 		 * issues...
731 		 */
732 		rc = is_dynamicObject - was_dynamicObject;
733 		if ( rc ) {
734 #if 0 /* fix subordinate issues first */
735 			if ( get_relax( op ) ) {
736 				switch ( rc ) {
737 				case -1:
738 					/* need to delete entryTtl to have a consistent entry */
739 					if ( entryTtl != -1 ) {
740 						rs->sr_text = "objectClass modification from dynamicObject to static entry requires entryTtl deletion";
741 						rs->sr_err = LDAP_OBJECT_CLASS_VIOLATION;
742 					}
743 					break;
744 
745 				case 1:
746 					/* need to add entryTtl to have a consistent entry */
747 					if ( entryTtl <= 0 ) {
748 						rs->sr_text = "objectClass modification from static entry to dynamicObject requires entryTtl addition";
749 						rs->sr_err = LDAP_OBJECT_CLASS_VIOLATION;
750 					}
751 					break;
752 				}
753 
754 			} else
755 #endif
756 			{
757 				switch ( rc ) {
758 				case -1:
759 					rs->sr_text = "objectClass modification cannot turn dynamicObject into static entry";
760 					break;
761 
762 				case 1:
763 					rs->sr_text = "objectClass modification cannot turn static entry into dynamicObject";
764 					break;
765 				}
766 				rs->sr_err = LDAP_OBJECT_CLASS_VIOLATION;
767 			}
768 
769 			if ( rc != LDAP_SUCCESS ) {
770 				rc = backend_attribute( op, NULL, &op->o_req_ndn,
771 					slap_schema.si_ad_entry, NULL, ACL_DISCLOSE );
772 				if ( rc == LDAP_INSUFFICIENT_ACCESS ) {
773 					rs->sr_text = NULL;
774 					rs->sr_err = LDAP_NO_SUCH_OBJECT;
775 				}
776 			}
777 		}
778 	}
779 
780 	if ( rs->sr_err == LDAP_SUCCESS && entryTtl != 0 ) {
781 		Modifications	*tmpmod = NULL, **modp;
782 
783 		for ( modp = &op->orm_modlist; *modp; modp = &(*modp)->sml_next )
784 			;
785 
786 		tmpmod = ch_calloc( 1, sizeof( Modifications ) );
787 		tmpmod->sml_flags = SLAP_MOD_INTERNAL;
788 		tmpmod->sml_type = ad_entryExpireTimestamp->ad_cname;
789 		tmpmod->sml_desc = ad_entryExpireTimestamp;
790 
791 		*modp = tmpmod;
792 
793 		if ( entryTtl == -1 ) {
794 			/* delete entryExpireTimestamp */
795 			tmpmod->sml_op = LDAP_MOD_DELETE;
796 
797 		} else {
798 			time_t		expire;
799 			char		tsbuf[ LDAP_LUTIL_GENTIME_BUFSIZE ];
800 			struct berval	bv;
801 
802 			/* keep entryExpireTimestamp consistent
803 			 * with entryTtl */
804 			expire = slap_get_time() + entryTtl;
805 			bv.bv_val = tsbuf;
806 			bv.bv_len = sizeof( tsbuf );
807 			slap_timestamp( &expire, &bv );
808 
809 			tmpmod->sml_op = LDAP_MOD_REPLACE;
810 			value_add_one( &tmpmod->sml_values, &bv );
811 			value_add_one( &tmpmod->sml_nvalues, &bv );
812 			tmpmod->sml_numvals = 1;
813 		}
814 	}
815 
816 	if ( rs->sr_err ) {
817 		op->o_bd->bd_info = (BackendInfo *)on->on_info;
818 		send_ldap_result( op, rs );
819 		return rs->sr_err;
820 	}
821 
822 	return SLAP_CB_CONTINUE;
823 }
824 
825 static int
826 dds_op_rename( Operation *op, SlapReply *rs )
827 {
828 	slap_overinst	*on = (slap_overinst *)op->o_bd->bd_info;
829 	dds_info_t	*di = on->on_bi.bi_private;
830 
831 	if ( DDS_OFF( di ) ) {
832 		return SLAP_CB_CONTINUE;
833 	}
834 
835 	/* we don't allow dynamicObjects to have static subordinates */
836 	if ( op->orr_nnewSup != NULL ) {
837 		Entry		*e = NULL;
838 		BackendInfo	*bi = op->o_bd->bd_info;
839 		int		is_dynamicObject = 0,
840 				rc;
841 
842 		rs->sr_err = LDAP_SUCCESS;
843 
844 		op->o_bd->bd_info = (BackendInfo *)on->on_info;
845 		rc = be_entry_get_rw( op, &op->o_req_ndn,
846 			slap_schema.si_oc_dynamicObject, NULL, 0, &e );
847 		if ( rc == LDAP_SUCCESS && e != NULL ) {
848 			be_entry_release_r( op, e );
849 			e = NULL;
850 			is_dynamicObject = 1;
851 		}
852 
853 		rc = be_entry_get_rw( op, op->orr_nnewSup,
854 			slap_schema.si_oc_dynamicObject, NULL, 0, &e );
855 		if ( rc == LDAP_SUCCESS && e != NULL ) {
856 			if ( !is_dynamicObject ) {
857 				/* return referral only if "disclose"
858 				 * is granted on the object */
859 				if ( ! access_allowed( op, e,
860 						slap_schema.si_ad_entry,
861 						NULL, ACL_DISCLOSE, NULL ) )
862 				{
863 					rs->sr_err = LDAP_NO_SUCH_OBJECT;
864 					send_ldap_result( op, rs );
865 
866 				} else {
867 					send_ldap_error( op, rs, LDAP_CONSTRAINT_VIOLATION,
868 						"static entry cannot have dynamicObject as newSuperior" );
869 				}
870 			}
871 			be_entry_release_r( op, e );
872 		}
873 		op->o_bd->bd_info = bi;
874 		if ( rs->sr_err != LDAP_SUCCESS ) {
875 			return rs->sr_err;
876 		}
877 	}
878 
879 	return SLAP_CB_CONTINUE;
880 }
881 
882 static int
883 slap_parse_refresh(
884 	struct berval	*in,
885 	struct berval	*ndn,
886 	time_t		*ttl,
887 	const char	**text,
888 	void		*ctx )
889 {
890 	int			rc = LDAP_SUCCESS;
891 	ber_tag_t		tag;
892 	ber_len_t		len = -1;
893 	BerElementBuffer	berbuf;
894 	BerElement		*ber = (BerElement *)&berbuf;
895 	struct berval		reqdata = BER_BVNULL;
896 	int			tmp;
897 
898 	*text = NULL;
899 
900 	if ( ndn ) {
901 		BER_BVZERO( ndn );
902 	}
903 
904 	if ( in == NULL || in->bv_len == 0 ) {
905 		*text = "empty request data field in refresh exop";
906 		return LDAP_PROTOCOL_ERROR;
907 	}
908 
909 	ber_dupbv_x( &reqdata, in, ctx );
910 
911 	/* ber_init2 uses reqdata directly, doesn't allocate new buffers */
912 	ber_init2( ber, &reqdata, 0 );
913 
914 	tag = ber_scanf( ber, "{" /*}*/ );
915 
916 	if ( tag == LBER_ERROR ) {
917 		Log0( LDAP_DEBUG_TRACE, LDAP_LEVEL_ERR,
918 			"slap_parse_refresh: decoding error.\n" );
919 		goto decoding_error;
920 	}
921 
922 	tag = ber_peek_tag( ber, &len );
923 	if ( tag != LDAP_TAG_EXOP_REFRESH_REQ_DN ) {
924 		Log0( LDAP_DEBUG_TRACE, LDAP_LEVEL_ERR,
925 			"slap_parse_refresh: decoding error.\n" );
926 		goto decoding_error;
927 	}
928 
929 	if ( ndn ) {
930 		struct berval	dn;
931 
932 		tag = ber_scanf( ber, "m", &dn );
933 		if ( tag == LBER_ERROR ) {
934 			Log0( LDAP_DEBUG_TRACE, LDAP_LEVEL_ERR,
935 				"slap_parse_refresh: DN parse failed.\n" );
936 			goto decoding_error;
937 		}
938 
939 		rc = dnNormalize( 0, NULL, NULL, &dn, ndn, ctx );
940 		if ( rc != LDAP_SUCCESS ) {
941 			*text = "invalid DN in refresh exop request data";
942 			goto done;
943 		}
944 
945 	} else {
946 		tag = ber_scanf( ber, "x" /* "m" */ );
947 		if ( tag == LBER_DEFAULT ) {
948 			goto decoding_error;
949 		}
950 	}
951 
952 	tag = ber_peek_tag( ber, &len );
953 
954 	if ( tag != LDAP_TAG_EXOP_REFRESH_REQ_TTL ) {
955 		Log0( LDAP_DEBUG_TRACE, LDAP_LEVEL_ERR,
956 			"slap_parse_refresh: decoding error.\n" );
957 		goto decoding_error;
958 	}
959 
960 	tag = ber_scanf( ber, "i", &tmp );
961 	if ( tag == LBER_ERROR ) {
962 		Log0( LDAP_DEBUG_TRACE, LDAP_LEVEL_ERR,
963 			"slap_parse_refresh: TTL parse failed.\n" );
964 		goto decoding_error;
965 	}
966 
967 	if ( ttl ) {
968 		*ttl = tmp;
969 	}
970 
971 	tag = ber_peek_tag( ber, &len );
972 
973 	if ( tag != LBER_DEFAULT || len != 0 ) {
974 decoding_error:;
975 		Log1( LDAP_DEBUG_TRACE, LDAP_LEVEL_ERR,
976 			"slap_parse_refresh: decoding error, len=%ld\n",
977 			(long)len );
978 		rc = LDAP_PROTOCOL_ERROR;
979 		*text = "data decoding error";
980 
981 done:;
982 		if ( ndn && !BER_BVISNULL( ndn ) ) {
983 			slap_sl_free( ndn->bv_val, ctx );
984 			BER_BVZERO( ndn );
985 		}
986 	}
987 
988 	if ( !BER_BVISNULL( &reqdata ) ) {
989 		ber_memfree_x( reqdata.bv_val, ctx );
990 	}
991 
992 	return rc;
993 }
994 
995 static int
996 dds_op_extended( Operation *op, SlapReply *rs )
997 {
998 	slap_overinst	*on = (slap_overinst *)op->o_bd->bd_info;
999 	dds_info_t	*di = on->on_bi.bi_private;
1000 
1001 	if ( DDS_OFF( di ) ) {
1002 		return SLAP_CB_CONTINUE;
1003 	}
1004 
1005 	if ( bvmatch( &op->ore_reqoid, &slap_EXOP_REFRESH ) ) {
1006 		Entry		*e = NULL;
1007 		time_t		ttl;
1008 		BackendDB	db = *op->o_bd;
1009 		SlapReply	rs2 = { REP_RESULT };
1010 		Operation	op2 = *op;
1011 		slap_callback	sc = { 0 };
1012 		Modifications	ttlmod = { { 0 } };
1013 		struct berval	ttlvalues[ 2 ];
1014 		char		ttlbuf[STRLENOF("31557600") + 1];
1015 
1016 		rs->sr_err = slap_parse_refresh( op->ore_reqdata, NULL, &ttl,
1017 			&rs->sr_text, NULL );
1018 		assert( rs->sr_err == LDAP_SUCCESS );
1019 
1020 		if ( ttl <= 0 || ttl > DDS_RF2589_MAX_TTL ) {
1021 			rs->sr_err = LDAP_PROTOCOL_ERROR;
1022 			rs->sr_text = "invalid time-to-live for dynamicObject";
1023 			return rs->sr_err;
1024 		}
1025 
1026 		if ( ttl > di->di_max_ttl ) {
1027 			/* FIXME: I don't understand if this has to be an error,
1028 			 * or an indication that the requested Ttl has been
1029 			 * shortened to di->di_max_ttl >= 1 day */
1030 			rs->sr_err = LDAP_SIZELIMIT_EXCEEDED;
1031 			rs->sr_text = "time-to-live for dynamicObject exceeds limit";
1032 			return rs->sr_err;
1033 		}
1034 
1035 		if ( di->di_min_ttl && ttl < di->di_min_ttl ) {
1036 			ttl = di->di_min_ttl;
1037 		}
1038 
1039 		/* This does not apply to multi-master case */
1040 		if ( !( !SLAP_SINGLE_SHADOW( op->o_bd ) || be_isupdate( op ) ) ) {
1041 			/* we SHOULD return a referral in this case */
1042 			BerVarray defref = op->o_bd->be_update_refs
1043 				? op->o_bd->be_update_refs : default_referral;
1044 
1045 			if ( defref != NULL ) {
1046 				rs->sr_ref = referral_rewrite( op->o_bd->be_update_refs,
1047 					NULL, NULL, LDAP_SCOPE_DEFAULT );
1048 				if ( rs->sr_ref ) {
1049 					rs->sr_flags |= REP_REF_MUSTBEFREED;
1050 				} else {
1051 					rs->sr_ref = defref;
1052 				}
1053 				rs->sr_err = LDAP_REFERRAL;
1054 
1055 			} else {
1056 				rs->sr_text = "shadow context; no update referral";
1057 				rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
1058 			}
1059 
1060 			return rs->sr_err;
1061 		}
1062 
1063 		assert( !BER_BVISNULL( &op->o_req_ndn ) );
1064 
1065 
1066 
1067 		/* check if exists but not dynamicObject */
1068 		op->o_bd->bd_info = (BackendInfo *)on->on_info;
1069 		rs->sr_err = be_entry_get_rw( op, &op->o_req_ndn,
1070 			slap_schema.si_oc_dynamicObject, NULL, 0, &e );
1071 		if ( rs->sr_err != LDAP_SUCCESS ) {
1072 			rs->sr_err = be_entry_get_rw( op, &op->o_req_ndn,
1073 				NULL, NULL, 0, &e );
1074 			if ( rs->sr_err == LDAP_SUCCESS && e != NULL ) {
1075 				/* return referral only if "disclose"
1076 				 * is granted on the object */
1077 				if ( ! access_allowed( op, e,
1078 						slap_schema.si_ad_entry,
1079 						NULL, ACL_DISCLOSE, NULL ) )
1080 				{
1081 					rs->sr_err = LDAP_NO_SUCH_OBJECT;
1082 
1083 				} else {
1084 					rs->sr_err = LDAP_OBJECT_CLASS_VIOLATION;
1085 					rs->sr_text = "refresh operation only applies to dynamic objects";
1086 				}
1087 				be_entry_release_r( op, e );
1088 
1089 			} else {
1090 				rs->sr_err = LDAP_NO_SUCH_OBJECT;
1091 			}
1092 			return rs->sr_err;
1093 
1094 		} else if ( e != NULL ) {
1095 			be_entry_release_r( op, e );
1096 		}
1097 
1098 		/* we require manage privileges on the entryTtl,
1099 		 * and fake a Relax control */
1100 		op2.o_tag = LDAP_REQ_MODIFY;
1101 		op2.o_bd = &db;
1102 		db.bd_info = (BackendInfo *)on->on_info;
1103 		op2.o_callback = &sc;
1104 		sc.sc_response = slap_null_cb;
1105 		op2.o_relax = SLAP_CONTROL_CRITICAL;
1106 		op2.orm_modlist = &ttlmod;
1107 
1108 		ttlmod.sml_op = LDAP_MOD_REPLACE;
1109 		ttlmod.sml_flags = SLAP_MOD_MANAGING;
1110 		ttlmod.sml_desc = slap_schema.si_ad_entryTtl;
1111 		ttlmod.sml_values = ttlvalues;
1112 		ttlmod.sml_numvals = 1;
1113 		ttlvalues[ 0 ].bv_val = ttlbuf;
1114 		ttlvalues[ 0 ].bv_len = snprintf( ttlbuf, sizeof( ttlbuf ), "%ld", ttl );
1115 		BER_BVZERO( &ttlvalues[ 1 ] );
1116 
1117 		/* the entryExpireTimestamp is added by modify */
1118 		rs->sr_err = op2.o_bd->be_modify( &op2, &rs2 );
1119 
1120 		if ( ttlmod.sml_next != NULL ) {
1121 			slap_mods_free( ttlmod.sml_next, 1 );
1122 		}
1123 
1124 		if ( rs->sr_err == LDAP_SUCCESS ) {
1125 			int			rc;
1126 			BerElementBuffer	berbuf;
1127 			BerElement		*ber = (BerElement *)&berbuf;
1128 
1129 			ber_init_w_nullc( ber, LBER_USE_DER );
1130 
1131 			rc = ber_printf( ber, "{tiN}", LDAP_TAG_EXOP_REFRESH_RES_TTL, (int)ttl );
1132 
1133 			if ( rc < 0 ) {
1134 				rs->sr_err = LDAP_OTHER;
1135 				rs->sr_text = "internal error";
1136 
1137 			} else {
1138 				(void)ber_flatten( ber, &rs->sr_rspdata );
1139 				rs->sr_rspoid = ch_strdup( slap_EXOP_REFRESH.bv_val );
1140 
1141 				Log3( LDAP_DEBUG_TRACE, LDAP_LEVEL_INFO,
1142 					"%s REFRESH dn=\"%s\" TTL=%ld\n",
1143 					op->o_log_prefix, op->o_req_ndn.bv_val, ttl );
1144 			}
1145 
1146 			ber_free_buf( ber );
1147 		}
1148 
1149 		return rs->sr_err;
1150 	}
1151 
1152 	return SLAP_CB_CONTINUE;
1153 }
1154 
1155 enum {
1156 	DDS_STATE = 1,
1157 	DDS_MAXTTL,
1158 	DDS_MINTTL,
1159 	DDS_DEFAULTTTL,
1160 	DDS_INTERVAL,
1161 	DDS_TOLERANCE,
1162 	DDS_MAXDYNAMICOBJS,
1163 
1164 	DDS_LAST
1165 };
1166 
1167 static ConfigDriver dds_cfgen;
1168 #if 0
1169 static ConfigLDAPadd dds_ldadd;
1170 static ConfigCfAdd dds_cfadd;
1171 #endif
1172 
1173 static ConfigTable dds_cfg[] = {
1174 	{ "dds-state", "on|off",
1175 		2, 2, 0, ARG_MAGIC|ARG_ON_OFF|DDS_STATE, dds_cfgen,
1176 		"( OLcfgOvAt:9.1 NAME 'olcDDSstate' "
1177 			"DESC 'RFC2589 Dynamic directory services state' "
1178 			"SYNTAX OMsBoolean "
1179 			"SINGLE-VALUE )", NULL, NULL },
1180 	{ "dds-max-ttl", "ttl",
1181 		2, 2, 0, ARG_MAGIC|DDS_MAXTTL, dds_cfgen,
1182 		"( OLcfgOvAt:9.2 NAME 'olcDDSmaxTtl' "
1183 			"DESC 'RFC2589 Dynamic directory services max TTL' "
1184 			"SYNTAX OMsDirectoryString "
1185 			"SINGLE-VALUE )", NULL, NULL },
1186 	{ "dds-min-ttl", "ttl",
1187 		2, 2, 0, ARG_MAGIC|DDS_MINTTL, dds_cfgen,
1188 		"( OLcfgOvAt:9.3 NAME 'olcDDSminTtl' "
1189 			"DESC 'RFC2589 Dynamic directory services min TTL' "
1190 			"SYNTAX OMsDirectoryString "
1191 			"SINGLE-VALUE )", NULL, NULL },
1192 	{ "dds-default-ttl", "ttl",
1193 		2, 2, 0, ARG_MAGIC|DDS_DEFAULTTTL, dds_cfgen,
1194 		"( OLcfgOvAt:9.4 NAME 'olcDDSdefaultTtl' "
1195 			"DESC 'RFC2589 Dynamic directory services default TTL' "
1196 			"SYNTAX OMsDirectoryString "
1197 			"SINGLE-VALUE )", NULL, NULL },
1198 	{ "dds-interval", "interval",
1199 		2, 2, 0, ARG_MAGIC|DDS_INTERVAL, dds_cfgen,
1200 		"( OLcfgOvAt:9.5 NAME 'olcDDSinterval' "
1201 			"DESC 'RFC2589 Dynamic directory services expiration "
1202 				"task run interval' "
1203 			"SYNTAX OMsDirectoryString "
1204 			"SINGLE-VALUE )", NULL, NULL },
1205 	{ "dds-tolerance", "ttl",
1206 		2, 2, 0, ARG_MAGIC|DDS_TOLERANCE, dds_cfgen,
1207 		"( OLcfgOvAt:9.6 NAME 'olcDDStolerance' "
1208 			"DESC 'RFC2589 Dynamic directory services additional "
1209 				"TTL in expiration scheduling' "
1210 			"SYNTAX OMsDirectoryString "
1211 			"SINGLE-VALUE )", NULL, NULL },
1212 	{ "dds-max-dynamicObjects", "num",
1213 		2, 2, 0, ARG_MAGIC|ARG_INT|DDS_MAXDYNAMICOBJS, dds_cfgen,
1214 		"( OLcfgOvAt:9.7 NAME 'olcDDSmaxDynamicObjects' "
1215 			"DESC 'RFC2589 Dynamic directory services max number of dynamic objects' "
1216 			"SYNTAX OMsInteger "
1217 			"SINGLE-VALUE )", NULL, NULL },
1218 	{ NULL, NULL, 0, 0, 0, ARG_IGNORED }
1219 };
1220 
1221 static ConfigOCs dds_ocs[] = {
1222 	{ "( OLcfgOvOc:9.1 "
1223 		"NAME 'olcDDSConfig' "
1224 		"DESC 'RFC2589 Dynamic directory services configuration' "
1225 		"SUP olcOverlayConfig "
1226 		"MAY ( "
1227 			"olcDDSstate "
1228 			"$ olcDDSmaxTtl "
1229 			"$ olcDDSminTtl "
1230 			"$ olcDDSdefaultTtl "
1231 			"$ olcDDSinterval "
1232 			"$ olcDDStolerance "
1233 			"$ olcDDSmaxDynamicObjects "
1234 		" ) "
1235 		")", Cft_Overlay, dds_cfg, NULL, NULL /* dds_cfadd */ },
1236 	{ NULL, 0, NULL }
1237 };
1238 
1239 #if 0
1240 static int
1241 dds_ldadd( CfEntryInfo *p, Entry *e, ConfigArgs *ca )
1242 {
1243 	return LDAP_SUCCESS;
1244 }
1245 
1246 static int
1247 dds_cfadd( Operation *op, SlapReply *rs, Entry *p, ConfigArgs *ca )
1248 {
1249 	return 0;
1250 }
1251 #endif
1252 
1253 static int
1254 dds_cfgen( ConfigArgs *c )
1255 {
1256 	slap_overinst	*on = (slap_overinst *)c->bi;
1257 	dds_info_t	*di = on->on_bi.bi_private;
1258 	int		rc = 0;
1259 	unsigned long	t;
1260 
1261 
1262 	if ( c->op == SLAP_CONFIG_EMIT ) {
1263 		char		buf[ SLAP_TEXT_BUFLEN ];
1264 		struct berval	bv;
1265 
1266 		switch( c->type ) {
1267 		case DDS_STATE:
1268 			c->value_int = !DDS_OFF( di );
1269 			break;
1270 
1271 		case DDS_MAXTTL:
1272 			lutil_unparse_time( buf, sizeof( buf ), di->di_max_ttl );
1273 			ber_str2bv( buf, 0, 0, &bv );
1274 			value_add_one( &c->rvalue_vals, &bv );
1275 			break;
1276 
1277 		case DDS_MINTTL:
1278 			if ( di->di_min_ttl ) {
1279 				lutil_unparse_time( buf, sizeof( buf ), di->di_min_ttl );
1280 				ber_str2bv( buf, 0, 0, &bv );
1281 				value_add_one( &c->rvalue_vals, &bv );
1282 
1283 			} else {
1284 				rc = 1;
1285 			}
1286 			break;
1287 
1288 		case DDS_DEFAULTTTL:
1289 			if ( di->di_default_ttl ) {
1290 				lutil_unparse_time( buf, sizeof( buf ), di->di_default_ttl );
1291 				ber_str2bv( buf, 0, 0, &bv );
1292 				value_add_one( &c->rvalue_vals, &bv );
1293 
1294 			} else {
1295 				rc = 1;
1296 			}
1297 			break;
1298 
1299 		case DDS_INTERVAL:
1300 			if ( di->di_interval ) {
1301 				lutil_unparse_time( buf, sizeof( buf ), di->di_interval );
1302 				ber_str2bv( buf, 0, 0, &bv );
1303 				value_add_one( &c->rvalue_vals, &bv );
1304 
1305 			} else {
1306 				rc = 1;
1307 			}
1308 			break;
1309 
1310 		case DDS_TOLERANCE:
1311 			if ( di->di_tolerance ) {
1312 				lutil_unparse_time( buf, sizeof( buf ), di->di_tolerance );
1313 				ber_str2bv( buf, 0, 0, &bv );
1314 				value_add_one( &c->rvalue_vals, &bv );
1315 
1316 			} else {
1317 				rc = 1;
1318 			}
1319 			break;
1320 
1321 		case DDS_MAXDYNAMICOBJS:
1322 			if ( di->di_max_dynamicObjects > 0 ) {
1323 				c->value_int = di->di_max_dynamicObjects;
1324 
1325 			} else {
1326 				rc = 1;
1327 			}
1328 			break;
1329 
1330 		default:
1331 			rc = 1;
1332 			break;
1333 		}
1334 
1335 		return rc;
1336 
1337 	} else if ( c->op == LDAP_MOD_DELETE ) {
1338 		switch( c->type ) {
1339 		case DDS_STATE:
1340 			di->di_flags &= ~DDS_FOFF;
1341 			break;
1342 
1343 		case DDS_MAXTTL:
1344 			di->di_min_ttl = DDS_RF2589_DEFAULT_TTL;
1345 			break;
1346 
1347 		case DDS_MINTTL:
1348 			di->di_min_ttl = 0;
1349 			break;
1350 
1351 		case DDS_DEFAULTTTL:
1352 			di->di_default_ttl = 0;
1353 			break;
1354 
1355 		case DDS_INTERVAL:
1356 			di->di_interval = 0;
1357 			break;
1358 
1359 		case DDS_TOLERANCE:
1360 			di->di_tolerance = 0;
1361 			break;
1362 
1363 		case DDS_MAXDYNAMICOBJS:
1364 			di->di_max_dynamicObjects = 0;
1365 			break;
1366 
1367 		default:
1368 			rc = 1;
1369 			break;
1370 		}
1371 
1372 		return rc;
1373 	}
1374 
1375 	switch ( c->type ) {
1376 	case DDS_STATE:
1377 		if ( c->value_int ) {
1378 			di->di_flags &= ~DDS_FOFF;
1379 
1380 		} else {
1381 			di->di_flags |= DDS_FOFF;
1382 		}
1383 		break;
1384 
1385 	case DDS_MAXTTL:
1386 		if ( lutil_parse_time( c->argv[ 1 ], &t ) != 0 ) {
1387 			snprintf( c->cr_msg, sizeof( c->cr_msg),
1388 				"DDS unable to parse dds-max-ttl \"%s\"",
1389 				c->argv[ 1 ] );
1390 			Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1391 				"%s: %s.\n", c->log, c->cr_msg );
1392 			return 1;
1393 		}
1394 
1395 		if ( t < DDS_RF2589_DEFAULT_TTL || t > DDS_RF2589_MAX_TTL ) {
1396 			snprintf( c->cr_msg, sizeof( c->cr_msg ),
1397 				"DDS invalid dds-max-ttl=%lu; must be between %d and %d",
1398 				t, DDS_RF2589_DEFAULT_TTL, DDS_RF2589_MAX_TTL );
1399 			Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1400 				"%s: %s.\n", c->log, c->cr_msg );
1401 			return 1;
1402 		}
1403 
1404 		di->di_max_ttl = (time_t)t;
1405 		break;
1406 
1407 	case DDS_MINTTL:
1408 		if ( lutil_parse_time( c->argv[ 1 ], &t ) != 0 ) {
1409 			snprintf( c->cr_msg, sizeof( c->cr_msg),
1410 				"DDS unable to parse dds-min-ttl \"%s\"",
1411 				c->argv[ 1 ] );
1412 			Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1413 				"%s: %s.\n", c->log, c->cr_msg );
1414 			return 1;
1415 		}
1416 
1417 		if ( t > DDS_RF2589_MAX_TTL ) {
1418 			snprintf( c->cr_msg, sizeof( c->cr_msg ),
1419 				"DDS invalid dds-min-ttl=%lu",
1420 				t );
1421 			Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1422 				"%s: %s.\n", c->log, c->cr_msg );
1423 			return 1;
1424 		}
1425 
1426 		if ( t == 0 ) {
1427 			di->di_min_ttl = DDS_RF2589_DEFAULT_TTL;
1428 
1429 		} else {
1430 			di->di_min_ttl = (time_t)t;
1431 		}
1432 		break;
1433 
1434 	case DDS_DEFAULTTTL:
1435 		if ( lutil_parse_time( c->argv[ 1 ], &t ) != 0 ) {
1436 			snprintf( c->cr_msg, sizeof( c->cr_msg),
1437 				"DDS unable to parse dds-default-ttl \"%s\"",
1438 				c->argv[ 1 ] );
1439 			Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1440 				"%s: %s.\n", c->log, c->cr_msg );
1441 			return 1;
1442 		}
1443 
1444 		if ( t > DDS_RF2589_MAX_TTL ) {
1445 			snprintf( c->cr_msg, sizeof( c->cr_msg ),
1446 				"DDS invalid dds-default-ttl=%lu",
1447 				t );
1448 			Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1449 				"%s: %s.\n", c->log, c->cr_msg );
1450 			return 1;
1451 		}
1452 
1453 		if ( t == 0 ) {
1454 			di->di_default_ttl = DDS_RF2589_DEFAULT_TTL;
1455 
1456 		} else {
1457 			di->di_default_ttl = (time_t)t;
1458 		}
1459 		break;
1460 
1461 	case DDS_INTERVAL:
1462 		if ( lutil_parse_time( c->argv[ 1 ], &t ) != 0 ) {
1463 			snprintf( c->cr_msg, sizeof( c->cr_msg),
1464 				"DDS unable to parse dds-interval \"%s\"",
1465 				c->argv[ 1 ] );
1466 			Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1467 				"%s: %s.\n", c->log, c->cr_msg );
1468 			return 1;
1469 		}
1470 
1471 		if ( t <= 0 ) {
1472 			snprintf( c->cr_msg, sizeof( c->cr_msg ),
1473 				"DDS invalid dds-interval=%lu",
1474 				t );
1475 			Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1476 				"%s: %s.\n", c->log, c->cr_msg );
1477 			return 1;
1478 		}
1479 
1480 		if ( t < 60 ) {
1481 			Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_NOTICE,
1482 				"%s: dds-interval=%lu may be too small.\n",
1483 				c->log, t );
1484 		}
1485 
1486 		di->di_interval = (time_t)t;
1487 		if ( di->di_expire_task ) {
1488 			ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
1489 			if ( ldap_pvt_runqueue_isrunning( &slapd_rq, di->di_expire_task ) ) {
1490 				ldap_pvt_runqueue_stoptask( &slapd_rq, di->di_expire_task );
1491 			}
1492 			di->di_expire_task->interval.tv_sec = DDS_INTERVAL( di );
1493 			ldap_pvt_runqueue_resched( &slapd_rq, di->di_expire_task, 0 );
1494 			ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
1495 		}
1496 		break;
1497 
1498 	case DDS_TOLERANCE:
1499 		if ( lutil_parse_time( c->argv[ 1 ], &t ) != 0 ) {
1500 			snprintf( c->cr_msg, sizeof( c->cr_msg),
1501 				"DDS unable to parse dds-tolerance \"%s\"",
1502 				c->argv[ 1 ] );
1503 			Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1504 				"%s: %s.\n", c->log, c->cr_msg );
1505 			return 1;
1506 		}
1507 
1508 		if ( t > DDS_RF2589_MAX_TTL ) {
1509 			snprintf( c->cr_msg, sizeof( c->cr_msg ),
1510 				"DDS invalid dds-tolerance=%lu",
1511 				t );
1512 			Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1513 				"%s: %s.\n", c->log, c->cr_msg );
1514 			return 1;
1515 		}
1516 
1517 		di->di_tolerance = (time_t)t;
1518 		break;
1519 
1520 	case DDS_MAXDYNAMICOBJS:
1521 		if ( c->value_int < 0 ) {
1522 			snprintf( c->cr_msg, sizeof( c->cr_msg ),
1523 				"DDS invalid dds-max-dynamicObjects=%d", c->value_int );
1524 			Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1525 				"%s: %s.\n", c->log, c->cr_msg );
1526 			return 1;
1527 		}
1528 		di->di_max_dynamicObjects = c->value_int;
1529 		break;
1530 
1531 	default:
1532 		rc = 1;
1533 		break;
1534 	}
1535 
1536 	return rc;
1537 }
1538 
1539 static int
1540 dds_db_init(
1541 	BackendDB	*be,
1542 	ConfigReply	*cr)
1543 {
1544 	slap_overinst	*on = (slap_overinst *)be->bd_info;
1545 	dds_info_t	*di;
1546 	BackendInfo	*bi = on->on_info->oi_orig;
1547 
1548 	if ( SLAP_ISGLOBALOVERLAY( be ) ) {
1549 		Log0( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1550 			"DDS cannot be used as global overlay.\n" );
1551 		return 1;
1552 	}
1553 
1554 	/* check support for required functions */
1555 	/* FIXME: some could be provided by other overlays in between */
1556 	if ( bi->bi_op_add == NULL			/* object creation */
1557 		|| bi->bi_op_delete == NULL		/* object deletion */
1558 		|| bi->bi_op_modify == NULL		/* object refresh */
1559 		|| bi->bi_op_search == NULL		/* object expiration */
1560 		|| bi->bi_entry_get_rw == NULL )	/* object type/existence checking */
1561 	{
1562 		Log1( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1563 			"DDS backend \"%s\" does not provide "
1564 			"required functionality.\n",
1565 			bi->bi_type );
1566 		return 1;
1567 	}
1568 
1569 	di = (dds_info_t *)ch_calloc( 1, sizeof( dds_info_t ) );
1570 	on->on_bi.bi_private = di;
1571 
1572 	di->di_max_ttl = DDS_RF2589_DEFAULT_TTL;
1573 	di->di_max_ttl = DDS_RF2589_DEFAULT_TTL;
1574 
1575 	ldap_pvt_thread_mutex_init( &di->di_mutex );
1576 
1577 	SLAP_DBFLAGS( be ) |= SLAP_DBFLAG_DYNAMIC;
1578 
1579 	return 0;
1580 }
1581 
1582 /* adds dynamicSubtrees to root DSE */
1583 static int
1584 dds_entry_info( void *arg, Entry *e )
1585 {
1586 	dds_info_t	*di = (dds_info_t *)arg;
1587 
1588 	attr_merge( e, slap_schema.si_ad_dynamicSubtrees,
1589 		di->di_suffix, di->di_nsuffix );
1590 
1591 	return 0;
1592 }
1593 
1594 /* callback that counts the returned entries, since the search
1595  * does not get to the point in slap_send_search_entries where
1596  * the actual count occurs */
1597 static int
1598 dds_count_cb( Operation *op, SlapReply *rs )
1599 {
1600 	int	*nump = (int *)op->o_callback->sc_private;
1601 
1602 	switch ( rs->sr_type ) {
1603 	case REP_SEARCH:
1604 		(*nump)++;
1605 		break;
1606 
1607 	case REP_SEARCHREF:
1608 	case REP_RESULT:
1609 		break;
1610 
1611 	default:
1612 		assert( 0 );
1613 	}
1614 
1615 	return 0;
1616 }
1617 
1618 /* count dynamic objects existing in the database at startup */
1619 static int
1620 dds_count( void *ctx, BackendDB *be )
1621 {
1622 	slap_overinst	*on = (slap_overinst *)be->bd_info;
1623 	dds_info_t	*di = (dds_info_t *)on->on_bi.bi_private;
1624 
1625 	Connection	conn = { 0 };
1626 	OperationBuffer opbuf;
1627 	Operation	*op;
1628 	slap_callback	sc = { 0 };
1629 	SlapReply	rs = { REP_RESULT };
1630 
1631 	int		rc;
1632 	char		*extra = "";
1633 
1634 	connection_fake_init2( &conn, &opbuf, ctx, 0 );
1635 	op = &opbuf.ob_op;
1636 
1637 	op->o_tag = LDAP_REQ_SEARCH;
1638 	memset( &op->oq_search, 0, sizeof( op->oq_search ) );
1639 
1640 	op->o_bd = be;
1641 
1642 	op->o_req_dn = op->o_bd->be_suffix[ 0 ];
1643 	op->o_req_ndn = op->o_bd->be_nsuffix[ 0 ];
1644 
1645 	op->o_dn = op->o_bd->be_rootdn;
1646 	op->o_ndn = op->o_bd->be_rootndn;
1647 
1648 	op->ors_scope = LDAP_SCOPE_SUBTREE;
1649 	op->ors_tlimit = SLAP_NO_LIMIT;
1650 	op->ors_slimit = SLAP_NO_LIMIT;
1651 	op->ors_attrs = slap_anlist_no_attrs;
1652 
1653 	op->ors_filterstr.bv_len = STRLENOF( "(objectClass=" ")" )
1654 		+ slap_schema.si_oc_dynamicObject->soc_cname.bv_len;
1655 	op->ors_filterstr.bv_val = op->o_tmpalloc( op->ors_filterstr.bv_len + 1, op->o_tmpmemctx );
1656 	snprintf( op->ors_filterstr.bv_val, op->ors_filterstr.bv_len + 1,
1657 		"(objectClass=%s)",
1658 		slap_schema.si_oc_dynamicObject->soc_cname.bv_val );
1659 
1660 	op->ors_filter = str2filter_x( op, op->ors_filterstr.bv_val );
1661 	if ( op->ors_filter == NULL ) {
1662 		rs.sr_err = LDAP_OTHER;
1663 		goto done_search;
1664 	}
1665 
1666 	op->o_callback = &sc;
1667 	sc.sc_response = dds_count_cb;
1668 	sc.sc_private = &di->di_num_dynamicObjects;
1669 	di->di_num_dynamicObjects = 0;
1670 
1671 	op->o_bd->bd_info = (BackendInfo *)on->on_info;
1672 	(void)op->o_bd->bd_info->bi_op_search( op, &rs );
1673 	op->o_bd->bd_info = (BackendInfo *)on;
1674 
1675 done_search:;
1676 	op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx );
1677 	filter_free_x( op, op->ors_filter, 1 );
1678 
1679 	rc = rs.sr_err;
1680 	switch ( rs.sr_err ) {
1681 	case LDAP_SUCCESS:
1682 		Log1( LDAP_DEBUG_STATS, LDAP_LEVEL_INFO,
1683 			"DDS non-expired=%d\n",
1684 			di->di_num_dynamicObjects );
1685 		break;
1686 
1687 	case LDAP_NO_SUCH_OBJECT:
1688 		/* (ITS#5267) database not created yet? */
1689 		rs.sr_err = LDAP_SUCCESS;
1690 		extra = " (ignored)";
1691 		/* fallthru */
1692 
1693 	default:
1694 		Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1695 			"DDS non-expired objects lookup failed err=%d%s\n",
1696 			rc, extra );
1697 		break;
1698 	}
1699 
1700 	return rs.sr_err;
1701 }
1702 
1703 static int
1704 dds_db_open(
1705 	BackendDB	*be,
1706 	ConfigReply	*cr )
1707 {
1708 	slap_overinst	*on = (slap_overinst *)be->bd_info;
1709 	dds_info_t	*di = on->on_bi.bi_private;
1710 	int		rc = 0;
1711 	void		*thrctx = ldap_pvt_thread_pool_context();
1712 
1713 	if ( slapMode & SLAP_TOOL_MODE )
1714 		return 0;
1715 
1716 	if ( DDS_OFF( di ) ) {
1717 		goto done;
1718 	}
1719 
1720 	if ( SLAP_SINGLE_SHADOW( be ) ) {
1721 		Log1( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1722 			"DDS incompatible with shadow database \"%s\".\n",
1723 			be->be_suffix[ 0 ].bv_val );
1724 		return 1;
1725 	}
1726 
1727 	if ( di->di_max_ttl == 0 ) {
1728 		di->di_max_ttl = DDS_RF2589_DEFAULT_TTL;
1729 	}
1730 
1731 	if ( di->di_min_ttl == 0 ) {
1732 		di->di_max_ttl = DDS_RF2589_DEFAULT_TTL;
1733 	}
1734 
1735 	di->di_suffix = be->be_suffix;
1736 	di->di_nsuffix = be->be_nsuffix;
1737 
1738 	/* count the dynamic objects first */
1739 	rc = dds_count( thrctx, be );
1740 	if ( rc != LDAP_SUCCESS ) {
1741 		rc = 1;
1742 		goto done;
1743 	}
1744 
1745 	/* ... if there are dynamic objects, delete those expired */
1746 	if ( di->di_num_dynamicObjects > 0 ) {
1747 		/* force deletion of expired entries... */
1748 		be->bd_info = (BackendInfo *)on->on_info;
1749 		rc = dds_expire( thrctx, di );
1750 		be->bd_info = (BackendInfo *)on;
1751 		if ( rc != LDAP_SUCCESS ) {
1752 			rc = 1;
1753 			goto done;
1754 		}
1755 	}
1756 
1757 	/* start expire task */
1758 	ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
1759 	di->di_expire_task = ldap_pvt_runqueue_insert( &slapd_rq,
1760 		DDS_INTERVAL( di ),
1761 		dds_expire_fn, di, "dds_expire_fn",
1762 		be->be_suffix[ 0 ].bv_val );
1763 	ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
1764 
1765 	/* register dinamicSubtrees root DSE info support */
1766 	rc = entry_info_register( dds_entry_info, (void *)di );
1767 
1768 done:;
1769 
1770 	return rc;
1771 }
1772 
1773 static int
1774 dds_db_close(
1775 	BackendDB	*be,
1776 	ConfigReply	*cr )
1777 {
1778 	slap_overinst	*on = (slap_overinst *)be->bd_info;
1779 	dds_info_t	*di = on->on_bi.bi_private;
1780 
1781 	/* stop expire task */
1782 	if ( di && di->di_expire_task ) {
1783 		ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
1784 		if ( ldap_pvt_runqueue_isrunning( &slapd_rq, di->di_expire_task ) ) {
1785 			ldap_pvt_runqueue_stoptask( &slapd_rq, di->di_expire_task );
1786 		}
1787 		ldap_pvt_runqueue_remove( &slapd_rq, di->di_expire_task );
1788 		ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
1789 	}
1790 
1791 	(void)entry_info_unregister( dds_entry_info, (void *)di );
1792 
1793 	return 0;
1794 }
1795 
1796 static int
1797 dds_db_destroy(
1798 	BackendDB	*be,
1799 	ConfigReply	*cr )
1800 {
1801 	slap_overinst	*on = (slap_overinst *)be->bd_info;
1802 	dds_info_t	*di = on->on_bi.bi_private;
1803 
1804 	if ( di != NULL ) {
1805 		ldap_pvt_thread_mutex_destroy( &di->di_mutex );
1806 
1807 		free( di );
1808 	}
1809 
1810 	return 0;
1811 }
1812 
1813 static int
1814 slap_exop_refresh(
1815 		Operation	*op,
1816 		SlapReply	*rs )
1817 {
1818 	BackendDB		*bd = op->o_bd;
1819 
1820 	rs->sr_err = slap_parse_refresh( op->ore_reqdata, &op->o_req_ndn, NULL,
1821 		&rs->sr_text, op->o_tmpmemctx );
1822 	if ( rs->sr_err != LDAP_SUCCESS ) {
1823 		return rs->sr_err;
1824 	}
1825 
1826 	Log2( LDAP_DEBUG_STATS, LDAP_LEVEL_INFO,
1827 		"%s REFRESH dn=\"%s\"\n",
1828 		op->o_log_prefix, op->o_req_ndn.bv_val );
1829 	op->o_req_dn = op->o_req_ndn;
1830 
1831 	op->o_bd = select_backend( &op->o_req_ndn, 0 );
1832 	if ( op->o_bd == NULL ) {
1833 		send_ldap_error( op, rs, LDAP_NO_SUCH_OBJECT,
1834 			"no global superior knowledge" );
1835 		goto done;
1836 	}
1837 
1838 	if ( !SLAP_DYNAMIC( op->o_bd ) ) {
1839 		send_ldap_error( op, rs, LDAP_UNAVAILABLE_CRITICAL_EXTENSION,
1840 			"backend does not support dynamic directory services" );
1841 		goto done;
1842 	}
1843 
1844 	rs->sr_err = backend_check_restrictions( op, rs,
1845 		(struct berval *)&slap_EXOP_REFRESH );
1846 	if ( rs->sr_err != LDAP_SUCCESS ) {
1847 		goto done;
1848 	}
1849 
1850 	if ( op->o_bd->be_extended == NULL ) {
1851 		send_ldap_error( op, rs, LDAP_UNAVAILABLE_CRITICAL_EXTENSION,
1852 			"backend does not support extended operations" );
1853 		goto done;
1854 	}
1855 
1856 	op->o_bd->be_extended( op, rs );
1857 
1858 done:;
1859 	if ( !BER_BVISNULL( &op->o_req_ndn ) ) {
1860 		op->o_tmpfree( op->o_req_ndn.bv_val, op->o_tmpmemctx );
1861 		BER_BVZERO( &op->o_req_ndn );
1862 		BER_BVZERO( &op->o_req_dn );
1863 	}
1864 	op->o_bd = bd;
1865 
1866         return rs->sr_err;
1867 }
1868 
1869 static slap_overinst dds;
1870 
1871 static int do_not_load_exop;
1872 static int do_not_replace_exop;
1873 static int do_not_load_schema;
1874 
1875 #if SLAPD_OVER_DDS == SLAPD_MOD_DYNAMIC
1876 static
1877 #endif /* SLAPD_OVER_DDS == SLAPD_MOD_DYNAMIC */
1878 int
1879 dds_initialize()
1880 {
1881 	int		rc = 0;
1882 	int		i, code;
1883 
1884 	/* Make sure we don't exceed the bits reserved for userland */
1885 	config_check_userland( DDS_LAST );
1886 
1887 	if ( !do_not_load_schema ) {
1888 		static struct {
1889 			char			*desc;
1890 			slap_mask_t		flags;
1891 			AttributeDescription	**ad;
1892 		}		s_at[] = {
1893 			{ "( 1.3.6.1.4.1.4203.666.1.57 "
1894 				"NAME ( 'entryExpireTimestamp' ) "
1895 				"DESC 'RFC2589 OpenLDAP extension: expire time of a dynamic object, "
1896 					"computed as now + entryTtl' "
1897 				"EQUALITY generalizedTimeMatch "
1898 				"ORDERING generalizedTimeOrderingMatch "
1899 				"SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 "
1900 				"SINGLE-VALUE "
1901 				"NO-USER-MODIFICATION "
1902 				"USAGE dSAOperation )",
1903 				SLAP_AT_HIDE,
1904 				&ad_entryExpireTimestamp },
1905 			{ NULL }
1906 		};
1907 
1908 		for ( i = 0; s_at[ i ].desc != NULL; i++ ) {
1909 			code = register_at( s_at[ i ].desc, s_at[ i ].ad, 0 );
1910 			if ( code ) {
1911 				Debug( LDAP_DEBUG_ANY,
1912 					"dds_initialize: register_at failed\n", 0, 0, 0 );
1913 				return code;
1914 			}
1915 			(*s_at[ i ].ad)->ad_type->sat_flags |= SLAP_AT_HIDE;
1916 		}
1917 	}
1918 
1919 	if ( !do_not_load_exop ) {
1920 		rc = load_extop2( (struct berval *)&slap_EXOP_REFRESH,
1921 			SLAP_EXOP_WRITES|SLAP_EXOP_HIDE, slap_exop_refresh,
1922 			!do_not_replace_exop );
1923 		if ( rc != LDAP_SUCCESS ) {
1924 			Log1( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1925 				"DDS unable to register refresh exop: %d.\n",
1926 				rc );
1927 			return rc;
1928 		}
1929 	}
1930 
1931 	dds.on_bi.bi_type = "dds";
1932 
1933 	dds.on_bi.bi_db_init = dds_db_init;
1934 	dds.on_bi.bi_db_open = dds_db_open;
1935 	dds.on_bi.bi_db_close = dds_db_close;
1936 	dds.on_bi.bi_db_destroy = dds_db_destroy;
1937 
1938 	dds.on_bi.bi_op_add = dds_op_add;
1939 	dds.on_bi.bi_op_delete = dds_op_delete;
1940 	dds.on_bi.bi_op_modify = dds_op_modify;
1941 	dds.on_bi.bi_op_modrdn = dds_op_rename;
1942 	dds.on_bi.bi_extended = dds_op_extended;
1943 
1944 	dds.on_bi.bi_cf_ocs = dds_ocs;
1945 
1946 	rc = config_register_schema( dds_cfg, dds_ocs );
1947 	if ( rc ) {
1948 		return rc;
1949 	}
1950 
1951 	return overlay_register( &dds );
1952 }
1953 
1954 #if SLAPD_OVER_DDS == SLAPD_MOD_DYNAMIC
1955 int
1956 init_module( int argc, char *argv[] )
1957 {
1958 	int	i;
1959 
1960 	for ( i = 0; i < argc; i++ ) {
1961 		char	*arg = argv[ i ];
1962 		int	no = 0;
1963 
1964 		if ( strncasecmp( arg, "no-", STRLENOF( "no-" ) ) == 0 ) {
1965 			arg += STRLENOF( "no-" );
1966 			no = 1;
1967 		}
1968 
1969 		if ( strcasecmp( arg, "exop" ) == 0 ) {
1970 			do_not_load_exop = no;
1971 
1972 		} else if ( strcasecmp( arg, "replace" ) == 0 ) {
1973 			do_not_replace_exop = no;
1974 
1975 		} else if ( strcasecmp( arg, "schema" ) == 0 ) {
1976 			do_not_load_schema = no;
1977 
1978 		} else {
1979 			Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1980 				"DDS unknown module arg[#%d]=\"%s\".\n",
1981 				i, argv[ i ] );
1982 			return 1;
1983 		}
1984 	}
1985 
1986 	return dds_initialize();
1987 }
1988 #endif /* SLAPD_OVER_DDS == SLAPD_MOD_DYNAMIC */
1989 
1990 #endif	/* defined(SLAPD_OVER_DDS) */
1991