]> git.sur5r.net Git - openldap/blob - servers/slapd/back-bdb/init.c
Merge from HEAD
[openldap] / servers / slapd / back-bdb / init.c
1 /* init.c - initialize bdb backend */
2 /* $OpenLDAP$ */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4  *
5  * Copyright 2000-2005 The OpenLDAP Foundation.
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
17 #include "portable.h"
18
19 #include <stdio.h>
20 #include <ac/string.h>
21 #include <ac/unistd.h>
22 #include <ac/stdlib.h>
23 #include <ac/errno.h>
24 #include <sys/stat.h>
25 #include "back-bdb.h"
26 #include <lutil.h>
27 #include <ldap_rq.h>
28 #include "alock.h"
29
30 static const struct bdbi_database {
31         char *file;
32         char *name;
33         int type;
34         int flags;
35 } bdbi_databases[] = {
36         { "id2entry" BDB_SUFFIX, "id2entry", DB_BTREE, 0 },
37         { "dn2id" BDB_SUFFIX, "dn2id", DB_BTREE, 0 },
38         { NULL, NULL, 0, 0 }
39 };
40
41 typedef void * db_malloc(size_t);
42 typedef void * db_realloc(void *, size_t);
43
44 static int
45 bdb_db_init( BackendDB *be )
46 {
47         struct bdb_info *bdb;
48
49         Debug( LDAP_DEBUG_TRACE,
50                 LDAP_XSTRING(bdb_db_init) ": Initializing " BDB_UCTYPE " database\n",
51                 0, 0, 0 );
52
53         /* allocate backend-database-specific stuff */
54         bdb = (struct bdb_info *) ch_calloc( 1, sizeof(struct bdb_info) );
55
56         /* DBEnv parameters */
57         bdb->bi_dbenv_home = ch_strdup( SLAPD_DEFAULT_DB_DIR );
58         bdb->bi_dbenv_xflags = 0;
59         bdb->bi_dbenv_mode = SLAPD_DEFAULT_DB_MODE;
60
61         bdb->bi_cache.c_maxsize = DEFAULT_CACHE_SIZE;
62
63         bdb->bi_lock_detect = DB_LOCK_DEFAULT;
64         bdb->bi_search_stack_depth = DEFAULT_SEARCH_STACK_DEPTH;
65         bdb->bi_search_stack = NULL;
66
67         ldap_pvt_thread_mutex_init( &bdb->bi_database_mutex );
68         ldap_pvt_thread_mutex_init( &bdb->bi_lastid_mutex );
69         ldap_pvt_thread_mutex_init( &bdb->bi_cache.lru_mutex );
70         ldap_pvt_thread_mutex_init( &bdb->bi_cache.c_dntree.bei_kids_mutex );
71         ldap_pvt_thread_rdwr_init ( &bdb->bi_cache.c_rwlock );
72
73         be->be_private = bdb;
74         be->be_cf_table = be->bd_info->bi_cf_table;
75
76         return 0;
77 }
78
79 /*
80  * Unconditionally perform a database recovery. Only works on
81  * databases that were previously opened with transactions and
82  * logs enabled.
83  */
84 static int
85 bdb_do_recovery( BackendDB *be )
86 {
87         struct bdb_info *bdb = (struct bdb_info *) be->be_private;
88         DB_ENV  *re_dbenv;
89         u_int32_t flags;
90         int             rc;
91         char    path[MAXPATHLEN], *ptr;
92
93         /* Create and init the recovery environment */
94         rc = db_env_create( &re_dbenv, 0 );
95         if( rc != 0 ) {
96                 Debug( LDAP_DEBUG_ANY,
97                         "bdb_do_recovery: db_env_create failed: %s (%d)\n",
98                         db_strerror(rc), rc, 0 );
99                 return rc;
100         }
101         re_dbenv->set_errpfx( re_dbenv, be->be_suffix[0].bv_val );
102         re_dbenv->set_errcall( re_dbenv, bdb_errcall );
103         (void)re_dbenv->set_verbose(re_dbenv, DB_VERB_RECOVERY, 1);
104 #if DB_VERSION_FULL < 0x04030000
105         (void)re_dbenv->set_verbose(re_dbenv, DB_VERB_CHKPOINT, 1);
106 #else
107         re_dbenv->set_msgcall( re_dbenv, bdb_msgcall );
108 #endif
109
110         flags = DB_CREATE | DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL |
111                 DB_INIT_TXN | DB_USE_ENVIRON | DB_RECOVER;
112
113         /* If a key was set, use shared memory for the BDB environment */
114         if ( bdb->bi_shm_key ) {
115                 re_dbenv->set_shm_key( re_dbenv, bdb->bi_shm_key );
116                 flags |= DB_SYSTEM_MEM;
117         }
118
119         /* Open the environment, which will also perform the recovery */
120 #ifdef HAVE_EBCDIC
121         strcpy( path, bdb->bi_dbenv_home );
122         __atoe( path );
123         rc = re_dbenv->open( re_dbenv,
124                 path,
125                 flags,
126                 bdb->bi_dbenv_mode );
127 #else
128         rc = re_dbenv->open( re_dbenv,
129                 bdb->bi_dbenv_home,
130                 flags,
131                 bdb->bi_dbenv_mode );
132 #endif
133         if( rc != 0 ) {
134                 Debug( LDAP_DEBUG_ANY,
135                         "bdb_do_recovery: dbenv_open failed: %s (%d)\n",
136                         db_strerror(rc), rc, 0 );
137                 return rc;
138         }
139         (void) re_dbenv->close( re_dbenv, 0 );
140
141         /* By convention we reset the mtime for id2entry.bdb to the current time */
142         ptr = lutil_strcopy( path, bdb->bi_dbenv_home);
143         *ptr++ = LDAP_DIRSEP[0];
144         strcpy( ptr, bdbi_databases[0].file);
145         (void) utime( path, NULL);
146
147         return 0;
148 }
149
150 /*
151  * Database recovery logic:
152  * This function is called whenever the database appears to have been
153  * shut down uncleanly, as determined by the alock functions. 
154  * Because of the -q function in slapadd, there is also the possibility
155  * that the shutdown happened when transactions weren't being used and
156  * the database is likely to be corrupt. The function checks for this
157  * condition by examining the environment to make sure it had previously
158  * been opened with transactions enabled. If this is the case, the
159  * database is recovered as usual. If transactions were not enabled,
160  * then this function will return a fail.
161  */
162 static int
163 bdb_db_recover( BackendDB *be )
164 {
165         struct bdb_info *bdb = (struct bdb_info *) be->be_private;
166         DB_ENV  *re_dbenv;
167         u_int32_t flags;
168         int             rc;
169 #ifdef HAVE_EBCDIC
170         char    path[MAXPATHLEN];
171 #endif
172
173         /* Create the recovery environment, then open it.
174          * We use the DB_JOIN in combination with a flags value of
175          * zero so we join an existing environment and can read the
176          * value of the flags that were used the last time the 
177          * environment was opened. DB_CREATE is added because the
178          * open would fail if the only thing that had been done
179          * was an open with transactions and logs disabled.
180          */
181         rc = db_env_create( &re_dbenv, 0 );
182         if( rc != 0 ) {
183                 Debug( LDAP_DEBUG_ANY,
184                         "bdb_db_recover: db_env_create failed: %s (%d)\n",
185                         db_strerror(rc), rc, 0 );
186                 return rc;
187         }
188         re_dbenv->set_errpfx( re_dbenv, be->be_suffix[0].bv_val );
189         re_dbenv->set_errcall( re_dbenv, bdb_errcall );
190
191         Debug( LDAP_DEBUG_TRACE,
192                 "bdb_db_recover: dbenv_open(%s)\n",
193                 bdb->bi_dbenv_home, 0, 0);
194
195 #ifdef HAVE_EBCDIC
196         strcpy( path, bdb->bi_dbenv_home );
197         __atoe( path );
198         rc = re_dbenv->open( re_dbenv,
199                 path,
200                 DB_JOINENV,
201                 bdb->bi_dbenv_mode );
202 #else
203         rc = re_dbenv->open( re_dbenv,
204                 bdb->bi_dbenv_home,
205                 DB_JOINENV,
206                 bdb->bi_dbenv_mode );
207 #endif
208
209         if( rc == ENOENT ) {
210                 goto re_exit;
211         }
212         else if( rc != 0 ) {
213                 Debug( LDAP_DEBUG_ANY,
214                         "bdb_db_recover: dbenv_open failed: %s (%d)\n",
215                         db_strerror(rc), rc, 0 );
216                 return rc;
217         }
218
219         /*
220          * Check the flags that had been used in the previous open.
221          * The environment needed to have had both
222          * DB_INIT_LOG and DB_INIT_TXN set for us to be willing to
223          * recover the database. Otherwise the an app failed while running
224          * without transactions and logs enabled and the dn2id and id2entry
225          * mapping is likely to be corrupt.
226          */
227         rc = re_dbenv->get_open_flags( re_dbenv, &flags );
228         if( rc != 0 ) {
229                 Debug( LDAP_DEBUG_ANY,
230                         "bdb_db_recover: get_open_flags failed: %s (%d)\n",
231                         db_strerror(rc), rc, 0 );
232                 return rc;
233         }
234
235         (void) re_dbenv->close( re_dbenv, 0 );
236
237         if( (flags & DB_INIT_LOG) && (flags & DB_INIT_TXN) ) {
238                 return bdb_do_recovery( be );
239         }
240
241 re_exit:
242         Debug( LDAP_DEBUG_ANY,
243                 "bdb_db_recover: Database cannot be recovered. "\
244                 "Restore from backup!\n", 0, 0, 0);
245         return -1;
246
247 }
248
249
250 static int
251 bdb_db_open( BackendDB *be )
252 {
253         int rc, i;
254         struct bdb_info *bdb = (struct bdb_info *) be->be_private;
255         struct stat stat1, stat2;
256         u_int32_t flags;
257         char path[MAXPATHLEN];
258         char *ptr;
259
260         Debug( LDAP_DEBUG_ARGS,
261                 "bdb_db_open: %s\n",
262                 be->be_suffix[0].bv_val, 0, 0 );
263
264 #ifndef BDB_MULTIPLE_SUFFIXES
265         if ( be->be_suffix[1].bv_val ) {
266         Debug( LDAP_DEBUG_ANY,
267                 "bdb_db_open: only one suffix allowed\n", 0, 0, 0 );
268                 return -1;
269         }
270 #endif
271
272         /* Check existence of dbenv_home. Any error means trouble */
273         rc = stat( bdb->bi_dbenv_home, &stat1 );
274         if( rc !=0 ) {
275                 Debug( LDAP_DEBUG_ANY,
276                         "bdb_db_open: Cannot access database directory %s (%d)\n",
277                         bdb->bi_dbenv_home, errno, 0 );
278                         return -1;
279         }
280         
281         /* Perform database use arbitration/recovery logic */
282         rc = alock_open( &bdb->bi_alock_info, 
283                                 "slapd", 
284                                 bdb->bi_dbenv_home,
285                                 slapMode & SLAP_TOOL_READONLY ?
286                                 ALOCK_LOCKED : ALOCK_UNIQUE );
287
288         if( rc == ALOCK_RECOVER ) {
289                 Debug( LDAP_DEBUG_ANY,
290                         "bdb_db_open: unclean shutdown detected;"
291                         " attempting recovery.\n", 
292                         0, 0, 0 );
293                 if( bdb_db_recover( be ) != 0 ) {
294                         Debug( LDAP_DEBUG_ANY,
295                                 "bdb_db_open: DB recovery failed.\n",
296                                 0, 0, 0 );
297                         return -1;
298                 }
299                 if( alock_recover (&bdb->bi_alock_info) != 0 ) {
300                         Debug( LDAP_DEBUG_ANY,
301                                 "bdb_db_open: alock_recover failed\n",
302                                 0, 0, 0 );
303                         return -1;
304                 }
305
306         } else if( rc == ALOCK_BUSY ) {
307                 Debug( LDAP_DEBUG_ANY,
308                         "bdb_db_open: database already in use\n", 
309                         0, 0, 0 );
310                 return -1;
311         } else if( rc != ALOCK_CLEAN ) {
312                 Debug( LDAP_DEBUG_ANY,
313                         "bdb_db_open: alock package is unstable\n", 
314                         0, 0, 0 );
315                 return -1;
316         }
317         
318         /*
319          * The DB_CONFIG file may have changed. If so, recover the
320          * database so that new settings are put into effect. Also
321          * note the possible absence of DB_CONFIG in the log.
322          */
323         if( stat( bdb->bi_db_config_path, &stat1 ) == 0 ) {
324                 ptr = lutil_strcopy(path, bdb->bi_dbenv_home);
325                 *ptr++ = LDAP_DIRSEP[0];
326                 strcpy( ptr, bdbi_databases[0].file);
327                 if( stat( path, &stat2 ) == 0 ) {
328                         if( stat2.st_mtime <= stat1.st_mtime ) {
329                                 Debug( LDAP_DEBUG_ANY,
330                                         "bdb_db_open: DB_CONFIG for suffix %s has changed.\n"
331                                         "Performing database recovery to activate new settings.\n",
332                                         be->be_suffix[0].bv_val, 0, 0 );
333                                 if( bdb_do_recovery( be ) != 0) {
334                                         Debug( LDAP_DEBUG_ANY,
335                                                 "bdb_db_open: db recovery failed.\n",
336                                                 0, 0, 0 );
337                                         return -1;
338                                 }
339                         }
340                                         
341                 }
342         }
343         else {
344                 Debug( LDAP_DEBUG_ANY,
345                         "bdb_db_open: Warning - No DB_CONFIG file found "
346                         "in directory %s: (%d)\n"
347                         "Expect poor performance for suffix %s.\n",
348                         bdb->bi_dbenv_home, errno, be->be_suffix[0].bv_val );
349         }
350                 
351         flags = DB_INIT_MPOOL | DB_THREAD | DB_CREATE;
352         if ( !( slapMode & SLAP_TOOL_QUICK ))
353                 flags |= DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_TXN;
354
355         rc = db_env_create( &bdb->bi_dbenv, 0 );
356         if( rc != 0 ) {
357                 Debug( LDAP_DEBUG_ANY,
358                         "bdb_db_open: db_env_create failed: %s (%d)\n",
359                         db_strerror(rc), rc, 0 );
360                 return rc;
361         }
362
363         /* If a key was set, use shared memory for the BDB environment */
364         if ( bdb->bi_shm_key ) {
365                 bdb->bi_dbenv->set_shm_key( bdb->bi_dbenv, bdb->bi_shm_key );
366                 flags |= DB_SYSTEM_MEM;
367         }
368
369         bdb->bi_dbenv->set_errpfx( bdb->bi_dbenv, be->be_suffix[0].bv_val );
370         bdb->bi_dbenv->set_errcall( bdb->bi_dbenv, bdb_errcall );
371         bdb->bi_dbenv->set_lk_detect( bdb->bi_dbenv, bdb->bi_lock_detect );
372
373         /* One long-lived TXN per thread, two TXNs per write op */
374         bdb->bi_dbenv->set_tx_max( bdb->bi_dbenv, connection_pool_max * 3 );
375
376 #ifdef SLAP_ZONE_ALLOC
377         if ( bdb->bi_cache.c_maxsize ) {
378                 bdb->bi_cache.c_zctx = slap_zn_mem_create(
379                                                                 SLAP_ZONE_INITSIZE,
380                                                                 SLAP_ZONE_MAXSIZE,
381                                                                 SLAP_ZONE_DELTA,
382                                                                 SLAP_ZONE_SIZE);
383         }
384 #endif
385
386         if ( bdb->bi_idl_cache_max_size ) {
387                 bdb->bi_idl_tree = NULL;
388                 ldap_pvt_thread_rdwr_init( &bdb->bi_idl_tree_rwlock );
389                 ldap_pvt_thread_mutex_init( &bdb->bi_idl_tree_lrulock );
390                 bdb->bi_idl_cache_size = 0;
391         }
392
393         if( bdb->bi_dbenv_xflags != 0 ) {
394                 rc = bdb->bi_dbenv->set_flags( bdb->bi_dbenv,
395                         bdb->bi_dbenv_xflags, 1);
396                 if( rc != 0 ) {
397                         Debug( LDAP_DEBUG_ANY,
398                                 "bdb_db_open: dbenv_set_flags failed: %s (%d)\n",
399                                 db_strerror(rc), rc, 0 );
400                         return rc;
401                 }
402         }
403
404         Debug( LDAP_DEBUG_TRACE,
405                 "bdb_db_open: dbenv_open(%s)\n",
406                 bdb->bi_dbenv_home, 0, 0);
407
408 #ifdef HAVE_EBCDIC
409         strcpy( path, bdb->bi_dbenv_home );
410         __atoe( path );
411         rc = bdb->bi_dbenv->open( bdb->bi_dbenv,
412                 path,
413                 flags,
414                 bdb->bi_dbenv_mode );
415 #else
416         rc = bdb->bi_dbenv->open( bdb->bi_dbenv,
417                 bdb->bi_dbenv_home,
418                 flags,
419                 bdb->bi_dbenv_mode );
420 #endif
421         if( rc != 0 ) {
422                 Debug( LDAP_DEBUG_ANY,
423                         "bdb_db_open: dbenv_open failed: %s (%d)\n",
424                         db_strerror(rc), rc, 0 );
425                 return rc;
426         }
427
428         flags = DB_THREAD | bdb->bi_db_opflags;
429
430 #ifdef DB_AUTO_COMMIT
431         if ( !( slapMode & SLAP_TOOL_QUICK ))
432                 flags |= DB_AUTO_COMMIT;
433 #endif
434
435         bdb->bi_databases = (struct bdb_db_info **) ch_malloc(
436                 BDB_INDICES * sizeof(struct bdb_db_info *) );
437
438         /* open (and create) main database */
439         for( i = 0; bdbi_databases[i].name; i++ ) {
440                 struct bdb_db_info *db;
441
442                 db = (struct bdb_db_info *) ch_calloc(1, sizeof(struct bdb_db_info));
443
444                 rc = db_create( &db->bdi_db, bdb->bi_dbenv, 0 );
445                 if( rc != 0 ) {
446                         Debug( LDAP_DEBUG_ANY,
447                                 "bdb_db_open: db_create(%s) failed: %s (%d)\n",
448                                 bdb->bi_dbenv_home, db_strerror(rc), rc );
449                         return rc;
450                 }
451
452                 if( i == BDB_ID2ENTRY ) {
453                         rc = db->bdi_db->set_pagesize( db->bdi_db,
454                                 BDB_ID2ENTRY_PAGESIZE );
455                         if ( slapMode & SLAP_TOOL_READMAIN ) {
456                                 flags |= DB_RDONLY;
457                         } else {
458                                 flags |= DB_CREATE;
459                         }
460                 } else {
461                         rc = db->bdi_db->set_flags( db->bdi_db, 
462                                 DB_DUP | DB_DUPSORT );
463 #ifndef BDB_HIER
464                         if ( slapMode & SLAP_TOOL_READONLY ) {
465                                 flags |= DB_RDONLY;
466                         } else {
467                                 flags |= DB_CREATE;
468                         }
469 #else
470                         if ( slapMode & (SLAP_TOOL_READONLY|SLAP_TOOL_READMAIN) ) {
471                                 flags |= DB_RDONLY;
472                         } else {
473                                 flags |= DB_CREATE;
474                         }
475 #endif
476                         rc = db->bdi_db->set_pagesize( db->bdi_db,
477                                 BDB_PAGESIZE );
478                 }
479
480 #ifdef HAVE_EBCDIC
481                 strcpy( path, bdbi_databases[i].file );
482                 __atoe( path );
483                 rc = DB_OPEN( db->bdi_db,
484                         path,
485                 /*      bdbi_databases[i].name, */ NULL,
486                         bdbi_databases[i].type,
487                         bdbi_databases[i].flags | flags,
488                         bdb->bi_dbenv_mode );
489 #else
490                 rc = DB_OPEN( db->bdi_db,
491                         bdbi_databases[i].file,
492                 /*      bdbi_databases[i].name, */ NULL,
493                         bdbi_databases[i].type,
494                         bdbi_databases[i].flags | flags,
495                         bdb->bi_dbenv_mode );
496 #endif
497
498                 if ( rc != 0 ) {
499                         char    buf[SLAP_TEXT_BUFLEN];
500
501                         snprintf( buf, sizeof(buf), "%s/%s", 
502                                 bdb->bi_dbenv_home, bdbi_databases[i].file );
503                         Debug( LDAP_DEBUG_ANY,
504                                 "bdb_db_open: db_open(%s) failed: %s (%d)\n",
505                                 buf, db_strerror(rc), rc );
506                         return rc;
507                 }
508
509                 flags &= ~(DB_CREATE | DB_RDONLY);
510                 db->bdi_name = bdbi_databases[i].name;
511                 bdb->bi_databases[i] = db;
512         }
513
514         bdb->bi_databases[i] = NULL;
515         bdb->bi_ndatabases = i;
516
517         /* get nextid */
518         rc = bdb_last_id( be, NULL );
519         if( rc != 0 ) {
520                 Debug( LDAP_DEBUG_ANY,
521                         "bdb_db_open: last_id(%s) failed: %s (%d)\n",
522                         bdb->bi_dbenv_home, db_strerror(rc), rc );
523                 return rc;
524         }
525
526         if ( !( slapMode & SLAP_TOOL_QUICK )) {
527                 XLOCK_ID(bdb->bi_dbenv, &bdb->bi_cache.c_locker);
528         }
529
530         bdb->bi_flags |= BDB_IS_OPEN;
531
532         return 0;
533 }
534
535 static int
536 bdb_db_close( BackendDB *be )
537 {
538         int rc;
539         struct bdb_info *bdb = (struct bdb_info *) be->be_private;
540         struct bdb_db_info *db;
541         bdb_idl_cache_entry_t *entry, *next_entry;
542
543         bdb->bi_flags &= ~BDB_IS_OPEN;
544
545         ber_bvarray_free( bdb->bi_db_config );
546         bdb->bi_db_config = NULL;
547
548         while( bdb->bi_databases && bdb->bi_ndatabases-- ) {
549                 db = bdb->bi_databases[bdb->bi_ndatabases];
550                 rc = db->bdi_db->close( db->bdi_db, 0 );
551                 /* Lower numbered names are not strdup'd */
552                 if( bdb->bi_ndatabases >= BDB_NDB )
553                         free( db->bdi_name );
554                 free( db );
555         }
556         free( bdb->bi_databases );
557         bdb->bi_databases = NULL;
558
559         bdb_cache_release_all (&bdb->bi_cache);
560
561         if ( bdb->bi_idl_cache_max_size ) {
562                 ldap_pvt_thread_rdwr_wlock ( &bdb->bi_idl_tree_rwlock );
563                 avl_free( bdb->bi_idl_tree, NULL );
564                 bdb->bi_idl_tree = NULL;
565                 entry = bdb->bi_idl_lru_head;
566                 while ( entry != NULL ) {
567                         next_entry = entry->idl_lru_next;
568                         if ( entry->idl )
569                                 free( entry->idl );
570                         free( entry->kstr.bv_val );
571                         free( entry );
572                         entry = next_entry;
573                 }
574                 bdb->bi_idl_lru_head = bdb->bi_idl_lru_tail = NULL;
575                 ldap_pvt_thread_rdwr_wunlock ( &bdb->bi_idl_tree_rwlock );
576         }
577
578         if ( !( slapMode & SLAP_TOOL_QUICK ) && bdb->bi_dbenv ) {
579                 XLOCK_ID_FREE(bdb->bi_dbenv, bdb->bi_cache.c_locker);
580                 bdb->bi_cache.c_locker = 0;
581         }
582
583         /* close db environment */
584         if( bdb->bi_dbenv ) {
585                 /* force a checkpoint, but not if we were ReadOnly,
586                  * and not in Quick mode since there are no transactions there.
587                  */
588                 if ( !( slapMode & ( SLAP_TOOL_QUICK|SLAP_TOOL_READONLY ))) {
589                         rc = TXN_CHECKPOINT( bdb->bi_dbenv, 0, 0, DB_FORCE );
590                         if( rc != 0 ) {
591                                 Debug( LDAP_DEBUG_ANY,
592                                         "bdb_db_close: txn_checkpoint failed: %s (%d)\n",
593                                         db_strerror(rc), rc, 0 );
594                         }
595                 }
596
597                 rc = bdb->bi_dbenv->close( bdb->bi_dbenv, 0 );
598                 bdb->bi_dbenv = NULL;
599                 if( rc != 0 ) {
600                         Debug( LDAP_DEBUG_ANY,
601                                 "bdb_db_close: close failed: %s (%d)\n",
602                                 db_strerror(rc), rc, 0 );
603                         return rc;
604                 }
605         }
606
607         rc = alock_close( &bdb->bi_alock_info );
608         if( rc != 0 ) {
609                 Debug( LDAP_DEBUG_ANY,
610                         "bdb_db_close: alock_close failed\n", 0, 0, 0 );
611                 return -1;
612         }
613
614         return 0;
615 }
616
617 static int
618 bdb_db_destroy( BackendDB *be )
619 {
620         int rc;
621         struct bdb_info *bdb = (struct bdb_info *) be->be_private;
622
623         if( bdb->bi_dbenv_home ) ch_free( bdb->bi_dbenv_home );
624         if( bdb->bi_db_config_path ) ch_free( bdb->bi_db_config_path );
625
626         bdb_attr_index_destroy( bdb->bi_attrs );
627
628         ldap_pvt_thread_rdwr_destroy ( &bdb->bi_cache.c_rwlock );
629         ldap_pvt_thread_mutex_destroy( &bdb->bi_cache.lru_mutex );
630         ldap_pvt_thread_mutex_destroy( &bdb->bi_cache.c_dntree.bei_kids_mutex );
631         ldap_pvt_thread_mutex_destroy( &bdb->bi_lastid_mutex );
632         ldap_pvt_thread_mutex_destroy( &bdb->bi_database_mutex );
633         if ( bdb->bi_idl_cache_max_size ) {
634                 ldap_pvt_thread_rdwr_destroy( &bdb->bi_idl_tree_rwlock );
635                 ldap_pvt_thread_mutex_destroy( &bdb->bi_idl_tree_lrulock );
636         }
637
638         ch_free( bdb );
639         be->be_private = NULL;
640
641         return 0;
642 }
643
644 int
645 bdb_back_initialize(
646         BackendInfo     *bi )
647 {
648         int rc;
649
650         static char *controls[] = {
651                 LDAP_CONTROL_ASSERT,
652                 LDAP_CONTROL_MANAGEDSAIT,
653                 LDAP_CONTROL_NOOP,
654                 LDAP_CONTROL_PAGEDRESULTS,
655 #ifdef LDAP_CONTROL_SUBENTRIES
656                 LDAP_CONTROL_SUBENTRIES,
657 #endif
658 #ifdef LDAP_CONTROL_X_PERMISSIVE_MODIFY
659                 LDAP_CONTROL_X_PERMISSIVE_MODIFY,
660 #endif
661                 NULL
662         };
663
664         /* initialize the underlying database system */
665         Debug( LDAP_DEBUG_TRACE,
666                 LDAP_XSTRING(bdb_back_initialize) ": initialize " 
667                 BDB_UCTYPE " backend\n", 0, 0, 0 );
668
669         bi->bi_flags |=
670                 SLAP_BFLAG_INCREMENT |
671 #ifdef BDB_SUBENTRIES
672                 SLAP_BFLAG_SUBENTRIES |
673 #endif
674                 SLAP_BFLAG_ALIASES |
675                 SLAP_BFLAG_REFERRALS;
676
677         bi->bi_controls = controls;
678
679         {       /* version check */
680                 int major, minor, patch, ver;
681                 char *version = db_version( &major, &minor, &patch );
682 #ifdef HAVE_EBCDIC
683                 char v2[1024];
684
685                 /* All our stdio does an ASCII to EBCDIC conversion on
686                  * the output. Strings from the BDB library are already
687                  * in EBCDIC; we have to go back and forth...
688                  */
689                 strcpy( v2, version );
690                 __etoa( v2 );
691                 version = v2;
692 #endif
693
694                 ver = (major << 24) | (minor << 16) | patch;
695                 if( ver != DB_VERSION_FULL ) {
696                         /* fail if a versions don't match */
697                         Debug( LDAP_DEBUG_ANY,
698                                 LDAP_XSTRING(bdb_back_initialize) ": "
699                                 "BDB library version mismatch:"
700                                 " expected " DB_VERSION_STRING ","
701                                 " got %s\n", version, 0, 0 );
702                         return -1;
703                 }
704
705                 Debug( LDAP_DEBUG_TRACE, LDAP_XSTRING(bdb_back_initialize)
706                         ": %s\n", version, 0, 0 );
707         }
708
709         db_env_set_func_free( ber_memfree );
710         db_env_set_func_malloc( (db_malloc *)ber_memalloc );
711         db_env_set_func_realloc( (db_realloc *)ber_memrealloc );
712 #ifndef NO_THREAD
713         /* This is a no-op on a NO_THREAD build. Leave the default
714          * alone so that BDB will sleep on interprocess conflicts.
715          */
716         db_env_set_func_yield( ldap_pvt_thread_yield );
717 #endif
718
719         bi->bi_open = 0;
720         bi->bi_close = 0;
721         bi->bi_config = 0;
722         bi->bi_destroy = 0;
723
724         bi->bi_db_init = bdb_db_init;
725         bi->bi_db_config = config_generic_wrapper;
726         bi->bi_db_open = bdb_db_open;
727         bi->bi_db_close = bdb_db_close;
728         bi->bi_db_destroy = bdb_db_destroy;
729
730         bi->bi_op_add = bdb_add;
731         bi->bi_op_bind = bdb_bind;
732         bi->bi_op_compare = bdb_compare;
733         bi->bi_op_delete = bdb_delete;
734         bi->bi_op_modify = bdb_modify;
735         bi->bi_op_modrdn = bdb_modrdn;
736         bi->bi_op_search = bdb_search;
737
738         bi->bi_op_unbind = 0;
739
740         bi->bi_extended = bdb_extended;
741
742         bi->bi_chk_referrals = bdb_referrals;
743         bi->bi_operational = bdb_operational;
744         bi->bi_has_subordinates = bdb_hasSubordinates;
745         bi->bi_entry_release_rw = bdb_entry_release;
746         bi->bi_entry_get_rw = bdb_entry_get;
747
748         /*
749          * hooks for slap tools
750          */
751         bi->bi_tool_entry_open = bdb_tool_entry_open;
752         bi->bi_tool_entry_close = bdb_tool_entry_close;
753         bi->bi_tool_entry_first = bdb_tool_entry_next;
754         bi->bi_tool_entry_next = bdb_tool_entry_next;
755         bi->bi_tool_entry_get = bdb_tool_entry_get;
756         bi->bi_tool_entry_put = bdb_tool_entry_put;
757         bi->bi_tool_entry_reindex = bdb_tool_entry_reindex;
758         bi->bi_tool_sync = 0;
759         bi->bi_tool_dn2id_get = bdb_tool_dn2id_get;
760         bi->bi_tool_id2entry_get = bdb_tool_id2entry_get;
761         bi->bi_tool_entry_modify = bdb_tool_entry_modify;
762
763         bi->bi_connection_init = 0;
764         bi->bi_connection_destroy = 0;
765
766         rc = bdb_back_init_cf( bi );
767
768         return rc;
769 }
770
771 #if     (SLAPD_BDB == SLAPD_MOD_DYNAMIC && !defined(BDB_HIER)) || \
772         (SLAPD_HDB == SLAPD_MOD_DYNAMIC && defined(BDB_HIER))
773
774 /* conditionally define the init_module() function */
775 #ifdef BDB_HIER
776 SLAP_BACKEND_INIT_MODULE( hdb )
777 #else /* !BDB_HIER */
778 SLAP_BACKEND_INIT_MODULE( bdb )
779 #endif /* !BDB_HIER */
780
781 #endif /* SLAPD_[BH]DB == SLAPD_MOD_DYNAMIC */
782