]> git.sur5r.net Git - openldap/blob - servers/slapd/overlays/dds.c
ITS#5705
[openldap] / servers / slapd / overlays / dds.c
1 /* $OpenLDAP$ */
2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
3  *
4  * Copyright 2005-2008 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 };
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         int             rc;
138         char            *extra = "";
139
140         connection_fake_init( &conn, &opbuf, ctx );
141         op = &opbuf.ob_op;
142
143         op->o_tag = LDAP_REQ_SEARCH;
144         memset( &op->oq_search, 0, sizeof( op->oq_search ) );
145
146         op->o_bd = select_backend( &di->di_nsuffix[ 0 ], 0 );
147
148         op->o_req_dn = op->o_bd->be_suffix[ 0 ];
149         op->o_req_ndn = op->o_bd->be_nsuffix[ 0 ];
150
151         op->o_dn = op->o_bd->be_rootdn;
152         op->o_ndn = op->o_bd->be_rootndn;
153
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;
158
159         expire = slap_get_time() + di->di_tolerance;
160         ts.bv_val = tsbuf;
161         ts.bv_len = sizeof( tsbuf );
162         slap_timestamp( &expire, &ts );
163
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
167                 + ts.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 );
173
174         op->ors_filter = str2filter_x( op, op->ors_filterstr.bv_val );
175         if ( op->ors_filter == NULL ) {
176                 rs.sr_err = LDAP_OTHER;
177                 goto done_search;
178         }
179         
180         op->o_callback = &sc;
181         sc.sc_response = dds_expire_cb;
182         sc.sc_private = &dc;
183
184         (void)op->o_bd->bd_info->bi_op_search( op, &rs );
185
186 done_search:;
187         op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx );
188         filter_free_x( op, op->ors_filter, 1 );
189
190         rc = rs.sr_err;
191         switch ( rs.sr_err ) {
192         case LDAP_SUCCESS:
193                 break;
194
195         case LDAP_NO_SUCH_OBJECT:
196                 /* (ITS#5267) database not created yet? */
197                 rs.sr_err = LDAP_SUCCESS;
198                 extra = " (ignored)";
199                 /* fallthru */
200
201         default:
202                 Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
203                         "DDS expired objects lookup failed err=%d%s\n",
204                         rc, extra );
205                 goto done;
206         }
207
208         op->o_tag = LDAP_REQ_DELETE;
209         op->o_callback = &sc;
210         sc.sc_response = slap_null_cb;
211         sc.sc_private = NULL;
212
213         for ( ntotdeletes = 0, ndeletes = 1; dc.dc_ndnlist != NULL  && ndeletes > 0; ) {
214                 ndeletes = 0;
215
216                 for ( dep = &dc.dc_ndnlist; *dep != NULL; ) {
217                         de = *dep;
218
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 ) {
223                         case LDAP_SUCCESS:
224                                 Log1( LDAP_DEBUG_STATS, LDAP_LEVEL_INFO,
225                                         "DDS dn=\"%s\" expired.\n",
226                                         de->de_ndn.bv_val );
227                                 ndeletes++;
228                                 break;
229
230                         case LDAP_NOT_ALLOWED_ON_NONLEAF:
231                                 Log1( LDAP_DEBUG_ANY, LDAP_LEVEL_NOTICE,
232                                         "DDS dn=\"%s\" is non-leaf; "
233                                         "deferring.\n",
234                                         de->de_ndn.bv_val );
235                                 dep = &de->de_next;
236                                 de = NULL;
237                                 break;
238         
239                         default:
240                                 Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_NOTICE,
241                                         "DDS dn=\"%s\" err=%d; "
242                                         "deferring.\n",
243                                         de->de_ndn.bv_val, rs.sr_err );
244                                 break;
245                         }
246         
247                         if ( de != NULL ) {
248                                 *dep = de->de_next;
249                                 dep = &de->de_next;
250                                 op->o_tmpfree( de, op->o_tmpmemctx );
251                         }
252                 }
253
254                 ntotdeletes += ndeletes;
255         }
256
257         rs.sr_err = LDAP_SUCCESS;
258
259         Log1( LDAP_DEBUG_STATS, LDAP_LEVEL_INFO,
260                 "DDS expired=%d\n", ntotdeletes );
261
262 done:;
263         return rs.sr_err;
264 }
265
266 static void *
267 dds_expire_fn( void *ctx, void *arg )
268 {
269         struct re_s     *rtask = arg;
270         dds_info_t      *di = rtask->arg;
271
272         assert( di->di_expire_task == rtask );
273
274         (void)dds_expire( ctx, di );
275         
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 );
279         }
280         ldap_pvt_runqueue_resched( &slapd_rq, rtask, 0 );
281         ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
282
283         return NULL;
284 }
285
286 /* frees the callback */
287 static int
288 dds_freeit_cb( Operation *op, SlapReply *rs )
289 {
290         op->o_tmpfree( op->o_callback, op->o_tmpmemctx );
291         op->o_callback = NULL;
292
293         return SLAP_CB_CONTINUE;
294 }
295
296 /* updates counter - installed on add/delete only if required */
297 static int
298 dds_counter_cb( Operation *op, SlapReply *rs )
299 {
300         assert( rs->sr_type == REP_RESULT );
301
302         if ( rs->sr_err == LDAP_SUCCESS ) {
303                 dds_info_t      *di = op->o_callback->sc_private;
304
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--;
310                         break;
311
312                 case LDAP_REQ_ADD:
313                         assert( di->di_num_dynamicObjects < di->di_max_dynamicObjects );
314                         di->di_num_dynamicObjects++;
315                         break;
316
317                 default:
318                         assert( 0 );
319                 }
320                 ldap_pvt_thread_mutex_unlock( &di->di_mutex );
321         }
322
323         return dds_freeit_cb( op, rs );
324 }
325
326 static int
327 dds_op_add( Operation *op, SlapReply *rs )
328 {
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;
332
333         if ( DDS_OFF( di ) ) {
334                 return SLAP_CB_CONTINUE;
335         }
336
337         is_dynamicObject = is_entry_dynamicObject( op->ora_e );
338
339         /* FIXME: do not allow this right now, pending clarification */
340         if ( is_dynamicObject ) {
341                 rs->sr_err = LDAP_SUCCESS;
342
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";
346
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";
350                 }
351
352                 if ( rs->sr_err != LDAP_SUCCESS ) {
353                         op->o_bd->bd_info = (BackendInfo *)on->on_info;
354                         send_ldap_result( op, rs );
355                         return rs->sr_err;
356                 }
357         }
358
359         /* we don't allow dynamicObjects to have static subordinates */
360         if ( !dn_match( &op->o_req_ndn, &op->o_bd->be_nsuffix[ 0 ] ) ) {
361                 struct berval   p_ndn;
362                 Entry           *e = NULL;
363                 int             rc;
364                 BackendInfo     *bi = op->o_bd->bd_info;
365
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 ) )
377                                 {
378                                         rc = rs->sr_err = LDAP_NO_SUCH_OBJECT;
379                                         send_ldap_result( op, rs );
380
381                                 } else {
382                                         rc = rs->sr_err = LDAP_CONSTRAINT_VIOLATION;
383                                         send_ldap_error( op, rs, rc, "no static subordinate entries allowed for dynamicObject" );
384                                 }
385                         }
386
387                         be_entry_release_r( op, e );
388                         if ( rc != LDAP_SUCCESS ) {
389                                 return rc;
390                         }
391                 }
392                 op->o_bd->bd_info = bi;
393         }
394
395         /* handle dynamic object operational attr(s) */
396         if ( is_dynamicObject ) {
397                 time_t          ttl, expire;
398                 char            ttlbuf[STRLENOF("31557600") + 1];
399                 char            tsbuf[ LDAP_LUTIL_GENTIME_BUFSIZE ];
400                 struct berval   bv;
401
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 );
407                         if ( rs->sr_err ) {
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" );
411                                 return rs->sr_err;
412                         }
413                 }
414
415                 ttl = DDS_DEFAULT_TTL( di );
416
417                 /* assert because should be checked at configure */
418                 assert( ttl <= DDS_RF2589_MAX_TTL );
419
420                 bv.bv_val = ttlbuf;
421                 bv.bv_len = snprintf( ttlbuf, sizeof( ttlbuf ), "%ld", ttl );
422                 assert( bv.bv_len < sizeof( ttlbuf ) );
423
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 );
429
430                 expire = slap_get_time() + ttl;
431                 bv.bv_val = tsbuf;
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 );
436
437                 /* if required, install counter callback */
438                 if ( di->di_max_dynamicObjects > 0) {
439                         slap_callback   *sc;
440
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;
444                         sc->sc_private = di;
445                         sc->sc_next = op->o_callback;
446
447                         op->o_callback = sc;
448                 }
449         }
450
451         return SLAP_CB_CONTINUE;
452 }
453
454 static int
455 dds_op_delete( Operation *op, SlapReply *rs )
456 {
457         slap_overinst   *on = (slap_overinst *)op->o_bd->bd_info;
458         dds_info_t      *di = on->on_bi.bi_private;
459
460         /* if required, install counter callback */
461         if ( !DDS_OFF( di ) && di->di_max_dynamicObjects > 0 ) {
462                 Entry           *e = NULL;
463                 BackendInfo     *bi = op->o_bd->bd_info;
464
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 );
468
469                 /* FIXME: couldn't the entry be added before deletion? */
470                 if ( rs->sr_err == LDAP_SUCCESS && e != NULL ) {
471                         slap_callback   *sc;
472         
473                         be_entry_release_r( op, e );
474                         e = NULL;
475         
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;
479                         sc->sc_private = di;
480                         sc->sc_next = op->o_callback;
481         
482                         op->o_callback = sc;
483                 }
484                 op->o_bd->bd_info = bi;
485         }
486
487         return SLAP_CB_CONTINUE;
488 }
489
490 static int
491 dds_op_modify( Operation *op, SlapReply *rs )
492 {
493         slap_overinst   *on = (slap_overinst *)op->o_bd->bd_info;
494         dds_info_t      *di = (dds_info_t *)on->on_bi.bi_private;
495         Modifications   *mod;
496         Entry           *e = NULL;
497         BackendInfo     *bi = op->o_bd->bd_info;
498         int             was_dynamicObject = 0,
499                         is_dynamicObject = 0;
500         struct berval   bv_entryTtl = BER_BVNULL;
501         time_t          entryTtl = 0;
502         char            textbuf[ SLAP_TEXT_BUFLEN ];
503
504         if ( DDS_OFF( di ) ) {
505                 return SLAP_CB_CONTINUE;
506         }
507
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;
517
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 );
523
524                 /* the value of the entryTtl is saved for later checks */
525                 if ( a != NULL ) {
526                         unsigned long   ttl;
527                         int             rc;
528
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 );
533                         assert( rc == 0 );
534                         entryTtl = (time_t)ttl;
535                 }
536
537                 be_entry_release_r( op, e );
538                 e = NULL;
539                 was_dynamicObject = is_dynamicObject = 1;
540         }
541         op->o_bd->bd_info = bi;
542
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 ) {
546                         int             i;
547                         ObjectClass     *oc;
548
549                         switch ( mod->sml_op ) {
550                         case LDAP_MOD_DELETE:
551                                 if ( mod->sml_values == NULL ) {
552                                         is_dynamicObject = 0;
553                                         break;
554                                 }
555         
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;
560                                                 break;
561                                         }
562                                 }
563         
564                                 break;
565         
566                         case LDAP_MOD_REPLACE:
567                                 if ( mod->sml_values == NULL ) {
568                                         is_dynamicObject = 0;
569                                         break;
570                                 }
571                                 /* fallthru */
572         
573                         case LDAP_MOD_ADD:
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;
578                                                 break;
579                                         }
580                                 }
581                                 break;
582                         }
583
584                 } else if ( mod->sml_desc == slap_schema.si_ad_entryTtl ) {
585                         unsigned long   ttl;
586                         int             rc;
587
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 ] ) )
593                                         {
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;
598
599                                                 } else {
600                                                         rs->sr_err = LDAP_NO_SUCH_ATTRIBUTE;
601                                                 }
602                                                 goto done;
603                                         }
604                                 }
605                                 bv_entryTtl.bv_len = 0;
606                                 entryTtl = -1;
607                                 break;
608
609                         case LDAP_MOD_REPLACE:
610                                 bv_entryTtl.bv_len = 0;
611                                 entryTtl = -1;
612                                 /* fallthru */
613
614                         case SLAP_MOD_SOFTADD: /* FIXME? */
615                         case LDAP_MOD_ADD:
616                                 assert( mod->sml_values != NULL );
617                                 assert( BER_BVISNULL( &mod->sml_values[ 1 ] ) );
618
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;
624
625                                         } else {
626                                                 rs->sr_text = "attribute 'entryTtl' cannot have multiple values";
627                                                 rs->sr_err = LDAP_CONSTRAINT_VIOLATION;
628                                         }
629                                         goto done;
630                                 }
631
632                                 rc = lutil_atoul( &ttl, mod->sml_values[ 0 ].bv_val );
633                                 assert( rc == 0 );
634                                 if ( ttl > DDS_RF2589_MAX_TTL ) {
635                                         rs->sr_err = LDAP_PROTOCOL_ERROR;
636                                         rs->sr_text = "invalid time-to-live for dynamicObject";
637                                         goto done;
638                                 }
639
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";
646                                         goto done;
647                                 }
648
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';
653                                 break;
654
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;
661
662                                         } else {
663                                                 rs->sr_err = LDAP_NO_SUCH_ATTRIBUTE;
664                                                 rs->sr_text = "modify/increment: entryTtl: no such attribute";
665                                         }
666                                         goto done;
667                                 }
668
669                                 entryTtl++;
670                                 if ( entryTtl > DDS_RF2589_MAX_TTL ) {
671                                         rs->sr_err = LDAP_PROTOCOL_ERROR;
672                                         rs->sr_text = "invalid time-to-live for dynamicObject";
673
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";
680                                 }
681
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 ) {
686                                                 rs->sr_text = NULL;
687                                                 rs->sr_err = LDAP_NO_SUCH_OBJECT;
688
689                                         }
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 the Relax control 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_relax( 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                         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 ) {
768                                         rs->sr_text = NULL;
769                                         rs->sr_err = LDAP_NO_SUCH_OBJECT;
770                                 }
771                         }
772                 }
773         }
774
775         if ( rs->sr_err == LDAP_SUCCESS && entryTtl != 0 ) {
776                 Modifications   *tmpmod = NULL, **modp;
777
778                 for ( modp = &op->orm_modlist; *modp; modp = &(*modp)->sml_next )
779                         ;
780         
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;
785
786                 *modp = tmpmod;
787
788                 if ( entryTtl == -1 ) {
789                         /* delete entryExpireTimestamp */
790                         tmpmod->sml_op = LDAP_MOD_DELETE;
791
792                 } else {
793                         time_t          expire;
794                         char            tsbuf[ LDAP_LUTIL_GENTIME_BUFSIZE ];
795                         struct berval   bv;
796
797                         /* keep entryExpireTimestamp consistent
798                          * with entryTtl */
799                         expire = slap_get_time() + entryTtl;
800                         bv.bv_val = tsbuf;
801                         bv.bv_len = sizeof( tsbuf );
802                         slap_timestamp( &expire, &bv );
803
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;
808                 }
809         }
810
811         if ( rs->sr_err ) {
812                 op->o_bd->bd_info = (BackendInfo *)on->on_info;
813                 send_ldap_result( op, rs );
814                 return rs->sr_err;
815         }
816
817         return SLAP_CB_CONTINUE;
818 }
819
820 static int
821 dds_op_rename( Operation *op, SlapReply *rs )
822 {
823         slap_overinst   *on = (slap_overinst *)op->o_bd->bd_info;
824         dds_info_t      *di = on->on_bi.bi_private;
825
826         if ( DDS_OFF( di ) ) {
827                 return SLAP_CB_CONTINUE;
828         }
829
830         /* we don't allow dynamicObjects to have static subordinates */
831         if ( op->orr_nnewSup != NULL ) {
832                 Entry           *e = NULL;
833                 BackendInfo     *bi = op->o_bd->bd_info;
834                 int             is_dynamicObject = 0,
835                                 rc;
836
837                 rs->sr_err = LDAP_SUCCESS;
838
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 );
844                         e = NULL;
845                         is_dynamicObject = 1;
846                 }
847
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 ) )
857                                 {
858                                         rs->sr_err = LDAP_NO_SUCH_OBJECT;
859                                         send_ldap_result( op, rs );
860
861                                 } else {
862                                         send_ldap_error( op, rs, LDAP_CONSTRAINT_VIOLATION,
863                                                 "static entry cannot have dynamicObject as newSuperior" );
864                                 }
865                         }
866                         be_entry_release_r( op, e );
867                 }
868                 op->o_bd->bd_info = bi;
869                 if ( rs->sr_err != LDAP_SUCCESS ) {
870                         return rs->sr_err;
871                 }
872         }
873
874         return SLAP_CB_CONTINUE;
875 }
876
877 static int
878 slap_parse_refresh(
879         struct berval   *in,
880         struct berval   *ndn,
881         time_t          *ttl,
882         const char      **text,
883         void            *ctx )
884 {
885         int                     rc = LDAP_SUCCESS;
886         ber_tag_t               tag;
887         ber_len_t               len = -1;
888         BerElementBuffer        berbuf;
889         BerElement              *ber = (BerElement *)&berbuf;
890         struct berval           reqdata = BER_BVNULL;
891         int                     tmp;
892
893         *text = NULL;
894
895         if ( ndn ) {
896                 BER_BVZERO( ndn );
897         }
898
899         if ( in == NULL || in->bv_len == 0 ) {
900                 *text = "empty request data field in refresh exop";
901                 return LDAP_PROTOCOL_ERROR;
902         }
903
904         ber_dupbv_x( &reqdata, in, ctx );
905
906         /* ber_init2 uses reqdata directly, doesn't allocate new buffers */
907         ber_init2( ber, &reqdata, 0 );
908
909         tag = ber_scanf( ber, "{" /*}*/ );
910
911         if ( tag == LBER_ERROR ) {
912                 Log0( LDAP_DEBUG_TRACE, LDAP_LEVEL_ERR,
913                         "slap_parse_refresh: decoding error.\n" );
914                 goto decoding_error;
915         }
916
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" );
921                 goto decoding_error;
922         }
923
924         if ( ndn ) {
925                 struct berval   dn;
926
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" );
931                         goto decoding_error;
932                 }
933
934                 rc = dnNormalize( 0, NULL, NULL, &dn, ndn, ctx );
935                 if ( rc != LDAP_SUCCESS ) {
936                         *text = "invalid DN in refresh exop request data";
937                         goto done;
938                 }
939
940         } else {
941                 tag = ber_scanf( ber, "x" /* "m" */ );
942                 if ( tag == LBER_DEFAULT ) {
943                         goto decoding_error;
944                 }
945         }
946
947         tag = ber_peek_tag( ber, &len );
948
949         if ( tag != LDAP_TAG_EXOP_REFRESH_REQ_TTL ) {
950                 Log0( LDAP_DEBUG_TRACE, LDAP_LEVEL_ERR,
951                         "slap_parse_refresh: decoding error.\n" );
952                 goto decoding_error;
953         }
954
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" );
959                 goto decoding_error;
960         }
961
962         if ( ttl ) {
963                 *ttl = tmp;
964         }
965
966         tag = ber_peek_tag( ber, &len );
967
968         if ( tag != LBER_DEFAULT || len != 0 ) {
969 decoding_error:;
970                 Log1( LDAP_DEBUG_TRACE, LDAP_LEVEL_ERR,
971                         "slap_parse_refresh: decoding error, len=%ld\n",
972                         (long)len );
973                 rc = LDAP_PROTOCOL_ERROR;
974                 *text = "data decoding error";
975
976 done:;
977                 if ( ndn && !BER_BVISNULL( ndn ) ) {
978                         slap_sl_free( ndn->bv_val, ctx );
979                         BER_BVZERO( ndn );
980                 }
981         }
982
983         if ( !BER_BVISNULL( &reqdata ) ) {
984                 ber_memfree_x( reqdata.bv_val, ctx );
985         }
986
987         return rc;
988 }
989
990 static int
991 dds_op_extended( Operation *op, SlapReply *rs )
992 {
993         slap_overinst   *on = (slap_overinst *)op->o_bd->bd_info;
994         dds_info_t      *di = on->on_bi.bi_private;
995
996         if ( DDS_OFF( di ) ) {
997                 return SLAP_CB_CONTINUE;
998         }
999
1000         if ( bvmatch( &op->ore_reqoid, &slap_EXOP_REFRESH ) ) {
1001                 Entry           *e = NULL;
1002                 time_t          ttl;
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];
1010
1011                 rs->sr_err = slap_parse_refresh( op->ore_reqdata, NULL, &ttl,
1012                         &rs->sr_text, NULL );
1013                 assert( rs->sr_err == LDAP_SUCCESS );
1014
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";
1018                         return rs->sr_err;
1019                 }
1020
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";
1027                         return rs->sr_err;
1028                 }
1029
1030                 if ( di->di_min_ttl && ttl < di->di_min_ttl ) {
1031                         ttl = di->di_min_ttl;
1032                 }
1033
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; 
1039
1040                         if ( defref != NULL ) {
1041                                 rs->sr_ref = referral_rewrite( op->o_bd->be_update_refs,
1042                                         NULL, NULL, LDAP_SCOPE_DEFAULT );
1043                                 if ( rs->sr_ref ) {
1044                                         rs->sr_flags |= REP_REF_MUSTBEFREED;
1045                                 } else {
1046                                         rs->sr_ref = defref;
1047                                 }
1048                                 rs->sr_err = LDAP_REFERRAL;
1049
1050                         } else {
1051                                 rs->sr_text = "shadow context; no update referral";
1052                                 rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
1053                         }
1054
1055                         return rs->sr_err;
1056                 }
1057
1058                 assert( !BER_BVISNULL( &op->o_req_ndn ) );
1059
1060
1061
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 ) )
1075                                 {
1076                                         rs->sr_err = LDAP_NO_SUCH_OBJECT;
1077
1078                                 } else {
1079                                         rs->sr_err = LDAP_OBJECT_CLASS_VIOLATION;
1080                                         rs->sr_text = "refresh operation only applies to dynamic objects";
1081                                 }
1082                                 be_entry_release_r( op, e );
1083
1084                         } else {
1085                                 rs->sr_err = LDAP_NO_SUCH_OBJECT;
1086                         }
1087                         return rs->sr_err;
1088
1089                 } else if ( e != NULL ) {
1090                         be_entry_release_r( op, e );
1091                 }
1092
1093                 /* we require manage privileges on the entryTtl,
1094                  * and fake a Relax control */
1095                 op2.o_tag = LDAP_REQ_MODIFY;
1096                 op2.o_bd = &db;
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;
1102
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 ] );
1111
1112                 /* the entryExpireTimestamp is added by modify */
1113                 rs->sr_err = op2.o_bd->be_modify( &op2, &rs2 );
1114
1115                 if ( ttlmod.sml_next != NULL ) {
1116                         slap_mods_free( ttlmod.sml_next, 1 );
1117                 }
1118
1119                 if ( rs->sr_err == LDAP_SUCCESS ) {
1120                         int                     rc;
1121                         BerElementBuffer        berbuf;
1122                         BerElement              *ber = (BerElement *)&berbuf;
1123
1124                         if ( rs->sr_err == LDAP_SUCCESS ) {
1125                                 ber_init_w_nullc( ber, LBER_USE_DER );
1126
1127                                 rc = ber_printf( ber, "{tiN}", LDAP_TAG_EXOP_REFRESH_RES_TTL, (int)ttl );
1128
1129                                 if ( rc < 0 ) {
1130                                         rs->sr_err = LDAP_OTHER;
1131                                         rs->sr_text = "internal error";
1132
1133                                 } else {
1134                                         (void)ber_flatten( ber, &rs->sr_rspdata );
1135                                         rs->sr_rspoid = ch_strdup( slap_EXOP_REFRESH.bv_val );
1136
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 );
1140                                 }
1141
1142                                 ber_free_buf( ber );
1143                         }
1144                 }
1145
1146                 return rs->sr_err;
1147         }
1148
1149         return SLAP_CB_CONTINUE;
1150 }
1151
1152 enum {
1153         DDS_STATE = 1,
1154         DDS_MAXTTL,
1155         DDS_MINTTL,
1156         DDS_DEFAULTTTL,
1157         DDS_INTERVAL,
1158         DDS_TOLERANCE,
1159         DDS_MAXDYNAMICOBJS,
1160
1161         DDS_LAST
1162 };
1163
1164 static ConfigDriver dds_cfgen;
1165 #if 0
1166 static ConfigLDAPadd dds_ldadd;
1167 static ConfigCfAdd dds_cfadd;
1168 #endif
1169
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 }
1216 };
1217
1218 static ConfigOCs dds_ocs[] = {
1219         { "( OLcfgOvOc:9.1 "
1220                 "NAME 'olcDDSConfig' "
1221                 "DESC 'RFC2589 Dynamic directory services configuration' "
1222                 "SUP olcOverlayConfig "
1223                 "MAY ( "
1224                         "olcDDSstate "
1225                         "$ olcDDSmaxTtl "
1226                         "$ olcDDSminTtl "
1227                         "$ olcDDSdefaultTtl "
1228                         "$ olcDDSinterval "
1229                         "$ olcDDStolerance "
1230                         "$ olcDDSmaxDynamicObjects "
1231                 " ) "
1232                 ")", Cft_Overlay, dds_cfg, NULL, NULL /* dds_cfadd */ },
1233         { NULL, 0, NULL }
1234 };
1235
1236 #if 0
1237 static int
1238 dds_ldadd( CfEntryInfo *p, Entry *e, ConfigArgs *ca )
1239 {
1240         return LDAP_SUCCESS;
1241 }
1242
1243 static int
1244 dds_cfadd( Operation *op, SlapReply *rs, Entry *p, ConfigArgs *ca )
1245 {
1246         return 0;
1247 }
1248 #endif
1249
1250 static int
1251 dds_cfgen( ConfigArgs *c )
1252 {
1253         slap_overinst   *on = (slap_overinst *)c->bi;
1254         dds_info_t      *di = on->on_bi.bi_private;
1255         int             rc = 0;
1256         unsigned long   t;
1257
1258
1259         if ( c->op == SLAP_CONFIG_EMIT ) {
1260                 char            buf[ SLAP_TEXT_BUFLEN ];
1261                 struct berval   bv;
1262
1263                 switch( c->type ) {
1264                 case DDS_STATE: 
1265                         c->value_int = !DDS_OFF( di );
1266                         break;
1267
1268                 case DDS_MAXTTL: 
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 );
1272                         break;
1273
1274                 case DDS_MINTTL:
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 );
1279
1280                         } else {
1281                                 rc = 1;
1282                         }
1283                         break;
1284
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 );
1290
1291                         } else {
1292                                 rc = 1;
1293                         }
1294                         break;
1295
1296                 case DDS_INTERVAL:
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 );
1301
1302                         } else {
1303                                 rc = 1;
1304                         }
1305                         break;
1306
1307                 case DDS_TOLERANCE:
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 );
1312
1313                         } else {
1314                                 rc = 1;
1315                         }
1316                         break;
1317
1318                 case DDS_MAXDYNAMICOBJS:
1319                         if ( di->di_max_dynamicObjects > 0 ) {
1320                                 c->value_int = di->di_max_dynamicObjects;
1321
1322                         } else {
1323                                 rc = 1;
1324                         }
1325                         break;
1326
1327                 default:
1328                         rc = 1;
1329                         break;
1330                 }
1331
1332                 return rc;
1333
1334         } else if ( c->op == LDAP_MOD_DELETE ) {
1335                 switch( c->type ) {
1336                 case DDS_STATE:
1337                         di->di_flags &= ~DDS_FOFF;
1338                         break;
1339
1340                 case DDS_MAXTTL:
1341                         di->di_min_ttl = DDS_RF2589_DEFAULT_TTL;
1342                         break;
1343
1344                 case DDS_MINTTL:
1345                         di->di_min_ttl = 0;
1346                         break;
1347
1348                 case DDS_DEFAULTTTL:
1349                         di->di_default_ttl = 0;
1350                         break;
1351
1352                 case DDS_INTERVAL:
1353                         di->di_interval = 0;
1354                         break;
1355
1356                 case DDS_TOLERANCE:
1357                         di->di_tolerance = 0;
1358                         break;
1359
1360                 case DDS_MAXDYNAMICOBJS:
1361                         di->di_max_dynamicObjects = 0;
1362                         break;
1363
1364                 default:
1365                         rc = 1;
1366                         break;
1367                 }
1368
1369                 return rc;
1370         }
1371
1372         switch ( c->type ) {
1373         case DDS_STATE:
1374                 if ( c->value_int ) {
1375                         di->di_flags &= ~DDS_FOFF;
1376
1377                 } else {
1378                         di->di_flags |= DDS_FOFF;
1379                 }
1380                 break;
1381
1382         case DDS_MAXTTL:
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\"",
1386                                 c->argv[ 1 ] );
1387                         Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1388                                 "%s: %s.\n", c->log, c->cr_msg );
1389                         return 1;
1390                 }
1391
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 );
1398                         return 1;
1399                 }
1400
1401                 di->di_max_ttl = (time_t)t;
1402                 break;
1403
1404         case DDS_MINTTL:
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\"",
1408                                 c->argv[ 1 ] );
1409                         Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1410                                 "%s: %s.\n", c->log, c->cr_msg );
1411                         return 1;
1412                 }
1413
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",
1417                                 t );
1418                         Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1419                                 "%s: %s.\n", c->log, c->cr_msg );
1420                         return 1;
1421                 }
1422
1423                 if ( t == 0 ) {
1424                         di->di_min_ttl = DDS_RF2589_DEFAULT_TTL;
1425
1426                 } else {
1427                         di->di_min_ttl = (time_t)t;
1428                 }
1429                 break;
1430
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\"",
1435                                 c->argv[ 1 ] );
1436                         Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1437                                 "%s: %s.\n", c->log, c->cr_msg );
1438                         return 1;
1439                 }
1440
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",
1444                                 t );
1445                         Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1446                                 "%s: %s.\n", c->log, c->cr_msg );
1447                         return 1;
1448                 }
1449
1450                 if ( t == 0 ) {
1451                         di->di_default_ttl = DDS_RF2589_DEFAULT_TTL;
1452
1453                 } else {
1454                         di->di_default_ttl = (time_t)t;
1455                 }
1456                 break;
1457
1458         case DDS_INTERVAL:
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\"",
1462                                 c->argv[ 1 ] );
1463                         Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1464                                 "%s: %s.\n", c->log, c->cr_msg );
1465                         return 1;
1466                 }
1467
1468                 if ( t <= 0 ) {
1469                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
1470                                 "DDS invalid dds-interval=%ld",
1471                                 t );
1472                         Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1473                                 "%s: %s.\n", c->log, c->cr_msg );
1474                         return 1;
1475                 }
1476
1477                 if ( t < 60 ) {
1478                         Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_NOTICE,
1479                                 "%s: dds-interval=%lu may be too small.\n",
1480                                 c->log, t );
1481                 }
1482
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 );
1488                         }
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 );
1492                 }
1493                 break;
1494
1495         case DDS_TOLERANCE:
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\"",
1499                                 c->argv[ 1 ] );
1500                         Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1501                                 "%s: %s.\n", c->log, c->cr_msg );
1502                         return 1;
1503                 }
1504
1505                 if ( t < 0 || t > DDS_RF2589_MAX_TTL ) {
1506                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
1507                                 "DDS invalid dds-tolerance=%ld",
1508                                 t );
1509                         Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1510                                 "%s: %s.\n", c->log, c->cr_msg );
1511                         return 1;
1512                 }
1513
1514                 di->di_tolerance = (time_t)t;
1515                 break;
1516
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 );
1523                         return 1;
1524                 }
1525                 di->di_max_dynamicObjects = c->value_int;
1526                 break;
1527
1528         default:
1529                 rc = 1;
1530                 break;
1531         }
1532
1533         return rc;
1534 }
1535
1536 static int
1537 dds_db_init(
1538         BackendDB       *be,
1539         ConfigReply     *cr)
1540 {
1541         slap_overinst   *on = (slap_overinst *)be->bd_info;
1542         dds_info_t      *di;
1543         BackendInfo     *bi = on->on_info->oi_orig;
1544
1545         if ( SLAP_ISGLOBALOVERLAY( be ) ) {
1546                 Log0( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1547                         "DDS cannot be used as global overlay.\n" );
1548                 return 1;
1549         }
1550
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 */
1558         {
1559                 Log1( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1560                         "DDS backend \"%s\" does not provide "
1561                         "required functionality.\n",
1562                         bi->bi_type );
1563                 return 1;
1564         }
1565
1566         di = (dds_info_t *)ch_calloc( 1, sizeof( dds_info_t ) );
1567         on->on_bi.bi_private = di;
1568
1569         di->di_max_ttl = DDS_RF2589_DEFAULT_TTL;
1570         di->di_max_ttl = DDS_RF2589_DEFAULT_TTL;
1571
1572         ldap_pvt_thread_mutex_init( &di->di_mutex );
1573
1574         SLAP_DBFLAGS( be ) |= SLAP_DBFLAG_DYNAMIC;
1575
1576         return 0;
1577 }
1578
1579 /* adds dynamicSubtrees to root DSE */
1580 static int
1581 dds_entry_info( void *arg, Entry *e )
1582 {
1583         dds_info_t      *di = (dds_info_t *)arg;
1584
1585         attr_merge( e, slap_schema.si_ad_dynamicSubtrees,
1586                 di->di_suffix, di->di_nsuffix );
1587
1588         return 0;
1589 }
1590
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 */
1594 static int
1595 dds_count_cb( Operation *op, SlapReply *rs )
1596 {
1597         int     *nump = (int *)op->o_callback->sc_private;
1598
1599         switch ( rs->sr_type ) {
1600         case REP_SEARCH:
1601                 (*nump)++;
1602                 break;
1603
1604         case REP_SEARCHREF:
1605         case REP_RESULT:
1606                 break;
1607
1608         default:
1609                 assert( 0 );
1610         }
1611
1612         return 0;
1613 }
1614
1615 /* count dynamic objects existing in the database at startup */
1616 static int
1617 dds_count( void *ctx, BackendDB *be )
1618 {
1619         slap_overinst   *on = (slap_overinst *)be->bd_info;
1620         dds_info_t      *di = (dds_info_t *)on->on_bi.bi_private;
1621         
1622         Connection      conn = { 0 };
1623         OperationBuffer opbuf;
1624         Operation       *op;
1625         slap_callback   sc = { 0 };
1626         SlapReply       rs = { REP_RESULT };
1627
1628         int             rc;
1629         char            *extra = "";
1630
1631         connection_fake_init( &conn, &opbuf, ctx );
1632         op = &opbuf.ob_op;
1633
1634         op->o_tag = LDAP_REQ_SEARCH;
1635         memset( &op->oq_search, 0, sizeof( op->oq_search ) );
1636
1637         op->o_bd = be;
1638
1639         op->o_req_dn = op->o_bd->be_suffix[ 0 ];
1640         op->o_req_ndn = op->o_bd->be_nsuffix[ 0 ];
1641
1642         op->o_dn = op->o_bd->be_rootdn;
1643         op->o_ndn = op->o_bd->be_rootndn;
1644
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;
1649
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,
1654                 "(objectClass=%s)",
1655                 slap_schema.si_oc_dynamicObject->soc_cname.bv_val );
1656
1657         op->ors_filter = str2filter_x( op, op->ors_filterstr.bv_val );
1658         if ( op->ors_filter == NULL ) {
1659                 rs.sr_err = LDAP_OTHER;
1660                 goto done_search;
1661         }
1662         
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;
1667
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;
1671
1672 done_search:;
1673         op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx );
1674         filter_free_x( op, op->ors_filter, 1 );
1675
1676         rc = rs.sr_err;
1677         switch ( rs.sr_err ) {
1678         case LDAP_SUCCESS:
1679                 Log1( LDAP_DEBUG_STATS, LDAP_LEVEL_INFO,
1680                         "DDS non-expired=%d\n",
1681                         di->di_num_dynamicObjects );
1682                 break;
1683
1684         case LDAP_NO_SUCH_OBJECT:
1685                 /* (ITS#5267) database not created yet? */
1686                 rs.sr_err = LDAP_SUCCESS;
1687                 extra = " (ignored)";
1688                 /* fallthru */
1689
1690         default:
1691                 Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1692                         "DDS non-expired objects lookup failed err=%d%s\n",
1693                         rc, extra );
1694                 break;
1695         }
1696
1697         return rs.sr_err;
1698 }
1699
1700 static int
1701 dds_db_open(
1702         BackendDB       *be,
1703         ConfigReply     *cr )
1704 {
1705         slap_overinst   *on = (slap_overinst *)be->bd_info;
1706         dds_info_t      *di = on->on_bi.bi_private;
1707         int             rc = 0;
1708         void            *thrctx = ldap_pvt_thread_pool_context();
1709
1710         if ( DDS_OFF( di ) ) {
1711                 goto done;
1712         }
1713
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 );
1718                 return 1;
1719         }
1720
1721         if ( di->di_max_ttl == 0 ) {
1722                 di->di_max_ttl = DDS_RF2589_DEFAULT_TTL;
1723         }
1724
1725         if ( di->di_min_ttl == 0 ) {
1726                 di->di_max_ttl = DDS_RF2589_DEFAULT_TTL;
1727         }
1728
1729         di->di_suffix = be->be_suffix;
1730         di->di_nsuffix = be->be_nsuffix;
1731
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 ) {
1739                         rc = 1;
1740                         goto done;
1741                 }
1742
1743                 rc = dds_count( thrctx, be );
1744                 if ( rc != LDAP_SUCCESS ) {
1745                         rc = 1;
1746                         goto done;
1747                 }
1748         }
1749
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,
1753                 DDS_INTERVAL( di ),
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 );
1757
1758         /* register dinamicSubtrees root DSE info support */
1759         rc = entry_info_register( dds_entry_info, (void *)di );
1760
1761 done:;
1762
1763         return rc;
1764 }
1765
1766 static int
1767 dds_db_close(
1768         BackendDB       *be,
1769         ConfigReply     *cr )
1770 {
1771         slap_overinst   *on = (slap_overinst *)be->bd_info;
1772         dds_info_t      *di = on->on_bi.bi_private;
1773
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 );
1779                 }
1780                 ldap_pvt_runqueue_remove( &slapd_rq, di->di_expire_task );
1781                 ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
1782         }
1783
1784         (void)entry_info_unregister( dds_entry_info, (void *)di );
1785
1786         return 0;
1787 }
1788
1789 static int
1790 dds_db_destroy(
1791         BackendDB       *be,
1792         ConfigReply     *cr )
1793 {
1794         slap_overinst   *on = (slap_overinst *)be->bd_info;
1795         dds_info_t      *di = on->on_bi.bi_private;
1796
1797         if ( di != NULL ) {
1798                 ldap_pvt_thread_mutex_destroy( &di->di_mutex );
1799
1800                 free( di );
1801         }
1802
1803         return 0;
1804 }
1805
1806 static int
1807 slap_exop_refresh(
1808                 Operation       *op,
1809                 SlapReply       *rs )
1810 {
1811         BackendDB               *bd = op->o_bd;
1812
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 ) {
1816                 return rs->sr_err;
1817         }
1818
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;
1823
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" );
1828                 goto done;
1829         }
1830
1831         rs->sr_err = backend_check_restrictions( op, rs,
1832                 (struct berval *)&slap_EXOP_REFRESH );
1833         if ( rs->sr_err != LDAP_SUCCESS ) {
1834                 goto done;
1835         }
1836
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" );
1840                 goto done;
1841         }
1842
1843         op->o_bd->be_extended( op, rs );
1844
1845 done:;
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 );
1850         }
1851         op->o_bd = bd;
1852
1853         return rs->sr_err;
1854 }
1855
1856 static slap_overinst dds;
1857
1858 static int do_not_load_exop;
1859 static int do_not_replace_exop;
1860 static int do_not_load_schema;
1861
1862 #if SLAPD_OVER_DDS == SLAPD_MOD_DYNAMIC
1863 static
1864 #endif /* SLAPD_OVER_DDS == SLAPD_MOD_DYNAMIC */
1865 int
1866 dds_initialize()
1867 {
1868         int             rc = 0;
1869         int             i, code;
1870
1871         /* Make sure we don't exceed the bits reserved for userland */
1872         config_check_userland( DDS_LAST );
1873
1874         if ( !do_not_load_schema ) {
1875                 static struct {
1876                         char                    *desc;
1877                         slap_mask_t             flags;
1878                         AttributeDescription    **ad;
1879                 }               s_at[] = {
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 "
1887                                 "SINGLE-VALUE "
1888                                 "NO-USER-MODIFICATION "
1889                                 "USAGE dSAOperation )",
1890                                 SLAP_AT_HIDE,
1891                                 &ad_entryExpireTimestamp },
1892                         { NULL }
1893                 };
1894
1895                 for ( i = 0; s_at[ i ].desc != NULL; i++ ) {
1896                         code = register_at( s_at[ i ].desc, s_at[ i ].ad, 0 );
1897                         if ( code ) {
1898                                 Debug( LDAP_DEBUG_ANY,
1899                                         "dds_initialize: register_at failed\n", 0, 0, 0 );
1900                                 return code;
1901                         }
1902                         (*s_at[ i ].ad)->ad_type->sat_flags |= SLAP_AT_HIDE;
1903                 }
1904         }
1905
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",
1913                                 rc );
1914                         return rc;
1915                 }
1916         }
1917
1918         dds.on_bi.bi_type = "dds";
1919
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;
1924
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;
1930
1931         dds.on_bi.bi_cf_ocs = dds_ocs;
1932
1933         rc = config_register_schema( dds_cfg, dds_ocs );
1934         if ( rc ) {
1935                 return rc;
1936         }
1937
1938         return overlay_register( &dds );
1939 }
1940
1941 #if SLAPD_OVER_DDS == SLAPD_MOD_DYNAMIC
1942 int
1943 init_module( int argc, char *argv[] )
1944 {
1945         int     i;
1946
1947         for ( i = 0; i < argc; i++ ) {
1948                 char    *arg = argv[ i ];
1949                 int     no = 0;
1950
1951                 if ( strncasecmp( arg, "no-", STRLENOF( "no-" ) ) == 0 ) {
1952                         arg += STRLENOF( "no-" );
1953                         no = 1;
1954                 }
1955
1956                 if ( strcasecmp( arg, "exop" ) == 0 ) {
1957                         do_not_load_exop = no;
1958
1959                 } else if ( strcasecmp( arg, "replace" ) == 0 ) {
1960                         do_not_replace_exop = no;
1961
1962                 } else if ( strcasecmp( arg, "schema" ) == 0 ) {
1963                         do_not_load_schema = no;
1964
1965                 } else {
1966                         Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1967                                 "DDS unknown module arg[#%d]=\"%s\".\n",
1968                                 i, argv[ i ] );
1969                         return 1;
1970                 }
1971         }
1972
1973         return dds_initialize();
1974 }
1975 #endif /* SLAPD_OVER_DDS == SLAPD_MOD_DYNAMIC */
1976
1977 #endif  /* defined(SLAPD_OVER_DDS) */