1/*	$NetBSD$	*/
2
3/* config.c - bdb backend configuration file routine */
4/* OpenLDAP: pkg/ldap/servers/slapd/back-bdb/config.c,v 1.91.2.19 2010/04/13 20:23:23 kurt Exp */
5/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
6 *
7 * Copyright 2000-2010 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-bdb.h"
27
28#include "config.h"
29
30#include "lutil.h"
31#include "ldap_rq.h"
32
33#ifdef DB_DIRTY_READ
34#	define	SLAP_BDB_ALLOW_DIRTY_READ
35#endif
36
37#define bdb_cf_gen		BDB_SYMBOL(cf_gen)
38#define	bdb_cf_cleanup		BDB_SYMBOL(cf_cleanup)
39#define bdb_checkpoint		BDB_SYMBOL(checkpoint)
40#define bdb_online_index	BDB_SYMBOL(online_index)
41
42static ConfigDriver bdb_cf_gen;
43
44enum {
45	BDB_CHKPT = 1,
46	BDB_CONFIG,
47	BDB_CRYPTFILE,
48	BDB_CRYPTKEY,
49	BDB_DIRECTORY,
50	BDB_NOSYNC,
51	BDB_DIRTYR,
52	BDB_INDEX,
53	BDB_LOCKD,
54	BDB_SSTACK,
55	BDB_MODE,
56	BDB_PGSIZE,
57	BDB_CHECKSUM
58};
59
60static ConfigTable bdbcfg[] = {
61	{ "directory", "dir", 2, 2, 0, ARG_STRING|ARG_MAGIC|BDB_DIRECTORY,
62		bdb_cf_gen, "( OLcfgDbAt:0.1 NAME 'olcDbDirectory' "
63			"DESC 'Directory for database content' "
64			"EQUALITY caseIgnoreMatch "
65			"SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
66	{ "cachefree", "size", 2, 2, 0, ARG_ULONG|ARG_OFFSET,
67		(void *)offsetof(struct bdb_info, bi_cache.c_minfree),
68		"( OLcfgDbAt:1.11 NAME 'olcDbCacheFree' "
69			"DESC 'Number of extra entries to free when max is reached' "
70			"SYNTAX OMsInteger SINGLE-VALUE )", NULL, NULL },
71	{ "cachesize", "size", 2, 2, 0, ARG_ULONG|ARG_OFFSET,
72		(void *)offsetof(struct bdb_info, bi_cache.c_maxsize),
73		"( OLcfgDbAt:1.1 NAME 'olcDbCacheSize' "
74			"DESC 'Entry cache size in entries' "
75			"SYNTAX OMsInteger SINGLE-VALUE )", NULL, NULL },
76	{ "checkpoint", "kbyte> <min", 3, 3, 0, ARG_MAGIC|BDB_CHKPT,
77		bdb_cf_gen, "( OLcfgDbAt:1.2 NAME 'olcDbCheckpoint' "
78			"DESC 'Database checkpoint interval in kbytes and minutes' "
79			"SYNTAX OMsDirectoryString SINGLE-VALUE )",NULL, NULL },
80	{ "checksum", NULL, 1, 2, 0, ARG_ON_OFF|ARG_MAGIC|BDB_CHECKSUM,
81		bdb_cf_gen, "( OLcfgDbAt:1.16 NAME 'olcDbChecksum' "
82			"DESC 'Enable database checksum validation' "
83			"SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
84	{ "cryptfile", "file", 2, 2, 0, ARG_STRING|ARG_MAGIC|BDB_CRYPTFILE,
85		bdb_cf_gen, "( OLcfgDbAt:1.13 NAME 'olcDbCryptFile' "
86			"DESC 'Pathname of file containing the DB encryption key' "
87			"SYNTAX OMsDirectoryString SINGLE-VALUE )",NULL, NULL },
88	{ "cryptkey", "key", 2, 2, 0, ARG_BERVAL|ARG_MAGIC|BDB_CRYPTKEY,
89		bdb_cf_gen, "( OLcfgDbAt:1.14 NAME 'olcDbCryptKey' "
90			"DESC 'DB encryption key' "
91			"SYNTAX OMsOctetString SINGLE-VALUE )",NULL, NULL },
92	{ "dbconfig", "DB_CONFIG setting", 1, 0, 0, ARG_MAGIC|BDB_CONFIG,
93		bdb_cf_gen, "( OLcfgDbAt:1.3 NAME 'olcDbConfig' "
94			"DESC 'BerkeleyDB DB_CONFIG configuration directives' "
95			"SYNTAX OMsIA5String X-ORDERED 'VALUES' )", NULL, NULL },
96	{ "dbnosync", NULL, 1, 2, 0, ARG_ON_OFF|ARG_MAGIC|BDB_NOSYNC,
97		bdb_cf_gen, "( OLcfgDbAt:1.4 NAME 'olcDbNoSync' "
98			"DESC 'Disable synchronous database writes' "
99			"SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
100	{ "dbpagesize", "db> <size", 3, 3, 0, ARG_MAGIC|BDB_PGSIZE,
101		bdb_cf_gen, "( OLcfgDbAt:1.15 NAME 'olcDbPageSize' "
102			"DESC 'Page size of specified DB, in Kbytes' "
103			"EQUALITY caseExactMatch "
104			"SYNTAX OMsDirectoryString )", NULL, NULL },
105	{ "dirtyread", NULL, 1, 2, 0,
106#ifdef SLAP_BDB_ALLOW_DIRTY_READ
107		ARG_ON_OFF|ARG_MAGIC|BDB_DIRTYR, bdb_cf_gen,
108#else
109		ARG_IGNORED, NULL,
110#endif
111		"( OLcfgDbAt:1.5 NAME 'olcDbDirtyRead' "
112		"DESC 'Allow reads of uncommitted data' "
113		"SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
114	{ "dncachesize", "size", 2, 2, 0, ARG_ULONG|ARG_OFFSET,
115		(void *)offsetof(struct bdb_info, bi_cache.c_eimax),
116		"( OLcfgDbAt:1.12 NAME 'olcDbDNcacheSize' "
117			"DESC 'DN cache size' "
118			"SYNTAX OMsInteger SINGLE-VALUE )", NULL, NULL },
119	{ "idlcachesize", "size", 2, 2, 0, ARG_ULONG|ARG_OFFSET,
120		(void *)offsetof(struct bdb_info, bi_idl_cache_max_size),
121		"( OLcfgDbAt:1.6 NAME 'olcDbIDLcacheSize' "
122		"DESC 'IDL cache size in IDLs' "
123		"SYNTAX OMsInteger SINGLE-VALUE )", NULL, NULL },
124	{ "index", "attr> <[pres,eq,approx,sub]", 2, 3, 0, ARG_MAGIC|BDB_INDEX,
125		bdb_cf_gen, "( OLcfgDbAt:0.2 NAME 'olcDbIndex' "
126		"DESC 'Attribute index parameters' "
127		"EQUALITY caseIgnoreMatch "
128		"SYNTAX OMsDirectoryString )", NULL, NULL },
129	{ "linearindex", NULL, 1, 2, 0, ARG_ON_OFF|ARG_OFFSET,
130		(void *)offsetof(struct bdb_info, bi_linear_index),
131		"( OLcfgDbAt:1.7 NAME 'olcDbLinearIndex' "
132		"DESC 'Index attributes one at a time' "
133		"SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
134	{ "lockdetect", "policy", 2, 2, 0, ARG_MAGIC|BDB_LOCKD,
135		bdb_cf_gen, "( OLcfgDbAt:1.8 NAME 'olcDbLockDetect' "
136		"DESC 'Deadlock detection algorithm' "
137		"SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
138	{ "mode", "mode", 2, 2, 0, ARG_MAGIC|BDB_MODE,
139		bdb_cf_gen, "( OLcfgDbAt:0.3 NAME 'olcDbMode' "
140		"DESC 'Unix permissions of database files' "
141		"SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
142	{ "searchstack", "depth", 2, 2, 0, ARG_INT|ARG_MAGIC|BDB_SSTACK,
143		bdb_cf_gen, "( OLcfgDbAt:1.9 NAME 'olcDbSearchStack' "
144		"DESC 'Depth of search stack in IDLs' "
145		"SYNTAX OMsInteger SINGLE-VALUE )", NULL, NULL },
146	{ "shm_key", "key", 2, 2, 0, ARG_LONG|ARG_OFFSET,
147		(void *)offsetof(struct bdb_info, bi_shm_key),
148		"( OLcfgDbAt:1.10 NAME 'olcDbShmKey' "
149		"DESC 'Key for shared memory region' "
150		"SYNTAX OMsInteger SINGLE-VALUE )", NULL, NULL },
151	{ NULL, NULL, 0, 0, 0, ARG_IGNORED,
152		NULL, NULL, NULL, NULL }
153};
154
155static ConfigOCs bdbocs[] = {
156	{
157#ifdef BDB_HIER
158		"( OLcfgDbOc:1.2 "
159		"NAME 'olcHdbConfig' "
160		"DESC 'HDB backend configuration' "
161#else
162		"( OLcfgDbOc:1.1 "
163		"NAME 'olcBdbConfig' "
164		"DESC 'BDB backend configuration' "
165#endif
166		"SUP olcDatabaseConfig "
167		"MUST olcDbDirectory "
168		"MAY ( olcDbCacheSize $ olcDbCheckpoint $ olcDbConfig $ "
169		"olcDbCryptFile $ olcDbCryptKey $ "
170		"olcDbNoSync $ olcDbDirtyRead $ olcDbIDLcacheSize $ "
171		"olcDbIndex $ olcDbLinearIndex $ olcDbLockDetect $ "
172		"olcDbMode $ olcDbSearchStack $ olcDbShmKey $ "
173		"olcDbCacheFree $ olcDbDNcacheSize $ olcDbPageSize ) )",
174		 	Cft_Database, bdbcfg },
175	{ NULL, 0, NULL }
176};
177
178static slap_verbmasks bdb_lockd[] = {
179	{ BER_BVC("default"), DB_LOCK_DEFAULT },
180	{ BER_BVC("oldest"), DB_LOCK_OLDEST },
181	{ BER_BVC("random"), DB_LOCK_RANDOM },
182	{ BER_BVC("youngest"), DB_LOCK_YOUNGEST },
183	{ BER_BVC("fewest"), DB_LOCK_MINLOCKS },
184	{ BER_BVNULL, 0 }
185};
186
187/* perform periodic checkpoints */
188static void *
189bdb_checkpoint( void *ctx, void *arg )
190{
191	struct re_s *rtask = arg;
192	struct bdb_info *bdb = rtask->arg;
193
194	TXN_CHECKPOINT( bdb->bi_dbenv, bdb->bi_txn_cp_kbyte,
195		bdb->bi_txn_cp_min, 0 );
196	ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
197	ldap_pvt_runqueue_stoptask( &slapd_rq, rtask );
198	ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
199	return NULL;
200}
201
202/* reindex entries on the fly */
203static void *
204bdb_online_index( void *ctx, void *arg )
205{
206	struct re_s *rtask = arg;
207	BackendDB *be = rtask->arg;
208	struct bdb_info *bdb = be->be_private;
209
210	Connection conn = {0};
211	OperationBuffer opbuf;
212	Operation *op;
213
214	DBC *curs;
215	DBT key, data;
216	DB_TXN *txn;
217	DB_LOCK lock;
218	ID id, nid;
219	EntryInfo *ei;
220	int rc, getnext = 1;
221	int i;
222
223	connection_fake_init( &conn, &opbuf, ctx );
224	op = &opbuf.ob_op;
225
226	op->o_bd = be;
227
228	DBTzero( &key );
229	DBTzero( &data );
230
231	id = 1;
232	key.data = &nid;
233	key.size = key.ulen = sizeof(ID);
234	key.flags = DB_DBT_USERMEM;
235
236	data.flags = DB_DBT_USERMEM | DB_DBT_PARTIAL;
237	data.dlen = data.ulen = 0;
238
239	while ( 1 ) {
240		if ( slapd_shutdown )
241			break;
242
243		rc = TXN_BEGIN( bdb->bi_dbenv, NULL, &txn, bdb->bi_db_opflags );
244		if ( rc )
245			break;
246		if ( getnext ) {
247			getnext = 0;
248			BDB_ID2DISK( id, &nid );
249			rc = bdb->bi_id2entry->bdi_db->cursor(
250				bdb->bi_id2entry->bdi_db, txn, &curs, bdb->bi_db_opflags );
251			if ( rc ) {
252				TXN_ABORT( txn );
253				break;
254			}
255			rc = curs->c_get( curs, &key, &data, DB_SET_RANGE );
256			curs->c_close( curs );
257			if ( rc ) {
258				TXN_ABORT( txn );
259				if ( rc == DB_NOTFOUND )
260					rc = 0;
261				if ( rc == DB_LOCK_DEADLOCK ) {
262					ldap_pvt_thread_yield();
263					continue;
264				}
265				break;
266			}
267			BDB_DISK2ID( &nid, &id );
268		}
269
270		ei = NULL;
271		rc = bdb_cache_find_id( op, txn, id, &ei, 0, &lock );
272		if ( rc ) {
273			TXN_ABORT( txn );
274			if ( rc == DB_LOCK_DEADLOCK ) {
275				ldap_pvt_thread_yield();
276				continue;
277			}
278			if ( rc == DB_NOTFOUND ) {
279				id++;
280				getnext = 1;
281				continue;
282			}
283			break;
284		}
285		if ( ei->bei_e ) {
286			rc = bdb_index_entry( op, txn, BDB_INDEX_UPDATE_OP, ei->bei_e );
287			if ( rc == DB_LOCK_DEADLOCK ) {
288				TXN_ABORT( txn );
289				ldap_pvt_thread_yield();
290				continue;
291			}
292			if ( rc == 0 ) {
293				rc = TXN_COMMIT( txn, 0 );
294				txn = NULL;
295			}
296			if ( rc )
297				break;
298		}
299		id++;
300		getnext = 1;
301	}
302
303	for ( i = 0; i < bdb->bi_nattrs; i++ ) {
304		if ( bdb->bi_attrs[ i ]->ai_indexmask & BDB_INDEX_DELETING
305			|| bdb->bi_attrs[ i ]->ai_newmask == 0 )
306		{
307			continue;
308		}
309		bdb->bi_attrs[ i ]->ai_indexmask = bdb->bi_attrs[ i ]->ai_newmask;
310		bdb->bi_attrs[ i ]->ai_newmask = 0;
311	}
312
313	ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
314	ldap_pvt_runqueue_stoptask( &slapd_rq, rtask );
315	bdb->bi_index_task = NULL;
316	ldap_pvt_runqueue_remove( &slapd_rq, rtask );
317	ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
318
319	return NULL;
320}
321
322/* Cleanup loose ends after Modify completes */
323static int
324bdb_cf_cleanup( ConfigArgs *c )
325{
326	struct bdb_info *bdb = c->be->be_private;
327	int rc = 0;
328
329	if ( bdb->bi_flags & BDB_UPD_CONFIG ) {
330		if ( bdb->bi_db_config ) {
331			int i;
332			FILE *f = fopen( bdb->bi_db_config_path, "w" );
333			if ( f ) {
334				for (i=0; bdb->bi_db_config[i].bv_val; i++)
335					fprintf( f, "%s\n", bdb->bi_db_config[i].bv_val );
336				fclose( f );
337			}
338		} else {
339			unlink( bdb->bi_db_config_path );
340		}
341		bdb->bi_flags ^= BDB_UPD_CONFIG;
342	}
343
344	if ( bdb->bi_flags & BDB_DEL_INDEX ) {
345		bdb_attr_flush( bdb );
346		bdb->bi_flags ^= BDB_DEL_INDEX;
347	}
348
349	if ( bdb->bi_flags & BDB_RE_OPEN ) {
350		bdb->bi_flags ^= BDB_RE_OPEN;
351		rc = c->be->bd_info->bi_db_close( c->be, &c->reply );
352		if ( rc == 0 )
353			rc = c->be->bd_info->bi_db_open( c->be, &c->reply );
354		/* If this fails, we need to restart */
355		if ( rc ) {
356			slapd_shutdown = 2;
357			snprintf( c->cr_msg, sizeof( c->cr_msg ),
358				"failed to reopen database, rc=%d", rc );
359			Debug( LDAP_DEBUG_ANY, LDAP_XSTRING(bdb_cf_cleanup)
360				": %s\n", c->cr_msg, 0, 0 );
361			rc = LDAP_OTHER;
362		}
363	}
364	return rc;
365}
366
367static int
368bdb_cf_gen( ConfigArgs *c )
369{
370	struct bdb_info *bdb = c->be->be_private;
371	int rc;
372
373	if ( c->op == SLAP_CONFIG_EMIT ) {
374		rc = 0;
375		switch( c->type ) {
376		case BDB_MODE: {
377			char buf[64];
378			struct berval bv;
379			bv.bv_len = snprintf( buf, sizeof(buf), "0%o", bdb->bi_dbenv_mode );
380			if ( bv.bv_len > 0 && bv.bv_len < sizeof(buf) ) {
381				bv.bv_val = buf;
382				value_add_one( &c->rvalue_vals, &bv );
383			} else {
384				rc = 1;
385			}
386			} break;
387
388		case BDB_CHKPT:
389			if ( bdb->bi_txn_cp ) {
390				char buf[64];
391				struct berval bv;
392				bv.bv_len = snprintf( buf, sizeof(buf), "%d %d", bdb->bi_txn_cp_kbyte,
393					bdb->bi_txn_cp_min );
394				if ( bv.bv_len > 0 && bv.bv_len < sizeof(buf) ) {
395					bv.bv_val = buf;
396					value_add_one( &c->rvalue_vals, &bv );
397				} else {
398					rc = 1;
399				}
400			} else {
401				rc = 1;
402			}
403			break;
404
405		case BDB_CRYPTFILE:
406			if ( bdb->bi_db_crypt_file ) {
407				c->value_string = ch_strdup( bdb->bi_db_crypt_file );
408			} else {
409				rc = 1;
410			}
411			break;
412
413		/* If a crypt file has been set, its contents are copied here.
414		 * But we don't want the key to be incorporated here.
415		 */
416		case BDB_CRYPTKEY:
417			if ( !bdb->bi_db_crypt_file && !BER_BVISNULL( &bdb->bi_db_crypt_key )) {
418				value_add_one( &c->rvalue_vals, &bdb->bi_db_crypt_key );
419			} else {
420				rc = 1;
421			}
422			break;
423
424		case BDB_DIRECTORY:
425			if ( bdb->bi_dbenv_home ) {
426				c->value_string = ch_strdup( bdb->bi_dbenv_home );
427			} else {
428				rc = 1;
429			}
430			break;
431
432		case BDB_CONFIG:
433			if ( !( bdb->bi_flags & BDB_IS_OPEN )
434				&& !bdb->bi_db_config )
435			{
436				char	buf[SLAP_TEXT_BUFLEN];
437				FILE *f = fopen( bdb->bi_db_config_path, "r" );
438				struct berval bv;
439
440				if ( f ) {
441					bdb->bi_flags |= BDB_HAS_CONFIG;
442					while ( fgets( buf, sizeof(buf), f )) {
443						ber_str2bv( buf, 0, 1, &bv );
444						if ( bv.bv_len > 0 && bv.bv_val[bv.bv_len-1] == '\n' ) {
445							bv.bv_len--;
446							bv.bv_val[bv.bv_len] = '\0';
447						}
448						/* shouldn't need this, but ... */
449						if ( bv.bv_len > 0 && bv.bv_val[bv.bv_len-1] == '\r' ) {
450							bv.bv_len--;
451							bv.bv_val[bv.bv_len] = '\0';
452						}
453						ber_bvarray_add( &bdb->bi_db_config, &bv );
454					}
455					fclose( f );
456				}
457			}
458			if ( bdb->bi_db_config ) {
459				int i;
460				struct berval bv;
461
462				bv.bv_val = c->log;
463				for (i=0; !BER_BVISNULL(&bdb->bi_db_config[i]); i++) {
464					bv.bv_len = sprintf( bv.bv_val, "{%d}%s", i,
465						bdb->bi_db_config[i].bv_val );
466					value_add_one( &c->rvalue_vals, &bv );
467				}
468			}
469			if ( !c->rvalue_vals ) rc = 1;
470			break;
471
472		case BDB_NOSYNC:
473			if ( bdb->bi_dbenv_xflags & DB_TXN_NOSYNC )
474				c->value_int = 1;
475			break;
476
477		case BDB_CHECKSUM:
478			if ( bdb->bi_flags & BDB_CHKSUM )
479				c->value_int = 1;
480			break;
481
482		case BDB_INDEX:
483			bdb_attr_index_unparse( bdb, &c->rvalue_vals );
484			if ( !c->rvalue_vals ) rc = 1;
485			break;
486
487		case BDB_LOCKD:
488			rc = 1;
489			if ( bdb->bi_lock_detect != DB_LOCK_DEFAULT ) {
490				int i;
491				for (i=0; !BER_BVISNULL(&bdb_lockd[i].word); i++) {
492					if ( bdb->bi_lock_detect == (u_int32_t)bdb_lockd[i].mask ) {
493						value_add_one( &c->rvalue_vals, &bdb_lockd[i].word );
494						rc = 0;
495						break;
496					}
497				}
498			}
499			break;
500
501		case BDB_SSTACK:
502			c->value_int = bdb->bi_search_stack_depth;
503			break;
504
505		case BDB_PGSIZE: {
506				struct bdb_db_pgsize *ps;
507				char buf[SLAP_TEXT_BUFLEN];
508				struct berval bv;
509				int rc = 1;
510
511				bv.bv_val = buf;
512				for ( ps = bdb->bi_pagesizes; ps; ps = ps->bdp_next ) {
513					bv.bv_len = sprintf( buf, "%s %d", ps->bdp_name.bv_val,
514						ps->bdp_size / 1024 );
515					value_add_one( &c->rvalue_vals, &bv );
516					rc = 0;
517
518				}
519				break;
520			}
521		}
522		return rc;
523	} else if ( c->op == LDAP_MOD_DELETE ) {
524		rc = 0;
525		switch( c->type ) {
526		case BDB_MODE:
527#if 0
528			/* FIXME: does it make any sense to change the mode,
529			 * if we don't exec a chmod()? */
530			bdb->bi_dbenv_mode = SLAPD_DEFAULT_DB_MODE;
531			break;
532#endif
533
534		/* single-valued no-ops */
535		case BDB_LOCKD:
536		case BDB_SSTACK:
537			break;
538
539		case BDB_CHKPT:
540			if ( bdb->bi_txn_cp_task ) {
541				struct re_s *re = bdb->bi_txn_cp_task;
542				bdb->bi_txn_cp_task = NULL;
543				ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
544				if ( ldap_pvt_runqueue_isrunning( &slapd_rq, re ) )
545					ldap_pvt_runqueue_stoptask( &slapd_rq, re );
546				ldap_pvt_runqueue_remove( &slapd_rq, re );
547				ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
548			}
549			bdb->bi_txn_cp = 0;
550			break;
551		case BDB_CONFIG:
552			if ( c->valx < 0 ) {
553				ber_bvarray_free( bdb->bi_db_config );
554				bdb->bi_db_config = NULL;
555			} else {
556				int i = c->valx;
557				ch_free( bdb->bi_db_config[i].bv_val );
558				for (; bdb->bi_db_config[i].bv_val; i++)
559					bdb->bi_db_config[i] = bdb->bi_db_config[i+1];
560			}
561			bdb->bi_flags |= BDB_UPD_CONFIG;
562			c->cleanup = bdb_cf_cleanup;
563			break;
564		/* Doesn't really make sense to change these on the fly;
565		 * the entire DB must be dumped and reloaded
566		 */
567		case BDB_CRYPTFILE:
568			if ( bdb->bi_db_crypt_file ) {
569				ch_free( bdb->bi_db_crypt_file );
570				bdb->bi_db_crypt_file = NULL;
571			}
572			/* FALLTHRU */
573		case BDB_CRYPTKEY:
574			if ( !BER_BVISNULL( &bdb->bi_db_crypt_key )) {
575				ch_free( bdb->bi_db_crypt_key.bv_val );
576				BER_BVZERO( &bdb->bi_db_crypt_key );
577			}
578			break;
579		case BDB_DIRECTORY:
580			bdb->bi_flags |= BDB_RE_OPEN;
581			bdb->bi_flags ^= BDB_HAS_CONFIG;
582			ch_free( bdb->bi_dbenv_home );
583			bdb->bi_dbenv_home = NULL;
584			ch_free( bdb->bi_db_config_path );
585			bdb->bi_db_config_path = NULL;
586			c->cleanup = bdb_cf_cleanup;
587			ldap_pvt_thread_pool_purgekey( bdb->bi_dbenv );
588			break;
589		case BDB_NOSYNC:
590			bdb->bi_dbenv->set_flags( bdb->bi_dbenv, DB_TXN_NOSYNC, 0 );
591			break;
592		case BDB_CHECKSUM:
593			bdb->bi_flags &= ~BDB_CHKSUM;
594			break;
595		case BDB_INDEX:
596			if ( c->valx == -1 ) {
597				int i;
598
599				/* delete all (FIXME) */
600				for ( i = 0; i < bdb->bi_nattrs; i++ ) {
601					bdb->bi_attrs[i]->ai_indexmask |= BDB_INDEX_DELETING;
602				}
603				bdb->bi_flags |= BDB_DEL_INDEX;
604				c->cleanup = bdb_cf_cleanup;
605
606			} else {
607				struct berval bv, def = BER_BVC("default");
608				char *ptr;
609
610				for (ptr = c->line; !isspace( (unsigned char) *ptr ); ptr++);
611
612				bv.bv_val = c->line;
613				bv.bv_len = ptr - bv.bv_val;
614				if ( bvmatch( &bv, &def )) {
615					bdb->bi_defaultmask = 0;
616
617				} else {
618					int i;
619					char **attrs;
620					char sep;
621
622					sep = bv.bv_val[ bv.bv_len ];
623					bv.bv_val[ bv.bv_len ] = '\0';
624					attrs = ldap_str2charray( bv.bv_val, "," );
625
626					for ( i = 0; attrs[ i ]; i++ ) {
627						AttributeDescription *ad = NULL;
628						const char *text;
629						AttrInfo *ai;
630
631						slap_str2ad( attrs[ i ], &ad, &text );
632						/* if we got here... */
633						assert( ad != NULL );
634
635						ai = bdb_attr_mask( bdb, ad );
636						/* if we got here... */
637						assert( ai != NULL );
638
639						ai->ai_indexmask |= BDB_INDEX_DELETING;
640						bdb->bi_flags |= BDB_DEL_INDEX;
641						c->cleanup = bdb_cf_cleanup;
642					}
643
644					bv.bv_val[ bv.bv_len ] = sep;
645					ldap_charray_free( attrs );
646				}
647			}
648			break;
649		/* doesn't make sense on the fly; the DB file must be
650		 * recreated
651		 */
652		case BDB_PGSIZE: {
653				struct bdb_db_pgsize *ps, **prev;
654				int i;
655
656				for ( i = 0, prev = &bdb->bi_pagesizes, ps = *prev; ps;
657					prev = &ps->bdp_next, ps = ps->bdp_next, i++ ) {
658					if ( c->valx == -1 || i == c->valx ) {
659						*prev = ps->bdp_next;
660						ch_free( ps );
661						ps = *prev;
662						if ( i == c->valx ) break;
663					}
664				}
665			}
666			break;
667		}
668		return rc;
669	}
670
671	switch( c->type ) {
672	case BDB_MODE:
673		if ( ASCII_DIGIT( c->argv[1][0] ) ) {
674			long mode;
675			char *next;
676			errno = 0;
677			mode = strtol( c->argv[1], &next, 0 );
678			if ( errno != 0 || next == c->argv[1] || next[0] != '\0' ) {
679				fprintf( stderr, "%s: "
680					"unable to parse mode=\"%s\".\n",
681					c->log, c->argv[1] );
682				return 1;
683			}
684			bdb->bi_dbenv_mode = mode;
685
686		} else {
687			char *m = c->argv[1];
688			int who, what, mode = 0;
689
690			if ( strlen( m ) != STRLENOF("-rwxrwxrwx") ) {
691				return 1;
692			}
693
694			if ( m[0] != '-' ) {
695				return 1;
696			}
697
698			m++;
699			for ( who = 0; who < 3; who++ ) {
700				for ( what = 0; what < 3; what++, m++ ) {
701					if ( m[0] == '-' ) {
702						continue;
703					} else if ( m[0] != "rwx"[what] ) {
704						return 1;
705					}
706					mode += ((1 << (2 - what)) << 3*(2 - who));
707				}
708			}
709			bdb->bi_dbenv_mode = mode;
710		}
711		break;
712	case BDB_CHKPT: {
713		long	l;
714		bdb->bi_txn_cp = 1;
715		if ( lutil_atolx( &l, c->argv[1], 0 ) != 0 ) {
716			fprintf( stderr, "%s: "
717				"invalid kbyte \"%s\" in \"checkpoint\".\n",
718				c->log, c->argv[1] );
719			return 1;
720		}
721		bdb->bi_txn_cp_kbyte = l;
722		if ( lutil_atolx( &l, c->argv[2], 0 ) != 0 ) {
723			fprintf( stderr, "%s: "
724				"invalid minutes \"%s\" in \"checkpoint\".\n",
725				c->log, c->argv[2] );
726			return 1;
727		}
728		bdb->bi_txn_cp_min = l;
729		/* If we're in server mode and time-based checkpointing is enabled,
730		 * submit a task to perform periodic checkpoints.
731		 */
732		if ((slapMode & SLAP_SERVER_MODE) && bdb->bi_txn_cp_min ) {
733			struct re_s *re = bdb->bi_txn_cp_task;
734			if ( re ) {
735				re->interval.tv_sec = bdb->bi_txn_cp_min * 60;
736			} else {
737				if ( c->be->be_suffix == NULL || BER_BVISNULL( &c->be->be_suffix[0] ) ) {
738					fprintf( stderr, "%s: "
739						"\"checkpoint\" must occur after \"suffix\".\n",
740						c->log );
741					return 1;
742				}
743				ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
744				bdb->bi_txn_cp_task = ldap_pvt_runqueue_insert( &slapd_rq,
745					bdb->bi_txn_cp_min * 60, bdb_checkpoint, bdb,
746					LDAP_XSTRING(bdb_checkpoint), c->be->be_suffix[0].bv_val );
747				ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
748			}
749		}
750		} break;
751
752	case BDB_CONFIG: {
753		char *ptr = c->line;
754		struct berval bv;
755
756		if ( c->op == SLAP_CONFIG_ADD ) {
757			ptr += STRLENOF("dbconfig");
758			while (!isspace((unsigned char)*ptr)) ptr++;
759			while (isspace((unsigned char)*ptr)) ptr++;
760		}
761
762		if ( bdb->bi_flags & BDB_IS_OPEN ) {
763			bdb->bi_flags |= BDB_UPD_CONFIG;
764			c->cleanup = bdb_cf_cleanup;
765		} else {
766		/* If we're just starting up...
767		 */
768			FILE *f;
769			/* If a DB_CONFIG file exists, or we don't know the path
770			 * to the DB_CONFIG file, ignore these directives
771			 */
772			if (( bdb->bi_flags & BDB_HAS_CONFIG ) || !bdb->bi_db_config_path )
773				break;
774			f = fopen( bdb->bi_db_config_path, "a" );
775			if ( f ) {
776				/* FIXME: EBCDIC probably needs special handling */
777				fprintf( f, "%s\n", ptr );
778				fclose( f );
779			}
780		}
781		ber_str2bv( ptr, 0, 1, &bv );
782		ber_bvarray_add( &bdb->bi_db_config, &bv );
783		}
784		break;
785
786	case BDB_CRYPTFILE:
787		rc = lutil_get_filed_password( c->value_string, &bdb->bi_db_crypt_key );
788		if ( rc == 0 ) {
789			bdb->bi_db_crypt_file = c->value_string;
790		}
791		break;
792
793	/* Cannot set key if file was already set */
794	case BDB_CRYPTKEY:
795		if ( bdb->bi_db_crypt_file ) {
796			rc = 1;
797		} else {
798			bdb->bi_db_crypt_key = c->value_bv;
799		}
800		break;
801
802	case BDB_DIRECTORY: {
803		FILE *f;
804		char *ptr, *testpath;
805		int len;
806
807		len = strlen( c->value_string );
808		testpath = ch_malloc( len + STRLENOF(LDAP_DIRSEP) + STRLENOF("DUMMY") + 1 );
809		ptr = lutil_strcopy( testpath, c->value_string );
810		*ptr++ = LDAP_DIRSEP[0];
811		strcpy( ptr, "DUMMY" );
812		f = fopen( testpath, "w" );
813		if ( f ) {
814			fclose( f );
815			unlink( testpath );
816		}
817		ch_free( testpath );
818		if ( !f ) {
819			snprintf( c->cr_msg, sizeof( c->cr_msg ), "%s: invalid path: %s",
820				c->log, strerror( errno ));
821			Debug( LDAP_DEBUG_ANY, "%s\n", c->cr_msg, 0, 0 );
822			return -1;
823		}
824
825		if ( bdb->bi_dbenv_home )
826			ch_free( bdb->bi_dbenv_home );
827		bdb->bi_dbenv_home = c->value_string;
828
829		/* See if a DB_CONFIG file already exists here */
830		if ( bdb->bi_db_config_path )
831			ch_free( bdb->bi_db_config_path );
832		bdb->bi_db_config_path = ch_malloc( len +
833			STRLENOF(LDAP_DIRSEP) + STRLENOF("DB_CONFIG") + 1 );
834		ptr = lutil_strcopy( bdb->bi_db_config_path, bdb->bi_dbenv_home );
835		*ptr++ = LDAP_DIRSEP[0];
836		strcpy( ptr, "DB_CONFIG" );
837
838		f = fopen( bdb->bi_db_config_path, "r" );
839		if ( f ) {
840			bdb->bi_flags |= BDB_HAS_CONFIG;
841			fclose(f);
842		}
843		}
844		break;
845
846	case BDB_NOSYNC:
847		if ( c->value_int )
848			bdb->bi_dbenv_xflags |= DB_TXN_NOSYNC;
849		else
850			bdb->bi_dbenv_xflags &= ~DB_TXN_NOSYNC;
851		if ( bdb->bi_flags & BDB_IS_OPEN ) {
852			bdb->bi_dbenv->set_flags( bdb->bi_dbenv, DB_TXN_NOSYNC,
853				c->value_int );
854		}
855		break;
856
857	case BDB_CHECKSUM:
858		if ( c->value_int )
859			bdb->bi_flags |= BDB_CHKSUM;
860		else
861			bdb->bi_flags &= ~BDB_CHKSUM;
862		break;
863
864	case BDB_INDEX:
865		rc = bdb_attr_index_config( bdb, c->fname, c->lineno,
866			c->argc - 1, &c->argv[1], &c->reply);
867
868		if( rc != LDAP_SUCCESS ) return 1;
869		if (( bdb->bi_flags & BDB_IS_OPEN ) && !bdb->bi_index_task ) {
870			/* Start the task as soon as we finish here. Set a long
871			 * interval (10 hours) so that it only gets scheduled once.
872			 */
873			if ( c->be->be_suffix == NULL || BER_BVISNULL( &c->be->be_suffix[0] ) ) {
874				fprintf( stderr, "%s: "
875					"\"index\" must occur after \"suffix\".\n",
876					c->log );
877				return 1;
878			}
879			ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
880			bdb->bi_index_task = ldap_pvt_runqueue_insert( &slapd_rq, 36000,
881				bdb_online_index, c->be,
882				LDAP_XSTRING(bdb_online_index), c->be->be_suffix[0].bv_val );
883			ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
884		}
885		break;
886
887	case BDB_LOCKD:
888		rc = verb_to_mask( c->argv[1], bdb_lockd );
889		if ( BER_BVISNULL(&bdb_lockd[rc].word) ) {
890			fprintf( stderr, "%s: "
891				"bad policy (%s) in \"lockDetect <policy>\" line\n",
892				c->log, c->argv[1] );
893			return 1;
894		}
895		bdb->bi_lock_detect = (u_int32_t)rc;
896		break;
897
898	case BDB_SSTACK:
899		if ( c->value_int < MINIMUM_SEARCH_STACK_DEPTH ) {
900			fprintf( stderr,
901		"%s: depth %d too small, using %d\n",
902			c->log, c->value_int, MINIMUM_SEARCH_STACK_DEPTH );
903			c->value_int = MINIMUM_SEARCH_STACK_DEPTH;
904		}
905		bdb->bi_search_stack_depth = c->value_int;
906		break;
907
908	case BDB_PGSIZE: {
909		struct bdb_db_pgsize *ps, **prev;
910		int i, s;
911
912		s = atoi(c->argv[2]);
913		if ( s < 1 || s > 64 ) {
914			snprintf( c->cr_msg, sizeof( c->cr_msg ),
915				"%s: size must be > 0 and <= 64: %d",
916				c->log, s );
917			Debug( LDAP_DEBUG_ANY, "%s\n", c->cr_msg, 0, 0 );
918			return -1;
919		}
920		i = strlen(c->argv[1]);
921		ps = ch_malloc( sizeof(struct bdb_db_pgsize) + i + 1 );
922		ps->bdp_next = NULL;
923		ps->bdp_name.bv_len = i;
924		ps->bdp_name.bv_val = (char *)(ps+1);
925		strcpy( ps->bdp_name.bv_val, c->argv[1] );
926		ps->bdp_size = s * 1024;
927		for ( prev = &bdb->bi_pagesizes; *prev; prev = &(*prev)->bdp_next )
928			;
929		*prev = ps;
930		}
931		break;
932	}
933	return 0;
934}
935
936int bdb_back_init_cf( BackendInfo *bi )
937{
938	int rc;
939	bi->bi_cf_ocs = bdbocs;
940
941	rc = config_register_schema( bdbcfg, bdbocs );
942	if ( rc ) return rc;
943	return 0;
944}
945