2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4 * Copyright 2005-2007 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 };
128 dds_expire_t *de = NULL, **dep;
129 SlapReply rs = { REP_RESULT };
132 char tsbuf[ LDAP_LUTIL_GENTIME_BUFSIZE ];
135 int ndeletes, ntotdeletes;
140 connection_fake_init( &conn, &opbuf, ctx );
143 op->o_tag = LDAP_REQ_SEARCH;
144 memset( &op->oq_search, 0, sizeof( op->oq_search ) );
146 op->o_bd = select_backend( &di->di_nsuffix[ 0 ], 0 );
148 op->o_req_dn = op->o_bd->be_suffix[ 0 ];
149 op->o_req_ndn = op->o_bd->be_nsuffix[ 0 ];
151 op->o_dn = op->o_bd->be_rootdn;
152 op->o_ndn = op->o_bd->be_rootndn;
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;
159 expire = slap_get_time() + di->di_tolerance;
161 ts.bv_len = sizeof( tsbuf );
162 slap_timestamp( &expire, &ts );
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
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 );
174 op->ors_filter = str2filter_x( op, op->ors_filterstr.bv_val );
175 if ( op->ors_filter == NULL ) {
176 rs.sr_err = LDAP_OTHER;
180 op->o_callback = ≻
181 sc.sc_response = dds_expire_cb;
184 (void)op->o_bd->bd_info->bi_op_search( op, &rs );
187 op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx );
188 filter_free_x( op, op->ors_filter );
191 switch ( rs.sr_err ) {
195 case LDAP_NO_SUCH_OBJECT:
196 /* (ITS#5267) database not created yet? */
197 rs.sr_err = LDAP_SUCCESS;
198 extra = " (ignored)";
202 Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
203 "DDS expired objects lookup failed err=%d%s\n",
208 op->o_tag = LDAP_REQ_DELETE;
209 op->o_callback = ≻
210 sc.sc_response = slap_null_cb;
211 sc.sc_private = NULL;
213 for ( ntotdeletes = 0, ndeletes = 1; dc.dc_ndnlist != NULL && ndeletes > 0; ) {
216 for ( dep = &dc.dc_ndnlist; *dep != NULL; ) {
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 ) {
224 Log1( LDAP_DEBUG_STATS, LDAP_LEVEL_INFO,
225 "DDS dn=\"%s\" expired.\n",
230 case LDAP_NOT_ALLOWED_ON_NONLEAF:
231 Log1( LDAP_DEBUG_ANY, LDAP_LEVEL_NOTICE,
232 "DDS dn=\"%s\" is non-leaf; "
240 Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_NOTICE,
241 "DDS dn=\"%s\" err=%d; "
243 de->de_ndn.bv_val, rs.sr_err );
250 op->o_tmpfree( de, op->o_tmpmemctx );
254 ntotdeletes += ndeletes;
257 rs.sr_err = LDAP_SUCCESS;
259 Log1( LDAP_DEBUG_STATS, LDAP_LEVEL_INFO,
260 "DDS expired=%d\n", ntotdeletes );
267 dds_expire_fn( void *ctx, void *arg )
269 struct re_s *rtask = arg;
270 dds_info_t *di = rtask->arg;
272 assert( di->di_expire_task == rtask );
274 (void)dds_expire( ctx, di );
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 );
280 ldap_pvt_runqueue_resched( &slapd_rq, rtask, 0 );
281 ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
286 /* frees the callback */
288 dds_freeit_cb( Operation *op, SlapReply *rs )
290 op->o_tmpfree( op->o_callback, op->o_tmpmemctx );
291 op->o_callback = NULL;
293 return SLAP_CB_CONTINUE;
296 /* updates counter - installed on add/delete only if required */
298 dds_counter_cb( Operation *op, SlapReply *rs )
300 assert( rs->sr_type == REP_RESULT );
302 if ( rs->sr_err == LDAP_SUCCESS ) {
303 dds_info_t *di = op->o_callback->sc_private;
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--;
313 assert( di->di_num_dynamicObjects < di->di_max_dynamicObjects );
314 di->di_num_dynamicObjects++;
320 ldap_pvt_thread_mutex_unlock( &di->di_mutex );
323 return dds_freeit_cb( op, rs );
327 dds_op_add( Operation *op, SlapReply *rs )
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;
333 if ( DDS_OFF( di ) ) {
334 return SLAP_CB_CONTINUE;
337 is_dynamicObject = is_entry_dynamicObject( op->ora_e );
339 /* FIXME: do not allow this right now, pending clarification */
340 if ( is_dynamicObject ) {
341 rs->sr_err = LDAP_SUCCESS;
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";
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";
352 if ( rs->sr_err != LDAP_SUCCESS ) {
353 op->o_bd->bd_info = (BackendInfo *)on->on_info;
354 send_ldap_result( op, rs );
359 /* we don't allow dynamicObjects to have static subordinates */
360 if ( !dn_match( &op->o_req_ndn, &op->o_bd->be_nsuffix[ 0 ] ) ) {
364 BackendInfo *bi = op->o_bd->bd_info;
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 ) )
378 rc = rs->sr_err = LDAP_NO_SUCH_OBJECT;
379 send_ldap_result( op, rs );
382 rc = rs->sr_err = LDAP_CONSTRAINT_VIOLATION;
383 send_ldap_error( op, rs, rc, "no static subordinate entries allowed for dynamicObject" );
387 be_entry_release_r( op, e );
388 if ( rc != LDAP_SUCCESS ) {
392 op->o_bd->bd_info = bi;
395 /* handle dynamic object operational attr(s) */
396 if ( is_dynamicObject ) {
398 char ttlbuf[] = "31557600";
399 char tsbuf[ LDAP_LUTIL_GENTIME_BUFSIZE ];
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 );
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" );
415 ttl = DDS_DEFAULT_TTL( di );
417 assert( ttl <= DDS_RF2589_MAX_TTL );
420 bv.bv_len = snprintf( ttlbuf, sizeof( ttlbuf ), "%ld", ttl );
422 /* FIXME: apparently, values in op->ora_e are malloc'ed
423 * on the thread's slab; works fine by chance,
424 * only because the attribute doesn't exist yet. */
425 assert( attr_find( op->ora_e->e_attrs, slap_schema.si_ad_entryTtl ) == NULL );
426 attr_merge_one( op->ora_e, slap_schema.si_ad_entryTtl, &bv, &bv );
428 expire = slap_get_time() + ttl;
430 bv.bv_len = sizeof( tsbuf );
431 slap_timestamp( &expire, &bv );
432 assert( attr_find( op->ora_e->e_attrs, ad_entryExpireTimestamp ) == NULL );
433 attr_merge_one( op->ora_e, ad_entryExpireTimestamp, &bv, &bv );
435 /* if required, install counter callback */
436 if ( di->di_max_dynamicObjects > 0) {
439 sc = op->o_tmpalloc( sizeof( slap_callback ), op->o_tmpmemctx );
440 sc->sc_cleanup = dds_freeit_cb;
441 sc->sc_response = dds_counter_cb;
443 sc->sc_next = op->o_callback;
449 return SLAP_CB_CONTINUE;
453 dds_op_delete( Operation *op, SlapReply *rs )
455 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
456 dds_info_t *di = on->on_bi.bi_private;
458 /* if required, install counter callback */
459 if ( !DDS_OFF( di ) && di->di_max_dynamicObjects > 0 ) {
461 BackendInfo *bi = op->o_bd->bd_info;
463 op->o_bd->bd_info = (BackendInfo *)on->on_info;
464 rs->sr_err = be_entry_get_rw( op, &op->o_req_ndn,
465 slap_schema.si_oc_dynamicObject, NULL, 0, &e );
467 /* FIXME: couldn't the entry be added before deletion? */
468 if ( rs->sr_err == LDAP_SUCCESS && e != NULL ) {
471 be_entry_release_r( op, e );
474 sc = op->o_tmpalloc( sizeof( slap_callback ), op->o_tmpmemctx );
475 sc->sc_cleanup = dds_freeit_cb;
476 sc->sc_response = dds_counter_cb;
478 sc->sc_next = op->o_callback;
482 op->o_bd->bd_info = bi;
485 return SLAP_CB_CONTINUE;
489 dds_op_modify( Operation *op, SlapReply *rs )
491 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
492 dds_info_t *di = (dds_info_t *)on->on_bi.bi_private;
495 BackendInfo *bi = op->o_bd->bd_info;
496 int was_dynamicObject = 0,
497 is_dynamicObject = 0;
498 struct berval bv_entryTtl = BER_BVNULL;
500 char textbuf[ SLAP_TEXT_BUFLEN ];
502 if ( DDS_OFF( di ) ) {
503 return SLAP_CB_CONTINUE;
506 /* bv_entryTtl stores the string representation of the entryTtl
507 * across modifies for consistency checks of the final value;
508 * the bv_val points to a static buffer; the bv_len is zero when
509 * the attribute is deleted.
510 * entryTtl stores the integer representation of the entryTtl;
511 * its value is -1 when the attribute is deleted; it is 0 only
512 * if no modifications of the entryTtl occurred, as an entryTtl
513 * of 0 is invalid. */
514 bv_entryTtl.bv_val = textbuf;
516 op->o_bd->bd_info = (BackendInfo *)on->on_info;
517 rs->sr_err = be_entry_get_rw( op, &op->o_req_ndn,
518 slap_schema.si_oc_dynamicObject, slap_schema.si_ad_entryTtl, 0, &e );
519 if ( rs->sr_err == LDAP_SUCCESS && e != NULL ) {
520 Attribute *a = attr_find( e->e_attrs, slap_schema.si_ad_entryTtl );
522 /* the value of the entryTtl is saved for later checks */
527 bv_entryTtl.bv_len = a->a_nvals[ 0 ].bv_len;
528 AC_MEMCPY( bv_entryTtl.bv_val, a->a_nvals[ 0 ].bv_val, bv_entryTtl.bv_len );
529 bv_entryTtl.bv_val[ bv_entryTtl.bv_len ] = '\0';
530 rc = lutil_atoul( &ttl, bv_entryTtl.bv_val );
532 entryTtl = (time_t)ttl;
535 be_entry_release_r( op, e );
537 was_dynamicObject = is_dynamicObject = 1;
539 op->o_bd->bd_info = bi;
541 rs->sr_err = LDAP_SUCCESS;
542 for ( mod = op->orm_modlist; mod; mod = mod->sml_next ) {
543 if ( mod->sml_desc == slap_schema.si_ad_objectClass ) {
547 switch ( mod->sml_op ) {
548 case LDAP_MOD_DELETE:
549 if ( mod->sml_values == NULL ) {
550 is_dynamicObject = 0;
554 for ( i = 0; !BER_BVISNULL( &mod->sml_values[ i ] ); i++ ) {
555 oc = oc_bvfind( &mod->sml_values[ i ] );
556 if ( oc == slap_schema.si_oc_dynamicObject ) {
557 is_dynamicObject = 0;
564 case LDAP_MOD_REPLACE:
565 if ( mod->sml_values == NULL ) {
566 is_dynamicObject = 0;
572 for ( i = 0; !BER_BVISNULL( &mod->sml_values[ i ] ); i++ ) {
573 oc = oc_bvfind( &mod->sml_values[ i ] );
574 if ( oc == slap_schema.si_oc_dynamicObject ) {
575 is_dynamicObject = 1;
582 } else if ( mod->sml_desc == slap_schema.si_ad_entryTtl ) {
586 switch ( mod->sml_op ) {
587 case LDAP_MOD_DELETE:
588 if ( mod->sml_values != NULL ) {
589 if ( BER_BVISEMPTY( &bv_entryTtl )
590 || !bvmatch( &bv_entryTtl, &mod->sml_values[ 0 ] ) )
592 rs->sr_err = backend_attribute( op, NULL, &op->o_req_ndn,
593 slap_schema.si_ad_entry, NULL, ACL_DISCLOSE );
594 if ( rs->sr_err == LDAP_INSUFFICIENT_ACCESS ) {
595 rs->sr_err = LDAP_NO_SUCH_OBJECT;
598 rs->sr_err = LDAP_NO_SUCH_ATTRIBUTE;
603 bv_entryTtl.bv_len = 0;
607 case LDAP_MOD_REPLACE:
608 bv_entryTtl.bv_len = 0;
612 case SLAP_MOD_SOFTADD: /* FIXME? */
614 assert( mod->sml_values != NULL );
615 assert( BER_BVISNULL( &mod->sml_values[ 1 ] ) );
617 if ( !BER_BVISEMPTY( &bv_entryTtl ) ) {
618 rs->sr_err = backend_attribute( op, NULL, &op->o_req_ndn,
619 slap_schema.si_ad_entry, NULL, ACL_DISCLOSE );
620 if ( rs->sr_err == LDAP_INSUFFICIENT_ACCESS ) {
621 rs->sr_err = LDAP_NO_SUCH_OBJECT;
624 rs->sr_text = "attribute 'entryTtl' cannot have multiple values";
625 rs->sr_err = LDAP_CONSTRAINT_VIOLATION;
630 rc = lutil_atoul( &ttl, mod->sml_values[ 0 ].bv_val );
632 if ( ttl > DDS_RF2589_MAX_TTL ) {
633 rs->sr_err = LDAP_PROTOCOL_ERROR;
634 rs->sr_text = "invalid time-to-live for dynamicObject";
638 if ( ttl <= 0 || ttl > di->di_max_ttl ) {
639 /* FIXME: I don't understand if this has to be an error,
640 * or an indication that the requested Ttl has been
641 * shortened to di->di_max_ttl >= 1 day */
642 rs->sr_err = LDAP_SIZELIMIT_EXCEEDED;
643 rs->sr_text = "time-to-live for dynamicObject exceeds administrative limit";
647 entryTtl = (time_t)ttl;
648 bv_entryTtl.bv_len = mod->sml_values[ 0 ].bv_len;
649 AC_MEMCPY( bv_entryTtl.bv_val, mod->sml_values[ 0 ].bv_val, bv_entryTtl.bv_len );
650 bv_entryTtl.bv_val[ bv_entryTtl.bv_len ] = '\0';
653 case LDAP_MOD_INCREMENT:
654 if ( BER_BVISEMPTY( &bv_entryTtl ) ) {
655 rs->sr_err = backend_attribute( op, NULL, &op->o_req_ndn,
656 slap_schema.si_ad_entry, NULL, ACL_DISCLOSE );
657 if ( rs->sr_err == LDAP_INSUFFICIENT_ACCESS ) {
658 rs->sr_err = LDAP_NO_SUCH_OBJECT;
661 rs->sr_err = LDAP_NO_SUCH_ATTRIBUTE;
662 rs->sr_text = "modify/increment: entryTtl: no such attribute";
668 if ( entryTtl > DDS_RF2589_MAX_TTL ) {
669 rs->sr_err = LDAP_PROTOCOL_ERROR;
670 rs->sr_text = "invalid time-to-live for dynamicObject";
672 } else if ( entryTtl <= 0 || entryTtl > di->di_max_ttl ) {
673 /* FIXME: I don't understand if this has to be an error,
674 * or an indication that the requested Ttl has been
675 * shortened to di->di_max_ttl >= 1 day */
676 rs->sr_err = LDAP_SIZELIMIT_EXCEEDED;
677 rs->sr_text = "time-to-live for dynamicObject exceeds administrative limit";
680 if ( rs->sr_err != LDAP_SUCCESS ) {
681 rc = backend_attribute( op, NULL, &op->o_req_ndn,
682 slap_schema.si_ad_entry, NULL, ACL_DISCLOSE );
683 if ( rc == LDAP_INSUFFICIENT_ACCESS ) {
685 rs->sr_err = LDAP_NO_SUCH_OBJECT;
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 the Relax control 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_relax( 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 if ( rc != LDAP_SUCCESS ) {
763 rc = backend_attribute( op, NULL, &op->o_req_ndn,
764 slap_schema.si_ad_entry, NULL, ACL_DISCLOSE );
765 if ( rc == LDAP_INSUFFICIENT_ACCESS ) {
767 rs->sr_err = LDAP_NO_SUCH_OBJECT;
773 if ( rs->sr_err == LDAP_SUCCESS && entryTtl != 0 ) {
774 Modifications *tmpmod = NULL, **modp;
776 for ( modp = &op->orm_modlist; *modp; modp = &(*modp)->sml_next )
779 tmpmod = ch_calloc( 1, sizeof( Modifications ) );
780 tmpmod->sml_flags = SLAP_MOD_INTERNAL;
781 tmpmod->sml_type = ad_entryExpireTimestamp->ad_cname;
782 tmpmod->sml_desc = ad_entryExpireTimestamp;
786 if ( entryTtl == -1 ) {
787 /* delete entryExpireTimestamp */
788 tmpmod->sml_op = LDAP_MOD_DELETE;
792 char tsbuf[ LDAP_LUTIL_GENTIME_BUFSIZE ];
795 /* keep entryExpireTimestamp consistent
797 expire = slap_get_time() + entryTtl;
799 bv.bv_len = sizeof( tsbuf );
800 slap_timestamp( &expire, &bv );
802 tmpmod->sml_op = LDAP_MOD_REPLACE;
803 value_add_one( &tmpmod->sml_values, &bv );
804 value_add_one( &tmpmod->sml_nvalues, &bv );
805 tmpmod->sml_numvals = 1;
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 /* return referral only if "disclose"
851 * is granted on the object */
852 if ( ! access_allowed( op, e,
853 slap_schema.si_ad_entry,
854 NULL, ACL_DISCLOSE, NULL ) )
856 rs->sr_err = LDAP_NO_SUCH_OBJECT;
857 send_ldap_result( op, rs );
860 send_ldap_error( op, rs, LDAP_CONSTRAINT_VIOLATION,
861 "static entry cannot have dynamicObject as newSuperior" );
864 be_entry_release_r( op, e );
866 op->o_bd->bd_info = bi;
867 if ( rs->sr_err != LDAP_SUCCESS ) {
872 return SLAP_CB_CONTINUE;
883 int rc = LDAP_SUCCESS;
886 BerElementBuffer berbuf;
887 BerElement *ber = (BerElement *)&berbuf;
888 struct berval reqdata = BER_BVNULL;
897 if ( in == NULL || in->bv_len == 0 ) {
898 *text = "empty request data field in refresh exop";
899 return LDAP_PROTOCOL_ERROR;
902 ber_dupbv_x( &reqdata, in, ctx );
904 /* ber_init2 uses reqdata directly, doesn't allocate new buffers */
905 ber_init2( ber, &reqdata, 0 );
907 tag = ber_scanf( ber, "{" /*}*/ );
909 if ( tag == LBER_ERROR ) {
910 Log0( LDAP_DEBUG_TRACE, LDAP_LEVEL_ERR,
911 "slap_parse_refresh: decoding error.\n" );
915 tag = ber_peek_tag( ber, &len );
916 if ( tag != LDAP_TAG_EXOP_REFRESH_REQ_DN ) {
917 Log0( LDAP_DEBUG_TRACE, LDAP_LEVEL_ERR,
918 "slap_parse_refresh: decoding error.\n" );
925 tag = ber_scanf( ber, "m", &dn );
926 if ( tag == LBER_ERROR ) {
927 Log0( LDAP_DEBUG_TRACE, LDAP_LEVEL_ERR,
928 "slap_parse_refresh: DN parse failed.\n" );
932 rc = dnNormalize( 0, NULL, NULL, &dn, ndn, ctx );
933 if ( rc != LDAP_SUCCESS ) {
934 *text = "invalid DN in refresh exop request data";
939 tag = ber_scanf( ber, "x" /* "m" */ );
940 if ( tag == LBER_DEFAULT ) {
945 tag = ber_peek_tag( ber, &len );
947 if ( tag != LDAP_TAG_EXOP_REFRESH_REQ_TTL ) {
948 Log0( LDAP_DEBUG_TRACE, LDAP_LEVEL_ERR,
949 "slap_parse_refresh: decoding error.\n" );
953 tag = ber_scanf( ber, "i", &tmp );
954 if ( tag == LBER_ERROR ) {
955 Log0( LDAP_DEBUG_TRACE, LDAP_LEVEL_ERR,
956 "slap_parse_refresh: TTL parse failed.\n" );
964 tag = ber_peek_tag( ber, &len );
966 if ( tag != LBER_DEFAULT || len != 0 ) {
968 Log1( LDAP_DEBUG_TRACE, LDAP_LEVEL_ERR,
969 "slap_parse_refresh: decoding error, len=%ld\n",
971 rc = LDAP_PROTOCOL_ERROR;
972 *text = "data decoding error";
975 if ( ndn && !BER_BVISNULL( ndn ) ) {
976 slap_sl_free( ndn->bv_val, ctx );
981 if ( !BER_BVISNULL( &reqdata ) ) {
982 ber_memfree_x( reqdata.bv_val, ctx );
989 dds_op_extended( Operation *op, SlapReply *rs )
991 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
992 dds_info_t *di = on->on_bi.bi_private;
994 if ( DDS_OFF( di ) ) {
995 return SLAP_CB_CONTINUE;
998 if ( bvmatch( &op->ore_reqoid, &slap_EXOP_REFRESH ) ) {
1001 BackendDB db = *op->o_bd;
1002 SlapReply rs2 = { REP_RESULT };
1003 Operation op2 = *op;
1004 slap_callback sc = { 0 };
1005 Modifications ttlmod = { { 0 } };
1006 struct berval ttlvalues[ 2 ];
1007 char ttlbuf[] = "31557600";
1009 rs->sr_err = slap_parse_refresh( op->ore_reqdata, NULL, &ttl,
1010 &rs->sr_text, NULL );
1011 assert( rs->sr_err == LDAP_SUCCESS );
1013 if ( ttl <= 0 || ttl > DDS_RF2589_MAX_TTL ) {
1014 rs->sr_err = LDAP_PROTOCOL_ERROR;
1015 rs->sr_text = "invalid time-to-live for dynamicObject";
1019 if ( ttl > di->di_max_ttl ) {
1020 /* FIXME: I don't understand if this has to be an error,
1021 * or an indication that the requested Ttl has been
1022 * shortened to di->di_max_ttl >= 1 day */
1023 rs->sr_err = LDAP_SIZELIMIT_EXCEEDED;
1024 rs->sr_text = "time-to-live for dynamicObject exceeds limit";
1028 if ( di->di_min_ttl && ttl < di->di_min_ttl ) {
1029 ttl = di->di_min_ttl;
1032 /* This does not apply to multi-master case */
1033 if ( !( !SLAP_SINGLE_SHADOW( op->o_bd ) || be_isupdate( op ) ) ) {
1034 /* we SHOULD return a referral in this case */
1035 BerVarray defref = op->o_bd->be_update_refs
1036 ? op->o_bd->be_update_refs : default_referral;
1038 if ( defref != NULL ) {
1039 rs->sr_ref = referral_rewrite( op->o_bd->be_update_refs,
1040 NULL, NULL, LDAP_SCOPE_DEFAULT );
1042 rs->sr_flags |= REP_REF_MUSTBEFREED;
1044 rs->sr_ref = defref;
1046 rs->sr_err = LDAP_REFERRAL;
1049 rs->sr_text = "shadow context; no update referral";
1050 rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
1056 assert( !BER_BVISNULL( &op->o_req_ndn ) );
1060 /* check if exists but not dynamicObject */
1061 op->o_bd->bd_info = (BackendInfo *)on->on_info;
1062 rs->sr_err = be_entry_get_rw( op, &op->o_req_ndn,
1063 slap_schema.si_oc_dynamicObject, NULL, 0, &e );
1064 if ( rs->sr_err != LDAP_SUCCESS ) {
1065 rs->sr_err = be_entry_get_rw( op, &op->o_req_ndn,
1066 NULL, NULL, 0, &e );
1067 if ( rs->sr_err == LDAP_SUCCESS && e != NULL ) {
1068 /* return referral only if "disclose"
1069 * is granted on the object */
1070 if ( ! access_allowed( op, e,
1071 slap_schema.si_ad_entry,
1072 NULL, ACL_DISCLOSE, NULL ) )
1074 rs->sr_err = LDAP_NO_SUCH_OBJECT;
1077 rs->sr_err = LDAP_OBJECT_CLASS_VIOLATION;
1078 rs->sr_text = "refresh operation only applies to dynamic objects";
1080 be_entry_release_r( op, e );
1083 rs->sr_err = LDAP_NO_SUCH_OBJECT;
1087 } else if ( e != NULL ) {
1088 be_entry_release_r( op, e );
1091 /* we require manage privileges on the entryTtl,
1092 * and fake a Relax control */
1093 op2.o_tag = LDAP_REQ_MODIFY;
1095 db.bd_info = (BackendInfo *)on->on_info;
1096 op2.o_callback = ≻
1097 sc.sc_response = slap_null_cb;
1098 op2.o_relax = SLAP_CONTROL_CRITICAL;
1099 op2.orm_modlist = &ttlmod;
1101 ttlmod.sml_op = LDAP_MOD_REPLACE;
1102 ttlmod.sml_flags = SLAP_MOD_MANAGING;
1103 ttlmod.sml_desc = slap_schema.si_ad_entryTtl;
1104 ttlmod.sml_values = ttlvalues;
1105 ttlmod.sml_numvals = 1;
1106 ttlvalues[ 0 ].bv_val = ttlbuf;
1107 ttlvalues[ 0 ].bv_len = snprintf( ttlbuf, sizeof( ttlbuf ), "%ld", ttl );
1108 BER_BVZERO( &ttlvalues[ 1 ] );
1110 /* the entryExpireTimestamp is added by modify */
1111 rs->sr_err = op2.o_bd->be_modify( &op2, &rs2 );
1113 if ( ttlmod.sml_next != NULL ) {
1114 slap_mods_free( ttlmod.sml_next, 1 );
1117 if ( rs->sr_err == LDAP_SUCCESS ) {
1119 BerElementBuffer berbuf;
1120 BerElement *ber = (BerElement *)&berbuf;
1122 if ( rs->sr_err == LDAP_SUCCESS ) {
1123 ber_init_w_nullc( ber, LBER_USE_DER );
1125 rc = ber_printf( ber, "{tiN}", LDAP_TAG_EXOP_REFRESH_RES_TTL, (int)ttl );
1128 rs->sr_err = LDAP_OTHER;
1129 rs->sr_text = "internal error";
1132 (void)ber_flatten( ber, &rs->sr_rspdata );
1133 rs->sr_rspoid = ch_strdup( slap_EXOP_REFRESH.bv_val );
1135 Log3( LDAP_DEBUG_TRACE, LDAP_LEVEL_INFO,
1136 "%s REFRESH dn=\"%s\" TTL=%ld\n",
1137 op->o_log_prefix, op->o_req_ndn.bv_val, ttl );
1140 ber_free_buf( ber );
1147 return SLAP_CB_CONTINUE;
1162 static ConfigDriver dds_cfgen;
1164 static ConfigLDAPadd dds_ldadd;
1165 static ConfigCfAdd dds_cfadd;
1168 static ConfigTable dds_cfg[] = {
1169 { "dds-state", "on|off",
1170 2, 2, 0, ARG_MAGIC|ARG_ON_OFF|DDS_STATE, dds_cfgen,
1171 "( OLcfgOvAt:9.1 NAME 'olcDDSstate' "
1172 "DESC 'RFC2589 Dynamic directory services state' "
1173 "SYNTAX OMsBoolean "
1174 "SINGLE-VALUE )", NULL, NULL },
1175 { "dds-max-ttl", "ttl",
1176 2, 2, 0, ARG_MAGIC|DDS_MAXTTL, dds_cfgen,
1177 "( OLcfgOvAt:9.2 NAME 'olcDDSmaxTtl' "
1178 "DESC 'RFC2589 Dynamic directory services max TTL' "
1179 "SYNTAX OMsDirectoryString "
1180 "SINGLE-VALUE )", NULL, NULL },
1181 { "dds-min-ttl", "ttl",
1182 2, 2, 0, ARG_MAGIC|DDS_MINTTL, dds_cfgen,
1183 "( OLcfgOvAt:9.3 NAME 'olcDDSminTtl' "
1184 "DESC 'RFC2589 Dynamic directory services min TTL' "
1185 "SYNTAX OMsDirectoryString "
1186 "SINGLE-VALUE )", NULL, NULL },
1187 { "dds-default-ttl", "ttl",
1188 2, 2, 0, ARG_MAGIC|DDS_DEFAULTTTL, dds_cfgen,
1189 "( OLcfgOvAt:9.4 NAME 'olcDDSdefaultTtl' "
1190 "DESC 'RFC2589 Dynamic directory services default TTL' "
1191 "SYNTAX OMsDirectoryString "
1192 "SINGLE-VALUE )", NULL, NULL },
1193 { "dds-interval", "interval",
1194 2, 2, 0, ARG_MAGIC|DDS_INTERVAL, dds_cfgen,
1195 "( OLcfgOvAt:9.5 NAME 'olcDDSinterval' "
1196 "DESC 'RFC2589 Dynamic directory services expiration "
1197 "task run interval' "
1198 "SYNTAX OMsDirectoryString "
1199 "SINGLE-VALUE )", NULL, NULL },
1200 { "dds-tolerance", "ttl",
1201 2, 2, 0, ARG_MAGIC|DDS_TOLERANCE, dds_cfgen,
1202 "( OLcfgOvAt:9.6 NAME 'olcDDStolerance' "
1203 "DESC 'RFC2589 Dynamic directory services additional "
1204 "TTL in expiration scheduling' "
1205 "SYNTAX OMsDirectoryString "
1206 "SINGLE-VALUE )", NULL, NULL },
1207 { "dds-max-dynamicObjects", "num",
1208 2, 2, 0, ARG_MAGIC|ARG_INT|DDS_MAXDYNAMICOBJS, dds_cfgen,
1209 "( OLcfgOvAt:9.7 NAME 'olcDDSmaxDynamicObjects' "
1210 "DESC 'RFC2589 Dynamic directory services max number of dynamic objects' "
1211 "SYNTAX OMsInteger "
1212 "SINGLE-VALUE )", NULL, NULL },
1213 { NULL, NULL, 0, 0, 0, ARG_IGNORED }
1216 static ConfigOCs dds_ocs[] = {
1217 { "( OLcfgOvOc:9.1 "
1218 "NAME 'olcDDSConfig' "
1219 "DESC 'RFC2589 Dynamic directory services configuration' "
1220 "SUP olcOverlayConfig "
1225 "$ olcDDSdefaultTtl "
1227 "$ olcDDStolerance "
1228 "$ olcDDSmaxDynamicObjects "
1230 ")", Cft_Overlay, dds_cfg, NULL, NULL /* dds_cfadd */ },
1236 dds_ldadd( CfEntryInfo *p, Entry *e, ConfigArgs *ca )
1238 return LDAP_SUCCESS;
1242 dds_cfadd( Operation *op, SlapReply *rs, Entry *p, ConfigArgs *ca )
1249 dds_cfgen( ConfigArgs *c )
1251 slap_overinst *on = (slap_overinst *)c->bi;
1252 dds_info_t *di = on->on_bi.bi_private;
1257 if ( c->op == SLAP_CONFIG_EMIT ) {
1258 char buf[ SLAP_TEXT_BUFLEN ];
1263 c->value_int = !DDS_OFF( di );
1267 lutil_unparse_time( buf, sizeof( buf ), di->di_max_ttl );
1268 ber_str2bv( buf, 0, 0, &bv );
1269 value_add_one( &c->rvalue_vals, &bv );
1273 if ( di->di_min_ttl ) {
1274 lutil_unparse_time( buf, sizeof( buf ), di->di_min_ttl );
1275 ber_str2bv( buf, 0, 0, &bv );
1276 value_add_one( &c->rvalue_vals, &bv );
1283 case DDS_DEFAULTTTL:
1284 if ( di->di_default_ttl ) {
1285 lutil_unparse_time( buf, sizeof( buf ), di->di_default_ttl );
1286 ber_str2bv( buf, 0, 0, &bv );
1287 value_add_one( &c->rvalue_vals, &bv );
1295 if ( di->di_interval ) {
1296 lutil_unparse_time( buf, sizeof( buf ), di->di_interval );
1297 ber_str2bv( buf, 0, 0, &bv );
1298 value_add_one( &c->rvalue_vals, &bv );
1306 if ( di->di_tolerance ) {
1307 lutil_unparse_time( buf, sizeof( buf ), di->di_tolerance );
1308 ber_str2bv( buf, 0, 0, &bv );
1309 value_add_one( &c->rvalue_vals, &bv );
1316 case DDS_MAXDYNAMICOBJS:
1317 if ( di->di_max_dynamicObjects > 0 ) {
1318 c->value_int = di->di_max_dynamicObjects;
1332 } else if ( c->op == LDAP_MOD_DELETE ) {
1335 di->di_flags &= ~DDS_FOFF;
1339 di->di_min_ttl = DDS_RF2589_DEFAULT_TTL;
1346 case DDS_DEFAULTTTL:
1347 di->di_default_ttl = 0;
1351 di->di_interval = 0;
1355 di->di_tolerance = 0;
1358 case DDS_MAXDYNAMICOBJS:
1359 di->di_max_dynamicObjects = 0;
1370 switch ( c->type ) {
1372 if ( c->value_int ) {
1373 di->di_flags &= ~DDS_FOFF;
1376 di->di_flags |= DDS_FOFF;
1381 if ( lutil_parse_time( c->argv[ 1 ], &t ) != 0 ) {
1382 snprintf( c->cr_msg, sizeof( c->cr_msg),
1383 "DDS unable to parse dds-max-ttl \"%s\"",
1385 Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1386 "%s: %s.\n", c->log, c->cr_msg );
1390 if ( t < DDS_RF2589_DEFAULT_TTL || t > DDS_RF2589_MAX_TTL ) {
1391 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1392 "DDS invalid dds-max-ttl=%ld; must be between %d and %d",
1393 t, DDS_RF2589_DEFAULT_TTL, DDS_RF2589_MAX_TTL );
1394 Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1395 "%s: %s.\n", c->log, c->cr_msg );
1399 di->di_max_ttl = (time_t)t;
1403 if ( lutil_parse_time( c->argv[ 1 ], &t ) != 0 ) {
1404 snprintf( c->cr_msg, sizeof( c->cr_msg),
1405 "DDS unable to parse dds-min-ttl \"%s\"",
1407 Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1408 "%s: %s.\n", c->log, c->cr_msg );
1412 if ( t < 0 || t > DDS_RF2589_MAX_TTL ) {
1413 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1414 "DDS invalid dds-min-ttl=%ld",
1416 Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1417 "%s: %s.\n", c->log, c->cr_msg );
1422 di->di_min_ttl = DDS_RF2589_DEFAULT_TTL;
1425 di->di_min_ttl = (time_t)t;
1429 case DDS_DEFAULTTTL:
1430 if ( lutil_parse_time( c->argv[ 1 ], &t ) != 0 ) {
1431 snprintf( c->cr_msg, sizeof( c->cr_msg),
1432 "DDS unable to parse dds-default-ttl \"%s\"",
1434 Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1435 "%s: %s.\n", c->log, c->cr_msg );
1439 if ( t < 0 || t > DDS_RF2589_MAX_TTL ) {
1440 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1441 "DDS invalid dds-default-ttl=%ld",
1443 Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1444 "%s: %s.\n", c->log, c->cr_msg );
1449 di->di_default_ttl = DDS_RF2589_DEFAULT_TTL;
1452 di->di_default_ttl = (time_t)t;
1457 if ( lutil_parse_time( c->argv[ 1 ], &t ) != 0 ) {
1458 snprintf( c->cr_msg, sizeof( c->cr_msg),
1459 "DDS unable to parse dds-interval \"%s\"",
1461 Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1462 "%s: %s.\n", c->log, c->cr_msg );
1467 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1468 "DDS invalid dds-interval=%ld",
1470 Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1471 "%s: %s.\n", c->log, c->cr_msg );
1476 Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_NOTICE,
1477 "%s: dds-interval=%lu may be too small.\n",
1481 di->di_interval = (time_t)t;
1482 if ( di->di_expire_task ) {
1483 ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
1484 if ( ldap_pvt_runqueue_isrunning( &slapd_rq, di->di_expire_task ) ) {
1485 ldap_pvt_runqueue_stoptask( &slapd_rq, di->di_expire_task );
1487 di->di_expire_task->interval.tv_sec = DDS_INTERVAL( di );
1488 ldap_pvt_runqueue_resched( &slapd_rq, di->di_expire_task, 0 );
1489 ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
1494 if ( lutil_parse_time( c->argv[ 1 ], &t ) != 0 ) {
1495 snprintf( c->cr_msg, sizeof( c->cr_msg),
1496 "DDS unable to parse dds-tolerance \"%s\"",
1498 Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1499 "%s: %s.\n", c->log, c->cr_msg );
1503 if ( t < 0 || t > DDS_RF2589_MAX_TTL ) {
1504 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1505 "DDS invalid dds-tolerance=%ld",
1507 Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1508 "%s: %s.\n", c->log, c->cr_msg );
1512 di->di_tolerance = (time_t)t;
1515 case DDS_MAXDYNAMICOBJS:
1516 if ( c->value_int < 0 ) {
1517 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1518 "DDS invalid dds-max-dynamicObjects=%d", c->value_int );
1519 Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1520 "%s: %s.\n", c->log, c->cr_msg );
1523 di->di_max_dynamicObjects = c->value_int;
1539 slap_overinst *on = (slap_overinst *)be->bd_info;
1541 BackendInfo *bi = on->on_info->oi_orig;
1543 if ( SLAP_ISGLOBALOVERLAY( be ) ) {
1544 Log0( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1545 "DDS cannot be used as global overlay.\n" );
1549 /* check support for required functions */
1550 /* FIXME: some could be provided by other overlays in between */
1551 if ( bi->bi_op_add == NULL /* object creation */
1552 || bi->bi_op_delete == NULL /* object deletion */
1553 || bi->bi_op_modify == NULL /* object refresh */
1554 || bi->bi_op_search == NULL /* object expiration */
1555 || bi->bi_entry_get_rw == NULL ) /* object type/existence checking */
1557 Log1( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1558 "DDS backend \"%s\" does not provide "
1559 "required functionality.\n",
1564 di = (dds_info_t *)ch_calloc( 1, sizeof( dds_info_t ) );
1565 on->on_bi.bi_private = di;
1567 di->di_max_ttl = DDS_RF2589_DEFAULT_TTL;
1568 di->di_max_ttl = DDS_RF2589_DEFAULT_TTL;
1570 ldap_pvt_thread_mutex_init( &di->di_mutex );
1572 SLAP_DBFLAGS( be ) |= SLAP_DBFLAG_DYNAMIC;
1577 /* adds dynamicSubtrees to root DSE */
1579 dds_entry_info( void *arg, Entry *e )
1581 dds_info_t *di = (dds_info_t *)arg;
1583 attr_merge( e, slap_schema.si_ad_dynamicSubtrees,
1584 di->di_suffix, di->di_nsuffix );
1589 /* callback that counts the returned entries, since the search
1590 * does not get to the point in slap_send_search_entries where
1591 * the actual count occurs */
1593 dds_count_cb( Operation *op, SlapReply *rs )
1595 int *nump = (int *)op->o_callback->sc_private;
1597 switch ( rs->sr_type ) {
1613 /* count dynamic objects existing in the database at startup */
1615 dds_count( void *ctx, BackendDB *be )
1617 slap_overinst *on = (slap_overinst *)be->bd_info;
1618 dds_info_t *di = (dds_info_t *)on->on_bi.bi_private;
1620 Connection conn = { 0 };
1621 OperationBuffer opbuf;
1623 slap_callback sc = { 0 };
1624 SlapReply rs = { REP_RESULT };
1629 connection_fake_init( &conn, &opbuf, ctx );
1632 op->o_tag = LDAP_REQ_SEARCH;
1633 memset( &op->oq_search, 0, sizeof( op->oq_search ) );
1637 op->o_req_dn = op->o_bd->be_suffix[ 0 ];
1638 op->o_req_ndn = op->o_bd->be_nsuffix[ 0 ];
1640 op->o_dn = op->o_bd->be_rootdn;
1641 op->o_ndn = op->o_bd->be_rootndn;
1643 op->ors_scope = LDAP_SCOPE_SUBTREE;
1644 op->ors_tlimit = SLAP_NO_LIMIT;
1645 op->ors_slimit = SLAP_NO_LIMIT;
1646 op->ors_attrs = slap_anlist_no_attrs;
1648 op->ors_filterstr.bv_len = STRLENOF( "(objectClass=" ")" )
1649 + slap_schema.si_oc_dynamicObject->soc_cname.bv_len;
1650 op->ors_filterstr.bv_val = op->o_tmpalloc( op->ors_filterstr.bv_len + 1, op->o_tmpmemctx );
1651 snprintf( op->ors_filterstr.bv_val, op->ors_filterstr.bv_len + 1,
1653 slap_schema.si_oc_dynamicObject->soc_cname.bv_val );
1655 op->ors_filter = str2filter_x( op, op->ors_filterstr.bv_val );
1656 if ( op->ors_filter == NULL ) {
1657 rs.sr_err = LDAP_OTHER;
1661 op->o_callback = ≻
1662 sc.sc_response = dds_count_cb;
1663 sc.sc_private = &di->di_num_dynamicObjects;
1664 di->di_num_dynamicObjects = 0;
1666 op->o_bd->bd_info = (BackendInfo *)on->on_info;
1667 (void)op->o_bd->bd_info->bi_op_search( op, &rs );
1668 op->o_bd->bd_info = (BackendInfo *)on;
1671 op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx );
1672 filter_free_x( op, op->ors_filter );
1675 switch ( rs.sr_err ) {
1677 Log1( LDAP_DEBUG_STATS, LDAP_LEVEL_INFO,
1678 "DDS non-expired=%d\n",
1679 di->di_num_dynamicObjects );
1682 case LDAP_NO_SUCH_OBJECT:
1683 /* (ITS#5267) database not created yet? */
1684 rs.sr_err = LDAP_SUCCESS;
1685 extra = " (ignored)";
1689 Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1690 "DDS non-expired objects lookup failed err=%d%s\n",
1703 slap_overinst *on = (slap_overinst *)be->bd_info;
1704 dds_info_t *di = on->on_bi.bi_private;
1706 void *thrctx = ldap_pvt_thread_pool_context();
1708 if ( DDS_OFF( di ) ) {
1712 if ( SLAP_SINGLE_SHADOW( be ) ) {
1713 Log1( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1714 "DDS incompatible with shadow database \"%s\".\n",
1715 be->be_suffix[ 0 ].bv_val );
1719 if ( di->di_max_ttl == 0 ) {
1720 di->di_max_ttl = DDS_RF2589_DEFAULT_TTL;
1723 if ( di->di_min_ttl == 0 ) {
1724 di->di_max_ttl = DDS_RF2589_DEFAULT_TTL;
1727 di->di_suffix = be->be_suffix;
1728 di->di_nsuffix = be->be_nsuffix;
1730 /* ... so that count, if required, is accurate */
1731 if ( di->di_max_dynamicObjects > 0 ) {
1732 /* force deletion of expired entries... */
1733 be->bd_info = (BackendInfo *)on->on_info;
1734 rc = dds_expire( thrctx, di );
1735 be->bd_info = (BackendInfo *)on;
1736 if ( rc != LDAP_SUCCESS ) {
1741 rc = dds_count( thrctx, be );
1742 if ( rc != LDAP_SUCCESS ) {
1748 /* start expire task */
1749 ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
1750 di->di_expire_task = ldap_pvt_runqueue_insert( &slapd_rq,
1752 dds_expire_fn, di, "dds_expire_fn",
1753 be->be_suffix[ 0 ].bv_val );
1754 ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
1756 /* register dinamicSubtrees root DSE info support */
1757 rc = entry_info_register( dds_entry_info, (void *)di );
1769 slap_overinst *on = (slap_overinst *)be->bd_info;
1770 dds_info_t *di = on->on_bi.bi_private;
1772 /* stop expire task */
1773 if ( di && di->di_expire_task ) {
1774 ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
1775 if ( ldap_pvt_runqueue_isrunning( &slapd_rq, di->di_expire_task ) ) {
1776 ldap_pvt_runqueue_stoptask( &slapd_rq, di->di_expire_task );
1778 ldap_pvt_runqueue_remove( &slapd_rq, di->di_expire_task );
1779 ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
1782 (void)entry_info_unregister( dds_entry_info, (void *)di );
1792 slap_overinst *on = (slap_overinst *)be->bd_info;
1793 dds_info_t *di = on->on_bi.bi_private;
1796 ldap_pvt_thread_mutex_destroy( &di->di_mutex );
1809 BackendDB *bd = op->o_bd;
1811 rs->sr_err = slap_parse_refresh( op->ore_reqdata, &op->o_req_ndn, NULL,
1812 &rs->sr_text, op->o_tmpmemctx );
1813 if ( rs->sr_err != LDAP_SUCCESS ) {
1817 Log2( LDAP_DEBUG_STATS, LDAP_LEVEL_INFO,
1818 "%s REFRESH dn=\"%s\"\n",
1819 op->o_log_prefix, op->o_req_ndn.bv_val );
1820 op->o_req_dn = op->o_req_ndn;
1822 op->o_bd = select_backend( &op->o_req_ndn, 0 );
1823 if ( !SLAP_DYNAMIC( op->o_bd ) ) {
1824 send_ldap_error( op, rs, LDAP_UNAVAILABLE_CRITICAL_EXTENSION,
1825 "backend does not support dynamic directory services" );
1829 rs->sr_err = backend_check_restrictions( op, rs,
1830 (struct berval *)&slap_EXOP_REFRESH );
1831 if ( rs->sr_err != LDAP_SUCCESS ) {
1835 if ( op->o_bd->be_extended == NULL ) {
1836 send_ldap_error( op, rs, LDAP_UNAVAILABLE_CRITICAL_EXTENSION,
1837 "backend does not support extended operations" );
1841 op->o_bd->be_extended( op, rs );
1844 if ( !BER_BVISNULL( &op->o_req_ndn ) ) {
1845 op->o_tmpfree( op->o_req_ndn.bv_val, op->o_tmpmemctx );
1846 BER_BVZERO( &op->o_req_ndn );
1847 BER_BVZERO( &op->o_req_dn );
1854 static slap_overinst dds;
1856 static int do_not_load_exop;
1857 static int do_not_replace_exop;
1858 static int do_not_load_schema;
1860 #if SLAPD_OVER_DDS == SLAPD_MOD_DYNAMIC
1862 #endif /* SLAPD_OVER_DDS == SLAPD_MOD_DYNAMIC */
1869 /* Make sure we don't exceed the bits reserved for userland */
1870 config_check_userland( DDS_LAST );
1872 if ( !do_not_load_schema ) {
1876 AttributeDescription **ad;
1878 { "( 1.3.6.1.4.1.4203.666.1.57 "
1879 "NAME ( 'entryExpireTimestamp' ) "
1880 "DESC 'RFC2589 OpenLDAP extension: expire time of a dynamic object, "
1881 "computed as now + entryTtl' "
1882 "EQUALITY generalizedTimeMatch "
1883 "ORDERING generalizedTimeOrderingMatch "
1884 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 "
1886 "NO-USER-MODIFICATION "
1887 "USAGE dSAOperation )",
1889 &ad_entryExpireTimestamp },
1893 for ( i = 0; s_at[ i ].desc != NULL; i++ ) {
1894 code = register_at( s_at[ i ].desc, s_at[ i ].ad, 0 );
1896 Debug( LDAP_DEBUG_ANY,
1897 "dds_initialize: register_at failed\n", 0, 0, 0 );
1900 (*s_at[ i ].ad)->ad_type->sat_flags |= SLAP_AT_HIDE;
1904 if ( !do_not_load_exop ) {
1905 rc = load_extop2( (struct berval *)&slap_EXOP_REFRESH,
1906 SLAP_EXOP_WRITES|SLAP_EXOP_HIDE, slap_exop_refresh,
1907 !do_not_replace_exop );
1908 if ( rc != LDAP_SUCCESS ) {
1909 Log1( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1910 "DDS unable to register refresh exop: %d.\n",
1916 dds.on_bi.bi_type = "dds";
1918 dds.on_bi.bi_db_init = dds_db_init;
1919 dds.on_bi.bi_db_open = dds_db_open;
1920 dds.on_bi.bi_db_close = dds_db_close;
1921 dds.on_bi.bi_db_destroy = dds_db_destroy;
1923 dds.on_bi.bi_op_add = dds_op_add;
1924 dds.on_bi.bi_op_delete = dds_op_delete;
1925 dds.on_bi.bi_op_modify = dds_op_modify;
1926 dds.on_bi.bi_op_modrdn = dds_op_rename;
1927 dds.on_bi.bi_extended = dds_op_extended;
1929 dds.on_bi.bi_cf_ocs = dds_ocs;
1931 rc = config_register_schema( dds_cfg, dds_ocs );
1936 return overlay_register( &dds );
1939 #if SLAPD_OVER_DDS == SLAPD_MOD_DYNAMIC
1941 init_module( int argc, char *argv[] )
1945 for ( i = 0; i < argc; i++ ) {
1946 char *arg = argv[ i ];
1949 if ( strncasecmp( arg, "no-", STRLENOF( "no-" ) ) == 0 ) {
1950 arg += STRLENOF( "no-" );
1954 if ( strcasecmp( arg, "exop" ) == 0 ) {
1955 do_not_load_exop = no;
1957 } else if ( strcasecmp( arg, "replace" ) == 0 ) {
1958 do_not_replace_exop = no;
1960 } else if ( strcasecmp( arg, "schema" ) == 0 ) {
1961 do_not_load_schema = no;
1964 Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1965 "DDS unknown module arg[#%d]=\"%s\".\n",
1971 return dds_initialize();
1973 #endif /* SLAPD_OVER_DDS == SLAPD_MOD_DYNAMIC */
1975 #endif /* defined(SLAPD_OVER_DDS) */