1 /* init.c - initialize mdb backend */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5 * Copyright 2000-2024 The OpenLDAP Foundation.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted only as authorized by the OpenLDAP
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>.
20 #include <ac/string.h>
21 #include <ac/unistd.h>
22 #include <ac/stdlib.h>
28 #include "slap-config.h"
30 static const struct berval mdmi_databases
[] = {
40 mdb_id_compare( const MDB_val
*a
, const MDB_val
*b
)
42 return *(ID
*)a
->mv_data
< *(ID
*)b
->mv_data
? -1 : *(ID
*)a
->mv_data
> *(ID
*)b
->mv_data
;
46 mdb_db_init( BackendDB
*be
, ConfigReply
*cr
)
51 Debug( LDAP_DEBUG_TRACE
,
52 LDAP_XSTRING(mdb_db_init
) ": Initializing mdb database\n" );
54 /* allocate backend-database-specific stuff */
55 mdb
= (struct mdb_info
*) ch_calloc( 1, sizeof(struct mdb_info
) );
57 /* DBEnv parameters */
58 mdb
->mi_dbenv_home
= ch_strdup( SLAPD_DEFAULT_DB_DIR
);
59 mdb
->mi_dbenv_flags
= 0;
60 mdb
->mi_dbenv_mode
= SLAPD_DEFAULT_DB_MODE
;
62 mdb
->mi_search_stack_depth
= DEFAULT_SEARCH_STACK_DEPTH
;
63 mdb
->mi_search_stack
= NULL
;
65 mdb
->mi_mapsize
= DEFAULT_MAPSIZE
;
66 mdb
->mi_rtxn_size
= DEFAULT_RTXN_SIZE
;
67 mdb
->mi_multi_hi
= UINT_MAX
;
68 mdb
->mi_multi_lo
= UINT_MAX
;
71 be
->be_cf_ocs
= be
->bd_info
->bi_cf_ocs
+1;
73 #ifndef MDB_MULTIPLE_SUFFIXES
74 SLAP_DBFLAGS( be
) |= SLAP_DBFLAG_ONE_SUFFIX
;
77 rc
= mdb_monitor_db_init( be
);
83 mdb_db_close( BackendDB
*be
, ConfigReply
*cr
);
86 mdb_db_open( BackendDB
*be
, ConfigReply
*cr
)
89 struct mdb_info
*mdb
= (struct mdb_info
*) be
->be_private
;
96 if ( be
->be_suffix
== NULL
) {
97 Debug( LDAP_DEBUG_ANY
,
98 LDAP_XSTRING(mdb_db_open
) ": need suffix.\n" );
102 Debug( LDAP_DEBUG_ARGS
,
103 LDAP_XSTRING(mdb_db_open
) ": \"%s\"\n",
104 be
->be_suffix
[0].bv_val
);
106 /* Check existence of dbenv_home. Any error means trouble */
107 rc
= stat( mdb
->mi_dbenv_home
, &stat1
);
109 int saved_errno
= errno
;
110 Debug( LDAP_DEBUG_ANY
,
111 LDAP_XSTRING(mdb_db_open
) ": database \"%s\": "
112 "cannot access database directory \"%s\" (%d).\n",
113 be
->be_suffix
[0].bv_val
, mdb
->mi_dbenv_home
, saved_errno
);
117 /* mdb is always clean */
118 be
->be_flags
|= SLAP_DBFLAG_CLEAN
;
120 rc
= mdb_env_create( &mdb
->mi_dbenv
);
122 Debug( LDAP_DEBUG_ANY
,
123 LDAP_XSTRING(mdb_db_open
) ": database \"%s\": "
124 "mdb_env_create failed: %s (%d).\n",
125 be
->be_suffix
[0].bv_val
, mdb_strerror(rc
), rc
);
129 if ( mdb
->mi_readers
) {
130 rc
= mdb_env_set_maxreaders( mdb
->mi_dbenv
, mdb
->mi_readers
);
132 Debug( LDAP_DEBUG_ANY
,
133 LDAP_XSTRING(mdb_db_open
) ": database \"%s\": "
134 "mdb_env_set_maxreaders failed: %s (%d).\n",
135 be
->be_suffix
[0].bv_val
, mdb_strerror(rc
), rc
);
140 rc
= mdb_env_set_mapsize( mdb
->mi_dbenv
, mdb
->mi_mapsize
);
142 Debug( LDAP_DEBUG_ANY
,
143 LDAP_XSTRING(mdb_db_open
) ": database \"%s\": "
144 "mdb_env_set_mapsize failed: %s (%d).\n",
145 be
->be_suffix
[0].bv_val
, mdb_strerror(rc
), rc
);
149 rc
= mdb_env_set_maxdbs( mdb
->mi_dbenv
, MDB_INDICES
);
151 Debug( LDAP_DEBUG_ANY
,
152 LDAP_XSTRING(mdb_db_open
) ": database \"%s\": "
153 "mdb_env_set_maxdbs failed: %s (%d).\n",
154 be
->be_suffix
[0].bv_val
, mdb_strerror(rc
), rc
);
159 strcpy( path
, mdb
->mi_dbenv_home
);
163 dbhome
= mdb
->mi_dbenv_home
;
166 Debug( LDAP_DEBUG_TRACE
,
167 LDAP_XSTRING(mdb_db_open
) ": database \"%s\": "
169 be
->be_suffix
[0].bv_val
, mdb
->mi_dbenv_home
);
171 flags
= mdb
->mi_dbenv_flags
;
173 if ( slapMode
& SLAP_TOOL_QUICK
)
174 flags
|= MDB_NOSYNC
|MDB_WRITEMAP
;
176 if ( slapMode
& SLAP_TOOL_READONLY
)
179 rc
= mdb_env_open( mdb
->mi_dbenv
, dbhome
,
180 flags
, mdb
->mi_dbenv_mode
);
183 Debug( LDAP_DEBUG_ANY
,
184 LDAP_XSTRING(mdb_db_open
) ": database \"%s\" cannot be opened: %s (%d). "
185 "Administrator intervention needed!\n",
186 be
->be_suffix
[0].bv_val
, mdb_strerror(rc
), rc
);
190 rc
= mdb_txn_begin( mdb
->mi_dbenv
, NULL
, flags
& MDB_RDONLY
, &txn
);
192 Debug( LDAP_DEBUG_ANY
,
193 LDAP_XSTRING(mdb_db_open
) ": database \"%s\" cannot be opened: %s (%d). "
194 "Administrator intervention needed!\n",
195 be
->be_suffix
[0].bv_val
, mdb_strerror(rc
), rc
);
199 /* open (and create) main databases */
200 for( i
= 0; mdmi_databases
[i
].bv_val
; i
++ ) {
201 flags
= MDB_INTEGERKEY
;
202 if( i
== MDB_ID2ENTRY
) {
203 if ( !(slapMode
& (SLAP_TOOL_READMAIN
|SLAP_TOOL_READONLY
) ))
206 if ( i
== MDB_DN2ID
)
207 flags
|= MDB_DUPSORT
;
208 if ( i
== MDB_ID2VAL
)
209 flags
^= MDB_INTEGERKEY
|MDB_DUPSORT
;
210 if ( !(slapMode
& SLAP_TOOL_READONLY
) )
214 rc
= mdb_dbi_open( txn
,
215 mdmi_databases
[i
].bv_val
,
220 /* when read-only, it's ok for ID2VAL or IDXCKP to not exist */
221 if (( flags
& MDB_CREATE
) || ( i
< MDB_ID2VAL
)) {
222 snprintf( cr
->msg
, sizeof(cr
->msg
), "database \"%s\": "
223 "mdb_dbi_open(%s/%s) failed: %s (%d).",
224 be
->be_suffix
[0].bv_val
,
225 mdb
->mi_dbenv_home
, mdmi_databases
[i
].bv_val
,
226 mdb_strerror(rc
), rc
);
227 Debug( LDAP_DEBUG_ANY
,
228 LDAP_XSTRING(mdb_db_open
) ": %s\n",
234 if ( i
== MDB_ID2ENTRY
)
235 mdb_set_compare( txn
, mdb
->mi_dbis
[i
], mdb_id_compare
);
236 else if ( i
== MDB_ID2VAL
) {
237 mdb_set_compare( txn
, mdb
->mi_dbis
[i
], mdb_id2v_compare
);
238 mdb_set_dupsort( txn
, mdb
->mi_dbis
[i
], mdb_id2v_dupsort
);
239 } else if ( i
== MDB_DN2ID
) {
242 mdb_set_dupsort( txn
, mdb
->mi_dbis
[i
], mdb_dup_compare
);
243 /* check for old dn2id format */
244 rc
= mdb_cursor_open( txn
, mdb
->mi_dbis
[i
], &mc
);
245 /* first record is always ID 0 */
246 rc
= mdb_cursor_get( mc
, &key
, &data
, MDB_FIRST
);
248 rc
= mdb_cursor_get( mc
, &key
, &data
, MDB_NEXT
);
253 len
= (ptr
[0] & 0x7f) << 8 | ptr
[1];
254 if (data
.mv_size
< 2*len
+ 4 + 2*sizeof(ID
)) {
255 snprintf( cr
->msg
, sizeof(cr
->msg
),
256 "database \"%s\": DN index needs upgrade, "
257 "run \"slapindex entryDN\".",
258 be
->be_suffix
[0].bv_val
);
259 Debug( LDAP_DEBUG_ANY
,
260 LDAP_XSTRING(mdb_db_open
) ": %s\n",
262 if ( !(slapMode
& SLAP_TOOL_READMAIN
))
264 mdb
->mi_flags
|= MDB_NEED_UPGRADE
;
268 mdb_cursor_close( mc
);
269 if ( rc
== LDAP_OTHER
)
274 rc
= mdb_ad_read( mdb
, txn
);
276 mdb_txn_abort( txn
);
280 /* slapcat doesn't need indexes. avoid a failure if
281 * a configured index wasn't created yet.
283 if ( !(slapMode
& SLAP_TOOL_READONLY
) ) {
284 rc
= mdb_attr_dbs_open( be
, txn
, cr
);
286 mdb_txn_abort( txn
);
291 if ( slapMode
& SLAP_SERVER_MODE
) {
293 rc
= mdb_stat( txn
, mdb
->mi_idxckp
, &st
);
295 do_index
= mdb_resume_index( be
, txn
);
298 rc
= mdb_txn_commit(txn
);
300 Debug( LDAP_DEBUG_ANY
,
301 LDAP_XSTRING(mdb_db_open
) ": database %s: "
302 "txn_commit failed: %s (%d)\n",
303 be
->be_suffix
[0].bv_val
, mdb_strerror(rc
), rc
);
308 rc
= mdb_monitor_db_open( be
);
313 mdb
->mi_flags
|= MDB_IS_OPEN
;
316 mdb_start_index_task( be
->bd_self
);
321 mdb_db_close( be
, NULL
);
326 mdb_db_close( BackendDB
*be
, ConfigReply
*cr
)
329 struct mdb_info
*mdb
= (struct mdb_info
*) be
->be_private
;
331 /* monitor handling */
332 (void)mdb_monitor_db_close( be
);
334 mdb
->mi_flags
&= ~MDB_IS_OPEN
;
336 /* remove indexer task */
337 if ( mdb
->mi_index_task
) {
338 struct re_s
*re
= mdb
->mi_index_task
;
339 ldap_pvt_thread_mutex_lock( &slapd_rq
.rq_mutex
);
340 mdb
->mi_index_task
= NULL
;
341 /* can never actually be running at this point, but paranoia */
342 if ( ldap_pvt_runqueue_isrunning( &slapd_rq
, re
) )
343 ldap_pvt_runqueue_stoptask( &slapd_rq
, re
);
344 ldap_pvt_runqueue_remove( &slapd_rq
, re
);
345 ldap_pvt_thread_mutex_unlock( &slapd_rq
.rq_mutex
);
348 if ( mdb
->mi_dbenv
) {
349 mdb_reader_flush( mdb
->mi_dbenv
);
351 if ( mdb
->mi_dbis
[0] ) {
354 mdb_attr_dbs_close( mdb
);
355 for ( i
=0; i
<MDB_NDB
; i
++ )
356 mdb_dbi_close( mdb
->mi_dbenv
, mdb
->mi_dbis
[i
] );
358 /* force a sync, but not if we were ReadOnly,
359 * and not in Quick mode.
361 if (!(slapMode
& (SLAP_TOOL_QUICK
|SLAP_TOOL_READONLY
))) {
362 rc
= mdb_env_sync( mdb
->mi_dbenv
, 1 );
364 Debug( LDAP_DEBUG_ANY
,
365 "mdb_db_close: database \"%s\": "
366 "mdb_env_sync failed: %s (%d).\n",
367 be
->be_suffix
[0].bv_val
, mdb_strerror(rc
), rc
);
372 mdb_env_close( mdb
->mi_dbenv
);
373 mdb
->mi_dbenv
= NULL
;
380 mdb_db_destroy( BackendDB
*be
, ConfigReply
*cr
)
382 struct mdb_info
*mdb
= (struct mdb_info
*) be
->be_private
;
384 /* stop and remove checkpoint task */
385 if ( mdb
->mi_txn_cp_task
) {
386 struct re_s
*re
= mdb
->mi_txn_cp_task
;
387 mdb
->mi_txn_cp_task
= NULL
;
388 ldap_pvt_thread_mutex_lock( &slapd_rq
.rq_mutex
);
389 if ( ldap_pvt_runqueue_isrunning( &slapd_rq
, re
) )
390 ldap_pvt_runqueue_stoptask( &slapd_rq
, re
);
391 ldap_pvt_runqueue_remove( &slapd_rq
, re
);
392 ldap_pvt_thread_mutex_unlock( &slapd_rq
.rq_mutex
);
395 /* monitor handling */
396 (void)mdb_monitor_db_destroy( be
);
398 if( mdb
->mi_dbenv_home
) ch_free( mdb
->mi_dbenv_home
);
400 mdb_attr_index_destroy( mdb
);
403 be
->be_private
= NULL
;
414 static char *controls
[] = {
416 LDAP_CONTROL_MANAGEDSAIT
,
418 LDAP_CONTROL_PAGEDRESULTS
,
419 LDAP_CONTROL_PRE_READ
,
420 LDAP_CONTROL_POST_READ
,
421 LDAP_CONTROL_SUBENTRIES
,
422 LDAP_CONTROL_X_PERMISSIVE_MODIFY
,
423 LDAP_CONTROL_TXN_SPEC
,
427 /* initialize the underlying database system */
428 Debug( LDAP_DEBUG_TRACE
,
429 LDAP_XSTRING(mdb_back_initialize
) ": initialize "
430 MDB_UCTYPE
" backend\n" );
433 SLAP_BFLAG_INCREMENT
|
434 SLAP_BFLAG_SUBENTRIES
|
436 SLAP_BFLAG_REFERRALS
|
439 bi
->bi_controls
= controls
;
441 { /* version check */
442 int major
, minor
, patch
, ver
;
443 char *version
= mdb_version( &major
, &minor
, &patch
);
447 /* All our stdio does an ASCII to EBCDIC conversion on
448 * the output. Strings from the MDB library are already
449 * in EBCDIC; we have to go back and forth...
451 strcpy( v2
, version
);
455 ver
= (major
<< 24) | (minor
<< 16) | patch
;
456 if( ver
!= MDB_VERSION_FULL
) {
457 /* fail if a versions don't match */
458 Debug( LDAP_DEBUG_ANY
,
459 LDAP_XSTRING(mdb_back_initialize
) ": "
460 "MDB library version mismatch:"
461 " expected " MDB_VERSION_STRING
","
462 " got %s\n", version
);
466 Debug( LDAP_DEBUG_TRACE
, LDAP_XSTRING(mdb_back_initialize
)
475 bi
->bi_db_init
= mdb_db_init
;
476 bi
->bi_db_config
= config_generic_wrapper
;
477 bi
->bi_db_open
= mdb_db_open
;
478 bi
->bi_db_close
= mdb_db_close
;
479 bi
->bi_db_destroy
= mdb_db_destroy
;
481 bi
->bi_op_add
= mdb_add
;
482 bi
->bi_op_bind
= mdb_bind
;
483 bi
->bi_op_compare
= mdb_compare
;
484 bi
->bi_op_delete
= mdb_delete
;
485 bi
->bi_op_modify
= mdb_modify
;
486 bi
->bi_op_modrdn
= mdb_modrdn
;
487 bi
->bi_op_search
= mdb_search
;
489 bi
->bi_op_unbind
= 0;
490 bi
->bi_op_txn
= mdb_txn
;
492 bi
->bi_extended
= mdb_extended
;
494 bi
->bi_chk_referrals
= 0;
495 bi
->bi_operational
= mdb_operational
;
497 bi
->bi_has_subordinates
= mdb_hasSubordinates
;
498 bi
->bi_entry_release_rw
= mdb_entry_release
;
499 bi
->bi_entry_get_rw
= mdb_entry_get
;
502 * hooks for slap tools
504 bi
->bi_tool_entry_open
= mdb_tool_entry_open
;
505 bi
->bi_tool_entry_close
= mdb_tool_entry_close
;
506 bi
->bi_tool_entry_first
= backend_tool_entry_first
;
507 bi
->bi_tool_entry_first_x
= mdb_tool_entry_first_x
;
508 bi
->bi_tool_entry_next
= mdb_tool_entry_next
;
509 bi
->bi_tool_entry_get
= mdb_tool_entry_get
;
510 bi
->bi_tool_entry_put
= mdb_tool_entry_put
;
511 bi
->bi_tool_entry_reindex
= mdb_tool_entry_reindex
;
512 bi
->bi_tool_sync
= 0;
513 bi
->bi_tool_dn2id_get
= mdb_tool_dn2id_get
;
514 bi
->bi_tool_entry_modify
= mdb_tool_entry_modify
;
515 bi
->bi_tool_entry_delete
= mdb_tool_entry_delete
;
517 bi
->bi_connection_init
= 0;
518 bi
->bi_connection_destroy
= 0;
520 rc
= mdb_back_init_cf( bi
);
525 #if (SLAPD_MDB == SLAPD_MOD_DYNAMIC)
527 SLAP_BACKEND_INIT_MODULE( mdb
)
529 #endif /* SLAPD_MDB == SLAPD_MOD_DYNAMIC */