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