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