Home | History | Annotate | Line # | Download | only in back-mdb
init.c revision 1.1.1.1
      1 /*	$NetBSD: init.c,v 1.1.1.1 2014/05/28 09:58:50 tron Exp $	*/
      2 
      3 /* init.c - initialize mdb backend */
      4 /* $OpenLDAP$ */
      5 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
      6  *
      7  * Copyright 2000-2014 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-mdb.h"
     28 #include <lutil.h>
     29 #include <ldap_rq.h>
     30 #include "config.h"
     31 
     32 static const struct berval mdmi_databases[] = {
     33 	BER_BVC("ad2i"),
     34 	BER_BVC("dn2i"),
     35 	BER_BVC("id2e"),
     36 	BER_BVNULL
     37 };
     38 
     39 static int
     40 mdb_id_compare( const MDB_val *a, const MDB_val *b )
     41 {
     42 	return *(ID *)a->mv_data < *(ID *)b->mv_data ? -1 : *(ID *)a->mv_data > *(ID *)b->mv_data;
     43 }
     44 
     45 static int
     46 mdb_db_init( BackendDB *be, ConfigReply *cr )
     47 {
     48 	struct mdb_info	*mdb;
     49 	int rc;
     50 
     51 	Debug( LDAP_DEBUG_TRACE,
     52 		LDAP_XSTRING(mdb_db_init) ": Initializing mdb database\n",
     53 		0, 0, 0 );
     54 
     55 	/* allocate backend-database-specific stuff */
     56 	mdb = (struct mdb_info *) ch_calloc( 1, sizeof(struct mdb_info) );
     57 
     58 	/* DBEnv parameters */
     59 	mdb->mi_dbenv_home = ch_strdup( SLAPD_DEFAULT_DB_DIR );
     60 	mdb->mi_dbenv_flags = 0;
     61 	mdb->mi_dbenv_mode = SLAPD_DEFAULT_DB_MODE;
     62 
     63 	mdb->mi_search_stack_depth = DEFAULT_SEARCH_STACK_DEPTH;
     64 	mdb->mi_search_stack = NULL;
     65 
     66 	mdb->mi_mapsize = DEFAULT_MAPSIZE;
     67 
     68 	be->be_private = mdb;
     69 	be->be_cf_ocs = be->bd_info->bi_cf_ocs;
     70 
     71 #ifndef MDB_MULTIPLE_SUFFIXES
     72 	SLAP_DBFLAGS( be ) |= SLAP_DBFLAG_ONE_SUFFIX;
     73 #endif
     74 
     75 	rc = mdb_monitor_db_init( be );
     76 
     77 	return rc;
     78 }
     79 
     80 static int
     81 mdb_db_close( BackendDB *be, ConfigReply *cr );
     82 
     83 static int
     84 mdb_db_open( BackendDB *be, ConfigReply *cr )
     85 {
     86 	int rc, i;
     87 	struct mdb_info *mdb = (struct mdb_info *) be->be_private;
     88 	struct stat stat1;
     89 	uint32_t flags;
     90 	char *dbhome;
     91 	MDB_txn *txn;
     92 
     93 	if ( be->be_suffix == NULL ) {
     94 		Debug( LDAP_DEBUG_ANY,
     95 			LDAP_XSTRING(mdb_db_open) ": need suffix.\n",
     96 			1, 0, 0 );
     97 		return -1;
     98 	}
     99 
    100 	Debug( LDAP_DEBUG_ARGS,
    101 		LDAP_XSTRING(mdb_db_open) ": \"%s\"\n",
    102 		be->be_suffix[0].bv_val, 0, 0 );
    103 
    104 	/* Check existence of dbenv_home. Any error means trouble */
    105 	rc = stat( mdb->mi_dbenv_home, &stat1 );
    106 	if( rc != 0 ) {
    107 		Debug( LDAP_DEBUG_ANY,
    108 			LDAP_XSTRING(mdb_db_open) ": database \"%s\": "
    109 			"cannot access database directory \"%s\" (%d).\n",
    110 			be->be_suffix[0].bv_val, mdb->mi_dbenv_home, errno );
    111 		return -1;
    112 	}
    113 
    114 	/* mdb is always clean */
    115 	be->be_flags |= SLAP_DBFLAG_CLEAN;
    116 
    117 	rc = mdb_env_create( &mdb->mi_dbenv );
    118 	if( rc != 0 ) {
    119 		Debug( LDAP_DEBUG_ANY,
    120 			LDAP_XSTRING(mdb_db_open) ": database \"%s\": "
    121 			"mdb_env_create failed: %s (%d).\n",
    122 			be->be_suffix[0].bv_val, mdb_strerror(rc), rc );
    123 		goto fail;
    124 	}
    125 
    126 	if ( mdb->mi_readers ) {
    127 		rc = mdb_env_set_maxreaders( mdb->mi_dbenv, mdb->mi_readers );
    128 		if( rc != 0 ) {
    129 			Debug( LDAP_DEBUG_ANY,
    130 				LDAP_XSTRING(mdb_db_open) ": database \"%s\": "
    131 				"mdb_env_set_maxreaders failed: %s (%d).\n",
    132 				be->be_suffix[0].bv_val, mdb_strerror(rc), rc );
    133 			goto fail;
    134 		}
    135 	}
    136 
    137 	rc = mdb_env_set_mapsize( mdb->mi_dbenv, mdb->mi_mapsize );
    138 	if( rc != 0 ) {
    139 		Debug( LDAP_DEBUG_ANY,
    140 			LDAP_XSTRING(mdb_db_open) ": database \"%s\": "
    141 			"mdb_env_set_mapsize failed: %s (%d).\n",
    142 			be->be_suffix[0].bv_val, mdb_strerror(rc), rc );
    143 		goto fail;
    144 	}
    145 
    146 	rc = mdb_env_set_maxdbs( mdb->mi_dbenv, MDB_INDICES );
    147 	if( rc != 0 ) {
    148 		Debug( LDAP_DEBUG_ANY,
    149 			LDAP_XSTRING(mdb_db_open) ": database \"%s\": "
    150 			"mdb_env_set_maxdbs failed: %s (%d).\n",
    151 			be->be_suffix[0].bv_val, mdb_strerror(rc), rc );
    152 		goto fail;
    153 	}
    154 
    155 #ifdef HAVE_EBCDIC
    156 	strcpy( path, mdb->mi_dbenv_home );
    157 	__atoe( path );
    158 	dbhome = path;
    159 #else
    160 	dbhome = mdb->mi_dbenv_home;
    161 #endif
    162 
    163 	Debug( LDAP_DEBUG_TRACE,
    164 		LDAP_XSTRING(mdb_db_open) ": database \"%s\": "
    165 		"dbenv_open(%s).\n",
    166 		be->be_suffix[0].bv_val, mdb->mi_dbenv_home, 0);
    167 
    168 	flags = mdb->mi_dbenv_flags;
    169 
    170 	if ( slapMode & SLAP_TOOL_QUICK )
    171 		flags |= MDB_NOSYNC|MDB_WRITEMAP;
    172 
    173 	if ( slapMode & SLAP_TOOL_READONLY)
    174 		flags |= MDB_RDONLY;
    175 
    176 	rc = mdb_env_open( mdb->mi_dbenv, dbhome,
    177 			flags, mdb->mi_dbenv_mode );
    178 
    179 	if ( rc ) {
    180 		Debug( LDAP_DEBUG_ANY,
    181 			LDAP_XSTRING(mdb_db_open) ": database \"%s\" cannot be opened, err %d. "
    182 			"Restore from backup!\n",
    183 			be->be_suffix[0].bv_val, rc, 0 );
    184 		goto fail;
    185 	}
    186 
    187 	rc = mdb_txn_begin( mdb->mi_dbenv, NULL, flags & MDB_RDONLY, &txn );
    188 	if ( rc ) {
    189 		Debug( LDAP_DEBUG_ANY,
    190 			LDAP_XSTRING(mdb_db_open) ": database \"%s\" cannot be opened, err %d. "
    191 			"Restore from backup!\n",
    192 			be->be_suffix[0].bv_val, rc, 0 );
    193 		goto fail;
    194 	}
    195 
    196 	/* open (and create) main databases */
    197 	for( i = 0; mdmi_databases[i].bv_val; i++ ) {
    198 		flags = MDB_INTEGERKEY;
    199 		if( i == MDB_ID2ENTRY ) {
    200 			if ( !(slapMode & (SLAP_TOOL_READMAIN|SLAP_TOOL_READONLY) ))
    201 				flags |= MDB_CREATE;
    202 		} else {
    203 			if ( i == MDB_DN2ID )
    204 				flags |= MDB_DUPSORT;
    205 			if ( !(slapMode & SLAP_TOOL_READONLY) )
    206 				flags |= MDB_CREATE;
    207 		}
    208 
    209 		rc = mdb_dbi_open( txn,
    210 			mdmi_databases[i].bv_val,
    211 			flags,
    212 			&mdb->mi_dbis[i] );
    213 
    214 		if ( rc != 0 ) {
    215 			snprintf( cr->msg, sizeof(cr->msg), "database \"%s\": "
    216 				"mdb_dbi_open(%s/%s) failed: %s (%d).",
    217 				be->be_suffix[0].bv_val,
    218 				mdb->mi_dbenv_home, mdmi_databases[i].bv_val,
    219 				mdb_strerror(rc), rc );
    220 			Debug( LDAP_DEBUG_ANY,
    221 				LDAP_XSTRING(mdb_db_open) ": %s\n",
    222 				cr->msg, 0, 0 );
    223 			goto fail;
    224 		}
    225 
    226 		if ( i == MDB_ID2ENTRY )
    227 			mdb_set_compare( txn, mdb->mi_dbis[i], mdb_id_compare );
    228 		else if ( i == MDB_DN2ID ) {
    229 			MDB_cursor *mc;
    230 			MDB_val key, data;
    231 			ID id;
    232 			mdb_set_dupsort( txn, mdb->mi_dbis[i], mdb_dup_compare );
    233 			/* check for old dn2id format */
    234 			rc = mdb_cursor_open( txn, mdb->mi_dbis[i], &mc );
    235 			/* first record is always ID 0 */
    236 			rc = mdb_cursor_get( mc, &key, &data, MDB_FIRST );
    237 			if ( rc == 0 ) {
    238 				rc = mdb_cursor_get( mc, &key, &data, MDB_NEXT );
    239 				if ( rc == 0 ) {
    240 					int len;
    241 					unsigned char *ptr;
    242 					ptr = data.mv_data;
    243 					len = (ptr[0] & 0x7f) << 8 | ptr[1];
    244 					if (data.mv_size < 2*len + 4 + 2*sizeof(ID)) {
    245 						snprintf( cr->msg, sizeof(cr->msg),
    246 						"database \"%s\": DN index needs upgrade, "
    247 						"run \"slapindex entryDN\".",
    248 						be->be_suffix[0].bv_val );
    249 						Debug( LDAP_DEBUG_ANY,
    250 							LDAP_XSTRING(mdb_db_open) ": %s\n",
    251 							cr->msg, 0, 0 );
    252 						if ( !(slapMode & SLAP_TOOL_READMAIN ))
    253 							rc = LDAP_OTHER;
    254 						mdb->mi_flags |= MDB_NEED_UPGRADE;
    255 					}
    256 				}
    257 			}
    258 			mdb_cursor_close( mc );
    259 			if ( rc == LDAP_OTHER )
    260 				goto fail;
    261 		}
    262 	}
    263 
    264 	rc = mdb_ad_read( mdb, txn );
    265 	if ( rc ) {
    266 		mdb_txn_abort( txn );
    267 		goto fail;
    268 	}
    269 
    270 	rc = mdb_attr_dbs_open( be, txn, cr );
    271 	if ( rc ) {
    272 		mdb_txn_abort( txn );
    273 		goto fail;
    274 	}
    275 
    276 	rc = mdb_txn_commit(txn);
    277 	if ( rc != 0 ) {
    278 		Debug( LDAP_DEBUG_ANY,
    279 			LDAP_XSTRING(mdb_db_open) ": database %s: "
    280 			"txn_commit failed: %s (%d)\n",
    281 			be->be_suffix[0].bv_val, mdb_strerror(rc), rc );
    282 		goto fail;
    283 	}
    284 
    285 	/* monitor setup */
    286 	rc = mdb_monitor_db_open( be );
    287 	if ( rc != 0 ) {
    288 		goto fail;
    289 	}
    290 
    291 	mdb->mi_flags |= MDB_IS_OPEN;
    292 
    293 	return 0;
    294 
    295 fail:
    296 	mdb_db_close( be, NULL );
    297 	return rc;
    298 }
    299 
    300 static int
    301 mdb_db_close( BackendDB *be, ConfigReply *cr )
    302 {
    303 	int rc;
    304 	struct mdb_info *mdb = (struct mdb_info *) be->be_private;
    305 
    306 	/* monitor handling */
    307 	(void)mdb_monitor_db_close( be );
    308 
    309 	mdb->mi_flags &= ~MDB_IS_OPEN;
    310 
    311 	if( mdb->mi_dbenv ) {
    312 		mdb_reader_flush( mdb->mi_dbenv );
    313 	}
    314 
    315 	if ( mdb->mi_dbenv ) {
    316 		if ( mdb->mi_dbis[0] ) {
    317 			int i;
    318 
    319 			mdb_attr_dbs_close( mdb );
    320 			for ( i=0; i<MDB_NDB; i++ )
    321 				mdb_dbi_close( mdb->mi_dbenv, mdb->mi_dbis[i] );
    322 
    323 			/* force a sync, but not if we were ReadOnly,
    324 			 * and not in Quick mode.
    325 			 */
    326 			if (!(slapMode & (SLAP_TOOL_QUICK|SLAP_TOOL_READONLY))) {
    327 				rc = mdb_env_sync( mdb->mi_dbenv, 1 );
    328 				if( rc != 0 ) {
    329 					Debug( LDAP_DEBUG_ANY,
    330 						"mdb_db_close: database \"%s\": "
    331 						"mdb_env_sync failed: %s (%d).\n",
    332 						be->be_suffix[0].bv_val, mdb_strerror(rc), rc );
    333 				}
    334 			}
    335 		}
    336 
    337 		mdb_env_close( mdb->mi_dbenv );
    338 		mdb->mi_dbenv = NULL;
    339 	}
    340 
    341 	return 0;
    342 }
    343 
    344 static int
    345 mdb_db_destroy( BackendDB *be, ConfigReply *cr )
    346 {
    347 	struct mdb_info *mdb = (struct mdb_info *) be->be_private;
    348 
    349 	/* stop and remove checkpoint task */
    350 	if ( mdb->mi_txn_cp_task ) {
    351 		struct re_s *re = mdb->mi_txn_cp_task;
    352 		mdb->mi_txn_cp_task = NULL;
    353 		ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
    354 		if ( ldap_pvt_runqueue_isrunning( &slapd_rq, re ) )
    355 			ldap_pvt_runqueue_stoptask( &slapd_rq, re );
    356 		ldap_pvt_runqueue_remove( &slapd_rq, re );
    357 		ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
    358 	}
    359 
    360 	/* monitor handling */
    361 	(void)mdb_monitor_db_destroy( be );
    362 
    363 	if( mdb->mi_dbenv_home ) ch_free( mdb->mi_dbenv_home );
    364 
    365 	mdb_attr_index_destroy( mdb );
    366 
    367 	ch_free( mdb );
    368 	be->be_private = NULL;
    369 
    370 	return 0;
    371 }
    372 
    373 int
    374 mdb_back_initialize(
    375 	BackendInfo	*bi )
    376 {
    377 	int rc;
    378 
    379 	static char *controls[] = {
    380 		LDAP_CONTROL_ASSERT,
    381 		LDAP_CONTROL_MANAGEDSAIT,
    382 		LDAP_CONTROL_NOOP,
    383 		LDAP_CONTROL_PAGEDRESULTS,
    384 		LDAP_CONTROL_PRE_READ,
    385 		LDAP_CONTROL_POST_READ,
    386 		LDAP_CONTROL_SUBENTRIES,
    387 		LDAP_CONTROL_X_PERMISSIVE_MODIFY,
    388 #ifdef LDAP_X_TXN
    389 		LDAP_CONTROL_X_TXN_SPEC,
    390 #endif
    391 		NULL
    392 	};
    393 
    394 	/* initialize the underlying database system */
    395 	Debug( LDAP_DEBUG_TRACE,
    396 		LDAP_XSTRING(mdb_back_initialize) ": initialize "
    397 		MDB_UCTYPE " backend\n", 0, 0, 0 );
    398 
    399 	bi->bi_flags |=
    400 		SLAP_BFLAG_INCREMENT |
    401 		SLAP_BFLAG_SUBENTRIES |
    402 		SLAP_BFLAG_ALIASES |
    403 		SLAP_BFLAG_REFERRALS;
    404 
    405 	bi->bi_controls = controls;
    406 
    407 	{	/* version check */
    408 		int major, minor, patch, ver;
    409 		char *version = mdb_version( &major, &minor, &patch );
    410 #ifdef HAVE_EBCDIC
    411 		char v2[1024];
    412 
    413 		/* All our stdio does an ASCII to EBCDIC conversion on
    414 		 * the output. Strings from the MDB library are already
    415 		 * in EBCDIC; we have to go back and forth...
    416 		 */
    417 		strcpy( v2, version );
    418 		__etoa( v2 );
    419 		version = v2;
    420 #endif
    421 		ver = (major << 24) | (minor << 16) | patch;
    422 		if( ver != MDB_VERSION_FULL ) {
    423 			/* fail if a versions don't match */
    424 			Debug( LDAP_DEBUG_ANY,
    425 				LDAP_XSTRING(mdb_back_initialize) ": "
    426 				"MDB library version mismatch:"
    427 				" expected " MDB_VERSION_STRING ","
    428 				" got %s\n", version, 0, 0 );
    429 			return -1;
    430 		}
    431 
    432 		Debug( LDAP_DEBUG_TRACE, LDAP_XSTRING(mdb_back_initialize)
    433 			": %s\n", version, 0, 0 );
    434 	}
    435 
    436 	bi->bi_open = 0;
    437 	bi->bi_close = 0;
    438 	bi->bi_config = 0;
    439 	bi->bi_destroy = 0;
    440 
    441 	bi->bi_db_init = mdb_db_init;
    442 	bi->bi_db_config = config_generic_wrapper;
    443 	bi->bi_db_open = mdb_db_open;
    444 	bi->bi_db_close = mdb_db_close;
    445 	bi->bi_db_destroy = mdb_db_destroy;
    446 
    447 	bi->bi_op_add = mdb_add;
    448 	bi->bi_op_bind = mdb_bind;
    449 	bi->bi_op_compare = mdb_compare;
    450 	bi->bi_op_delete = mdb_delete;
    451 	bi->bi_op_modify = mdb_modify;
    452 	bi->bi_op_modrdn = mdb_modrdn;
    453 	bi->bi_op_search = mdb_search;
    454 
    455 	bi->bi_op_unbind = 0;
    456 
    457 	bi->bi_extended = mdb_extended;
    458 
    459 	bi->bi_chk_referrals = 0;
    460 	bi->bi_operational = mdb_operational;
    461 
    462 	bi->bi_has_subordinates = mdb_hasSubordinates;
    463 	bi->bi_entry_release_rw = mdb_entry_release;
    464 	bi->bi_entry_get_rw = mdb_entry_get;
    465 
    466 	/*
    467 	 * hooks for slap tools
    468 	 */
    469 	bi->bi_tool_entry_open = mdb_tool_entry_open;
    470 	bi->bi_tool_entry_close = mdb_tool_entry_close;
    471 	bi->bi_tool_entry_first = backend_tool_entry_first;
    472 	bi->bi_tool_entry_first_x = mdb_tool_entry_first_x;
    473 	bi->bi_tool_entry_next = mdb_tool_entry_next;
    474 	bi->bi_tool_entry_get = mdb_tool_entry_get;
    475 	bi->bi_tool_entry_put = mdb_tool_entry_put;
    476 	bi->bi_tool_entry_reindex = mdb_tool_entry_reindex;
    477 	bi->bi_tool_sync = 0;
    478 	bi->bi_tool_dn2id_get = mdb_tool_dn2id_get;
    479 	bi->bi_tool_entry_modify = mdb_tool_entry_modify;
    480 
    481 	bi->bi_connection_init = 0;
    482 	bi->bi_connection_destroy = 0;
    483 
    484 	rc = mdb_back_init_cf( bi );
    485 
    486 	return rc;
    487 }
    488 
    489 #if	(SLAPD_MDB == SLAPD_MOD_DYNAMIC)
    490 
    491 SLAP_BACKEND_INIT_MODULE( mdb )
    492 
    493 #endif /* SLAPD_MDB == SLAPD_MOD_DYNAMIC */
    494 
    495