/* Takes a return code supposed to be errno or from lidb which we don't expect to see and prints a handy log message */ void ldbm_nasty(const char* str, int c, int err) { char *msg = NULL; char buffer[200]; if (err == DB_LOCK_DEADLOCK) { PR_snprintf(buffer,200,"%s WARNING %d",str,c); LDAPDebug(LDAP_DEBUG_BACKLDBM,"%s, err=%d %s\n", buffer,err,(msg = dblayer_strerror( err )) ? msg : ""); } else if (err == DB_RUNRECOVERY) { LDAPDebug2Args(LDAP_DEBUG_ANY, "FATAL ERROR at %s (%d); " "server stopping as database recovery needed.\n", str, c); exit(1); } else { PR_snprintf(buffer,200,"%s BAD %d",str,c); LDAPDebug(LDAP_DEBUG_ANY, "%s, err=%d %s\n", buffer, err, (msg = dblayer_strerror( err )) ? msg : ""); } }
int ldbm_back_modify( Slapi_PBlock *pb ) { backend *be; ldbm_instance *inst = NULL; struct ldbminfo *li; struct backentry *e = NULL, *ec = NULL; struct backentry *original_entry = NULL, *tmpentry = NULL; Slapi_Entry *postentry = NULL; LDAPMod **mods = NULL; LDAPMod **mods_original = NULL; Slapi_Mods smods = {0}; back_txn txn; back_txnid parent_txn; modify_context ruv_c = {0}; int ruv_c_init = 0; int retval = -1; char *msg; char *errbuf = NULL; int retry_count = 0; int disk_full = 0; int ldap_result_code= LDAP_SUCCESS; char *ldap_result_message= NULL; int rc = 0; Slapi_Operation *operation; entry_address *addr; int is_fixup_operation= 0; int is_ruv = 0; /* True if the current entry is RUV */ CSN *opcsn = NULL; int repl_op; int opreturn = 0; int mod_count = 0; int not_an_error = 0; int fixup_tombstone = 0; int ec_locked = 0; int result_sent = 0; slapi_pblock_get( pb, SLAPI_BACKEND, &be); slapi_pblock_get( pb, SLAPI_PLUGIN_PRIVATE, &li ); slapi_pblock_get( pb, SLAPI_TARGET_ADDRESS, &addr ); slapi_pblock_get( pb, SLAPI_MODIFY_MODS, &mods ); slapi_pblock_get( pb, SLAPI_TXN, (void**)&parent_txn ); slapi_pblock_get( pb, SLAPI_IS_REPLICATED_OPERATION, &repl_op); slapi_pblock_get( pb, SLAPI_OPERATION, &operation ); fixup_tombstone = operation_is_flag_set(operation, OP_FLAG_TOMBSTONE_FIXUP); dblayer_txn_init(li,&txn); /* must do this before first goto error_return */ /* the calls to perform searches require the parent txn if any so set txn to the parent_txn until we begin the child transaction */ if (parent_txn) { txn.back_txn_txn = parent_txn; } else { parent_txn = txn.back_txn_txn; slapi_pblock_set( pb, SLAPI_TXN, parent_txn ); } if (NULL == operation) { ldap_result_code = LDAP_OPERATIONS_ERROR; goto error_return; } is_fixup_operation = operation_is_flag_set(operation, OP_FLAG_REPL_FIXUP); is_ruv = operation_is_flag_set(operation, OP_FLAG_REPL_RUV); inst = (ldbm_instance *) be->be_instance_info; if (NULL == addr) { goto error_return; } if (inst && inst->inst_ref_count) { slapi_counter_increment(inst->inst_ref_count); } else { slapi_log_err(SLAPI_LOG_ERR, "ldbm_back_modify", "Instance \"%s\" does not exist.\n", inst ? inst->inst_name : "null instance"); goto error_return; } /* no need to check the dn syntax as this is a replicated op */ if(!repl_op){ ldap_result_code = slapi_dn_syntax_check(pb, slapi_sdn_get_dn(addr->sdn), 1); if (ldap_result_code) { ldap_result_code = LDAP_INVALID_DN_SYNTAX; slapi_pblock_get(pb, SLAPI_PB_RESULT_TEXT, &ldap_result_message); goto error_return; } } /* The dblock serializes writes to the database, * which reduces deadlocking in the db code, * which means that we run faster. * * But, this lock is re-enterant for the fixup * operations that the URP code in the Replication * plugin generates. * * SERIALLOCK is moved to dblayer_txn_begin along with exposing be * transaction to plugins (see slapi_back_transaction_* APIs). * if(SERIALLOCK(li) && !operation_is_flag_set(operation,OP_FLAG_REPL_FIXUP)) { dblayer_lock_backend(be); dblock_acquired= 1; } */ if ( MANAGE_ENTRY_BEFORE_DBLOCK(li)) { /* find and lock the entry we are about to modify */ if (fixup_tombstone) { e = find_entry2modify_only_ext( pb, be, addr, TOMBSTONE_INCLUDED, &txn, &result_sent ); } else { e = find_entry2modify( pb, be, addr, &txn, &result_sent ); } if (e == NULL) { ldap_result_code = -1; goto error_return; /* error result sent by find_entry2modify() */ } } txn.back_txn_txn = NULL; /* ready to create the child transaction */ for (retry_count = 0; retry_count < RETRY_TIMES; retry_count++) { int cache_rc = 0; int new_mod_count = 0; if (txn.back_txn_txn && (txn.back_txn_txn != parent_txn)) { /* don't release SERIAL LOCK */ dblayer_txn_abort_ext(li, &txn, PR_FALSE); slapi_pblock_set(pb, SLAPI_TXN, parent_txn); /* * Since be_txn_preop functions could have modified the entry/mods, * We need to grab the current mods, free them, and restore the * originals. Same thing for the entry. */ slapi_pblock_get(pb, SLAPI_MODIFY_MODS, &mods); ldap_mods_free(mods, 1); slapi_pblock_set(pb, SLAPI_MODIFY_MODS, copy_mods(mods_original)); /* reset ec set cache in id2entry_add_ext */ if (ec) { /* must duplicate ec before returning it to cache, * which could free the entry. */ if ((tmpentry = backentry_dup(original_entry?original_entry:ec)) == NULL) { ldap_result_code= LDAP_OPERATIONS_ERROR; goto error_return; } if (cache_is_in_cache(&inst->inst_cache, ec)) { CACHE_REMOVE(&inst->inst_cache, ec); } CACHE_RETURN(&inst->inst_cache, &ec); slapi_pblock_set( pb, SLAPI_MODIFY_EXISTING_ENTRY, original_entry->ep_entry ); ec = original_entry; original_entry = tmpentry; tmpentry = NULL; } if (ruv_c_init) { /* reset the ruv txn stuff */ modify_term(&ruv_c, be); ruv_c_init = 0; } slapi_log_err(SLAPI_LOG_BACKLDBM, "ldbm_back_modify", "Modify Retrying Transaction\n"); #ifndef LDBM_NO_BACKOFF_DELAY { PRIntervalTime interval; interval = PR_MillisecondsToInterval(slapi_rand() % 100); DS_Sleep(interval); } #endif } /* Nothing above here modifies persistent store, everything after here is subject to the transaction */ /* dblayer_txn_begin holds SERIAL lock, * which should be outside of locking the entry (find_entry2modify) */ if (0 == retry_count) { /* First time, hold SERIAL LOCK */ retval = dblayer_txn_begin(be, parent_txn, &txn); } else { /* Otherwise, no SERIAL LOCK */ retval = dblayer_txn_begin_ext(li, parent_txn, &txn, PR_FALSE); } if (0 != retval) { if (LDBM_OS_ERR_IS_DISKFULL(retval)) disk_full = 1; ldap_result_code= LDAP_OPERATIONS_ERROR; goto error_return; } /* stash the transaction for plugins */ slapi_pblock_set(pb, SLAPI_TXN, txn.back_txn_txn); if (0 == retry_count) { /* just once */ if ( !MANAGE_ENTRY_BEFORE_DBLOCK(li)) { /* find and lock the entry we are about to modify */ if (fixup_tombstone) { e = find_entry2modify_only_ext( pb, be, addr, TOMBSTONE_INCLUDED, &txn, &result_sent ); } else { e = find_entry2modify( pb, be, addr, &txn, &result_sent ); } if (e == NULL) { ldap_result_code = -1; goto error_return; /* error result sent by find_entry2modify() */ } } if ( !is_fixup_operation && !fixup_tombstone) { if (!repl_op && slapi_entry_flag_is_set(e->ep_entry, SLAPI_ENTRY_FLAG_TOMBSTONE)) { ldap_result_code = LDAP_UNWILLING_TO_PERFORM; ldap_result_message = "Operation not allowed on tombstone entry."; slapi_log_err(SLAPI_LOG_ERR, "ldbm_back_modify", "Attempt to modify a tombstone entry %s\n", slapi_sdn_get_dn(slapi_entry_get_sdn_const( e->ep_entry ))); goto error_return; } opcsn = operation_get_csn (operation); if (NULL == opcsn && operation->o_csngen_handler) { /* * Current op is a user request. Opcsn will be assigned * if the dn is in an updatable replica. */ opcsn = entry_assign_operation_csn ( pb, e->ep_entry, NULL ); } if (opcsn) { entry_set_maxcsn (e->ep_entry, opcsn); } } /* Save away a copy of the entry, before modifications */ slapi_pblock_set( pb, SLAPI_ENTRY_PRE_OP, slapi_entry_dup( e->ep_entry )); if ( (ldap_result_code = plugin_call_acl_mods_access( pb, e->ep_entry, mods, &errbuf)) != LDAP_SUCCESS ) { ldap_result_message= errbuf; goto error_return; } /* create a copy of the entry and apply the changes to it */ if ( (ec = backentry_dup( e )) == NULL ) { ldap_result_code= LDAP_OPERATIONS_ERROR; goto error_return; } if(!repl_op){ remove_illegal_mods(mods); } /* ec is the entry that our bepreop should get to mess with */ slapi_pblock_set( pb, SLAPI_MODIFY_EXISTING_ENTRY, ec->ep_entry ); slapi_pblock_set(pb, SLAPI_RESULT_CODE, &ldap_result_code); opreturn = plugin_call_plugins(pb, SLAPI_PLUGIN_BE_PRE_MODIFY_FN); if (opreturn || (slapi_pblock_get(pb, SLAPI_RESULT_CODE, &ldap_result_code) && ldap_result_code) || (slapi_pblock_get(pb, SLAPI_PLUGIN_OPRETURN, &opreturn) && opreturn)) { slapi_pblock_get(pb, SLAPI_RESULT_CODE, &ldap_result_code); slapi_pblock_get(pb, SLAPI_PLUGIN_OPRETURN, &opreturn); if (!ldap_result_code) { slapi_log_err(SLAPI_LOG_ERR, "ldbm_back_modify", "SLAPI_PLUGIN_BE_PRE_MODIFY_FN " "returned error but did not set SLAPI_RESULT_CODE\n"); ldap_result_code = LDAP_OPERATIONS_ERROR; } if (SLAPI_PLUGIN_NOOP == opreturn) { not_an_error = 1; rc = opreturn = LDAP_SUCCESS; } else if (!opreturn) { opreturn = SLAPI_PLUGIN_FAILURE; slapi_pblock_set(pb, SLAPI_PLUGIN_OPRETURN, &opreturn); } slapi_pblock_get(pb, SLAPI_PB_RESULT_TEXT, &ldap_result_message); goto error_return; } /* The Plugin may have messed about with some of the PBlock parameters... ie. mods */ slapi_pblock_get( pb, SLAPI_MODIFY_MODS, &mods ); /* apply the mods, check for syntax, schema problems, etc. */ if (modify_apply_check_expand(pb, operation, mods, e, ec, &postentry, &ldap_result_code, &ldap_result_message)) { goto error_return; } /* the schema check could have added a repl conflict mod * get the mods again */ slapi_pblock_get( pb, SLAPI_MODIFY_MODS, &mods ); slapi_mods_init_byref(&smods,mods); mod_count = slapi_mods_get_num_mods(&smods); /* * Grab a copy of the mods and the entry in case the be_txn_preop changes * the them. If we have a failure, then we need to reset the mods to their * their original state; */ mods_original = copy_mods(mods); if ( (original_entry = backentry_dup( ec )) == NULL ) { ldap_result_code= LDAP_OPERATIONS_ERROR; goto error_return; } } /* if (0 == retry_count) just once */ /* call the transaction pre modify plugins just after creating the transaction */ retval = plugin_call_plugins(pb, SLAPI_PLUGIN_BE_TXN_PRE_MODIFY_FN); if (retval) { slapi_log_err(SLAPI_LOG_TRACE, "ldbm_back_modify", "SLAPI_PLUGIN_BE_TXN_PRE_MODIFY_FN plugin " "returned error code %d\n", retval ); slapi_pblock_get(pb, SLAPI_RESULT_CODE, &ldap_result_code); slapi_pblock_get(pb, SLAPI_PLUGIN_OPRETURN, &opreturn); if (SLAPI_PLUGIN_NOOP == retval) { not_an_error = 1; rc = retval = LDAP_SUCCESS; } if (!opreturn) { slapi_pblock_set(pb, SLAPI_PLUGIN_OPRETURN, ldap_result_code ? &ldap_result_code : &retval); } slapi_pblock_get(pb, SLAPI_PB_RESULT_TEXT, &ldap_result_message); goto error_return; } /* the mods might have been changed, so get the latest */ slapi_pblock_get( pb, SLAPI_MODIFY_MODS, &mods ); /* make sure the betxnpreop did not alter any of the mods that had already previously been applied */ slapi_mods_done(&smods); slapi_mods_init_byref(&smods,mods); new_mod_count = slapi_mods_get_num_mods(&smods); if (new_mod_count < mod_count) { slapi_log_err(SLAPI_LOG_ERR, "ldbm_back_modify", "Error: BE_TXN_PRE_MODIFY plugin has removed " "mods from the original list - mod count was [%d] now [%d] " "mods will not be applied - mods list changes must be done " "in the BE_PRE_MODIFY plugin, not the BE_TXN_PRE_MODIFY\n", mod_count, new_mod_count ); } else if (new_mod_count > mod_count) { /* apply the new betxnpremod mods */ /* apply the mods, check for syntax, schema problems, etc. */ if (modify_apply_check_expand(pb, operation, &mods[mod_count], e, ec, &postentry, &ldap_result_code, &ldap_result_message)) { goto error_return; } } /* else if new_mod_count == mod_count then betxnpremod plugin did nothing */ /* * Update the ID to Entry index. * Note that id2entry_add replaces the entry, so the Entry ID * stays the same. */ retval = id2entry_add_ext( be, ec, &txn, 1, &cache_rc ); if (DB_LOCK_DEADLOCK == retval) { /* Abort and re-try */ continue; } if (0 != retval) { slapi_log_err(SLAPI_LOG_ERR, "ldbm_back_modify", "id2entry_add failed, err=%d %s\n", retval, (msg = dblayer_strerror( retval )) ? msg : ""); if (LDBM_OS_ERR_IS_DISKFULL(retval)) disk_full = 1; MOD_SET_ERROR(ldap_result_code, LDAP_OPERATIONS_ERROR, retry_count); goto error_return; } retval = index_add_mods( be, mods, e, ec, &txn ); if (DB_LOCK_DEADLOCK == retval) { /* Abort and re-try */ continue; } if (0 != retval) { slapi_log_err(SLAPI_LOG_ERR, "ldbm_back_modify", "index_add_mods failed, err=%d %s\n", retval, (msg = dblayer_strerror( retval )) ? msg : ""); if (LDBM_OS_ERR_IS_DISKFULL(retval)) disk_full = 1; MOD_SET_ERROR(ldap_result_code, LDAP_OPERATIONS_ERROR, retry_count); goto error_return; } /* * Remove the old entry from the Virtual List View indexes. * Add the new entry to the Virtual List View indexes. * If the entry is ruv, no need to update vlv. */ if (!is_ruv) { retval= vlv_update_all_indexes(&txn, be, pb, e, ec); if (DB_LOCK_DEADLOCK == retval) { /* Abort and re-try */ continue; } if (0 != retval) { slapi_log_err(SLAPI_LOG_ERR, "ldbm_back_modify", "vlv_update_index failed, err=%d %s\n", retval, (msg = dblayer_strerror( retval )) ? msg : ""); if (LDBM_OS_ERR_IS_DISKFULL(retval)) disk_full = 1; MOD_SET_ERROR(ldap_result_code, LDAP_OPERATIONS_ERROR, retry_count); goto error_return; } } if (!is_ruv && !is_fixup_operation && !NO_RUV_UPDATE(li)) { ruv_c_init = ldbm_txn_ruv_modify_context( pb, &ruv_c ); if (-1 == ruv_c_init) { slapi_log_err(SLAPI_LOG_ERR, "ldbm_back_modify", "ldbm_txn_ruv_modify_context failed to construct RUV modify context\n"); ldap_result_code= LDAP_OPERATIONS_ERROR; retval = 0; goto error_return; } } if (ruv_c_init) { retval = modify_update_all( be, pb, &ruv_c, &txn ); if (DB_LOCK_DEADLOCK == retval) { /* Abort and re-try */ continue; } if (0 != retval) { slapi_log_err(SLAPI_LOG_ERR, "ldbm_back_modify", "modify_update_all failed, err=%d %s\n", retval, (msg = dblayer_strerror( retval )) ? msg : ""); if (LDBM_OS_ERR_IS_DISKFULL(retval)) disk_full = 1; ldap_result_code= LDAP_OPERATIONS_ERROR; goto error_return; } } if (0 == retval) { break; } } if (retry_count == RETRY_TIMES) { slapi_log_err(SLAPI_LOG_ERR, "ldbm_back_modify", "Retry count exceeded in modify\n"); ldap_result_code= LDAP_BUSY; goto error_return; } if (ruv_c_init) { if (modify_switch_entries(&ruv_c, be) != 0 ) { ldap_result_code= LDAP_OPERATIONS_ERROR; slapi_log_err(SLAPI_LOG_ERR, "ldbm_back_modify", "modify_switch_entries failed\n"); goto error_return; } } if (cache_replace( &inst->inst_cache, e, ec ) != 0 ) { MOD_SET_ERROR(ldap_result_code, LDAP_OPERATIONS_ERROR, retry_count); goto error_return; } /* e uncached */ /* we must return both e (which has been deleted) and new entry ec to cache */ /* cache_replace removes e from the cache hash tables */ cache_unlock_entry( &inst->inst_cache, e ); CACHE_RETURN( &inst->inst_cache, &e ); /* lock new entry in cache to prevent usage until we are complete */ cache_lock_entry( &inst->inst_cache, ec ); ec_locked = 1; postentry = slapi_entry_dup( ec->ep_entry ); slapi_pblock_set( pb, SLAPI_ENTRY_POST_OP, postentry ); /* invalidate virtual cache */ ec->ep_entry->e_virtual_watermark = 0; /* * LP Fix of crash when the commit will fail: * If the commit fail, the common error path will * try to unlock the entry again and crash (PR_ASSERT * in debug mode. * By just setting e to NULL, we avoid this. It's OK since * we don't use e after that in the normal case. */ e = NULL; /* call the transaction post modify plugins just before the commit */ if ((retval = plugin_call_plugins(pb, SLAPI_PLUGIN_BE_TXN_POST_MODIFY_FN))) { slapi_log_err(SLAPI_LOG_TRACE, "ldbm_back_modify", "SLAPI_PLUGIN_BE_TXN_POST_MODIFY_FN plugin " "returned error code %d\n", retval ); if (!ldap_result_code) { slapi_pblock_get(pb, SLAPI_RESULT_CODE, &ldap_result_code); } if (!opreturn) { slapi_pblock_get(pb, SLAPI_PLUGIN_OPRETURN, &opreturn); } if (!opreturn) { slapi_pblock_set(pb, SLAPI_PLUGIN_OPRETURN, ldap_result_code ? &ldap_result_code : &retval); } slapi_pblock_get(pb, SLAPI_PB_RESULT_TEXT, &ldap_result_message); goto error_return; } /* Release SERIAL LOCK */ retval = dblayer_txn_commit(be, &txn); /* after commit - txn is no longer valid - replace SLAPI_TXN with parent */ slapi_pblock_set(pb, SLAPI_TXN, parent_txn); if (0 != retval) { if (LDBM_OS_ERR_IS_DISKFULL(retval)) disk_full = 1; ldap_result_code= LDAP_OPERATIONS_ERROR; goto error_return; } rc= 0; goto common_return; error_return: if ( postentry != NULL ) { slapi_entry_free( postentry ); postentry = NULL; slapi_pblock_set( pb, SLAPI_ENTRY_POST_OP, NULL ); } if (retval == DB_RUNRECOVERY) { dblayer_remember_disk_filled(li); ldbm_nasty("ldbm_back_modify","Modify",81,retval); disk_full = 1; } if (disk_full) { rc= return_on_disk_full(li); } else { if (txn.back_txn_txn && (txn.back_txn_txn != parent_txn)) { /* make sure SLAPI_RESULT_CODE and SLAPI_PLUGIN_OPRETURN are set */ int val = 0; slapi_pblock_get(pb, SLAPI_RESULT_CODE, &val); if (!val) { if (!ldap_result_code) { ldap_result_code = LDAP_OPERATIONS_ERROR; } slapi_pblock_set(pb, SLAPI_RESULT_CODE, &ldap_result_code); } slapi_pblock_get( pb, SLAPI_PLUGIN_OPRETURN, &val ); if (!val) { opreturn = -1; slapi_pblock_set( pb, SLAPI_PLUGIN_OPRETURN, &opreturn ); } /* call the transaction post modify plugins just before the abort */ /* plugins called before abort should check for the OPRETURN or RESULT_CODE and skip processing if they don't want do anything - some plugins that keep track of a counter (usn, dna) may want to "rollback" the counter in this case */ if ((retval = plugin_call_plugins(pb, SLAPI_PLUGIN_BE_TXN_POST_MODIFY_FN))) { slapi_log_err(SLAPI_LOG_TRACE, "ldbm_back_modify", "SLAPI_PLUGIN_BE_TXN_POST_MODIFY_FN plugin returned error code %d\n", retval ); slapi_pblock_get(pb, SLAPI_RESULT_CODE, &ldap_result_code); slapi_pblock_get(pb, SLAPI_PB_RESULT_TEXT, &ldap_result_message); slapi_pblock_get(pb, SLAPI_PLUGIN_OPRETURN, &opreturn); if (!opreturn) { slapi_pblock_set(pb, SLAPI_PLUGIN_OPRETURN, ldap_result_code ? &ldap_result_code : &retval); } } /* It is safer not to abort when the transaction is not started. */ /* Release SERIAL LOCK */ dblayer_txn_abort(be, &txn); /* abort crashes in case disk full */ /* txn is no longer valid - reset the txn pointer to the parent */ slapi_pblock_set(pb, SLAPI_TXN, parent_txn); } if (!not_an_error) { rc = SLAPI_FAIL_GENERAL; } } /* if ec is in cache, remove it, then add back e if we still have it */ if (inst && cache_is_in_cache(&inst->inst_cache, ec)) { CACHE_REMOVE( &inst->inst_cache, ec ); /* if ec was in cache, e was not - add back e */ if (e) { if (CACHE_ADD( &inst->inst_cache, e, NULL ) < 0) { slapi_log_err(SLAPI_LOG_CACHE, "ldbm_back_modify", "CACHE_ADD %s failed\n", slapi_entry_get_dn(e->ep_entry)); } } } common_return: slapi_mods_done(&smods); if (inst) { if (ec_locked || cache_is_in_cache(&inst->inst_cache, ec)) { cache_unlock_entry(&inst->inst_cache, ec); } else if (e) { /* if ec was not in cache, cache_replace was not done. * i.e., e was not unlocked. */ cache_unlock_entry(&inst->inst_cache, e); CACHE_RETURN(&inst->inst_cache, &e); } CACHE_RETURN(&inst->inst_cache, &ec); if (inst->inst_ref_count) { slapi_counter_decrement(inst->inst_ref_count); } } /* result code could be used in the bepost plugin functions. */ slapi_pblock_set(pb, SLAPI_RESULT_CODE, &ldap_result_code); /* The bepostop is called even if the operation fails. */ if (!disk_full) plugin_call_plugins (pb, SLAPI_PLUGIN_BE_POST_MODIFY_FN); if (ruv_c_init) { modify_term(&ruv_c, be); } if (ldap_result_code == -1) { /* Reset to LDAP_NO_SUCH_OBJECT*/ ldap_result_code = LDAP_NO_SUCH_OBJECT; slapi_pblock_set(pb, SLAPI_RESULT_CODE, &ldap_result_code); } else { if (not_an_error) { /* This is mainly used by urp. Solved conflict is not an error. * And we don't want the supplier to halt sending the updates. */ ldap_result_code = LDAP_SUCCESS; } if (!result_sent) { /* result is already sent in find_entry. */ slapi_send_ldap_result( pb, ldap_result_code, NULL, ldap_result_message, 0, NULL ); } } /* free our backups */ ldap_mods_free(mods_original, 1); backentry_free(&original_entry); backentry_free(&tmpentry); slapi_ch_free_string(&errbuf); return rc; }
/* * Start the LDBM plugin, and all its instances. */ int ldbm_back_start( Slapi_PBlock *pb ) { struct ldbminfo *li; char *home_dir; int action; int retval; int issane = 0; PRUint64 total_cache_size = 0; size_t pagesize = 0; size_t pages = 0; size_t procpages = 0; size_t availpages = 0; char *msg = ""; /* This will be set by one of the two cache sizing paths below. */ char s[32]; /* big enough to hold %ld */ unsigned long cache_size_to_configure = 0; int zone_pages; int db_pages; int entry_pages; int import_pages; size_t zone_size; size_t import_size; size_t total_size; Object *inst_obj; ldbm_instance *inst; PRUint64 cache_size; PRUint64 dncache_size; PRUint64 db_size; #ifndef LINUX PRUint64 memsize = pages * pagesize; #endif slapi_log_err(SLAPI_LOG_TRACE, "ldbm_back_start", "ldbm backend starting\n"); slapi_pblock_get( pb, SLAPI_PLUGIN_PRIVATE, &li ); /* parse the config file here */ if (0 != ldbm_config_load_dse_info(li)) { slapi_log_err(SLAPI_LOG_ERR, "ldbm_back_start", "Loading database configuration failed\n"); return SLAPI_FAIL_GENERAL; } /* register with the binder-based resource limit subsystem so that */ /* lookthroughlimit can be supported on a per-connection basis. */ if ( slapi_reslimit_register( SLAPI_RESLIMIT_TYPE_INT, LDBM_LOOKTHROUGHLIMIT_AT, &li->li_reslimit_lookthrough_handle ) != SLAPI_RESLIMIT_STATUS_SUCCESS ) { slapi_log_err(SLAPI_LOG_ERR, "ldbm_back_start", "Resource limit registration failed for lookthroughlimit\n"); return SLAPI_FAIL_GENERAL; } /* register with the binder-based resource limit subsystem so that */ /* allidslimit (aka idlistscanlimit) can be supported on a per-connection basis. */ if ( slapi_reslimit_register( SLAPI_RESLIMIT_TYPE_INT, LDBM_ALLIDSLIMIT_AT, &li->li_reslimit_allids_handle ) != SLAPI_RESLIMIT_STATUS_SUCCESS ) { slapi_log_err(SLAPI_LOG_ERR, "ldbm_back_start", "Resource limit registration failed for allidslimit\n"); return SLAPI_FAIL_GENERAL; } /* register with the binder-based resource limit subsystem so that */ /* pagedlookthroughlimit can be supported on a per-connection basis. */ if ( slapi_reslimit_register( SLAPI_RESLIMIT_TYPE_INT, LDBM_PAGEDLOOKTHROUGHLIMIT_AT, &li->li_reslimit_pagedlookthrough_handle ) != SLAPI_RESLIMIT_STATUS_SUCCESS ) { slapi_log_err(SLAPI_LOG_ERR, "ldbm_back_start", "Resource limit registration failed for pagedlookthroughlimit\n"); return SLAPI_FAIL_GENERAL; } /* register with the binder-based resource limit subsystem so that */ /* pagedallidslimit (aka idlistscanlimit) can be supported on a per-connection basis. */ if ( slapi_reslimit_register( SLAPI_RESLIMIT_TYPE_INT, LDBM_PAGEDALLIDSLIMIT_AT, &li->li_reslimit_pagedallids_handle ) != SLAPI_RESLIMIT_STATUS_SUCCESS ) { slapi_log_err(SLAPI_LOG_ERR, "ldbm_back_start", "Resource limit registration failed for pagedallidslimit\n"); return SLAPI_FAIL_GENERAL; } /* lookthrough limit for the rangesearch */ if ( slapi_reslimit_register( SLAPI_RESLIMIT_TYPE_INT, LDBM_RANGELOOKTHROUGHLIMIT_AT, &li->li_reslimit_rangelookthrough_handle ) != SLAPI_RESLIMIT_STATUS_SUCCESS ) { slapi_log_err(SLAPI_LOG_ERR, "ldbm_back_start", "Resource limit registration failed for rangelookthroughlimit\n"); return SLAPI_FAIL_GENERAL; } /* If the db directory hasn't been set yet, we need to set it to * the default. */ if (NULL == li->li_directory || '\0' == li->li_directory[0]) { /* "get default" is a special string that tells the config * routines to figure out the default db directory by * reading cn=config. */ ldbm_config_internal_set(li, CONFIG_DIRECTORY, "get default"); } /* sanity check the autosizing values, no value or sum of values larger than 100. */ if ((li->li_cache_autosize > 100) || (li->li_cache_autosize_split > 100) || (li->li_import_cache_autosize > 100) || ((li->li_cache_autosize > 0) && (li->li_import_cache_autosize > 0) && (li->li_cache_autosize + li->li_import_cache_autosize > 100))) { slapi_log_err(SLAPI_LOG_ERR, "ldbm_back_start", "Cache autosizing: bad settings, " "value or sum of values can not larger than 100.\n"); } else { if (util_info_sys_pages(&pagesize, &pages, &procpages, &availpages) != 0) { slapi_log_err(SLAPI_LOG_ERR, "ldbm_back_start", "Unable to determine system page limits\n"); return SLAPI_FAIL_GENERAL; } if (pagesize) { if (li->li_cache_autosize == 0) { /* First, set our message. */ msg = "This can be corrected by altering the values of nsslapd-dbcachesize, nsslapd-cachememsize and nsslapd-dncachememsize\n"; for (inst_obj = objset_first_obj(li->li_instance_set); inst_obj; inst_obj = objset_next_obj(li->li_instance_set, inst_obj)) { inst = (ldbm_instance *)object_get_data(inst_obj); cache_size = (PRUint64)cache_get_max_size(&(inst->inst_cache)); db_size = dblayer_get_id2entry_size(inst); if (cache_size < db_size) { slapi_log_err(SLAPI_LOG_NOTICE, "ldbm_back_start - " "%s: entry cache size %llu B is " "less than db size %llu B; " "We recommend to increase the entry cache size " "nsslapd-cachememsize.\n", inst->inst_name, cache_size, db_size); } else { slapi_log_err(SLAPI_LOG_BACKLDBM, "ldbm_back_start", "%s: entry cache size: %lu B; db size: %lu B\n", inst->inst_name, cache_size, db_size); } /* Get the dn_cachesize */ dncache_size = (PRUint64)cache_get_max_size(&(inst->inst_dncache)); total_cache_size += cache_size + dncache_size; slapi_log_err(SLAPI_LOG_BACKLDBM, "ldbm_back_start", "total cache size: %lu B; \n", total_cache_size); } slapi_log_err(SLAPI_LOG_BACKLDBM, "ldbm_back_start", "Total entry cache size: %lu B; " "dbcache size: %lu B; " "available memory size: %lu B;\n", #ifdef LINUX (PRUint64)total_cache_size, (PRUint64)li->li_dbcachesize, availpages * pagesize #else (PRUint64)total_cache_size, (PRUint64)li->li_dbcachesize, memsize #endif ); /* autosizing dbCache and entryCache */ } else if (li->li_cache_autosize > 0) { msg = "This can be corrected by altering the values of nsslapd-cache-autosize, nsslapd-cache-autosize-split and nsslapd-dncachememsize\n"; zone_pages = (li->li_cache_autosize * pages) / 100; zone_size = zone_pages * pagesize; /* This is how much we "might" use, lets check it's sane. */ /* In the case it is not, this will *reduce* the allocation */ issane = util_is_cachesize_sane(&zone_size); if (!issane) { slapi_log_err(SLAPI_LOG_ERR, "ldbm_back_start", "Your autosized cache values have been reduced. Likely your nsslapd-cache-autosize percentage is too high.\n"); slapi_log_err(SLAPI_LOG_ERR, "ldbm_back_start", "%s", msg); } /* It's valid, lets divide it up and set according to user prefs */ zone_pages = zone_size / pagesize; db_pages = (li->li_cache_autosize_split * zone_pages) / 100; entry_pages = (zone_pages - db_pages) / objset_size(li->li_instance_set); /* We update this for the is-sane check below. */ total_cache_size = (zone_pages - db_pages) * pagesize; slapi_log_err(SLAPI_LOG_ERR, "ldbm_back_start", "cache autosizing. found %luk physical memory\n", pages*(pagesize/1024)); slapi_log_err(SLAPI_LOG_ERR, "ldbm_back_start", "cache autosizing. found %luk avaliable\n", zone_pages*(pagesize/1024)); slapi_log_err(SLAPI_LOG_ERR, "ldbm_back_start", "cache autosizing: db cache: %luk, " "each entry cache (%d total): %luk\n", db_pages*(pagesize/1024), objset_size(li->li_instance_set), entry_pages*(pagesize/1024)); /* libdb allocates 1.25x the amount we tell it to, * but only for values < 500Meg * For the larger memory, the overhead is relatively small. */ cache_size_to_configure = (unsigned long)(db_pages * pagesize); if (cache_size_to_configure < (500 * MEGABYTE)) { cache_size_to_configure = (unsigned long)((db_pages * pagesize) / 1.25); } sprintf(s, "%lu", cache_size_to_configure); ldbm_config_internal_set(li, CONFIG_DBCACHESIZE, s); li->li_cache_autosize_ec = (unsigned long)entry_pages * pagesize; for (inst_obj = objset_first_obj(li->li_instance_set); inst_obj; inst_obj = objset_next_obj(li->li_instance_set, inst_obj)) { inst = (ldbm_instance *)object_get_data(inst_obj); cache_set_max_entries(&(inst->inst_cache), -1); cache_set_max_size(&(inst->inst_cache), li->li_cache_autosize_ec, CACHE_TYPE_ENTRY); /* We need to get each instances dncache size to add to the total */ /* Else we can't properly check the cache allocations below */ /* Trac 48831 exists to allow this to be auto-sized too ... */ total_cache_size += (PRUint64)cache_get_max_size(&(inst->inst_dncache)); } } /* autosizing importCache */ if (li->li_import_cache_autosize > 0) { /* For some reason, -1 means 50 ... */ if (li->li_import_cache_autosize == -1) { li->li_import_cache_autosize = 50; } import_pages = (li->li_import_cache_autosize * pages) / 100; import_size = import_pages * pagesize; issane = util_is_cachesize_sane(&import_size); if (!issane) { slapi_log_err(SLAPI_LOG_NOTICE, "ldbm_back_start", "Your autosized import cache values have been reduced. " "Likely your nsslapd-import-cache-autosize percentage is too high.\n"); } /* We just accept the reduced allocation here. */ import_pages = import_size / pagesize; slapi_log_err(SLAPI_LOG_NOTICE, "ldbm_back_start", "cache autosizing: import cache: %luk\n", import_pages*(pagesize/1024)); sprintf(s, "%lu", (unsigned long)(import_pages * pagesize)); ldbm_config_internal_set(li, CONFIG_IMPORT_CACHESIZE, s); } } } /* Finally, lets check that the total result is sane. */ total_size = total_cache_size + (PRUint64)li->li_dbcachesize; issane = util_is_cachesize_sane(&total_size); if (!issane) { /* Right, it's time to panic */ slapi_log_err(SLAPI_LOG_CRIT, "ldbm_back_start", "It is highly likely your memory configuration of all backends will EXCEED your systems memory.\n"); slapi_log_err(SLAPI_LOG_CRIT, "ldbm_back_start", "In a future release this WILL prevent server start up. You MUST alter your configuration.\n"); slapi_log_err(SLAPI_LOG_CRIT, "ldbm_back_start", "Total entry cache size: %lu B; " "dbcache size: %lu B; " "available memory size: %lu B; \n", #ifdef LINUX (PRUint64)total_cache_size, (PRUint64)li->li_dbcachesize, availpages * pagesize #else (PRUint64)total_cache_size, (PRUint64)li->li_dbcachesize, memsize #endif ); slapi_log_err(SLAPI_LOG_ERR, "ldbm_back_start", "%s\n", msg); /* WB 2016 - This should be UNCOMMENTED in a future release */ /* return SLAPI_FAIL_GENERAL; */ } retval = check_db_version(li, &action); if (0 != retval) { slapi_log_err(SLAPI_LOG_ERR, "ldbm_back_start", "db version is not supported\n"); return SLAPI_FAIL_GENERAL; } if (action & (DBVERSION_UPGRADE_3_4|DBVERSION_UPGRADE_4_4|DBVERSION_UPGRADE_4_5)) { retval = dblayer_start(li,DBLAYER_CLEAN_RECOVER_MODE); } else { retval = dblayer_start(li,DBLAYER_NORMAL_MODE); } if (0 != retval) { char *msg; slapi_log_err(SLAPI_LOG_ERR, "ldbm_back_start", "Failed to init database, err=%d %s\n", retval, (msg = dblayer_strerror( retval )) ? msg : ""); if (LDBM_OS_ERR_IS_DISKFULL(retval)) return return_on_disk_full(li); else return SLAPI_FAIL_GENERAL; } /* Walk down the instance list, starting all the instances. */ retval = ldbm_instance_startall(li); if (0 != retval) { char *msg; slapi_log_err(SLAPI_LOG_ERR, "ldbm_back_start", "Failed to start databases, err=%d %s\n", retval, (msg = dblayer_strerror( retval )) ? msg : ""); if (LDBM_OS_ERR_IS_DISKFULL(retval)) return return_on_disk_full(li); else { if ((li->li_cache_autosize > 0) && (li->li_cache_autosize <= 100)) { slapi_log_err(SLAPI_LOG_ERR, "ldbm_back_start", "Failed to allocate %lu byte dbcache. " "Please reduce the value of %s and restart the server.\n", li->li_dbcachesize, CONFIG_CACHE_AUTOSIZE); } return SLAPI_FAIL_GENERAL; } } /* write DBVERSION file if one does not exist */ home_dir = dblayer_get_home_dir(li, NULL); if (!dbversion_exists(li, home_dir)) { dbversion_write (li, home_dir, NULL, DBVERSION_ALL); } /* this function is called every time new db is initialized */ /* currently it is called the 2nd time when changelog db is */ /* dynamically created. Code below should only be called once */ if (!initialized) { ldbm_compute_init(); initialized = 1; } /* initialize the USN counter */ ldbm_usn_init(li); slapi_log_err(SLAPI_LOG_TRACE, "ldbm_back_start", "ldbm backend done starting\n"); return( 0 ); }