summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNathan Kinder <nkinder@redhat.com>2008-09-24 21:21:52 +0000
committerNathan Kinder <nkinder@redhat.com>2008-09-24 21:21:52 +0000
commit7ebdd01f304ad5e6607e3dec98b5a3fd381fb7d8 (patch)
treebbe178c6bb6a43da48401aeba4ea6c4395d5233d
parentfb3e6ac2f5d60d6184388c482cc4117b50955c99 (diff)
downloadds-7ebdd01f304ad5e6607e3dec98b5a3fd381fb7d8.tar.gz
ds-7ebdd01f304ad5e6607e3dec98b5a3fd381fb7d8.tar.xz
ds-7ebdd01f304ad5e6607e3dec98b5a3fd381fb7d8.zip
Resolves: 462920
Summary: Make DNA plug-in auto-extended exhausted ranges.
-rw-r--r--ldap/ldif/template-dnaplugin.ldif.in1
-rw-r--r--ldap/servers/plugins/dna/dna.c1825
-rw-r--r--ldap/servers/slapd/entry.c30
-rw-r--r--ldap/servers/slapd/slapi-plugin.h4
-rw-r--r--ldap/servers/slapd/value.c32
5 files changed, 1773 insertions, 119 deletions
diff --git a/ldap/ldif/template-dnaplugin.ldif.in b/ldap/ldif/template-dnaplugin.ldif.in
index 391afe64..aa0008a6 100644
--- a/ldap/ldif/template-dnaplugin.ldif.in
+++ b/ldap/ldif/template-dnaplugin.ldif.in
@@ -8,4 +8,5 @@ nsslapd-plugininitfunc: dna_init
nsslapd-plugintype: preoperation
nsslapd-pluginenabled: off
nsslapd-pluginPath: libdna-plugin
+nsslapd-plugin-depends-on-type: database
diff --git a/ldap/servers/plugins/dna/dna.c b/ldap/servers/plugins/dna/dna.c
index cf8a5c27..a106110e 100644
--- a/ldap/servers/plugins/dna/dna.c
+++ b/ldap/servers/plugins/dna/dna.c
@@ -68,6 +68,9 @@
#define DNA_SUCCESS 0
#define DNA_FAILURE -1
+/* Default range request timeout */
+#define DNA_DEFAULT_TIMEOUT 10
+
/**
* DNA config types
*/
@@ -84,18 +87,43 @@
#define DNA_SHARED_CFG_DN "dnaSharedCfgDN"
/* Shared Config */
-#define DNA_GLOBAL_RANGE "dnaGlobalRange"
-#define DNA_RANGE "dnaRange"
-#define DNA_MAX_RANGE_SIZE "dnaMaxRangeSize"
-#define DNA_CHUNK_SIZE "dnaChunkSize"
-
-#define FEATURE_DESC "Distributed Numeric Assignment"
-#define PLUGIN_DESC "Distributed Numeric Assignment plugin"
-
-static Slapi_PluginDesc pdesc = { FEATURE_DESC,
+#define DNA_REMAINING "dnaRemainingValues"
+#define DNA_THRESHOLD "dnaThreshold"
+#define DNA_HOSTNAME "dnaHostname"
+#define DNA_PORTNUM "dnaPortNum"
+#define DNA_SECURE_PORTNUM "dnaSecurePortNum"
+
+/* For transferred ranges */
+#define DNA_NEXT_RANGE "dnaNextRange"
+#define DNA_RANGE_REQUEST_TIMEOUT "dnaRangeRequestTimeout"
+
+/* Replication types */
+#define DNA_REPL_BIND_DN "nsds5ReplicaBindDN"
+#define DNA_REPL_CREDS "nsds5ReplicaCredentials"
+#define DNA_REPL_BIND_METHOD "nsds5ReplicaBindMethod"
+#define DNA_REPL_TRANSPORT "nsds5ReplicaTransportInfo"
+
+#define DNA_FEATURE_DESC "Distributed Numeric Assignment"
+#define DNA_EXOP_FEATURE_DESC "DNA Range Extension Request"
+#define DNA_PLUGIN_DESC "Distributed Numeric Assignment plugin"
+#define DNA_INT_PREOP_DESC "Distributed Numeric Assignment internal preop plugin"
+#define DNA_POSTOP_DESC "Distributed Numeric Assignment postop plugin"
+#define DNA_EXOP_DESC "Distributed Numeric Assignment range extension extop plugin"
+
+#define INTEGER_SYNTAX_OID "1.3.6.1.4.1.1466.115.121.1.27"
+
+#define DNA_EXTEND_EXOP_REQUEST_OID "2.16.840.1.113730.3.5.10"
+#define DNA_EXTEND_EXOP_RESPONSE_OID "2.16.840.1.113730.3.5.11"
+
+static Slapi_PluginDesc pdesc = { DNA_FEATURE_DESC,
PLUGIN_MAGIC_VENDOR_STR,
PRODUCTTEXT,
- PLUGIN_DESC };
+ DNA_PLUGIN_DESC };
+
+static Slapi_PluginDesc exop_pdesc = { DNA_EXOP_FEATURE_DESC,
+ PLUGIN_MAGIC_VENDOR_STR,
+ PRODUCTTEXT,
+ DNA_EXOP_DESC };
/**
@@ -107,14 +135,31 @@ struct configEntry {
char *dn;
char *type;
char *prefix;
- PRUint64 nextval;
- PRUint64 interval;
- PRUint64 maxval;
char *filter;
Slapi_Filter *slapi_filter;
char *generate;
char *scope;
- Slapi_Mutex *new_value_lock;
+ PRUint64 interval;
+ PRUint64 threshold;
+ char *shared_cfg_base;
+ char *shared_cfg_dn;
+ PRUint64 timeout;
+ /* This lock protects the 5 members below. All
+ * of the above members are safe to read as long
+ * as you call dna_read_lock() first. */
+ Slapi_Mutex *lock;
+ PRUint64 nextval;
+ PRUint64 maxval;
+ PRUint64 remaining;
+ PRUint64 next_range_lower;
+ PRUint64 next_range_upper;
+ /* This lock protects the extend_in_progress
+ * member. This is used to prevent us from
+ * processing a range extention request and
+ * trying to extend out own range at the same
+ * time. */
+ Slapi_Mutex *extend_lock;
+ int extend_in_progress;
};
static PRCList *dna_global_config = NULL;
@@ -123,6 +168,29 @@ static PRRWLock *g_dna_cache_lock;
static void *_PluginID = NULL;
static char *_PluginDN = NULL;
+static int g_plugin_started = 0;
+
+static char *hostname = NULL;
+static char *portnum = NULL;
+static char *secureportnum = NULL;
+
+
+/**
+ * server struct for shared ranges
+ */
+struct dnaServer {
+ PRCList list;
+ char *host;
+ unsigned int port;
+ unsigned int secureport;
+ PRUint64 remaining;
+};
+
+static char *dna_extend_exop_oid_list[] = {
+ DNA_EXTEND_EXOP_REQUEST_OID,
+ NULL
+};
+
/**
*
@@ -132,17 +200,20 @@ static char *_PluginDN = NULL;
int dna_init(Slapi_PBlock * pb);
static int dna_start(Slapi_PBlock * pb);
static int dna_close(Slapi_PBlock * pb);
+static int dna_internal_preop_init(Slapi_PBlock *pb);
static int dna_postop_init(Slapi_PBlock * pb);
+static int dna_exop_init(Slapi_PBlock * pb);
/**
*
* Local operation functions
*
*/
-static int loadPluginConfig();
-static int parseConfigEntry(Slapi_Entry * e);
-static void deleteConfig();
-static void freeConfigEntry(struct configEntry ** entry);
+static int dna_load_plugin_config();
+static int dna_parse_config_entry(Slapi_Entry * e);
+static void dna_delete_config();
+static void dna_free_config_entry(struct configEntry ** entry);
+static int dna_load_host_port();
/**
*
@@ -153,6 +224,28 @@ static char *dna_get_dn(Slapi_PBlock * pb);
static int dna_dn_is_config(char *dn);
static int dna_get_next_value(struct configEntry * config_entry,
char **next_value_ret);
+static int dna_first_free_value(struct configEntry *config_entry,
+ PRUint64 *newval);
+static int dna_fix_maxval(struct configEntry *config_entry);
+static void dna_notice_allocation(struct configEntry *config_entry,
+ PRUint64 new, PRUint64 last, int fix);
+static int dna_update_shared_config(struct configEntry * config_entry);
+static void dna_update_config_event(time_t event_time, void *arg);
+static int dna_get_shared_servers(struct configEntry *config_entry, PRCList **servers);
+static void dna_free_shared_server(struct dnaServer **server);
+static void dna_delete_shared_servers(PRCList **servers);
+static int dna_release_range(char *range_dn, PRUint64 *lower, PRUint64 *upper);
+static int dna_request_range(struct configEntry *config_entry,
+ struct dnaServer *server,
+ PRUint64 *lower, PRUint64 *upper);
+static struct berval *dna_create_range_request(char *range_dn);
+static int dna_update_next_range(struct configEntry *config_entry,
+ PRUint64 lower, PRUint64 upper);
+static int dna_activate_next_range(struct configEntry *config_entry);
+static int dna_is_replica_bind_dn(char *range_dn, char *bind_dn);
+static int dna_get_replica_bind_creds(char *range_dn, struct dnaServer *server,
+ char **bind_dn, char **bind_passwd,
+ char **bind_method, int *is_ssl);
/**
*
@@ -163,12 +256,13 @@ static int dna_config_check_post_op(Slapi_PBlock * pb);
static int dna_pre_op(Slapi_PBlock * pb, int modtype);
static int dna_mod_pre_op(Slapi_PBlock * pb);
static int dna_add_pre_op(Slapi_PBlock * pb);
+static int dna_extend_exop(Slapi_PBlock *pb);
/**
* debug functions - global, for the debugger
*/
-void dnaDumpConfig();
-void dnaDumpConfigEntry(struct configEntry *);
+void dna_dump_config();
+void dna_dump_config_entry(struct configEntry *);
/**
* set the debug level
@@ -240,7 +334,8 @@ char *getPluginDN()
-------------
adds our callbacks to the list
*/
-int dna_init(Slapi_PBlock * pb)
+int
+dna_init(Slapi_PBlock *pb)
{
int status = DNA_SUCCESS;
char *plugin_identity = NULL;
@@ -269,12 +364,30 @@ int dna_init(Slapi_PBlock * pb)
(void *) dna_mod_pre_op) != 0 ||
slapi_pblock_set(pb, SLAPI_PLUGIN_PRE_ADD_FN,
(void *) dna_add_pre_op) != 0 ||
+ /* internal preoperation */
+ slapi_register_plugin("internalpreoperation", /* op type */
+ 1, /* Enabled */
+ "dna_init", /* this function desc */
+ dna_internal_preop_init, /* init func */
+ DNA_INT_PREOP_DESC, /* plugin desc */
+ NULL, /* ? */
+ plugin_identity /* access control */
+ ) ||
/* the config change checking post op */
slapi_register_plugin("postoperation", /* op type */
1, /* Enabled */
"dna_init", /* this function desc */
dna_postop_init, /* init func for post op */
- PLUGIN_DESC, /* plugin desc */
+ DNA_POSTOP_DESC, /* plugin desc */
+ NULL, /* ? */
+ plugin_identity /* access control */
+ ) ||
+ /* the range extension extended operation */
+ slapi_register_plugin("extendedop", /* op type */
+ 1, /* Enabled */
+ "dna_init", /* this function desc */
+ dna_exop_init, /* init func for exop */
+ DNA_EXOP_DESC, /* plugin desc */
NULL, /* ? */
plugin_identity /* access control */
)
@@ -289,8 +402,27 @@ int dna_init(Slapi_PBlock * pb)
return status;
}
+static int
+dna_internal_preop_init(Slapi_PBlock *pb)
+{
+ int status = DNA_SUCCESS;
-static int dna_postop_init(Slapi_PBlock * pb)
+ if (slapi_pblock_set(pb, SLAPI_PLUGIN_VERSION,
+ SLAPI_PLUGIN_VERSION_01) != 0 ||
+ slapi_pblock_set(pb, SLAPI_PLUGIN_DESCRIPTION,
+ (void *) &pdesc) != 0 ||
+ slapi_pblock_set(pb, SLAPI_PLUGIN_INTERNAL_PRE_MODIFY_FN,
+ (void *) dna_mod_pre_op) != 0 ||
+ slapi_pblock_set(pb, SLAPI_PLUGIN_INTERNAL_PRE_ADD_FN,
+ (void *) dna_add_pre_op) != 0) {
+ status = DNA_FAILURE;
+ }
+
+ return status;
+}
+
+static int
+dna_postop_init(Slapi_PBlock *pb)
{
int status = DNA_SUCCESS;
@@ -314,19 +446,48 @@ static int dna_postop_init(Slapi_PBlock * pb)
return status;
}
+
+static int
+dna_exop_init(Slapi_PBlock * pb)
+{
+ int status = DNA_SUCCESS;
+
+ if (slapi_pblock_set(pb, SLAPI_PLUGIN_VERSION,
+ SLAPI_PLUGIN_VERSION_01) != 0 ||
+ slapi_pblock_set(pb, SLAPI_PLUGIN_DESCRIPTION,
+ (void *) &exop_pdesc) != 0 ||
+ slapi_pblock_set(pb, SLAPI_PLUGIN_EXT_OP_OIDLIST,
+ (void *) dna_extend_exop_oid_list) != 0 ||
+ slapi_pblock_set(pb, SLAPI_PLUGIN_EXT_OP_FN,
+ (void *) dna_extend_exop) != 0) {
+ slapi_log_error(SLAPI_LOG_FATAL, DNA_PLUGIN_SUBSYSTEM,
+ "dna_exop_init: failed to register plugin\n");
+ status = DNA_FAILURE;
+ }
+
+ return status;
+}
+
+
/*
dna_start
--------------
Kicks off the config cache.
It is called after dna_init.
*/
-static int dna_start(Slapi_PBlock * pb)
+static int
+dna_start(Slapi_PBlock * pb)
{
char *plugindn = NULL;
slapi_log_error(SLAPI_LOG_TRACE, DNA_PLUGIN_SUBSYSTEM,
"--> dna_start\n");
+ /* Check if we're already started */
+ if (g_plugin_started) {
+ goto done;
+ }
+
g_dna_cache_lock = PR_NewRWLock(PR_RWLOCK_RANK_NONE, "dna");
if (!g_dna_cache_lock) {
@@ -354,24 +515,34 @@ static int dna_start(Slapi_PBlock * pb)
setPluginDN(plugindn);
- /**
- * Load the config for our plug-in
- */
+ /* We need the host and port number of this server
+ * in case shared config is enabled for any of the
+ * ranges we are managing. */
+ if (dna_load_host_port() != DNA_SUCCESS) {
+ slapi_log_error(SLAPI_LOG_FATAL, DNA_PLUGIN_SUBSYSTEM,
+ "dna_start: unable to load host and port information\n");
+ }
+
+ /*
+ * Load the config for our plug-in
+ */
dna_global_config = (PRCList *)
slapi_ch_calloc(1, sizeof(struct configEntry));
PR_INIT_CLIST(dna_global_config);
- if (loadPluginConfig() != DNA_SUCCESS) {
+ if (dna_load_plugin_config() != DNA_SUCCESS) {
slapi_log_error(SLAPI_LOG_FATAL, DNA_PLUGIN_SUBSYSTEM,
"dna_start: unable to load plug-in configuration\n");
return DNA_FAILURE;
}
+ g_plugin_started = 1;
slapi_log_error(SLAPI_LOG_PLUGIN, DNA_PLUGIN_SUBSYSTEM,
"dna: ready for service\n");
slapi_log_error(SLAPI_LOG_TRACE, DNA_PLUGIN_SUBSYSTEM,
"<-- dna_start\n");
+done:
return DNA_SUCCESS;
}
@@ -380,15 +551,20 @@ static int dna_start(Slapi_PBlock * pb)
--------------
closes down the cache
*/
-static int dna_close(Slapi_PBlock * pb)
+static int
+dna_close(Slapi_PBlock * pb)
{
slapi_log_error(SLAPI_LOG_TRACE, DNA_PLUGIN_SUBSYSTEM,
"--> dna_close\n");
- deleteConfig();
+ dna_delete_config();
slapi_ch_free((void **)&dna_global_config);
+ slapi_ch_free_string(&hostname);
+ slapi_ch_free_string(&portnum);
+ slapi_ch_free_string(&secureportnum);
+
slapi_log_error(SLAPI_LOG_TRACE, DNA_PLUGIN_SUBSYSTEM,
"<-- dna_close\n");
@@ -405,7 +581,8 @@ static int dna_close(Slapi_PBlock * pb)
* --- cn=etc
* ------ cn=etc etc
*/
-static int loadPluginConfig()
+static int
+dna_load_plugin_config()
{
int status = DNA_SUCCESS;
int result;
@@ -414,10 +591,10 @@ static int loadPluginConfig()
Slapi_Entry **entries = NULL;
slapi_log_error(SLAPI_LOG_TRACE, DNA_PLUGIN_SUBSYSTEM,
- "--> loadPluginConfig\n");
+ "--> dna_load_plugin_config\n");
dna_write_lock();
- deleteConfig();
+ dna_delete_config();
search_pb = slapi_pblock_new();
@@ -440,7 +617,7 @@ static int loadPluginConfig()
}
for (i = 0; (entries[i] != NULL); i++) {
- status = parseConfigEntry(entries[i]);
+ status = dna_parse_config_entry(entries[i]);
if (DNA_SUCCESS != status)
break;
}
@@ -450,12 +627,13 @@ static int loadPluginConfig()
slapi_pblock_destroy(search_pb);
dna_unlock();
slapi_log_error(SLAPI_LOG_TRACE, DNA_PLUGIN_SUBSYSTEM,
- "<-- loadPluginConfig\n");
+ "<-- dna_load_plugin_config\n");
return status;
}
-static int parseConfigEntry(Slapi_Entry * e)
+static int
+dna_parse_config_entry(Slapi_Entry * e)
{
char *value;
struct configEntry *entry;
@@ -464,7 +642,7 @@ static int parseConfigEntry(Slapi_Entry * e)
int entry_added = 0;
slapi_log_error(SLAPI_LOG_TRACE, DNA_PLUGIN_SUBSYSTEM,
- "--> parseConfigEntry\n");
+ "--> dna_parse_config_entry\n");
entry = (struct configEntry *)
slapi_ch_calloc(1, sizeof(struct configEntry));
@@ -486,41 +664,47 @@ static int parseConfigEntry(Slapi_Entry * e)
goto bail;
slapi_log_error(SLAPI_LOG_CONFIG, DNA_PLUGIN_SUBSYSTEM,
- "----------> dnaType [%s]\n", entry->type, 0, 0);
-
- /* FIXME: check the attribute type, it must suport matching rules and be
- * indexed, these are requirements and failure to meet them should result in
- * the configuration to be disarded and an ERROR logged prominently */
+ "----------> %s [%s]\n", DNA_TYPE, entry->type, 0, 0);
value = slapi_entry_attr_get_charptr(e, DNA_NEXTVAL);
if (value) {
- entry->nextval = strtoul(value, 0, 0);
+ entry->nextval = strtoull(value, 0, 0);
slapi_ch_free_string(&value);
} else
goto bail;
slapi_log_error(SLAPI_LOG_CONFIG, DNA_PLUGIN_SUBSYSTEM,
- "----------> dnaNextValue [%d]\n", entry->nextval, 0,
+ "----------> %s [%llu]\n", DNA_NEXTVAL, entry->nextval, 0,
0);
value = slapi_entry_attr_get_charptr(e, DNA_PREFIX);
if (value && value[0]) {
entry->prefix = value;
+ } else {
+ /* TODO - If a prefix is not defined, then we need to ensure
+ * that the proper matching rule is in place for this
+ * attribute type. We require this since we internally
+ * perform a sorted range search on what we assume to
+ * be an INTEGER syntax. */
}
slapi_log_error(SLAPI_LOG_CONFIG, DNA_PLUGIN_SUBSYSTEM,
- "----------> dnaPrefix [%s]\n", entry->prefix, 0, 0);
+ "----------> %s [%s]\n", DNA_PREFIX, entry->prefix, 0, 0);
+ /* Set the default interval to 1 */
+ entry->interval = 1;
+
+#ifdef DNA_ENABLE_INTERVAL
value = slapi_entry_attr_get_charptr(e, DNA_INTERVAL);
if (value) {
- entry->interval = strtoul(value, 0, 0);
+ entry->interval = strtoull(value, 0, 0);
+ slapi_ch_free_string(&value);
} else
goto bail;
slapi_log_error(SLAPI_LOG_CONFIG, DNA_PLUGIN_SUBSYSTEM,
- "----------> dnaInterval [%s]\n", value, 0, 0);
-
- slapi_ch_free_string(&value);
+ "----------> %s [%llu]\n", DNA_INTERVAL, entry->interval, 0, 0);
+#endif
value = slapi_entry_attr_get_charptr(e, DNA_GENERATE);
if (value) {
@@ -528,7 +712,7 @@ static int parseConfigEntry(Slapi_Entry * e)
}
slapi_log_error(SLAPI_LOG_CONFIG, DNA_PLUGIN_SUBSYSTEM,
- "----------> dnaMagicRegen [%s]\n", entry->generate,
+ "----------> %s [%s]\n", DNA_GENERATE, entry->generate,
0, 0);
value = slapi_entry_attr_get_charptr(e, DNA_FILTER);
@@ -545,7 +729,7 @@ static int parseConfigEntry(Slapi_Entry * e)
}
slapi_log_error(SLAPI_LOG_CONFIG, DNA_PLUGIN_SUBSYSTEM,
- "----------> dnaFilter [%s]\n", value, 0, 0);
+ "----------> %s [%s]\n", DNA_FILTER, value, 0, 0);
value = slapi_entry_attr_get_charptr(e, DNA_SCOPE);
if (value) {
@@ -553,25 +737,125 @@ static int parseConfigEntry(Slapi_Entry * e)
}
slapi_log_error(SLAPI_LOG_CONFIG, DNA_PLUGIN_SUBSYSTEM,
- "----------> dnaScope [%s]\n", entry->scope, 0, 0);
+ "----------> %s [%s]\n", DNA_SCOPE, entry->scope, 0, 0);
/* optional, if not specified set -1 which is converted to the max unisgnee
* value */
value = slapi_entry_attr_get_charptr(e, DNA_MAXVAL);
if (value) {
- entry->maxval = strtoul(value, 0, 0);
+ entry->maxval = strtoull(value, 0, 0);
slapi_log_error(SLAPI_LOG_CONFIG, DNA_PLUGIN_SUBSYSTEM,
- "----------> dnaMaxValue [%ld]\n", value, 0, 0);
+ "----------> %s [%llu]\n", DNA_MAXVAL, value, 0, 0);
slapi_ch_free_string(&value);
} else {
entry->maxval = -1;
}
+ value = slapi_entry_attr_get_charptr(e, DNA_SHARED_CFG_DN);
+ if (value) {
+ Slapi_Entry *shared_e = NULL;
+ Slapi_DN *sdn = NULL;
+
+ sdn = slapi_sdn_new_dn_byref(value);
+
+ if (sdn) {
+ slapi_search_internal_get_entry(sdn, NULL, &shared_e, getPluginID());
+ slapi_sdn_free(&sdn);
+ }
+
+ /* Make sure that the shared config entry exists. */
+ if (!shared_e) {
+ /* We didn't locate the shared config container entry. Log
+ * a message and skip this config entry. */
+ slapi_log_error(SLAPI_LOG_FATAL, DNA_PLUGIN_SUBSYSTEM,
+ "dna_parse_config_entry: Unable to locate "
+ "shared configuration entry (%s)\n", value);
+ goto bail;
+ } else {
+ slapi_entry_free(shared_e);
+ shared_e = NULL;
+ }
+
+ entry->shared_cfg_base = slapi_dn_normalize(value);
+
+ /* We prepend the host & port of this instance as a
+ * multi-part RDN for the shared config entry. */
+ entry->shared_cfg_dn = slapi_ch_smprintf("%s=%s+%s=%s,%s", DNA_HOSTNAME,
+ hostname, DNA_PORTNUM, portnum, value);
+ slapi_dn_normalize(entry->shared_cfg_dn);
+
+ slapi_log_error(SLAPI_LOG_CONFIG, DNA_PLUGIN_SUBSYSTEM,
+ "----------> %s [%s]\n", DNA_SHARED_CFG_DN,
+ entry->shared_cfg_base, 0, 0);
+ }
+
+ value = slapi_entry_attr_get_charptr(e, DNA_THRESHOLD);
+ if (value) {
+ entry->threshold = strtoull(value, 0, 0);
+
+ slapi_log_error(SLAPI_LOG_CONFIG, DNA_PLUGIN_SUBSYSTEM,
+ "----------> %s [%llu]\n", DNA_THRESHOLD, value, 0, 0);
+
+ slapi_ch_free_string(&value);
+ } else {
+ entry->threshold = 1;
+ }
+
+ value = slapi_entry_attr_get_charptr(e, DNA_RANGE_REQUEST_TIMEOUT);
+ if (value) {
+ entry->timeout = strtoull(value, 0, 0);
+
+ slapi_log_error(SLAPI_LOG_CONFIG, DNA_PLUGIN_SUBSYSTEM,
+ "----------> %s [%llu]\n", DNA_RANGE_REQUEST_TIMEOUT,
+ value, 0, 0);
+
+ slapi_ch_free_string(&value);
+ } else {
+ entry->timeout = DNA_DEFAULT_TIMEOUT;
+ }
+
+ value = slapi_entry_attr_get_charptr(e, DNA_NEXT_RANGE);
+ if (value) {
+ char *p = NULL;
+
+ /* the next range value is in the form "<lower>-<upper>" */
+ if ((p = strstr(value, "-")) != NULL) {
+ *p = '\0';
+ ++p;
+ entry->next_range_lower = strtoull(value, 0, 0);
+ entry->next_range_upper = strtoull(p, 0, 0);
+
+ /* validate that upper is greater than lower */
+ if (entry->next_range_upper <= entry->next_range_lower) {
+ slapi_log_error(SLAPI_LOG_FATAL, DNA_PLUGIN_SUBSYSTEM,
+ "dna_parse_config_entry: Illegal %s "
+ "setting specified for range %s.\n",
+ DNA_NEXT_RANGE, entry->dn);
+ entry->next_range_lower = 0;
+ entry->next_range_upper = 0;
+ }
+ }
+
+ slapi_ch_free_string(&value);
+ }
+
+ /* Calculate number of remaining values. */
+ if (entry->next_range_lower != 0) {
+ entry->remaining = ((entry->next_range_upper - entry->next_range_lower + 1) /
+ entry->interval) + ((entry->maxval - entry->nextval + 1) /
+ entry->interval);
+ } else if (entry->nextval >= entry->maxval) {
+ entry->remaining = 0;
+ } else {
+ entry->remaining = ((entry->maxval - entry->nextval + 1) /
+ entry->interval);
+ }
+
/* create the new value lock for this range */
- entry->new_value_lock = slapi_new_mutex();
- if (!entry->new_value_lock) {
+ entry->lock = slapi_new_mutex();
+ if (!entry->lock) {
goto bail;
}
@@ -630,16 +914,27 @@ static int parseConfigEntry(Slapi_Entry * e)
if (0 == entry_added) {
slapi_log_error(SLAPI_LOG_CONFIG, DNA_PLUGIN_SUBSYSTEM,
"config entry [%s] skipped\n", entry->dn, 0, 0);
- freeConfigEntry(&entry);
+ dna_free_config_entry(&entry);
+ } else {
+ time_t now;
+
+ time(&now);
+
+ /* Setup an event to update the shared config 30
+ * seconds from now. We need to do this since
+ * performing the operation now would cause the
+ * change to not get changelogged. */
+ slapi_eq_once(dna_update_config_event, entry, now + 30);
}
slapi_log_error(SLAPI_LOG_TRACE, DNA_PLUGIN_SUBSYSTEM,
- "<-- parseConfigEntry\n");
+ "<-- dna_parse_config_entry\n");
return DNA_SUCCESS;
}
-static void freeConfigEntry(struct configEntry ** entry)
+static void
+dna_free_config_entry(struct configEntry ** entry)
{
struct configEntry *e = *entry;
@@ -667,60 +962,597 @@ static void freeConfigEntry(struct configEntry ** entry)
if (e->scope)
slapi_ch_free_string(&e->scope);
- if (e->new_value_lock)
- slapi_destroy_mutex(e->new_value_lock);
+ if (e->shared_cfg_base)
+ slapi_ch_free_string(&e->shared_cfg_base);
+
+ if (e->shared_cfg_dn)
+ slapi_ch_free_string(&e->shared_cfg_dn);
+
+ if (e->lock)
+ slapi_destroy_mutex(e->lock);
slapi_ch_free((void **) entry);
}
-static void deleteConfigEntry(PRCList * entry)
+static void
+dna_delete_configEntry(PRCList *entry)
{
PR_REMOVE_LINK(entry);
- freeConfigEntry((struct configEntry **) & entry);
+ dna_free_config_entry((struct configEntry **) &entry);
}
-static void deleteConfig()
+static void
+dna_delete_config()
{
PRCList *list;
while (!PR_CLIST_IS_EMPTY(dna_global_config)) {
list = PR_LIST_HEAD(dna_global_config);
- deleteConfigEntry(list);
+ dna_delete_configEntry(list);
+ }
+
+ return;
+}
+
+static void
+dna_free_shared_server(struct dnaServer **server)
+{
+ struct dnaServer *s = *server;
+
+ slapi_ch_free_string(&s->host);
+
+ slapi_ch_free((void **)server);
+}
+
+static void
+dna_delete_shared_servers(PRCList **servers)
+{
+ PRCList *server;
+
+ while (!PR_CLIST_IS_EMPTY(*servers)) {
+ server = PR_LIST_HEAD(*servers);
+ PR_REMOVE_LINK(server);
+ dna_free_shared_server((struct dnaServer **)&server);
}
+ slapi_ch_free((void **)servers);
+ *servers = NULL;
+
return;
}
+static int
+dna_load_host_port()
+{
+ int status = DNA_SUCCESS;
+ Slapi_Entry *e = NULL;
+ Slapi_DN *config_dn = NULL;
+ char *attrs[4];
+
+ slapi_log_error(SLAPI_LOG_TRACE, DNA_PLUGIN_SUBSYSTEM,
+ "--> dna_load_host_port\n");
+
+ attrs[0] = "nsslapd-localhost";
+ attrs[1] = "nsslapd-port";
+ attrs[2] = "nsslapd-secureport";
+ attrs[3] = NULL;
+
+ config_dn = slapi_sdn_new_dn_byref("cn=config");
+ if (config_dn) {
+ slapi_search_internal_get_entry(config_dn, attrs, &e, getPluginID());
+ slapi_sdn_free(&config_dn);
+ }
+
+ if (e) {
+ hostname = slapi_entry_attr_get_charptr(e, "nsslapd-localhost");
+ portnum = slapi_entry_attr_get_charptr(e, "nsslapd-port");
+ secureportnum = slapi_entry_attr_get_charptr(e, "nsslapd-secureport");
+ slapi_entry_free(e);
+ }
+
+ if (!hostname || !portnum) {
+ status = DNA_FAILURE;
+ }
+
+ slapi_log_error(SLAPI_LOG_TRACE, DNA_PLUGIN_SUBSYSTEM,
+ "<-- dna_load_host_port\n");
+
+ return status;
+}
+
+/*
+ * dna_update_config_event()
+ *
+ * Event queue callback that we use to do the initial
+ * update of the shared config entry shortly after
+ * startup.
+ */
+static void
+dna_update_config_event(time_t event_time, void *arg)
+{
+ Slapi_PBlock *pb = NULL;
+ struct configEntry *config_entry = arg;
+
+ if ((pb = slapi_pblock_new()) == NULL)
+ goto bail;
+
+ /* Get read lock to prevent config changes */
+ dna_read_lock();
+
+ /* First delete the existing shared config entry. This
+ * will allow the entry to be updated for things like
+ * port number changes, etc. */
+ slapi_delete_internal_set_pb(pb, config_entry->shared_cfg_dn,
+ NULL, NULL, getPluginID(), 0);
+
+ /* We don't care about the results */
+ slapi_delete_internal_pb(pb);
+
+ /* Now force the entry to be recreated */
+ slapi_lock_mutex(config_entry->lock);
+ dna_update_shared_config(config_entry);
+ slapi_unlock_mutex(config_entry->lock);
+ dna_unlock();
+
+ bail:
+ slapi_pblock_destroy(pb);
+ pb = NULL;
+}
+
/****************************************************
Distributed ranges Helpers
****************************************************/
-static int dna_fix_maxval(struct configEntry *config_entry, PRUint64 *cur)
+/*
+ * dna_fix_maxval()
+ *
+ * Attempts to extend the range represented by
+ * config_entry.
+ *
+ * The lock for configEntry should be obtained
+ * before calling this function.
+ */
+static int dna_fix_maxval(struct configEntry *config_entry)
{
- /* TODO: check the main partition to see if another range
- * is available, and set the new local configuration
- * accordingly.
- * If a new range is not available run the retrieval task
- * and simply return error
- */
+ PRCList *servers = NULL;
+ PRCList *server = NULL;
+ PRUint64 lower = 0;
+ PRUint64 upper = 0;
+ int ret = LDAP_OPERATIONS_ERROR;
+
+ if (config_entry == NULL) {
+ goto bail;
+ }
- return LDAP_OPERATIONS_ERROR;
+ /* If we already have a next range we only need
+ * to activate it. */
+ if (config_entry->next_range_lower != 0) {
+ ret = dna_activate_next_range(config_entry);
+ if (ret != 0) {
+ slapi_log_error(SLAPI_LOG_FATAL, DNA_PLUGIN_SUBSYSTEM,
+ "dna_fix_maxval: Unable to activate the "
+ "next range for range %s.\n", config_entry->dn);
+ }
+ } else if (config_entry->shared_cfg_base) {
+ /* Find out if there are any other servers to request
+ * range from. */
+ dna_get_shared_servers(config_entry, &servers);
+
+ if (servers) {
+ /* We have other servers we can try to extend
+ * our range from. Loop through them and
+ * request range until someone gives us some
+ * values, or we hit the end of the list. */
+ server = PR_LIST_HEAD(servers);
+ while (server != servers) {
+ if (dna_request_range(config_entry, (struct dnaServer *)server,
+ &lower, &upper) != 0) {
+ server = PR_NEXT_LINK(server);
+ } else {
+ /* Someone provided us with a new range. Attempt
+ * to update the config. */
+ if ((ret = dna_update_next_range(config_entry, lower, upper)) == 0) {
+ break;
+ }
+ }
+ }
+
+ /* free the list of servers */
+ dna_delete_shared_servers(&servers);
+ }
+ }
+
+bail:
+ return ret;
}
-static void dna_notice_allocation(struct configEntry *config_entry, PRUint64 new)
+/* dna_notice_allocation()
+ *
+ * Called after a new value has been allocated from the range.
+ * This function will update the config entries and cached info
+ * appropriately. This includes activating the next range if
+ * we've exhausted the current range.
+ *
+ * The last parameter is the value that has just been allocated.
+ * The new parameter should be the next available value. If you
+ * set both of these parameters to 0, then this function will
+ * just check if the next range needs to be activated and update
+ * the config accordingly.
+ *
+ * The lock for configEntry should be obtained before calling
+ * this function. */
+static void
+dna_notice_allocation(struct configEntry *config_entry, PRUint64 new,
+ PRUint64 last, int fix)
{
- /* update our cached config entry with the newly allocated
- * value.
- */
- config_entry->nextval = new;
+ /* update our cached config entry */
+ if ((new != 0) && (new <= (config_entry->maxval + config_entry->interval))) {
+ config_entry->nextval = new;
+ }
- /* TODO: check if we passed a new chunk threshold and update
- * the shared configuration on the public partition.
- */
+ /* check if we've exhausted our active range */
+ if ((last == config_entry->maxval) ||
+ (config_entry->nextval > config_entry->maxval)) {
+ /* If we already have a next range set, make it the
+ * new active range. */
+ if (config_entry->next_range_lower != 0) {
+ /* Make the next range active */
+ if (dna_activate_next_range(config_entry) != 0) {
+ slapi_log_error(SLAPI_LOG_FATAL, DNA_PLUGIN_SUBSYSTEM,
+ "dna_notice_allocation: Unable to activate "
+ "the next range for range %s.\n", config_entry->dn);
+ }
+ } else {
+ config_entry->remaining = 0;
+ /* update the shared configuration */
+ dna_update_shared_config(config_entry);
+ }
+ } else {
+ if (config_entry->next_range_lower != 0) {
+ config_entry->remaining = ((config_entry->maxval - config_entry->nextval + 1) /
+ config_entry->interval) + ((config_entry->next_range_upper -
+ config_entry->next_range_lower +1) / config_entry->interval);
+ } else {
+ config_entry->remaining = ((config_entry->maxval - config_entry->nextval + 1) /
+ config_entry->interval);
+ }
+
+ /* update the shared configuration */
+ dna_update_shared_config(config_entry);
+ }
+
+ /* Check if we passed the threshold and try to fix maxval if so. We
+ * don't need to do this if we already have a next range on deck. */
+ if ((config_entry->next_range_lower == 0) && (config_entry->remaining <= config_entry->threshold)) {
+ slapi_log_error(SLAPI_LOG_FATAL, DNA_PLUGIN_SUBSYSTEM,
+ "dna_notice_allocation: Passed threshold of %llu remaining values "
+ "for range %s. (%llu values remain)\n",
+ config_entry->threshold, config_entry->dn, config_entry->remaining);
+ /* Only attempt to fix maxval if the fix flag is set. */
+ if (fix != 0) {
+ dna_fix_maxval(config_entry);
+ }
+ }
return;
}
+static int
+dna_get_shared_servers(struct configEntry *config_entry, PRCList **servers)
+{
+ int ret = LDAP_SUCCESS;
+ Slapi_PBlock *pb = NULL;
+ Slapi_Entry **entries = NULL;
+ char *attrs[5];
+
+ /* First do a search in the shared config area for this
+ * range to find other servers who are managing this range. */
+ attrs[0] = DNA_HOSTNAME;
+ attrs[1] = DNA_PORTNUM;
+ attrs[2] = DNA_SECURE_PORTNUM;
+ attrs[3] = DNA_REMAINING;
+ attrs[4] = NULL;
+
+ pb = slapi_pblock_new();
+ if (NULL == pb) {
+ ret = LDAP_OPERATIONS_ERROR;
+ goto cleanup;
+ }
+
+ slapi_search_internal_set_pb(pb, config_entry->shared_cfg_base,
+ LDAP_SCOPE_ONELEVEL, "objectclass=*",
+ attrs, 0, NULL,
+ NULL, getPluginID(), 0);
+ slapi_search_internal_pb(pb);
+
+ slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &ret);
+ if (LDAP_SUCCESS != ret) {
+ slapi_log_error(SLAPI_LOG_FATAL, DNA_PLUGIN_SUBSYSTEM,
+ "dna_get_shared_servers: search failed for shared "
+ "config: %s [error %d]\n", config_entry->shared_cfg_base,
+ ret);
+ goto cleanup;
+ }
+
+ slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES,
+ &entries);
+
+ if (entries && entries[0]) {
+ Slapi_DN *cfg_sdn = NULL;
+ int i;
+
+ cfg_sdn = slapi_sdn_new_dn_byref(config_entry->shared_cfg_dn);
+
+ /* We found some entries. Go through them and
+ * order them based off of remaining values. */
+ for (i = 0; entries[i]; i++) {
+ /* skip our own shared config entry */
+ if (slapi_sdn_compare(cfg_sdn, slapi_entry_get_sdn(entries[i]))) {
+ struct dnaServer *server = NULL;
+
+ /* set up the server list entry */
+ server = (struct dnaServer *) slapi_ch_calloc(1,
+ sizeof(struct dnaServer));
+ server->host = slapi_entry_attr_get_charptr(entries[i],
+ DNA_HOSTNAME);
+ server->port = slapi_entry_attr_get_uint(entries[i], DNA_PORTNUM);
+ server->secureport = slapi_entry_attr_get_uint(entries[i], DNA_SECURE_PORTNUM);
+ server->remaining = slapi_entry_attr_get_ulonglong(entries[i],
+ DNA_REMAINING);
+
+ /* validate the entry */
+ if (!server->host || server->port == 0 || server->remaining == 0) {
+ /* free and skip this one */
+ slapi_log_error(SLAPI_LOG_PLUGIN, DNA_PLUGIN_SUBSYSTEM,
+ "dna_get_shared_servers: skipping invalid "
+ "shared config entry (%s)\n", slapi_entry_get_dn(entries[i]));
+ dna_free_shared_server(&server);
+ continue;
+ }
+
+ /* add a server entry to the list */
+ if (*servers == NULL) {
+ /* first entry */
+ *servers = (PRCList *) slapi_ch_calloc(1,
+ sizeof(struct dnaServer));
+ PR_INIT_CLIST(*servers);
+ PR_INSERT_LINK(&(server->list), *servers);
+ } else {
+ /* Find the right slot for this entry. We
+ * want to order the entries based off of
+ * the remaining number of values, higest
+ * to lowest. */
+ struct dnaServer *sitem;
+ PRCList* item = PR_LIST_HEAD(*servers);
+
+ while (item != *servers) {
+ sitem = (struct dnaServer *)item;
+ if (server->remaining > sitem->remaining) {
+ PR_INSERT_BEFORE(&(server->list), item);
+ break;
+ }
+
+ item = PR_NEXT_LINK(item);
+
+ if (*servers == item) {
+ /* add to tail */
+ PR_INSERT_BEFORE(&(server->list), item);
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ slapi_sdn_free(&cfg_sdn);
+ }
+
+
+ cleanup:
+ slapi_free_search_results_internal(pb);
+ slapi_pblock_destroy(pb);
+ return ret;
+}
+
+
+/*
+ * dna_request_range()
+ *
+ * Requests range extension from another server.
+ * Returns 0 on success and will fill in upper
+ * and lower. Returns non-0 on failure and will
+ * zero out upper and lower.
+ */
+static int dna_request_range(struct configEntry *config_entry,
+ struct dnaServer *server,
+ PRUint64 *lower, PRUint64 *upper)
+{
+ Slapi_DN *agmt_sdn = NULL;
+ char *bind_dn = NULL;
+ char *bind_passwd = NULL;
+ char *bind_method = NULL;
+ int is_ssl = 0;
+ int is_client_auth = 0;
+ int replport = 0;
+ struct berval *request = NULL;
+ char *retoid = NULL;
+ struct berval *responsedata = NULL;
+ BerElement *respber = NULL;
+ LDAP *ld = NULL;
+ char *lower_str = NULL;
+ char *upper_str = NULL;
+ int set_extend_flag = 0;
+ int ret = LDAP_OPERATIONS_ERROR;
+
+ /* See if we're allowed to send a range request now */
+ slapi_lock_mutex(config_entry->extend_lock);
+ if (config_entry->extend_in_progress) {
+ /* We're already processing a range extention, so bail. */
+ slapi_log_error(SLAPI_LOG_PLUGIN, DNA_PLUGIN_SUBSYSTEM,
+ "dna_request_range: Already processing a "
+ "range extension request. Skipping request.\n");
+ slapi_unlock_mutex(config_entry->extend_lock);
+ goto bail;
+ } else {
+ /* Set a flag indicating that we're attempting to extend this range */
+ config_entry->extend_in_progress = 1;
+ set_extend_flag = 1;
+ slapi_unlock_mutex(config_entry->extend_lock);
+ }
+
+ /* Fetch the replication bind dn info */
+ if (dna_get_replica_bind_creds(config_entry->shared_cfg_base, server,
+ &bind_dn, &bind_passwd, &bind_method, &is_ssl) != 0) {
+ slapi_log_error(SLAPI_LOG_FATAL, DNA_PLUGIN_SUBSYSTEM,
+ "dna_request_range: Unable to retrieve "
+ "replica bind credentials.\n");
+ goto bail;
+ }
+
+ if (strcasecmp(bind_method, "SIMPLE") == 0) {
+ slapi_log_error(SLAPI_LOG_PLUGIN, DNA_PLUGIN_SUBSYSTEM,
+ "dna_request_range: Using SIMPLE bind method.\n");
+ } else if (strcasecmp(bind_method, "SSLCLIENTAUTH") == 0) {
+ slapi_log_error(SLAPI_LOG_PLUGIN, DNA_PLUGIN_SUBSYSTEM,
+ "dna_request_range: Using SSLCLIENTAUTH bind method.\n");
+ is_client_auth = 1;
+ } else {
+ slapi_log_error(SLAPI_LOG_FATAL, DNA_PLUGIN_SUBSYSTEM,
+ "dna_request_range: Unknown bind method.\n");
+ goto bail;
+ }
+
+ if ((request = dna_create_range_request(config_entry->shared_cfg_base)) == NULL) {
+ slapi_log_error(SLAPI_LOG_FATAL, DNA_PLUGIN_SUBSYSTEM,
+ "dna_request_range: Failed to create "
+ "range extension extended operation request.\n");
+ goto bail;
+ }
+
+ if ((ld = slapi_ldap_init(server->host, is_ssl?server->secureport:server->port, is_ssl, 0)) == NULL) {
+ slapi_log_error(SLAPI_LOG_FATAL, DNA_PLUGIN_SUBSYSTEM,
+ "dna_request_range: Unable to "
+ "initialize LDAP session to server %s:%u.\n",
+ server->host, server->port);
+ goto bail;
+ }
+
+ /* Disable referrals and set timelimit and a connect timeout */
+ ldap_set_option(ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
+ ldap_set_option(ld, LDAP_OPT_TIMELIMIT, &config_entry->timeout);
+ ldap_set_option(ld, LDAP_X_OPT_CONNECT_TIMEOUT, &config_entry->timeout);
+
+ /* Bind to the replica server */
+ if (is_client_auth) {
+ ret = slapd_SSL_client_bind_s(ld, bind_dn, bind_passwd,
+ is_ssl, LDAP_VERSION3);
+ } else {
+ ret = ldap_simple_bind_s(ld, bind_dn, bind_passwd);
+ }
+
+ if (ret != LDAP_SUCCESS) {
+ slapi_log_error(SLAPI_LOG_FATAL, DNA_PLUGIN_SUBSYSTEM,
+ "dna_request_range: Error binding "
+ " to replica server %s:%u. [error %d]\n",
+ server->host, server->port, ret);
+ goto bail;
+ }
+
+ /* Send range extension request */
+ ret = ldap_extended_operation_s(ld, DNA_EXTEND_EXOP_REQUEST_OID,
+ request, NULL, NULL, &retoid, &responsedata);
+
+ if (ret != LDAP_SUCCESS) {
+ slapi_log_error(SLAPI_LOG_FATAL, DNA_PLUGIN_SUBSYSTEM,
+ "dna_request_range: Error sending "
+ "range extension extended operation request "
+ "to server %s:%u [error %d]\n", server->host,
+ server->port, ret);
+ goto bail;
+ }
+
+ /* Verify that the OID is correct. */
+ if (strcmp(retoid, DNA_EXTEND_EXOP_RESPONSE_OID) != 0) {
+ ret = LDAP_OPERATIONS_ERROR;
+ slapi_log_error(SLAPI_LOG_FATAL, DNA_PLUGIN_SUBSYSTEM,
+ "dna_request_range: Received incorrect response OID.\n");
+ goto bail;
+ }
+
+ /* Parse response */
+ if (responsedata) {
+ respber = ber_init(responsedata);
+ ber_scanf(respber, "{aa}", &lower_str, &upper_str);
+ }
+
+ /* Fill in upper and lower */
+ if (upper_str && lower_str) {
+ *upper = strtoull(upper_str, 0, 0);
+ *lower = strtoull(lower_str, 0, 0);
+ ret = 0;
+ } else {
+ ret = LDAP_OPERATIONS_ERROR;
+ }
+
+bail:
+ if (set_extend_flag) {
+ slapi_lock_mutex(config_entry->extend_lock);
+ config_entry->extend_in_progress = 0;
+ slapi_unlock_mutex(config_entry->extend_lock);
+ }
+ slapi_ldap_unbind(ld);
+ slapi_ch_free_string(&bind_dn);
+ slapi_ch_free_string(&bind_passwd);
+ slapi_ch_free_string(&bind_method);
+ slapi_ch_free_string(&retoid);
+ slapi_ch_free_string(&lower_str);
+ slapi_ch_free_string(&upper_str);
+ ber_free(respber, 1);
+
+ if (ret != 0) {
+ *upper = 0;
+ *lower = 0;
+ }
+
+ return ret;
+}
+
+static struct berval *dna_create_range_request(char *range_dn)
+{
+ struct berval *requestdata = NULL;
+ struct berval shared_dn = { 0, NULL };
+ BerElement *ber = NULL;
+
+ shared_dn.bv_val = range_dn;
+ shared_dn.bv_len = strlen(shared_dn.bv_val);
+
+ if((ber = ber_alloc()) == NULL) {
+ slapi_log_error(SLAPI_LOG_FATAL, DNA_PLUGIN_SUBSYSTEM,
+ "dna_create_range_request: Error "
+ "allocating request data.\n");
+ goto bail;
+ }
+
+ if (LBER_ERROR == (ber_printf(ber, "{o}", shared_dn.bv_val, shared_dn.bv_len))) {
+ slapi_log_error(SLAPI_LOG_FATAL, DNA_PLUGIN_SUBSYSTEM,
+ "dna_create_range_request: Error "
+ "encoding request data.\n");
+ goto bail;
+ }
+
+ if (ber_flatten(ber, &requestdata) == -1) {
+ slapi_log_error(SLAPI_LOG_FATAL, DNA_PLUGIN_SUBSYSTEM,
+ "dna_create_range_request: Error "
+ "encoding request data.\n");
+ goto bail;
+ }
+
+bail:
+ ber_free(ber, 1);
+
+ return requestdata;
+}
+
/****************************************************
Helpers
****************************************************/
@@ -799,12 +1631,16 @@ static LDAPControl *dna_build_sort_control(const char *attr)
than config and startup
****************************************************/
-/* we do search all values between nextval and maxval asking the
+/*
+ * dna_first_free_value()
+ *
+ * We do search all values between nextval and maxval asking the
* server to sort them, then we check the first free spot and
* use it as newval. If we go past the end of the range, we
* return LDAP_OPERATIONS_ERROR and set newval to be > the
* maximum configured value for this range. */
-static int dna_first_free_value(struct configEntry *config_entry,
+static int
+dna_first_free_value(struct configEntry *config_entry,
PRUint64 *newval)
{
Slapi_Entry **entries = NULL;
@@ -945,7 +1781,7 @@ static int dna_first_free_value(struct configEntry *config_entry,
for (i = 0; NULL != entries[i]; i++) {
strval = slapi_entry_attr_get_charptr(entries[i], type);
errno = 0;
- sval = strtoul(strval, 0, 0);
+ sval = strtoull(strval, 0, 0);
if (errno) {
/* something very wrong here ... */
status = LDAP_OPERATIONS_ERROR;
@@ -995,7 +1831,8 @@ static int dna_get_next_value(struct configEntry *config_entry,
LDAPMod mod_replace;
LDAPMod *mods[2];
char *replace_val[2];
- char next_value[16];
+ /* 16 for max 64-bit unsigned plus the trailing '\0' */
+ char next_value[17];
PRUint64 setval = 0;
PRUint64 nextval = 0;
int ret;
@@ -1005,7 +1842,7 @@ static int dna_get_next_value(struct configEntry *config_entry,
/* get the lock to prevent contention with other threads over
* the next new value for this range. */
- slapi_lock_mutex(config_entry->new_value_lock);
+ slapi_lock_mutex(config_entry->lock);
/* get the first value */
ret = dna_first_free_value(config_entry, &setval);
@@ -1013,10 +1850,10 @@ static int dna_get_next_value(struct configEntry *config_entry,
/* check if we overflowed the configured range */
if (setval > config_entry->maxval) {
/* try for a new range or fail */
- ret = dna_fix_maxval(config_entry, &setval);
+ ret = dna_fix_maxval(config_entry);
if (LDAP_SUCCESS != ret) {
slapi_log_error(SLAPI_LOG_FATAL, DNA_PLUGIN_SUBSYSTEM,
- "dna_get_next_value: no more IDs available!!\n");
+ "dna_get_next_value: no more values available!!\n");
goto done;
}
@@ -1033,27 +1870,9 @@ static int dna_get_next_value(struct configEntry *config_entry,
}
nextval = setval + config_entry->interval;
- /* check if the next value will overflow the range */
- if (nextval > config_entry->maxval) {
- /* try for a new range now, but let this operation through either way */
- ret = dna_fix_maxval(config_entry, &nextval);
- if (LDAP_SUCCESS != ret) {
- /* We were unable to extend the range. The allocation
- * for this operation was fine, but the next free value
- * is outside of the configured range.
- *
- * We log an error message, but let the original operation
- * go through. We skip updating the config entry with
- * the new nextval since it falls outside of the configured
- * range.
- *
- * The next attempt to allocate a new value from this
- * range will fail. */
- slapi_log_error(SLAPI_LOG_FATAL, DNA_PLUGIN_SUBSYSTEM,
- "dna_get_next_value: no more IDs available!!\n");
- ret = LDAP_SUCCESS;
- }
- } else {
+ /* update nextval if we have not reached the end
+ * of our current range */
+ if (nextval <= (config_entry->maxval + config_entry->interval)) {
/* try to set the new next value in the config entry */
snprintf(next_value, sizeof(next_value),"%llu", nextval);
@@ -1084,6 +1903,7 @@ static int dna_get_next_value(struct configEntry *config_entry,
}
if (LDAP_SUCCESS == ret) {
+ slapi_ch_free_string(next_value_ret);
*next_value_ret = slapi_ch_smprintf("%llu", setval);
if (NULL == *next_value_ret) {
ret = LDAP_OPERATIONS_ERROR;
@@ -1091,12 +1911,11 @@ static int dna_get_next_value(struct configEntry *config_entry,
}
/* update our cached config */
- dna_notice_allocation(config_entry, nextval);
+ dna_notice_allocation(config_entry, nextval, setval, 1);
}
done:
-
- slapi_unlock_mutex(config_entry->new_value_lock);
+ slapi_unlock_mutex(config_entry->lock);
if (pb)
slapi_pblock_destroy(pb);
@@ -1107,6 +1926,444 @@ static int dna_get_next_value(struct configEntry *config_entry,
return ret;
}
+/*
+ * dna_update_shared_config()
+ *
+ * Updates the shared config entry if one is
+ * configured. Returns the LDAP result code.
+ *
+ * The lock for configEntry should be obtained
+ * before calling this function.
+ * */
+static int
+dna_update_shared_config(struct configEntry * config_entry)
+{
+ int ret = LDAP_SUCCESS;
+
+ if (config_entry && config_entry->shared_cfg_dn) {
+ /* Update the shared config entry if one is configured */
+ Slapi_PBlock *pb = NULL;
+ LDAPMod mod_replace;
+ LDAPMod *mods[2];
+ char *replace_val[2];
+ /* 16 for max 64-bit unsigned plus the trailing '\0' */
+ char remaining_vals[17];
+
+ /* We store the number of remaining assigned values
+ * in the shared config entry. */
+ snprintf(remaining_vals, sizeof(remaining_vals),"%llu", config_entry->remaining);
+
+ /* set up our replace modify operation */
+ replace_val[0] = remaining_vals;
+ replace_val[1] = 0;
+ mod_replace.mod_op = LDAP_MOD_REPLACE;
+ mod_replace.mod_type = DNA_REMAINING;
+ mod_replace.mod_values = replace_val;
+ mods[0] = &mod_replace;
+ mods[1] = 0;
+
+ pb = slapi_pblock_new();
+ if (NULL == pb) {
+ ret = LDAP_OPERATIONS_ERROR;
+ } else {
+ slapi_modify_internal_set_pb(pb, config_entry->shared_cfg_dn,
+ mods, NULL, NULL, getPluginID(), 0);
+
+ slapi_modify_internal_pb(pb);
+
+ slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &ret);
+
+ /* If the shared config for this instance doesn't
+ * already exist, we add it. */
+ if (ret == LDAP_NO_SUCH_OBJECT) {
+ Slapi_Entry *e = NULL;
+
+ /* Set up the new shared config entry */
+ e = slapi_entry_alloc();
+ /* the entry now owns the dup'd dn */
+ slapi_entry_init(e, slapi_ch_strdup(config_entry->shared_cfg_dn), NULL);
+
+ slapi_entry_add_string(e, SLAPI_ATTR_OBJECTCLASS, "extensibleObject");
+ slapi_entry_add_string(e, DNA_HOSTNAME, hostname);
+ slapi_entry_add_string(e, DNA_PORTNUM, portnum);
+ if (secureportnum) {
+ slapi_entry_add_string(e, DNA_SECURE_PORTNUM, secureportnum);
+ }
+ slapi_entry_add_string(e, DNA_REMAINING, remaining_vals);
+
+ /* clear pb for re-use */
+ slapi_pblock_init(pb);
+
+ /* e will be consumed by slapi_add_internal() */
+ slapi_add_entry_internal_set_pb(pb, e, NULL, getPluginID(), 0);
+ slapi_add_internal_pb(pb);
+ slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &ret);
+ }
+
+ if (ret != LDAP_SUCCESS) {
+ slapi_log_error(SLAPI_LOG_FATAL, DNA_PLUGIN_SUBSYSTEM,
+ "dna_update_shared_config: Unable to update shared config entry: %s [error %d]\n",
+ config_entry->shared_cfg_dn, ret);
+ }
+
+ slapi_pblock_destroy(pb);
+ pb = NULL;
+ }
+ }
+
+ return ret;
+}
+
+/*
+ * dna_update_next_range()
+ *
+ * Sets the proper value for the next range in
+ * all configuration entries and in-memory cache.
+ *
+ * The range you are updating should be locked
+ * before calling this function.
+ */
+static int
+dna_update_next_range(struct configEntry *config_entry,
+ PRUint64 lower, PRUint64 upper)
+{
+ Slapi_PBlock *pb = NULL;
+ LDAPMod mod_replace;
+ LDAPMod *mods[2];
+ char *replace_val[2];
+ /* 32 for the two numbers, 1 for the '-', and one for the '\0' */
+ char nextrange_value[34];
+ int ret = 0;
+
+ /* Try to set the new next range in the config entry. */
+ snprintf(nextrange_value, sizeof(nextrange_value), "%llu-%llu",
+ lower, upper);
+
+ /* set up our replace modify operation */
+ replace_val[0] = nextrange_value;
+ replace_val[1] = 0;
+ mod_replace.mod_op = LDAP_MOD_REPLACE;
+ mod_replace.mod_type = DNA_NEXT_RANGE;
+ mod_replace.mod_values = replace_val;
+ mods[0] = &mod_replace;
+ mods[1] = 0;
+
+ pb = slapi_pblock_new();
+ if (NULL == pb) {
+ ret = LDAP_OPERATIONS_ERROR;
+ goto bail;
+ }
+
+ slapi_modify_internal_set_pb(pb, config_entry->dn,
+ mods, 0, 0, getPluginID(), 0);
+
+ slapi_modify_internal_pb(pb);
+
+ slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &ret);
+
+ slapi_pblock_destroy(pb);
+ pb = NULL;
+
+ if (ret != LDAP_SUCCESS) {
+ slapi_log_error(SLAPI_LOG_FATAL, DNA_PLUGIN_SUBSYSTEM,
+ "dna_update_next_range: Error updating "
+ "configuration entry [err=%d]\n", ret);
+ } else {
+ /* update the cached config and the shared config */
+ config_entry->next_range_lower = lower;
+ config_entry->next_range_upper = upper;
+ dna_notice_allocation(config_entry, 0, 0, 0);
+ }
+
+bail:
+ return ret;
+}
+
+/* dna_activate_next_range()
+ *
+ * Makes the next range the active range. This
+ * will update the config entry, the in-memory
+ * config info, and the shared config entry.
+ *
+ * The lock for configEntry should
+ * be obtained before calling this function.
+ */
+static int
+dna_activate_next_range(struct configEntry *config_entry)
+{
+ Slapi_PBlock *pb = NULL;
+ LDAPMod mod_maxval;
+ LDAPMod mod_nextval;
+ LDAPMod mod_nextrange;
+ LDAPMod *mods[4];
+ char *maxval_vals[2];
+ char *nextval_vals[2];
+ char *nextrange_vals[1];
+ /* 16 for max 64-bit unsigned plus the trailing '\0' */
+ char maxval_val[17];
+ char nextval_val[17];
+ int ret = 0;
+
+ /* Setup the modify operation for the config entry */
+ snprintf(maxval_val, sizeof(maxval_val),"%llu", config_entry->next_range_upper);
+ snprintf(nextval_val, sizeof(nextval_val),"%llu", config_entry->next_range_lower);
+
+ maxval_vals[0] = maxval_val;
+ maxval_vals[1] = 0;
+ nextval_vals[0] = nextval_val;
+ nextval_vals[1] = 0;
+ nextrange_vals[0] = 0;
+
+ mod_maxval.mod_op = LDAP_MOD_REPLACE;
+ mod_maxval.mod_type = DNA_MAXVAL;
+ mod_maxval.mod_values = maxval_vals;
+ mod_nextval.mod_op = LDAP_MOD_REPLACE;
+ mod_nextval.mod_type = DNA_NEXTVAL;
+ mod_nextval.mod_values = nextval_vals;
+ mod_nextrange.mod_op = LDAP_MOD_DELETE;
+ mod_nextrange.mod_type = DNA_NEXT_RANGE;
+ mod_nextrange.mod_values = nextrange_vals;
+
+ mods[0] = &mod_maxval;
+ mods[1] = &mod_nextval;
+ mods[2] = &mod_nextrange;
+ mods[3] = 0;
+
+ /* Update the config entry first */
+ pb = slapi_pblock_new();
+ if (NULL == pb) {
+ ret = LDAP_OPERATIONS_ERROR;
+ goto bail;
+ }
+
+ slapi_modify_internal_set_pb(pb, config_entry->dn,
+ mods, 0, 0, getPluginID(), 0);
+
+ slapi_modify_internal_pb(pb);
+
+ slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &ret);
+
+ slapi_pblock_destroy(pb);
+ pb = NULL;
+
+ if (ret != LDAP_SUCCESS) {
+ slapi_log_error(SLAPI_LOG_FATAL, DNA_PLUGIN_SUBSYSTEM,
+ "dna_activate_next_range: Error updating "
+ "configuration entry [err=%d]\n", ret);
+ } else {
+ /* Update the in-memory config info */
+ config_entry->maxval = config_entry->next_range_upper;
+ config_entry->nextval = config_entry->next_range_lower;
+ config_entry->next_range_upper = 0;
+ config_entry->next_range_lower = 0;
+ config_entry->remaining = ((config_entry->maxval - config_entry->nextval + 1) /
+ config_entry->interval);
+ /* update the shared configuration */
+ dna_update_shared_config(config_entry);
+ }
+
+bail:
+ return ret;
+}
+
+/*
+ * dna_is_replica_bind_dn()
+ *
+ * Checks if the passed in DN is the replica bind DN. This
+ * is used to check if a user is allowed to request range
+ * from us.
+ *
+ * Returns 1 if bind_dn matches the replica bind dn, 0 otherwise. */
+static int dna_is_replica_bind_dn(char *range_dn, char *bind_dn)
+{
+ char *replica_dn = NULL;
+ Slapi_DN *replica_sdn = NULL;
+ char *replica_bind_dn = NULL;
+ Slapi_DN *replica_bind_sdn = NULL;
+ Slapi_DN *range_sdn = NULL;
+ Slapi_DN *bind_sdn = NULL;
+ Slapi_Entry *e = NULL;
+ char *attrs[2];
+ Slapi_Backend *be = NULL;
+ const char *be_suffix = NULL;
+ int ret = 0;
+
+ /* Find the backend suffix where the shared config is stored. */
+ range_sdn = slapi_sdn_new_dn_byref(range_dn);
+ if ((be = slapi_be_select(range_sdn)) != NULL) {
+ be_suffix = slapi_sdn_get_dn(slapi_be_getsuffix(be, 0));
+ }
+
+ /* Fetch the "cn=replica" entry for the backend that stores
+ * the shared config. We need to see what the configured
+ * replica bind DN is. */
+ if (be_suffix) {
+ replica_dn = slapi_ch_smprintf("cn=replica,cn=\"%s\",cn=mapping tree,cn=config", be_suffix);
+ replica_sdn = slapi_sdn_new_dn_passin(replica_dn);
+
+ attrs[0] = DNA_REPL_BIND_DN;
+ attrs[1] = 0;
+
+ /* Find cn=replica entry via search */
+ slapi_search_internal_get_entry(replica_sdn, attrs, &e, getPluginID());
+
+ if (e) {
+ replica_bind_dn = slapi_entry_attr_get_charptr(e, DNA_REPL_BIND_DN);
+ } else {
+ slapi_log_error(SLAPI_LOG_PLUGIN, DNA_PLUGIN_SUBSYSTEM,
+ "dna_is_replica_bind_dn: Failed to fetch replica entry "
+ "for range %s\n", range_dn);
+ }
+ }
+
+ if (replica_bind_dn) {
+ /* Compare the passed in bind dn to the replica bind dn */
+ bind_sdn = slapi_sdn_new_dn_byref(bind_dn);
+ replica_bind_sdn = slapi_sdn_new_dn_passin(replica_bind_dn);
+ if (slapi_sdn_compare(bind_sdn, replica_bind_sdn) == 0) {
+ ret = 1;
+ }
+ }
+
+ slapi_entry_free(e);
+ slapi_sdn_free(&range_sdn);
+ slapi_sdn_free(&replica_sdn);
+ slapi_sdn_free(&replica_bind_sdn);
+ slapi_sdn_free(&bind_sdn);
+
+ return ret;
+}
+
+static int dna_get_replica_bind_creds(char *range_dn, struct dnaServer *server,
+ char **bind_dn, char **bind_passwd,
+ char **bind_method, int *is_ssl)
+{
+ Slapi_PBlock *pb = NULL;
+ Slapi_DN *range_sdn = NULL;
+ char *replica_dn = NULL;
+ Slapi_Backend *be = NULL;
+ const char *be_suffix = NULL;
+ char *attrs[5];
+ char *filter = NULL;
+ char *bind_cred = NULL;
+ char *transport = NULL;
+ Slapi_Entry **entries = NULL;
+ int ret = LDAP_OPERATIONS_ERROR;
+
+ /* Find the backend suffix where the shared config is stored. */
+ range_sdn = slapi_sdn_new_dn_byref(range_dn);
+ if ((be = slapi_be_select(range_sdn)) != NULL) {
+ be_suffix = slapi_sdn_get_dn(slapi_be_getsuffix(be, 0));
+ }
+
+ /* Fetch the replication agreement entry */
+ if (be_suffix) {
+ replica_dn = slapi_ch_smprintf("cn=replica,cn=\"%s\",cn=mapping tree,cn=config",
+ be_suffix);
+
+ filter = slapi_ch_smprintf("(&(nsds5ReplicaHost=%s)(|(nsds5ReplicaPort=%u)"
+ "(nsds5ReplicaPort=%u)))",
+ server->host, server->port, server->secureport);
+
+ attrs[0] = DNA_REPL_BIND_DN;
+ attrs[1] = DNA_REPL_CREDS;
+ attrs[2] = DNA_REPL_BIND_METHOD;
+ attrs[3] = DNA_REPL_TRANSPORT;
+ attrs[4] = 0;
+
+ pb = slapi_pblock_new();
+ if (NULL == pb) {
+ slapi_log_error(SLAPI_LOG_FATAL, DNA_PLUGIN_SUBSYSTEM,
+ "dna_get_replica_bind_creds: Failed to "
+ "allocate pblock\n");
+ goto bail;
+ }
+
+ slapi_search_internal_set_pb(pb, replica_dn,
+ LDAP_SCOPE_ONELEVEL, filter,
+ attrs, 0, NULL, NULL, getPluginID(), 0);
+ slapi_search_internal_pb(pb);
+ slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &ret);
+
+ if (LDAP_SUCCESS != ret) {
+ slapi_log_error(SLAPI_LOG_FATAL, DNA_PLUGIN_SUBSYSTEM,
+ "dna_get_replica_bind_creds: Failed to fetch replica "
+ "bind credentials for range %s, server %s, port %u [error %d]\n",
+ range_dn, server->host, server->port, ret);
+ goto bail;
+ }
+
+ slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES,
+ &entries);
+
+ if (NULL == entries || NULL == entries[0]) {
+ slapi_log_error(SLAPI_LOG_FATAL, DNA_PLUGIN_SUBSYSTEM,
+ "dna_get_replica_bind_creds: Failed to fetch replication "
+ "agreement for range %s, server %s, port %u\n", range_dn,
+ server->host, server->port);
+ ret = LDAP_OPERATIONS_ERROR;
+ goto bail;
+ }
+
+ /* Get the replication bind dn and password from the agreement. It
+ * is up to the caller to free these when they are finished. */
+ slapi_ch_free_string(bind_dn);
+ slapi_ch_free_string(bind_method);
+ *bind_dn = slapi_entry_attr_get_charptr(entries[0], DNA_REPL_BIND_DN);
+ *bind_method = slapi_entry_attr_get_charptr(entries[0], DNA_REPL_BIND_METHOD);
+ bind_cred = slapi_entry_attr_get_charptr(entries[0], DNA_REPL_CREDS);
+ transport = slapi_entry_attr_get_charptr(entries[0], DNA_REPL_TRANSPORT);
+
+ /* Check if we should use SSL */
+ if (transport && (strcasecmp(transport, "SSL") == 0)) {
+ *is_ssl = 1;
+ } else {
+ *is_ssl = 0;
+ }
+
+ /* Decode the password */
+ if (bind_cred) {
+ int pw_ret = 0;
+ slapi_ch_free_string(bind_passwd);
+ pw_ret = pw_rever_decode(bind_cred, bind_passwd, DNA_REPL_CREDS);
+
+ if (pw_ret == -1) {
+ slapi_log_error(SLAPI_LOG_FATAL, DNA_PLUGIN_SUBSYSTEM,
+ "dna_get_replica_bind_creds: Failed to decode "
+ "replica bind credentials for range %s, server %s, "
+ "port %u\n", range_dn, server->host, server->port);
+ goto bail;
+ } else if (pw_ret != 0) {
+ /* The password was already in clear text, so pw_rever_decode
+ * simply set bind_passwd to bind_cred. Set bind_cred to NULL
+ * to prevent a double free. The memory is now owned by
+ * bind_passwd, which is the callers responsibility to free. */
+ bind_cred = NULL;
+ }
+ }
+ }
+
+ /* If we didn't get both a bind DN and a decoded password,
+ * then just free everything and return an error. */
+ if (*bind_dn && *bind_passwd && *bind_method) {
+ ret = 0;
+ } else {
+ slapi_ch_free_string(bind_dn);
+ slapi_ch_free_string(bind_passwd);
+ slapi_ch_free_string(bind_method);
+ }
+
+bail:
+ slapi_ch_free_string(&filter);
+ slapi_sdn_free(&range_sdn);
+ slapi_ch_free_string(&replica_dn);
+ slapi_ch_free_string(&bind_cred);
+ slapi_free_search_results_internal(pb);
+ slapi_pblock_destroy(pb);
+
+ return ret;
+}
+
/* for mods and adds:
where dn's are supplied, the closest in scope
is used as long as the type and filter
@@ -1132,6 +2389,10 @@ static int dna_pre_op(Slapi_PBlock * pb, int modtype)
slapi_log_error(SLAPI_LOG_TRACE, DNA_PLUGIN_SUBSYSTEM,
"--> dna_pre_op\n");
+ /* Just bail if we aren't ready to service requests yet. */
+ if (!g_plugin_started)
+ goto bail;
+
if (0 == (dn = dna_get_dn(pb)))
goto bail;
@@ -1350,7 +2611,7 @@ static int dna_config_check_post_op(Slapi_PBlock * pb)
if ((dn = dna_get_dn(pb))) {
if (dna_dn_is_config(dn))
- loadPluginConfig();
+ dna_load_plugin_config();
}
slapi_log_error(SLAPI_LOG_TRACE, DNA_PLUGIN_SUBSYSTEM,
@@ -1359,6 +2620,326 @@ static int dna_config_check_post_op(Slapi_PBlock * pb)
return 0;
}
+
+/****************************************************
+ * Range Extension Extended Operation
+ ***************************************************/
+static int dna_extend_exop(Slapi_PBlock *pb)
+{
+ int ret = -1;
+ struct berval *reqdata = NULL;
+ BerElement *tmp_bere = NULL;
+ char *shared_dn = NULL;
+ char *bind_dn = NULL;
+ char *oid = NULL;
+ PRUint64 lower = 0;
+ PRUint64 upper = 0;
+
+ slapi_log_error(SLAPI_LOG_TRACE, DNA_PLUGIN_SUBSYSTEM,
+ "--> dna_extend_exop\n");
+
+ /* Fetch the request OID */
+ slapi_pblock_get(pb, SLAPI_EXT_OP_REQ_OID, &oid);
+ if (!oid) {
+ slapi_log_error(SLAPI_LOG_FATAL, DNA_PLUGIN_SUBSYSTEM,
+ "dna_extend_exop: Unable to retrieve request OID.\n");
+ goto free_and_return;
+ }
+
+ /* Make sure the request OID is correct. */
+ if (strcmp(oid, DNA_EXTEND_EXOP_REQUEST_OID) != 0) {
+ slapi_log_error(SLAPI_LOG_FATAL, DNA_PLUGIN_SUBSYSTEM,
+ "dna_extend_exop: Received incorrect request OID.\n");
+ goto free_and_return;
+ }
+
+ /* Fetch the request data */
+ slapi_pblock_get(pb, SLAPI_EXT_OP_REQ_VALUE, &reqdata);
+ if (!reqdata) {
+ slapi_log_error(SLAPI_LOG_FATAL, DNA_PLUGIN_SUBSYSTEM,
+ "dna_extend_exop: No request data received.\n");
+ goto free_and_return;
+ }
+
+ /* decode the exop */
+ if ((tmp_bere = ber_init(reqdata)) == NULL) {
+ ret = -1;
+ goto free_and_return;
+ }
+
+ if (ber_scanf(tmp_bere, "{a}", &shared_dn) == LBER_ERROR) {
+ ret = -1;
+ goto free_and_return;
+ }
+
+ slapi_log_error(SLAPI_LOG_PLUGIN, DNA_PLUGIN_SUBSYSTEM,
+ "dna_extend_exop: received range extension "
+ "request for range [%s]\n", shared_dn);
+
+ /* Only allow range requests from the replication bind DN user */
+ slapi_pblock_get(pb, SLAPI_CONN_DN, &bind_dn);
+ if (!dna_is_replica_bind_dn(shared_dn, bind_dn)) {
+ ret = LDAP_INSUFFICIENT_ACCESS;
+ goto free_and_return;
+ }
+
+ /* See if we have the req. range configured.
+ * If so, we need to see if we have range to provide. */
+ ret = dna_release_range(shared_dn, &lower, &upper);
+
+ if (ret == LDAP_SUCCESS) {
+ /* We have range to give away, so construct
+ * and send the response. */
+ BerElement *respber = NULL;
+ struct berval *respdata = NULL;
+ struct berval range_low = {0, NULL};
+ struct berval range_high = {0, NULL};
+ char lowstr[16];
+ char highstr[16];
+
+ /* Create the exop response */
+ snprintf(lowstr, sizeof(lowstr), "%llu", lower);
+ snprintf(highstr, sizeof(highstr), "%llu", upper);
+ range_low.bv_val = lowstr;
+ range_low.bv_len = strlen(range_low.bv_val);
+ range_high.bv_val = highstr;
+ range_high.bv_len = strlen(range_high.bv_val);
+
+ if ((respber = ber_alloc()) == NULL) {
+ ret = LDAP_NO_MEMORY;
+ goto free_and_return;
+ }
+
+ if (LBER_ERROR == (ber_printf(respber, "{oo}",
+ range_low.bv_val, range_low.bv_len,
+ range_high.bv_val, range_high.bv_len))) {
+ slapi_log_error(SLAPI_LOG_FATAL, DNA_PLUGIN_SUBSYSTEM,
+ "dna_extend_exop: Unable to encode exop response.\n");
+ ber_free(respber, 1);
+ ret = LDAP_ENCODING_ERROR;
+ goto free_and_return;
+ }
+
+ ber_flatten(respber, &respdata);
+ ber_free(respber, 1);
+
+ slapi_pblock_set(pb, SLAPI_EXT_OP_RET_OID, DNA_EXTEND_EXOP_RESPONSE_OID);
+ slapi_pblock_set(pb, SLAPI_EXT_OP_RET_VALUE, respdata);
+
+ /* send the response ourselves */
+ send_ldap_result( pb, ret, NULL, NULL, 0, NULL );
+ ret = SLAPI_PLUGIN_EXTENDED_SENT_RESULT;
+ ber_bvfree(respdata);
+
+ slapi_log_error(SLAPI_LOG_PLUGIN, DNA_PLUGIN_SUBSYSTEM,
+ "dna_extend_exop: Released range %llu-%llu.\n",
+ lower, upper);
+ }
+
+ free_and_return:
+ slapi_ch_free_string(&shared_dn);
+ slapi_ch_free_string(&bind_dn);
+ if (NULL != tmp_bere) {
+ ber_free(tmp_bere, 1);
+ tmp_bere = NULL;
+ }
+
+ slapi_log_error(SLAPI_LOG_TRACE, DNA_PLUGIN_SUBSYSTEM,
+ "<-- dna_extend_exop\n");
+
+ return ret;
+}
+
+/*
+ * dna_release_range()
+ *
+ * Checks if we have any values that we can release
+ * for the range specified by range_dn.
+ */
+static int
+dna_release_range(char *range_dn, PRUint64 *lower, PRUint64 *upper)
+{
+ int ret = 0;
+ int match = 0;
+ PRCList *list = NULL;
+ Slapi_DN *cfg_base_sdn = NULL;
+ Slapi_DN *range_sdn = NULL;
+ struct configEntry *config_entry = NULL;
+ int set_extend_flag = 0;
+
+ slapi_log_error(SLAPI_LOG_TRACE, DNA_PLUGIN_SUBSYSTEM,
+ "--> dna_release_range\n");
+
+ if (range_dn) {
+ range_sdn = slapi_sdn_new_dn_byref(range_dn);
+
+ dna_read_lock();
+
+ /* Go through the config entries to see if we
+ * have a shared range configured that matches
+ * the range from the exop request. */
+ if (!PR_CLIST_IS_EMPTY(dna_global_config)) {
+ list = PR_LIST_HEAD(dna_global_config);
+ while ((list != dna_global_config) && match != 1) {
+ config_entry = (struct configEntry *)list;
+ cfg_base_sdn = slapi_sdn_new_dn_byref(config_entry->shared_cfg_base);
+
+ if (slapi_sdn_compare(cfg_base_sdn, range_sdn) == 0) {
+ /* We found a match. Set match flag to
+ * break out of the loop. */
+ match = 1;
+ } else {
+ config_entry = NULL;
+ list = PR_NEXT_LINK(list);
+ }
+
+ slapi_sdn_free(&cfg_base_sdn);
+ }
+
+ }
+
+ /* config_entry will point to our match if we found one */
+ if (config_entry) {
+ int release = 0;
+ Slapi_PBlock *pb = NULL;
+ LDAPMod mod_replace;
+ LDAPMod *mods[2];
+ char *replace_val[2];
+ /* 16 for max 64-bit unsigned plus the trailing '\0' */
+ char max_value[17];
+
+ /* Need to bail if we're performing a range request
+ * for this range. This is to prevent the case where two
+ * servers are asking each other for more range for the
+ * same managed range. This would result in a network
+ * deadlock until the idletimeout kills one of the
+ * connections. */
+ slapi_lock_mutex(config_entry->extend_lock);
+ if (config_entry->extend_in_progress) {
+ /* We're already processing a range extention, so bail. */
+ slapi_log_error(SLAPI_LOG_PLUGIN, DNA_PLUGIN_SUBSYSTEM,
+ "dna_release_range: Already processing a "
+ "range extension request. Skipping request.\n");
+ slapi_unlock_mutex(config_entry->extend_lock);
+ ret = LDAP_UNWILLING_TO_PERFORM;
+ goto bail;
+ } else {
+ /* Set a flag indicating that we're attempting to extend this range */
+ config_entry->extend_in_progress = 1;
+ set_extend_flag = 1;
+ slapi_unlock_mutex(config_entry->extend_lock);
+ }
+
+ /* Obtain the lock for this range */
+ slapi_lock_mutex(config_entry->lock);
+
+ /* Refuse if we're at or below our threshold */
+ if (config_entry->remaining <= config_entry->threshold) {
+ ret = LDAP_UNWILLING_TO_PERFORM;
+ goto bail;
+ }
+
+ /* If we have a next range, we need to give up values from
+ * it instead of from the active range */
+ if (config_entry->next_range_lower != 0) {
+ /* Release up to half of our values from the next range. */
+ release = (((config_entry->next_range_upper - config_entry->next_range_lower + 1) /
+ 2) / config_entry->threshold) * config_entry->threshold;
+
+ if (release == 0) {
+ ret = LDAP_UNWILLING_TO_PERFORM;
+ goto bail;
+ }
+
+ *upper = config_entry->next_range_upper;
+ *lower = *upper - release + 1;
+
+ /* Try to set the new next range in the config */
+ ret = dna_update_next_range(config_entry, config_entry->next_range_lower,
+ *lower - 1);
+ } else {
+ /* We release up to half of our remaining values,
+ * but we'll only release a range that is a multiple
+ * of our threshold. */
+ release = ((config_entry->remaining / 2) /
+ config_entry->threshold) * config_entry->threshold;
+
+ if (release == 0) {
+ ret = LDAP_UNWILLING_TO_PERFORM;
+ goto bail;
+ }
+
+ /* We give away values from the upper end of our range. */
+ *upper = config_entry->maxval;
+ *lower = *upper - release + 1;
+
+ /* try to set the new maxval in the config entry */
+ snprintf(max_value, sizeof(max_value),"%llu", (*lower - 1));
+
+ /* set up our replace modify operation */
+ replace_val[0] = max_value;
+ replace_val[1] = 0;
+ mod_replace.mod_op = LDAP_MOD_REPLACE;
+ mod_replace.mod_type = DNA_MAXVAL;
+ mod_replace.mod_values = replace_val;
+ mods[0] = &mod_replace;
+ mods[1] = 0;
+
+ pb = slapi_pblock_new();
+ if (NULL == pb) {
+ ret = LDAP_OPERATIONS_ERROR;
+ goto bail;
+ }
+
+ slapi_modify_internal_set_pb(pb, config_entry->dn,
+ mods, 0, 0, getPluginID(), 0);
+
+ slapi_modify_internal_pb(pb);
+
+ slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &ret);
+
+ slapi_pblock_destroy(pb);
+ pb = NULL;
+
+ if (ret == LDAP_SUCCESS) {
+ /* Adjust maxval in our cached config and shared config */
+ config_entry->maxval = *lower - 1;
+ dna_notice_allocation(config_entry, config_entry->nextval, 0, 0);
+ }
+ }
+
+ if (ret != LDAP_SUCCESS) {
+ /* Updating the config failed, so reset. We don't
+ * want to give the caller any range */
+ *lower = 0;
+ *upper = 0;
+ slapi_log_error(SLAPI_LOG_FATAL, DNA_PLUGIN_SUBSYSTEM,
+ "dna_release_range: Error updating "
+ "configuration entry [err=%d]\n", ret);
+ }
+ }
+
+bail:
+ if (set_extend_flag) {
+ slapi_lock_mutex(config_entry->extend_lock);
+ config_entry->extend_in_progress = 0;
+ slapi_unlock_mutex(config_entry->extend_lock);
+ }
+
+ if (config_entry) {
+ slapi_unlock_mutex(config_entry->lock);
+ }
+ slapi_sdn_free(&range_sdn);
+
+ dna_unlock();
+ }
+
+ slapi_log_error(SLAPI_LOG_TRACE, DNA_PLUGIN_SUBSYSTEM,
+ "<-- dna_release_range\n");
+
+ return ret;
+}
+
/****************************************************
End of
Functions that actually do things other
@@ -1368,7 +2949,7 @@ static int dna_config_check_post_op(Slapi_PBlock * pb)
/**
* debug functions to print config
*/
-void dnaDumpConfig()
+void dna_dump_config()
{
PRCList *list;
@@ -1377,7 +2958,7 @@ void dnaDumpConfig()
if (!PR_CLIST_IS_EMPTY(dna_global_config)) {
list = PR_LIST_HEAD(dna_global_config);
while (list != dna_global_config) {
- dnaDumpConfigEntry((struct configEntry *) list);
+ dna_dump_config_entry((struct configEntry *) list);
list = PR_NEXT_LINK(list);
}
}
@@ -1386,11 +2967,17 @@ void dnaDumpConfig()
}
-void dnaDumpConfigEntry(struct configEntry * entry)
+void dna_dump_config_entry(struct configEntry * entry)
{
- printf("<- type --------------> %s\n", entry->type);
+ printf("<---- type -----------> %s\n", entry->type);
+ printf("<---- filter ---------> %s\n", entry->filter);
printf("<---- prefix ---------> %s\n", entry->prefix);
- printf("<---- next value -----> %lu\n", entry->nextval);
- printf("<---- interval -------> %lu\n", entry->interval);
+ printf("<---- scope ----------> %s\n", entry->scope);
+ printf("<---- next value -----> %llu\n", entry->nextval);
+ printf("<---- max value ------> %llu\n", entry->maxval);
+ printf("<---- interval -------> %llu\n", entry->interval);
printf("<---- generate flag --> %s\n", entry->generate);
+ printf("<---- shared cfg base > %s\n", entry->shared_cfg_base);
+ printf("<---- shared cfg DN --> %s\n", entry->shared_cfg_dn);
+ printf("<---- threshold -----> %llu", entry->threshold);
}
diff --git a/ldap/servers/slapd/entry.c b/ldap/servers/slapd/entry.c
index d01c2c41..9172aef2 100644
--- a/ldap/servers/slapd/entry.c
+++ b/ldap/servers/slapd/entry.c
@@ -2200,6 +2200,36 @@ slapi_entry_attr_get_ulong( const Slapi_Entry* e, const char *type)
return r;
}
+long long
+slapi_entry_attr_get_longlong( const Slapi_Entry* e, const char *type)
+{
+ long long r = 0;
+ Slapi_Attr* attr;
+ slapi_entry_attr_find(e, type, &attr);
+ if (attr!=NULL)
+ {
+ Slapi_Value *v;
+ slapi_valueset_first_value( &attr->a_present_values, &v);
+ r = slapi_value_get_longlong(v);
+ }
+ return r;
+}
+
+unsigned long long
+slapi_entry_attr_get_ulonglong( const Slapi_Entry* e, const char *type)
+{
+ unsigned long long r = 0;
+ Slapi_Attr* attr;
+ slapi_entry_attr_find(e, type, &attr);
+ if (attr!=NULL)
+ {
+ Slapi_Value *v;
+ slapi_valueset_first_value( &attr->a_present_values, &v);
+ r = slapi_value_get_ulonglong(v);
+ }
+ return r;
+}
+
PRBool
slapi_entry_attr_get_bool( const Slapi_Entry* e, const char *type)
{
diff --git a/ldap/servers/slapd/slapi-plugin.h b/ldap/servers/slapd/slapi-plugin.h
index 396150a5..359e957e 100644
--- a/ldap/servers/slapd/slapi-plugin.h
+++ b/ldap/servers/slapd/slapi-plugin.h
@@ -256,6 +256,8 @@ int slapi_entry_attr_get_int(const Slapi_Entry* e, const char *type);
unsigned int slapi_entry_attr_get_uint(const Slapi_Entry* e, const char *type);
long slapi_entry_attr_get_long( const Slapi_Entry* e, const char *type);
unsigned long slapi_entry_attr_get_ulong( const Slapi_Entry* e, const char *type);
+long long slapi_entry_attr_get_longlong( const Slapi_Entry* e, const char *type);
+unsigned long long slapi_entry_attr_get_ulonglong( const Slapi_Entry* e, const char *type);
PRBool slapi_entry_attr_get_bool( const Slapi_Entry* e, const char *type);
void slapi_entry_attr_set_charptr(Slapi_Entry* e, const char *type, const char *value);
void slapi_entry_attr_set_int( Slapi_Entry* e, const char *type, int l);
@@ -459,6 +461,8 @@ int slapi_value_get_int(const Slapi_Value *value);
unsigned int slapi_value_get_uint(const Slapi_Value *value);
long slapi_value_get_long(const Slapi_Value *value);
unsigned long slapi_value_get_ulong(const Slapi_Value *value);
+long long slapi_value_get_longlong(const Slapi_Value *value);
+unsigned long long slapi_value_get_ulonglong(const Slapi_Value *value);
size_t slapi_value_get_length(const Slapi_Value *value);
int slapi_value_compare(const Slapi_Attr *a,const Slapi_Value *v1,const Slapi_Value *v2);
diff --git a/ldap/servers/slapd/value.c b/ldap/servers/slapd/value.c
index 909881cf..fd95bc92 100644
--- a/ldap/servers/slapd/value.c
+++ b/ldap/servers/slapd/value.c
@@ -477,6 +477,38 @@ slapi_value_get_ulong(const Slapi_Value *value)
return r;
}
+long long
+slapi_value_get_longlong(const Slapi_Value *value)
+{
+ long long r= 0;
+ if(NULL!=value)
+ {
+ char *p;
+ p = slapi_ch_malloc(value->bv.bv_len + 1);
+ memcpy (p, value->bv.bv_val, value->bv.bv_len);
+ p [value->bv.bv_len] = '\0';
+ r = atoll(p);
+ slapi_ch_free((void **)&p);
+ }
+ return r;
+}
+
+unsigned long long
+slapi_value_get_ulonglong(const Slapi_Value *value)
+{
+ unsigned long long r= 0;
+ if(NULL!=value)
+ {
+ char *p;
+ p = slapi_ch_malloc(value->bv.bv_len + 1);
+ memcpy (p, value->bv.bv_val, value->bv.bv_len);
+ p [value->bv.bv_len] = '\0';
+ r = (unsigned long long)atoll(p);
+ slapi_ch_free((void **)&p);
+ }
+ return r;
+}
+
int
slapi_value_compare(const Slapi_Attr *a,const Slapi_Value *v1,const Slapi_Value *v2)
{