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