/** BEGIN COPYRIGHT BLOCK
 * Copyright (C) 2001 Sun Microsystems, Inc. Used by permission.
 * Copyright (C) 2005 Red Hat, Inc.
 * All rights reserved.
 *
 * License: GPL (version 3 or any later version).
 * See LICENSE for details. 
 * END COPYRIGHT BLOCK **/

#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

/* This file handles configuration information that is specific
 * to ldbm instances.
 */

#include "back-ldbm.h"
#include "dblayer.h"

/* Forward declarations for the callbacks */
int ldbm_instance_search_config_entry_callback(Slapi_PBlock *pb, Slapi_Entry *e, Slapi_Entry *entryAfter, int *returncode, char *returntext, void *arg);
int ldbm_instance_modify_config_entry_callback(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e, int *returncode, char *returntext, void *arg);

static char *ldbm_instance_attrcrypt_filter = "(objectclass=nsAttributeEncryption)";

/* dse entries add for a new ldbm instance */
static char *ldbm_instance_skeleton_entries[] =
{
    "dn:cn=monitor, cn=%s, cn=%s, cn=plugins, cn=config\n"
    "objectclass:top\n"
    "objectclass:extensibleObject\n"
    "cn:monitor\n",

    "dn:cn=index, cn=%s, cn=%s, cn=plugins, cn=config\n"
    "objectclass:top\n"
    "objectclass:extensibleObject\n"
    "cn:index\n",
    
    "dn:cn=encrypted attributes, cn=%s, cn=%s, cn=plugins, cn=config\n"
    "objectclass:top\n"
    "objectclass:extensibleObject\n"
    "cn:encrypted attributes\n",
    
    "dn:cn=encrypted attribute keys, cn=%s, cn=%s, cn=plugins, cn=config\n"
    "objectclass:top\n"
    "objectclass:extensibleObject\n"
    "cn:encrypted attribute keys\n",

    ""
};


/*------------------------------------------------------------------------
 * Get and set functions for ldbm instance variables
 *----------------------------------------------------------------------*/
static void *
ldbm_instance_config_cachesize_get(void *arg) 
{
    ldbm_instance *inst = (ldbm_instance *) arg;
    
    return (void *) cache_get_max_entries(&(inst->inst_cache));
}

static int 
ldbm_instance_config_cachesize_set(void *arg, void *value, char *errorbuf, int phase, int apply) 
{
    ldbm_instance *inst = (ldbm_instance *) arg;
    int retval = LDAP_SUCCESS;
    long val = (long) value;

    /* Do whatever we can to make sure the data is ok. */

    if (apply) {
        cache_set_max_entries(&(inst->inst_cache), val);
    }

    return retval;
}

static void * 
ldbm_instance_config_cachememsize_get(void *arg) 
{
    ldbm_instance *inst = (ldbm_instance *) arg;
    
    return (void *) cache_get_max_size(&(inst->inst_cache));
}

static int 
ldbm_instance_config_cachememsize_set(void *arg, void *value, char *errorbuf, int phase, int apply) 
{
    ldbm_instance *inst = (ldbm_instance *) arg;
    int retval = LDAP_SUCCESS;
    size_t val = (size_t) value;
    size_t chkval = val;

    /* Do whatever we can to make sure the data is ok. */

    if (apply) {
        if (!dblayer_is_cachesize_sane(&chkval)){
            PR_snprintf(errorbuf, SLAPI_DSE_RETURNTEXT_SIZE,
                    "Error: cachememsize value is too large.");
            LDAPDebug( LDAP_DEBUG_ANY,"Error: cachememsize value is too large.\n",
                    0, 0, 0);
            return LDAP_UNWILLING_TO_PERFORM;
        }
        cache_set_max_size(&(inst->inst_cache), val, CACHE_TYPE_ENTRY);
    }

    return retval;
}

static void * 
ldbm_instance_config_dncachememsize_get(void *arg) 
{
    ldbm_instance *inst = (ldbm_instance *) arg;
    
    return (void *) cache_get_max_size(&(inst->inst_dncache));
}

static int 
ldbm_instance_config_dncachememsize_set(void *arg, void *value, char *errorbuf, int phase, int apply) 
{
    ldbm_instance *inst = (ldbm_instance *) arg;
    int retval = LDAP_SUCCESS;
    size_t val = (size_t)value;
    size_t chkval = val;

    /* Do whatever we can to make sure the data is ok. */

    if (apply) {
        if (!dblayer_is_cachesize_sane(&chkval)){
            PR_snprintf(errorbuf, SLAPI_DSE_RETURNTEXT_SIZE,
                    "Error: dncachememsize value is too large.");
            LDAPDebug( LDAP_DEBUG_ANY,"Error: dncachememsize value is too large.\n",
                    0, 0, 0);
            return LDAP_UNWILLING_TO_PERFORM;
        }
        cache_set_max_size(&(inst->inst_dncache), val, CACHE_TYPE_DN);
    }

    return retval;
}

static void *
ldbm_instance_config_readonly_get(void *arg)
{
    ldbm_instance *inst = (ldbm_instance *)arg;

    return (void *)((uintptr_t)inst->inst_be->be_readonly);
}

static void *
ldbm_instance_config_instance_dir_get(void *arg)
{
    ldbm_instance *inst = (ldbm_instance *)arg;

    if (inst->inst_dir_name == NULL)
        return slapi_ch_strdup("");
    else if (inst->inst_parent_dir_name)
    {
        int len = strlen(inst->inst_parent_dir_name) +
                  strlen(inst->inst_dir_name) + 2;
        char *full_inst_dir = (char *)slapi_ch_malloc(len);
        PR_snprintf(full_inst_dir, len, "%s%c%s",
            inst->inst_parent_dir_name, get_sep(inst->inst_parent_dir_name),
            inst->inst_dir_name);
        return full_inst_dir;
    }
    else 
        return slapi_ch_strdup(inst->inst_dir_name);
}

static void *
ldbm_instance_config_require_index_get(void *arg)
{
    ldbm_instance *inst = (ldbm_instance *)arg;
    
    return (void *)((uintptr_t)inst->require_index);
}

static int
ldbm_instance_config_instance_dir_set(void *arg, void *value, char *errorbuf, int phase, int apply)
{
    ldbm_instance *inst = (ldbm_instance *)arg;

    if (!apply) {
        return LDAP_SUCCESS;
    }

    if ((value == NULL) || (strlen(value) == 0))
    {
        inst->inst_dir_name = NULL;
        inst->inst_parent_dir_name = NULL;
    }
    else
    {
        char *dir = (char *)value;
        if (is_fullpath(dir))
        {
            char sep = get_sep(dir);
            char *p = strrchr(dir, sep);
            if (NULL == p)    /* should not happens, tho */
            {
                inst->inst_parent_dir_name = NULL;
                inst->inst_dir_name = rel2abspath(dir); /* normalize dir;
                                                           strdup'ed in 
                                                           rel2abspath */
            }
            else
            {
                *p = '\0';
                inst->inst_parent_dir_name = rel2abspath(dir); /* normalize dir;
                                                                  strdup'ed in
                                                                  rel2abspath */
                inst->inst_dir_name = slapi_ch_strdup(p+1);
                *p = sep;
            }
        }
        else
        {
            inst->inst_parent_dir_name = NULL;
            inst->inst_dir_name = slapi_ch_strdup(dir);
        }
    }
    return LDAP_SUCCESS;
}

static int
ldbm_instance_config_readonly_set(void *arg, void *value, char *errorbuf, int phase, int apply)
{
    ldbm_instance *inst = (ldbm_instance *)arg;
    uintptr_t pval = (uintptr_t)value;

    if (!apply) {
        return LDAP_SUCCESS;
    }

    if (CONFIG_PHASE_RUNNING == phase) {
        /* if the instance is busy, we'll save the user's readonly settings
         * but won't change them until the instance is un-busy again.
         */
        if (! (inst->inst_flags & INST_FLAG_BUSY)) {
            slapi_mtn_be_set_readonly(inst->inst_be, (int)pval);
        }
        if ((int)pval) {
            inst->inst_flags |= INST_FLAG_READONLY;
        } else {
            inst->inst_flags &= ~INST_FLAG_READONLY;
        }
    } else {
        slapi_be_set_readonly(inst->inst_be, (int)pval);
    }

    return LDAP_SUCCESS;
}

static int
ldbm_instance_config_require_index_set(void *arg, void *value, char *errorbuf, int phase, int apply)
{
    ldbm_instance *inst = (ldbm_instance *)arg;

    if (!apply) {
        return LDAP_SUCCESS;
    }

    inst->require_index = (int)((uintptr_t)value);

    return LDAP_SUCCESS;
}


/*------------------------------------------------------------------------
 * ldbm instance configuration array
 *----------------------------------------------------------------------*/
static config_info ldbm_instance_config[] = {
    {CONFIG_INSTANCE_CACHESIZE, CONFIG_TYPE_LONG, "-1", &ldbm_instance_config_cachesize_get, &ldbm_instance_config_cachesize_set, CONFIG_FLAG_ALWAYS_SHOW|CONFIG_FLAG_ALLOW_RUNNING_CHANGE},
    {CONFIG_INSTANCE_CACHEMEMSIZE, CONFIG_TYPE_SIZE_T, "10485760", &ldbm_instance_config_cachememsize_get, &ldbm_instance_config_cachememsize_set, CONFIG_FLAG_ALWAYS_SHOW|CONFIG_FLAG_ALLOW_RUNNING_CHANGE},
    {CONFIG_INSTANCE_READONLY, CONFIG_TYPE_ONOFF, "off", &ldbm_instance_config_readonly_get, &ldbm_instance_config_readonly_set, CONFIG_FLAG_ALWAYS_SHOW|CONFIG_FLAG_ALLOW_RUNNING_CHANGE},
    {CONFIG_INSTANCE_REQUIRE_INDEX, CONFIG_TYPE_ONOFF, "off", &ldbm_instance_config_require_index_get, &ldbm_instance_config_require_index_set, CONFIG_FLAG_ALWAYS_SHOW|CONFIG_FLAG_ALLOW_RUNNING_CHANGE},
    {CONFIG_INSTANCE_DIR, CONFIG_TYPE_STRING, NULL, &ldbm_instance_config_instance_dir_get, &ldbm_instance_config_instance_dir_set, CONFIG_FLAG_ALWAYS_SHOW},
    {CONFIG_INSTANCE_DNCACHEMEMSIZE, CONFIG_TYPE_SIZE_T, "10485760", &ldbm_instance_config_dncachememsize_get, &ldbm_instance_config_dncachememsize_set, CONFIG_FLAG_ALWAYS_SHOW|CONFIG_FLAG_ALLOW_RUNNING_CHANGE},
    {NULL, 0, NULL, NULL, NULL, 0}
};

void 
ldbm_instance_config_setup_default(ldbm_instance *inst) 
{
    config_info *config;
    char err_buf[BUFSIZ];

    for (config = ldbm_instance_config; config->config_name != NULL; config++) {
        ldbm_config_set((void *)inst, config->config_name, ldbm_instance_config, NULL /* use default */, err_buf, CONFIG_PHASE_INITIALIZATION, 1 /* apply */, LDAP_MOD_REPLACE);
    }
}

static int 
parse_ldbm_instance_entry(Slapi_Entry *e, char **instance_name)
{
    Slapi_Attr *attr = NULL;
    
    for (slapi_entry_first_attr(e, &attr); attr;
         slapi_entry_next_attr(e, attr, &attr)) {
        char *attr_name = NULL;

        slapi_attr_get_type(attr, &attr_name);
        if (strcasecmp(attr_name, "cn") == 0) {
            Slapi_Value *sval = NULL;
            struct berval *bval;
            slapi_attr_first_value(attr, &sval);
            bval = (struct berval *) slapi_value_get_berval(sval);
            *instance_name = slapi_ch_strdup((char *)bval->bv_val);
        }
    }
    return 0;
}

/* When a new instance is started, we need to read the dse to
 * find out what indexes should be maintained.  This function
 * does that.  Returns 0 on success. */
static int 
read_instance_index_entries(ldbm_instance *inst)
{
    Slapi_PBlock *tmp_pb;
    int scope = LDAP_SCOPE_SUBTREE;
    const char *searchfilter = "(objectclass=nsIndex)";
    char *basedn = NULL;

    /* Construct the base dn of the subtree that holds the index entries 
     * for this instance. */
    basedn = slapi_create_dn_string("cn=index,cn=%s,cn=%s,cn=plugins,cn=config",
                          inst->inst_name, inst->inst_li->li_plugin->plg_name); 
    if (NULL == basedn) {
        LDAPDebug2Args(LDAP_DEBUG_ANY,
                       "read_instance_index_entries: "
                       "failed create index dn for plugin %s, instance %s\n",
                       inst->inst_li->li_plugin->plg_name, inst->inst_name); 
        return 1;
    }

    /* Set up a tmp callback that will handle the init for each index entry */
    slapi_config_register_callback(SLAPI_OPERATION_SEARCH, DSE_FLAG_PREOP,
        basedn, scope, searchfilter, ldbm_index_init_entry_callback,
        (void *) inst);

    /* Do a search of the subtree containing the index entries */
    tmp_pb = slapi_pblock_new();
    slapi_search_internal_set_pb(tmp_pb, basedn, LDAP_SCOPE_SUBTREE, 
        searchfilter, NULL, 0, NULL, NULL, inst->inst_li->li_identity, 0);
    slapi_search_internal_pb (tmp_pb);

    /* Remove the tmp callback */
    slapi_config_remove_callback(SLAPI_OPERATION_SEARCH, DSE_FLAG_PREOP,
        basedn, scope, searchfilter, ldbm_index_init_entry_callback);
    slapi_free_search_results_internal(tmp_pb);
    slapi_pblock_destroy(tmp_pb);

    slapi_ch_free_string(&basedn);
    return 0;
}

/* When a new instance is started, we need to read the dse to
 * find out what attributes should be encrypted.  This function
 * does that.  Returns 0 on success. */
static int 
read_instance_attrcrypt_entries(ldbm_instance *inst)
{
    Slapi_PBlock *tmp_pb;
    int scope = LDAP_SCOPE_SUBTREE;
    const char *searchfilter = ldbm_instance_attrcrypt_filter;
    char *basedn = NULL;

    /* Construct the base dn of the subtree that holds the index entries 
     * for this instance. */
    basedn = slapi_create_dn_string("cn=encrypted attributes,cn=%s,cn=%s,cn=plugins,cn=config",
                          inst->inst_name, inst->inst_li->li_plugin->plg_name); 
    if (NULL == basedn) {
        LDAPDebug2Args(LDAP_DEBUG_ANY,
                       "read_instance_attrcrypt_entries: "
                       "failed create encrypted attributes dn for plugin %s, "
                       "instance %s\n",
                       inst->inst_li->li_plugin->plg_name, inst->inst_name);
        return 1;
    }

    /* Set up a tmp callback that will handle the init for each index entry */
    slapi_config_register_callback(SLAPI_OPERATION_SEARCH, DSE_FLAG_PREOP,
        basedn, scope, searchfilter, ldbm_attrcrypt_init_entry_callback,
        (void *) inst);

    /* Do a search of the subtree containing the index entries */
    tmp_pb = slapi_pblock_new();
    slapi_search_internal_set_pb(tmp_pb, basedn, LDAP_SCOPE_SUBTREE, 
        searchfilter, NULL, 0, NULL, NULL, inst->inst_li->li_identity, 0);
    slapi_search_internal_pb (tmp_pb);

    /* Remove the tmp callback */
    slapi_config_remove_callback(SLAPI_OPERATION_SEARCH, DSE_FLAG_PREOP,
        basedn, scope, searchfilter, ldbm_attrcrypt_init_entry_callback);
    slapi_free_search_results_internal(tmp_pb);
    slapi_pblock_destroy(tmp_pb);

    slapi_ch_free_string(&basedn);
    return 0;
}

/* Handles the parsing of the config entry for an ldbm instance.  Returns 0
 * on success. */    
static int 
parse_ldbm_instance_config_entry(ldbm_instance *inst, Slapi_Entry *e, config_info *config_array)
{
    Slapi_Attr *attr = NULL;
    
    for (slapi_entry_first_attr(e, &attr); attr; 
         slapi_entry_next_attr(e, attr, &attr)) { 
        char *attr_name = NULL;
        Slapi_Value *sval = NULL;
        struct berval *bval;
        char err_buf[BUFSIZ];
        
        slapi_attr_get_type(attr, &attr_name);

        /* There are some attributes that we don't care about, 
         * like objectclass. */
        if (ldbm_config_ignored_attr(attr_name)) {
            continue;
        }

        /* We have to handle suffix attributes a little differently */
        if (strcasecmp(attr_name, CONFIG_INSTANCE_SUFFIX) == 0) {
            Slapi_DN suffix;

            slapi_attr_first_value(attr, &sval);
            
            bval = (struct berval *) slapi_value_get_berval(sval);
            slapi_sdn_init_dn_byref(&suffix, bval->bv_val);
            if (!slapi_be_issuffix(inst->inst_be, &suffix)) {
                be_addsuffix(inst->inst_be, &suffix);
            }
            slapi_sdn_done(&suffix);
            continue;
        }

        /* We are assuming that each of these attributes are to have 
         * only one value.  If they have more than one value, like
         * the nsslapd-suffix attribute, then they need to be 
         * handled differently. */
        slapi_attr_first_value(attr, &sval);
        bval = (struct berval *) slapi_value_get_berval(sval);

        if (ldbm_config_set((void *) inst, attr_name, config_array, bval,
            err_buf, CONFIG_PHASE_STARTUP, 1 /* apply */, LDAP_MOD_REPLACE) != LDAP_SUCCESS) {
            LDAPDebug(LDAP_DEBUG_ANY, "Error with config attribute %s : %s\n",
                      attr_name, err_buf, 0);
            return 1;
        }
    }

    /* Read the index entries */
    read_instance_index_entries(inst);
    /* Read the attribute encryption entries */
    read_instance_attrcrypt_entries(inst);

    return 0;
}

/* general-purpose callback to deny an operation */
static int ldbm_instance_deny_config(Slapi_PBlock *pb, Slapi_Entry *e,
    Slapi_Entry *entryAfter, int *returncode, char *returntext, void *arg)
{
    *returncode = LDAP_UNWILLING_TO_PERFORM;
    return SLAPI_DSE_CALLBACK_ERROR;
}

/* Reads in any config information held in the dse for the given
 * entry.  Creates dse entries used to configure the given instance
 * if they don't already exist.  Registers dse callback functions to
 * maintain those dse entries.  Returns 0 on success. */
int 
ldbm_instance_config_load_dse_info(ldbm_instance *inst)
{
    struct ldbminfo *li = inst->inst_li;
    Slapi_PBlock *search_pb;
    Slapi_Entry **entries = NULL;
    char *dn = NULL;
    int rval = 0;

    /* We try to read the entry 
     * cn=instance_name, cn=ldbm database, cn=plugins, cn=config.  If the
     * entry is there, then we process the config information it stores.
     */
    dn = slapi_create_dn_string("cn=%s,cn=%s,cn=plugins,cn=config",
                                inst->inst_name, li->li_plugin->plg_name);
    if (NULL == dn) {
        LDAPDebug2Args(LDAP_DEBUG_ANY,
                       "ldbm_instance_config_load_dse_info: "
                       "failed create instance dn %s for plugin %s\n",
                       inst->inst_name, inst->inst_li->li_plugin->plg_name);
        rval = 1;
        goto bail;
    }

    search_pb = slapi_pblock_new();
    if (!search_pb) {
        LDAPDebug(LDAP_DEBUG_ANY,
                       "ldbm_instance_config_load_dse_info: Out of memory\n",
                       0, 0, 0);
        rval = 1;
        goto bail;
    }

    slapi_search_internal_set_pb(search_pb, dn, LDAP_SCOPE_BASE, 
                                 "objectclass=*", NULL, 0, NULL, NULL,
                                 li->li_identity, 0);
    slapi_search_internal_pb (search_pb);
    slapi_pblock_get(search_pb, SLAPI_PLUGIN_INTOP_RESULT, &rval);

    if (rval != LDAP_SUCCESS) {
        LDAPDebug(LDAP_DEBUG_ANY, "Error accessing the config DSE\n", 0, 0, 0);
        rval = 1;
        goto bail;
    } else {
        /* Need to parse the configuration information for the ldbm
         * plugin that is held in the DSE. */
        slapi_pblock_get(search_pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES,
                         &entries);
        if ((!entries) || (!entries[0])) {
            LDAPDebug(LDAP_DEBUG_ANY, "Error accessing the config DSE\n",
                      0, 0, 0);
            rval = 1;
            goto bail;
        }
        if (0 != parse_ldbm_instance_config_entry(inst, entries[0],
                                         ldbm_instance_config)) {
            LDAPDebug(LDAP_DEBUG_ANY, "Error parsing the config DSE\n",
                      0, 0, 0);
            rval = 1;
            goto bail;
        }
    }

    if (search_pb)
    {
        slapi_free_search_results_internal(search_pb);
        slapi_pblock_destroy(search_pb);
    }

    /* Add skeleton dse entries for this instance */
    /* IF they already exist, that's ok */
    ldbm_config_add_dse_entries(li, ldbm_instance_skeleton_entries,
                                inst->inst_name, li->li_plugin->plg_name,
                                inst->inst_name, 0);

    /* setup the dse callback functions for the ldbm instance config entry */
    slapi_config_register_callback(SLAPI_OPERATION_SEARCH, DSE_FLAG_PREOP, dn,
        LDAP_SCOPE_BASE, "(objectclass=*)",
        ldbm_instance_search_config_entry_callback, (void *) inst);
    slapi_config_register_callback(SLAPI_OPERATION_MODIFY, DSE_FLAG_PREOP, dn,
        LDAP_SCOPE_BASE, "(objectclass=*)",
        ldbm_instance_modify_config_entry_callback, (void *) inst);
    slapi_config_register_callback(DSE_OPERATION_WRITE, DSE_FLAG_PREOP, dn,
        LDAP_SCOPE_BASE, "(objectclass=*)",
        ldbm_instance_search_config_entry_callback, (void *) inst);
    slapi_config_register_callback(SLAPI_OPERATION_ADD, DSE_FLAG_PREOP, dn,
        LDAP_SCOPE_BASE, "(objectclass=*)",
        ldbm_instance_deny_config, (void *)inst);
    /* delete is handled by a callback set in ldbm_config.c */

    slapi_ch_free_string(&dn);

    /* don't forget the monitor! */
    dn = slapi_create_dn_string("cn=monitor,cn=%s,cn=%s,cn=plugins,cn=config",
                                inst->inst_name, li->li_plugin->plg_name);
    if (NULL == dn) {
        LDAPDebug2Args(LDAP_DEBUG_ANY,
                       "ldbm_instance_config_load_dse_info: "
                       "failed create monitor instance dn for plugin %s, "
                       "instance %s\n",
                       inst->inst_li->li_plugin->plg_name, inst->inst_name);
        rval = 1;
        goto bail;
    }
    /* make callback on search; deny add/modify/delete */
    slapi_config_register_callback(SLAPI_OPERATION_SEARCH, DSE_FLAG_PREOP, dn,
        LDAP_SCOPE_BASE, "(objectclass=*)", ldbm_back_monitor_instance_search,
        (void *)inst);
    slapi_config_register_callback(SLAPI_OPERATION_ADD, DSE_FLAG_PREOP, dn,
        LDAP_SCOPE_SUBTREE, "(objectclass=*)", ldbm_instance_deny_config,
        (void *)inst);
    slapi_config_register_callback(SLAPI_OPERATION_MODIFY, DSE_FLAG_PREOP, dn,
        LDAP_SCOPE_BASE, "(objectclass=*)", ldbm_instance_deny_config,
        (void *)inst);
    /* delete is okay */
    slapi_ch_free_string(&dn);

    /* Callbacks to handle indexes */
    dn = slapi_create_dn_string("cn=index,cn=%s,cn=%s,cn=plugins,cn=config",
                                inst->inst_name, li->li_plugin->plg_name);
    if (NULL == dn) {
        LDAPDebug2Args(LDAP_DEBUG_ANY,
                       "ldbm_instance_config_load_dse_info: "
                       "failed create index instance dn for plugin %s, "
                       "instance %s\n",
                       inst->inst_li->li_plugin->plg_name, inst->inst_name);
        rval = 1;
        goto bail;
    }
    slapi_config_register_callback(SLAPI_OPERATION_ADD, DSE_FLAG_PREOP, dn,
        LDAP_SCOPE_SUBTREE, "(objectclass=nsIndex)",
        ldbm_instance_index_config_add_callback, (void *) inst);
    slapi_config_register_callback(SLAPI_OPERATION_DELETE, DSE_FLAG_PREOP, dn,
        LDAP_SCOPE_SUBTREE, "(objectclass=nsIndex)",
        ldbm_instance_index_config_delete_callback, (void *) inst);
    slapi_config_register_callback(SLAPI_OPERATION_MODIFY, DSE_FLAG_PREOP, dn,
        LDAP_SCOPE_SUBTREE, "(objectclass=nsIndex)",
        ldbm_instance_index_config_modify_callback, (void *) inst);
    slapi_ch_free_string(&dn);

    /* Callbacks to handle attribute encryption */
    dn = slapi_create_dn_string("cn=encrypted attributes,cn=%s,cn=%s,cn=plugins,cn=config",
                                inst->inst_name, li->li_plugin->plg_name);
    if (NULL == dn) {
        LDAPDebug2Args(LDAP_DEBUG_ANY,
                       "ldbm_instance_config_load_dse_info: "
                       "failed create encrypted attribute instance dn "
                       "for plugin %s, instance %s\n",
                       inst->inst_li->li_plugin->plg_name, inst->inst_name);
        rval = 1;
        goto bail;
    }
    slapi_config_register_callback(SLAPI_OPERATION_ADD, DSE_FLAG_PREOP, dn,
        LDAP_SCOPE_SUBTREE, ldbm_instance_attrcrypt_filter,
        ldbm_instance_attrcrypt_config_add_callback, (void *) inst);
    slapi_config_register_callback(SLAPI_OPERATION_DELETE, DSE_FLAG_PREOP, dn,
        LDAP_SCOPE_SUBTREE, ldbm_instance_attrcrypt_filter,
        ldbm_instance_attrcrypt_config_delete_callback, (void *) inst);
    slapi_config_register_callback(SLAPI_OPERATION_MODIFY, DSE_FLAG_PREOP, dn,
        LDAP_SCOPE_SUBTREE, ldbm_instance_attrcrypt_filter,
        ldbm_instance_attrcrypt_config_modify_callback, (void *) inst);
    rval = 0;
bail:
    slapi_ch_free_string(&dn);
    return rval;
}

/*
 * Config. DSE callback for instance entry searches.
 */
int 
ldbm_instance_search_config_entry_callback(Slapi_PBlock *pb, Slapi_Entry *e, Slapi_Entry *entryAfter, int *returncode, char *returntext, void *arg)
{
    char buf[BUFSIZ];
    struct berval *vals[2];
    struct berval val;
    ldbm_instance *inst = (ldbm_instance *) arg;
    config_info *config;
    int x;
    const Slapi_DN *suffix;

    vals[0] = &val;
    vals[1] = NULL;  

    returntext[0] = '\0';

    /* show the suffixes */
    attrlist_delete(&e->e_attrs, CONFIG_INSTANCE_SUFFIX);
    x = 0;
    do {
        suffix = slapi_be_getsuffix(inst->inst_be, x);
        if (suffix != NULL) {
            val.bv_val = (char *) slapi_sdn_get_dn(suffix);
            val.bv_len = strlen (val.bv_val);
            attrlist_merge( &e->e_attrs, CONFIG_INSTANCE_SUFFIX, vals );
        }
        x++;
    } while(suffix!=NULL);

    PR_Lock(inst->inst_config_mutex);

    for(config = ldbm_instance_config; config->config_name != NULL; config++) {
        /* Go through the ldbm_config table and fill in the entry. */

        if (!(config->config_flags & (CONFIG_FLAG_ALWAYS_SHOW | CONFIG_FLAG_PREVIOUSLY_SET))) {
            /* This config option shouldn't be shown */
            continue;
        }

        ldbm_config_get((void *) inst, config, buf);

        val.bv_val = buf;
        val.bv_len = strlen(buf);
        slapi_entry_attr_replace(e, config->config_name, vals);
    }
    
    PR_Unlock(inst->inst_config_mutex);

    *returncode = LDAP_SUCCESS;
    return SLAPI_DSE_CALLBACK_OK;
}

/* This function is used by the instance modify callback to add a new
 * suffix.  It return LDAP_SUCCESS on success.
 */
int 
add_suffix(ldbm_instance *inst, struct berval **bvals, int apply_mod, char *returntext)
{
    Slapi_DN suffix;
    int x;

    returntext[0] = '\0';
    for (x = 0; bvals[x]; x++) {
        slapi_sdn_init_dn_byref(&suffix, bvals[x]->bv_val);
        if (!slapi_be_issuffix(inst->inst_be, &suffix) && apply_mod) {
            be_addsuffix(inst->inst_be, &suffix);
        }
        slapi_sdn_done(&suffix);
    }

    return LDAP_SUCCESS;
}

/*
 * Config. DSE callback for instance entry modifies.
 */        
int 
ldbm_instance_modify_config_entry_callback(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e, int *returncode, char *returntext, void *arg) 
{ 
    int i; 
    char *attr_name; 
    LDAPMod **mods; 
    int rc = LDAP_SUCCESS; 
    int apply_mod = 0; 
    ldbm_instance *inst = (ldbm_instance *) arg;

    /* This lock is probably way too conservative, but we don't expect much
     * contention for it. */
    PR_Lock(inst->inst_config_mutex);

    slapi_pblock_get( pb, SLAPI_MODIFY_MODS, &mods ); 

    if (!returntext) {
        rc = LDAP_OPERATIONS_ERROR;
        LDAPDebug(LDAP_DEBUG_ANY,
                  "ldbm_instance_modify_config_entry_callback: "
                  "NULL return text\n",
                  0, 0, 0);
        goto out;
    }

    returntext[0] = '\0'; 

    /* 
     * First pass: set apply mods to 0 so only input validation will be done; 
     * 2nd pass: set apply mods to 1 to apply changes to internal storage 
     */ 
    for ( apply_mod = 0; apply_mod <= 1 && LDAP_SUCCESS == rc; apply_mod++ ) {
        for (i = 0; mods && mods[i] && LDAP_SUCCESS == rc; i++) {
            attr_name = mods[i]->mod_type;

            if (strcasecmp(attr_name, CONFIG_INSTANCE_SUFFIX) == 0) {
                /* naughty naughty, we don't allow this */
                rc = LDAP_UNWILLING_TO_PERFORM;
                PR_snprintf(returntext, SLAPI_DSE_RETURNTEXT_SIZE,
                            "Can't change the root suffix of a backend");
                LDAPDebug(LDAP_DEBUG_ANY,
                          "ldbm: modify attempted to change the root suffix "
                          "of a backend (which is not allowed)\n",
                          0, 0, 0);
                continue;
            }

            /* There are some attributes that we don't care about, like
             * modifiersname. */
            if (ldbm_config_ignored_attr(attr_name)) {
                continue;
            }

        /* This assumes there is only one bval for this mod. */
            rc = ldbm_config_set((void *) inst, attr_name,
                ldbm_instance_config, mods[i]->mod_bvalues[0], returntext,
                CONFIG_PHASE_RUNNING, apply_mod, mods[i]->mod_op);
        }
    }

out:
    PR_Unlock(inst->inst_config_mutex);

    *returncode = rc;
    if (LDAP_SUCCESS == rc) {
        return SLAPI_DSE_CALLBACK_OK;
    } else {
        return SLAPI_DSE_CALLBACK_ERROR;
    }
}

/* This function is used to set instance config attributes. It can be used as a 
 * shortcut to doing an internal modify operation on the config DSE.
 */
void 
ldbm_instance_config_internal_set(ldbm_instance *inst, char *attrname, char *value)
{
    char err_buf[BUFSIZ];
    struct berval bval;

    bval.bv_val = value;
    bval.bv_len = strlen(value);

    if (ldbm_config_set((void *) inst, attrname, ldbm_instance_config, &bval, 
        err_buf, CONFIG_PHASE_INTERNAL, 1 /* apply */, LDAP_MOD_REPLACE) != LDAP_SUCCESS) {
        LDAPDebug(LDAP_DEBUG_ANY, 
            "Internal Error: Error setting instance config attr %s to %s: %s\n", 
            attrname, value, err_buf);
        exit(1);
    }
}



static int ldbm_instance_generate(struct ldbminfo *li, char *instance_name,
                                  Slapi_Backend **ret_be)
{
    Slapi_Backend *new_be = NULL;
    int rc = 0;

    /* Create a new instance, process config info for it,
     * and then call slapi_be_new and create a new backend here
     */
    new_be = slapi_be_new(LDBM_DATABASE_TYPE_NAME /* type */, instance_name, 
                          0 /* public */, 1 /* do log changes */);
    new_be->be_database = li->li_plugin;
    rc = ldbm_instance_create(new_be, instance_name);
    if (rc) {
        goto bail;
    }

    ldbm_instance_config_load_dse_info(new_be->be_instance_info);
    ldbm_instance_create_default_indexes(new_be);

    /* if USN plugin is enabled, set slapi_counter */
    if (plugin_enabled("USN", li->li_identity) && ldbm_back_isinitialized()) {
        /* 
         * ldbm_back is already initialized. 
         * I.e., a new instance is being added.
         * If not initialized, ldbm_usn_init is called later and
         * be usn counter is initialized there.
         */
        if (config_get_entryusn_global()) {
            /* global usn counter is already created. 
             * set it to be_usn_counter. */
            new_be->be_usn_counter = li->li_global_usn_counter;
        } else {
            new_be->be_usn_counter = slapi_counter_new();
            slapi_counter_set_value(new_be->be_usn_counter, INITIALUSN);
        }
    }

    if (ret_be != NULL) {
        *ret_be = new_be;
    }
bail:
    return rc;
}

int
ldbm_instance_postadd_instance_entry_callback(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* entryAfter, int *returncode, char *returntext, void *arg)
{
    backend *be = NULL;
    struct ldbm_instance *inst;
    char *instance_name;
    struct ldbminfo *li = (struct ldbminfo *)arg;
    int rval = 0;

    parse_ldbm_instance_entry(entryBefore, &instance_name);
    rval = ldbm_instance_generate(li, instance_name, &be);
    if (rval) {
        LDAPDebug(LDAP_DEBUG_ANY,
            "ldbm_instance_postadd_instance_entry_callback: "
            "ldbm_instance_generate (%s) failed (%d)\n",
            instance_name, rval, 0);
    }

    inst = ldbm_instance_find_by_name(li, instance_name);

    /* Add default indexes */
    ldbm_instance_create_default_user_indexes(inst);

    /* Initialize and register callbacks for VLV indexes */
    vlv_init(inst);

    /* this is an ACTUAL ADD being done while the server is running!
     * start up the appropriate backend...
     */
    rval = ldbm_instance_start(be);
    if (0 != rval)
    {
        LDAPDebug(LDAP_DEBUG_ANY,
            "ldbm_instance_postadd_instance_entry_callback: "
            "ldbm_instnace_start (%s) failed (%d)\n",
            instance_name, rval, 0);
    }

    slapi_ch_free((void **)&instance_name);

    /* instance must be fully ready before we call this */
    slapi_mtn_be_started(be);

    return SLAPI_DSE_CALLBACK_OK;
}

int
ldbm_instance_add_instance_entry_callback(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* entryAfter, int *returncode, char *returntext, void *arg) 
{
    char *instance_name;
    struct ldbm_instance *inst= NULL;
    struct ldbminfo *li= (struct ldbminfo *) arg;
    int rc = 0;

    parse_ldbm_instance_entry(entryBefore, &instance_name);

    /* Make sure we don't create two instances with the same name. */
    inst = ldbm_instance_find_by_name(li, instance_name);
    if (inst != NULL) {
        LDAPDebug(LDAP_DEBUG_ANY, "WARNING: ldbm instance %s already exists\n",
                  instance_name, 0, 0);
        if (returntext != NULL)
            PR_snprintf(returntext, SLAPI_DSE_RETURNTEXT_SIZE, "An ldbm instance with the name %s already exists\n",
                    instance_name);
        if (returncode != NULL)
            *returncode = LDAP_UNWILLING_TO_PERFORM;
        slapi_ch_free((void **)&instance_name);
        return SLAPI_DSE_CALLBACK_ERROR;
    }

    if (pb == NULL) {
        /* called during startup -- do the rest now */
        rc = ldbm_instance_generate(li, instance_name, NULL);
        if (!rc) {
            inst = ldbm_instance_find_by_name(li, instance_name);
            rc = ldbm_instance_create_default_user_indexes(inst);
        }
    }
    /* if called during a normal ADD operation, the postadd callback
     * will do the rest.
     */

    slapi_ch_free((void **)&instance_name);
    return (rc == 0) ? SLAPI_DSE_CALLBACK_OK : SLAPI_DSE_CALLBACK_ERROR;
}




/* unregister the DSE callbacks on a backend -- this needs to be done when
 * deleting a backend, so that adding the same backend later won't cause
 * these expired callbacks to be called.
 */
static void ldbm_instance_unregister_callbacks(ldbm_instance *inst)
{
    struct ldbminfo *li = inst->inst_li;
    char *dn = NULL;

    /* tear down callbacks for the instance config entry */
    dn = slapi_create_dn_string("cn=%s,cn=%s,cn=plugins,cn=config",
                                inst->inst_name, li->li_plugin->plg_name);
    if (NULL == dn) {
        LDAPDebug2Args(LDAP_DEBUG_ANY,
                       "ldbm_instance_unregister_callbacks: "
                       "failed create instance dn for plugin %s, "
                       "instance %s\n",
                       inst->inst_li->li_plugin->plg_name, inst->inst_name);
        goto bail;
    }
    slapi_config_remove_callback(SLAPI_OPERATION_SEARCH, DSE_FLAG_PREOP, dn,
        LDAP_SCOPE_BASE, "(objectclass=*)",
        ldbm_instance_search_config_entry_callback);
    slapi_config_remove_callback(SLAPI_OPERATION_MODIFY, DSE_FLAG_PREOP, dn,
        LDAP_SCOPE_BASE, "(objectclass=*)",
        ldbm_instance_modify_config_entry_callback);
    slapi_config_remove_callback(DSE_OPERATION_WRITE, DSE_FLAG_PREOP, dn,
        LDAP_SCOPE_BASE, "(objectclass=*)",
        ldbm_instance_search_config_entry_callback);
    slapi_config_remove_callback(SLAPI_OPERATION_ADD, DSE_FLAG_PREOP, dn,
        LDAP_SCOPE_BASE, "(objectclass=*)",
        ldbm_instance_deny_config);
    slapi_ch_free_string(&dn);

    /* now the cn=monitor entry */
    dn = slapi_create_dn_string("cn=monitor,cn=%s,cn=%s,cn=plugins,cn=config",
                                inst->inst_name, li->li_plugin->plg_name);
    if (NULL == dn) {
        LDAPDebug2Args(LDAP_DEBUG_ANY,
                       "ldbm_instance_unregister_callbacks: "
                       "failed create monitor instance dn for plugin %s, "
                       "instance %s\n",
                       inst->inst_li->li_plugin->plg_name, inst->inst_name);
        goto bail;
    }
    slapi_config_remove_callback(SLAPI_OPERATION_SEARCH, DSE_FLAG_PREOP, dn,
        LDAP_SCOPE_BASE, "(objectclass=*)", ldbm_back_monitor_instance_search);
    slapi_config_remove_callback(SLAPI_OPERATION_ADD, DSE_FLAG_PREOP, dn,
        LDAP_SCOPE_SUBTREE, "(objectclass=*)", ldbm_instance_deny_config);
    slapi_config_remove_callback(SLAPI_OPERATION_MODIFY, DSE_FLAG_PREOP, dn,
        LDAP_SCOPE_BASE, "(objectclass=*)", ldbm_instance_deny_config);
    slapi_ch_free_string(&dn);

    /* now the cn=index entries */
    dn = slapi_create_dn_string("cn=index,cn=%s,cn=%s,cn=plugins,cn=config",
                                inst->inst_name, li->li_plugin->plg_name);
    if (NULL == dn) {
        LDAPDebug2Args(LDAP_DEBUG_ANY,
                       "ldbm_instance_unregister_callbacks: "
                       "failed create index dn for plugin %s, "
                       "instance %s\n",
                       inst->inst_li->li_plugin->plg_name, inst->inst_name);
        goto bail;
    }
    slapi_config_remove_callback(SLAPI_OPERATION_ADD, DSE_FLAG_PREOP, dn,
        LDAP_SCOPE_SUBTREE, "(objectclass=nsIndex)",
        ldbm_instance_index_config_add_callback);
    slapi_config_remove_callback(SLAPI_OPERATION_DELETE, DSE_FLAG_PREOP, dn,
        LDAP_SCOPE_SUBTREE, "(objectclass=nsIndex)",
        ldbm_instance_index_config_delete_callback);
    slapi_config_remove_callback(SLAPI_OPERATION_MODIFY, DSE_FLAG_PREOP, dn,
        LDAP_SCOPE_SUBTREE, "(objectclass=nsIndex)",
        ldbm_instance_index_config_modify_callback);
    slapi_ch_free_string(&dn);

    /* now the cn=encrypted attributes entries */
    dn = slapi_create_dn_string("cn=encrypted attributes,cn=%s,cn=%s,cn=plugins,cn=config",
                                inst->inst_name, li->li_plugin->plg_name);
    if (NULL == dn) {
        LDAPDebug2Args(LDAP_DEBUG_ANY,
                       "ldbm_instance_unregister_callbacks: "
                       "failed create encrypted attributes dn for plugin %s, "
                       "instance %s\n",
                       inst->inst_li->li_plugin->plg_name, inst->inst_name);
        goto bail;
    }
    slapi_config_remove_callback(SLAPI_OPERATION_ADD, DSE_FLAG_PREOP, dn,
        LDAP_SCOPE_SUBTREE, ldbm_instance_attrcrypt_filter,
        ldbm_instance_attrcrypt_config_add_callback);
    slapi_config_remove_callback(SLAPI_OPERATION_DELETE, DSE_FLAG_PREOP, dn,
        LDAP_SCOPE_SUBTREE, ldbm_instance_attrcrypt_filter,
        ldbm_instance_attrcrypt_config_delete_callback);
    slapi_config_remove_callback(SLAPI_OPERATION_MODIFY, DSE_FLAG_PREOP, dn,
        LDAP_SCOPE_SUBTREE, ldbm_instance_attrcrypt_filter,
        ldbm_instance_attrcrypt_config_modify_callback);

    vlv_remove_callbacks(inst);
bail:
    slapi_ch_free_string(&dn);
}


int
ldbm_instance_post_delete_instance_entry_callback(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* entryAfter, int *returncode, char *returntext, void *arg)
{
    char *instance_name;
    struct ldbminfo *li = (struct ldbminfo *)arg;
    struct ldbm_instance *inst = NULL;

    parse_ldbm_instance_entry(entryBefore, &instance_name);
    inst = ldbm_instance_find_by_name(li, instance_name);

    if (inst == NULL) {
        LDAPDebug(LDAP_DEBUG_ANY, "ldbm: instance '%s' does not exist! (2)\n",
                  instance_name, 0, 0);
        if (returntext) {
            PR_snprintf(returntext, SLAPI_DSE_RETURNTEXT_SIZE, "No ldbm instance exists with the name '%s' (2)\n",
                    instance_name);
        }
        if (returncode) {
            *returncode = LDAP_UNWILLING_TO_PERFORM;
        }
        slapi_ch_free((void **)&instance_name);
        return SLAPI_DSE_CALLBACK_ERROR;
    }

    LDAPDebug(LDAP_DEBUG_ANY, "ldbm: removing '%s'.\n", instance_name, 0, 0);

    cache_destroy_please(&inst->inst_cache, CACHE_TYPE_ENTRY);
    if (entryrdn_get_switch()) { /* subtree-rename: on */
        cache_destroy_please(&inst->inst_dncache, CACHE_TYPE_DN);
    }
    {
        struct ldbminfo *li = (struct ldbminfo *) inst->inst_be->be_database->plg_private;
        dblayer_private *priv = (dblayer_private*) li->li_dblayer_private;
        struct dblayer_private_env *pEnv = priv->dblayer_env; 
        if(pEnv) {
            PRDir *dirhandle = NULL;
            char inst_dir[MAXPATHLEN*2];
            char *inst_dirp = NULL;

            if (inst->inst_dir_name == NULL){
                dblayer_get_instance_data_dir(inst->inst_be);
            }    
            inst_dirp = dblayer_get_full_inst_dir(li, inst, 
                                                  inst_dir, MAXPATHLEN*2);
            if (NULL != inst_dirp) {
                dirhandle = PR_OpenDir(inst_dirp);
                /* the db dir instance may have been removed already */
                if (dirhandle) {
                    PRDirEntry *direntry = NULL;
                    char *dbp = NULL;
                    char *p = NULL;
                    while (NULL != (direntry = PR_ReadDir(dirhandle,
                                               PR_SKIP_DOT|PR_SKIP_DOT_DOT))) {
                        int rc;
                        if (!direntry->name)
                            break;

                        dbp = PR_smprintf("%s/%s", inst_dirp, direntry->name);
                        if (NULL == dbp) {
                            LDAPDebug (LDAP_DEBUG_ANY,
                            "ldbm_instance_post_delete_instance_entry_callback:"
                            " failed to generate db path: %s/%s\n",
                            inst_dirp, direntry->name, 0);
                            break;
                        }

                        p = strstr(dbp, LDBM_FILENAME_SUFFIX);
                        if (NULL != p &&
                            strlen(p) == strlen(LDBM_FILENAME_SUFFIX)) {
                            rc = dblayer_db_remove(pEnv, dbp, 0);
                        } else {
                            rc = PR_Delete(dbp);
                        }
                        PR_ASSERT(rc == 0);
                        PR_smprintf_free(dbp);
                    }
                    PR_CloseDir(dirhandle);                
                }
                /* 
                 * When a backend was removed, the db instance directory 
                 * was removed as well (See also bz463774).
                 * In case DB_RECOVER_FATAL is set in the DB open after 
                 * the removal (e.g., in restore), the logs in the transaction
                 * logs are replayed and compared with the contents of the DB 
                 * files.  At that time, if the db instance directory does not 
                 * exist, libdb returns FATAL error.  To prevent the problem, 
                 * we have to leave the empty directory. (bz597375)
                 *
                 * PR_RmDir(inst_dirp);
                 */
            } /* non-null dirhandle */
            if (inst_dirp != inst_dir) {
                slapi_ch_free_string(&inst_dirp);
            }
        } /* non-null pEnv */
    }

    ldbm_instance_unregister_callbacks(inst);    
    slapi_be_free(&inst->inst_be);
    ldbm_instance_destroy(inst);
    slapi_ch_free((void **)&instance_name);

    return SLAPI_DSE_CALLBACK_OK;
}

int
ldbm_instance_delete_instance_entry_callback(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* entryAfter, int *returncode, char *returntext, void *arg)
{
    char *instance_name;
    struct ldbminfo *li = (struct ldbminfo *)arg;
    struct ldbm_instance *inst = NULL;

    parse_ldbm_instance_entry(entryBefore, &instance_name);
    inst = ldbm_instance_find_by_name(li, instance_name);
    if (inst == NULL) {
        LDAPDebug(LDAP_DEBUG_ANY, "ldbm: instance '%s' does not exist!\n",
                  instance_name, 0, 0);
        if (returntext) {
            PR_snprintf(returntext, SLAPI_DSE_RETURNTEXT_SIZE, "No ldbm instance exists with the name '%s'\n",
                    instance_name);
        }
        if (returncode) {
            *returncode = LDAP_UNWILLING_TO_PERFORM;
        }
        slapi_ch_free((void **)&instance_name);
        return SLAPI_DSE_CALLBACK_ERROR;
    }

    /* check if some online task is happening */
    if ((instance_set_busy(inst) != 0) ||
        (slapi_counter_get_value(inst->inst_ref_count) > 0)) {
        LDAPDebug(LDAP_DEBUG_ANY, "ldbm: '%s' is in the middle of a task. "
                  "Cancel the task or wait for it to finish, "
                  "then try again.\n", instance_name, 0, 0);
        if (returntext) {
            PR_snprintf(returntext, SLAPI_DSE_RETURNTEXT_SIZE, "ldbm instance '%s' is in the middle of a "
                    "task. Cancel the task or wait for it to finish, "
                    "then try again.\n", instance_name);
        }
        if (returncode) {
            *returncode = LDAP_UNWILLING_TO_PERFORM;
        }
        slapi_ch_free((void **)&instance_name);
        return SLAPI_DSE_CALLBACK_ERROR;
    }

    /* okay, we're gonna delete this database instance.  take it offline. */
    LDAPDebug(LDAP_DEBUG_ANY, "ldbm: Bringing %s offline...\n",
              instance_name, 0, 0);
    slapi_mtn_be_stopping(inst->inst_be);
    dblayer_instance_close(inst->inst_be);
    slapi_ch_free((void **)&instance_name);

    return SLAPI_DSE_CALLBACK_OK;
}

