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;
137 connection_fake_init( &conn, &opbuf, 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 );
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_null_cb;
197 sc.sc_private = NULL;
199 for ( ntotdeletes = 0, ndeletes = 1; dc.dc_ndnlist != NULL && ndeletes > 0; ) {
202 for ( dep = &dc.dc_ndnlist; *dep != NULL; ) {
205 op->o_req_dn = de->de_ndn;
206 op->o_req_ndn = de->de_ndn;
207 (void)op->o_bd->bd_info->bi_op_delete( op, &rs );
208 switch ( rs.sr_err ) {
210 Log1( LDAP_DEBUG_STATS, LDAP_LEVEL_INFO,
211 "DDS dn=\"%s\" expired.\n",
216 case LDAP_NOT_ALLOWED_ON_NONLEAF:
217 Log1( LDAP_DEBUG_ANY, LDAP_LEVEL_NOTICE,
218 "DDS dn=\"%s\" is non-leaf; "
226 Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_NOTICE,
227 "DDS dn=\"%s\" err=%d; "
229 de->de_ndn.bv_val, rs.sr_err );
236 op->o_tmpfree( de, op->o_tmpmemctx );
240 ntotdeletes += ndeletes;
243 rs.sr_err = LDAP_SUCCESS;
245 Log1( LDAP_DEBUG_STATS, LDAP_LEVEL_INFO,
246 "DDS expired=%d\n", ntotdeletes );
253 dds_expire_fn( void *ctx, void *arg )
255 struct re_s *rtask = arg;
256 dds_info_t *di = rtask->arg;
258 assert( di->di_expire_task == rtask );
260 (void)dds_expire( ctx, di );
262 ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
263 if ( ldap_pvt_runqueue_isrunning( &slapd_rq, rtask )) {
264 ldap_pvt_runqueue_stoptask( &slapd_rq, rtask );
266 ldap_pvt_runqueue_resched( &slapd_rq, rtask, 0 );
267 ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
272 /* frees the callback */
274 dds_freeit_cb( Operation *op, SlapReply *rs )
276 op->o_tmpfree( op->o_callback, op->o_tmpmemctx );
277 op->o_callback = NULL;
279 return SLAP_CB_CONTINUE;
282 /* updates counter - installed on add/delete only if required */
284 dds_counter_cb( Operation *op, SlapReply *rs )
286 assert( rs->sr_type == REP_RESULT );
288 if ( rs->sr_err == LDAP_SUCCESS ) {
289 dds_info_t *di = op->o_callback->sc_private;
291 ldap_pvt_thread_mutex_lock( &di->di_mutex );
292 switch ( op->o_tag ) {
293 case LDAP_REQ_DELETE:
294 assert( di->di_num_dynamicObjects > 0 );
295 di->di_num_dynamicObjects--;
299 assert( di->di_num_dynamicObjects < di->di_max_dynamicObjects );
300 di->di_num_dynamicObjects++;
306 ldap_pvt_thread_mutex_unlock( &di->di_mutex );
309 return dds_freeit_cb( op, rs );
313 dds_op_add( Operation *op, SlapReply *rs )
315 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
316 dds_info_t *di = on->on_bi.bi_private;
317 int is_dynamicObject;
319 if ( DDS_OFF( di ) ) {
320 return SLAP_CB_CONTINUE;
323 is_dynamicObject = is_entry_dynamicObject( op->ora_e );
325 /* FIXME: do not allow this right now, pending clarification */
326 if ( is_dynamicObject ) {
327 rs->sr_err = LDAP_SUCCESS;
329 if ( is_entry_referral( op->ora_e ) ) {
330 rs->sr_err = LDAP_OBJECT_CLASS_VIOLATION;
331 rs->sr_text = "a referral cannot be a dynamicObject";
333 } else if ( is_entry_alias( op->ora_e ) ) {
334 rs->sr_err = LDAP_OBJECT_CLASS_VIOLATION;
335 rs->sr_text = "an alias cannot be a dynamicObject";
338 if ( rs->sr_err != LDAP_SUCCESS ) {
339 op->o_bd->bd_info = (BackendInfo *)on->on_info;
340 send_ldap_result( op, rs );
345 /* we don't allow dynamicObjects to have static subordinates */
346 if ( !dn_match( &op->o_req_ndn, &op->o_bd->be_nsuffix[ 0 ] ) ) {
350 BackendInfo *bi = op->o_bd->bd_info;
352 dnParent( &op->o_req_ndn, &p_ndn );
353 op->o_bd->bd_info = (BackendInfo *)on->on_info;
354 rc = be_entry_get_rw( op, &p_ndn,
355 slap_schema.si_oc_dynamicObject, NULL, 0, &e );
356 if ( rc == LDAP_SUCCESS && e != NULL ) {
357 if ( !is_dynamicObject ) {
358 /* return referral only if "disclose"
359 * is granted on the object */
360 if ( ! access_allowed( op, e,
361 slap_schema.si_ad_entry,
362 NULL, ACL_DISCLOSE, NULL ) )
364 rc = rs->sr_err = LDAP_NO_SUCH_OBJECT;
365 send_ldap_result( op, rs );
368 rc = rs->sr_err = LDAP_CONSTRAINT_VIOLATION;
369 send_ldap_error( op, rs, rc, "no static subordinate entries allowed for dynamicObject" );
373 be_entry_release_r( op, e );
374 if ( rc != LDAP_SUCCESS ) {
378 op->o_bd->bd_info = bi;
381 /* handle dynamic object operational attr(s) */
382 if ( is_dynamicObject ) {
384 char ttlbuf[] = "31557600";
385 char tsbuf[ LDAP_LUTIL_GENTIME_BUFSIZE ];
388 if ( !be_isroot_dn( op->o_bd, &op->o_req_ndn ) ) {
389 ldap_pvt_thread_mutex_lock( &di->di_mutex );
390 rs->sr_err = ( di->di_max_dynamicObjects &&
391 di->di_num_dynamicObjects >= di->di_max_dynamicObjects );
392 ldap_pvt_thread_mutex_unlock( &di->di_mutex );
394 op->o_bd->bd_info = (BackendInfo *)on->on_info;
395 send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM,
396 "too many dynamicObjects in context" );
401 ttl = DDS_DEFAULT_TTL( di );
403 assert( ttl <= DDS_RF2589_MAX_TTL );
406 bv.bv_len = snprintf( ttlbuf, sizeof( ttlbuf ), "%ld", ttl );
408 /* FIXME: apparently, values in op->ora_e are malloc'ed
409 * on the thread's slab; works fine by chance,
410 * only because the attribute doesn't exist yet. */
411 assert( attr_find( op->ora_e->e_attrs, slap_schema.si_ad_entryTtl ) == NULL );
412 attr_merge_one( op->ora_e, slap_schema.si_ad_entryTtl, &bv, &bv );
414 expire = slap_get_time() + ttl;
416 bv.bv_len = sizeof( tsbuf );
417 slap_timestamp( &expire, &bv );
418 assert( attr_find( op->ora_e->e_attrs, ad_entryExpireTimestamp ) == NULL );
419 attr_merge_one( op->ora_e, ad_entryExpireTimestamp, &bv, &bv );
421 /* if required, install counter callback */
422 if ( di->di_max_dynamicObjects > 0) {
425 sc = op->o_tmpalloc( sizeof( slap_callback ), op->o_tmpmemctx );
426 sc->sc_cleanup = dds_freeit_cb;
427 sc->sc_response = dds_counter_cb;
429 sc->sc_next = op->o_callback;
435 return SLAP_CB_CONTINUE;
439 dds_op_delete( Operation *op, SlapReply *rs )
441 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
442 dds_info_t *di = on->on_bi.bi_private;
444 /* if required, install counter callback */
445 if ( !DDS_OFF( di ) && di->di_max_dynamicObjects > 0 ) {
447 BackendInfo *bi = op->o_bd->bd_info;
449 op->o_bd->bd_info = (BackendInfo *)on->on_info;
450 rs->sr_err = be_entry_get_rw( op, &op->o_req_ndn,
451 slap_schema.si_oc_dynamicObject, NULL, 0, &e );
453 /* FIXME: couldn't the entry be added before deletion? */
454 if ( rs->sr_err == LDAP_SUCCESS && e != NULL ) {
457 be_entry_release_r( op, e );
460 sc = op->o_tmpalloc( sizeof( slap_callback ), op->o_tmpmemctx );
461 sc->sc_cleanup = dds_freeit_cb;
462 sc->sc_response = dds_counter_cb;
464 sc->sc_next = op->o_callback;
468 op->o_bd->bd_info = bi;
471 return SLAP_CB_CONTINUE;
475 dds_op_modify( Operation *op, SlapReply *rs )
477 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
478 dds_info_t *di = (dds_info_t *)on->on_bi.bi_private;
481 BackendInfo *bi = op->o_bd->bd_info;
482 int was_dynamicObject = 0,
483 is_dynamicObject = 0;
484 struct berval bv_entryTtl = BER_BVNULL;
486 char textbuf[ SLAP_TEXT_BUFLEN ];
488 if ( DDS_OFF( di ) ) {
489 return SLAP_CB_CONTINUE;
492 /* bv_entryTtl stores the string representation of the entryTtl
493 * across modifies for consistency checks of the final value;
494 * the bv_val points to a static buffer; the bv_len is zero when
495 * the attribute is deleted.
496 * entryTtl stores the integer representation of the entryTtl;
497 * its value is -1 when the attribute is deleted; it is 0 only
498 * if no modifications of the entryTtl occurred, as an entryTtl
499 * of 0 is invalid. */
500 bv_entryTtl.bv_val = textbuf;
502 op->o_bd->bd_info = (BackendInfo *)on->on_info;
503 rs->sr_err = be_entry_get_rw( op, &op->o_req_ndn,
504 slap_schema.si_oc_dynamicObject, slap_schema.si_ad_entryTtl, 0, &e );
505 if ( rs->sr_err == LDAP_SUCCESS && e != NULL ) {
506 Attribute *a = attr_find( e->e_attrs, slap_schema.si_ad_entryTtl );
508 /* the value of the entryTtl is saved for later checks */
513 bv_entryTtl.bv_len = a->a_nvals[ 0 ].bv_len;
514 AC_MEMCPY( bv_entryTtl.bv_val, a->a_nvals[ 0 ].bv_val, bv_entryTtl.bv_len );
515 bv_entryTtl.bv_val[ bv_entryTtl.bv_len ] = '\0';
516 rc = lutil_atoul( &ttl, bv_entryTtl.bv_val );
518 entryTtl = (time_t)ttl;
521 be_entry_release_r( op, e );
523 was_dynamicObject = is_dynamicObject = 1;
525 op->o_bd->bd_info = bi;
527 rs->sr_err = LDAP_SUCCESS;
528 for ( mod = op->orm_modlist; mod; mod = mod->sml_next ) {
529 if ( mod->sml_desc == slap_schema.si_ad_objectClass ) {
533 switch ( mod->sml_op ) {
534 case LDAP_MOD_DELETE:
535 if ( mod->sml_values == NULL ) {
536 is_dynamicObject = 0;
540 for ( i = 0; !BER_BVISNULL( &mod->sml_values[ i ] ); i++ ) {
541 oc = oc_bvfind( &mod->sml_values[ i ] );
542 if ( oc == slap_schema.si_oc_dynamicObject ) {
543 is_dynamicObject = 0;
550 case LDAP_MOD_REPLACE:
551 if ( mod->sml_values == NULL ) {
552 is_dynamicObject = 0;
558 for ( i = 0; !BER_BVISNULL( &mod->sml_values[ i ] ); i++ ) {
559 oc = oc_bvfind( &mod->sml_values[ i ] );
560 if ( oc == slap_schema.si_oc_dynamicObject ) {
561 is_dynamicObject = 1;
568 } else if ( mod->sml_desc == slap_schema.si_ad_entryTtl ) {
572 switch ( mod->sml_op ) {
573 case LDAP_MOD_DELETE:
574 if ( mod->sml_values != NULL ) {
575 if ( BER_BVISEMPTY( &bv_entryTtl )
576 || !bvmatch( &bv_entryTtl, &mod->sml_values[ 0 ] ) )
578 rs->sr_err = backend_attribute( op, NULL, &op->o_req_ndn,
579 slap_schema.si_ad_entry, NULL, ACL_DISCLOSE );
580 if ( rs->sr_err == LDAP_INSUFFICIENT_ACCESS ) {
581 rs->sr_err = LDAP_NO_SUCH_OBJECT;
584 rs->sr_err = LDAP_NO_SUCH_ATTRIBUTE;
589 bv_entryTtl.bv_len = 0;
593 case LDAP_MOD_REPLACE:
594 bv_entryTtl.bv_len = 0;
598 case SLAP_MOD_SOFTADD: /* FIXME? */
600 assert( mod->sml_values != NULL );
601 assert( BER_BVISNULL( &mod->sml_values[ 1 ] ) );
603 if ( !BER_BVISEMPTY( &bv_entryTtl ) ) {
604 rs->sr_err = backend_attribute( op, NULL, &op->o_req_ndn,
605 slap_schema.si_ad_entry, NULL, ACL_DISCLOSE );
606 if ( rs->sr_err == LDAP_INSUFFICIENT_ACCESS ) {
607 rs->sr_err = LDAP_NO_SUCH_OBJECT;
610 rs->sr_text = "attribute 'entryTtl' cannot have multiple values";
611 rs->sr_err = LDAP_CONSTRAINT_VIOLATION;
616 rc = lutil_atoul( &ttl, mod->sml_values[ 0 ].bv_val );
618 if ( ttl > DDS_RF2589_MAX_TTL ) {
619 rs->sr_err = LDAP_PROTOCOL_ERROR;
620 rs->sr_text = "invalid time-to-live for dynamicObject";
624 if ( ttl <= 0 || ttl > di->di_max_ttl ) {
625 /* FIXME: I don't understand if this has to be an error,
626 * or an indication that the requested Ttl has been
627 * shortened to di->di_max_ttl >= 1 day */
628 rs->sr_err = LDAP_SIZELIMIT_EXCEEDED;
629 rs->sr_text = "time-to-live for dynamicObject exceeds administrative limit";
633 entryTtl = (time_t)ttl;
634 bv_entryTtl.bv_len = mod->sml_values[ 0 ].bv_len;
635 AC_MEMCPY( bv_entryTtl.bv_val, mod->sml_values[ 0 ].bv_val, bv_entryTtl.bv_len );
636 bv_entryTtl.bv_val[ bv_entryTtl.bv_len ] = '\0';
639 case LDAP_MOD_INCREMENT:
640 if ( BER_BVISEMPTY( &bv_entryTtl ) ) {
641 rs->sr_err = backend_attribute( op, NULL, &op->o_req_ndn,
642 slap_schema.si_ad_entry, NULL, ACL_DISCLOSE );
643 if ( rs->sr_err == LDAP_INSUFFICIENT_ACCESS ) {
644 rs->sr_err = LDAP_NO_SUCH_OBJECT;
647 rs->sr_err = LDAP_NO_SUCH_ATTRIBUTE;
648 rs->sr_text = "modify/increment: entryTtl: no such attribute";
654 if ( entryTtl > DDS_RF2589_MAX_TTL ) {
655 rs->sr_err = LDAP_PROTOCOL_ERROR;
656 rs->sr_text = "invalid time-to-live for dynamicObject";
658 } else if ( entryTtl <= 0 || entryTtl > di->di_max_ttl ) {
659 /* FIXME: I don't understand if this has to be an error,
660 * or an indication that the requested Ttl has been
661 * shortened to di->di_max_ttl >= 1 day */
662 rs->sr_err = LDAP_SIZELIMIT_EXCEEDED;
663 rs->sr_text = "time-to-live for dynamicObject exceeds administrative limit";
666 if ( rs->sr_err != LDAP_SUCCESS ) {
667 rc = backend_attribute( op, NULL, &op->o_req_ndn,
668 slap_schema.si_ad_entry, NULL, ACL_DISCLOSE );
669 if ( rc == LDAP_INSUFFICIENT_ACCESS ) {
671 rs->sr_err = LDAP_NO_SUCH_OBJECT;
677 bv_entryTtl.bv_len = snprintf( textbuf, sizeof( textbuf ), "%ld", entryTtl );
685 } else if ( mod->sml_desc == ad_entryExpireTimestamp ) {
686 /* should have been trapped earlier */
687 assert( mod->sml_flags & SLAP_MOD_INTERNAL );
692 if ( rs->sr_err == LDAP_SUCCESS ) {
695 /* FIXME: this could be allowed when the Relax control is used...
701 * entryTtl must be provided; add
702 * entryExpireTimestamp accordingly
705 * entryTtl must be removed; remove
706 * entryTimestamp accordingly
708 * ... but we need to make sure that there are no subordinate
711 rc = is_dynamicObject - was_dynamicObject;
713 #if 0 /* fix subordinate issues first */
714 if ( get_relax( op ) ) {
717 /* need to delete entryTtl to have a consistent entry */
718 if ( entryTtl != -1 ) {
719 rs->sr_text = "objectClass modification from dynamicObject to static entry requires entryTtl deletion";
720 rs->sr_err = LDAP_OBJECT_CLASS_VIOLATION;
725 /* need to add entryTtl to have a consistent entry */
726 if ( entryTtl <= 0 ) {
727 rs->sr_text = "objectClass modification from static entry to dynamicObject requires entryTtl addition";
728 rs->sr_err = LDAP_OBJECT_CLASS_VIOLATION;
738 rs->sr_text = "objectClass modification cannot turn dynamicObject into static entry";
742 rs->sr_text = "objectClass modification cannot turn static entry into dynamicObject";
745 rs->sr_err = LDAP_OBJECT_CLASS_VIOLATION;
748 if ( rc != LDAP_SUCCESS ) {
749 rc = backend_attribute( op, NULL, &op->o_req_ndn,
750 slap_schema.si_ad_entry, NULL, ACL_DISCLOSE );
751 if ( rc == LDAP_INSUFFICIENT_ACCESS ) {
753 rs->sr_err = LDAP_NO_SUCH_OBJECT;
759 if ( rs->sr_err == LDAP_SUCCESS && entryTtl != 0 ) {
760 Modifications *tmpmod = NULL, **modp;
762 for ( modp = &op->orm_modlist; *modp; modp = &(*modp)->sml_next )
765 tmpmod = ch_calloc( 1, sizeof( Modifications ) );
766 tmpmod->sml_flags = SLAP_MOD_INTERNAL;
767 tmpmod->sml_type = ad_entryExpireTimestamp->ad_cname;
768 tmpmod->sml_desc = ad_entryExpireTimestamp;
772 if ( entryTtl == -1 ) {
773 /* delete entryExpireTimestamp */
774 tmpmod->sml_op = LDAP_MOD_DELETE;
778 char tsbuf[ LDAP_LUTIL_GENTIME_BUFSIZE ];
781 /* keep entryExpireTimestamp consistent
783 expire = slap_get_time() + entryTtl;
785 bv.bv_len = sizeof( tsbuf );
786 slap_timestamp( &expire, &bv );
788 tmpmod->sml_op = LDAP_MOD_REPLACE;
789 value_add_one( &tmpmod->sml_values, &bv );
790 value_add_one( &tmpmod->sml_nvalues, &bv );
791 tmpmod->sml_numvals = 1;
796 op->o_bd->bd_info = (BackendInfo *)on->on_info;
797 send_ldap_result( op, rs );
801 return SLAP_CB_CONTINUE;
805 dds_op_rename( Operation *op, SlapReply *rs )
807 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
808 dds_info_t *di = on->on_bi.bi_private;
810 if ( DDS_OFF( di ) ) {
811 return SLAP_CB_CONTINUE;
814 /* we don't allow dynamicObjects to have static subordinates */
815 if ( op->orr_nnewSup != NULL ) {
817 BackendInfo *bi = op->o_bd->bd_info;
818 int is_dynamicObject = 0,
821 rs->sr_err = LDAP_SUCCESS;
823 op->o_bd->bd_info = (BackendInfo *)on->on_info;
824 rc = be_entry_get_rw( op, &op->o_req_ndn,
825 slap_schema.si_oc_dynamicObject, NULL, 0, &e );
826 if ( rc == LDAP_SUCCESS && e != NULL ) {
827 be_entry_release_r( op, e );
829 is_dynamicObject = 1;
832 rc = be_entry_get_rw( op, op->orr_nnewSup,
833 slap_schema.si_oc_dynamicObject, NULL, 0, &e );
834 if ( rc == LDAP_SUCCESS && e != NULL ) {
835 if ( !is_dynamicObject ) {
836 /* return referral only if "disclose"
837 * is granted on the object */
838 if ( ! access_allowed( op, e,
839 slap_schema.si_ad_entry,
840 NULL, ACL_DISCLOSE, NULL ) )
842 rs->sr_err = LDAP_NO_SUCH_OBJECT;
843 send_ldap_result( op, rs );
846 send_ldap_error( op, rs, LDAP_CONSTRAINT_VIOLATION,
847 "static entry cannot have dynamicObject as newSuperior" );
850 be_entry_release_r( op, e );
852 op->o_bd->bd_info = bi;
853 if ( rs->sr_err != LDAP_SUCCESS ) {
858 return SLAP_CB_CONTINUE;
869 int rc = LDAP_SUCCESS;
872 BerElementBuffer berbuf;
873 BerElement *ber = (BerElement *)&berbuf;
874 struct berval reqdata = BER_BVNULL;
883 if ( in == NULL || in->bv_len == 0 ) {
884 *text = "empty request data field in refresh exop";
885 return LDAP_PROTOCOL_ERROR;
888 ber_dupbv_x( &reqdata, in, ctx );
890 /* ber_init2 uses reqdata directly, doesn't allocate new buffers */
891 ber_init2( ber, &reqdata, 0 );
893 tag = ber_scanf( ber, "{" /*}*/ );
895 if ( tag == LBER_ERROR ) {
896 Log0( LDAP_DEBUG_TRACE, LDAP_LEVEL_ERR,
897 "slap_parse_refresh: decoding error.\n" );
901 tag = ber_peek_tag( ber, &len );
902 if ( tag != LDAP_TAG_EXOP_REFRESH_REQ_DN ) {
903 Log0( LDAP_DEBUG_TRACE, LDAP_LEVEL_ERR,
904 "slap_parse_refresh: decoding error.\n" );
911 tag = ber_scanf( ber, "m", &dn );
912 if ( tag == LBER_ERROR ) {
913 Log0( LDAP_DEBUG_TRACE, LDAP_LEVEL_ERR,
914 "slap_parse_refresh: DN parse failed.\n" );
918 rc = dnNormalize( 0, NULL, NULL, &dn, ndn, ctx );
919 if ( rc != LDAP_SUCCESS ) {
920 *text = "invalid DN in refresh exop request data";
925 tag = ber_scanf( ber, "x" /* "m" */ );
926 if ( tag == LBER_DEFAULT ) {
931 tag = ber_peek_tag( ber, &len );
933 if ( tag != LDAP_TAG_EXOP_REFRESH_REQ_TTL ) {
934 Log0( LDAP_DEBUG_TRACE, LDAP_LEVEL_ERR,
935 "slap_parse_refresh: decoding error.\n" );
939 tag = ber_scanf( ber, "i", &tmp );
940 if ( tag == LBER_ERROR ) {
941 Log0( LDAP_DEBUG_TRACE, LDAP_LEVEL_ERR,
942 "slap_parse_refresh: TTL parse failed.\n" );
950 tag = ber_peek_tag( ber, &len );
952 if ( tag != LBER_DEFAULT || len != 0 ) {
954 Log1( LDAP_DEBUG_TRACE, LDAP_LEVEL_ERR,
955 "slap_parse_refresh: decoding error, len=%ld\n",
957 rc = LDAP_PROTOCOL_ERROR;
958 *text = "data decoding error";
961 if ( ndn && !BER_BVISNULL( ndn ) ) {
962 slap_sl_free( ndn->bv_val, ctx );
967 if ( !BER_BVISNULL( &reqdata ) ) {
968 ber_memfree_x( reqdata.bv_val, ctx );
975 dds_op_extended( Operation *op, SlapReply *rs )
977 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
978 dds_info_t *di = on->on_bi.bi_private;
980 if ( DDS_OFF( di ) ) {
981 return SLAP_CB_CONTINUE;
984 if ( bvmatch( &op->ore_reqoid, &slap_EXOP_REFRESH ) ) {
987 BackendDB db = *op->o_bd;
988 SlapReply rs2 = { REP_RESULT };
990 slap_callback sc = { 0 };
991 Modifications ttlmod = { { 0 } };
992 struct berval ttlvalues[ 2 ];
993 char ttlbuf[] = "31557600";
995 rs->sr_err = slap_parse_refresh( op->ore_reqdata, NULL, &ttl,
996 &rs->sr_text, NULL );
997 assert( rs->sr_err == LDAP_SUCCESS );
999 if ( ttl <= 0 || ttl > DDS_RF2589_MAX_TTL ) {
1000 rs->sr_err = LDAP_PROTOCOL_ERROR;
1001 rs->sr_text = "invalid time-to-live for dynamicObject";
1005 if ( ttl > di->di_max_ttl ) {
1006 /* FIXME: I don't understand if this has to be an error,
1007 * or an indication that the requested Ttl has been
1008 * shortened to di->di_max_ttl >= 1 day */
1009 rs->sr_err = LDAP_SIZELIMIT_EXCEEDED;
1010 rs->sr_text = "time-to-live for dynamicObject exceeds limit";
1014 if ( di->di_min_ttl && ttl < di->di_min_ttl ) {
1015 ttl = di->di_min_ttl;
1018 /* This does not apply to multi-master case */
1019 if ( !( !SLAP_SINGLE_SHADOW( op->o_bd ) || be_isupdate( op ) ) ) {
1020 /* we SHOULD return a referral in this case */
1021 BerVarray defref = op->o_bd->be_update_refs
1022 ? op->o_bd->be_update_refs : default_referral;
1024 if ( defref != NULL ) {
1025 rs->sr_ref = referral_rewrite( op->o_bd->be_update_refs,
1026 NULL, NULL, LDAP_SCOPE_DEFAULT );
1028 rs->sr_flags |= REP_REF_MUSTBEFREED;
1030 rs->sr_ref = defref;
1032 rs->sr_err = LDAP_REFERRAL;
1035 rs->sr_text = "shadow context; no update referral";
1036 rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
1042 assert( !BER_BVISNULL( &op->o_req_ndn ) );
1046 /* check if exists but not dynamicObject */
1047 op->o_bd->bd_info = (BackendInfo *)on->on_info;
1048 rs->sr_err = be_entry_get_rw( op, &op->o_req_ndn,
1049 slap_schema.si_oc_dynamicObject, NULL, 0, &e );
1050 if ( rs->sr_err != LDAP_SUCCESS ) {
1051 rs->sr_err = be_entry_get_rw( op, &op->o_req_ndn,
1052 NULL, NULL, 0, &e );
1053 if ( rs->sr_err == LDAP_SUCCESS && e != NULL ) {
1054 /* return referral only if "disclose"
1055 * is granted on the object */
1056 if ( ! access_allowed( op, e,
1057 slap_schema.si_ad_entry,
1058 NULL, ACL_DISCLOSE, NULL ) )
1060 rs->sr_err = LDAP_NO_SUCH_OBJECT;
1063 rs->sr_err = LDAP_OBJECT_CLASS_VIOLATION;
1064 rs->sr_text = "refresh operation only applies to dynamic objects";
1066 be_entry_release_r( op, e );
1069 rs->sr_err = LDAP_NO_SUCH_OBJECT;
1073 } else if ( e != NULL ) {
1074 be_entry_release_r( op, e );
1077 /* we require manage privileges on the entryTtl,
1078 * and fake a Relax control */
1079 op2.o_tag = LDAP_REQ_MODIFY;
1081 db.bd_info = (BackendInfo *)on->on_info;
1082 op2.o_callback = ≻
1083 sc.sc_response = slap_null_cb;
1084 op2.o_relax = SLAP_CONTROL_CRITICAL;
1085 op2.orm_modlist = &ttlmod;
1087 ttlmod.sml_op = LDAP_MOD_REPLACE;
1088 ttlmod.sml_flags = SLAP_MOD_MANAGING;
1089 ttlmod.sml_desc = slap_schema.si_ad_entryTtl;
1090 ttlmod.sml_values = ttlvalues;
1091 ttlmod.sml_numvals = 1;
1092 ttlvalues[ 0 ].bv_val = ttlbuf;
1093 ttlvalues[ 0 ].bv_len = snprintf( ttlbuf, sizeof( ttlbuf ), "%ld", ttl );
1094 BER_BVZERO( &ttlvalues[ 1 ] );
1096 /* the entryExpireTimestamp is added by modify */
1097 rs->sr_err = op2.o_bd->be_modify( &op2, &rs2 );
1099 if ( ttlmod.sml_next != NULL ) {
1100 slap_mods_free( ttlmod.sml_next, 1 );
1103 if ( rs->sr_err == LDAP_SUCCESS ) {
1105 BerElementBuffer berbuf;
1106 BerElement *ber = (BerElement *)&berbuf;
1108 if ( rs->sr_err == LDAP_SUCCESS ) {
1109 ber_init_w_nullc( ber, LBER_USE_DER );
1111 rc = ber_printf( ber, "{tiN}", LDAP_TAG_EXOP_REFRESH_RES_TTL, (int)ttl );
1114 rs->sr_err = LDAP_OTHER;
1115 rs->sr_text = "internal error";
1118 (void)ber_flatten( ber, &rs->sr_rspdata );
1119 rs->sr_rspoid = ch_strdup( slap_EXOP_REFRESH.bv_val );
1121 Log3( LDAP_DEBUG_TRACE, LDAP_LEVEL_INFO,
1122 "%s REFRESH dn=\"%s\" TTL=%ld\n",
1123 op->o_log_prefix, op->o_req_ndn.bv_val, ttl );
1126 ber_free_buf( ber );
1133 return SLAP_CB_CONTINUE;
1148 static ConfigDriver dds_cfgen;
1150 static ConfigLDAPadd dds_ldadd;
1151 static ConfigCfAdd dds_cfadd;
1154 static ConfigTable dds_cfg[] = {
1155 { "dds-state", "on|off",
1156 2, 2, 0, ARG_MAGIC|ARG_ON_OFF|DDS_STATE, dds_cfgen,
1157 "( OLcfgOvAt:9.1 NAME 'olcDDSstate' "
1158 "DESC 'RFC2589 Dynamic directory services state' "
1159 "SYNTAX OMsBoolean "
1160 "SINGLE-VALUE )", NULL, NULL },
1161 { "dds-max-ttl", "ttl",
1162 2, 2, 0, ARG_MAGIC|DDS_MAXTTL, dds_cfgen,
1163 "( OLcfgOvAt:9.2 NAME 'olcDDSmaxTtl' "
1164 "DESC 'RFC2589 Dynamic directory services max TTL' "
1165 "SYNTAX OMsDirectoryString "
1166 "SINGLE-VALUE )", NULL, NULL },
1167 { "dds-min-ttl", "ttl",
1168 2, 2, 0, ARG_MAGIC|DDS_MINTTL, dds_cfgen,
1169 "( OLcfgOvAt:9.3 NAME 'olcDDSminTtl' "
1170 "DESC 'RFC2589 Dynamic directory services min TTL' "
1171 "SYNTAX OMsDirectoryString "
1172 "SINGLE-VALUE )", NULL, NULL },
1173 { "dds-default-ttl", "ttl",
1174 2, 2, 0, ARG_MAGIC|DDS_DEFAULTTTL, dds_cfgen,
1175 "( OLcfgOvAt:9.4 NAME 'olcDDSdefaultTtl' "
1176 "DESC 'RFC2589 Dynamic directory services default TTL' "
1177 "SYNTAX OMsDirectoryString "
1178 "SINGLE-VALUE )", NULL, NULL },
1179 { "dds-interval", "interval",
1180 2, 2, 0, ARG_MAGIC|DDS_INTERVAL, dds_cfgen,
1181 "( OLcfgOvAt:9.5 NAME 'olcDDSinterval' "
1182 "DESC 'RFC2589 Dynamic directory services expiration "
1183 "task run interval' "
1184 "SYNTAX OMsDirectoryString "
1185 "SINGLE-VALUE )", NULL, NULL },
1186 { "dds-tolerance", "ttl",
1187 2, 2, 0, ARG_MAGIC|DDS_TOLERANCE, dds_cfgen,
1188 "( OLcfgOvAt:9.6 NAME 'olcDDStolerance' "
1189 "DESC 'RFC2589 Dynamic directory services additional "
1190 "TTL in expiration scheduling' "
1191 "SYNTAX OMsDirectoryString "
1192 "SINGLE-VALUE )", NULL, NULL },
1193 { "dds-max-dynamicObjects", "num",
1194 2, 2, 0, ARG_MAGIC|ARG_INT|DDS_MAXDYNAMICOBJS, dds_cfgen,
1195 "( OLcfgOvAt:9.7 NAME 'olcDDSmaxDynamicObjects' "
1196 "DESC 'RFC2589 Dynamic directory services max number of dynamic objects' "
1197 "SYNTAX OMsInteger "
1198 "SINGLE-VALUE )", NULL, NULL },
1199 { NULL, NULL, 0, 0, 0, ARG_IGNORED }
1202 static ConfigOCs dds_ocs[] = {
1203 { "( OLcfgOvOc:9.1 "
1204 "NAME 'olcDDSConfig' "
1205 "DESC 'RFC2589 Dynamic directory services configuration' "
1206 "SUP olcOverlayConfig "
1211 "$ olcDDSdefaultTtl "
1213 "$ olcDDStolerance "
1214 "$ olcDDSmaxDynamicObjects "
1216 ")", Cft_Overlay, dds_cfg, NULL, NULL /* dds_cfadd */ },
1222 dds_ldadd( CfEntryInfo *p, Entry *e, ConfigArgs *ca )
1224 return LDAP_SUCCESS;
1228 dds_cfadd( Operation *op, SlapReply *rs, Entry *p, ConfigArgs *ca )
1235 dds_cfgen( ConfigArgs *c )
1237 slap_overinst *on = (slap_overinst *)c->bi;
1238 dds_info_t *di = on->on_bi.bi_private;
1243 if ( c->op == SLAP_CONFIG_EMIT ) {
1244 char buf[ SLAP_TEXT_BUFLEN ];
1249 c->value_int = !DDS_OFF( di );
1253 lutil_unparse_time( buf, sizeof( buf ), di->di_max_ttl );
1254 ber_str2bv( buf, 0, 0, &bv );
1255 value_add_one( &c->rvalue_vals, &bv );
1259 if ( di->di_min_ttl ) {
1260 lutil_unparse_time( buf, sizeof( buf ), di->di_min_ttl );
1261 ber_str2bv( buf, 0, 0, &bv );
1262 value_add_one( &c->rvalue_vals, &bv );
1269 case DDS_DEFAULTTTL:
1270 if ( di->di_default_ttl ) {
1271 lutil_unparse_time( buf, sizeof( buf ), di->di_default_ttl );
1272 ber_str2bv( buf, 0, 0, &bv );
1273 value_add_one( &c->rvalue_vals, &bv );
1281 if ( di->di_interval ) {
1282 lutil_unparse_time( buf, sizeof( buf ), di->di_interval );
1283 ber_str2bv( buf, 0, 0, &bv );
1284 value_add_one( &c->rvalue_vals, &bv );
1292 if ( di->di_tolerance ) {
1293 lutil_unparse_time( buf, sizeof( buf ), di->di_tolerance );
1294 ber_str2bv( buf, 0, 0, &bv );
1295 value_add_one( &c->rvalue_vals, &bv );
1302 case DDS_MAXDYNAMICOBJS:
1303 if ( di->di_max_dynamicObjects > 0 ) {
1304 c->value_int = di->di_max_dynamicObjects;
1318 } else if ( c->op == LDAP_MOD_DELETE ) {
1321 di->di_flags &= ~DDS_FOFF;
1325 di->di_min_ttl = DDS_RF2589_DEFAULT_TTL;
1332 case DDS_DEFAULTTTL:
1333 di->di_default_ttl = 0;
1337 di->di_interval = 0;
1341 di->di_tolerance = 0;
1344 case DDS_MAXDYNAMICOBJS:
1345 di->di_max_dynamicObjects = 0;
1356 switch ( c->type ) {
1358 if ( c->value_int ) {
1359 di->di_flags &= ~DDS_FOFF;
1362 di->di_flags |= DDS_FOFF;
1367 if ( lutil_parse_time( c->argv[ 1 ], &t ) != 0 ) {
1368 snprintf( c->cr_msg, sizeof( c->cr_msg),
1369 "DDS unable to parse dds-max-ttl \"%s\"",
1371 Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1372 "%s: %s.\n", c->log, c->cr_msg );
1376 if ( t < DDS_RF2589_DEFAULT_TTL || t > DDS_RF2589_MAX_TTL ) {
1377 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1378 "DDS invalid dds-max-ttl=%ld; must be between %d and %d",
1379 t, DDS_RF2589_DEFAULT_TTL, DDS_RF2589_MAX_TTL );
1380 Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1381 "%s: %s.\n", c->log, c->cr_msg );
1385 di->di_max_ttl = (time_t)t;
1389 if ( lutil_parse_time( c->argv[ 1 ], &t ) != 0 ) {
1390 snprintf( c->cr_msg, sizeof( c->cr_msg),
1391 "DDS unable to parse dds-min-ttl \"%s\"",
1393 Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1394 "%s: %s.\n", c->log, c->cr_msg );
1398 if ( t < 0 || t > DDS_RF2589_MAX_TTL ) {
1399 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1400 "DDS invalid dds-min-ttl=%ld",
1402 Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1403 "%s: %s.\n", c->log, c->cr_msg );
1408 di->di_min_ttl = DDS_RF2589_DEFAULT_TTL;
1411 di->di_min_ttl = (time_t)t;
1415 case DDS_DEFAULTTTL:
1416 if ( lutil_parse_time( c->argv[ 1 ], &t ) != 0 ) {
1417 snprintf( c->cr_msg, sizeof( c->cr_msg),
1418 "DDS unable to parse dds-default-ttl \"%s\"",
1420 Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1421 "%s: %s.\n", c->log, c->cr_msg );
1425 if ( t < 0 || t > DDS_RF2589_MAX_TTL ) {
1426 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1427 "DDS invalid dds-default-ttl=%ld",
1429 Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1430 "%s: %s.\n", c->log, c->cr_msg );
1435 di->di_default_ttl = DDS_RF2589_DEFAULT_TTL;
1438 di->di_default_ttl = (time_t)t;
1443 if ( lutil_parse_time( c->argv[ 1 ], &t ) != 0 ) {
1444 snprintf( c->cr_msg, sizeof( c->cr_msg),
1445 "DDS unable to parse dds-interval \"%s\"",
1447 Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1448 "%s: %s.\n", c->log, c->cr_msg );
1453 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1454 "DDS invalid dds-interval=%ld",
1456 Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1457 "%s: %s.\n", c->log, c->cr_msg );
1462 Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_NOTICE,
1463 "%s: dds-interval=%lu may be too small.\n",
1467 di->di_interval = (time_t)t;
1468 if ( di->di_expire_task ) {
1469 ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
1470 if ( ldap_pvt_runqueue_isrunning( &slapd_rq, di->di_expire_task ) ) {
1471 ldap_pvt_runqueue_stoptask( &slapd_rq, di->di_expire_task );
1473 di->di_expire_task->interval.tv_sec = DDS_INTERVAL( di );
1474 ldap_pvt_runqueue_resched( &slapd_rq, di->di_expire_task, 0 );
1475 ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
1480 if ( lutil_parse_time( c->argv[ 1 ], &t ) != 0 ) {
1481 snprintf( c->cr_msg, sizeof( c->cr_msg),
1482 "DDS unable to parse dds-tolerance \"%s\"",
1484 Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1485 "%s: %s.\n", c->log, c->cr_msg );
1489 if ( t < 0 || t > DDS_RF2589_MAX_TTL ) {
1490 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1491 "DDS invalid dds-tolerance=%ld",
1493 Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1494 "%s: %s.\n", c->log, c->cr_msg );
1498 di->di_tolerance = (time_t)t;
1501 case DDS_MAXDYNAMICOBJS:
1502 if ( c->value_int < 0 ) {
1503 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1504 "DDS invalid dds-max-dynamicObjects=%d", c->value_int );
1505 Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1506 "%s: %s.\n", c->log, c->cr_msg );
1509 di->di_max_dynamicObjects = c->value_int;
1525 slap_overinst *on = (slap_overinst *)be->bd_info;
1527 BackendInfo *bi = on->on_info->oi_orig;
1529 if ( SLAP_ISGLOBALOVERLAY( be ) ) {
1530 Log0( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1531 "DDS cannot be used as global overlay.\n" );
1535 /* check support for required functions */
1536 /* FIXME: some could be provided by other overlays in between */
1537 if ( bi->bi_op_add == NULL /* object creation */
1538 || bi->bi_op_delete == NULL /* object deletion */
1539 || bi->bi_op_modify == NULL /* object refresh */
1540 || bi->bi_op_search == NULL /* object expiration */
1541 || bi->bi_entry_get_rw == NULL ) /* object type/existence checking */
1543 Log1( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1544 "DDS backend \"%s\" does not provide "
1545 "required functionality.\n",
1550 di = (dds_info_t *)ch_calloc( 1, sizeof( dds_info_t ) );
1551 on->on_bi.bi_private = di;
1553 di->di_max_ttl = DDS_RF2589_DEFAULT_TTL;
1554 di->di_max_ttl = DDS_RF2589_DEFAULT_TTL;
1556 ldap_pvt_thread_mutex_init( &di->di_mutex );
1558 SLAP_DBFLAGS( be ) |= SLAP_DBFLAG_DYNAMIC;
1563 /* adds dynamicSubtrees to root DSE */
1565 dds_entry_info( void *arg, Entry *e )
1567 dds_info_t *di = (dds_info_t *)arg;
1569 attr_merge( e, slap_schema.si_ad_dynamicSubtrees,
1570 di->di_suffix, di->di_nsuffix );
1575 /* callback that counts the returned entries, since the search
1576 * does not get to the point in slap_send_search_entries where
1577 * the actual count occurs */
1579 dds_count_cb( Operation *op, SlapReply *rs )
1581 int *nump = (int *)op->o_callback->sc_private;
1583 switch ( rs->sr_type ) {
1599 /* count dynamic objects existing in the database at startup */
1601 dds_count( void *ctx, BackendDB *be )
1603 slap_overinst *on = (slap_overinst *)be->bd_info;
1604 dds_info_t *di = (dds_info_t *)on->on_bi.bi_private;
1606 Connection conn = { 0 };
1607 OperationBuffer opbuf;
1609 slap_callback sc = { 0 };
1610 SlapReply rs = { REP_RESULT };
1612 connection_fake_init( &conn, &opbuf, ctx );
1615 op->o_tag = LDAP_REQ_SEARCH;
1616 memset( &op->oq_search, 0, sizeof( op->oq_search ) );
1620 op->o_req_dn = op->o_bd->be_suffix[ 0 ];
1621 op->o_req_ndn = op->o_bd->be_nsuffix[ 0 ];
1623 op->o_dn = op->o_bd->be_rootdn;
1624 op->o_ndn = op->o_bd->be_rootndn;
1626 op->ors_scope = LDAP_SCOPE_SUBTREE;
1627 op->ors_tlimit = SLAP_NO_LIMIT;
1628 op->ors_slimit = SLAP_NO_LIMIT;
1629 op->ors_attrs = slap_anlist_no_attrs;
1631 op->ors_filterstr.bv_len = STRLENOF( "(objectClass=" ")" )
1632 + slap_schema.si_oc_dynamicObject->soc_cname.bv_len;
1633 op->ors_filterstr.bv_val = op->o_tmpalloc( op->ors_filterstr.bv_len + 1, op->o_tmpmemctx );
1634 snprintf( op->ors_filterstr.bv_val, op->ors_filterstr.bv_len + 1,
1636 slap_schema.si_oc_dynamicObject->soc_cname.bv_val );
1638 op->ors_filter = str2filter_x( op, op->ors_filterstr.bv_val );
1639 if ( op->ors_filter == NULL ) {
1640 rs.sr_err = LDAP_OTHER;
1644 op->o_callback = ≻
1645 sc.sc_response = dds_count_cb;
1646 sc.sc_private = &di->di_num_dynamicObjects;
1648 op->o_bd->bd_info = (BackendInfo *)on->on_info;
1649 (void)op->o_bd->bd_info->bi_op_search( op, &rs );
1650 op->o_bd->bd_info = (BackendInfo *)on;
1653 op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx );
1654 filter_free_x( op, op->ors_filter );
1656 if ( rs.sr_err == LDAP_SUCCESS ) {
1657 Log1( LDAP_DEBUG_STATS, LDAP_LEVEL_INFO,
1658 "DDS non-expired=%d\n",
1659 di->di_num_dynamicObjects );
1662 Log1( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1663 "DDS non-expired objects lookup failed err=%d\n",
1675 slap_overinst *on = (slap_overinst *)be->bd_info;
1676 dds_info_t *di = on->on_bi.bi_private;
1678 void *thrctx = ldap_pvt_thread_pool_context();
1680 if ( DDS_OFF( di ) ) {
1684 if ( SLAP_SINGLE_SHADOW( be ) ) {
1685 Log1( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1686 "DDS incompatible with shadow database \"%s\".\n",
1687 be->be_suffix[ 0 ].bv_val );
1691 if ( di->di_max_ttl == 0 ) {
1692 di->di_max_ttl = DDS_RF2589_DEFAULT_TTL;
1695 if ( di->di_min_ttl == 0 ) {
1696 di->di_max_ttl = DDS_RF2589_DEFAULT_TTL;
1699 di->di_suffix = be->be_suffix;
1700 di->di_nsuffix = be->be_nsuffix;
1702 /* ... so that count, if required, is accurate */
1703 if ( di->di_max_dynamicObjects > 0 ) {
1704 /* force deletion of expired entries... */
1705 be->bd_info = (BackendInfo *)on->on_info;
1706 rc = dds_expire( thrctx, di );
1707 be->bd_info = (BackendInfo *)on;
1708 if ( rc != LDAP_SUCCESS ) {
1713 rc = dds_count( thrctx, be );
1714 if ( rc != LDAP_SUCCESS ) {
1720 /* start expire task */
1721 ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
1722 di->di_expire_task = ldap_pvt_runqueue_insert( &slapd_rq,
1724 dds_expire_fn, di, "dds_expire_fn",
1725 be->be_suffix[ 0 ].bv_val );
1726 ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
1728 /* register dinamicSubtrees root DSE info support */
1729 rc = entry_info_register( dds_entry_info, (void *)di );
1741 slap_overinst *on = (slap_overinst *)be->bd_info;
1742 dds_info_t *di = on->on_bi.bi_private;
1744 /* stop expire task */
1745 if ( di && di->di_expire_task ) {
1746 ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
1747 if ( ldap_pvt_runqueue_isrunning( &slapd_rq, di->di_expire_task ) ) {
1748 ldap_pvt_runqueue_stoptask( &slapd_rq, di->di_expire_task );
1750 ldap_pvt_runqueue_remove( &slapd_rq, di->di_expire_task );
1751 ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
1754 (void)entry_info_unregister( dds_entry_info, (void *)di );
1764 slap_overinst *on = (slap_overinst *)be->bd_info;
1765 dds_info_t *di = on->on_bi.bi_private;
1768 ldap_pvt_thread_mutex_destroy( &di->di_mutex );
1781 BackendDB *bd = op->o_bd;
1783 rs->sr_err = slap_parse_refresh( op->ore_reqdata, &op->o_req_ndn, NULL,
1784 &rs->sr_text, op->o_tmpmemctx );
1785 if ( rs->sr_err != LDAP_SUCCESS ) {
1789 Log2( LDAP_DEBUG_STATS, LDAP_LEVEL_INFO,
1790 "%s REFRESH dn=\"%s\"\n",
1791 op->o_log_prefix, op->o_req_ndn.bv_val );
1792 op->o_req_dn = op->o_req_ndn;
1794 op->o_bd = select_backend( &op->o_req_ndn, 0 );
1795 if ( !SLAP_DYNAMIC( op->o_bd ) ) {
1796 send_ldap_error( op, rs, LDAP_UNAVAILABLE_CRITICAL_EXTENSION,
1797 "backend does not support dynamic directory services" );
1801 rs->sr_err = backend_check_restrictions( op, rs,
1802 (struct berval *)&slap_EXOP_REFRESH );
1803 if ( rs->sr_err != LDAP_SUCCESS ) {
1807 if ( op->o_bd->be_extended == NULL ) {
1808 send_ldap_error( op, rs, LDAP_UNAVAILABLE_CRITICAL_EXTENSION,
1809 "backend does not support extended operations" );
1813 op->o_bd->be_extended( op, rs );
1816 if ( !BER_BVISNULL( &op->o_req_ndn ) ) {
1817 op->o_tmpfree( op->o_req_ndn.bv_val, op->o_tmpmemctx );
1818 BER_BVZERO( &op->o_req_ndn );
1819 BER_BVZERO( &op->o_req_dn );
1826 static slap_overinst dds;
1828 static int do_not_load_exop;
1829 static int do_not_replace_exop;
1830 static int do_not_load_schema;
1832 #if SLAPD_OVER_DDS == SLAPD_MOD_DYNAMIC
1834 #endif /* SLAPD_OVER_DDS == SLAPD_MOD_DYNAMIC */
1841 /* Make sure we don't exceed the bits reserved for userland */
1842 config_check_userland( DDS_LAST );
1844 if ( !do_not_load_schema ) {
1848 AttributeDescription **ad;
1850 { "( 1.3.6.1.4.1.4203.666.1.57 "
1851 "NAME ( 'entryExpireTimestamp' ) "
1852 "DESC 'RFC2589 OpenLDAP extension: expire time of a dynamic object, "
1853 "computed as now + entryTtl' "
1854 "EQUALITY generalizedTimeMatch "
1855 "ORDERING generalizedTimeOrderingMatch "
1856 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 "
1858 "NO-USER-MODIFICATION "
1859 "USAGE dSAOperation )",
1861 &ad_entryExpireTimestamp },
1865 for ( i = 0; s_at[ i ].desc != NULL; i++ ) {
1866 code = register_at( s_at[ i ].desc, s_at[ i ].ad, 0 );
1868 Debug( LDAP_DEBUG_ANY,
1869 "dds_initialize: register_at failed\n", 0, 0, 0 );
1872 (*s_at[ i ].ad)->ad_type->sat_flags |= SLAP_AT_HIDE;
1876 if ( !do_not_load_exop ) {
1877 rc = load_extop2( (struct berval *)&slap_EXOP_REFRESH,
1878 SLAP_EXOP_WRITES|SLAP_EXOP_HIDE, slap_exop_refresh,
1879 !do_not_replace_exop );
1880 if ( rc != LDAP_SUCCESS ) {
1881 Log1( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1882 "DDS unable to register refresh exop: %d.\n",
1888 dds.on_bi.bi_type = "dds";
1890 dds.on_bi.bi_db_init = dds_db_init;
1891 dds.on_bi.bi_db_open = dds_db_open;
1892 dds.on_bi.bi_db_close = dds_db_close;
1893 dds.on_bi.bi_db_destroy = dds_db_destroy;
1895 dds.on_bi.bi_op_add = dds_op_add;
1896 dds.on_bi.bi_op_delete = dds_op_delete;
1897 dds.on_bi.bi_op_modify = dds_op_modify;
1898 dds.on_bi.bi_op_modrdn = dds_op_rename;
1899 dds.on_bi.bi_extended = dds_op_extended;
1901 dds.on_bi.bi_cf_ocs = dds_ocs;
1903 rc = config_register_schema( dds_cfg, dds_ocs );
1908 return overlay_register( &dds );
1911 #if SLAPD_OVER_DDS == SLAPD_MOD_DYNAMIC
1913 init_module( int argc, char *argv[] )
1917 for ( i = 0; i < argc; i++ ) {
1918 char *arg = argv[ i ];
1921 if ( strncasecmp( arg, "no-", STRLENOF( "no-" ) ) == 0 ) {
1922 arg += STRLENOF( "no-" );
1926 if ( strcasecmp( arg, "exop" ) == 0 ) {
1927 do_not_load_exop = no;
1929 } else if ( strcasecmp( arg, "replace" ) == 0 ) {
1930 do_not_replace_exop = no;
1932 } else if ( strcasecmp( arg, "schema" ) == 0 ) {
1933 do_not_load_schema = no;
1936 Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1937 "DDS unknown module arg[#%d]=\"%s\".\n",
1943 return dds_initialize();
1945 #endif /* SLAPD_OVER_DDS == SLAPD_MOD_DYNAMIC */
1947 #endif /* defined(SLAPD_OVER_DDS) */