diff options
author | Nathan Kinder <nkinder@redhat.com> | 2008-09-24 21:21:52 +0000 |
---|---|---|
committer | Nathan Kinder <nkinder@redhat.com> | 2008-09-24 21:21:52 +0000 |
commit | 7ebdd01f304ad5e6607e3dec98b5a3fd381fb7d8 (patch) | |
tree | bbe178c6bb6a43da48401aeba4ea6c4395d5233d | |
parent | fb3e6ac2f5d60d6184388c482cc4117b50955c99 (diff) | |
download | ds-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.in | 1 | ||||
-rw-r--r-- | ldap/servers/plugins/dna/dna.c | 1825 | ||||
-rw-r--r-- | ldap/servers/slapd/entry.c | 30 | ||||
-rw-r--r-- | ldap/servers/slapd/slapi-plugin.h | 4 | ||||
-rw-r--r-- | ldap/servers/slapd/value.c | 32 |
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) { |