1 /* $OpenLDAP: pkg/ldap/servers/slapd/overlays/dds.c,v 1.7.2.9 2008/02/11 23:26:48 kurt Exp $ */
2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4 * Copyright 2005-2008 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
= &sc
;
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
= &sc
;
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
[STRLENOF("31557600") + 1];
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 because should be checked at configure */
418 assert( ttl
<= DDS_RF2589_MAX_TTL
);
421 bv
.bv_len
= snprintf( ttlbuf
, sizeof( ttlbuf
), "%ld", ttl
);
422 assert( bv
.bv_len
< sizeof( ttlbuf
) );
424 /* FIXME: apparently, values in op->ora_e are malloc'ed
425 * on the thread's slab; works fine by chance,
426 * only because the attribute doesn't exist yet. */
427 assert( attr_find( op
->ora_e
->e_attrs
, slap_schema
.si_ad_entryTtl
) == NULL
);
428 attr_merge_one( op
->ora_e
, slap_schema
.si_ad_entryTtl
, &bv
, &bv
);
430 expire
= slap_get_time() + ttl
;
432 bv
.bv_len
= sizeof( tsbuf
);
433 slap_timestamp( &expire
, &bv
);
434 assert( attr_find( op
->ora_e
->e_attrs
, ad_entryExpireTimestamp
) == NULL
);
435 attr_merge_one( op
->ora_e
, ad_entryExpireTimestamp
, &bv
, &bv
);
437 /* if required, install counter callback */
438 if ( di
->di_max_dynamicObjects
> 0) {
441 sc
= op
->o_tmpalloc( sizeof( slap_callback
), op
->o_tmpmemctx
);
442 sc
->sc_cleanup
= dds_freeit_cb
;
443 sc
->sc_response
= dds_counter_cb
;
445 sc
->sc_next
= op
->o_callback
;
451 return SLAP_CB_CONTINUE
;
455 dds_op_delete( Operation
*op
, SlapReply
*rs
)
457 slap_overinst
*on
= (slap_overinst
*)op
->o_bd
->bd_info
;
458 dds_info_t
*di
= on
->on_bi
.bi_private
;
460 /* if required, install counter callback */
461 if ( !DDS_OFF( di
) && di
->di_max_dynamicObjects
> 0 ) {
463 BackendInfo
*bi
= op
->o_bd
->bd_info
;
465 op
->o_bd
->bd_info
= (BackendInfo
*)on
->on_info
;
466 rs
->sr_err
= be_entry_get_rw( op
, &op
->o_req_ndn
,
467 slap_schema
.si_oc_dynamicObject
, NULL
, 0, &e
);
469 /* FIXME: couldn't the entry be added before deletion? */
470 if ( rs
->sr_err
== LDAP_SUCCESS
&& e
!= NULL
) {
473 be_entry_release_r( op
, e
);
476 sc
= op
->o_tmpalloc( sizeof( slap_callback
), op
->o_tmpmemctx
);
477 sc
->sc_cleanup
= dds_freeit_cb
;
478 sc
->sc_response
= dds_counter_cb
;
480 sc
->sc_next
= op
->o_callback
;
484 op
->o_bd
->bd_info
= bi
;
487 return SLAP_CB_CONTINUE
;
491 dds_op_modify( Operation
*op
, SlapReply
*rs
)
493 slap_overinst
*on
= (slap_overinst
*)op
->o_bd
->bd_info
;
494 dds_info_t
*di
= (dds_info_t
*)on
->on_bi
.bi_private
;
497 BackendInfo
*bi
= op
->o_bd
->bd_info
;
498 int was_dynamicObject
= 0,
499 is_dynamicObject
= 0;
500 struct berval bv_entryTtl
= BER_BVNULL
;
502 char textbuf
[ SLAP_TEXT_BUFLEN
];
504 if ( DDS_OFF( di
) ) {
505 return SLAP_CB_CONTINUE
;
508 /* bv_entryTtl stores the string representation of the entryTtl
509 * across modifies for consistency checks of the final value;
510 * the bv_val points to a static buffer; the bv_len is zero when
511 * the attribute is deleted.
512 * entryTtl stores the integer representation of the entryTtl;
513 * its value is -1 when the attribute is deleted; it is 0 only
514 * if no modifications of the entryTtl occurred, as an entryTtl
515 * of 0 is invalid. */
516 bv_entryTtl
.bv_val
= textbuf
;
518 op
->o_bd
->bd_info
= (BackendInfo
*)on
->on_info
;
519 rs
->sr_err
= be_entry_get_rw( op
, &op
->o_req_ndn
,
520 slap_schema
.si_oc_dynamicObject
, slap_schema
.si_ad_entryTtl
, 0, &e
);
521 if ( rs
->sr_err
== LDAP_SUCCESS
&& e
!= NULL
) {
522 Attribute
*a
= attr_find( e
->e_attrs
, slap_schema
.si_ad_entryTtl
);
524 /* the value of the entryTtl is saved for later checks */
529 bv_entryTtl
.bv_len
= a
->a_nvals
[ 0 ].bv_len
;
530 AC_MEMCPY( bv_entryTtl
.bv_val
, a
->a_nvals
[ 0 ].bv_val
, bv_entryTtl
.bv_len
);
531 bv_entryTtl
.bv_val
[ bv_entryTtl
.bv_len
] = '\0';
532 rc
= lutil_atoul( &ttl
, bv_entryTtl
.bv_val
);
534 entryTtl
= (time_t)ttl
;
537 be_entry_release_r( op
, e
);
539 was_dynamicObject
= is_dynamicObject
= 1;
541 op
->o_bd
->bd_info
= bi
;
543 rs
->sr_err
= LDAP_SUCCESS
;
544 for ( mod
= op
->orm_modlist
; mod
; mod
= mod
->sml_next
) {
545 if ( mod
->sml_desc
== slap_schema
.si_ad_objectClass
) {
549 switch ( mod
->sml_op
) {
550 case LDAP_MOD_DELETE
:
551 if ( mod
->sml_values
== NULL
) {
552 is_dynamicObject
= 0;
556 for ( i
= 0; !BER_BVISNULL( &mod
->sml_values
[ i
] ); i
++ ) {
557 oc
= oc_bvfind( &mod
->sml_values
[ i
] );
558 if ( oc
== slap_schema
.si_oc_dynamicObject
) {
559 is_dynamicObject
= 0;
566 case LDAP_MOD_REPLACE
:
567 if ( mod
->sml_values
== NULL
) {
568 is_dynamicObject
= 0;
574 for ( i
= 0; !BER_BVISNULL( &mod
->sml_values
[ i
] ); i
++ ) {
575 oc
= oc_bvfind( &mod
->sml_values
[ i
] );
576 if ( oc
== slap_schema
.si_oc_dynamicObject
) {
577 is_dynamicObject
= 1;
584 } else if ( mod
->sml_desc
== slap_schema
.si_ad_entryTtl
) {
588 switch ( mod
->sml_op
) {
589 case LDAP_MOD_DELETE
:
590 if ( mod
->sml_values
!= NULL
) {
591 if ( BER_BVISEMPTY( &bv_entryTtl
)
592 || !bvmatch( &bv_entryTtl
, &mod
->sml_values
[ 0 ] ) )
594 rs
->sr_err
= backend_attribute( op
, NULL
, &op
->o_req_ndn
,
595 slap_schema
.si_ad_entry
, NULL
, ACL_DISCLOSE
);
596 if ( rs
->sr_err
== LDAP_INSUFFICIENT_ACCESS
) {
597 rs
->sr_err
= LDAP_NO_SUCH_OBJECT
;
600 rs
->sr_err
= LDAP_NO_SUCH_ATTRIBUTE
;
605 bv_entryTtl
.bv_len
= 0;
609 case LDAP_MOD_REPLACE
:
610 bv_entryTtl
.bv_len
= 0;
614 case SLAP_MOD_SOFTADD
: /* FIXME? */
616 assert( mod
->sml_values
!= NULL
);
617 assert( BER_BVISNULL( &mod
->sml_values
[ 1 ] ) );
619 if ( !BER_BVISEMPTY( &bv_entryTtl
) ) {
620 rs
->sr_err
= backend_attribute( op
, NULL
, &op
->o_req_ndn
,
621 slap_schema
.si_ad_entry
, NULL
, ACL_DISCLOSE
);
622 if ( rs
->sr_err
== LDAP_INSUFFICIENT_ACCESS
) {
623 rs
->sr_err
= LDAP_NO_SUCH_OBJECT
;
626 rs
->sr_text
= "attribute 'entryTtl' cannot have multiple values";
627 rs
->sr_err
= LDAP_CONSTRAINT_VIOLATION
;
632 rc
= lutil_atoul( &ttl
, mod
->sml_values
[ 0 ].bv_val
);
634 if ( ttl
> DDS_RF2589_MAX_TTL
) {
635 rs
->sr_err
= LDAP_PROTOCOL_ERROR
;
636 rs
->sr_text
= "invalid time-to-live for dynamicObject";
640 if ( ttl
<= 0 || ttl
> di
->di_max_ttl
) {
641 /* FIXME: I don't understand if this has to be an error,
642 * or an indication that the requested Ttl has been
643 * shortened to di->di_max_ttl >= 1 day */
644 rs
->sr_err
= LDAP_SIZELIMIT_EXCEEDED
;
645 rs
->sr_text
= "time-to-live for dynamicObject exceeds administrative limit";
649 entryTtl
= (time_t)ttl
;
650 bv_entryTtl
.bv_len
= mod
->sml_values
[ 0 ].bv_len
;
651 AC_MEMCPY( bv_entryTtl
.bv_val
, mod
->sml_values
[ 0 ].bv_val
, bv_entryTtl
.bv_len
);
652 bv_entryTtl
.bv_val
[ bv_entryTtl
.bv_len
] = '\0';
655 case LDAP_MOD_INCREMENT
:
656 if ( BER_BVISEMPTY( &bv_entryTtl
) ) {
657 rs
->sr_err
= backend_attribute( op
, NULL
, &op
->o_req_ndn
,
658 slap_schema
.si_ad_entry
, NULL
, ACL_DISCLOSE
);
659 if ( rs
->sr_err
== LDAP_INSUFFICIENT_ACCESS
) {
660 rs
->sr_err
= LDAP_NO_SUCH_OBJECT
;
663 rs
->sr_err
= LDAP_NO_SUCH_ATTRIBUTE
;
664 rs
->sr_text
= "modify/increment: entryTtl: no such attribute";
670 if ( entryTtl
> DDS_RF2589_MAX_TTL
) {
671 rs
->sr_err
= LDAP_PROTOCOL_ERROR
;
672 rs
->sr_text
= "invalid time-to-live for dynamicObject";
674 } else if ( entryTtl
<= 0 || entryTtl
> di
->di_max_ttl
) {
675 /* FIXME: I don't understand if this has to be an error,
676 * or an indication that the requested Ttl has been
677 * shortened to di->di_max_ttl >= 1 day */
678 rs
->sr_err
= LDAP_SIZELIMIT_EXCEEDED
;
679 rs
->sr_text
= "time-to-live for dynamicObject exceeds administrative limit";
682 if ( rs
->sr_err
!= LDAP_SUCCESS
) {
683 rc
= backend_attribute( op
, NULL
, &op
->o_req_ndn
,
684 slap_schema
.si_ad_entry
, NULL
, ACL_DISCLOSE
);
685 if ( rc
== LDAP_INSUFFICIENT_ACCESS
) {
687 rs
->sr_err
= LDAP_NO_SUCH_OBJECT
;
693 bv_entryTtl
.bv_len
= snprintf( textbuf
, sizeof( textbuf
), "%ld", entryTtl
);
701 } else if ( mod
->sml_desc
== ad_entryExpireTimestamp
) {
702 /* should have been trapped earlier */
703 assert( mod
->sml_flags
& SLAP_MOD_INTERNAL
);
708 if ( rs
->sr_err
== LDAP_SUCCESS
) {
711 /* FIXME: this could be allowed when the Relax control is used...
717 * entryTtl must be provided; add
718 * entryExpireTimestamp accordingly
721 * entryTtl must be removed; remove
722 * entryTimestamp accordingly
724 * ... but we need to make sure that there are no subordinate
727 rc
= is_dynamicObject
- was_dynamicObject
;
729 #if 0 /* fix subordinate issues first */
730 if ( get_relax( op
) ) {
733 /* need to delete entryTtl to have a consistent entry */
734 if ( entryTtl
!= -1 ) {
735 rs
->sr_text
= "objectClass modification from dynamicObject to static entry requires entryTtl deletion";
736 rs
->sr_err
= LDAP_OBJECT_CLASS_VIOLATION
;
741 /* need to add entryTtl to have a consistent entry */
742 if ( entryTtl
<= 0 ) {
743 rs
->sr_text
= "objectClass modification from static entry to dynamicObject requires entryTtl addition";
744 rs
->sr_err
= LDAP_OBJECT_CLASS_VIOLATION
;
754 rs
->sr_text
= "objectClass modification cannot turn dynamicObject into static entry";
758 rs
->sr_text
= "objectClass modification cannot turn static entry into dynamicObject";
761 rs
->sr_err
= LDAP_OBJECT_CLASS_VIOLATION
;
764 if ( rc
!= LDAP_SUCCESS
) {
765 rc
= backend_attribute( op
, NULL
, &op
->o_req_ndn
,
766 slap_schema
.si_ad_entry
, NULL
, ACL_DISCLOSE
);
767 if ( rc
== LDAP_INSUFFICIENT_ACCESS
) {
769 rs
->sr_err
= LDAP_NO_SUCH_OBJECT
;
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
);
806 value_add_one( &tmpmod
->sml_nvalues
, &bv
);
807 tmpmod
->sml_numvals
= 1;
812 op
->o_bd
->bd_info
= (BackendInfo
*)on
->on_info
;
813 send_ldap_result( op
, rs
);
817 return SLAP_CB_CONTINUE
;
821 dds_op_rename( Operation
*op
, SlapReply
*rs
)
823 slap_overinst
*on
= (slap_overinst
*)op
->o_bd
->bd_info
;
824 dds_info_t
*di
= on
->on_bi
.bi_private
;
826 if ( DDS_OFF( di
) ) {
827 return SLAP_CB_CONTINUE
;
830 /* we don't allow dynamicObjects to have static subordinates */
831 if ( op
->orr_nnewSup
!= NULL
) {
833 BackendInfo
*bi
= op
->o_bd
->bd_info
;
834 int is_dynamicObject
= 0,
837 rs
->sr_err
= LDAP_SUCCESS
;
839 op
->o_bd
->bd_info
= (BackendInfo
*)on
->on_info
;
840 rc
= be_entry_get_rw( op
, &op
->o_req_ndn
,
841 slap_schema
.si_oc_dynamicObject
, NULL
, 0, &e
);
842 if ( rc
== LDAP_SUCCESS
&& e
!= NULL
) {
843 be_entry_release_r( op
, e
);
845 is_dynamicObject
= 1;
848 rc
= be_entry_get_rw( op
, op
->orr_nnewSup
,
849 slap_schema
.si_oc_dynamicObject
, NULL
, 0, &e
);
850 if ( rc
== LDAP_SUCCESS
&& e
!= NULL
) {
851 if ( !is_dynamicObject
) {
852 /* return referral only if "disclose"
853 * is granted on the object */
854 if ( ! access_allowed( op
, e
,
855 slap_schema
.si_ad_entry
,
856 NULL
, ACL_DISCLOSE
, NULL
) )
858 rs
->sr_err
= LDAP_NO_SUCH_OBJECT
;
859 send_ldap_result( op
, rs
);
862 send_ldap_error( op
, rs
, LDAP_CONSTRAINT_VIOLATION
,
863 "static entry cannot have dynamicObject as newSuperior" );
866 be_entry_release_r( op
, e
);
868 op
->o_bd
->bd_info
= bi
;
869 if ( rs
->sr_err
!= LDAP_SUCCESS
) {
874 return SLAP_CB_CONTINUE
;
885 int rc
= LDAP_SUCCESS
;
888 BerElementBuffer berbuf
;
889 BerElement
*ber
= (BerElement
*)&berbuf
;
890 struct berval reqdata
= BER_BVNULL
;
899 if ( in
== NULL
|| in
->bv_len
== 0 ) {
900 *text
= "empty request data field in refresh exop";
901 return LDAP_PROTOCOL_ERROR
;
904 ber_dupbv_x( &reqdata
, in
, ctx
);
906 /* ber_init2 uses reqdata directly, doesn't allocate new buffers */
907 ber_init2( ber
, &reqdata
, 0 );
909 tag
= ber_scanf( ber
, "{" /*}*/ );
911 if ( tag
== LBER_ERROR
) {
912 Log0( LDAP_DEBUG_TRACE
, LDAP_LEVEL_ERR
,
913 "slap_parse_refresh: decoding error.\n" );
917 tag
= ber_peek_tag( ber
, &len
);
918 if ( tag
!= LDAP_TAG_EXOP_REFRESH_REQ_DN
) {
919 Log0( LDAP_DEBUG_TRACE
, LDAP_LEVEL_ERR
,
920 "slap_parse_refresh: decoding error.\n" );
927 tag
= ber_scanf( ber
, "m", &dn
);
928 if ( tag
== LBER_ERROR
) {
929 Log0( LDAP_DEBUG_TRACE
, LDAP_LEVEL_ERR
,
930 "slap_parse_refresh: DN parse failed.\n" );
934 rc
= dnNormalize( 0, NULL
, NULL
, &dn
, ndn
, ctx
);
935 if ( rc
!= LDAP_SUCCESS
) {
936 *text
= "invalid DN in refresh exop request data";
941 tag
= ber_scanf( ber
, "x" /* "m" */ );
942 if ( tag
== LBER_DEFAULT
) {
947 tag
= ber_peek_tag( ber
, &len
);
949 if ( tag
!= LDAP_TAG_EXOP_REFRESH_REQ_TTL
) {
950 Log0( LDAP_DEBUG_TRACE
, LDAP_LEVEL_ERR
,
951 "slap_parse_refresh: decoding error.\n" );
955 tag
= ber_scanf( ber
, "i", &tmp
);
956 if ( tag
== LBER_ERROR
) {
957 Log0( LDAP_DEBUG_TRACE
, LDAP_LEVEL_ERR
,
958 "slap_parse_refresh: TTL parse failed.\n" );
966 tag
= ber_peek_tag( ber
, &len
);
968 if ( tag
!= LBER_DEFAULT
|| len
!= 0 ) {
970 Log1( LDAP_DEBUG_TRACE
, LDAP_LEVEL_ERR
,
971 "slap_parse_refresh: decoding error, len=%ld\n",
973 rc
= LDAP_PROTOCOL_ERROR
;
974 *text
= "data decoding error";
977 if ( ndn
&& !BER_BVISNULL( ndn
) ) {
978 slap_sl_free( ndn
->bv_val
, ctx
);
983 if ( !BER_BVISNULL( &reqdata
) ) {
984 ber_memfree_x( reqdata
.bv_val
, ctx
);
991 dds_op_extended( Operation
*op
, SlapReply
*rs
)
993 slap_overinst
*on
= (slap_overinst
*)op
->o_bd
->bd_info
;
994 dds_info_t
*di
= on
->on_bi
.bi_private
;
996 if ( DDS_OFF( di
) ) {
997 return SLAP_CB_CONTINUE
;
1000 if ( bvmatch( &op
->ore_reqoid
, &slap_EXOP_REFRESH
) ) {
1003 BackendDB db
= *op
->o_bd
;
1004 SlapReply rs2
= { REP_RESULT
};
1005 Operation op2
= *op
;
1006 slap_callback sc
= { 0 };
1007 Modifications ttlmod
= { { 0 } };
1008 struct berval ttlvalues
[ 2 ];
1009 char ttlbuf
[STRLENOF("31557600") + 1];
1011 rs
->sr_err
= slap_parse_refresh( op
->ore_reqdata
, NULL
, &ttl
,
1012 &rs
->sr_text
, NULL
);
1013 assert( rs
->sr_err
== LDAP_SUCCESS
);
1015 if ( ttl
<= 0 || ttl
> DDS_RF2589_MAX_TTL
) {
1016 rs
->sr_err
= LDAP_PROTOCOL_ERROR
;
1017 rs
->sr_text
= "invalid time-to-live for dynamicObject";
1021 if ( ttl
> di
->di_max_ttl
) {
1022 /* FIXME: I don't understand if this has to be an error,
1023 * or an indication that the requested Ttl has been
1024 * shortened to di->di_max_ttl >= 1 day */
1025 rs
->sr_err
= LDAP_SIZELIMIT_EXCEEDED
;
1026 rs
->sr_text
= "time-to-live for dynamicObject exceeds limit";
1030 if ( di
->di_min_ttl
&& ttl
< di
->di_min_ttl
) {
1031 ttl
= di
->di_min_ttl
;
1034 /* This does not apply to multi-master case */
1035 if ( !( !SLAP_SINGLE_SHADOW( op
->o_bd
) || be_isupdate( op
) ) ) {
1036 /* we SHOULD return a referral in this case */
1037 BerVarray defref
= op
->o_bd
->be_update_refs
1038 ? op
->o_bd
->be_update_refs
: default_referral
;
1040 if ( defref
!= NULL
) {
1041 rs
->sr_ref
= referral_rewrite( op
->o_bd
->be_update_refs
,
1042 NULL
, NULL
, LDAP_SCOPE_DEFAULT
);
1044 rs
->sr_flags
|= REP_REF_MUSTBEFREED
;
1046 rs
->sr_ref
= defref
;
1048 rs
->sr_err
= LDAP_REFERRAL
;
1051 rs
->sr_text
= "shadow context; no update referral";
1052 rs
->sr_err
= LDAP_UNWILLING_TO_PERFORM
;
1058 assert( !BER_BVISNULL( &op
->o_req_ndn
) );
1062 /* check if exists but not dynamicObject */
1063 op
->o_bd
->bd_info
= (BackendInfo
*)on
->on_info
;
1064 rs
->sr_err
= be_entry_get_rw( op
, &op
->o_req_ndn
,
1065 slap_schema
.si_oc_dynamicObject
, NULL
, 0, &e
);
1066 if ( rs
->sr_err
!= LDAP_SUCCESS
) {
1067 rs
->sr_err
= be_entry_get_rw( op
, &op
->o_req_ndn
,
1068 NULL
, NULL
, 0, &e
);
1069 if ( rs
->sr_err
== LDAP_SUCCESS
&& e
!= NULL
) {
1070 /* return referral only if "disclose"
1071 * is granted on the object */
1072 if ( ! access_allowed( op
, e
,
1073 slap_schema
.si_ad_entry
,
1074 NULL
, ACL_DISCLOSE
, NULL
) )
1076 rs
->sr_err
= LDAP_NO_SUCH_OBJECT
;
1079 rs
->sr_err
= LDAP_OBJECT_CLASS_VIOLATION
;
1080 rs
->sr_text
= "refresh operation only applies to dynamic objects";
1082 be_entry_release_r( op
, e
);
1085 rs
->sr_err
= LDAP_NO_SUCH_OBJECT
;
1089 } else if ( e
!= NULL
) {
1090 be_entry_release_r( op
, e
);
1093 /* we require manage privileges on the entryTtl,
1094 * and fake a Relax control */
1095 op2
.o_tag
= LDAP_REQ_MODIFY
;
1097 db
.bd_info
= (BackendInfo
*)on
->on_info
;
1098 op2
.o_callback
= &sc
;
1099 sc
.sc_response
= slap_null_cb
;
1100 op2
.o_relax
= SLAP_CONTROL_CRITICAL
;
1101 op2
.orm_modlist
= &ttlmod
;
1103 ttlmod
.sml_op
= LDAP_MOD_REPLACE
;
1104 ttlmod
.sml_flags
= SLAP_MOD_MANAGING
;
1105 ttlmod
.sml_desc
= slap_schema
.si_ad_entryTtl
;
1106 ttlmod
.sml_values
= ttlvalues
;
1107 ttlmod
.sml_numvals
= 1;
1108 ttlvalues
[ 0 ].bv_val
= ttlbuf
;
1109 ttlvalues
[ 0 ].bv_len
= snprintf( ttlbuf
, sizeof( ttlbuf
), "%ld", ttl
);
1110 BER_BVZERO( &ttlvalues
[ 1 ] );
1112 /* the entryExpireTimestamp is added by modify */
1113 rs
->sr_err
= op2
.o_bd
->be_modify( &op2
, &rs2
);
1115 if ( ttlmod
.sml_next
!= NULL
) {
1116 slap_mods_free( ttlmod
.sml_next
, 1 );
1119 if ( rs
->sr_err
== LDAP_SUCCESS
) {
1121 BerElementBuffer berbuf
;
1122 BerElement
*ber
= (BerElement
*)&berbuf
;
1124 if ( rs
->sr_err
== LDAP_SUCCESS
) {
1125 ber_init_w_nullc( ber
, LBER_USE_DER
);
1127 rc
= ber_printf( ber
, "{tiN}", LDAP_TAG_EXOP_REFRESH_RES_TTL
, (int)ttl
);
1130 rs
->sr_err
= LDAP_OTHER
;
1131 rs
->sr_text
= "internal error";
1134 (void)ber_flatten( ber
, &rs
->sr_rspdata
);
1135 rs
->sr_rspoid
= ch_strdup( slap_EXOP_REFRESH
.bv_val
);
1137 Log3( LDAP_DEBUG_TRACE
, LDAP_LEVEL_INFO
,
1138 "%s REFRESH dn=\"%s\" TTL=%ld\n",
1139 op
->o_log_prefix
, op
->o_req_ndn
.bv_val
, ttl
);
1142 ber_free_buf( ber
);
1149 return SLAP_CB_CONTINUE
;
1164 static ConfigDriver dds_cfgen
;
1166 static ConfigLDAPadd dds_ldadd
;
1167 static ConfigCfAdd dds_cfadd
;
1170 static ConfigTable dds_cfg
[] = {
1171 { "dds-state", "on|off",
1172 2, 2, 0, ARG_MAGIC
|ARG_ON_OFF
|DDS_STATE
, dds_cfgen
,
1173 "( OLcfgOvAt:9.1 NAME 'olcDDSstate' "
1174 "DESC 'RFC2589 Dynamic directory services state' "
1175 "SYNTAX OMsBoolean "
1176 "SINGLE-VALUE )", NULL
, NULL
},
1177 { "dds-max-ttl", "ttl",
1178 2, 2, 0, ARG_MAGIC
|DDS_MAXTTL
, dds_cfgen
,
1179 "( OLcfgOvAt:9.2 NAME 'olcDDSmaxTtl' "
1180 "DESC 'RFC2589 Dynamic directory services max TTL' "
1181 "SYNTAX OMsDirectoryString "
1182 "SINGLE-VALUE )", NULL
, NULL
},
1183 { "dds-min-ttl", "ttl",
1184 2, 2, 0, ARG_MAGIC
|DDS_MINTTL
, dds_cfgen
,
1185 "( OLcfgOvAt:9.3 NAME 'olcDDSminTtl' "
1186 "DESC 'RFC2589 Dynamic directory services min TTL' "
1187 "SYNTAX OMsDirectoryString "
1188 "SINGLE-VALUE )", NULL
, NULL
},
1189 { "dds-default-ttl", "ttl",
1190 2, 2, 0, ARG_MAGIC
|DDS_DEFAULTTTL
, dds_cfgen
,
1191 "( OLcfgOvAt:9.4 NAME 'olcDDSdefaultTtl' "
1192 "DESC 'RFC2589 Dynamic directory services default TTL' "
1193 "SYNTAX OMsDirectoryString "
1194 "SINGLE-VALUE )", NULL
, NULL
},
1195 { "dds-interval", "interval",
1196 2, 2, 0, ARG_MAGIC
|DDS_INTERVAL
, dds_cfgen
,
1197 "( OLcfgOvAt:9.5 NAME 'olcDDSinterval' "
1198 "DESC 'RFC2589 Dynamic directory services expiration "
1199 "task run interval' "
1200 "SYNTAX OMsDirectoryString "
1201 "SINGLE-VALUE )", NULL
, NULL
},
1202 { "dds-tolerance", "ttl",
1203 2, 2, 0, ARG_MAGIC
|DDS_TOLERANCE
, dds_cfgen
,
1204 "( OLcfgOvAt:9.6 NAME 'olcDDStolerance' "
1205 "DESC 'RFC2589 Dynamic directory services additional "
1206 "TTL in expiration scheduling' "
1207 "SYNTAX OMsDirectoryString "
1208 "SINGLE-VALUE )", NULL
, NULL
},
1209 { "dds-max-dynamicObjects", "num",
1210 2, 2, 0, ARG_MAGIC
|ARG_INT
|DDS_MAXDYNAMICOBJS
, dds_cfgen
,
1211 "( OLcfgOvAt:9.7 NAME 'olcDDSmaxDynamicObjects' "
1212 "DESC 'RFC2589 Dynamic directory services max number of dynamic objects' "
1213 "SYNTAX OMsInteger "
1214 "SINGLE-VALUE )", NULL
, NULL
},
1215 { NULL
, NULL
, 0, 0, 0, ARG_IGNORED
}
1218 static ConfigOCs dds_ocs
[] = {
1219 { "( OLcfgOvOc:9.1 "
1220 "NAME 'olcDDSConfig' "
1221 "DESC 'RFC2589 Dynamic directory services configuration' "
1222 "SUP olcOverlayConfig "
1227 "$ olcDDSdefaultTtl "
1229 "$ olcDDStolerance "
1230 "$ olcDDSmaxDynamicObjects "
1232 ")", Cft_Overlay
, dds_cfg
, NULL
, NULL
/* dds_cfadd */ },
1238 dds_ldadd( CfEntryInfo
*p
, Entry
*e
, ConfigArgs
*ca
)
1240 return LDAP_SUCCESS
;
1244 dds_cfadd( Operation
*op
, SlapReply
*rs
, Entry
*p
, ConfigArgs
*ca
)
1251 dds_cfgen( ConfigArgs
*c
)
1253 slap_overinst
*on
= (slap_overinst
*)c
->bi
;
1254 dds_info_t
*di
= on
->on_bi
.bi_private
;
1259 if ( c
->op
== SLAP_CONFIG_EMIT
) {
1260 char buf
[ SLAP_TEXT_BUFLEN
];
1265 c
->value_int
= !DDS_OFF( di
);
1269 lutil_unparse_time( buf
, sizeof( buf
), di
->di_max_ttl
);
1270 ber_str2bv( buf
, 0, 0, &bv
);
1271 value_add_one( &c
->rvalue_vals
, &bv
);
1275 if ( di
->di_min_ttl
) {
1276 lutil_unparse_time( buf
, sizeof( buf
), di
->di_min_ttl
);
1277 ber_str2bv( buf
, 0, 0, &bv
);
1278 value_add_one( &c
->rvalue_vals
, &bv
);
1285 case DDS_DEFAULTTTL
:
1286 if ( di
->di_default_ttl
) {
1287 lutil_unparse_time( buf
, sizeof( buf
), di
->di_default_ttl
);
1288 ber_str2bv( buf
, 0, 0, &bv
);
1289 value_add_one( &c
->rvalue_vals
, &bv
);
1297 if ( di
->di_interval
) {
1298 lutil_unparse_time( buf
, sizeof( buf
), di
->di_interval
);
1299 ber_str2bv( buf
, 0, 0, &bv
);
1300 value_add_one( &c
->rvalue_vals
, &bv
);
1308 if ( di
->di_tolerance
) {
1309 lutil_unparse_time( buf
, sizeof( buf
), di
->di_tolerance
);
1310 ber_str2bv( buf
, 0, 0, &bv
);
1311 value_add_one( &c
->rvalue_vals
, &bv
);
1318 case DDS_MAXDYNAMICOBJS
:
1319 if ( di
->di_max_dynamicObjects
> 0 ) {
1320 c
->value_int
= di
->di_max_dynamicObjects
;
1334 } else if ( c
->op
== LDAP_MOD_DELETE
) {
1337 di
->di_flags
&= ~DDS_FOFF
;
1341 di
->di_min_ttl
= DDS_RF2589_DEFAULT_TTL
;
1348 case DDS_DEFAULTTTL
:
1349 di
->di_default_ttl
= 0;
1353 di
->di_interval
= 0;
1357 di
->di_tolerance
= 0;
1360 case DDS_MAXDYNAMICOBJS
:
1361 di
->di_max_dynamicObjects
= 0;
1372 switch ( c
->type
) {
1374 if ( c
->value_int
) {
1375 di
->di_flags
&= ~DDS_FOFF
;
1378 di
->di_flags
|= DDS_FOFF
;
1383 if ( lutil_parse_time( c
->argv
[ 1 ], &t
) != 0 ) {
1384 snprintf( c
->cr_msg
, sizeof( c
->cr_msg
),
1385 "DDS unable to parse dds-max-ttl \"%s\"",
1387 Log2( LDAP_DEBUG_ANY
, LDAP_LEVEL_ERR
,
1388 "%s: %s.\n", c
->log
, c
->cr_msg
);
1392 if ( t
< DDS_RF2589_DEFAULT_TTL
|| t
> DDS_RF2589_MAX_TTL
) {
1393 snprintf( c
->cr_msg
, sizeof( c
->cr_msg
),
1394 "DDS invalid dds-max-ttl=%ld; must be between %d and %d",
1395 t
, DDS_RF2589_DEFAULT_TTL
, DDS_RF2589_MAX_TTL
);
1396 Log2( LDAP_DEBUG_ANY
, LDAP_LEVEL_ERR
,
1397 "%s: %s.\n", c
->log
, c
->cr_msg
);
1401 di
->di_max_ttl
= (time_t)t
;
1405 if ( lutil_parse_time( c
->argv
[ 1 ], &t
) != 0 ) {
1406 snprintf( c
->cr_msg
, sizeof( c
->cr_msg
),
1407 "DDS unable to parse dds-min-ttl \"%s\"",
1409 Log2( LDAP_DEBUG_ANY
, LDAP_LEVEL_ERR
,
1410 "%s: %s.\n", c
->log
, c
->cr_msg
);
1414 if ( t
< 0 || t
> DDS_RF2589_MAX_TTL
) {
1415 snprintf( c
->cr_msg
, sizeof( c
->cr_msg
),
1416 "DDS invalid dds-min-ttl=%ld",
1418 Log2( LDAP_DEBUG_ANY
, LDAP_LEVEL_ERR
,
1419 "%s: %s.\n", c
->log
, c
->cr_msg
);
1424 di
->di_min_ttl
= DDS_RF2589_DEFAULT_TTL
;
1427 di
->di_min_ttl
= (time_t)t
;
1431 case DDS_DEFAULTTTL
:
1432 if ( lutil_parse_time( c
->argv
[ 1 ], &t
) != 0 ) {
1433 snprintf( c
->cr_msg
, sizeof( c
->cr_msg
),
1434 "DDS unable to parse dds-default-ttl \"%s\"",
1436 Log2( LDAP_DEBUG_ANY
, LDAP_LEVEL_ERR
,
1437 "%s: %s.\n", c
->log
, c
->cr_msg
);
1441 if ( t
< 0 || t
> DDS_RF2589_MAX_TTL
) {
1442 snprintf( c
->cr_msg
, sizeof( c
->cr_msg
),
1443 "DDS invalid dds-default-ttl=%ld",
1445 Log2( LDAP_DEBUG_ANY
, LDAP_LEVEL_ERR
,
1446 "%s: %s.\n", c
->log
, c
->cr_msg
);
1451 di
->di_default_ttl
= DDS_RF2589_DEFAULT_TTL
;
1454 di
->di_default_ttl
= (time_t)t
;
1459 if ( lutil_parse_time( c
->argv
[ 1 ], &t
) != 0 ) {
1460 snprintf( c
->cr_msg
, sizeof( c
->cr_msg
),
1461 "DDS unable to parse dds-interval \"%s\"",
1463 Log2( LDAP_DEBUG_ANY
, LDAP_LEVEL_ERR
,
1464 "%s: %s.\n", c
->log
, c
->cr_msg
);
1469 snprintf( c
->cr_msg
, sizeof( c
->cr_msg
),
1470 "DDS invalid dds-interval=%ld",
1472 Log2( LDAP_DEBUG_ANY
, LDAP_LEVEL_ERR
,
1473 "%s: %s.\n", c
->log
, c
->cr_msg
);
1478 Log2( LDAP_DEBUG_ANY
, LDAP_LEVEL_NOTICE
,
1479 "%s: dds-interval=%lu may be too small.\n",
1483 di
->di_interval
= (time_t)t
;
1484 if ( di
->di_expire_task
) {
1485 ldap_pvt_thread_mutex_lock( &slapd_rq
.rq_mutex
);
1486 if ( ldap_pvt_runqueue_isrunning( &slapd_rq
, di
->di_expire_task
) ) {
1487 ldap_pvt_runqueue_stoptask( &slapd_rq
, di
->di_expire_task
);
1489 di
->di_expire_task
->interval
.tv_sec
= DDS_INTERVAL( di
);
1490 ldap_pvt_runqueue_resched( &slapd_rq
, di
->di_expire_task
, 0 );
1491 ldap_pvt_thread_mutex_unlock( &slapd_rq
.rq_mutex
);
1496 if ( lutil_parse_time( c
->argv
[ 1 ], &t
) != 0 ) {
1497 snprintf( c
->cr_msg
, sizeof( c
->cr_msg
),
1498 "DDS unable to parse dds-tolerance \"%s\"",
1500 Log2( LDAP_DEBUG_ANY
, LDAP_LEVEL_ERR
,
1501 "%s: %s.\n", c
->log
, c
->cr_msg
);
1505 if ( t
< 0 || t
> DDS_RF2589_MAX_TTL
) {
1506 snprintf( c
->cr_msg
, sizeof( c
->cr_msg
),
1507 "DDS invalid dds-tolerance=%ld",
1509 Log2( LDAP_DEBUG_ANY
, LDAP_LEVEL_ERR
,
1510 "%s: %s.\n", c
->log
, c
->cr_msg
);
1514 di
->di_tolerance
= (time_t)t
;
1517 case DDS_MAXDYNAMICOBJS
:
1518 if ( c
->value_int
< 0 ) {
1519 snprintf( c
->cr_msg
, sizeof( c
->cr_msg
),
1520 "DDS invalid dds-max-dynamicObjects=%d", c
->value_int
);
1521 Log2( LDAP_DEBUG_ANY
, LDAP_LEVEL_ERR
,
1522 "%s: %s.\n", c
->log
, c
->cr_msg
);
1525 di
->di_max_dynamicObjects
= c
->value_int
;
1541 slap_overinst
*on
= (slap_overinst
*)be
->bd_info
;
1543 BackendInfo
*bi
= on
->on_info
->oi_orig
;
1545 if ( SLAP_ISGLOBALOVERLAY( be
) ) {
1546 Log0( LDAP_DEBUG_ANY
, LDAP_LEVEL_ERR
,
1547 "DDS cannot be used as global overlay.\n" );
1551 /* check support for required functions */
1552 /* FIXME: some could be provided by other overlays in between */
1553 if ( bi
->bi_op_add
== NULL
/* object creation */
1554 || bi
->bi_op_delete
== NULL
/* object deletion */
1555 || bi
->bi_op_modify
== NULL
/* object refresh */
1556 || bi
->bi_op_search
== NULL
/* object expiration */
1557 || bi
->bi_entry_get_rw
== NULL
) /* object type/existence checking */
1559 Log1( LDAP_DEBUG_ANY
, LDAP_LEVEL_ERR
,
1560 "DDS backend \"%s\" does not provide "
1561 "required functionality.\n",
1566 di
= (dds_info_t
*)ch_calloc( 1, sizeof( dds_info_t
) );
1567 on
->on_bi
.bi_private
= di
;
1569 di
->di_max_ttl
= DDS_RF2589_DEFAULT_TTL
;
1570 di
->di_max_ttl
= DDS_RF2589_DEFAULT_TTL
;
1572 ldap_pvt_thread_mutex_init( &di
->di_mutex
);
1574 SLAP_DBFLAGS( be
) |= SLAP_DBFLAG_DYNAMIC
;
1579 /* adds dynamicSubtrees to root DSE */
1581 dds_entry_info( void *arg
, Entry
*e
)
1583 dds_info_t
*di
= (dds_info_t
*)arg
;
1585 attr_merge( e
, slap_schema
.si_ad_dynamicSubtrees
,
1586 di
->di_suffix
, di
->di_nsuffix
);
1591 /* callback that counts the returned entries, since the search
1592 * does not get to the point in slap_send_search_entries where
1593 * the actual count occurs */
1595 dds_count_cb( Operation
*op
, SlapReply
*rs
)
1597 int *nump
= (int *)op
->o_callback
->sc_private
;
1599 switch ( rs
->sr_type
) {
1615 /* count dynamic objects existing in the database at startup */
1617 dds_count( void *ctx
, BackendDB
*be
)
1619 slap_overinst
*on
= (slap_overinst
*)be
->bd_info
;
1620 dds_info_t
*di
= (dds_info_t
*)on
->on_bi
.bi_private
;
1622 Connection conn
= { 0 };
1623 OperationBuffer opbuf
;
1625 slap_callback sc
= { 0 };
1626 SlapReply rs
= { REP_RESULT
};
1631 connection_fake_init( &conn
, &opbuf
, ctx
);
1634 op
->o_tag
= LDAP_REQ_SEARCH
;
1635 memset( &op
->oq_search
, 0, sizeof( op
->oq_search
) );
1639 op
->o_req_dn
= op
->o_bd
->be_suffix
[ 0 ];
1640 op
->o_req_ndn
= op
->o_bd
->be_nsuffix
[ 0 ];
1642 op
->o_dn
= op
->o_bd
->be_rootdn
;
1643 op
->o_ndn
= op
->o_bd
->be_rootndn
;
1645 op
->ors_scope
= LDAP_SCOPE_SUBTREE
;
1646 op
->ors_tlimit
= SLAP_NO_LIMIT
;
1647 op
->ors_slimit
= SLAP_NO_LIMIT
;
1648 op
->ors_attrs
= slap_anlist_no_attrs
;
1650 op
->ors_filterstr
.bv_len
= STRLENOF( "(objectClass=" ")" )
1651 + slap_schema
.si_oc_dynamicObject
->soc_cname
.bv_len
;
1652 op
->ors_filterstr
.bv_val
= op
->o_tmpalloc( op
->ors_filterstr
.bv_len
+ 1, op
->o_tmpmemctx
);
1653 snprintf( op
->ors_filterstr
.bv_val
, op
->ors_filterstr
.bv_len
+ 1,
1655 slap_schema
.si_oc_dynamicObject
->soc_cname
.bv_val
);
1657 op
->ors_filter
= str2filter_x( op
, op
->ors_filterstr
.bv_val
);
1658 if ( op
->ors_filter
== NULL
) {
1659 rs
.sr_err
= LDAP_OTHER
;
1663 op
->o_callback
= &sc
;
1664 sc
.sc_response
= dds_count_cb
;
1665 sc
.sc_private
= &di
->di_num_dynamicObjects
;
1666 di
->di_num_dynamicObjects
= 0;
1668 op
->o_bd
->bd_info
= (BackendInfo
*)on
->on_info
;
1669 (void)op
->o_bd
->bd_info
->bi_op_search( op
, &rs
);
1670 op
->o_bd
->bd_info
= (BackendInfo
*)on
;
1673 op
->o_tmpfree( op
->ors_filterstr
.bv_val
, op
->o_tmpmemctx
);
1674 filter_free_x( op
, op
->ors_filter
);
1677 switch ( rs
.sr_err
) {
1679 Log1( LDAP_DEBUG_STATS
, LDAP_LEVEL_INFO
,
1680 "DDS non-expired=%d\n",
1681 di
->di_num_dynamicObjects
);
1684 case LDAP_NO_SUCH_OBJECT
:
1685 /* (ITS#5267) database not created yet? */
1686 rs
.sr_err
= LDAP_SUCCESS
;
1687 extra
= " (ignored)";
1691 Log2( LDAP_DEBUG_ANY
, LDAP_LEVEL_ERR
,
1692 "DDS non-expired objects lookup failed err=%d%s\n",
1705 slap_overinst
*on
= (slap_overinst
*)be
->bd_info
;
1706 dds_info_t
*di
= on
->on_bi
.bi_private
;
1708 void *thrctx
= ldap_pvt_thread_pool_context();
1710 if ( DDS_OFF( di
) ) {
1714 if ( SLAP_SINGLE_SHADOW( be
) ) {
1715 Log1( LDAP_DEBUG_ANY
, LDAP_LEVEL_ERR
,
1716 "DDS incompatible with shadow database \"%s\".\n",
1717 be
->be_suffix
[ 0 ].bv_val
);
1721 if ( di
->di_max_ttl
== 0 ) {
1722 di
->di_max_ttl
= DDS_RF2589_DEFAULT_TTL
;
1725 if ( di
->di_min_ttl
== 0 ) {
1726 di
->di_max_ttl
= DDS_RF2589_DEFAULT_TTL
;
1729 di
->di_suffix
= be
->be_suffix
;
1730 di
->di_nsuffix
= be
->be_nsuffix
;
1732 /* ... so that count, if required, is accurate */
1733 if ( di
->di_max_dynamicObjects
> 0 ) {
1734 /* force deletion of expired entries... */
1735 be
->bd_info
= (BackendInfo
*)on
->on_info
;
1736 rc
= dds_expire( thrctx
, di
);
1737 be
->bd_info
= (BackendInfo
*)on
;
1738 if ( rc
!= LDAP_SUCCESS
) {
1743 rc
= dds_count( thrctx
, be
);
1744 if ( rc
!= LDAP_SUCCESS
) {
1750 /* start expire task */
1751 ldap_pvt_thread_mutex_lock( &slapd_rq
.rq_mutex
);
1752 di
->di_expire_task
= ldap_pvt_runqueue_insert( &slapd_rq
,
1754 dds_expire_fn
, di
, "dds_expire_fn",
1755 be
->be_suffix
[ 0 ].bv_val
);
1756 ldap_pvt_thread_mutex_unlock( &slapd_rq
.rq_mutex
);
1758 /* register dinamicSubtrees root DSE info support */
1759 rc
= entry_info_register( dds_entry_info
, (void *)di
);
1771 slap_overinst
*on
= (slap_overinst
*)be
->bd_info
;
1772 dds_info_t
*di
= on
->on_bi
.bi_private
;
1774 /* stop expire task */
1775 if ( di
&& di
->di_expire_task
) {
1776 ldap_pvt_thread_mutex_lock( &slapd_rq
.rq_mutex
);
1777 if ( ldap_pvt_runqueue_isrunning( &slapd_rq
, di
->di_expire_task
) ) {
1778 ldap_pvt_runqueue_stoptask( &slapd_rq
, di
->di_expire_task
);
1780 ldap_pvt_runqueue_remove( &slapd_rq
, di
->di_expire_task
);
1781 ldap_pvt_thread_mutex_unlock( &slapd_rq
.rq_mutex
);
1784 (void)entry_info_unregister( dds_entry_info
, (void *)di
);
1794 slap_overinst
*on
= (slap_overinst
*)be
->bd_info
;
1795 dds_info_t
*di
= on
->on_bi
.bi_private
;
1798 ldap_pvt_thread_mutex_destroy( &di
->di_mutex
);
1811 BackendDB
*bd
= op
->o_bd
;
1813 rs
->sr_err
= slap_parse_refresh( op
->ore_reqdata
, &op
->o_req_ndn
, NULL
,
1814 &rs
->sr_text
, op
->o_tmpmemctx
);
1815 if ( rs
->sr_err
!= LDAP_SUCCESS
) {
1819 Log2( LDAP_DEBUG_STATS
, LDAP_LEVEL_INFO
,
1820 "%s REFRESH dn=\"%s\"\n",
1821 op
->o_log_prefix
, op
->o_req_ndn
.bv_val
);
1822 op
->o_req_dn
= op
->o_req_ndn
;
1824 op
->o_bd
= select_backend( &op
->o_req_ndn
, 0 );
1825 if ( !SLAP_DYNAMIC( op
->o_bd
) ) {
1826 send_ldap_error( op
, rs
, LDAP_UNAVAILABLE_CRITICAL_EXTENSION
,
1827 "backend does not support dynamic directory services" );
1831 rs
->sr_err
= backend_check_restrictions( op
, rs
,
1832 (struct berval
*)&slap_EXOP_REFRESH
);
1833 if ( rs
->sr_err
!= LDAP_SUCCESS
) {
1837 if ( op
->o_bd
->be_extended
== NULL
) {
1838 send_ldap_error( op
, rs
, LDAP_UNAVAILABLE_CRITICAL_EXTENSION
,
1839 "backend does not support extended operations" );
1843 op
->o_bd
->be_extended( op
, rs
);
1846 if ( !BER_BVISNULL( &op
->o_req_ndn
) ) {
1847 op
->o_tmpfree( op
->o_req_ndn
.bv_val
, op
->o_tmpmemctx
);
1848 BER_BVZERO( &op
->o_req_ndn
);
1849 BER_BVZERO( &op
->o_req_dn
);
1856 static slap_overinst dds
;
1858 static int do_not_load_exop
;
1859 static int do_not_replace_exop
;
1860 static int do_not_load_schema
;
1862 #if SLAPD_OVER_DDS == SLAPD_MOD_DYNAMIC
1864 #endif /* SLAPD_OVER_DDS == SLAPD_MOD_DYNAMIC */
1871 /* Make sure we don't exceed the bits reserved for userland */
1872 config_check_userland( DDS_LAST
);
1874 if ( !do_not_load_schema
) {
1878 AttributeDescription
**ad
;
1880 { "( 1.3.6.1.4.1.4203.666.1.57 "
1881 "NAME ( 'entryExpireTimestamp' ) "
1882 "DESC 'RFC2589 OpenLDAP extension: expire time of a dynamic object, "
1883 "computed as now + entryTtl' "
1884 "EQUALITY generalizedTimeMatch "
1885 "ORDERING generalizedTimeOrderingMatch "
1886 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 "
1888 "NO-USER-MODIFICATION "
1889 "USAGE dSAOperation )",
1891 &ad_entryExpireTimestamp
},
1895 for ( i
= 0; s_at
[ i
].desc
!= NULL
; i
++ ) {
1896 code
= register_at( s_at
[ i
].desc
, s_at
[ i
].ad
, 0 );
1898 Debug( LDAP_DEBUG_ANY
,
1899 "dds_initialize: register_at failed\n", 0, 0, 0 );
1902 (*s_at
[ i
].ad
)->ad_type
->sat_flags
|= SLAP_AT_HIDE
;
1906 if ( !do_not_load_exop
) {
1907 rc
= load_extop2( (struct berval
*)&slap_EXOP_REFRESH
,
1908 SLAP_EXOP_WRITES
|SLAP_EXOP_HIDE
, slap_exop_refresh
,
1909 !do_not_replace_exop
);
1910 if ( rc
!= LDAP_SUCCESS
) {
1911 Log1( LDAP_DEBUG_ANY
, LDAP_LEVEL_ERR
,
1912 "DDS unable to register refresh exop: %d.\n",
1918 dds
.on_bi
.bi_type
= "dds";
1920 dds
.on_bi
.bi_db_init
= dds_db_init
;
1921 dds
.on_bi
.bi_db_open
= dds_db_open
;
1922 dds
.on_bi
.bi_db_close
= dds_db_close
;
1923 dds
.on_bi
.bi_db_destroy
= dds_db_destroy
;
1925 dds
.on_bi
.bi_op_add
= dds_op_add
;
1926 dds
.on_bi
.bi_op_delete
= dds_op_delete
;
1927 dds
.on_bi
.bi_op_modify
= dds_op_modify
;
1928 dds
.on_bi
.bi_op_modrdn
= dds_op_rename
;
1929 dds
.on_bi
.bi_extended
= dds_op_extended
;
1931 dds
.on_bi
.bi_cf_ocs
= dds_ocs
;
1933 rc
= config_register_schema( dds_cfg
, dds_ocs
);
1938 return overlay_register( &dds
);
1941 #if SLAPD_OVER_DDS == SLAPD_MOD_DYNAMIC
1943 init_module( int argc
, char *argv
[] )
1947 for ( i
= 0; i
< argc
; i
++ ) {
1948 char *arg
= argv
[ i
];
1951 if ( strncasecmp( arg
, "no-", STRLENOF( "no-" ) ) == 0 ) {
1952 arg
+= STRLENOF( "no-" );
1956 if ( strcasecmp( arg
, "exop" ) == 0 ) {
1957 do_not_load_exop
= no
;
1959 } else if ( strcasecmp( arg
, "replace" ) == 0 ) {
1960 do_not_replace_exop
= no
;
1962 } else if ( strcasecmp( arg
, "schema" ) == 0 ) {
1963 do_not_load_schema
= no
;
1966 Log2( LDAP_DEBUG_ANY
, LDAP_LEVEL_ERR
,
1967 "DDS unknown module arg[#%d]=\"%s\".\n",
1973 return dds_initialize();
1975 #endif /* SLAPD_OVER_DDS == SLAPD_MOD_DYNAMIC */
1977 #endif /* defined(SLAPD_OVER_DDS) */