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