]> git.ipfire.org Git - thirdparty/openldap.git/blob - servers/slapd/back-mdb/init.c
Happy New Year!
[thirdparty/openldap.git] / servers / slapd / back-mdb / init.c
1 /* init.c - initialize mdb backend */
2 /* $OpenLDAP$ */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4 *
5 * Copyright 2000-2024 The OpenLDAP Foundation.
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted only as authorized by the OpenLDAP
10 * Public License.
11 *
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>.
15 */
16
17 #include "portable.h"
18
19 #include <stdio.h>
20 #include <ac/string.h>
21 #include <ac/unistd.h>
22 #include <ac/stdlib.h>
23 #include <ac/errno.h>
24 #include <sys/stat.h>
25 #include "back-mdb.h"
26 #include <lutil.h>
27 #include <ldap_rq.h>
28 #include "slap-config.h"
29
30 static const struct berval mdmi_databases[] = {
31 BER_BVC("ad2i"),
32 BER_BVC("dn2i"),
33 BER_BVC("id2e"),
34 BER_BVC("id2v"),
35 BER_BVC("ixck"),
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
54 /* allocate backend-database-specific stuff */
55 mdb = (struct mdb_info *) ch_calloc( 1, sizeof(struct mdb_info) );
56
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;
61
62 mdb->mi_search_stack_depth = DEFAULT_SEARCH_STACK_DEPTH;
63 mdb->mi_search_stack = NULL;
64
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;
69
70 be->be_private = mdb;
71 be->be_cf_ocs = be->bd_info->bi_cf_ocs+1;
72
73 #ifndef MDB_MULTIPLE_SUFFIXES
74 SLAP_DBFLAGS( be ) |= SLAP_DBFLAG_ONE_SUFFIX;
75 #endif
76
77 rc = mdb_monitor_db_init( be );
78
79 return rc;
80 }
81
82 static int
83 mdb_db_close( BackendDB *be, ConfigReply *cr );
84
85 static int
86 mdb_db_open( BackendDB *be, ConfigReply *cr )
87 {
88 int rc, i;
89 struct mdb_info *mdb = (struct mdb_info *) be->be_private;
90 struct stat stat1;
91 unsigned flags;
92 char *dbhome;
93 MDB_txn *txn;
94 int do_index = 0;
95
96 if ( be->be_suffix == NULL ) {
97 Debug( LDAP_DEBUG_ANY,
98 LDAP_XSTRING(mdb_db_open) ": need suffix.\n" );
99 return -1;
100 }
101
102 Debug( LDAP_DEBUG_ARGS,
103 LDAP_XSTRING(mdb_db_open) ": \"%s\"\n",
104 be->be_suffix[0].bv_val );
105
106 /* Check existence of dbenv_home. Any error means trouble */
107 rc = stat( mdb->mi_dbenv_home, &stat1 );
108 if( rc != 0 ) {
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 );
114 return -1;
115 }
116
117 /* mdb is always clean */
118 be->be_flags |= SLAP_DBFLAG_CLEAN;
119
120 rc = mdb_env_create( &mdb->mi_dbenv );
121 if( rc != 0 ) {
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 );
126 goto fail;
127 }
128
129 if ( mdb->mi_readers ) {
130 rc = mdb_env_set_maxreaders( mdb->mi_dbenv, mdb->mi_readers );
131 if( rc != 0 ) {
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 );
136 goto fail;
137 }
138 }
139
140 rc = mdb_env_set_mapsize( mdb->mi_dbenv, mdb->mi_mapsize );
141 if( rc != 0 ) {
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 );
146 goto fail;
147 }
148
149 rc = mdb_env_set_maxdbs( mdb->mi_dbenv, MDB_INDICES );
150 if( rc != 0 ) {
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 );
155 goto fail;
156 }
157
158 #ifdef HAVE_EBCDIC
159 strcpy( path, mdb->mi_dbenv_home );
160 __atoe( path );
161 dbhome = path;
162 #else
163 dbhome = mdb->mi_dbenv_home;
164 #endif
165
166 Debug( LDAP_DEBUG_TRACE,
167 LDAP_XSTRING(mdb_db_open) ": database \"%s\": "
168 "dbenv_open(%s).\n",
169 be->be_suffix[0].bv_val, mdb->mi_dbenv_home );
170
171 flags = mdb->mi_dbenv_flags;
172
173 if ( slapMode & SLAP_TOOL_QUICK )
174 flags |= MDB_NOSYNC|MDB_WRITEMAP;
175
176 if ( slapMode & SLAP_TOOL_READONLY)
177 flags |= MDB_RDONLY;
178
179 rc = mdb_env_open( mdb->mi_dbenv, dbhome,
180 flags, mdb->mi_dbenv_mode );
181
182 if ( rc ) {
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 );
187 goto fail;
188 }
189
190 rc = mdb_txn_begin( mdb->mi_dbenv, NULL, flags & MDB_RDONLY, &txn );
191 if ( rc ) {
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 );
196 goto fail;
197 }
198
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) ))
204 flags |= MDB_CREATE;
205 } else {
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) )
211 flags |= MDB_CREATE;
212 }
213
214 rc = mdb_dbi_open( txn,
215 mdmi_databases[i].bv_val,
216 flags,
217 &mdb->mi_dbis[i] );
218
219 if ( rc != 0 ) {
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",
229 cr->msg );
230 goto fail;
231 }
232 }
233
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 ) {
240 MDB_cursor *mc;
241 MDB_val key, data;
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 );
247 if ( rc == 0 ) {
248 rc = mdb_cursor_get( mc, &key, &data, MDB_NEXT );
249 if ( rc == 0 ) {
250 int len;
251 unsigned char *ptr;
252 ptr = data.mv_data;
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",
261 cr->msg );
262 if ( !(slapMode & SLAP_TOOL_READMAIN ))
263 rc = LDAP_OTHER;
264 mdb->mi_flags |= MDB_NEED_UPGRADE;
265 }
266 }
267 }
268 mdb_cursor_close( mc );
269 if ( rc == LDAP_OTHER )
270 goto fail;
271 }
272 }
273
274 rc = mdb_ad_read( mdb, txn );
275 if ( rc ) {
276 mdb_txn_abort( txn );
277 goto fail;
278 }
279
280 /* slapcat doesn't need indexes. avoid a failure if
281 * a configured index wasn't created yet.
282 */
283 if ( !(slapMode & SLAP_TOOL_READONLY) ) {
284 rc = mdb_attr_dbs_open( be, txn, cr );
285 if ( rc ) {
286 mdb_txn_abort( txn );
287 goto fail;
288 }
289 }
290
291 if ( slapMode & SLAP_SERVER_MODE ) {
292 MDB_stat st;
293 rc = mdb_stat( txn, mdb->mi_idxckp, &st );
294 if ( st.ms_entries )
295 do_index = mdb_resume_index( be, txn );
296 }
297
298 rc = mdb_txn_commit(txn);
299 if ( rc != 0 ) {
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 );
304 goto fail;
305 }
306
307 /* monitor setup */
308 rc = mdb_monitor_db_open( be );
309 if ( rc != 0 ) {
310 goto fail;
311 }
312
313 mdb->mi_flags |= MDB_IS_OPEN;
314
315 if ( do_index )
316 mdb_start_index_task( be->bd_self );
317
318 return 0;
319
320 fail:
321 mdb_db_close( be, NULL );
322 return rc;
323 }
324
325 static int
326 mdb_db_close( BackendDB *be, ConfigReply *cr )
327 {
328 int rc;
329 struct mdb_info *mdb = (struct mdb_info *) be->be_private;
330
331 /* monitor handling */
332 (void)mdb_monitor_db_close( be );
333
334 mdb->mi_flags &= ~MDB_IS_OPEN;
335
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 );
346 }
347
348 if ( mdb->mi_dbenv ) {
349 mdb_reader_flush( mdb->mi_dbenv );
350
351 if ( mdb->mi_dbis[0] ) {
352 int i;
353
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] );
357
358 /* force a sync, but not if we were ReadOnly,
359 * and not in Quick mode.
360 */
361 if (!(slapMode & (SLAP_TOOL_QUICK|SLAP_TOOL_READONLY))) {
362 rc = mdb_env_sync( mdb->mi_dbenv, 1 );
363 if( rc != 0 ) {
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 );
368 }
369 }
370 }
371
372 mdb_env_close( mdb->mi_dbenv );
373 mdb->mi_dbenv = NULL;
374 }
375
376 return 0;
377 }
378
379 static int
380 mdb_db_destroy( BackendDB *be, ConfigReply *cr )
381 {
382 struct mdb_info *mdb = (struct mdb_info *) be->be_private;
383
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 );
393 }
394
395 /* monitor handling */
396 (void)mdb_monitor_db_destroy( be );
397
398 if( mdb->mi_dbenv_home ) ch_free( mdb->mi_dbenv_home );
399
400 mdb_attr_index_destroy( mdb );
401
402 ch_free( mdb );
403 be->be_private = NULL;
404
405 return 0;
406 }
407
408 int
409 mdb_back_initialize(
410 BackendInfo *bi )
411 {
412 int rc;
413
414 static char *controls[] = {
415 LDAP_CONTROL_ASSERT,
416 LDAP_CONTROL_MANAGEDSAIT,
417 LDAP_CONTROL_NOOP,
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,
424 NULL
425 };
426
427 /* initialize the underlying database system */
428 Debug( LDAP_DEBUG_TRACE,
429 LDAP_XSTRING(mdb_back_initialize) ": initialize "
430 MDB_UCTYPE " backend\n" );
431
432 bi->bi_flags |=
433 SLAP_BFLAG_INCREMENT |
434 SLAP_BFLAG_SUBENTRIES |
435 SLAP_BFLAG_ALIASES |
436 SLAP_BFLAG_REFERRALS |
437 SLAP_BFLAG_TXNS;
438
439 bi->bi_controls = controls;
440
441 { /* version check */
442 int major, minor, patch, ver;
443 char *version = mdb_version( &major, &minor, &patch );
444 #ifdef HAVE_EBCDIC
445 char v2[1024];
446
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...
450 */
451 strcpy( v2, version );
452 __etoa( v2 );
453 version = v2;
454 #endif
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 );
463 return -1;
464 }
465
466 Debug( LDAP_DEBUG_TRACE, LDAP_XSTRING(mdb_back_initialize)
467 ": %s\n", version );
468 }
469
470 bi->bi_open = 0;
471 bi->bi_close = 0;
472 bi->bi_config = 0;
473 bi->bi_destroy = 0;
474
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;
480
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;
488
489 bi->bi_op_unbind = 0;
490 bi->bi_op_txn = mdb_txn;
491
492 bi->bi_extended = mdb_extended;
493
494 bi->bi_chk_referrals = 0;
495 bi->bi_operational = mdb_operational;
496
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;
500
501 /*
502 * hooks for slap tools
503 */
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;
516
517 bi->bi_connection_init = 0;
518 bi->bi_connection_destroy = 0;
519
520 rc = mdb_back_init_cf( bi );
521
522 return rc;
523 }
524
525 #if (SLAPD_MDB == SLAPD_MOD_DYNAMIC)
526
527 SLAP_BACKEND_INIT_MODULE( mdb )
528
529 #endif /* SLAPD_MDB == SLAPD_MOD_DYNAMIC */
530