2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4 * Copyright 2005-2006 The OpenLDAP Foundation.
5 * Portions Copyright 2005-2006 SysNet s.n.c.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted only as authorized by the OpenLDAP
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>.
17 * This work was initially developed by Pierangelo Masarati for inclusion
18 * in OpenLDAP Software, sponsored by SysNet s.n.c.
27 #include <ac/string.h>
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 */
40 typedef struct dds_info_t {
42 #define DDS_FOFF (0x1U) /* is this really needed? */
43 #define DDS_SET(di, f) ( (di)->di_flags & (f) )
45 #define DDS_OFF(di) DDS_SET( (di), DDS_FOFF )
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 )
55 /* expire check interval and task */
57 #define DDS_INTERVAL(di) \
58 ( (di)->di_interval ? (di)->di_interval : DDS_DEFAULT_INTERVAL )
59 struct re_s *di_expire_task;
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;
66 /* used to advertize the dynamicSubtrees in the root DSE,
67 * and to select the database in the expiration task */
72 static struct berval slap_EXOP_REFRESH = BER_BVC( LDAP_EXOP_REFRESH );
73 static AttributeDescription *ad_entryExpireTimestamp;
75 /* list of expired DNs */
76 typedef struct dds_expire_t {
78 struct dds_expire_t *de_next;
81 typedef struct dds_cb_t {
82 dds_expire_t *dc_ndnlist;
86 dds_expire_cb( Operation *op, SlapReply *rs )
88 dds_cb_t *dc = (dds_cb_t *)op->o_callback->sc_private;
92 switch ( rs->sr_type ) {
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,
98 de->de_next = dc->dc_ndnlist;
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 );
121 dds_expire( void *ctx, dds_info_t *di )
123 Connection conn = { 0 };
124 OperationBuffer opbuf;
126 slap_callback sc = { 0 }, sc2 = { 0 };
128 dds_expire_t *de = NULL, **dep;
129 SlapReply rs = { REP_RESULT };
132 char tsbuf[ LDAP_LUTIL_GENTIME_BUFSIZE ];
135 int ndeletes, ntotdeletes;
137 op = (Operation *)&opbuf;
138 connection_fake_init( &conn, op, ctx );
140 op->o_tag = LDAP_REQ_SEARCH;
141 memset( &op->oq_search, 0, sizeof( op->oq_search ) );
143 op->o_bd = select_backend( &di->di_nsuffix[ 0 ], 0, 0 );
145 op->o_req_dn = op->o_bd->be_suffix[ 0 ];
146 op->o_req_ndn = op->o_bd->be_nsuffix[ 0 ];
148 op->o_dn = op->o_bd->be_rootdn;
149 op->o_ndn = op->o_bd->be_rootndn;
151 op->ors_scope = LDAP_SCOPE_SUBTREE;
152 op->ors_tlimit = DDS_INTERVAL( di )/2 + 1;
153 op->ors_slimit = SLAP_NO_LIMIT;
154 op->ors_attrs = slap_anlist_no_attrs;
156 expire = slap_get_time() + di->di_tolerance;
158 ts.bv_len = sizeof( tsbuf );
159 slap_timestamp( &expire, &ts );
161 op->ors_filterstr.bv_len = STRLENOF( "(&(objectClass=" ")(" "<=" "))" )
162 + slap_schema.si_oc_dynamicObject->soc_cname.bv_len
163 + ad_entryExpireTimestamp->ad_cname.bv_len
165 op->ors_filterstr.bv_val = op->o_tmpalloc( op->ors_filterstr.bv_len + 1, op->o_tmpmemctx );
166 snprintf( op->ors_filterstr.bv_val, op->ors_filterstr.bv_len + 1,
167 "(&(objectClass=%s)(%s<=%s))",
168 slap_schema.si_oc_dynamicObject->soc_cname.bv_val,
169 ad_entryExpireTimestamp->ad_cname.bv_val, ts.bv_val );
171 op->ors_filter = str2filter_x( op, op->ors_filterstr.bv_val );
172 if ( op->ors_filter == NULL ) {
173 rs.sr_err = LDAP_OTHER;
177 op->o_callback = ≻
178 sc.sc_response = dds_expire_cb;
181 (void)op->o_bd->bd_info->bi_op_search( op, &rs );
184 op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx );
185 filter_free_x( op, op->ors_filter );
187 if ( rs.sr_err != LDAP_SUCCESS ) {
188 Log1( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
189 "DDS expired objects lookup failed err=%d\n",
194 op->o_tag = LDAP_REQ_DELETE;
195 op->o_callback = ≻
196 sc.sc_response = slap_replog_cb;
197 sc.sc_private = NULL;
199 sc2.sc_response = slap_null_cb;
201 for ( ntotdeletes = 0, ndeletes = 1; dc.dc_ndnlist != NULL && ndeletes > 0; ) {
204 for ( dep = &dc.dc_ndnlist; *dep != NULL; ) {
207 op->o_req_dn = de->de_ndn;
208 op->o_req_ndn = de->de_ndn;
209 (void)op->o_bd->bd_info->bi_op_delete( op, &rs );
210 switch ( rs.sr_err ) {
212 Log1( LDAP_DEBUG_STATS, LDAP_LEVEL_INFO,
213 "DDS dn=\"%s\" expired.\n",
218 case LDAP_NOT_ALLOWED_ON_NONLEAF:
219 Log1( LDAP_DEBUG_ANY, LDAP_LEVEL_NOTICE,
220 "DDS dn=\"%s\" is non-leaf; "
228 Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_NOTICE,
229 "DDS dn=\"%s\" err=%d; "
231 de->de_ndn.bv_val, rs.sr_err );
238 op->o_tmpfree( de, op->o_tmpmemctx );
242 ntotdeletes += ndeletes;
245 rs.sr_err = LDAP_SUCCESS;
247 Log1( LDAP_DEBUG_STATS, LDAP_LEVEL_INFO,
248 "DDS expired=%d\n", ntotdeletes );
255 dds_expire_fn( void *ctx, void *arg )
257 struct re_s *rtask = arg;
258 dds_info_t *di = rtask->arg;
260 assert( di->di_expire_task == rtask );
262 (void)dds_expire( ctx, di );
264 ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
265 if ( ldap_pvt_runqueue_isrunning( &slapd_rq, rtask )) {
266 ldap_pvt_runqueue_stoptask( &slapd_rq, rtask );
268 ldap_pvt_runqueue_resched( &slapd_rq, rtask, 0 );
269 ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
274 /* frees the callback */
276 dds_freeit_cb( Operation *op, SlapReply *rs )
278 op->o_tmpfree( op->o_callback, op->o_tmpmemctx );
279 op->o_callback = NULL;
281 return SLAP_CB_CONTINUE;
284 /* updates counter - installed on add/delete only if required */
286 dds_counter_cb( Operation *op, SlapReply *rs )
288 assert( rs->sr_type == REP_RESULT );
290 if ( rs->sr_err == LDAP_SUCCESS ) {
291 dds_info_t *di = op->o_callback->sc_private;
293 ldap_pvt_thread_mutex_lock( &di->di_mutex );
294 switch ( op->o_tag ) {
295 case LDAP_REQ_DELETE:
296 assert( di->di_num_dynamicObjects > 0 );
297 di->di_num_dynamicObjects--;
301 assert( di->di_num_dynamicObjects < di->di_max_dynamicObjects );
302 di->di_num_dynamicObjects++;
308 ldap_pvt_thread_mutex_unlock( &di->di_mutex );
311 return dds_freeit_cb( op, rs );
315 dds_op_add( Operation *op, SlapReply *rs )
317 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
318 dds_info_t *di = on->on_bi.bi_private;
319 int is_dynamicObject;
321 if ( DDS_OFF( di ) ) {
322 return SLAP_CB_CONTINUE;
325 is_dynamicObject = is_entry_dynamicObject( op->ora_e );
327 /* FIXME: do not allow this right now, pending clarification */
328 if ( is_dynamicObject ) {
329 rs->sr_err = LDAP_SUCCESS;
331 if ( is_entry_referral( op->ora_e ) ) {
332 rs->sr_err = LDAP_OBJECT_CLASS_VIOLATION;
333 rs->sr_text = "a referral cannot be a dynamicObject";
335 } else if ( is_entry_alias( op->ora_e ) ) {
336 rs->sr_err = LDAP_OBJECT_CLASS_VIOLATION;
337 rs->sr_text = "an alias cannot be a dynamicObject";
340 if ( rs->sr_err != LDAP_SUCCESS ) {
341 op->o_bd->bd_info = (BackendInfo *)on->on_info;
342 send_ldap_result( op, rs );
347 /* we don't allow dynamicObjects to have static subordinates */
348 if ( !dn_match( &op->o_req_ndn, &op->o_bd->be_nsuffix[ 0 ] ) ) {
352 BackendInfo *bi = op->o_bd->bd_info;
354 dnParent( &op->o_req_ndn, &p_ndn );
355 op->o_bd->bd_info = (BackendInfo *)on->on_info;
356 rc = be_entry_get_rw( op, &p_ndn,
357 slap_schema.si_oc_dynamicObject, NULL, 0, &e );
358 if ( rc == LDAP_SUCCESS && e != NULL ) {
359 if ( !is_dynamicObject ) {
360 #ifdef SLAP_ACL_HONOR_DISCLOSE
361 /* return referral only if "disclose"
362 * is granted on the object */
363 if ( ! access_allowed( op, e,
364 slap_schema.si_ad_entry,
365 NULL, ACL_DISCLOSE, NULL ) )
367 rc = rs->sr_err = LDAP_NO_SUCH_OBJECT;
368 send_ldap_result( op, rs );
371 #endif /* SLAP_ACL_HONOR_DISCLOSE */
373 rc = rs->sr_err = LDAP_CONSTRAINT_VIOLATION;
374 send_ldap_error( op, rs, rc, "no static subordinate entries allowed for dynamicObject" );
378 be_entry_release_r( op, e );
379 if ( rc != LDAP_SUCCESS ) {
383 op->o_bd->bd_info = bi;
386 /* handle dynamic object operational attr(s) */
387 if ( is_dynamicObject ) {
389 char ttlbuf[] = "31557600";
390 char tsbuf[ LDAP_LUTIL_GENTIME_BUFSIZE ];
393 ldap_pvt_thread_mutex_lock( &di->di_mutex );
394 rs->sr_err = ( di->di_max_dynamicObjects &&
395 di->di_num_dynamicObjects >= di->di_max_dynamicObjects );
396 ldap_pvt_thread_mutex_unlock( &di->di_mutex );
398 op->o_bd->bd_info = (BackendInfo *)on->on_info;
399 send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM,
400 "too many dynamicObjects in context" );
404 ttl = DDS_DEFAULT_TTL( di );
406 assert( ttl <= DDS_RF2589_MAX_TTL );
409 bv.bv_len = snprintf( ttlbuf, sizeof( ttlbuf ), "%ld", ttl );
411 /* FIXME: apparently, values in op->ora_e are malloc'ed
412 * on the thread's slab; works fine by chance,
413 * only because the attribute doesn't exist yet. */
414 assert( attr_find( op->ora_e->e_attrs, slap_schema.si_ad_entryTtl ) == NULL );
415 attr_merge_one( op->ora_e, slap_schema.si_ad_entryTtl, &bv, &bv );
417 expire = slap_get_time() + ttl;
419 bv.bv_len = sizeof( tsbuf );
420 slap_timestamp( &expire, &bv );
421 assert( attr_find( op->ora_e->e_attrs, ad_entryExpireTimestamp ) == NULL );
422 attr_merge_one( op->ora_e, ad_entryExpireTimestamp, &bv, &bv );
424 /* if required, install counter callback */
425 if ( di->di_max_dynamicObjects > 0) {
428 sc = op->o_tmpalloc( sizeof( slap_callback ), op->o_tmpmemctx );
429 sc->sc_cleanup = dds_freeit_cb;
430 sc->sc_response = dds_counter_cb;
432 sc->sc_next = op->o_callback;
438 return SLAP_CB_CONTINUE;
442 dds_op_delete( Operation *op, SlapReply *rs )
444 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
445 dds_info_t *di = on->on_bi.bi_private;
447 /* if required, install counter callback */
448 if ( !DDS_OFF( di ) && di->di_max_dynamicObjects > 0 ) {
450 BackendInfo *bi = op->o_bd->bd_info;
452 op->o_bd->bd_info = (BackendInfo *)on->on_info;
453 rs->sr_err = be_entry_get_rw( op, &op->o_req_ndn,
454 slap_schema.si_oc_dynamicObject, NULL, 0, &e );
456 /* FIXME: couldn't the entry be added before deletion? */
457 if ( rs->sr_err == LDAP_SUCCESS && e != NULL ) {
460 be_entry_release_r( op, e );
463 sc = op->o_tmpalloc( sizeof( slap_callback ), op->o_tmpmemctx );
464 sc->sc_cleanup = dds_freeit_cb;
465 sc->sc_response = dds_counter_cb;
467 sc->sc_next = op->o_callback;
471 op->o_bd->bd_info = bi;
474 return SLAP_CB_CONTINUE;
478 dds_op_modify( Operation *op, SlapReply *rs )
480 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
481 dds_info_t *di = (dds_info_t *)on->on_bi.bi_private;
484 BackendInfo *bi = op->o_bd->bd_info;
485 int was_dynamicObject = 0,
486 is_dynamicObject = 0;
487 struct berval bv_entryTtl = BER_BVNULL;
489 char textbuf[ SLAP_TEXT_BUFLEN ];
491 if ( DDS_OFF( di ) ) {
492 return SLAP_CB_CONTINUE;
495 /* bv_entryTtl stores the string representation of the entryTtl
496 * across modifies for consistency checks of the final value;
497 * the bv_val points to a static buffer; the bv_len is zero when
498 * the attribute is deleted.
499 * entryTtl stores the integer representation of the entryTtl;
500 * its value is -1 when the attribute is deleted; it is 0 only
501 * if no modifications of the entryTtl occurred, as an entryTtl
502 * of 0 is invalid. */
503 bv_entryTtl.bv_val = textbuf;
505 op->o_bd->bd_info = (BackendInfo *)on->on_info;
506 rs->sr_err = be_entry_get_rw( op, &op->o_req_ndn,
507 slap_schema.si_oc_dynamicObject, slap_schema.si_ad_entryTtl, 0, &e );
508 if ( rs->sr_err == LDAP_SUCCESS && e != NULL ) {
509 Attribute *a = attr_find( e->e_attrs, slap_schema.si_ad_entryTtl );
511 /* the value of the entryTtl is saved for later checks */
516 bv_entryTtl.bv_len = a->a_nvals[ 0 ].bv_len;
517 AC_MEMCPY( bv_entryTtl.bv_val, a->a_nvals[ 0 ].bv_val, bv_entryTtl.bv_len );
518 bv_entryTtl.bv_val[ bv_entryTtl.bv_len ] = '\0';
519 rc = lutil_atoul( &ttl, bv_entryTtl.bv_val );
521 entryTtl = (time_t)ttl;
524 be_entry_release_r( op, e );
526 was_dynamicObject = is_dynamicObject = 1;
528 op->o_bd->bd_info = bi;
530 rs->sr_err = LDAP_SUCCESS;
531 for ( mod = op->orm_modlist; mod; mod = mod->sml_next ) {
532 if ( mod->sml_desc == slap_schema.si_ad_objectClass ) {
536 switch ( mod->sml_op ) {
537 case LDAP_MOD_DELETE:
538 if ( mod->sml_values == NULL ) {
539 is_dynamicObject = 0;
543 for ( i = 0; !BER_BVISNULL( &mod->sml_values[ i ] ); i++ ) {
544 oc = oc_bvfind( &mod->sml_values[ i ] );
545 if ( oc == slap_schema.si_oc_dynamicObject ) {
546 is_dynamicObject = 0;
553 case LDAP_MOD_REPLACE:
554 if ( mod->sml_values == NULL ) {
555 is_dynamicObject = 0;
561 for ( i = 0; !BER_BVISNULL( &mod->sml_values[ i ] ); i++ ) {
562 oc = oc_bvfind( &mod->sml_values[ i ] );
563 if ( oc == slap_schema.si_oc_dynamicObject ) {
564 is_dynamicObject = 1;
571 } else if ( mod->sml_desc == slap_schema.si_ad_entryTtl ) {
575 switch ( mod->sml_op ) {
576 case LDAP_MOD_DELETE:
577 if ( mod->sml_values != NULL ) {
578 if ( BER_BVISEMPTY( &bv_entryTtl )
579 || !bvmatch( &bv_entryTtl, &mod->sml_values[ 0 ] ) )
581 #ifdef SLAP_ACL_HONOR_DISCLOSE
582 rs->sr_err = backend_attribute( op, NULL, &op->o_req_ndn,
583 slap_schema.si_ad_entry, NULL, ACL_DISCLOSE );
584 if ( rs->sr_err == LDAP_INSUFFICIENT_ACCESS ) {
585 rs->sr_err = LDAP_NO_SUCH_OBJECT;
588 #endif /* SLAP_ACL_HONOR_DISCLOSE */
590 rs->sr_err = LDAP_NO_SUCH_ATTRIBUTE;
595 bv_entryTtl.bv_len = 0;
599 case LDAP_MOD_REPLACE:
600 bv_entryTtl.bv_len = 0;
604 case SLAP_MOD_SOFTADD: /* FIXME? */
606 assert( mod->sml_values != NULL );
607 assert( BER_BVISNULL( &mod->sml_values[ 1 ] ) );
609 if ( !BER_BVISEMPTY( &bv_entryTtl ) ) {
610 #ifdef SLAP_ACL_HONOR_DISCLOSE
611 rs->sr_err = backend_attribute( op, NULL, &op->o_req_ndn,
612 slap_schema.si_ad_entry, NULL, ACL_DISCLOSE );
613 if ( rs->sr_err == LDAP_INSUFFICIENT_ACCESS ) {
614 rs->sr_err = LDAP_NO_SUCH_OBJECT;
617 #endif /* SLAP_ACL_HONOR_DISCLOSE */
619 rs->sr_text = "attribute 'entryTtl' cannot have multiple values";
620 rs->sr_err = LDAP_CONSTRAINT_VIOLATION;
625 rc = lutil_atoul( &ttl, mod->sml_values[ 0 ].bv_val );
627 if ( ttl > DDS_RF2589_MAX_TTL ) {
628 rs->sr_err = LDAP_PROTOCOL_ERROR;
629 rs->sr_text = "invalid time-to-live for dynamicObject";
633 if ( ttl <= 0 || ttl > di->di_max_ttl ) {
634 /* FIXME: I don't understand if this has to be an error,
635 * or an indication that the requested Ttl has been
636 * shortened to di->di_max_ttl >= 1 day */
637 rs->sr_err = LDAP_SIZELIMIT_EXCEEDED;
638 rs->sr_text = "time-to-live for dynamicObject exceeds administrative limit";
642 entryTtl = (time_t)ttl;
643 bv_entryTtl.bv_len = mod->sml_values[ 0 ].bv_len;
644 AC_MEMCPY( bv_entryTtl.bv_val, mod->sml_values[ 0 ].bv_val, bv_entryTtl.bv_len );
645 bv_entryTtl.bv_val[ bv_entryTtl.bv_len ] = '\0';
648 case LDAP_MOD_INCREMENT:
649 if ( BER_BVISEMPTY( &bv_entryTtl ) ) {
650 #ifdef SLAP_ACL_HONOR_DISCLOSE
651 rs->sr_err = backend_attribute( op, NULL, &op->o_req_ndn,
652 slap_schema.si_ad_entry, NULL, ACL_DISCLOSE );
653 if ( rs->sr_err == LDAP_INSUFFICIENT_ACCESS ) {
654 rs->sr_err = LDAP_NO_SUCH_OBJECT;
657 #endif /* SLAP_ACL_HONOR_DISCLOSE */
659 rs->sr_err = LDAP_NO_SUCH_ATTRIBUTE;
660 rs->sr_text = "modify/increment: entryTtl: no such attribute";
666 if ( entryTtl > DDS_RF2589_MAX_TTL ) {
667 rs->sr_err = LDAP_PROTOCOL_ERROR;
668 rs->sr_text = "invalid time-to-live for dynamicObject";
670 } else if ( entryTtl <= 0 || entryTtl > di->di_max_ttl ) {
671 /* FIXME: I don't understand if this has to be an error,
672 * or an indication that the requested Ttl has been
673 * shortened to di->di_max_ttl >= 1 day */
674 rs->sr_err = LDAP_SIZELIMIT_EXCEEDED;
675 rs->sr_text = "time-to-live for dynamicObject exceeds administrative limit";
678 if ( rs->sr_err != LDAP_SUCCESS ) {
679 #ifdef SLAP_ACL_HONOR_DISCLOSE
680 rc = backend_attribute( op, NULL, &op->o_req_ndn,
681 slap_schema.si_ad_entry, NULL, ACL_DISCLOSE );
682 if ( rc == LDAP_INSUFFICIENT_ACCESS ) {
684 rs->sr_err = LDAP_NO_SUCH_OBJECT;
687 #endif /* SLAP_ACL_HONOR_DISCLOSE */
691 bv_entryTtl.bv_len = snprintf( textbuf, sizeof( textbuf ), "%ld", entryTtl );
699 } else if ( mod->sml_desc == ad_entryExpireTimestamp ) {
700 /* should have been trapped earlier */
701 assert( mod->sml_flags & SLAP_MOD_INTERNAL );
706 if ( rs->sr_err == LDAP_SUCCESS ) {
709 /* FIXME: this could be allowed when manageDIT is used...
715 * entryTtl must be provided; add
716 * entryExpireTimestamp accordingly
719 * entryTtl must be removed; remove
720 * entryTimestamp accordingly
722 * ... but we need to make sure that there are no subordinate
725 rc = is_dynamicObject - was_dynamicObject;
727 #if 0 /* fix subordinate issues first */
728 if ( get_manageDIT( op ) ) {
731 /* need to delete entryTtl to have a consistent entry */
732 if ( entryTtl != -1 ) {
733 rs->sr_text = "objectClass modification from dynamicObject to static entry requires entryTtl deletion";
734 rs->sr_err = LDAP_OBJECT_CLASS_VIOLATION;
739 /* need to add entryTtl to have a consistent entry */
740 if ( entryTtl <= 0 ) {
741 rs->sr_text = "objectClass modification from static entry to dynamicObject requires entryTtl addition";
742 rs->sr_err = LDAP_OBJECT_CLASS_VIOLATION;
752 rs->sr_text = "objectClass modification cannot turn dynamicObject into static entry";
756 rs->sr_text = "objectClass modification cannot turn static entry into dynamicObject";
759 rs->sr_err = LDAP_OBJECT_CLASS_VIOLATION;
762 #ifdef SLAP_ACL_HONOR_DISCLOSE
763 if ( rc != LDAP_SUCCESS ) {
764 rc = backend_attribute( op, NULL, &op->o_req_ndn,
765 slap_schema.si_ad_entry, NULL, ACL_DISCLOSE );
766 if ( rc == LDAP_INSUFFICIENT_ACCESS ) {
768 rs->sr_err = LDAP_NO_SUCH_OBJECT;
771 #endif /* SLAP_ACL_HONOR_DISCLOSE */
775 if ( rs->sr_err == LDAP_SUCCESS && entryTtl != 0 ) {
776 Modifications *tmpmod = NULL, **modp;
778 for ( modp = &op->orm_modlist; *modp; modp = &(*modp)->sml_next )
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;
788 if ( entryTtl == -1 ) {
789 /* delete entryExpireTimestamp */
790 tmpmod->sml_op = LDAP_MOD_DELETE;
794 char tsbuf[ LDAP_LUTIL_GENTIME_BUFSIZE ];
797 /* keep entryExpireTimestamp consistent
799 expire = slap_get_time() + entryTtl;
801 bv.bv_len = sizeof( tsbuf );
802 slap_timestamp( &expire, &bv );
804 tmpmod->sml_op = LDAP_MOD_REPLACE;
805 value_add_one( &tmpmod->sml_values, &bv );
810 op->o_bd->bd_info = (BackendInfo *)on->on_info;
811 send_ldap_result( op, rs );
815 return SLAP_CB_CONTINUE;
819 dds_op_rename( Operation *op, SlapReply *rs )
821 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
822 dds_info_t *di = on->on_bi.bi_private;
824 if ( DDS_OFF( di ) ) {
825 return SLAP_CB_CONTINUE;
828 /* we don't allow dynamicObjects to have static subordinates */
829 if ( op->orr_nnewSup != NULL ) {
831 BackendInfo *bi = op->o_bd->bd_info;
832 int is_dynamicObject = 0,
835 rs->sr_err = LDAP_SUCCESS;
837 op->o_bd->bd_info = (BackendInfo *)on->on_info;
838 rc = be_entry_get_rw( op, &op->o_req_ndn,
839 slap_schema.si_oc_dynamicObject, NULL, 0, &e );
840 if ( rc == LDAP_SUCCESS && e != NULL ) {
841 be_entry_release_r( op, e );
843 is_dynamicObject = 1;
846 rc = be_entry_get_rw( op, op->orr_nnewSup,
847 slap_schema.si_oc_dynamicObject, NULL, 0, &e );
848 if ( rc == LDAP_SUCCESS && e != NULL ) {
849 if ( !is_dynamicObject ) {
850 #ifdef SLAP_ACL_HONOR_DISCLOSE
851 /* return referral only if "disclose"
852 * is granted on the object */
853 if ( ! access_allowed( op, e,
854 slap_schema.si_ad_entry,
855 NULL, ACL_DISCLOSE, NULL ) )
857 rs->sr_err = LDAP_NO_SUCH_OBJECT;
858 send_ldap_result( op, rs );
861 #endif /* SLAP_ACL_HONOR_DISCLOSE */
863 send_ldap_error( op, rs, LDAP_CONSTRAINT_VIOLATION,
864 "static entry cannot have dynamicObject as newSuperior" );
867 be_entry_release_r( op, e );
869 op->o_bd->bd_info = bi;
870 if ( rs->sr_err != LDAP_SUCCESS ) {
875 return SLAP_CB_CONTINUE;
886 int rc = LDAP_SUCCESS;
889 BerElementBuffer berbuf;
890 BerElement *ber = (BerElement *)&berbuf;
891 struct berval reqdata = BER_BVNULL;
900 if ( in == NULL || in->bv_len == 0 ) {
901 *text = "empty request data field in refresh exop";
902 return LDAP_PROTOCOL_ERROR;
905 ber_dupbv_x( &reqdata, in, ctx );
907 /* ber_init2 uses reqdata directly, doesn't allocate new buffers */
908 ber_init2( ber, &reqdata, 0 );
910 tag = ber_scanf( ber, "{" /*}*/ );
912 if ( tag == LBER_ERROR ) {
913 Log0( LDAP_DEBUG_TRACE, LDAP_LEVEL_ERR,
914 "slap_parse_refresh: decoding error.\n" );
918 tag = ber_peek_tag( ber, &len );
919 if ( tag != LDAP_TAG_EXOP_REFRESH_REQ_DN ) {
920 Log0( LDAP_DEBUG_TRACE, LDAP_LEVEL_ERR,
921 "slap_parse_refresh: decoding error.\n" );
928 tag = ber_scanf( ber, "m", &dn );
929 if ( tag == LBER_ERROR ) {
930 Log0( LDAP_DEBUG_TRACE, LDAP_LEVEL_ERR,
931 "slap_parse_refresh: DN parse failed.\n" );
935 rc = dnNormalize( 0, NULL, NULL, &dn, ndn, ctx );
936 if ( rc != LDAP_SUCCESS ) {
937 *text = "invalid DN in refresh exop request data";
942 tag = ber_scanf( ber, "x" /* "m" */ );
943 if ( tag == LBER_DEFAULT ) {
948 tag = ber_peek_tag( ber, &len );
950 if ( tag != LDAP_TAG_EXOP_REFRESH_REQ_TTL ) {
951 Log0( LDAP_DEBUG_TRACE, LDAP_LEVEL_ERR,
952 "slap_parse_refresh: decoding error.\n" );
956 tag = ber_scanf( ber, "i", &tmp );
957 if ( tag == LBER_ERROR ) {
958 Log0( LDAP_DEBUG_TRACE, LDAP_LEVEL_ERR,
959 "slap_parse_refresh: TTL parse failed.\n" );
967 tag = ber_peek_tag( ber, &len );
971 Log1( LDAP_DEBUG_TRACE, LDAP_LEVEL_ERR,
972 "slap_parse_refresh: decoding error, len=%ld\n",
974 rc = LDAP_PROTOCOL_ERROR;
975 *text = "data decoding error";
978 if ( ndn && !BER_BVISNULL( ndn ) ) {
979 slap_sl_free( ndn->bv_val, ctx );
984 if ( !BER_BVISNULL( &reqdata ) ) {
985 ber_memfree_x( reqdata.bv_val, ctx );
992 dds_op_extended( Operation *op, SlapReply *rs )
994 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
995 dds_info_t *di = on->on_bi.bi_private;
997 if ( DDS_OFF( di ) ) {
998 return SLAP_CB_CONTINUE;
1001 if ( bvmatch( &op->ore_reqoid, &slap_EXOP_REFRESH ) ) {
1004 BackendDB db = *op->o_bd;
1005 SlapReply rs2 = { REP_RESULT };
1006 Operation op2 = *op;
1007 slap_callback sc = { 0 };
1008 slap_callback sc2 = { 0 };
1009 Modifications ttlmod = { { 0 } };
1010 struct berval ttlvalues[ 2 ];
1011 char ttlbuf[] = "31557600";
1013 rs->sr_err = slap_parse_refresh( op->ore_reqdata, NULL, &ttl,
1014 &rs->sr_text, NULL );
1015 assert( rs->sr_err == LDAP_SUCCESS );
1017 if ( ttl <= 0 || ttl > DDS_RF2589_MAX_TTL ) {
1018 rs->sr_err = LDAP_PROTOCOL_ERROR;
1019 rs->sr_text = "invalid time-to-live for dynamicObject";
1023 if ( ttl > di->di_max_ttl ) {
1024 /* FIXME: I don't understand if this has to be an error,
1025 * or an indication that the requested Ttl has been
1026 * shortened to di->di_max_ttl >= 1 day */
1027 rs->sr_err = LDAP_SIZELIMIT_EXCEEDED;
1028 rs->sr_text = "time-to-live for dynamicObject exceeds limit";
1032 if ( di->di_min_ttl && ttl < di->di_min_ttl ) {
1033 ttl = di->di_min_ttl;
1036 #ifndef SLAPD_MULTIMASTER
1037 /* This does not apply to multi-master case */
1038 if ( !( !SLAP_SHADOW( op->o_bd ) || be_isupdate( op ) ) ) {
1039 /* we SHOULD return a referral in this case */
1040 BerVarray defref = op->o_bd->be_update_refs
1041 ? op->o_bd->be_update_refs : default_referral;
1043 if ( defref != NULL ) {
1044 rs->sr_ref = referral_rewrite( op->o_bd->be_update_refs,
1045 NULL, NULL, LDAP_SCOPE_DEFAULT );
1047 rs->sr_flags |= REP_REF_MUSTBEFREED;
1049 rs->sr_ref = defref;
1051 rs->sr_err = LDAP_REFERRAL;
1054 rs->sr_text = "shadow context; no update referral";
1055 rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
1060 #endif /* !SLAPD_MULTIMASTER */
1062 assert( !BER_BVISNULL( &op->o_req_ndn ) );
1066 /* check if exists but not dynamicObject */
1067 op->o_bd->bd_info = (BackendInfo *)on->on_info;
1068 rs->sr_err = be_entry_get_rw( op, &op->o_req_ndn,
1069 slap_schema.si_oc_dynamicObject, NULL, 0, &e );
1070 if ( rs->sr_err != LDAP_SUCCESS ) {
1071 rs->sr_err = be_entry_get_rw( op, &op->o_req_ndn,
1072 NULL, NULL, 0, &e );
1073 if ( rs->sr_err == LDAP_SUCCESS && e != NULL ) {
1074 #ifdef SLAP_ACL_HONOR_DISCLOSE
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 ) )
1081 rs->sr_err = LDAP_NO_SUCH_OBJECT;
1084 #endif /* SLAP_ACL_HONOR_DISCLOSE */
1086 rs->sr_err = LDAP_OBJECT_CLASS_VIOLATION;
1087 rs->sr_text = "refresh operation only applies to dynamic objects";
1089 be_entry_release_r( op, e );
1092 rs->sr_err = LDAP_NO_SUCH_OBJECT;
1096 } else if ( e != NULL ) {
1097 be_entry_release_r( op, e );
1100 /* we require manage privileges on the entryTtl,
1101 * and fake a manageDIT control */
1102 op2.o_tag = LDAP_REQ_MODIFY;
1104 db.bd_info = (BackendInfo *)on->on_info;
1105 op2.o_callback = ≻
1106 sc.sc_response = slap_replog_cb;
1108 sc2.sc_response = slap_null_cb;
1109 op2.o_managedit = SLAP_CONTROL_CRITICAL;
1110 op2.orm_modlist = &ttlmod;
1112 ttlmod.sml_op = LDAP_MOD_REPLACE;
1113 ttlmod.sml_flags = SLAP_MOD_MANAGING;
1114 ttlmod.sml_desc = slap_schema.si_ad_entryTtl;
1115 ttlmod.sml_values = ttlvalues;
1116 ttlvalues[ 0 ].bv_val = ttlbuf;
1117 ttlvalues[ 0 ].bv_len = snprintf( ttlbuf, sizeof( ttlbuf ), "%ld", ttl );
1118 BER_BVZERO( &ttlvalues[ 1 ] );
1120 /* the entryExpireTimestamp is added by modify */
1121 rs->sr_err = op2.o_bd->be_modify( &op2, &rs2 );
1123 if ( ttlmod.sml_next != NULL ) {
1124 slap_mods_free( ttlmod.sml_next, 1 );
1127 if ( rs->sr_err == LDAP_SUCCESS ) {
1129 BerElementBuffer berbuf;
1130 BerElement *ber = (BerElement *)&berbuf;
1132 if ( rs->sr_err == LDAP_SUCCESS ) {
1133 ber_init_w_nullc( ber, LBER_USE_DER );
1135 rc = ber_printf( ber, "{tiN}", LDAP_TAG_EXOP_REFRESH_RES_TTL, (int)ttl );
1138 rs->sr_err = LDAP_OTHER;
1139 rs->sr_text = "internal error";
1142 (void)ber_flatten( ber, &rs->sr_rspdata );
1143 rs->sr_rspoid = ch_strdup( slap_EXOP_REFRESH.bv_val );
1145 Log3( LDAP_DEBUG_TRACE, LDAP_LEVEL_INFO,
1146 "%s REFRESH dn=\"%s\" TTL=%ld\n",
1147 op->o_log_prefix, op->o_req_ndn.bv_val, ttl );
1150 ber_free_buf( ber );
1157 return SLAP_CB_CONTINUE;
1172 static ConfigDriver dds_cfgen;
1174 static ConfigLDAPadd dds_ldadd;
1175 static ConfigCfAdd dds_cfadd;
1178 static ConfigTable dds_cfg[] = {
1179 { "dds-state", "on|off",
1180 2, 2, 0, ARG_MAGIC|ARG_ON_OFF|DDS_STATE, dds_cfgen,
1181 "( OLcfgOvAt:9.1 NAME 'olcDDSstate' "
1182 "DESC 'RFC2589 Dynamic directory services state' "
1183 "SYNTAX OMsBoolean "
1184 "SINGLE-VALUE )", NULL, NULL },
1185 { "dds-max-ttl", "ttl",
1186 2, 2, 0, ARG_MAGIC|DDS_MAXTTL, dds_cfgen,
1187 "( OLcfgOvAt:9.2 NAME 'olcDDSmaxTtl' "
1188 "DESC 'RFC2589 Dynamic directory services max TTL' "
1189 "SYNTAX OMsDirectoryString "
1190 "SINGLE-VALUE )", NULL, NULL },
1191 { "dds-min-ttl", "ttl",
1192 2, 2, 0, ARG_MAGIC|DDS_MINTTL, dds_cfgen,
1193 "( OLcfgOvAt:9.3 NAME 'olcDDSminTtl' "
1194 "DESC 'RFC2589 Dynamic directory services min TTL' "
1195 "SYNTAX OMsDirectoryString "
1196 "SINGLE-VALUE )", NULL, NULL },
1197 { "dds-default-ttl", "ttl",
1198 2, 2, 0, ARG_MAGIC|DDS_DEFAULTTTL, dds_cfgen,
1199 "( OLcfgOvAt:9.4 NAME 'olcDDSdefaultTtl' "
1200 "DESC 'RFC2589 Dynamic directory services default TTL' "
1201 "SYNTAX OMsDirectoryString "
1202 "SINGLE-VALUE )", NULL, NULL },
1203 { "dds-interval", "interval",
1204 2, 2, 0, ARG_MAGIC|DDS_INTERVAL, dds_cfgen,
1205 "( OLcfgOvAt:9.5 NAME 'olcDDSinterval' "
1206 "DESC 'RFC2589 Dynamic directory services expiration "
1207 "task run interval' "
1208 "SYNTAX OMsDirectoryString "
1209 "SINGLE-VALUE )", NULL, NULL },
1210 { "dds-tolerance", "ttl",
1211 2, 2, 0, ARG_MAGIC|DDS_TOLERANCE, dds_cfgen,
1212 "( OLcfgOvAt:9.6 NAME 'olcDDStolerance' "
1213 "DESC 'RFC2589 Dynamic directory services additional "
1214 "TTL in expiration scheduling' "
1215 "SYNTAX OMsDirectoryString "
1216 "SINGLE-VALUE )", NULL, NULL },
1217 { "dds-max-dynamicObjects", "num",
1218 2, 2, 0, ARG_MAGIC|ARG_INT|DDS_MAXDYNAMICOBJS, dds_cfgen,
1219 "( OLcfgOvAt:9.7 NAME 'olcDDSmaxDynamicObjects' "
1220 "DESC 'RFC2589 Dynamic directory services max number of dynamic objects' "
1221 "SYNTAX OMsInteger "
1222 "SINGLE-VALUE )", NULL, NULL },
1223 { NULL, NULL, 0, 0, 0, ARG_IGNORED }
1226 static ConfigOCs dds_ocs[] = {
1227 { "( OLcfgOvOc:9.1 "
1228 "NAME 'olcDDSConfig' "
1229 "DESC 'RFC2589 Dynamic directory services configuration' "
1230 "SUP olcOverlayConfig "
1235 "$ olcDDSdefaultTtl "
1237 "$ olcDDStolerance "
1238 "$ olcDDSmaxDynamicObjects "
1240 ")", Cft_Overlay, dds_cfg, NULL, NULL /* dds_cfadd */ },
1246 dds_ldadd( CfEntryInfo *p, Entry *e, ConfigArgs *ca )
1248 return LDAP_SUCCESS;
1252 dds_cfadd( Operation *op, SlapReply *rs, Entry *p, ConfigArgs *ca )
1259 dds_cfgen( ConfigArgs *c )
1261 slap_overinst *on = (slap_overinst *)c->bi;
1262 dds_info_t *di = on->on_bi.bi_private;
1267 if ( c->op == SLAP_CONFIG_EMIT ) {
1268 char buf[ SLAP_TEXT_BUFLEN ];
1273 c->value_int = !DDS_OFF( di );
1277 lutil_unparse_time( buf, sizeof( buf ), di->di_max_ttl );
1278 ber_str2bv( buf, 0, 0, &bv );
1279 value_add_one( &c->rvalue_vals, &bv );
1283 if ( di->di_min_ttl ) {
1284 lutil_unparse_time( buf, sizeof( buf ), di->di_min_ttl );
1285 ber_str2bv( buf, 0, 0, &bv );
1286 value_add_one( &c->rvalue_vals, &bv );
1293 case DDS_DEFAULTTTL:
1294 if ( di->di_default_ttl ) {
1295 lutil_unparse_time( buf, sizeof( buf ), di->di_default_ttl );
1296 ber_str2bv( buf, 0, 0, &bv );
1297 value_add_one( &c->rvalue_vals, &bv );
1305 if ( di->di_interval ) {
1306 lutil_unparse_time( buf, sizeof( buf ), di->di_interval );
1307 ber_str2bv( buf, 0, 0, &bv );
1308 value_add_one( &c->rvalue_vals, &bv );
1316 if ( di->di_tolerance ) {
1317 lutil_unparse_time( buf, sizeof( buf ), di->di_tolerance );
1318 ber_str2bv( buf, 0, 0, &bv );
1319 value_add_one( &c->rvalue_vals, &bv );
1326 case DDS_MAXDYNAMICOBJS:
1327 if ( di->di_max_dynamicObjects > 0 ) {
1328 c->value_int = di->di_max_dynamicObjects;
1342 } else if ( c->op == LDAP_MOD_DELETE ) {
1345 di->di_flags &= ~DDS_FOFF;
1349 di->di_min_ttl = DDS_RF2589_DEFAULT_TTL;
1356 case DDS_DEFAULTTTL:
1357 di->di_default_ttl = 0;
1361 di->di_interval = 0;
1365 di->di_tolerance = 0;
1368 case DDS_MAXDYNAMICOBJS:
1369 di->di_max_dynamicObjects = 0;
1380 switch ( c->type ) {
1382 if ( c->value_int ) {
1383 di->di_flags &= ~DDS_FOFF;
1386 di->di_flags |= DDS_FOFF;
1391 if ( lutil_parse_time( c->argv[ 1 ], &t ) != 0 ) {
1392 snprintf( c->msg, sizeof( c->msg),
1393 "DDS unable to parse dds-max-ttl \"%s\"",
1395 Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1396 "%s: %s.\n", c->log, c->msg );
1400 if ( t < DDS_RF2589_DEFAULT_TTL || t > DDS_RF2589_MAX_TTL ) {
1401 snprintf( c->msg, sizeof( c->msg ),
1402 "DDS invalid dds-max-ttl=%ld; must be between %d and %d",
1403 t, DDS_RF2589_DEFAULT_TTL, DDS_RF2589_MAX_TTL );
1404 Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1405 "%s: %s.\n", c->log, c->msg );
1409 di->di_max_ttl = (time_t)t;
1413 if ( lutil_parse_time( c->argv[ 1 ], &t ) != 0 ) {
1414 snprintf( c->msg, sizeof( c->msg),
1415 "DDS unable to parse dds-min-ttl \"%s\"",
1417 Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1418 "%s: %s.\n", c->log, c->msg );
1422 if ( t < 0 || t > DDS_RF2589_MAX_TTL ) {
1423 snprintf( c->msg, sizeof( c->msg ),
1424 "DDS invalid dds-min-ttl=%ld",
1426 Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1427 "%s: %s.\n", c->log, c->msg );
1432 di->di_min_ttl = DDS_RF2589_DEFAULT_TTL;
1435 di->di_min_ttl = (time_t)t;
1439 case DDS_DEFAULTTTL:
1440 if ( lutil_parse_time( c->argv[ 1 ], &t ) != 0 ) {
1441 snprintf( c->msg, sizeof( c->msg),
1442 "DDS unable to parse dds-default-ttl \"%s\"",
1444 Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1445 "%s: %s.\n", c->log, c->msg );
1449 if ( t < 0 || t > DDS_RF2589_MAX_TTL ) {
1450 snprintf( c->msg, sizeof( c->msg ),
1451 "DDS invalid dds-default-ttl=%ld",
1453 Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1454 "%s: %s.\n", c->log, c->msg );
1459 di->di_default_ttl = DDS_RF2589_DEFAULT_TTL;
1462 di->di_default_ttl = (time_t)t;
1467 if ( lutil_parse_time( c->argv[ 1 ], &t ) != 0 ) {
1468 snprintf( c->msg, sizeof( c->msg),
1469 "DDS unable to parse dds-interval \"%s\"",
1471 Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1472 "%s: %s.\n", c->log, c->msg );
1477 snprintf( c->msg, sizeof( c->msg ),
1478 "DDS invalid dds-interval=%ld",
1480 Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1481 "%s: %s.\n", c->log, c->msg );
1486 Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_NOTICE,
1487 "%s: dds-interval=%lu may be too small.\n",
1491 di->di_interval = (time_t)t;
1492 if ( di->di_expire_task ) {
1493 ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
1494 if ( ldap_pvt_runqueue_isrunning( &slapd_rq, di->di_expire_task ) ) {
1495 ldap_pvt_runqueue_stoptask( &slapd_rq, di->di_expire_task );
1497 di->di_expire_task->interval.tv_sec = DDS_INTERVAL( di );
1498 ldap_pvt_runqueue_resched( &slapd_rq, di->di_expire_task, 0 );
1499 ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
1504 if ( lutil_parse_time( c->argv[ 1 ], &t ) != 0 ) {
1505 snprintf( c->msg, sizeof( c->msg),
1506 "DDS unable to parse dds-tolerance \"%s\"",
1508 Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1509 "%s: %s.\n", c->log, c->msg );
1513 if ( t < 0 || t > DDS_RF2589_MAX_TTL ) {
1514 snprintf( c->msg, sizeof( c->msg ),
1515 "DDS invalid dds-tolerance=%ld",
1517 Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1518 "%s: %s.\n", c->log, c->msg );
1522 di->di_tolerance = (time_t)t;
1525 case DDS_MAXDYNAMICOBJS:
1526 if ( c->value_int < 0 ) {
1527 snprintf( c->msg, sizeof( c->msg ),
1528 "DDS invalid dds-max-dynamicObjects=%d", c->value_int );
1529 Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1530 "%s: %s.\n", c->log, c->msg );
1533 di->di_max_dynamicObjects = c->value_int;
1548 slap_overinst *on = (slap_overinst *)be->bd_info;
1550 BackendInfo *bi = on->on_info->oi_orig;
1552 if ( SLAP_ISGLOBALOVERLAY( be ) ) {
1553 Log0( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1554 "DDS cannot be used as global overlay.\n" );
1558 /* check support for required functions */
1559 /* FIXME: some could be provided by other overlays in between */
1560 if ( bi->bi_op_add == NULL /* object creation */
1561 || bi->bi_op_delete == NULL /* object deletion */
1562 || bi->bi_op_modify == NULL /* object refresh */
1563 || bi->bi_op_search == NULL /* object expiration */
1564 || bi->bi_entry_get_rw == NULL ) /* object type/existence checking */
1566 Log1( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1567 "DDS backend \"%s\" does not provide "
1568 "required functionality.\n",
1573 di = (dds_info_t *)ch_calloc( 1, sizeof( dds_info_t ) );
1574 on->on_bi.bi_private = di;
1576 di->di_max_ttl = DDS_RF2589_DEFAULT_TTL;
1577 di->di_max_ttl = DDS_RF2589_DEFAULT_TTL;
1579 ldap_pvt_thread_mutex_init( &di->di_mutex );
1581 SLAP_DBFLAGS( be ) |= SLAP_DBFLAG_DYNAMIC;
1586 /* adds dynamicSubtrees to root DSE */
1588 dds_entry_info( void *arg, Entry *e )
1590 dds_info_t *di = (dds_info_t *)arg;
1592 attr_merge( e, slap_schema.si_ad_dynamicSubtrees,
1593 di->di_suffix, di->di_nsuffix );
1598 /* callback that counts the returned entries, since the search
1599 * does not get to the point in slap_send_search_entries where
1600 * the actual count occurs */
1602 dds_count_cb( Operation *op, SlapReply *rs )
1604 int *nump = (int *)op->o_callback->sc_private;
1606 switch ( rs->sr_type ) {
1622 /* count dynamic objects existing in the database at startup */
1624 dds_count( void *ctx, BackendDB *be )
1626 slap_overinst *on = (slap_overinst *)be->bd_info;
1627 dds_info_t *di = (dds_info_t *)on->on_bi.bi_private;
1629 Connection conn = { 0 };
1630 OperationBuffer opbuf;
1632 slap_callback sc = { 0 };
1633 SlapReply rs = { REP_RESULT };
1635 op = (Operation *)&opbuf;
1636 connection_fake_init( &conn, op, ctx );
1638 op->o_tag = LDAP_REQ_SEARCH;
1639 memset( &op->oq_search, 0, sizeof( op->oq_search ) );
1643 op->o_req_dn = op->o_bd->be_suffix[ 0 ];
1644 op->o_req_ndn = op->o_bd->be_nsuffix[ 0 ];
1646 op->o_dn = op->o_bd->be_rootdn;
1647 op->o_ndn = op->o_bd->be_rootndn;
1649 op->ors_scope = LDAP_SCOPE_SUBTREE;
1650 op->ors_tlimit = SLAP_NO_LIMIT;
1651 op->ors_slimit = SLAP_NO_LIMIT;
1652 op->ors_attrs = slap_anlist_no_attrs;
1654 op->ors_filterstr.bv_len = STRLENOF( "(objectClass=" ")" )
1655 + slap_schema.si_oc_dynamicObject->soc_cname.bv_len;
1656 op->ors_filterstr.bv_val = op->o_tmpalloc( op->ors_filterstr.bv_len + 1, op->o_tmpmemctx );
1657 snprintf( op->ors_filterstr.bv_val, op->ors_filterstr.bv_len + 1,
1659 slap_schema.si_oc_dynamicObject->soc_cname.bv_val );
1661 op->ors_filter = str2filter_x( op, op->ors_filterstr.bv_val );
1662 if ( op->ors_filter == NULL ) {
1663 rs.sr_err = LDAP_OTHER;
1667 op->o_callback = ≻
1668 sc.sc_response = dds_count_cb;
1669 sc.sc_private = &di->di_num_dynamicObjects;
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;
1676 op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx );
1677 filter_free_x( op, op->ors_filter );
1679 if ( rs.sr_err == LDAP_SUCCESS ) {
1680 Log1( LDAP_DEBUG_STATS, LDAP_LEVEL_INFO,
1681 "DDS non-expired=%d\n",
1682 di->di_num_dynamicObjects );
1685 Log1( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1686 "DDS non-expired objects lookup failed err=%d\n",
1697 slap_overinst *on = (slap_overinst *)be->bd_info;
1698 dds_info_t *di = on->on_bi.bi_private;
1700 void *thrctx = ldap_pvt_thread_pool_context();
1702 if ( DDS_OFF( di ) ) {
1706 #ifndef SLAPD_MULTIMASTER
1707 if ( SLAP_SHADOW( be ) ) {
1708 Log1( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1709 "DDS incompatible with shadow database \"%s\".\n",
1710 be->be_suffix[ 0 ].bv_val );
1713 #endif /* ! SLAPD_MULTIMASTER */
1715 if ( di->di_max_ttl == 0 ) {
1716 di->di_max_ttl = DDS_RF2589_DEFAULT_TTL;
1719 if ( di->di_min_ttl == 0 ) {
1720 di->di_max_ttl = DDS_RF2589_DEFAULT_TTL;
1723 di->di_suffix = be->be_suffix;
1724 di->di_nsuffix = be->be_nsuffix;
1726 /* force deletion of expired entries... */
1727 be->bd_info = (BackendInfo *)on->on_info;
1728 rc = dds_expire( thrctx, di );
1729 be->bd_info = (BackendInfo *)on;
1730 if ( rc != LDAP_SUCCESS ) {
1735 /* ... so that count, if required, is accurate */
1736 if ( di->di_max_dynamicObjects > 0 ) {
1737 rc = dds_count( thrctx, be );
1738 if ( rc != LDAP_SUCCESS ) {
1744 /* start expire task */
1745 ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
1746 di->di_expire_task = ldap_pvt_runqueue_insert( &slapd_rq,
1748 dds_expire_fn, di, "dds_expire_fn",
1749 be->be_suffix[ 0 ].bv_val );
1750 ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
1752 /* register dinamicSubtrees root DSE info support */
1753 rc = entry_info_register( dds_entry_info, (void *)di );
1756 ldap_pvt_thread_pool_context_reset( thrctx );
1765 slap_overinst *on = (slap_overinst *)be->bd_info;
1766 dds_info_t *di = on->on_bi.bi_private;
1768 /* stop expire task */
1769 if ( di && di->di_expire_task ) {
1770 ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
1771 if ( ldap_pvt_runqueue_isrunning( &slapd_rq, di->di_expire_task ) ) {
1772 ldap_pvt_runqueue_stoptask( &slapd_rq, di->di_expire_task );
1774 ldap_pvt_runqueue_remove( &slapd_rq, di->di_expire_task );
1775 ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
1778 (void)entry_info_unregister( dds_entry_info, (void *)di );
1787 slap_overinst *on = (slap_overinst *)be->bd_info;
1788 dds_info_t *di = on->on_bi.bi_private;
1791 ldap_pvt_thread_mutex_destroy( &di->di_mutex );
1804 BackendDB *bd = op->o_bd;
1806 rs->sr_err = slap_parse_refresh( op->ore_reqdata, &op->o_req_ndn, NULL,
1807 &rs->sr_text, op->o_tmpmemctx );
1808 if ( rs->sr_err != LDAP_SUCCESS ) {
1812 Log2( LDAP_DEBUG_STATS, LDAP_LEVEL_INFO,
1813 "%s REFRESH dn=\"%s\"\n",
1814 op->o_log_prefix, op->o_req_ndn.bv_val );
1815 op->o_req_dn = op->o_req_ndn;
1817 op->o_bd = select_backend( &op->o_req_ndn, 0, 0 );
1818 if ( !SLAP_DYNAMIC( op->o_bd ) ) {
1819 send_ldap_error( op, rs, LDAP_UNAVAILABLE_CRITICAL_EXTENSION,
1820 "backend does not support dynamic directory services" );
1824 rs->sr_err = backend_check_restrictions( op, rs,
1825 (struct berval *)&slap_EXOP_REFRESH );
1826 if ( rs->sr_err != LDAP_SUCCESS ) {
1830 if ( op->o_bd->be_extended == NULL ) {
1831 send_ldap_error( op, rs, LDAP_UNAVAILABLE_CRITICAL_EXTENSION,
1832 "backend does not support extended operations" );
1836 op->o_bd->be_extended( op, rs );
1839 if ( !BER_BVISNULL( &op->o_req_ndn ) ) {
1840 op->o_tmpfree( op->o_req_ndn.bv_val, op->o_tmpmemctx );
1841 BER_BVZERO( &op->o_req_ndn );
1842 BER_BVZERO( &op->o_req_dn );
1849 static slap_overinst dds;
1851 static int do_not_load_exop;
1852 static int do_not_replace_exop;
1853 static int do_not_load_schema;
1855 #if SLAPD_OVER_DDS == SLAPD_MOD_DYNAMIC
1857 #endif /* SLAPD_OVER_DDS == SLAPD_MOD_DYNAMIC */
1865 /* Make sure we don't exceed the bits reserved for userland */
1866 config_check_userland( DDS_LAST );
1868 if ( !do_not_load_schema ) {
1872 AttributeDescription **ad;
1874 { "entryExpireTimestamp", "( 1.3.6.1.4.1.4203.666.1.57 "
1875 "NAME ( 'entryExpireTimestamp' ) "
1876 "DESC 'RFC2589 extension: expire time of a dynamic object, "
1877 "computed as modifyTimestamp + entryTtl' "
1878 "EQUALITY generalizedTimeMatch "
1879 "ORDERING generalizedTimeOrderingMatch "
1880 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 "
1882 "NO-USER-MODIFICATION "
1883 "USAGE dSAOperation )",
1884 &ad_entryExpireTimestamp },
1888 for ( i = 0; s_at[ i ].name != NULL; i++ ) {
1889 LDAPAttributeType *at;
1891 at = ldap_str2attributetype( s_at[ i ].desc,
1892 &code, &err, LDAP_SCHEMA_ALLOW_ALL );
1894 fprintf( stderr, "dds_initialize: "
1895 "AttributeType load failed: %s %s\n",
1896 ldap_scherr2str( code ), err );
1900 code = at_add( at, 0, NULL, &err );
1902 if ( code != LDAP_SUCCESS ) {
1903 fprintf( stderr, "dds_initialize: "
1904 "AttributeType load failed: %s %s\n",
1905 scherr2str( code ), err );
1909 code = slap_str2ad( s_at[ i ].name, s_at[ i ].ad, &err );
1910 if ( code != LDAP_SUCCESS ) {
1911 fprintf( stderr, "dds_initialize: "
1912 "unable to find AttributeDescription "
1913 "\"%s\": %d (%s)\n",
1914 s_at[ i ].name, code, err );
1920 if ( !do_not_load_exop ) {
1921 rc = load_extop2( (struct berval *)&slap_EXOP_REFRESH,
1922 SLAP_EXOP_WRITES|SLAP_EXOP_HIDE, slap_exop_refresh,
1923 !do_not_replace_exop );
1924 if ( rc != LDAP_SUCCESS ) {
1925 Log1( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1926 "DDS unable to register refresh exop: %d.\n",
1932 dds.on_bi.bi_type = "dds";
1934 dds.on_bi.bi_db_init = dds_db_init;
1935 dds.on_bi.bi_db_open = dds_db_open;
1936 dds.on_bi.bi_db_close = dds_db_close;
1937 dds.on_bi.bi_db_destroy = dds_db_destroy;
1939 dds.on_bi.bi_op_add = dds_op_add;
1940 dds.on_bi.bi_op_delete = dds_op_delete;
1941 dds.on_bi.bi_op_modify = dds_op_modify;
1942 dds.on_bi.bi_op_modrdn = dds_op_rename;
1943 dds.on_bi.bi_extended = dds_op_extended;
1945 dds.on_bi.bi_cf_ocs = dds_ocs;
1947 rc = config_register_schema( dds_cfg, dds_ocs );
1952 return overlay_register( &dds );
1955 #if SLAPD_OVER_DDS == SLAPD_MOD_DYNAMIC
1957 init_module( int argc, char *argv[] )
1961 for ( i = 0; i < argc; i++ ) {
1962 char *arg = argv[ i ];
1965 if ( strncasecmp( arg, "no-", STRLENOF( "no-" ) ) == 0 ) {
1966 arg += STRLENOF( "no-" );
1970 if ( strcasecmp( arg, "exop" ) == 0 ) {
1971 do_not_load_exop = no;
1973 } else if ( strcasecmp( arg, "replace" ) == 0 ) {
1974 do_not_replace_exop = no;
1976 } else if ( strcasecmp( arg, "schema" ) == 0 ) {
1977 do_not_load_schema = no;
1980 Log( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1981 "DDS unknown module arg[#%d]=\"%s\".\n",
1987 return dds_initialize();
1989 #endif /* SLAPD_OVER_DDS == SLAPD_MOD_DYNAMIC */
1991 #endif /* defined(SLAPD_OVER_DDS) */