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