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