xref: /netbsd-src/external/bsd/openldap/dist/servers/slapd/back-mdb/config.c (revision b7b7574d3bf8eeb51a1fa3977b59142ec6434a55)
1 /*	$NetBSD: config.c,v 1.1.1.1 2014/05/28 09:58:49 tron Exp $	*/
2 
3 /* config.c - mdb backend configuration file routine */
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/ctype.h>
23 #include <ac/string.h>
24 #include <ac/errno.h>
25 
26 #include "back-mdb.h"
27 
28 #include "config.h"
29 
30 #include "lutil.h"
31 #include "ldap_rq.h"
32 
33 static ConfigDriver mdb_cf_gen;
34 
35 enum {
36 	MDB_CHKPT = 1,
37 	MDB_DIRECTORY,
38 	MDB_DBNOSYNC,
39 	MDB_ENVFLAGS,
40 	MDB_INDEX,
41 	MDB_MAXREADERS,
42 	MDB_MAXSIZE,
43 	MDB_MODE,
44 	MDB_SSTACK
45 };
46 
47 static ConfigTable mdbcfg[] = {
48 	{ "directory", "dir", 2, 2, 0, ARG_STRING|ARG_MAGIC|MDB_DIRECTORY,
49 		mdb_cf_gen, "( OLcfgDbAt:0.1 NAME 'olcDbDirectory' "
50 			"DESC 'Directory for database content' "
51 			"EQUALITY caseIgnoreMatch "
52 			"SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
53 	{ "checkpoint", "kbyte> <min", 3, 3, 0, ARG_MAGIC|MDB_CHKPT,
54 		mdb_cf_gen, "( OLcfgDbAt:1.2 NAME 'olcDbCheckpoint' "
55 			"DESC 'Database checkpoint interval in kbytes and minutes' "
56 			"SYNTAX OMsDirectoryString SINGLE-VALUE )",NULL, NULL },
57 	{ "dbnosync", NULL, 1, 2, 0, ARG_ON_OFF|ARG_MAGIC|MDB_DBNOSYNC,
58 		mdb_cf_gen, "( OLcfgDbAt:1.4 NAME 'olcDbNoSync' "
59 			"DESC 'Disable synchronous database writes' "
60 			"SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
61 	{ "envflags", "flags", 2, 0, 0, ARG_MAGIC|MDB_ENVFLAGS,
62 		mdb_cf_gen, "( OLcfgDbAt:12.3 NAME 'olcDbEnvFlags' "
63 			"DESC 'Database environment flags' "
64 			"EQUALITY caseIgnoreMatch "
65 			"SYNTAX OMsDirectoryString )", NULL, NULL },
66 	{ "index", "attr> <[pres,eq,approx,sub]", 2, 3, 0, ARG_MAGIC|MDB_INDEX,
67 		mdb_cf_gen, "( OLcfgDbAt:0.2 NAME 'olcDbIndex' "
68 		"DESC 'Attribute index parameters' "
69 		"EQUALITY caseIgnoreMatch "
70 		"SYNTAX OMsDirectoryString )", NULL, NULL },
71 	{ "maxreaders", "num", 2, 2, 0, ARG_UINT|ARG_MAGIC|MDB_MAXREADERS,
72 		mdb_cf_gen, "( OLcfgDbAt:12.1 NAME 'olcDbMaxReaders' "
73 		"DESC 'Maximum number of threads that may access the DB concurrently' "
74 		"SYNTAX OMsInteger SINGLE-VALUE )", NULL, NULL },
75 	{ "maxsize", "size", 2, 2, 0, ARG_ULONG|ARG_MAGIC|MDB_MAXSIZE,
76 		mdb_cf_gen, "( OLcfgDbAt:12.2 NAME 'olcDbMaxSize' "
77 		"DESC 'Maximum size of DB in bytes' "
78 		"SYNTAX OMsInteger SINGLE-VALUE )", NULL, NULL },
79 	{ "mode", "mode", 2, 2, 0, ARG_MAGIC|MDB_MODE,
80 		mdb_cf_gen, "( OLcfgDbAt:0.3 NAME 'olcDbMode' "
81 		"DESC 'Unix permissions of database files' "
82 		"SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
83 	{ "searchstack", "depth", 2, 2, 0, ARG_INT|ARG_MAGIC|MDB_SSTACK,
84 		mdb_cf_gen, "( OLcfgDbAt:1.9 NAME 'olcDbSearchStack' "
85 		"DESC 'Depth of search stack in IDLs' "
86 		"SYNTAX OMsInteger SINGLE-VALUE )", NULL, NULL },
87 	{ NULL, NULL, 0, 0, 0, ARG_IGNORED,
88 		NULL, NULL, NULL, NULL }
89 };
90 
91 static ConfigOCs mdbocs[] = {
92 	{
93 		"( OLcfgDbOc:12.1 "
94 		"NAME 'olcMdbConfig' "
95 		"DESC 'MDB backend configuration' "
96 		"SUP olcDatabaseConfig "
97 		"MUST olcDbDirectory "
98 		"MAY ( olcDbCheckpoint $ olcDbEnvFlags $ "
99 		"olcDbNoSync $ olcDbIndex $ olcDbMaxReaders $ olcDbMaxsize $ "
100 		"olcDbMode $ olcDbSearchStack ) )",
101 		 	Cft_Database, mdbcfg },
102 	{ NULL, 0, NULL }
103 };
104 
105 static slap_verbmasks mdb_envflags[] = {
106 	{ BER_BVC("nosync"),	MDB_NOSYNC },
107 	{ BER_BVC("nometasync"),	MDB_NOMETASYNC },
108 	{ BER_BVC("writemap"),	MDB_WRITEMAP },
109 	{ BER_BVC("mapasync"),	MDB_MAPASYNC },
110 	{ BER_BVC("nordahead"),	MDB_NORDAHEAD },
111 	{ BER_BVNULL, 0 }
112 };
113 
114 /* perform periodic syncs */
115 static void *
116 mdb_checkpoint( void *ctx, void *arg )
117 {
118 	struct re_s *rtask = arg;
119 	struct mdb_info *mdb = rtask->arg;
120 
121 	mdb_env_sync( mdb->mi_dbenv, 1 );
122 	ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
123 	ldap_pvt_runqueue_stoptask( &slapd_rq, rtask );
124 	ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
125 	return NULL;
126 }
127 
128 /* reindex entries on the fly */
129 static void *
130 mdb_online_index( void *ctx, void *arg )
131 {
132 	struct re_s *rtask = arg;
133 	BackendDB *be = rtask->arg;
134 	struct mdb_info *mdb = be->be_private;
135 
136 	Connection conn = {0};
137 	OperationBuffer opbuf;
138 	Operation *op;
139 
140 	MDB_cursor *curs;
141 	MDB_val key, data;
142 	MDB_txn *txn;
143 	ID id;
144 	Entry *e;
145 	int rc, getnext = 1;
146 	int i;
147 
148 	connection_fake_init( &conn, &opbuf, ctx );
149 	op = &opbuf.ob_op;
150 
151 	op->o_bd = be;
152 
153 	id = 1;
154 	key.mv_size = sizeof(ID);
155 
156 	while ( 1 ) {
157 		if ( slapd_shutdown )
158 			break;
159 
160 		rc = mdb_txn_begin( mdb->mi_dbenv, NULL, 0, &txn );
161 		if ( rc )
162 			break;
163 		rc = mdb_cursor_open( txn, mdb->mi_id2entry, &curs );
164 		if ( rc ) {
165 			mdb_txn_abort( txn );
166 			break;
167 		}
168 		if ( getnext ) {
169 			getnext = 0;
170 			key.mv_data = &id;
171 			rc = mdb_cursor_get( curs, &key, &data, MDB_SET_RANGE );
172 			if ( rc ) {
173 				mdb_txn_abort( txn );
174 				if ( rc == MDB_NOTFOUND )
175 					rc = 0;
176 				break;
177 			}
178 			memcpy( &id, key.mv_data, sizeof( id ));
179 		}
180 
181 		rc = mdb_id2entry( op, curs, id, &e );
182 		mdb_cursor_close( curs );
183 		if ( rc ) {
184 			mdb_txn_abort( txn );
185 			if ( rc == MDB_NOTFOUND ) {
186 				id++;
187 				getnext = 1;
188 				continue;
189 			}
190 			break;
191 		}
192 		rc = mdb_index_entry( op, txn, MDB_INDEX_UPDATE_OP, e );
193 		mdb_entry_return( op, e );
194 		if ( rc == 0 ) {
195 			rc = mdb_txn_commit( txn );
196 			txn = NULL;
197 		} else {
198 			mdb_txn_abort( txn );
199 			txn = NULL;
200 		}
201 		if ( rc ) {
202 			Debug( LDAP_DEBUG_ANY,
203 				LDAP_XSTRING(mdb_online_index) ": database %s: "
204 				"txn_commit failed: %s (%d)\n",
205 				be->be_suffix[0].bv_val, mdb_strerror(rc), rc );
206 			break;
207 		}
208 		id++;
209 		getnext = 1;
210 	}
211 
212 	for ( i = 0; i < mdb->mi_nattrs; i++ ) {
213 		if ( mdb->mi_attrs[ i ]->ai_indexmask & MDB_INDEX_DELETING
214 			|| mdb->mi_attrs[ i ]->ai_newmask == 0 )
215 		{
216 			continue;
217 		}
218 		mdb->mi_attrs[ i ]->ai_indexmask = mdb->mi_attrs[ i ]->ai_newmask;
219 		mdb->mi_attrs[ i ]->ai_newmask = 0;
220 	}
221 
222 	ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
223 	ldap_pvt_runqueue_stoptask( &slapd_rq, rtask );
224 	mdb->mi_index_task = NULL;
225 	ldap_pvt_runqueue_remove( &slapd_rq, rtask );
226 	ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
227 
228 	return NULL;
229 }
230 
231 /* Cleanup loose ends after Modify completes */
232 static int
233 mdb_cf_cleanup( ConfigArgs *c )
234 {
235 	struct mdb_info *mdb = c->be->be_private;
236 	int rc = 0;
237 
238 	if ( mdb->mi_flags & MDB_DEL_INDEX ) {
239 		mdb_attr_flush( mdb );
240 		mdb->mi_flags ^= MDB_DEL_INDEX;
241 	}
242 
243 	if ( mdb->mi_flags & MDB_RE_OPEN ) {
244 		mdb->mi_flags ^= MDB_RE_OPEN;
245 		rc = c->be->bd_info->bi_db_close( c->be, &c->reply );
246 		if ( rc == 0 )
247 			rc = c->be->bd_info->bi_db_open( c->be, &c->reply );
248 		/* If this fails, we need to restart */
249 		if ( rc ) {
250 			slapd_shutdown = 2;
251 			snprintf( c->cr_msg, sizeof( c->cr_msg ),
252 				"failed to reopen database, rc=%d", rc );
253 			Debug( LDAP_DEBUG_ANY, LDAP_XSTRING(mdb_cf_cleanup)
254 				": %s\n", c->cr_msg, 0, 0 );
255 			rc = LDAP_OTHER;
256 		}
257 	}
258 
259 	if ( mdb->mi_flags & MDB_OPEN_INDEX ) {
260 		rc = mdb_attr_dbs_open( c->be, NULL, &c->reply );
261 		if ( rc )
262 			rc = LDAP_OTHER;
263 	}
264 	return rc;
265 }
266 
267 static int
268 mdb_cf_gen( ConfigArgs *c )
269 {
270 	struct mdb_info *mdb = c->be->be_private;
271 	int rc;
272 
273 	if ( c->op == SLAP_CONFIG_EMIT ) {
274 		rc = 0;
275 		switch( c->type ) {
276 		case MDB_MODE: {
277 			char buf[64];
278 			struct berval bv;
279 			bv.bv_len = snprintf( buf, sizeof(buf), "0%o", mdb->mi_dbenv_mode );
280 			if ( bv.bv_len > 0 && bv.bv_len < sizeof(buf) ) {
281 				bv.bv_val = buf;
282 				value_add_one( &c->rvalue_vals, &bv );
283 			} else {
284 				rc = 1;
285 			}
286 			} break;
287 
288 		case MDB_CHKPT:
289 			if ( mdb->mi_txn_cp ) {
290 				char buf[64];
291 				struct berval bv;
292 				bv.bv_len = snprintf( buf, sizeof(buf), "%ld %ld",
293 					(long) mdb->mi_txn_cp_kbyte, (long) mdb->mi_txn_cp_min );
294 				if ( bv.bv_len > 0 && bv.bv_len < sizeof(buf) ) {
295 					bv.bv_val = buf;
296 					value_add_one( &c->rvalue_vals, &bv );
297 				} else {
298 					rc = 1;
299 				}
300 			} else {
301 				rc = 1;
302 			}
303 			break;
304 
305 		case MDB_DIRECTORY:
306 			if ( mdb->mi_dbenv_home ) {
307 				c->value_string = ch_strdup( mdb->mi_dbenv_home );
308 			} else {
309 				rc = 1;
310 			}
311 			break;
312 
313 		case MDB_DBNOSYNC:
314 			if ( mdb->mi_dbenv_flags & MDB_NOSYNC )
315 				c->value_int = 1;
316 			break;
317 
318 		case MDB_ENVFLAGS:
319 			if ( mdb->mi_dbenv_flags ) {
320 				mask_to_verbs( mdb_envflags, mdb->mi_dbenv_flags, &c->rvalue_vals );
321 			}
322 			if ( !c->rvalue_vals ) rc = 1;
323 			break;
324 
325 		case MDB_INDEX:
326 			mdb_attr_index_unparse( mdb, &c->rvalue_vals );
327 			if ( !c->rvalue_vals ) rc = 1;
328 			break;
329 
330 		case MDB_SSTACK:
331 			c->value_int = mdb->mi_search_stack_depth;
332 			break;
333 
334 		case MDB_MAXREADERS:
335 			c->value_int = mdb->mi_readers;
336 			break;
337 
338 		case MDB_MAXSIZE:
339 			c->value_ulong = mdb->mi_mapsize;
340 			break;
341 		}
342 		return rc;
343 	} else if ( c->op == LDAP_MOD_DELETE ) {
344 		rc = 0;
345 		switch( c->type ) {
346 		case MDB_MODE:
347 #if 0
348 			/* FIXME: does it make any sense to change the mode,
349 			 * if we don't exec a chmod()? */
350 			mdb->bi_dbenv_mode = SLAPD_DEFAULT_DB_MODE;
351 			break;
352 #endif
353 
354 		/* single-valued no-ops */
355 		case MDB_SSTACK:
356 		case MDB_MAXREADERS:
357 		case MDB_MAXSIZE:
358 			break;
359 
360 		case MDB_CHKPT:
361 			if ( mdb->mi_txn_cp_task ) {
362 				struct re_s *re = mdb->mi_txn_cp_task;
363 				mdb->mi_txn_cp_task = NULL;
364 				ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
365 				if ( ldap_pvt_runqueue_isrunning( &slapd_rq, re ) )
366 					ldap_pvt_runqueue_stoptask( &slapd_rq, re );
367 				ldap_pvt_runqueue_remove( &slapd_rq, re );
368 				ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
369 			}
370 			mdb->mi_txn_cp = 0;
371 			break;
372 		case MDB_DIRECTORY:
373 			mdb->mi_flags |= MDB_RE_OPEN;
374 			ch_free( mdb->mi_dbenv_home );
375 			mdb->mi_dbenv_home = NULL;
376 			c->cleanup = mdb_cf_cleanup;
377 			ldap_pvt_thread_pool_purgekey( mdb->mi_dbenv );
378 			break;
379 		case MDB_DBNOSYNC:
380 			mdb_env_set_flags( mdb->mi_dbenv, MDB_NOSYNC, 0 );
381 			mdb->mi_dbenv_flags &= ~MDB_NOSYNC;
382 			break;
383 
384 		case MDB_ENVFLAGS:
385 			if ( c->valx == -1 ) {
386 				int i;
387 				for ( i=0; mdb_envflags[i].mask; i++) {
388 					if ( mdb->mi_dbenv_flags & mdb_envflags[i].mask ) {
389 						/* not all flags are runtime resettable */
390 						rc = mdb_env_set_flags( mdb->mi_dbenv, mdb_envflags[i].mask, 0 );
391 						if ( rc ) {
392 							mdb->mi_flags |= MDB_RE_OPEN;
393 							c->cleanup = mdb_cf_cleanup;
394 							rc = 0;
395 						}
396 						mdb->mi_dbenv_flags ^= mdb_envflags[i].mask;
397 					}
398 				}
399 			} else {
400 				int i = verb_to_mask( c->line, mdb_envflags );
401 				if ( mdb_envflags[i].mask & mdb->mi_dbenv_flags ) {
402 					rc = mdb_env_set_flags( mdb->mi_dbenv, mdb_envflags[i].mask, 0 );
403 					if ( rc ) {
404 						mdb->mi_flags |= MDB_RE_OPEN;
405 						c->cleanup = mdb_cf_cleanup;
406 						rc = 0;
407 					}
408 					mdb->mi_dbenv_flags ^= mdb_envflags[i].mask;
409 				} else {
410 					/* unknown keyword */
411 					rc = 1;
412 				}
413 			}
414 			break;
415 
416 		case MDB_INDEX:
417 			if ( c->valx == -1 ) {
418 				int i;
419 
420 				/* delete all (FIXME) */
421 				for ( i = 0; i < mdb->mi_nattrs; i++ ) {
422 					mdb->mi_attrs[i]->ai_indexmask |= MDB_INDEX_DELETING;
423 				}
424 				mdb->mi_flags |= MDB_DEL_INDEX;
425 				c->cleanup = mdb_cf_cleanup;
426 
427 			} else {
428 				struct berval bv, def = BER_BVC("default");
429 				char *ptr;
430 
431 				for (ptr = c->line; !isspace( (unsigned char) *ptr ); ptr++);
432 
433 				bv.bv_val = c->line;
434 				bv.bv_len = ptr - bv.bv_val;
435 				if ( bvmatch( &bv, &def )) {
436 					mdb->mi_defaultmask = 0;
437 
438 				} else {
439 					int i;
440 					char **attrs;
441 					char sep;
442 
443 					sep = bv.bv_val[ bv.bv_len ];
444 					bv.bv_val[ bv.bv_len ] = '\0';
445 					attrs = ldap_str2charray( bv.bv_val, "," );
446 
447 					for ( i = 0; attrs[ i ]; i++ ) {
448 						AttributeDescription *ad = NULL;
449 						const char *text;
450 						AttrInfo *ai;
451 
452 						slap_str2ad( attrs[ i ], &ad, &text );
453 						/* if we got here... */
454 						assert( ad != NULL );
455 
456 						ai = mdb_attr_mask( mdb, ad );
457 						/* if we got here... */
458 						assert( ai != NULL );
459 
460 						ai->ai_indexmask |= MDB_INDEX_DELETING;
461 						mdb->mi_flags |= MDB_DEL_INDEX;
462 						c->cleanup = mdb_cf_cleanup;
463 					}
464 
465 					bv.bv_val[ bv.bv_len ] = sep;
466 					ldap_charray_free( attrs );
467 				}
468 			}
469 			break;
470 		}
471 		return rc;
472 	}
473 
474 	switch( c->type ) {
475 	case MDB_MODE:
476 		if ( ASCII_DIGIT( c->argv[1][0] ) ) {
477 			long mode;
478 			char *next;
479 			errno = 0;
480 			mode = strtol( c->argv[1], &next, 0 );
481 			if ( errno != 0 || next == c->argv[1] || next[0] != '\0' ) {
482 				fprintf( stderr, "%s: "
483 					"unable to parse mode=\"%s\".\n",
484 					c->log, c->argv[1] );
485 				return 1;
486 			}
487 			mdb->mi_dbenv_mode = mode;
488 
489 		} else {
490 			char *m = c->argv[1];
491 			int who, what, mode = 0;
492 
493 			if ( strlen( m ) != STRLENOF("-rwxrwxrwx") ) {
494 				return 1;
495 			}
496 
497 			if ( m[0] != '-' ) {
498 				return 1;
499 			}
500 
501 			m++;
502 			for ( who = 0; who < 3; who++ ) {
503 				for ( what = 0; what < 3; what++, m++ ) {
504 					if ( m[0] == '-' ) {
505 						continue;
506 					} else if ( m[0] != "rwx"[what] ) {
507 						return 1;
508 					}
509 					mode += ((1 << (2 - what)) << 3*(2 - who));
510 				}
511 			}
512 			mdb->mi_dbenv_mode = mode;
513 		}
514 		break;
515 	case MDB_CHKPT: {
516 		long	l;
517 		mdb->mi_txn_cp = 1;
518 		if ( lutil_atolx( &l, c->argv[1], 0 ) != 0 ) {
519 			fprintf( stderr, "%s: "
520 				"invalid kbyte \"%s\" in \"checkpoint\".\n",
521 				c->log, c->argv[1] );
522 			return 1;
523 		}
524 		mdb->mi_txn_cp_kbyte = l;
525 		if ( lutil_atolx( &l, c->argv[2], 0 ) != 0 ) {
526 			fprintf( stderr, "%s: "
527 				"invalid minutes \"%s\" in \"checkpoint\".\n",
528 				c->log, c->argv[2] );
529 			return 1;
530 		}
531 		mdb->mi_txn_cp_min = l;
532 		/* If we're in server mode and time-based checkpointing is enabled,
533 		 * submit a task to perform periodic checkpoints.
534 		 */
535 		if ((slapMode & SLAP_SERVER_MODE) && mdb->mi_txn_cp_min ) {
536 			struct re_s *re = mdb->mi_txn_cp_task;
537 			if ( re ) {
538 				re->interval.tv_sec = mdb->mi_txn_cp_min * 60;
539 			} else {
540 				if ( c->be->be_suffix == NULL || BER_BVISNULL( &c->be->be_suffix[0] ) ) {
541 					fprintf( stderr, "%s: "
542 						"\"checkpoint\" must occur after \"suffix\".\n",
543 						c->log );
544 					return 1;
545 				}
546 				ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
547 				mdb->mi_txn_cp_task = ldap_pvt_runqueue_insert( &slapd_rq,
548 					mdb->mi_txn_cp_min * 60, mdb_checkpoint, mdb,
549 					LDAP_XSTRING(mdb_checkpoint), c->be->be_suffix[0].bv_val );
550 				ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
551 			}
552 		}
553 		} break;
554 
555 	case MDB_DIRECTORY: {
556 		FILE *f;
557 		char *ptr, *testpath;
558 		int len;
559 
560 		len = strlen( c->value_string );
561 		testpath = ch_malloc( len + STRLENOF(LDAP_DIRSEP) + STRLENOF("DUMMY") + 1 );
562 		ptr = lutil_strcopy( testpath, c->value_string );
563 		*ptr++ = LDAP_DIRSEP[0];
564 		strcpy( ptr, "DUMMY" );
565 		f = fopen( testpath, "w" );
566 		if ( f ) {
567 			fclose( f );
568 			unlink( testpath );
569 		}
570 		ch_free( testpath );
571 		if ( !f ) {
572 			snprintf( c->cr_msg, sizeof( c->cr_msg ), "%s: invalid path: %s",
573 				c->log, strerror( errno ));
574 			Debug( LDAP_DEBUG_ANY, "%s\n", c->cr_msg, 0, 0 );
575 			return -1;
576 		}
577 
578 		if ( mdb->mi_dbenv_home )
579 			ch_free( mdb->mi_dbenv_home );
580 		mdb->mi_dbenv_home = c->value_string;
581 
582 		}
583 		break;
584 
585 	case MDB_DBNOSYNC:
586 		if ( c->value_int )
587 			mdb->mi_dbenv_flags |= MDB_NOSYNC;
588 		else
589 			mdb->mi_dbenv_flags ^= MDB_NOSYNC;
590 		if ( mdb->mi_flags & MDB_IS_OPEN ) {
591 			mdb_env_set_flags( mdb->mi_dbenv, MDB_NOSYNC,
592 				c->value_int );
593 		}
594 		break;
595 
596 	case MDB_ENVFLAGS: {
597 		int i, j;
598 		for ( i=1; i<c->argc; i++ ) {
599 			j = verb_to_mask( c->argv[i], mdb_envflags );
600 			if ( mdb_envflags[j].mask ) {
601 				if ( mdb->mi_flags & MDB_IS_OPEN )
602 					rc = mdb_env_set_flags( mdb->mi_dbenv, mdb_envflags[j].mask, 1 );
603 				else
604 					rc = 0;
605 				if ( rc ) {
606 					mdb->mi_flags |= MDB_RE_OPEN;
607 					c->cleanup = mdb_cf_cleanup;
608 					rc = 0;
609 				}
610 				mdb->mi_dbenv_flags |= mdb_envflags[j].mask;
611 			} else {
612 				/* unknown keyword */
613 				rc = 1;
614 			}
615 		}
616 		}
617 		break;
618 
619 	case MDB_INDEX:
620 		rc = mdb_attr_index_config( mdb, c->fname, c->lineno,
621 			c->argc - 1, &c->argv[1], &c->reply);
622 
623 		if( rc != LDAP_SUCCESS ) return 1;
624 		c->cleanup = mdb_cf_cleanup;
625 		mdb->mi_flags |= MDB_OPEN_INDEX;
626 		if (( mdb->mi_flags & MDB_IS_OPEN ) && !mdb->mi_index_task ) {
627 			/* Start the task as soon as we finish here. Set a long
628 			 * interval (10 hours) so that it only gets scheduled once.
629 			 */
630 			if ( c->be->be_suffix == NULL || BER_BVISNULL( &c->be->be_suffix[0] ) ) {
631 				fprintf( stderr, "%s: "
632 					"\"index\" must occur after \"suffix\".\n",
633 					c->log );
634 				return 1;
635 			}
636 			ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
637 			mdb->mi_index_task = ldap_pvt_runqueue_insert( &slapd_rq, 36000,
638 				mdb_online_index, c->be,
639 				LDAP_XSTRING(mdb_online_index), c->be->be_suffix[0].bv_val );
640 			ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
641 		}
642 		break;
643 
644 	case MDB_SSTACK:
645 		if ( c->value_int < MINIMUM_SEARCH_STACK_DEPTH ) {
646 			fprintf( stderr,
647 		"%s: depth %d too small, using %d\n",
648 			c->log, c->value_int, MINIMUM_SEARCH_STACK_DEPTH );
649 			c->value_int = MINIMUM_SEARCH_STACK_DEPTH;
650 		}
651 		mdb->mi_search_stack_depth = c->value_int;
652 		break;
653 
654 	case MDB_MAXREADERS:
655 		mdb->mi_readers = c->value_int;
656 		if ( mdb->mi_flags & MDB_IS_OPEN ) {
657 			mdb->mi_flags |= MDB_RE_OPEN;
658 			c->cleanup = mdb_cf_cleanup;
659 		}
660 		break;
661 
662 	case MDB_MAXSIZE:
663 		mdb->mi_mapsize = c->value_ulong;
664 		if ( mdb->mi_flags & MDB_IS_OPEN ) {
665 			mdb->mi_flags |= MDB_RE_OPEN;
666 			c->cleanup = mdb_cf_cleanup;
667 		}
668 		break;
669 
670 	}
671 	return 0;
672 }
673 
674 int mdb_back_init_cf( BackendInfo *bi )
675 {
676 	int rc;
677 	bi->bi_cf_ocs = mdbocs;
678 
679 	rc = config_register_schema( mdbcfg, mdbocs );
680 	if ( rc ) return rc;
681 	return 0;
682 }
683