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 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 * 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 * 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 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 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 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