1/*	$NetBSD: init.c,v 1.1.1.3 2010/12/12 15:22:57 adam Exp $	*/
2
3/* init.c - initialize bdb backend */
4/* OpenLDAP: pkg/ldap/servers/slapd/back-bdb/init.c,v 1.247.2.25 2010/04/14 22:59:10 quanah Exp */
5/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
6 *
7 * Copyright 2000-2010 The OpenLDAP Foundation.
8 * All rights reserved.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted only as authorized by the OpenLDAP
12 * Public License.
13 *
14 * A copy of this license is available in the file LICENSE in the
15 * top-level directory of the distribution or, alternatively, at
16 * <http://www.OpenLDAP.org/license.html>.
17 */
18
19#include "portable.h"
20
21#include <stdio.h>
22#include <ac/string.h>
23#include <ac/unistd.h>
24#include <ac/stdlib.h>
25#include <ac/errno.h>
26#include <sys/stat.h>
27#include "back-bdb.h"
28#include <lutil.h>
29#include <ldap_rq.h>
30#include "alock.h"
31#include "config.h"
32
33static const struct bdbi_database {
34	char *file;
35	struct berval name;
36	int type;
37	int flags;
38} bdbi_databases[] = {
39	{ "id2entry" BDB_SUFFIX, BER_BVC("id2entry"), DB_BTREE, 0 },
40	{ "dn2id" BDB_SUFFIX, BER_BVC("dn2id"), DB_BTREE, 0 },
41	{ NULL, BER_BVNULL, 0, 0 }
42};
43
44typedef void * db_malloc(size_t);
45typedef void * db_realloc(void *, size_t);
46
47#define bdb_db_init	BDB_SYMBOL(db_init)
48#define bdb_db_open BDB_SYMBOL(db_open)
49#define bdb_db_close BDB_SYMBOL(db_close)
50
51static int
52bdb_db_init( BackendDB *be, ConfigReply *cr )
53{
54	struct bdb_info	*bdb;
55	int rc;
56
57	Debug( LDAP_DEBUG_TRACE,
58		LDAP_XSTRING(bdb_db_init) ": Initializing " BDB_UCTYPE " database\n",
59		0, 0, 0 );
60
61	/* allocate backend-database-specific stuff */
62	bdb = (struct bdb_info *) ch_calloc( 1, sizeof(struct bdb_info) );
63
64	/* DBEnv parameters */
65	bdb->bi_dbenv_home = ch_strdup( SLAPD_DEFAULT_DB_DIR );
66	bdb->bi_dbenv_xflags = DB_TIME_NOTGRANTED;
67	bdb->bi_dbenv_mode = SLAPD_DEFAULT_DB_MODE;
68
69	bdb->bi_cache.c_maxsize = DEFAULT_CACHE_SIZE;
70	bdb->bi_cache.c_minfree = 1;
71
72	bdb->bi_lock_detect = DB_LOCK_DEFAULT;
73	bdb->bi_search_stack_depth = DEFAULT_SEARCH_STACK_DEPTH;
74	bdb->bi_search_stack = NULL;
75
76	ldap_pvt_thread_mutex_init( &bdb->bi_database_mutex );
77	ldap_pvt_thread_mutex_init( &bdb->bi_lastid_mutex );
78#ifdef BDB_HIER
79	ldap_pvt_thread_mutex_init( &bdb->bi_modrdns_mutex );
80#endif
81	ldap_pvt_thread_mutex_init( &bdb->bi_cache.c_lru_mutex );
82	ldap_pvt_thread_mutex_init( &bdb->bi_cache.c_count_mutex );
83	ldap_pvt_thread_mutex_init( &bdb->bi_cache.c_eifree_mutex );
84	ldap_pvt_thread_mutex_init( &bdb->bi_cache.c_dntree.bei_kids_mutex );
85	ldap_pvt_thread_rdwr_init ( &bdb->bi_cache.c_rwlock );
86	ldap_pvt_thread_rdwr_init( &bdb->bi_idl_tree_rwlock );
87	ldap_pvt_thread_mutex_init( &bdb->bi_idl_tree_lrulock );
88
89	be->be_private = bdb;
90	be->be_cf_ocs = be->bd_info->bi_cf_ocs;
91
92#ifndef BDB_MULTIPLE_SUFFIXES
93	SLAP_DBFLAGS( be ) |= SLAP_DBFLAG_ONE_SUFFIX;
94#endif
95
96	rc = bdb_monitor_db_init( be );
97
98	return rc;
99}
100
101static int
102bdb_db_close( BackendDB *be, ConfigReply *cr );
103
104static int
105bdb_db_open( BackendDB *be, ConfigReply *cr )
106{
107	int rc, i;
108	struct bdb_info *bdb = (struct bdb_info *) be->be_private;
109	struct stat stat1, stat2;
110	u_int32_t flags;
111	char path[MAXPATHLEN];
112	char *dbhome;
113	Entry *e = NULL;
114	int do_recover = 0, do_alock_recover = 0;
115	int alockt, quick = 0;
116	int do_retry = 1;
117
118	if ( be->be_suffix == NULL ) {
119		Debug( LDAP_DEBUG_ANY,
120			LDAP_XSTRING(bdb_db_open) ": need suffix.\n",
121			1, 0, 0 );
122		return -1;
123	}
124
125	Debug( LDAP_DEBUG_ARGS,
126		LDAP_XSTRING(bdb_db_open) ": \"%s\"\n",
127		be->be_suffix[0].bv_val, 0, 0 );
128
129	/* Check existence of dbenv_home. Any error means trouble */
130	rc = stat( bdb->bi_dbenv_home, &stat1 );
131	if( rc != 0 ) {
132		Debug( LDAP_DEBUG_ANY,
133			LDAP_XSTRING(bdb_db_open) ": database \"%s\": "
134			"cannot access database directory \"%s\" (%d).\n",
135			be->be_suffix[0].bv_val, bdb->bi_dbenv_home, errno );
136		return -1;
137	}
138
139	/* Perform database use arbitration/recovery logic */
140	alockt = (slapMode & SLAP_TOOL_READONLY) ? ALOCK_LOCKED : ALOCK_UNIQUE;
141	if ( slapMode & SLAP_TOOL_QUICK ) {
142		alockt |= ALOCK_NOSAVE;
143		quick = 1;
144	}
145
146	rc = alock_open( &bdb->bi_alock_info,
147				"slapd",
148				bdb->bi_dbenv_home, alockt );
149
150	/* alockt is TRUE if the existing environment was created in Quick mode */
151	alockt = (rc & ALOCK_NOSAVE) ? 1 : 0;
152	rc &= ~ALOCK_NOSAVE;
153
154	if( rc == ALOCK_RECOVER ) {
155		Debug( LDAP_DEBUG_ANY,
156			LDAP_XSTRING(bdb_db_open) ": database \"%s\": "
157			"unclean shutdown detected; attempting recovery.\n",
158			be->be_suffix[0].bv_val, 0, 0 );
159		do_alock_recover = 1;
160		do_recover = DB_RECOVER;
161	} else if( rc == ALOCK_BUSY ) {
162		Debug( LDAP_DEBUG_ANY,
163			LDAP_XSTRING(bdb_db_open) ": database \"%s\": "
164			"database already in use.\n",
165			be->be_suffix[0].bv_val, 0, 0 );
166		return -1;
167	} else if( rc != ALOCK_CLEAN ) {
168		Debug( LDAP_DEBUG_ANY,
169			LDAP_XSTRING(bdb_db_open) ": database \"%s\": "
170			"alock package is unstable.\n",
171			be->be_suffix[0].bv_val, 0, 0 );
172		return -1;
173	}
174	if ( rc == ALOCK_CLEAN )
175		be->be_flags |= SLAP_DBFLAG_CLEAN;
176
177	/*
178	 * The DB_CONFIG file may have changed. If so, recover the
179	 * database so that new settings are put into effect. Also
180	 * note the possible absence of DB_CONFIG in the log.
181	 */
182	if( stat( bdb->bi_db_config_path, &stat1 ) == 0 ) {
183		if ( !do_recover ) {
184			char *ptr = lutil_strcopy(path, bdb->bi_dbenv_home);
185			*ptr++ = LDAP_DIRSEP[0];
186			strcpy( ptr, "__db.001" );
187			if( stat( path, &stat2 ) == 0 ) {
188				if( stat2.st_mtime < stat1.st_mtime ) {
189					Debug( LDAP_DEBUG_ANY,
190						LDAP_XSTRING(bdb_db_open) ": DB_CONFIG for suffix \"%s\" has changed.\n",
191							be->be_suffix[0].bv_val, 0, 0 );
192					if ( quick ) {
193						Debug( LDAP_DEBUG_ANY,
194							"Cannot use Quick mode; perform manual recovery first.\n",
195							0, 0, 0 );
196						slapMode ^= SLAP_TOOL_QUICK;
197						rc = -1;
198						goto fail;
199					} else {
200						Debug( LDAP_DEBUG_ANY,
201							"Performing database recovery to activate new settings.\n",
202							0, 0, 0 );
203					}
204					do_recover = DB_RECOVER;
205				}
206			}
207		}
208	}
209	else {
210		Debug( LDAP_DEBUG_ANY,
211			LDAP_XSTRING(bdb_db_open) ": warning - no DB_CONFIG file found "
212			"in directory %s: (%d).\n"
213			"Expect poor performance for suffix \"%s\".\n",
214			bdb->bi_dbenv_home, errno, be->be_suffix[0].bv_val );
215	}
216
217	/* Always let slapcat run, regardless of environment state.
218	 * This can be used to cause a cache flush after an unclean
219	 * shutdown.
220	 */
221	if ( do_recover && ( slapMode & SLAP_TOOL_READONLY )) {
222		Debug( LDAP_DEBUG_ANY,
223			LDAP_XSTRING(bdb_db_open) ": database \"%s\": "
224			"recovery skipped in read-only mode. "
225			"Run manual recovery if errors are encountered.\n",
226			be->be_suffix[0].bv_val, 0, 0 );
227		do_recover = 0;
228		do_alock_recover = 0;
229		quick = alockt;
230	}
231
232	/* An existing environment in Quick mode has nothing to recover. */
233	if ( alockt && do_recover ) {
234		Debug( LDAP_DEBUG_ANY,
235			LDAP_XSTRING(bdb_db_open) ": database \"%s\": "
236			"cannot recover, database must be reinitialized.\n",
237			be->be_suffix[0].bv_val, 0, 0 );
238		rc = -1;
239		goto fail;
240	}
241
242	rc = db_env_create( &bdb->bi_dbenv, 0 );
243	if( rc != 0 ) {
244		Debug( LDAP_DEBUG_ANY,
245			LDAP_XSTRING(bdb_db_open) ": database \"%s\": "
246			"db_env_create failed: %s (%d).\n",
247			be->be_suffix[0].bv_val, db_strerror(rc), rc );
248		goto fail;
249	}
250
251#ifdef HAVE_EBCDIC
252	strcpy( path, bdb->bi_dbenv_home );
253	__atoe( path );
254	dbhome = path;
255#else
256	dbhome = bdb->bi_dbenv_home;
257#endif
258
259	/* If existing environment is clean but doesn't support
260	 * currently requested modes, remove it.
261	 */
262	if ( !do_recover && ( alockt ^ quick )) {
263shm_retry:
264		rc = bdb->bi_dbenv->remove( bdb->bi_dbenv, dbhome, DB_FORCE );
265		if ( rc ) {
266			Debug( LDAP_DEBUG_ANY,
267				LDAP_XSTRING(bdb_db_open) ": database \"%s\": "
268				"dbenv remove failed: %s (%d).\n",
269				be->be_suffix[0].bv_val, db_strerror(rc), rc );
270			bdb->bi_dbenv = NULL;
271			goto fail;
272		}
273		rc = db_env_create( &bdb->bi_dbenv, 0 );
274		if( rc != 0 ) {
275			Debug( LDAP_DEBUG_ANY,
276				LDAP_XSTRING(bdb_db_open) ": database \"%s\": "
277				"db_env_create failed: %s (%d).\n",
278				be->be_suffix[0].bv_val, db_strerror(rc), rc );
279			goto fail;
280		}
281	}
282
283	bdb->bi_dbenv->set_errpfx( bdb->bi_dbenv, be->be_suffix[0].bv_val );
284	bdb->bi_dbenv->set_errcall( bdb->bi_dbenv, bdb_errcall );
285
286	bdb->bi_dbenv->set_lk_detect( bdb->bi_dbenv, bdb->bi_lock_detect );
287
288	if ( !BER_BVISNULL( &bdb->bi_db_crypt_key )) {
289		rc = bdb->bi_dbenv->set_encrypt( bdb->bi_dbenv, bdb->bi_db_crypt_key.bv_val,
290			DB_ENCRYPT_AES );
291		if ( rc ) {
292			Debug( LDAP_DEBUG_ANY,
293				LDAP_XSTRING(bdb_db_open) ": database \"%s\": "
294				"dbenv set_encrypt failed: %s (%d).\n",
295				be->be_suffix[0].bv_val, db_strerror(rc), rc );
296			goto fail;
297		}
298	}
299
300	/* One long-lived TXN per thread, two TXNs per write op */
301	bdb->bi_dbenv->set_tx_max( bdb->bi_dbenv, connection_pool_max * 3 );
302
303	if( bdb->bi_dbenv_xflags != 0 ) {
304		rc = bdb->bi_dbenv->set_flags( bdb->bi_dbenv,
305			bdb->bi_dbenv_xflags, 1);
306		if( rc != 0 ) {
307			Debug( LDAP_DEBUG_ANY,
308				LDAP_XSTRING(bdb_db_open) ": database \"%s\": "
309				"dbenv_set_flags failed: %s (%d).\n",
310				be->be_suffix[0].bv_val, db_strerror(rc), rc );
311			goto fail;
312		}
313	}
314
315#define	BDB_TXN_FLAGS	(DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_TXN)
316
317	Debug( LDAP_DEBUG_TRACE,
318		LDAP_XSTRING(bdb_db_open) ": database \"%s\": "
319		"dbenv_open(%s).\n",
320		be->be_suffix[0].bv_val, bdb->bi_dbenv_home, 0);
321
322	flags = DB_INIT_MPOOL | DB_CREATE | DB_THREAD;
323
324	if ( !quick )
325		flags |= BDB_TXN_FLAGS;
326
327	/* If a key was set, use shared memory for the BDB environment */
328	if ( bdb->bi_shm_key ) {
329		bdb->bi_dbenv->set_shm_key( bdb->bi_dbenv, bdb->bi_shm_key );
330		flags |= DB_SYSTEM_MEM;
331	}
332	rc = (bdb->bi_dbenv->open)( bdb->bi_dbenv, dbhome,
333			flags | do_recover, bdb->bi_dbenv_mode );
334
335	if ( rc ) {
336		/* Regular open failed, probably a missing shm environment.
337		 * Start over, do a recovery.
338		 */
339		if ( !do_recover && bdb->bi_shm_key && do_retry ) {
340			bdb->bi_dbenv->close( bdb->bi_dbenv, 0 );
341			rc = db_env_create( &bdb->bi_dbenv, 0 );
342			if( rc == 0 ) {
343				Debug( LDAP_DEBUG_ANY, LDAP_XSTRING(bdb_db_open)
344					": database \"%s\": "
345					"shared memory env open failed, assuming stale env.\n",
346					be->be_suffix[0].bv_val, 0, 0 );
347				do_retry = 0;
348				goto shm_retry;
349			}
350		}
351		Debug( LDAP_DEBUG_ANY,
352			LDAP_XSTRING(bdb_db_open) ": database \"%s\" cannot be %s, err %d. "
353			"Restore from backup!\n",
354			be->be_suffix[0].bv_val, do_recover ? "recovered" : "opened", rc );
355		goto fail;
356	}
357
358	if ( do_alock_recover && alock_recover (&bdb->bi_alock_info) != 0 ) {
359		Debug( LDAP_DEBUG_ANY,
360			LDAP_XSTRING(bdb_db_open) ": database \"%s\": alock_recover failed\n",
361			be->be_suffix[0].bv_val, 0, 0 );
362		rc = -1;
363		goto fail;
364	}
365
366#ifdef SLAP_ZONE_ALLOC
367	if ( bdb->bi_cache.c_maxsize ) {
368		bdb->bi_cache.c_zctx = slap_zn_mem_create(
369			SLAP_ZONE_INITSIZE, SLAP_ZONE_MAXSIZE,
370			SLAP_ZONE_DELTA, SLAP_ZONE_SIZE);
371	}
372#endif
373
374	/* dncache defaults to 0 == unlimited
375	 * must be >= entrycache
376	 */
377	if ( bdb->bi_cache.c_eimax && bdb->bi_cache.c_eimax < bdb->bi_cache.c_maxsize ) {
378		bdb->bi_cache.c_eimax = bdb->bi_cache.c_maxsize;
379	}
380
381	if ( bdb->bi_idl_cache_max_size ) {
382		bdb->bi_idl_tree = NULL;
383		bdb->bi_idl_cache_size = 0;
384	}
385
386	flags = DB_THREAD | bdb->bi_db_opflags;
387
388#ifdef DB_AUTO_COMMIT
389	if ( !quick )
390		flags |= DB_AUTO_COMMIT;
391#endif
392
393	bdb->bi_databases = (struct bdb_db_info **) ch_malloc(
394		BDB_INDICES * sizeof(struct bdb_db_info *) );
395
396	/* open (and create) main database */
397	for( i = 0; bdbi_databases[i].name.bv_val; i++ ) {
398		struct bdb_db_info *db;
399
400		db = (struct bdb_db_info *) ch_calloc(1, sizeof(struct bdb_db_info));
401
402		rc = db_create( &db->bdi_db, bdb->bi_dbenv, 0 );
403		if( rc != 0 ) {
404			snprintf(cr->msg, sizeof(cr->msg),
405				"database \"%s\": db_create(%s) failed: %s (%d).",
406				be->be_suffix[0].bv_val,
407				bdb->bi_dbenv_home, db_strerror(rc), rc );
408			Debug( LDAP_DEBUG_ANY,
409				LDAP_XSTRING(bdb_db_open) ": %s\n",
410				cr->msg, 0, 0 );
411			goto fail;
412		}
413
414		if( !BER_BVISNULL( &bdb->bi_db_crypt_key )) {
415			rc = db->bdi_db->set_flags( db->bdi_db, DB_ENCRYPT );
416			if ( rc ) {
417				snprintf(cr->msg, sizeof(cr->msg),
418					"database \"%s\": db set_flags(DB_ENCRYPT)(%s) failed: %s (%d).",
419					be->be_suffix[0].bv_val,
420					bdb->bi_dbenv_home, db_strerror(rc), rc );
421				Debug( LDAP_DEBUG_ANY,
422					LDAP_XSTRING(bdb_db_open) ": %s\n",
423					cr->msg, 0, 0 );
424				goto fail;
425			}
426		}
427
428		if( bdb->bi_flags & BDB_CHKSUM ) {
429			rc = db->bdi_db->set_flags( db->bdi_db, DB_CHKSUM );
430			if ( rc ) {
431				snprintf(cr->msg, sizeof(cr->msg),
432					"database \"%s\": db set_flags(DB_CHKSUM)(%s) failed: %s (%d).",
433					be->be_suffix[0].bv_val,
434					bdb->bi_dbenv_home, db_strerror(rc), rc );
435				Debug( LDAP_DEBUG_ANY,
436					LDAP_XSTRING(bdb_db_open) ": %s\n",
437					cr->msg, 0, 0 );
438				goto fail;
439			}
440		}
441
442		rc = bdb_db_findsize( bdb, (struct berval *)&bdbi_databases[i].name );
443
444		if( i == BDB_ID2ENTRY ) {
445			if ( !rc ) rc = BDB_ID2ENTRY_PAGESIZE;
446			rc = db->bdi_db->set_pagesize( db->bdi_db, rc );
447
448			if ( slapMode & SLAP_TOOL_MODE )
449				db->bdi_db->mpf->set_priority( db->bdi_db->mpf,
450					DB_PRIORITY_VERY_LOW );
451
452			if ( slapMode & SLAP_TOOL_READMAIN ) {
453				flags |= DB_RDONLY;
454			} else {
455				flags |= DB_CREATE;
456			}
457		} else {
458			/* Use FS default size if not configured */
459			if ( rc )
460				rc = db->bdi_db->set_pagesize( db->bdi_db, rc );
461
462			rc = db->bdi_db->set_flags( db->bdi_db,
463				DB_DUP | DB_DUPSORT );
464#ifndef BDB_HIER
465			if ( slapMode & SLAP_TOOL_READONLY ) {
466				flags |= DB_RDONLY;
467			} else {
468				flags |= DB_CREATE;
469			}
470#else
471			rc = db->bdi_db->set_dup_compare( db->bdi_db,
472				bdb_dup_compare );
473			if ( slapMode & (SLAP_TOOL_READONLY|SLAP_TOOL_READMAIN) ) {
474				flags |= DB_RDONLY;
475			} else {
476				flags |= DB_CREATE;
477			}
478#endif
479		}
480
481#ifdef HAVE_EBCDIC
482		strcpy( path, bdbi_databases[i].file );
483		__atoe( path );
484		rc = DB_OPEN( db->bdi_db,
485			path,
486		/*	bdbi_databases[i].name, */ NULL,
487			bdbi_databases[i].type,
488			bdbi_databases[i].flags | flags,
489			bdb->bi_dbenv_mode );
490#else
491		rc = DB_OPEN( db->bdi_db,
492			bdbi_databases[i].file,
493		/*	bdbi_databases[i].name, */ NULL,
494			bdbi_databases[i].type,
495			bdbi_databases[i].flags | flags,
496			bdb->bi_dbenv_mode );
497#endif
498
499		if ( rc != 0 ) {
500			snprintf( cr->msg, sizeof(cr->msg), "database \"%s\": "
501				"db_open(%s/%s) failed: %s (%d).",
502				be->be_suffix[0].bv_val,
503				bdb->bi_dbenv_home, bdbi_databases[i].file,
504				db_strerror(rc), rc );
505			Debug( LDAP_DEBUG_ANY,
506				LDAP_XSTRING(bdb_db_open) ": %s\n",
507				cr->msg, 0, 0 );
508			db->bdi_db->close( db->bdi_db, 0 );
509			goto fail;
510		}
511
512		flags &= ~(DB_CREATE | DB_RDONLY);
513		db->bdi_name = bdbi_databases[i].name;
514		bdb->bi_databases[i] = db;
515	}
516
517	bdb->bi_databases[i] = NULL;
518	bdb->bi_ndatabases = i;
519
520	/* get nextid */
521	rc = bdb_last_id( be, NULL );
522	if( rc != 0 ) {
523		snprintf( cr->msg, sizeof(cr->msg), "database \"%s\": "
524			"last_id(%s) failed: %s (%d).",
525			be->be_suffix[0].bv_val, bdb->bi_dbenv_home,
526			db_strerror(rc), rc );
527		Debug( LDAP_DEBUG_ANY,
528			LDAP_XSTRING(bdb_db_open) ": %s\n",
529			cr->msg, 0, 0 );
530		goto fail;
531	}
532
533	if ( !quick ) {
534		TXN_BEGIN(bdb->bi_dbenv, NULL, &bdb->bi_cache.c_txn, DB_READ_COMMITTED | DB_TXN_NOWAIT);
535	}
536
537	entry_prealloc( bdb->bi_cache.c_maxsize );
538	attr_prealloc( bdb->bi_cache.c_maxsize * 20 );
539
540	/* setup for empty-DN contexts */
541	if ( BER_BVISEMPTY( &be->be_nsuffix[0] )) {
542		rc = bdb_id2entry( be, NULL, 0, &e );
543	}
544	if ( !e ) {
545		struct berval gluebv = BER_BVC("glue");
546		Operation op = {0};
547		Opheader ohdr = {0};
548		e = entry_alloc();
549		e->e_id = 0;
550		ber_dupbv( &e->e_name, (struct berval *)&slap_empty_bv );
551		ber_dupbv( &e->e_nname, (struct berval *)&slap_empty_bv );
552		attr_merge_one( e, slap_schema.si_ad_objectClass,
553			&gluebv, NULL );
554		attr_merge_one( e, slap_schema.si_ad_structuralObjectClass,
555			&gluebv, NULL );
556		op.o_hdr = &ohdr;
557		op.o_bd = be;
558		op.ora_e = e;
559		op.o_dn = be->be_rootdn;
560		op.o_ndn = be->be_rootndn;
561		slap_add_opattrs( &op, NULL, NULL, 0, 0 );
562	}
563	e->e_ocflags = SLAP_OC_GLUE|SLAP_OC__END;
564	e->e_private = &bdb->bi_cache.c_dntree;
565	bdb->bi_cache.c_dntree.bei_e = e;
566
567	/* monitor setup */
568	rc = bdb_monitor_db_open( be );
569	if ( rc != 0 ) {
570		goto fail;
571	}
572
573	bdb->bi_flags |= BDB_IS_OPEN;
574
575	return 0;
576
577fail:
578	bdb_db_close( be, NULL );
579	return rc;
580}
581
582static int
583bdb_db_close( BackendDB *be, ConfigReply *cr )
584{
585	int rc;
586	struct bdb_info *bdb = (struct bdb_info *) be->be_private;
587	struct bdb_db_info *db;
588	bdb_idl_cache_entry_t *entry, *next_entry;
589
590	/* monitor handling */
591	(void)bdb_monitor_db_close( be );
592
593	{
594		Entry *e = bdb->bi_cache.c_dntree.bei_e;
595		if ( e ) {
596			bdb->bi_cache.c_dntree.bei_e = NULL;
597			e->e_private = NULL;
598			bdb_entry_return( e );
599		}
600	}
601
602	bdb->bi_flags &= ~BDB_IS_OPEN;
603
604	ber_bvarray_free( bdb->bi_db_config );
605	bdb->bi_db_config = NULL;
606
607	if( bdb->bi_dbenv ) {
608		/* Free cache locker if we enabled locking.
609		 * TXNs must all be closed before DBs...
610		 */
611		if ( !( slapMode & SLAP_TOOL_QUICK ) && bdb->bi_cache.c_txn ) {
612			TXN_ABORT( bdb->bi_cache.c_txn );
613			bdb->bi_cache.c_txn = NULL;
614		}
615		bdb_reader_flush( bdb->bi_dbenv );
616	}
617
618	while( bdb->bi_databases && bdb->bi_ndatabases-- ) {
619		db = bdb->bi_databases[bdb->bi_ndatabases];
620		rc = db->bdi_db->close( db->bdi_db, 0 );
621		/* Lower numbered names are not strdup'd */
622		if( bdb->bi_ndatabases >= BDB_NDB )
623			free( db->bdi_name.bv_val );
624		free( db );
625	}
626	free( bdb->bi_databases );
627	bdb->bi_databases = NULL;
628
629	bdb_cache_release_all (&bdb->bi_cache);
630
631	if ( bdb->bi_idl_cache_size ) {
632		avl_free( bdb->bi_idl_tree, NULL );
633		bdb->bi_idl_tree = NULL;
634		entry = bdb->bi_idl_lru_head;
635		do {
636			next_entry = entry->idl_lru_next;
637			if ( entry->idl )
638				free( entry->idl );
639			free( entry->kstr.bv_val );
640			free( entry );
641			entry = next_entry;
642		} while ( entry != bdb->bi_idl_lru_head );
643		bdb->bi_idl_lru_head = bdb->bi_idl_lru_tail = NULL;
644	}
645
646	/* close db environment */
647	if( bdb->bi_dbenv ) {
648		/* force a checkpoint, but not if we were ReadOnly,
649		 * and not in Quick mode since there are no transactions there.
650		 */
651		if ( !( slapMode & ( SLAP_TOOL_QUICK|SLAP_TOOL_READONLY ))) {
652			rc = TXN_CHECKPOINT( bdb->bi_dbenv, 0, 0, DB_FORCE );
653			if( rc != 0 ) {
654				Debug( LDAP_DEBUG_ANY,
655					"bdb_db_close: database \"%s\": "
656					"txn_checkpoint failed: %s (%d).\n",
657					be->be_suffix[0].bv_val, db_strerror(rc), rc );
658			}
659		}
660
661		rc = bdb->bi_dbenv->close( bdb->bi_dbenv, 0 );
662		bdb->bi_dbenv = NULL;
663		if( rc != 0 ) {
664			Debug( LDAP_DEBUG_ANY,
665				"bdb_db_close: database \"%s\": "
666				"close failed: %s (%d)\n",
667				be->be_suffix[0].bv_val, db_strerror(rc), rc );
668			return rc;
669		}
670	}
671
672	rc = alock_close( &bdb->bi_alock_info, slapMode & SLAP_TOOL_QUICK );
673	if( rc != 0 ) {
674		Debug( LDAP_DEBUG_ANY,
675			"bdb_db_close: database \"%s\": alock_close failed\n",
676			be->be_suffix[0].bv_val, 0, 0 );
677		return -1;
678	}
679
680	return 0;
681}
682
683static int
684bdb_db_destroy( BackendDB *be, ConfigReply *cr )
685{
686	struct bdb_info *bdb = (struct bdb_info *) be->be_private;
687
688	/* stop and remove checkpoint task */
689	if ( bdb->bi_txn_cp_task ) {
690		struct re_s *re = bdb->bi_txn_cp_task;
691		bdb->bi_txn_cp_task = NULL;
692		ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
693		if ( ldap_pvt_runqueue_isrunning( &slapd_rq, re ) )
694			ldap_pvt_runqueue_stoptask( &slapd_rq, re );
695		ldap_pvt_runqueue_remove( &slapd_rq, re );
696		ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
697	}
698
699	/* monitor handling */
700	(void)bdb_monitor_db_destroy( be );
701
702	if( bdb->bi_dbenv_home ) ch_free( bdb->bi_dbenv_home );
703	if( bdb->bi_db_config_path ) ch_free( bdb->bi_db_config_path );
704
705	bdb_attr_index_destroy( bdb );
706
707	ldap_pvt_thread_rdwr_destroy ( &bdb->bi_cache.c_rwlock );
708	ldap_pvt_thread_mutex_destroy( &bdb->bi_cache.c_lru_mutex );
709	ldap_pvt_thread_mutex_destroy( &bdb->bi_cache.c_count_mutex );
710	ldap_pvt_thread_mutex_destroy( &bdb->bi_cache.c_eifree_mutex );
711	ldap_pvt_thread_mutex_destroy( &bdb->bi_cache.c_dntree.bei_kids_mutex );
712#ifdef BDB_HIER
713	ldap_pvt_thread_mutex_destroy( &bdb->bi_modrdns_mutex );
714#endif
715	ldap_pvt_thread_mutex_destroy( &bdb->bi_lastid_mutex );
716	ldap_pvt_thread_mutex_destroy( &bdb->bi_database_mutex );
717	ldap_pvt_thread_rdwr_destroy( &bdb->bi_idl_tree_rwlock );
718	ldap_pvt_thread_mutex_destroy( &bdb->bi_idl_tree_lrulock );
719
720	ch_free( bdb );
721	be->be_private = NULL;
722
723	return 0;
724}
725
726int
727bdb_back_initialize(
728	BackendInfo	*bi )
729{
730	int rc;
731
732	static char *controls[] = {
733		LDAP_CONTROL_ASSERT,
734		LDAP_CONTROL_MANAGEDSAIT,
735		LDAP_CONTROL_NOOP,
736		LDAP_CONTROL_PAGEDRESULTS,
737		LDAP_CONTROL_PRE_READ,
738		LDAP_CONTROL_POST_READ,
739		LDAP_CONTROL_SUBENTRIES,
740		LDAP_CONTROL_X_PERMISSIVE_MODIFY,
741#ifdef LDAP_X_TXN
742		LDAP_CONTROL_X_TXN_SPEC,
743#endif
744		NULL
745	};
746
747	/* initialize the underlying database system */
748	Debug( LDAP_DEBUG_TRACE,
749		LDAP_XSTRING(bdb_back_initialize) ": initialize "
750		BDB_UCTYPE " backend\n", 0, 0, 0 );
751
752	bi->bi_flags |=
753		SLAP_BFLAG_INCREMENT |
754		SLAP_BFLAG_SUBENTRIES |
755		SLAP_BFLAG_ALIASES |
756		SLAP_BFLAG_REFERRALS;
757
758	bi->bi_controls = controls;
759
760	{	/* version check */
761		int major, minor, patch, ver;
762		char *version = db_version( &major, &minor, &patch );
763#ifdef HAVE_EBCDIC
764		char v2[1024];
765
766		/* All our stdio does an ASCII to EBCDIC conversion on
767		 * the output. Strings from the BDB library are already
768		 * in EBCDIC; we have to go back and forth...
769		 */
770		strcpy( v2, version );
771		__etoa( v2 );
772		version = v2;
773#endif
774
775		ver = (major << 24) | (minor << 16) | patch;
776		if( ver != DB_VERSION_FULL ) {
777			/* fail if a versions don't match */
778			Debug( LDAP_DEBUG_ANY,
779				LDAP_XSTRING(bdb_back_initialize) ": "
780				"BDB library version mismatch:"
781				" expected " DB_VERSION_STRING ","
782				" got %s\n", version, 0, 0 );
783			return -1;
784		}
785
786		Debug( LDAP_DEBUG_TRACE, LDAP_XSTRING(bdb_back_initialize)
787			": %s\n", version, 0, 0 );
788	}
789
790	db_env_set_func_free( ber_memfree );
791	db_env_set_func_malloc( (db_malloc *)ber_memalloc );
792	db_env_set_func_realloc( (db_realloc *)ber_memrealloc );
793#if !defined(NO_THREAD) && DB_VERSION_FULL <= 0x04070000
794	/* This is a no-op on a NO_THREAD build. Leave the default
795	 * alone so that BDB will sleep on interprocess conflicts.
796	 * Don't bother on BDB 4.7...
797	 */
798	db_env_set_func_yield( ldap_pvt_thread_yield );
799#endif
800
801	bi->bi_open = 0;
802	bi->bi_close = 0;
803	bi->bi_config = 0;
804	bi->bi_destroy = 0;
805
806	bi->bi_db_init = bdb_db_init;
807	bi->bi_db_config = config_generic_wrapper;
808	bi->bi_db_open = bdb_db_open;
809	bi->bi_db_close = bdb_db_close;
810	bi->bi_db_destroy = bdb_db_destroy;
811
812	bi->bi_op_add = bdb_add;
813	bi->bi_op_bind = bdb_bind;
814	bi->bi_op_compare = bdb_compare;
815	bi->bi_op_delete = bdb_delete;
816	bi->bi_op_modify = bdb_modify;
817	bi->bi_op_modrdn = bdb_modrdn;
818	bi->bi_op_search = bdb_search;
819
820	bi->bi_op_unbind = 0;
821
822	bi->bi_extended = bdb_extended;
823
824	bi->bi_chk_referrals = bdb_referrals;
825	bi->bi_operational = bdb_operational;
826	bi->bi_has_subordinates = bdb_hasSubordinates;
827	bi->bi_entry_release_rw = bdb_entry_release;
828	bi->bi_entry_get_rw = bdb_entry_get;
829
830	/*
831	 * hooks for slap tools
832	 */
833	bi->bi_tool_entry_open = bdb_tool_entry_open;
834	bi->bi_tool_entry_close = bdb_tool_entry_close;
835	bi->bi_tool_entry_first = backend_tool_entry_first;
836	bi->bi_tool_entry_first_x = bdb_tool_entry_first_x;
837	bi->bi_tool_entry_next = bdb_tool_entry_next;
838	bi->bi_tool_entry_get = bdb_tool_entry_get;
839	bi->bi_tool_entry_put = bdb_tool_entry_put;
840	bi->bi_tool_entry_reindex = bdb_tool_entry_reindex;
841	bi->bi_tool_sync = 0;
842	bi->bi_tool_dn2id_get = bdb_tool_dn2id_get;
843	bi->bi_tool_entry_modify = bdb_tool_entry_modify;
844
845	bi->bi_connection_init = 0;
846	bi->bi_connection_destroy = 0;
847
848	rc = bdb_back_init_cf( bi );
849
850	return rc;
851}
852
853#if	(SLAPD_BDB == SLAPD_MOD_DYNAMIC && !defined(BDB_HIER)) || \
854	(SLAPD_HDB == SLAPD_MOD_DYNAMIC && defined(BDB_HIER))
855
856/* conditionally define the init_module() function */
857#ifdef BDB_HIER
858SLAP_BACKEND_INIT_MODULE( hdb )
859#else /* !BDB_HIER */
860SLAP_BACKEND_INIT_MODULE( bdb )
861#endif /* !BDB_HIER */
862
863#endif /* SLAPD_[BH]DB == SLAPD_MOD_DYNAMIC */
864
865