ldap/servers
by thierry bordaz
ldap/servers/slapd/schema.c | 18 +++++++++---------
1 file changed, 9 insertions(+), 9 deletions(-)
New commits:
commit ab84ab89d6a31b49bd8166d8839a3c398ac22b67
Author: Thierry bordaz (tbordaz) <tbordaz(a)redhat.com>
Date: Fri Apr 25 11:53:27 2014 +0200
Ticket 47721 - Schema Replication Issue (follow up)
Fix Description:
Forget to change (char *) into (const char *)
https://fedorahosted.org/389/ticket/47721
Reviewed by: Rich Megginson
Platforms tested: F19
Flag Day: no
Doc impact: no
diff --git a/ldap/servers/slapd/schema.c b/ldap/servers/slapd/schema.c
index 447e0c4..8744a6d 100644
--- a/ldap/servers/slapd/schema.c
+++ b/ldap/servers/slapd/schema.c
@@ -205,7 +205,7 @@ static PRBool check_replicated_schema(LDAPMod **mods, char *replica_role, char *
static void modify_schema_get_new_definitions(Slapi_PBlock *pb, LDAPMod **mods, struct schema_mods_indexes **at_list, struct schema_mods_indexes **oc_list);
static void modify_schema_apply_new_definitions(char *attr_name, struct schema_mods_indexes *list);
static void modify_schema_free_new_definitions(struct schema_mods_indexes *def_list);
-static int schema_oc_compare(struct objclass *oc_1, struct objclass *oc_2, char *description);
+static int schema_oc_compare(struct objclass *oc_1, struct objclass *oc_2, const char *description);
static int schema_at_compare(struct asyntaxinfo *at_1, struct asyntaxinfo *at_2, char *message, int debug_logging);
static int schema_at_superset_check(struct asyntaxinfo *at_list1, struct asyntaxinfo *at_list2, char *message, int replica_role);
static int schema_at_superset_check_syntax_oids(char *oid1, char *oid2);
@@ -6175,15 +6175,15 @@ slapi_schema_get_superior_name(const char *ocname_or_oid)
static int
schema_oc_superset_check(struct objclass *oc_list1, struct objclass *oc_list2, char *message, int replica_role) {
struct objclass *oc_1, *oc_2;
- char *description;
+ const char *description;
int debug_logging = 0;
int rc;
int repl_schema_policy;
if (message == NULL) {
- description = "";
+ description = (const char *) "";
} else {
- description = message;
+ description = (const char *) message;
}
/* by default assum oc_list1 == oc_list2 */
@@ -6272,12 +6272,12 @@ schema_list_oc2learn(struct objclass *oc_remote_list, struct objclass *oc_local_
struct schema_mods_indexes *head = NULL, *mods_index;
int index = 0;
int repl_schema_policy;
- char *message;
+ const char *message;
if (replica_role == REPL_SCHEMA_AS_SUPPLIER) {
- message = "remote consumer";
+ message = (const char *) "remote consumer";
} else {
- message = "remote supplier";
+ message = (const char *) "remote supplier";
}
slapi_rwlock_rdlock( schema_policy_lock );
@@ -6377,7 +6377,7 @@ schema_list_attr2learn(struct asyntaxinfo *at_list_local, struct asyntaxinfo *at
* else it returns 0
*/
static PRBool
-schema_oc_compare_strict(struct objclass *oc_1, struct objclass *oc_2, char *description)
+schema_oc_compare_strict(struct objclass *oc_1, struct objclass *oc_2, const char *description)
{
int found;
int i,j;
@@ -6486,7 +6486,7 @@ schema_oc_compare_strict(struct objclass *oc_1, struct objclass *oc_2, char *des
* 0: if oc_1 and at_2 are equivalent
*/
static int
-schema_oc_compare(struct objclass *oc_1, struct objclass *oc_2, char *description)
+schema_oc_compare(struct objclass *oc_1, struct objclass *oc_2, const char *description)
{
if (schema_oc_compare_strict(oc_1, oc_2, description) > 0) {
return 1;
9 years, 5 months
dirsrvtests/tickets
by thierry bordaz
dirsrvtests/tickets/ticket47490_test.py | 37 +++++++--------------
dirsrvtests/tickets/ticket47553_single_aci_test.py | 6 +--
dirsrvtests/tickets/ticket47619_test.py | 3 -
3 files changed, 18 insertions(+), 28 deletions(-)
New commits:
commit 48253af53658fcc33e8fef44be2bc4da996b83fb
Author: Thierry bordaz (tbordaz) <tbordaz(a)redhat.com>
Date: Fri Apr 25 11:26:54 2014 +0200
Ticket 47721 - Schema Replication Issue (follow up + cleanup)
Fix Description:
Fix test cases 47490
Fix test cases 47553 (py.test fixture commented)
Fix test cases 47619 (task timeout not yet in lib389)
Forget to change (char *) into (const char *)
https://fedorahosted.org/389/ticket/47721
Reviewed by: Rich Megginson
Platforms tested: F19
Flag Day: no
Doc impact: no
diff --git a/dirsrvtests/tickets/ticket47490_test.py b/dirsrvtests/tickets/ticket47490_test.py
index 438723a..eee4cca 100644
--- a/dirsrvtests/tickets/ticket47490_test.py
+++ b/dirsrvtests/tickets/ticket47490_test.py
@@ -331,10 +331,10 @@ def test_ticket47490_one(topology):
def test_ticket47490_two(topology):
"""
- Summary: Extra OC Schema is NOT pushed - error
+ Summary: Extra OC Schema is pushed - (ticket 47721 allows to learn missing def)
If consumer schema is a superset (one extra OC) of supplier schema, then
- schema is not pushed and there is a message in the error log
+ schema is pushed and there is a message in the error log
State at startup
- supplier +masterNewOCA
- consumer +masterNewOCA
@@ -357,14 +357,15 @@ def test_ticket47490_two(topology):
consumer_schema_csn = topology.consumer.schema.get_schema_csn()
# Check the schemaCSN was NOT updated on the consumer
+ # with 47721, supplier learns the missing definition
log.debug("test_ticket47490_two master_schema_csn=%s", master_schema_csn)
log.debug("test_ticket47490_two consumer_schema_csn=%s", consumer_schema_csn)
assert master_schema_csn != consumer_schema_csn
# Check the error log of the supplier does not contain an error
+ # This message may happen during the learning phase
regex = re.compile("must not be overwritten \(set replication log for additional info\)")
res = pattern_errorlog(topology.master.errorlog_file, regex)
- assert res
def test_ticket47490_three(topology):
@@ -435,10 +436,10 @@ def test_ticket47490_four(topology):
def test_ticket47490_five(topology):
"""
- Summary: Same OC - extra MUST: Schema is NOT pushed - error
+ Summary: Same OC - extra MUST: Schema is pushed - (fix for 47721)
If consumer schema is a superset (OC with more MUST), then
- schema is not pushed and there is a message in the error log
+ schema is pushed (fix for 47721) and there is a message in the error log
State at startup
- supplier +masterNewOCA +masterNewOCB +consumerNewOCA
+must=telexnumber
@@ -466,21 +467,15 @@ def test_ticket47490_five(topology):
consumer_schema_csn = topology.consumer.schema.get_schema_csn()
# Check the schemaCSN was NOT updated on the consumer
+ # with 47721, supplier learns the missing definition
log.debug("test_ticket47490_five master_schema_csn=%s", master_schema_csn)
log.debug("ctest_ticket47490_five onsumer_schema_csn=%s", consumer_schema_csn)
assert master_schema_csn != consumer_schema_csn
- #Check that replication logging display additional message about 'telexNumber' not being
- # required in the master schema
- # This message appears before 'must not be overwritten' so it should be check first
- regex = re.compile("Attribute telexNumber is not required in 'consumerNewOCA' of the local supplier schema")
- res = pattern_errorlog(topology.master.errorlog_file, regex)
- assert res != None
-
# Check the error log of the supplier does not contain an error
+ # This message may happen during the learning phase
regex = re.compile("must not be overwritten \(set replication log for additional info\)")
res = pattern_errorlog(topology.master.errorlog_file, regex)
- assert res != None
def test_ticket47490_six(topology):
"""
@@ -517,6 +512,7 @@ def test_ticket47490_six(topology):
assert master_schema_csn == consumer_schema_csn
# Check the error log of the supplier does not contain an error
+ # This message may happen during the learning phase
regex = re.compile("must not be overwritten \(set replication log for additional info\)")
res = pattern_errorlog(topology.master.errorlog_file, regex)
assert res == None
@@ -563,10 +559,10 @@ def test_ticket47490_seven(topology):
def test_ticket47490_eight(topology):
"""
- Summary: Same OC - extra MAY: Schema is NOT pushed - error
+ Summary: Same OC - extra MAY: Schema is pushed (fix for 47721)
If consumer schema is a superset (OC with more MAY), then
- schema is not pushed and there is message in the error log
+ schema is pushed (fix for 47721) and there is message in the error log
State at startup
- supplier +masterNewOCA +masterNewOCB +consumerNewOCA +masterNewOCC
+must=telexnumber +must=telexnumber
@@ -593,21 +589,16 @@ def test_ticket47490_eight(topology):
consumer_schema_csn = topology.consumer.schema.get_schema_csn()
# Check the schemaCSN was not updated on the consumer
+ # with 47721, supplier learns the missing definition
log.debug("test_ticket47490_eight master_schema_csn=%s", master_schema_csn)
log.debug("ctest_ticket47490_eight onsumer_schema_csn=%s", consumer_schema_csn)
assert master_schema_csn != consumer_schema_csn
- #Check that replication logging display additional message about 'postOfficeBox' not being
- # allowed in the master schema
- # This message appears before 'must not be overwritten' so it should be check first
- regex = re.compile("Attribute postOfficeBox is not allowed in 'consumerNewOCA' of the local supplier schema")
- res = pattern_errorlog(topology.master.errorlog_file, regex)
- assert res != None
-
# Check the error log of the supplier does not contain an error
+ # This message may happen during the learning phase
regex = re.compile("must not be overwritten \(set replication log for additional info\)")
res = pattern_errorlog(topology.master.errorlog_file, regex)
- assert res != None
+
def test_ticket47490_nine(topology):
"""
diff --git a/dirsrvtests/tickets/ticket47553_single_aci_test.py b/dirsrvtests/tickets/ticket47553_single_aci_test.py
index 87f8bb3..cb62ee1 100644
--- a/dirsrvtests/tickets/ticket47553_single_aci_test.py
+++ b/dirsrvtests/tickets/ticket47553_single_aci_test.py
@@ -62,7 +62,7 @@ class TopologyMaster1Master2(object):
self.master2 = master2
-#(a)pytest.fixture(scope="module")
+(a)pytest.fixture(scope="module")
def topology(request):
'''
This fixture is used to create a replicated topology for the 'module'.
@@ -1140,8 +1140,8 @@ def run_isolated():
'''
global installation1_prefix
global installation2_prefix
- installation1_prefix = '/home/tbordaz/install'
- installation2_prefix = '/home/tbordaz/install'
+ installation1_prefix = None
+ installation2_prefix = None
topo = topology(True)
topo.master1.log.info("\n\n######################### Ticket 47553 ######################\n")
diff --git a/dirsrvtests/tickets/ticket47619_test.py b/dirsrvtests/tickets/ticket47619_test.py
index c3eae8d..a8cacf3 100644
--- a/dirsrvtests/tickets/ticket47619_test.py
+++ b/dirsrvtests/tickets/ticket47619_test.py
@@ -253,8 +253,7 @@ def test_ticket47619_reindex(topology):
'''
Reindex all the attributes in ATTRIBUTES
'''
- args = {TASK_WAIT: True,
- TASK_TIMEOUT: 10}
+ args = {TASK_WAIT: True}
for attr in ATTRIBUTES:
rc = topology.master.tasks.reindex(suffix=RETROCL_SUFFIX, attrname=attr, args=args)
assert rc == 0
9 years, 5 months
dirsrvtests/tickets ldap/servers
by thierry bordaz
dirsrvtests/tickets/ticket47721_test.py | 492 +++++++++
ldap/servers/plugins/replication/repl5_connection.c | 191 ++-
ldap/servers/slapd/proto-slap.h | 1
ldap/servers/slapd/schema.c | 1003 ++++++++++++++++----
4 files changed, 1437 insertions(+), 250 deletions(-)
New commits:
commit 1446b3e4250535f63479f9b59bd6b715af9d50ed
Author: Thierry bordaz (tbordaz) <tbordaz(a)redhat.com>
Date: Tue Mar 25 18:35:46 2014 +0100
Ticket 47721 - Schema Replication Issue
Bug Description:
During a replication session, a supplier may send its schema in order to overwrite a consumer schema.
Since https://fedorahosted.org/389/ticket/47490, a replica acting as a consumer rejects the schema
if it evaluates that its own schema is a superset of the received schema. Also with 47490, a replica
acting as a supplier will not send its schema if it evaluates that the consumer schema is a superset
of its own schema.
This creates an issue if the schema on both side contains definitions that the other side ignore, or
that extend the other side definition. In that case each side will keep its own schema.
Fix Description:
http://directory.fedoraproject.org/wiki/Replication_of_custom_schema_(tic...
When acting as a consumer, it may receive a schema during a replication session.
It then evaluates each definition (objectclasses/attritetypes) and if a definition
is new or extend a current definition, then it add the definitions in its schema (in 99user.ldif)
When acting as a supplier, it looks up the consumer schema. Whether it decides to send
its own schema or not, it first evaluates each definition (objectclasses/attritetypes) and
if a definition is new or extend a current definition, then it add the definitions in its schema (in 99user.ldif)
https://fedorahosted.org/389/ticket/47721
Reviewed by: Rich Megginson (Big thanks Rich for the review)
Platforms tested:
for 389-DS CI tests: F17, F19 (jenkins)
for Dogtag: manual test case master/clone - F19 (10.0.7) / F20 (10.1.1)
for IPA: F20 3.3.5 manual test case and unit tests
Flag Day: no
diff --git a/dirsrvtests/tickets/ticket47721_test.py b/dirsrvtests/tickets/ticket47721_test.py
new file mode 100644
index 0000000..0d6cd8b
--- /dev/null
+++ b/dirsrvtests/tickets/ticket47721_test.py
@@ -0,0 +1,492 @@
+'''
+Created on Nov 7, 2013
+
+@author: tbordaz
+'''
+import os
+import sys
+import time
+import ldap
+import logging
+import socket
+import time
+import logging
+import pytest
+import re
+from lib389 import DirSrv, Entry, tools
+from lib389.tools import DirSrvTools
+from lib389._constants import *
+from lib389.properties import *
+from constants import *
+from lib389._constants import REPLICAROLE_MASTER
+
+logging.getLogger(__name__).setLevel(logging.DEBUG)
+log = logging.getLogger(__name__)
+
+#
+# important part. We can deploy Master1 and Master2 on different versions
+#
+installation1_prefix = None
+installation2_prefix = None
+
+SCHEMA_DN = "cn=schema"
+TEST_REPL_DN = "cn=test_repl, %s" % SUFFIX
+OC_NAME = 'OCticket47721'
+OC_OID_EXT = 2
+MUST = "(postalAddress $ postalCode)"
+MAY = "(member $ street)"
+
+OC2_NAME = 'OC2ticket47721'
+OC2_OID_EXT = 3
+MUST_2 = "(postalAddress $ postalCode)"
+MAY_2 = "(member $ street)"
+
+REPL_SCHEMA_POLICY_CONSUMER = "cn=consumerUpdatePolicy,cn=replSchema,cn=config"
+REPL_SCHEMA_POLICY_SUPPLIER = "cn=supplierUpdatePolicy,cn=replSchema,cn=config"
+
+OTHER_NAME = 'other_entry'
+MAX_OTHERS = 10
+
+BIND_NAME = 'bind_entry'
+BIND_DN = 'cn=%s, %s' % (BIND_NAME, SUFFIX)
+BIND_PW = 'password'
+
+ENTRY_NAME = 'test_entry'
+ENTRY_DN = 'cn=%s, %s' % (ENTRY_NAME, SUFFIX)
+ENTRY_OC = "top person %s" % OC_NAME
+
+BASE_OID = "1.2.3.4.5.6.7.8.9.10"
+
+def _add_custom_at_definition(name='ATticket47721'):
+ new_at = "( %s-oid NAME '%s' DESC 'test AT ticket 47721' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN ( 'Test 47721' 'user defined' ) )" % (name, name)
+ return new_at
+
+def _chg_std_at_defintion():
+ new_at = "( 2.16.840.1.113730.3.1.569 NAME 'cosPriority' DESC 'Netscape defined attribute type' SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 X-ORIGIN 'Netscape Directory Server' )"
+ return new_at
+
+def _add_custom_oc_defintion(name='OCticket47721'):
+ new_oc = "( %s-oid NAME '%s' DESC 'An group of related automount objects' SUP top STRUCTURAL MUST ou X-ORIGIN 'draft-howard-rfc2307bis' )" % (name, name)
+ return new_oc
+
+def _chg_std_oc_defintion():
+ new_oc = "( 5.3.6.1.1.1.2.0 NAME 'trustAccount' DESC 'Sets trust accounts information' SUP top AUXILIARY MUST trustModel MAY ( accessTo $ ou ) X-ORIGIN 'nss_ldap/pam_ldap' )"
+ return new_oc
+
+
+class TopologyMaster1Master2(object):
+ def __init__(self, master1, master2):
+ master1.open()
+ self.master1 = master1
+
+ master2.open()
+ self.master2 = master2
+
+
+(a)pytest.fixture(scope="module")
+def topology(request):
+ '''
+ This fixture is used to create a replicated topology for the 'module'.
+ The replicated topology is MASTER1 <-> Master2.
+ At the beginning, It may exists a master2 instance and/or a master2 instance.
+ It may also exists a backup for the master1 and/or the master2.
+
+ Principle:
+ If master1 instance exists:
+ restart it
+ If master2 instance exists:
+ restart it
+ If backup of master1 AND backup of master2 exists:
+ create or rebind to master1
+ create or rebind to master2
+
+ restore master1 from backup
+ restore master2 from backup
+ else:
+ Cleanup everything
+ remove instances
+ remove backups
+ Create instances
+ Initialize replication
+ Create backups
+ '''
+ global installation1_prefix
+ global installation2_prefix
+
+ # allocate master1 on a given deployement
+ master1 = DirSrv(verbose=False)
+ if installation1_prefix:
+ args_instance[SER_DEPLOYED_DIR] = installation1_prefix
+
+ # Args for the master1 instance
+ args_instance[SER_HOST] = HOST_MASTER_1
+ args_instance[SER_PORT] = PORT_MASTER_1
+ args_instance[SER_SERVERID_PROP] = SERVERID_MASTER_1
+ args_master = args_instance.copy()
+ master1.allocate(args_master)
+
+ # allocate master1 on a given deployement
+ master2 = DirSrv(verbose=False)
+ if installation2_prefix:
+ args_instance[SER_DEPLOYED_DIR] = installation2_prefix
+
+ # Args for the consumer instance
+ args_instance[SER_HOST] = HOST_MASTER_2
+ args_instance[SER_PORT] = PORT_MASTER_2
+ args_instance[SER_SERVERID_PROP] = SERVERID_MASTER_2
+ args_master = args_instance.copy()
+ master2.allocate(args_master)
+
+
+ # Get the status of the backups
+ backup_master1 = master1.checkBackupFS()
+ backup_master2 = master2.checkBackupFS()
+
+ # Get the status of the instance and restart it if it exists
+ instance_master1 = master1.exists()
+ if instance_master1:
+ master1.stop(timeout=10)
+ master1.start(timeout=10)
+
+ instance_master2 = master2.exists()
+ if instance_master2:
+ master2.stop(timeout=10)
+ master2.start(timeout=10)
+
+ if backup_master1 and backup_master2:
+ # The backups exist, assuming they are correct
+ # we just re-init the instances with them
+ if not instance_master1:
+ master1.create()
+ # Used to retrieve configuration information (dbdir, confdir...)
+ master1.open()
+
+ if not instance_master2:
+ master2.create()
+ # Used to retrieve configuration information (dbdir, confdir...)
+ master2.open()
+
+ # restore master1 from backup
+ master1.stop(timeout=10)
+ master1.restoreFS(backup_master1)
+ master1.start(timeout=10)
+
+ # restore master2 from backup
+ master2.stop(timeout=10)
+ master2.restoreFS(backup_master2)
+ master2.start(timeout=10)
+ else:
+ # We should be here only in two conditions
+ # - This is the first time a test involve master-consumer
+ # so we need to create everything
+ # - Something weird happened (instance/backup destroyed)
+ # so we discard everything and recreate all
+
+ # Remove all the backups. So even if we have a specific backup file
+ # (e.g backup_master) we clear all backups that an instance my have created
+ if backup_master1:
+ master1.clearBackupFS()
+ if backup_master2:
+ master2.clearBackupFS()
+
+ # Remove all the instances
+ if instance_master1:
+ master1.delete()
+ if instance_master2:
+ master2.delete()
+
+ # Create the instances
+ master1.create()
+ master1.open()
+ master2.create()
+ master2.open()
+
+ #
+ # Now prepare the Master-Consumer topology
+ #
+ # First Enable replication
+ master1.replica.enableReplication(suffix=SUFFIX, role=REPLICAROLE_MASTER, replicaId=REPLICAID_MASTER_1)
+ master2.replica.enableReplication(suffix=SUFFIX, role=REPLICAROLE_MASTER, replicaId=REPLICAID_MASTER_2)
+
+ # Initialize the supplier->consumer
+
+ properties = {RA_NAME: r'meTo_$host:$port',
+ RA_BINDDN: defaultProperties[REPLICATION_BIND_DN],
+ RA_BINDPW: defaultProperties[REPLICATION_BIND_PW],
+ RA_METHOD: defaultProperties[REPLICATION_BIND_METHOD],
+ RA_TRANSPORT_PROT: defaultProperties[REPLICATION_TRANSPORT]}
+ repl_agreement = master1.agreement.create(suffix=SUFFIX, host=master2.host, port=master2.port, properties=properties)
+
+ if not repl_agreement:
+ log.fatal("Fail to create a replica agreement")
+ sys.exit(1)
+
+ log.debug("%s created" % repl_agreement)
+
+ properties = {RA_NAME: r'meTo_$host:$port',
+ RA_BINDDN: defaultProperties[REPLICATION_BIND_DN],
+ RA_BINDPW: defaultProperties[REPLICATION_BIND_PW],
+ RA_METHOD: defaultProperties[REPLICATION_BIND_METHOD],
+ RA_TRANSPORT_PROT: defaultProperties[REPLICATION_TRANSPORT]}
+ master2.agreement.create(suffix=SUFFIX, host=master1.host, port=master1.port, properties=properties)
+
+ master1.agreement.init(SUFFIX, HOST_MASTER_2, PORT_MASTER_2)
+ master1.waitForReplInit(repl_agreement)
+
+ # Check replication is working fine
+ master1.add_s(Entry((TEST_REPL_DN, {
+ 'objectclass': "top person".split(),
+ 'sn': 'test_repl',
+ 'cn': 'test_repl'})))
+ loop = 0
+ while loop <= 10:
+ try:
+ ent = master2.getEntry(TEST_REPL_DN, ldap.SCOPE_BASE, "(objectclass=*)")
+ break
+ except ldap.NO_SUCH_OBJECT:
+ time.sleep(1)
+ loop += 1
+
+ # Time to create the backups
+ master1.stop(timeout=10)
+ master1.backupfile = master1.backupFS()
+ master1.start(timeout=10)
+
+ master2.stop(timeout=10)
+ master2.backupfile = master2.backupFS()
+ master2.start(timeout=10)
+
+ #
+ # Here we have two instances master and consumer
+ # with replication working. Either coming from a backup recovery
+ # or from a fresh (re)init
+ # Time to return the topology
+ return TopologyMaster1Master2(master1, master2)
+
+
+def test_ticket47721_init(topology):
+ """
+ It adds
+ - Objectclass with MAY 'member'
+ - an entry ('bind_entry') with which we bind to test the 'SELFDN' operation
+ It deletes the anonymous aci
+
+ """
+
+
+
+ # entry used to bind with
+ topology.master1.log.info("Add %s" % BIND_DN)
+ topology.master1.add_s(Entry((BIND_DN, {
+ 'objectclass': "top person".split(),
+ 'sn': BIND_NAME,
+ 'cn': BIND_NAME,
+ 'userpassword': BIND_PW})))
+
+ # enable acl error logging
+ mod = [(ldap.MOD_REPLACE, 'nsslapd-errorlog-level', str(8192))] # ACL + REPL
+ topology.master1.modify_s(DN_CONFIG, mod)
+ topology.master2.modify_s(DN_CONFIG, mod)
+
+ # add dummy entries
+ for cpt in range(MAX_OTHERS):
+ name = "%s%d" % (OTHER_NAME, cpt)
+ topology.master1.add_s(Entry(("cn=%s,%s" % (name, SUFFIX), {
+ 'objectclass': "top person".split(),
+ 'sn': name,
+ 'cn': name})))
+def test_ticket47721_0(topology):
+ dn = "cn=%s0,%s" % (OTHER_NAME, SUFFIX)
+ loop = 0
+ while loop <= 10:
+ try:
+ ent = topology.master2.getEntry(dn, ldap.SCOPE_BASE, "(objectclass=*)")
+ break
+ except ldap.NO_SUCH_OBJECT:
+ time.sleep(1)
+ loop += 1
+ assert loop <= 10
+
+def test_ticket47721_1(topology):
+ topology.master1.log.info("Attach debugger\n\n" )
+ #time.sleep(30)
+
+ new = _add_custom_at_definition()
+ topology.master1.log.info("Add (M2) %s " % new)
+ topology.master2.schema.add_schema('attributetypes', new)
+
+ new = _chg_std_at_defintion()
+ topology.master1.log.info("Chg (M2) %s " % new)
+ topology.master2.schema.add_schema('attributetypes', new)
+
+ new = _add_custom_oc_defintion()
+ topology.master1.log.info("Add (M2) %s " % new)
+ topology.master2.schema.add_schema('objectClasses', new)
+
+ new = _chg_std_oc_defintion()
+ topology.master1.log.info("Chg (M2) %s " % new)
+ topology.master2.schema.add_schema('objectClasses', new)
+
+ mod = [(ldap.MOD_REPLACE, 'description', 'Hello world 1')]
+ dn = "cn=%s0,%s" % (OTHER_NAME, SUFFIX)
+ topology.master2.modify_s(dn, mod)
+
+ loop = 0
+ while loop <= 10:
+ try:
+ ent = topology.master1.getEntry(dn, ldap.SCOPE_BASE, "(objectclass=*)")
+ if ent.hasAttr('description') and (ent.getValue('description') == 'Hello world 1'):
+ break
+ except ldap.NO_SUCH_OBJECT:
+ loop += 1
+ time.sleep(1)
+ assert loop <= 10
+
+def test_ticket47721_2(topology):
+ mod = [(ldap.MOD_REPLACE, 'description', 'Hello world 2')]
+ dn = "cn=%s0,%s" % (OTHER_NAME, SUFFIX)
+ topology.master1.modify_s(dn, mod)
+
+ loop = 0
+ while loop <= 10:
+ try:
+ ent = topology.master2.getEntry(dn, ldap.SCOPE_BASE, "(objectclass=*)")
+ if ent.hasAttr('description') and (ent.getValue('description') == 'Hello world 2'):
+ break
+ except ldap.NO_SUCH_OBJECT:
+ loop += 1
+ time.sleep(1)
+ assert loop <= 10
+
+ schema_csn_master1 = topology.master1.schema.get_schema_csn()
+ schema_csn_master2 = topology.master2.schema.get_schema_csn()
+ assert schema_csn_master1 != None
+ assert schema_csn_master1 == schema_csn_master2
+
+def test_ticket47721_3(topology):
+ '''
+ Check that the supplier can update its schema from consumer schema
+ Update M2 schema, then trigger a replication M1->M2
+ '''
+ # stop RA M2->M1, so that M1 can only learn being a supplier
+ ents = topology.master2.agreement.list(suffix=SUFFIX)
+ assert len(ents) == 1
+ topology.master2.agreement.pause(ents[0].dn)
+
+ new = _add_custom_at_definition('ATtest3')
+ topology.master1.log.info("Update schema (M2) %s " % new)
+ topology.master2.schema.add_schema('attributetypes', new)
+
+ new = _add_custom_oc_defintion('OCtest3')
+ topology.master1.log.info("Update schema (M2) %s " % new)
+ topology.master2.schema.add_schema('objectClasses', new)
+
+ mod = [(ldap.MOD_REPLACE, 'description', 'Hello world 3')]
+ dn = "cn=%s0,%s" % (OTHER_NAME, SUFFIX)
+ topology.master1.modify_s(dn, mod)
+
+ loop = 0
+ while loop <= 10:
+ try:
+ ent = topology.master2.getEntry(dn, ldap.SCOPE_BASE, "(objectclass=*)")
+ if ent.hasAttr('description') and (ent.getValue('description') == 'Hello world 3'):
+ break
+ except ldap.NO_SUCH_OBJECT:
+ loop += 1
+ time.sleep(1)
+ assert loop <= 10
+
+ schema_csn_master1 = topology.master1.schema.get_schema_csn()
+ schema_csn_master2 = topology.master2.schema.get_schema_csn()
+ assert schema_csn_master1 != None
+ # schema csn on M2 is larger that on M1. M1 only took the new definitions
+ assert schema_csn_master1 != schema_csn_master2
+
+def test_ticket47721_4(topology):
+ '''
+ Here M2->M1 agreement is disabled.
+ with test_ticket47721_3, M1 schema and M2 should be identical BUT
+ the nsschemacsn is M2>M1. But as the RA M2->M1 is disabled, M1 keeps its schemacsn.
+ Update schema on M2 (nsschemaCSN update), update M2. Check they have the same schemacsn
+ '''
+ new = _add_custom_at_definition('ATtest4')
+ topology.master1.log.info("Update schema (M1) %s " % new)
+ topology.master1.schema.add_schema('attributetypes', new)
+
+ new = _add_custom_oc_defintion('OCtest4')
+ topology.master1.log.info("Update schema (M1) %s " % new)
+ topology.master1.schema.add_schema('objectClasses', new)
+
+ topology.master1.log.info("trigger replication M1->M2: to update the schema")
+ mod = [(ldap.MOD_REPLACE, 'description', 'Hello world 4')]
+ dn = "cn=%s0,%s" % (OTHER_NAME, SUFFIX)
+ topology.master1.modify_s(dn, mod)
+
+ loop = 0
+ while loop <= 10:
+ try:
+ ent = topology.master2.getEntry(dn, ldap.SCOPE_BASE, "(objectclass=*)")
+ if ent.hasAttr('description') and (ent.getValue('description') == 'Hello world 4'):
+ break
+ except ldap.NO_SUCH_OBJECT:
+ loop += 1
+ time.sleep(1)
+ assert loop <= 10
+
+ topology.master1.log.info("trigger replication M1->M2: to push the schema")
+ mod = [(ldap.MOD_REPLACE, 'description', 'Hello world 5')]
+ dn = "cn=%s0,%s" % (OTHER_NAME, SUFFIX)
+ topology.master1.modify_s(dn, mod)
+
+ loop = 0
+ while loop <= 10:
+ try:
+ ent = topology.master2.getEntry(dn, ldap.SCOPE_BASE, "(objectclass=*)")
+ if ent.hasAttr('description') and (ent.getValue('description') == 'Hello world 5'):
+ break
+ except ldap.NO_SUCH_OBJECT:
+ loop += 1
+ time.sleep(1)
+ assert loop <= 10
+
+ schema_csn_master1 = topology.master1.schema.get_schema_csn()
+ schema_csn_master2 = topology.master2.schema.get_schema_csn()
+ assert schema_csn_master1 != None
+ assert schema_csn_master1 == schema_csn_master2
+
+def test_ticket47721_final(topology):
+ topology.master1.stop(timeout=10)
+ topology.master2.stop(timeout=10)
+
+def run_isolated():
+ '''
+ run_isolated is used to run these test cases independently of a test scheduler (xunit, py.test..)
+ To run isolated without py.test, you need to
+ - edit this file and comment '@pytest.fixture' line before 'topology' function.
+ - set the installation prefix
+ - run this program
+ '''
+ global installation1_prefix
+ global installation2_prefix
+ installation1_prefix = None
+ installation2_prefix = None
+
+ topo = topology(True)
+ topo.master1.log.info("\n\n######################### Ticket 47721 ######################\n")
+ test_ticket47721_init(topo)
+
+ test_ticket47721_0(topo)
+ test_ticket47721_1(topo)
+ test_ticket47721_2(topo)
+ test_ticket47721_3(topo)
+ test_ticket47721_4(topo)
+ sys.exit(0)
+
+ test_ticket47721_final(topo)
+
+
+
+
+if __name__ == '__main__':
+ run_isolated()
+
diff --git a/ldap/servers/plugins/replication/repl5_connection.c b/ldap/servers/plugins/replication/repl5_connection.c
index 3d29a79..c004bfb 100644
--- a/ldap/servers/plugins/replication/repl5_connection.c
+++ b/ldap/servers/plugins/replication/repl5_connection.c
@@ -99,6 +99,7 @@ typedef struct repl_connection
/*** from proto-slap.h ***/
int schema_objectclasses_superset_check(struct berval **remote_schema, char *type);
int schema_attributetypes_superset_check(struct berval **remote_schema, char *type);
+void supplier_learn_new_definitions(struct berval **objectclasses, struct berval **attributetypes);
/* Controls we add on every outbound operation */
static LDAPControl manageDSAITControl = {LDAP_CONTROL_MANAGEDSAIT, {0, ""}, '\0'};
@@ -1518,6 +1519,84 @@ attribute_string_value_present(LDAP *ld, LDAPMessage *entry, const char *type,
return return_value;
}
+/* It returns the objectclasses and attributetypes of the remote schema
+ * in the form of berval arrays.
+ * In case of success, the caller must free those berval arrays with ber_bvecfree
+ * */
+static ConnResult
+supplier_read_consumer_definitions(Repl_Connection *conn, struct berval ***remote_objectclasses, struct berval ***remote_attributetypes)
+{
+ ConnResult return_value = CONN_OPERATION_SUCCESS;
+ struct berval **remote_schema_objectclasses_bervals = NULL;
+ struct berval **remote_schema_attributetypes_bervals = NULL;
+
+ *remote_objectclasses = NULL;
+ *remote_attributetypes = NULL;
+
+ /* read the objectclass then the attribytetype from the remote schema */
+ return_value = conn_read_entry_attribute(conn, "cn=schema", "objectclasses", &remote_schema_objectclasses_bervals);
+ if (return_value == CONN_OPERATION_SUCCESS) {
+ *remote_objectclasses = remote_schema_objectclasses_bervals;
+
+ return_value = conn_read_entry_attribute(conn, "cn=schema", "attributetypes", &remote_schema_attributetypes_bervals);
+ if (return_value == CONN_OPERATION_SUCCESS) {
+ *remote_attributetypes = remote_schema_attributetypes_bervals;
+ } else {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "%s: Fail to retrieve the remote schema attributetypes\n",
+ agmt_get_long_name(conn->agmt));
+ }
+ } else {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "%s: Fail to retrieve the remote schema objectclasses\n",
+ agmt_get_long_name(conn->agmt));
+ }
+
+ if (return_value != CONN_OPERATION_SUCCESS) {
+ /* in case of failure free everything */
+ *remote_objectclasses = NULL;
+ if (remote_schema_objectclasses_bervals) {
+ ber_bvecfree(remote_schema_objectclasses_bervals);
+ }
+
+ *remote_attributetypes = NULL;
+ if (remote_schema_attributetypes_bervals) {
+ ber_bvecfree(remote_schema_attributetypes_bervals);
+ }
+ }
+ return return_value;
+
+}
+//
+
+static PRBool
+update_consumer_schema(Repl_Connection *conn)
+{
+ struct berval **remote_schema_objectclasses_bervals = NULL;
+ struct berval **remote_schema_attributetypes_bervals = NULL;
+ PRBool ok_to_send_schema = PR_TRUE;
+
+ if (supplier_read_consumer_definitions(conn, &remote_schema_objectclasses_bervals, &remote_schema_attributetypes_bervals) == CONN_OPERATION_SUCCESS) {
+ if (schema_objectclasses_superset_check(remote_schema_objectclasses_bervals, OC_SUPPLIER) ||
+ schema_attributetypes_superset_check(remote_schema_attributetypes_bervals, OC_SUPPLIER)) {
+
+ /* The consumer contains definitions that needs to be learned */
+ supplier_learn_new_definitions(remote_schema_objectclasses_bervals, remote_schema_attributetypes_bervals);
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "[S] Schema %s must not be overwritten (set replication log for additional info)\n",
+ agmt_get_long_name(conn->agmt));
+ ok_to_send_schema = PR_FALSE;
+ }
+ ber_bvecfree(remote_schema_objectclasses_bervals);
+ ber_bvecfree(remote_schema_attributetypes_bervals);
+ } else {
+ /* We can not be sure, be conservative and not send the schema */
+ ok_to_send_schema = PR_FALSE;
+ }
+ return ok_to_send_schema;
+
+}
+
/*
* Read the remote server's schema entry, then read the local schema entry,
* and compare the nsschemacsn attribute. If the local csn is newer, or
@@ -1568,79 +1647,45 @@ conn_push_schema(Repl_Connection *conn, CSN **remotecsn)
return_value = CONN_SCHEMA_NO_UPDATE_NEEDED;
}
else
- {
- struct berval **remote_schema_csn_bervals = NULL;
- /* Get remote server's schema */
- return_value = conn_read_entry_attribute(conn, "cn=schema", nsschemacsn,
- &remote_schema_csn_bervals);
- if (CONN_OPERATION_SUCCESS == return_value)
- {
- if (NULL != remote_schema_csn_bervals && NULL != remote_schema_csn_bervals[0])
- {
- char remotecsnstr[CSN_STRSIZE + 1] = {0};
- memcpy(remotecsnstr, remote_schema_csn_bervals[0]->bv_val,
- remote_schema_csn_bervals[0]->bv_len);
- remotecsnstr[remote_schema_csn_bervals[0]->bv_len] = '\0';
- *remotecsn = csn_new_by_string(remotecsnstr);
- if (*remotecsn && (csn_compare(localcsn, *remotecsn) <= 0))
- {
- return_value = CONN_SCHEMA_NO_UPDATE_NEEDED;
- }
- /* Need to free the remote_schema_csn_bervals */
- ber_bvecfree(remote_schema_csn_bervals);
- }
- if (return_value != CONN_SCHEMA_NO_UPDATE_NEEDED) {
- struct berval **remote_schema_objectclasses_bervals = NULL;
- struct berval **remote_schema_attributetypes_bervals = NULL;
- /* before pushing the schema do some checking */
-
- /* First objectclasses */
- return_value = conn_read_entry_attribute(conn, "cn=schema", "objectclasses",
- &remote_schema_objectclasses_bervals);
- if (return_value == CONN_OPERATION_SUCCESS) {
- /* Check if the consumer objectclasses are a superset of the local supplier schema */
- if (schema_objectclasses_superset_check(remote_schema_objectclasses_bervals, OC_SUPPLIER)) {
- slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
- "Schema %s must not be overwritten (set replication log for additional info)\n",
- agmt_get_long_name(conn->agmt));
- return_value = CONN_OPERATION_FAILED;
- }
- if(remote_schema_objectclasses_bervals){
- ber_bvecfree(remote_schema_objectclasses_bervals);
- }
- } else {
- slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
- "%s: Fail to retrieve the remote schema objectclasses\n",
- agmt_get_long_name(conn->agmt));
- }
- if (return_value == CONN_OPERATION_SUCCESS) {
- /* Next attribute types */
- return_value = conn_read_entry_attribute(conn, "cn=schema", "attributetypes",
- &remote_schema_attributetypes_bervals);
- if (return_value == CONN_OPERATION_SUCCESS) {
- /* Check if the consumer attributes are a superset of the local supplier schema */
- if (schema_attributetypes_superset_check(remote_schema_attributetypes_bervals, OC_SUPPLIER)) {
- slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
- "Schema %s must not be overwritten (set replication log for additional info)\n",
- agmt_get_long_name(conn->agmt));
- return_value = CONN_OPERATION_FAILED;
- }
- if(remote_schema_attributetypes_bervals){
- ber_bvecfree(remote_schema_attributetypes_bervals);
- }
- } else {
- slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
- "%s: Fail to retrieve the remote schema attribute types\n",
- agmt_get_long_name(conn->agmt));
- }
- }
- /* In case of success, possibly log a message */
- if (return_value == CONN_OPERATION_SUCCESS) {
- slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
- "Schema checking successful: ok to push the schema (%s)\n", agmt_get_long_name(conn->agmt));
- }
- }
- }
+ {
+ if (!update_consumer_schema(conn)) {
+ /* At least one schema definition (attributetypes/objectclasses) of the consumer
+ * is a superset of the supplier.
+ * It is not possible push the schema immediately.
+ * Note: in update_consumer_schema, it may update the local supplier schema.
+ * So it could be possible that a second attempt (right now) of update_consumer_schema
+ * would be successful
+ */
+ if (!update_consumer_schema(conn)) {
+ return_value = CONN_OPERATION_FAILED;
+ }
+ }
+ if (return_value == CONN_OPERATION_SUCCESS) {
+ struct berval **remote_schema_csn_bervals = NULL;
+
+ /* Get remote server's schema */
+ return_value = conn_read_entry_attribute(conn, "cn=schema", nsschemacsn,
+ &remote_schema_csn_bervals);
+ if (CONN_OPERATION_SUCCESS == return_value) {
+ if (NULL != remote_schema_csn_bervals && NULL != remote_schema_csn_bervals[0]) {
+ char remotecsnstr[CSN_STRSIZE + 1] = {0};
+ memcpy(remotecsnstr, remote_schema_csn_bervals[0]->bv_val,
+ remote_schema_csn_bervals[0]->bv_len);
+ remotecsnstr[remote_schema_csn_bervals[0]->bv_len] = '\0';
+ *remotecsn = csn_new_by_string(remotecsnstr);
+ if (*remotecsn && (csn_compare(localcsn, *remotecsn) <= 0)) {
+ return_value = CONN_SCHEMA_NO_UPDATE_NEEDED;
+ }
+ /* Need to free the remote_schema_csn_bervals */
+ ber_bvecfree(remote_schema_csn_bervals);
+ }
+ if (return_value == CONN_OPERATION_SUCCESS) {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
+ "Schema checking successful: ok to push the schema (%s)\n", agmt_get_long_name(conn->agmt));
+ }
+
+ }
+ }
}
}
if (CONN_OPERATION_SUCCESS == return_value)
diff --git a/ldap/servers/slapd/proto-slap.h b/ldap/servers/slapd/proto-slap.h
index 760a728..4c3e517 100644
--- a/ldap/servers/slapd/proto-slap.h
+++ b/ldap/servers/slapd/proto-slap.h
@@ -1028,6 +1028,7 @@ void schema_free_extensions(schemaext *extensions);
schemaext *schema_copy_extensions(schemaext *extensions);
int schema_objectclasses_superset_check(struct berval **remote_schema, char *type);
int schema_attributetypes_superset_check(struct berval **remote_schema, char *type);
+void supplier_learn_new_definitions(struct berval **objectclasses, struct berval **attributetypes);
/*
* schemaparse.c
diff --git a/ldap/servers/slapd/schema.c b/ldap/servers/slapd/schema.c
index d6aae58..447e0c4 100644
--- a/ldap/servers/slapd/schema.c
+++ b/ldap/servers/slapd/schema.c
@@ -61,6 +61,7 @@ typedef struct sizedbuffer
size_t size;
} sizedbuffer;
+
typedef char *(*schema_strstr_fn_t)( const char *big, const char *little);
/*
@@ -135,6 +136,12 @@ typedef struct repl_schema_policy {
schema_item_t *attributes;
} repl_schema_policy_t;
+struct schema_mods_indexes {
+ int index;
+ char *new_value;
+ struct schema_mods_indexes *next;
+};
+
/*
* pschemadse is based on the general implementation in dse
*/
@@ -194,6 +201,12 @@ static void schema_create_errormsg( char *errorbuf, size_t errorbufsize,
#else
;
#endif
+static PRBool check_replicated_schema(LDAPMod **mods, char *replica_role, char **attr_name);
+static void modify_schema_get_new_definitions(Slapi_PBlock *pb, LDAPMod **mods, struct schema_mods_indexes **at_list, struct schema_mods_indexes **oc_list);
+static void modify_schema_apply_new_definitions(char *attr_name, struct schema_mods_indexes *list);
+static void modify_schema_free_new_definitions(struct schema_mods_indexes *def_list);
+static int schema_oc_compare(struct objclass *oc_1, struct objclass *oc_2, char *description);
+static int schema_at_compare(struct asyntaxinfo *at_1, struct asyntaxinfo *at_2, char *message, int debug_logging);
static int schema_at_superset_check(struct asyntaxinfo *at_list1, struct asyntaxinfo *at_list2, char *message, int replica_role);
static int schema_at_superset_check_syntax_oids(char *oid1, char *oid2);
static int schema_at_superset_check_mr(struct asyntaxinfo *a1, struct asyntaxinfo *a2, char *info);
@@ -2072,6 +2085,46 @@ modify_schema_dse (Slapi_PBlock *pb, Slapi_Entry *entryBefore, Slapi_Entry *entr
slapi_pblock_get( pb, SLAPI_MODIFY_MODS, &mods );
slapi_pblock_get( pb, SLAPI_IS_REPLICATED_OPERATION, &is_replicated_operation);
+
+ /* In case we receive a schema from a supplier, check if we can accept it
+ * (it is a superset of our own schema).
+ * If it is not a superset, pick up what could extend our schema and return
+ */
+ if (is_replicated_operation) {
+ char *attr_name = NULL;
+ struct schema_mods_indexes *at_list = NULL;
+ struct schema_mods_indexes *oc_list = NULL;
+
+ if (!check_replicated_schema(mods, OC_CONSUMER, &attr_name)) {
+
+ /* we will refuse to apply this schema
+ * Try to capture in it what would extends our own schema
+ */
+ modify_schema_get_new_definitions(pb, mods, &at_list, &oc_list);
+ if (at_list) {
+ modify_schema_apply_new_definitions("attributetypes", at_list);
+ }
+ if (oc_list) {
+ modify_schema_apply_new_definitions("objectclasses", oc_list);
+ }
+
+ /* No need to hold the lock for these list that are local */
+ modify_schema_free_new_definitions(at_list);
+ modify_schema_free_new_definitions(oc_list);
+
+ /* now return, we will not apply that schema */
+ schema_create_errormsg( returntext, SLAPI_DSE_RETURNTEXT_SIZE,
+ schema_errprefix_generic, attr_name,
+ "Replace is not possible, local consumer schema is a superset of the supplier" );
+ slapi_log_error(SLAPI_LOG_FATAL, "schema",
+ "[C] Local %s must not be overwritten (set replication log for additional info)\n",
+ attr_name);
+ *returncode = LDAP_UNWILLING_TO_PERFORM;
+ return (SLAPI_DSE_CALLBACK_ERROR);
+ }
+ }
+
+
schema_dse_lock_write();
/*
@@ -2145,62 +2198,23 @@ modify_schema_dse (Slapi_PBlock *pb, Slapi_Entry *entryBefore, Slapi_Entry *entr
"Replace is not allowed on the subschema subentry" );
rc = SLAPI_DSE_CALLBACK_ERROR;
} else {
- if (strcasecmp (mods[i]->mod_type, "attributetypes") == 0) {
- if (is_replicated_operation) {
+ if (strcasecmp (mods[i]->mod_type, "attributetypes") == 0) {
/*
- * before accepting the schema checks if the local consumer schema is not
- * a superset of the supplier schema
- */
- if (schema_attributetypes_superset_check(mods[i]->mod_bvalues, OC_CONSUMER)) {
- schema_create_errormsg( returntext, SLAPI_DSE_RETURNTEXT_SIZE,
- schema_errprefix_generic, mods[i]->mod_type,
- "Replace is not possible, local consumer schema is a superset of the supplier" );
- slapi_log_error(SLAPI_LOG_FATAL, "schema",
- "Local %s must not be overwritten (set replication log for additional info)\n",
- mods[i]->mod_type);
- *returncode = LDAP_UNWILLING_TO_PERFORM;
- } else {
- /*
- * Replace all attributes
- */
- *returncode = schema_replace_attributes( pb, mods[i], returntext,
- SLAPI_DSE_RETURNTEXT_SIZE );
- }
- } else {
- /*
- * Replace all objectclasses
+ * Replace all attributetypes
+ * It has already been checked that if it was a replicated schema
+ * it is a superset of the current schema. That is fine to apply the mods
*/
*returncode = schema_replace_attributes( pb, mods[i], returntext,
SLAPI_DSE_RETURNTEXT_SIZE );
- }
} else if (strcasecmp (mods[i]->mod_type, "objectclasses") == 0) {
- if (is_replicated_operation) {
- /* before accepting the schema checks if the local consumer schema is not
- * a superset of the supplier schema
- */
- if (schema_objectclasses_superset_check(mods[i]->mod_bvalues, OC_CONSUMER)) {
-
- schema_create_errormsg( returntext, SLAPI_DSE_RETURNTEXT_SIZE,
- schema_errprefix_generic, mods[i]->mod_type,
- "Replace is not possible, local consumer schema is a superset of the supplier" );
- slapi_log_error(SLAPI_LOG_FATAL, "schema",
- "Local %s must not be overwritten (set replication log for additional info)\n",
- mods[i]->mod_type);
- *returncode = LDAP_UNWILLING_TO_PERFORM;
- } else {
- /*
- * Replace all objectclasses
- */
- *returncode = schema_replace_objectclasses(pb, mods[i],
- returntext, SLAPI_DSE_RETURNTEXT_SIZE);
- }
- } else {
/*
* Replace all objectclasses
+ * It has already been checked that if it was a replicated schema
+ * it is a superset of the current schema. That is fine to apply the mods
*/
*returncode = schema_replace_objectclasses(pb, mods[i],
returntext, SLAPI_DSE_RETURNTEXT_SIZE);
- }
+
} else if (strcasecmp (mods[i]->mod_type, "nsschemacsn") == 0) {
if (is_replicated_operation) {
/* Update the schema CSN */
@@ -6148,8 +6162,6 @@ slapi_schema_get_superior_name(const char *ocname_or_oid)
return superior;
}
-
-
/* Check if the oc_list1 is a superset of oc_list2.
* oc_list1 is a superset if it exists objectclass in oc_list1 that
* do not exist in oc_list2. Or if a OC in oc_list1 required more attributes
@@ -6165,10 +6177,8 @@ schema_oc_superset_check(struct objclass *oc_list1, struct objclass *oc_list2, c
struct objclass *oc_1, *oc_2;
char *description;
int debug_logging = 0;
- int rc, i, j;
+ int rc;
int repl_schema_policy;
- int found;
- PRBool moved_must_to_may;
if (message == NULL) {
description = "";
@@ -6219,12 +6229,28 @@ schema_oc_superset_check(struct objclass *oc_list1, struct objclass *oc_list2, c
/* try to retrieve it with the name*/
oc_2 = oc_find_nolock(oc_1->oc_name, oc_list2, PR_TRUE);
}
- if (oc_2 == NULL) {
+ if (oc_2) {
+ if (schema_oc_compare(oc_1, oc_2, description) > 0) {
+ rc = 1;
+ if (debug_logging) {
+ if (replica_role == REPL_SCHEMA_AS_CONSUMER) {
+ slapi_log_error(SLAPI_LOG_REPL, "schema", "Local %s schema objectclasses is a superset of"
+ " the received one.\n", oc_1->oc_name);
+ } else {
+ slapi_log_error(SLAPI_LOG_REPL, "schema", "Remote %s schema objectclasses is a superset of"
+ " the received one.\n", oc_1->oc_name);
+ }
+ continue;
+ } else {
+ break;
+ }
+ }
+ } else {
slapi_log_error(SLAPI_LOG_REPL, "schema", "Fail to retrieve in the %s schema [%s or %s]\n",
description,
oc_1->oc_name,
oc_1->oc_oid);
-
+
/* The oc_1 objectclasses is supperset */
rc = 1;
if(debug_logging){
@@ -6234,111 +6260,337 @@ schema_oc_superset_check(struct objclass *oc_list1, struct objclass *oc_list2, c
break;
}
}
+ }
+ slapi_rwlock_unlock( schema_policy_lock );
+
+ return rc;
+}
+/* call must hold oc_lock at least in read */
+static struct schema_mods_indexes *
+schema_list_oc2learn(struct objclass *oc_remote_list, struct objclass *oc_local_list, int replica_role) {
+ struct objclass *oc_remote, *oc_local;
+ struct schema_mods_indexes *head = NULL, *mods_index;
+ int index = 0;
+ int repl_schema_policy;
+ char *message;
+
+ if (replica_role == REPL_SCHEMA_AS_SUPPLIER) {
+ message = "remote consumer";
+ } else {
+ message = "remote supplier";
+ }
- /* First check the MUST */
- if (oc_1->oc_orig_required) {
- for (i = 0; oc_1->oc_orig_required[i] != NULL; i++) {
- /* For each required attribute from the remote schema check that
- * it is also required in the local schema
- */
- found = 0;
- if (oc_2->oc_orig_required) {
- for (j = 0; oc_2->oc_orig_required[j] != NULL; j++) {
- if (strcasecmp(oc_2->oc_orig_required[j], oc_1->oc_orig_required[i]) == 0) {
- found = 1;
- break;
- }
- }
- }
- if (!found) {
- /* Before stating that oc1 is a superset of oc2, we need to verify that the 'required'
- * attribute (from oc1) is missing in 'required' oc2 because it is
- * now 'allowed' in oc2
- */
- moved_must_to_may = PR_FALSE;
- if (oc_2->oc_orig_allowed) {
- for (j = 0; oc_2->oc_orig_allowed[j] != NULL; j++) {
- if (strcasecmp(oc_2->oc_orig_allowed[j], oc_1->oc_orig_required[i]) == 0) {
- moved_must_to_may = PR_TRUE;
- break;
- }
- }
- }
- if (moved_must_to_may) {
- /* This is a special case where oc1 is actually NOT a superset of oc2 */
- slapi_log_error(SLAPI_LOG_REPL, "schema", "Attribute %s is no longer 'required' in '%s' of the %s schema but is now 'allowed'\n",
- oc_1->oc_orig_required[i],
- oc_1->oc_name,
- description);
- } else {
+ slapi_rwlock_rdlock( schema_policy_lock );
+ for (oc_remote = oc_remote_list; oc_remote != NULL; oc_remote = oc_remote->oc_next, index++) {
+
+ /* If this objectclass is not checked (accept) or rejects schema update */
+ repl_schema_policy = schema_check_policy(replica_role, REPL_SCHEMA_OBJECTCLASS, oc_remote->oc_name, oc_remote->oc_oid);
+ if ((repl_schema_policy == REPL_SCHEMA_UPDATE_ACCEPT_VALUE) || (repl_schema_policy == REPL_SCHEMA_UPDATE_REJECT_VALUE)) {
+ continue;
+ }
+
- /* The required attribute in the remote protocol (remote_oc->oc_orig_required[i])
- * is not required in the local protocol
- */
- slapi_log_error(SLAPI_LOG_REPL, "schema", "Attribute %s is not required in '%s' of the %s schema\n",
- oc_1->oc_orig_required[i],
- oc_1->oc_name,
- description);
-
- /* The oc_1 objectclasses is supperset */
- rc = 1;
- if (debug_logging) {
- /* we continue to check all attributes so we log what is wrong */
- continue;
- } else {
- break;
- }
- }
- }
+ oc_local = oc_find_nolock(oc_remote->oc_oid, oc_local_list, PR_TRUE);
+ if (oc_local == NULL) {
+ /* try to retrieve it with the name*/
+ oc_local = oc_find_nolock(oc_remote->oc_name, oc_local_list, PR_TRUE);
+ }
+ if ((oc_local == NULL) ||
+ (schema_oc_compare(oc_local, oc_remote, message) < 0)) {
+ /* This replica does not know this objectclass, It needs to be added */
+ slapi_log_error(SLAPI_LOG_REPL, "schema", "Add that unknown/extended objectclass %s (%s)\n",
+ oc_remote->oc_name,
+ oc_remote->oc_oid);
+
+ if ((mods_index = (struct schema_mods_indexes *) slapi_ch_calloc(1, sizeof (struct schema_mods_indexes))) == NULL) {
+ slapi_log_error(SLAPI_LOG_FATAL, "schema", "Fail to Add (no memory) objectclass %s (%s)\n",
+ oc_remote->oc_name,
+ oc_remote->oc_oid);
+ continue;
}
+
+ /* insert it in the list */
+ mods_index->index = index;
+ mods_index->next = head;
+ mods_index->new_value = NULL;
+ head = mods_index;
}
+ }
+ slapi_rwlock_unlock( schema_policy_lock );
+ return head;
+}
+static struct schema_mods_indexes *
+schema_list_attr2learn(struct asyntaxinfo *at_list_local, struct asyntaxinfo *at_list_remote, int replica_role)
+{
+ struct asyntaxinfo *at_remote, *at_local;
+ struct schema_mods_indexes *head = NULL, *mods_index;
+ int index = 0;
+ int repl_schema_policy;
+ int debug_logging = 0;
+ char *message;
- /* Second check the MAY */
- if (oc_1->oc_orig_allowed) {
- for (i = 0; oc_1->oc_orig_allowed[i] != NULL; i++) {
- /* For each required attribute from the remote schema check that
- * it is also required in the local schema
+ if (slapi_is_loglevel_set(SLAPI_LOG_REPL)) {
+ debug_logging = 1;
+ }
+
+ if (replica_role == REPL_SCHEMA_AS_SUPPLIER) {
+ message = "remote consumer";
+ } else {
+ message = "remote supplier";
+ }
+
+ slapi_rwlock_rdlock(schema_policy_lock);
+ for (at_remote = at_list_remote; at_remote != NULL; at_remote = at_remote->asi_next, index++) {
+ /* If this objectclass is not checked (accept) or rejects schema update */
+ repl_schema_policy = schema_check_policy(replica_role, REPL_SCHEMA_ATTRIBUTE, at_remote->asi_name, at_remote->asi_oid);;
+ if ((repl_schema_policy == REPL_SCHEMA_UPDATE_ACCEPT_VALUE) || (repl_schema_policy == REPL_SCHEMA_UPDATE_REJECT_VALUE)) {
+ continue;
+ }
+
+ if (((at_local = attr_syntax_find(at_remote, at_list_local)) == NULL) ||
+ (schema_at_compare(at_local, at_remote, message, debug_logging) < 0)) {
+ /* This replica does not know this attribute, It needs to be added */
+ slapi_log_error(SLAPI_LOG_REPL, "schema", "Add that unknown/extended attribute %s (%s)\n",
+ at_remote->asi_name,
+ at_remote->asi_oid);
+
+ if ((mods_index = (struct schema_mods_indexes *) slapi_ch_calloc(1, sizeof (struct schema_mods_indexes))) == NULL) {
+ slapi_log_error(SLAPI_LOG_FATAL, "schema", "Fail to Add (no memory) attribute %s (%s)\n",
+ at_remote->asi_name,
+ at_remote->asi_oid);
+ continue;
+ }
+
+ /* insert it in the list */
+ mods_index->index = index;
+ mods_index->next = head;
+ mods_index->new_value = NULL;
+ head = mods_index;
+
+ }
+ }
+ slapi_rwlock_unlock(schema_policy_lock);
+ return head;
+}
+
+/* If oc_1 > oc2 returns 1
+ * else it returns 0
+ */
+static PRBool
+schema_oc_compare_strict(struct objclass *oc_1, struct objclass *oc_2, char *description)
+{
+ int found;
+ int i,j;
+ PRBool moved_must_to_may;
+
+ /* safety checking */
+ if (!oc_1) {
+ return 0;
+ } else if (!oc_2) {
+ return 1;
+ }
+
+
+ /* First check the MUST */
+ if (oc_1->oc_orig_required) {
+ for (i = 0; oc_1->oc_orig_required[i] != NULL; i++) {
+ /* For each required attribute from oc1 schema check that
+ * it is also required in the oc2 schema
+ */
+ found = 0;
+ if (oc_2->oc_orig_required) {
+ for (j = 0; oc_2->oc_orig_required[j] != NULL; j++) {
+ if (strcasecmp(oc_2->oc_orig_required[j], oc_1->oc_orig_required[i]) == 0) {
+ found = 1;
+ break;
+ }
+ }
+ }
+ if (!found) {
+ /* Before stating that oc1 is a superset of oc2, we need to verify that the 'required'
+ * attribute (from oc1) is missing in 'required' oc2 because it is
+ * now 'allowed' in oc2
*/
- found = 0;
+ moved_must_to_may = PR_FALSE;
if (oc_2->oc_orig_allowed) {
for (j = 0; oc_2->oc_orig_allowed[j] != NULL; j++) {
- if (strcasecmp(oc_2->oc_orig_allowed[j], oc_1->oc_orig_allowed[i]) == 0) {
- found = 1;
+ if (strcasecmp(oc_2->oc_orig_allowed[j], oc_1->oc_orig_required[i]) == 0) {
+ moved_must_to_may = PR_TRUE;
break;
}
}
}
- if (!found) {
- /* The allowed attribute in the remote schema (remote_oc->oc_orig_allowed[i])
- * is not allowed in the local schema
+ if (moved_must_to_may) {
+ /* This is a special case where oc1 is actually NOT a superset of oc2 */
+ slapi_log_error(SLAPI_LOG_REPL, "schema", "Attribute %s is no longer 'required' in '%s' of the %s schema but is now 'allowed'\n",
+ oc_1->oc_orig_required[i],
+ oc_1->oc_name,
+ description);
+ } else {
+ /* The required attribute in the oc1
+ * is not required in the oc2
*/
- slapi_log_error(SLAPI_LOG_REPL, "schema", "Attribute %s is not allowed in '%s' of the %s schema\n",
- oc_1->oc_orig_allowed[i],
+ slapi_log_error(SLAPI_LOG_REPL, "schema", "Attribute %s is not required in '%s' of the %s schema\n",
+ oc_1->oc_orig_required[i],
oc_1->oc_name,
description);
- /* The oc_1 objectclasses is superset */
- rc = 1;
- if (debug_logging) {
- /* we continue to check all attributes so we log what is wrong */
- continue;
- } else {
+ /* The oc_1 objectclasses is supperset */
+ return 1;
+ }
+ }
+ }
+ }
+
+ /* Second check the MAY */
+ if (oc_1->oc_orig_allowed) {
+ for (i = 0; oc_1->oc_orig_allowed[i] != NULL; i++) {
+ /* For each required attribute from the remote schema check that
+ * it is also required in the local schema
+ */
+ found = 0;
+ if (oc_2->oc_orig_allowed) {
+ for (j = 0; oc_2->oc_orig_allowed[j] != NULL; j++) {
+ if (strcasecmp(oc_2->oc_orig_allowed[j], oc_1->oc_orig_allowed[i]) == 0) {
+ found = 1;
break;
- }
+ }
}
}
+ if (!found) {
+ /* The allowed attribute in the remote schema (remote_oc->oc_orig_allowed[i])
+ * is not allowed in the local schema
+ */
+ slapi_log_error(SLAPI_LOG_REPL, "schema", "Attribute %s is not allowed in '%s' of the %s schema\n",
+ oc_1->oc_orig_allowed[i],
+ oc_1->oc_name,
+ description);
+
+ /* The oc_1 objectclasses is superset */
+ return 1;
+ }
}
}
- slapi_rwlock_unlock( schema_policy_lock );
+
+
+ return 0;
+}
+
+
+/* Compare two objectclass definitions
+ * it compares:
+
+ * It returns:
+ * 1: if oc_1 is a superset of oc_2
+ * -1: if oc_2 is a superset of oc_1
+ * 0: if oc_1 and at_2 are equivalent
+ */
+static int
+schema_oc_compare(struct objclass *oc_1, struct objclass *oc_2, char *description)
+{
+ if (schema_oc_compare_strict(oc_1, oc_2, description) > 0) {
+ return 1;
+ } else if (schema_oc_compare_strict(oc_2, oc_1, description) > 0) {
+ return -1;
+ } else {
+ return 0;
+ }
+}
+
+/* Compare two attributes definitions
+ * it compares:
+ * - single/multi value
+ * - syntax
+ * - matching rules
+ * It returns:
+ * 1: if at_1 is a superset of at_2
+ * -1: if at_2 is a superset of at_1
+ * 0: if at_1 and at_2 are equivalent
+ */
+static int
+schema_at_compare(struct asyntaxinfo *at_1, struct asyntaxinfo *at_2, char *message, int debug_logging)
+{
+ char *info = NULL;
- return rc;
+ /* safety checking */
+ if (! at_1) {
+ if (!at_2) {
+ return 0;
+ } else {
+ return -1;
+ }
+ } else if (!at_2) {
+ return 1;
+ }
+
+ /*
+ * Check for single vs. multi value
+ */
+ if (!(at_1->asi_flags & SLAPI_ATTR_FLAG_SINGLE) && (at_2->asi_flags & SLAPI_ATTR_FLAG_SINGLE)) {
+
+ /* at_1 is a superset */
+ if (debug_logging) {
+ slapi_log_error(SLAPI_LOG_REPL, "schema", "%s schema attribute [%s] is not "
+ "\"single-valued\" \n", message, at_1->asi_name);
+ }
+ return 1;
+ }
+ if ((at_1->asi_flags & SLAPI_ATTR_FLAG_SINGLE) && !(at_2->asi_flags & SLAPI_ATTR_FLAG_SINGLE)) {
+ /* at_2 is a superset */
+ if (debug_logging) {
+ slapi_log_error(SLAPI_LOG_REPL, "schema", "%s schema attribute [%s] is not "
+ "\"single-valued\" \n", message, at_1->asi_name);
+ }
+ return -1;
+ }
+
+ /*
+ * Check the syntaxes
+ */
+ if (schema_at_superset_check_syntax_oids(at_1->asi_syntax_oid, at_2->asi_syntax_oid)) {
+ /* at_1 is a superset */
+ if (debug_logging) {
+ slapi_log_error(SLAPI_LOG_REPL, "schema", "%s schema attribute [%s] syntax "
+ "can not be overwritten\n", message, at_1->asi_name);
+ }
+ return 1;
+ }
+ if (schema_at_superset_check_syntax_oids(at_2->asi_syntax_oid, at_1->asi_syntax_oid)) {
+ /* at_2 is a superset */
+ if (debug_logging) {
+ slapi_log_error(SLAPI_LOG_REPL, "schema", "%s schema attribute [%s] syntax "
+ "can not be overwritten\n", message, at_2->asi_name);
+ }
+ return -1;
+ }
+
+ /*
+ * Check some matching rules - not finished yet...
+ *
+ * For now, skip the matching rule check (rc is never equal to -1)
+ */
+ if (schema_at_superset_check_mr(at_1, at_2, info)) {
+ if (debug_logging) {
+ slapi_log_error(SLAPI_LOG_REPL, "schema", "%s schema attribute [%s] matching "
+ "rule can not be overwritten\n", message, at_1->asi_name);
+ }
+ return 1;
+ }
+ if (schema_at_superset_check_mr(at_2, at_1, info)) {
+ if (debug_logging) {
+ slapi_log_error(SLAPI_LOG_REPL, "schema", "%s schema attribute [%s] matching "
+ "rule can not be overwritten\n", message, at_2->asi_name);
+ }
+ return -1;
+ }
+
+ return 0;
}
static int
schema_at_superset_check(struct asyntaxinfo *at_list1, struct asyntaxinfo *at_list2, char *message, int replica_role)
{
struct asyntaxinfo *at_1, *at_2;
- char *info = NULL;
int debug_logging = 0;
int repl_schema_policy;
int rc = 0;
@@ -6376,51 +6628,22 @@ schema_at_superset_check(struct asyntaxinfo *at_list1, struct asyntaxinfo *at_li
}
/* check if at_1 exists in at_list2 */
- if((at_2 = attr_syntax_find(at_1, at_list2))){
- /*
- * Check for single vs. multi value
- */
- if(!(at_1->asi_flags & SLAPI_ATTR_FLAG_SINGLE) && (at_2->asi_flags & SLAPI_ATTR_FLAG_SINGLE)){
- /* at_list 1 is a superset */
- rc = 1;
- if(debug_logging){
- slapi_log_error(SLAPI_LOG_REPL, "schema", "%s schema attribute [%s] is not "
- "\"single-valued\" \n",message, at_1->asi_name);
- continue;
- } else {
- break;
+ if((at_2 = attr_syntax_find(at_1, at_list2))) {
+ if (schema_at_compare(at_1, at_2, message, debug_logging) > 0) {
+ rc = 1;
+ if (debug_logging) {
+ if (replica_role == REPL_SCHEMA_AS_CONSUMER) {
+ slapi_log_error(SLAPI_LOG_REPL, "schema", "Local %s schema attributetypes is a superset of"
+ " the received one.\n", at_1->asi_name);
+ } else {
+ slapi_log_error(SLAPI_LOG_REPL, "schema", "Remote %s schema attributetypes is a superset of"
+ " the received one.\n", at_1->asi_name);
+ }
+ continue;
+ } else {
+ break;
+ }
}
- }
-
- /*
- * Check the syntaxes
- */
- if(schema_at_superset_check_syntax_oids(at_1->asi_syntax_oid, at_2->asi_syntax_oid)){
- /* at_list 1 is a superset */
- rc = 1;
- if(debug_logging){
- slapi_log_error(SLAPI_LOG_REPL, "schema", "%s schema attribute [%s] syntax "
- "can not be overwritten\n",message, at_1->asi_name);
- continue;
- } else {
- break;
- }
- }
- /*
- * Check some matching rules - not finished yet...
- *
- * For now, skip the matching rule check (rc is never equal to -1)
- */
- if(rc == -1 && schema_at_superset_check_mr(at_1, at_2, info)){
- rc = 1;
- if(debug_logging){
- slapi_log_error(SLAPI_LOG_REPL, "schema", "%s schema attribute [%s] matching "
- "rule can not be overwritten\n",message, at_1->asi_name);
- continue;
- } else {
- break;
- }
- }
} else {
rc = 1;
if(debug_logging){
@@ -6834,6 +7057,7 @@ schema_berval_to_atlist(struct berval **at_berval)
return head;
}
+
int
schema_objectclasses_superset_check(struct berval **remote_schema, char *type)
{
@@ -6859,19 +7083,19 @@ schema_objectclasses_superset_check(struct berval **remote_schema, char *type)
*/
if (remote_oc_list) {
- oc_lock_read();
+ oc_lock_read();
if (strcmp(type, OC_SUPPLIER) == 0) {
/* Check if the remote_oc_list from a consumer are or not
* a superset of the objectclasses of the local supplier schema
*/
- rc = schema_oc_superset_check(remote_oc_list, g_get_global_oc_nolock(), "local supplier", REPL_SCHEMA_AS_SUPPLIER);
+ rc = schema_oc_superset_check(remote_oc_list, g_get_global_oc_nolock(), "remote consumer", REPL_SCHEMA_AS_SUPPLIER);
} else {
/* Check if the objectclasses of the local consumer schema are or not
* a superset of the remote_oc_list from a supplier
*/
rc = schema_oc_superset_check(g_get_global_oc_nolock(), remote_oc_list, "remote supplier", REPL_SCHEMA_AS_CONSUMER);
}
-
+
oc_unlock();
}
@@ -6922,3 +7146,428 @@ schema_attributetypes_superset_check(struct berval **remote_schema, char *type)
}
return rc;
}
+
+/* Do the internal MOD and update the local "nsSchemaCSN" with a local timestamp
+ * It could be a good idea to set the 'nsSchemaCSN' with the maximum of local time and
+ * the CSN received with the remote schema
+ */
+static void
+modify_schema_internal_mod(Slapi_DN *sdn, Slapi_Mods *smods)
+{
+ Slapi_PBlock *newpb;
+ int op_result;
+ CSN *schema_csn;
+
+ /* allocate internal mod components: pblock*/
+ newpb = slapi_pblock_new();
+
+ slapi_modify_internal_set_pb_ext (
+ newpb,
+ sdn,
+ slapi_mods_get_ldapmods_byref (smods),
+ NULL, /* Controls */
+ NULL,
+ (void *)plugin_get_default_component_id(),
+ 0);
+
+ /* do modify */
+ slapi_modify_internal_pb (newpb);
+ slapi_pblock_get (newpb, SLAPI_PLUGIN_INTOP_RESULT, &op_result);
+ if (op_result == LDAP_SUCCESS) {
+ /* Update the schema csn if the operation succeeded */
+ schema_csn = csn_new();
+ if (NULL != schema_csn) {
+ csn_set_replicaid(schema_csn, 0);
+ csn_set_time(schema_csn, current_time());
+ g_set_global_schema_csn(schema_csn);
+ }
+ }
+
+ slapi_pblock_destroy(newpb);
+}
+
+/* Prepare slapi_mods for the internal mod
+ * Caller must free smods with slapi_mods_done
+ */
+static void
+modify_schema_prepare_mods(Slapi_Mods *smods, char *type, struct schema_mods_indexes *values)
+{
+ struct schema_mods_indexes *object;
+ struct berval *bv;
+ struct berval **bvps;
+ int nb_values, i;
+
+ for (object = values, nb_values = 0; object != NULL; object = object->next, nb_values++);
+ bvps = (struct berval **) slapi_ch_calloc(1, (nb_values + 1) * sizeof(struct berval *));
+
+
+ for (i = 0, object = values; object != NULL; i++, object = object->next) {
+ bv = (struct berval *) slapi_ch_malloc(sizeof(struct berval));
+ bv->bv_len = strlen(object->new_value);
+ bv->bv_val = (void*) object->new_value;
+ bvps[i] = bv;
+ slapi_log_error(SLAPI_LOG_REPL, "schema", "MOD[%d] add (%s): %s\n", i, type, object->new_value);
+ }
+ bvps[nb_values] = NULL;
+ slapi_mods_init (smods, 2);
+ slapi_mods_add_modbvps( smods, LDAP_MOD_ADD, type, bvps );
+ for (i = 0; bvps[i] != NULL; i++) {
+ /* bv_val should not be free. It belongs to the incoming MOD */
+ slapi_ch_free((void **) &bvps[i]);
+ }
+ slapi_ch_free((void **) &bvps);
+
+}
+
+/* called by modify_schema_dse/supplier_learn_new_definitions to learn new
+ * definitions via internal mod.
+ * Internal mod is important, because we want those definitions to be updated in 99user.ldif
+ * and we are not sure that the current operation will succeeds or not.
+ */
+static void
+modify_schema_apply_new_definitions(char *attr_name, struct schema_mods_indexes *list)
+{
+ Slapi_Mods smods = {0};
+ Slapi_DN *sdn = NULL;
+
+ if (list == NULL)
+ return;
+
+ /* Then the sdn */
+ sdn = slapi_sdn_new();
+ if (!sdn) {
+ slapi_log_error( SLAPI_LOG_FATAL, "schema", "modify_schema_apply_new_definitions Out of memory \n");
+ goto done;
+ }
+ slapi_sdn_set_dn_byval(sdn, SLAPD_SCHEMA_DN);
+
+ /* prepare the mods */
+ modify_schema_prepare_mods(&smods, attr_name, list);
+
+ /* update the schema with the new attributetypes */
+ /* No need to lock the schema_dse as the internal mod will do */
+ modify_schema_internal_mod(sdn, &smods);
+
+
+done:
+ if (sdn) {
+ slapi_sdn_free(&sdn);
+ }
+ slapi_mods_done (&smods);
+}
+
+/*
+ * This routines retrieves from the remote schema (mods) the
+ * definitions (attributetypes/objectclasses), that are:
+ * - unknown from the local schema
+ * - a superset of the local definition
+ *
+ * It then builds two lists (to be freed by the caller) with those definitions.
+ * Those list contains a duplicate of the definition (string).
+ */
+static void
+modify_schema_get_new_definitions(Slapi_PBlock *pb, LDAPMod **mods, struct schema_mods_indexes **at_list, struct schema_mods_indexes **oc_list)
+{
+ struct asyntaxinfo *remote_at_list;
+ struct objclass *remote_oc_list;
+ int is_replicated_operation = 0;
+ int replace_allowed = 0;
+ slapdFrontendConfig_t *slapdFrontendConfig;
+ int i;
+ struct schema_mods_indexes *at2learn_list = NULL;
+ struct schema_mods_indexes *at2learn;
+ struct schema_mods_indexes *oc2learn_list = NULL;
+ struct schema_mods_indexes *oc2learn;
+
+
+ slapi_pblock_get(pb, SLAPI_IS_REPLICATED_OPERATION, &is_replicated_operation);
+
+ /* by default nothing to learn */
+ *at_list = NULL;
+ *oc_list = NULL;
+
+ /* We are only looking for schema received from a supplier */
+ if (!is_replicated_operation || !mods) {
+ return ;
+ }
+
+ /* Check if we are allowed to update the schema (if needed) */
+ slapdFrontendConfig = getFrontendConfig();
+ CFG_LOCK_READ(slapdFrontendConfig);
+ if ((0 == strcasecmp(slapdFrontendConfig->schemareplace, CONFIG_SCHEMAREPLACE_STR_ON)) ||
+ (0 == strcasecmp(slapdFrontendConfig->schemareplace, CONFIG_SCHEMAREPLACE_STR_REPLICATION_ONLY))) {
+ replace_allowed = 1;
+ }
+ CFG_UNLOCK_READ(slapdFrontendConfig);
+ if (!replace_allowed) {
+ return;
+ }
+
+
+ /* First retrieve unknowns attributetypes because an unknown objectclasses
+ * may be composed of unknown attributetypes
+ */
+ at2learn_list = NULL;
+ oc2learn_list = NULL;
+ schema_dse_lock_read();
+ for (i = 0; mods[i]; i++) {
+ if (SLAPI_IS_MOD_REPLACE(mods[i]->mod_op) && (mods[i]->mod_bvalues)) {
+
+ if (strcasecmp(mods[i]->mod_type, "attributetypes") == 0) {
+ /* we have some MOD_replace of attributetypes*/
+
+ /* First build an attribute list from the remote schema */
+ if ((remote_at_list = schema_berval_to_atlist(mods[i]->mod_bvalues)) == NULL) {
+ /* If we can not build an attributes list from the mods, just skip
+ * it and look for objectclasses
+ */
+ slapi_log_error(SLAPI_LOG_FATAL, "schema",
+ "Not able to build an attributes list (%s) from the schema received from the supplier\n",
+ mods[i]->mod_type);
+ continue;
+ }
+ /* Build a list of attributestype to learn from the remote definitions */
+ attr_syntax_read_lock();
+ at2learn_list = schema_list_attr2learn(attr_syntax_get_global_at(), remote_at_list, REPL_SCHEMA_AS_CONSUMER);
+ attr_syntax_unlock_read();
+
+ /* For each of them copy the value to set */
+ for (at2learn = at2learn_list; at2learn != NULL; at2learn = at2learn->next) {
+ struct berval *bv;
+ bv = mods[i]->mod_bvalues[at2learn->index]; /* takes the berval from the selected mod */
+ at2learn->new_value = (char *) slapi_ch_malloc(bv->bv_len + 1);
+ memcpy(at2learn->new_value, bv->bv_val, bv->bv_len);
+ at2learn->new_value[bv->bv_len] = '\0';
+ slapi_log_error(SLAPI_LOG_REPL, "schema", "take attributetypes: %s\n", at2learn->new_value);
+ }
+
+ /* Free the remote schema list */
+ schema_atlist_free(remote_at_list);
+
+ } else if (strcasecmp(mods[i]->mod_type, "objectclasses") == 0) {
+ /* we have some MOD_replace of objectclasses */
+
+ /* First build an objectclass list from the remote schema */
+ if ((remote_oc_list = schema_berval_to_oclist(mods[i]->mod_bvalues)) == NULL) {
+ /* If we can not build an objectclasses list from the mods, just skip
+ * it and look for attributes
+ */
+ slapi_log_error(SLAPI_LOG_FATAL, "schema",
+ "Not able to build an objectclasses list (%s) from the schema received from the supplier\n",
+ mods[i]->mod_type);
+ continue;
+ }
+ /* Build a list of objectclasses to learn from the remote definitions */
+ oc_lock_read();
+ oc2learn_list = schema_list_oc2learn(remote_oc_list, g_get_global_oc_nolock(), REPL_SCHEMA_AS_CONSUMER);
+ oc_unlock();
+
+ /* For each of them copy the value to set */
+ for (oc2learn = oc2learn_list; oc2learn != NULL; oc2learn = oc2learn->next) {
+ struct berval *bv;
+ bv = mods[i]->mod_bvalues[oc2learn->index]; /* takes the berval from the selected mod */
+ oc2learn->new_value = (char *) slapi_ch_malloc(bv->bv_len + 1);
+ memcpy(oc2learn->new_value, bv->bv_val, bv->bv_len);
+ oc2learn->new_value[bv->bv_len] = '\0';
+ slapi_log_error(SLAPI_LOG_REPL, "schema", "take objectclass: %s\n", oc2learn->new_value);
+ }
+
+ /* Free the remote schema list*/
+ schema_oclist_free(remote_oc_list);
+ }
+ }
+ }
+ schema_dse_unlock();
+
+ *at_list = at2learn_list;
+ *oc_list = oc2learn_list;
+}
+
+/*
+ * It evaluate if the schema in the mods, is a superset of
+ * the local schema.
+ * Called when we know the mods comes from/to a replicated session
+ * Caller must not hold schema_dse lock
+ *
+ * mods: set of mod to apply to the schema
+ * replica_role:
+ * OC_CONSUMER: means the caller is acting as a consumer (receiving a schema)
+ * OC_SUPPLIER: means the caller is acting as a supplier (sending a schema)
+ *
+ * It returns:
+ * - PR_TRUE: if replicated schema is a superset of local schema
+ * - PR_FALSE: if local schema is a superset of local schema
+ */
+static PRBool
+check_replicated_schema(LDAPMod **mods, char *replica_role, char **attr_name)
+{
+ int i;
+ PRBool rc = PR_TRUE;
+
+ schema_dse_lock_read();
+ for (i = 0; mods[i]; i++) {
+ if ((SLAPI_IS_MOD_REPLACE(mods[i]->mod_op)) && strcasecmp (mods[i]->mod_type, "attributetypes") == 0) {
+ if (schema_attributetypes_superset_check(mods[i]->mod_bvalues, replica_role)) {
+ rc = PR_FALSE;
+ *attr_name = mods[i]->mod_type;
+ break;
+ }
+ } else if ((SLAPI_IS_MOD_REPLACE(mods[i]->mod_op)) && strcasecmp (mods[i]->mod_type, "objectclasses") == 0) {
+ if (schema_objectclasses_superset_check(mods[i]->mod_bvalues, replica_role)) {
+ rc = PR_FALSE;
+ *attr_name = mods[i]->mod_type;
+ break;
+ }
+
+ }
+
+ }
+ schema_dse_unlock();
+
+ return rc;
+}
+
+/* Free the list of definitions allocated in modify_schema_get_new_definitions/supplier_get_new_definitions */
+static void
+modify_schema_free_new_definitions(struct schema_mods_indexes *def_list)
+{
+ struct schema_mods_indexes *def, *head;
+
+ for (head = def_list; head != NULL; ) {
+ def = head;
+ head = head->next;
+
+ /* Free the string definition that was copied from the berval */
+ if (def->new_value) {
+ slapi_ch_free((void**) &def->new_value);
+ }
+
+ /* Then the definition cell */
+ slapi_ch_free((void **) &def);
+ }
+}
+
+/* This functions is called by a supplier.
+ * It builds lists of definitions (attributetypes/objectclasses) to learn
+ * objectclasses: received objectclass definitions
+ * attributetypes: received attribute definitions
+ * new_oc: list of definitions to learn (list should be freed by the caller)
+ * new_at: list of definitions to learn (list should be freed by the caller)
+ */
+
+static void
+supplier_get_new_definitions(struct berval **objectclasses, struct berval **attributetypes, struct schema_mods_indexes **new_oc, struct schema_mods_indexes **new_at)
+{
+ int replace_allowed = 0;
+ slapdFrontendConfig_t *slapdFrontendConfig;
+ struct asyntaxinfo *remote_at_list;
+ struct objclass *remote_oc_list;
+ struct schema_mods_indexes *at2learn_list = NULL;
+ struct schema_mods_indexes *at2learn;
+ struct schema_mods_indexes *oc2learn_list = NULL;
+ struct schema_mods_indexes *oc2learn;
+
+ *new_oc = NULL;
+ *new_at = NULL;
+
+ if ((objectclasses == NULL) && (attributetypes == NULL)) {
+ return;
+ }
+ /* Check if we are allowed to update the schema (if needed) */
+ slapdFrontendConfig = getFrontendConfig();
+ CFG_LOCK_READ(slapdFrontendConfig);
+ if ((0 == strcasecmp(slapdFrontendConfig->schemareplace, CONFIG_SCHEMAREPLACE_STR_ON)) ||
+ (0 == strcasecmp(slapdFrontendConfig->schemareplace, CONFIG_SCHEMAREPLACE_STR_REPLICATION_ONLY))) {
+ replace_allowed = 1;
+ }
+ CFG_UNLOCK_READ(slapdFrontendConfig);
+ if (!replace_allowed) {
+ return;
+ }
+
+ schema_dse_lock_read();
+ /*
+ * Build the list of objectclasses
+ */
+ /* from berval to objclass more convenient to compare */
+ if ((remote_oc_list = schema_berval_to_oclist(objectclasses)) != NULL) {
+ /* Build a list of objectclasses to learn from the remote definitions */
+ oc_lock_read();
+ oc2learn_list = schema_list_oc2learn(remote_oc_list, g_get_global_oc_nolock(), REPL_SCHEMA_AS_SUPPLIER);
+ oc_unlock();
+
+ /* For each of them copy the value to set */
+ for (oc2learn = oc2learn_list; oc2learn != NULL; oc2learn = oc2learn->next) {
+ struct berval *bv;
+ bv = objectclasses[oc2learn->index]; /* takes the berval from the selected objectclass */
+ oc2learn->new_value = (char *) slapi_ch_malloc(bv->bv_len + 1);
+ memcpy(oc2learn->new_value, bv->bv_val, bv->bv_len);
+ oc2learn->new_value[bv->bv_len] = '\0';
+ slapi_log_error(SLAPI_LOG_REPL, "schema", "supplier takes objectclass: %s\n", oc2learn->new_value);
+ }
+
+ /* Free the remote schema list*/
+ schema_oclist_free(remote_oc_list);
+ } else {
+ /* If we can not build an objectclasses list */
+ slapi_log_error(SLAPI_LOG_FATAL, "schema",
+ "Not able to build an objectclasses list from the consumer schema\n");
+ }
+
+
+ /*
+ * Build the list of attributetypes
+ */
+ /* First build an attribute list from the remote schema */
+ if ((remote_at_list = schema_berval_to_atlist(attributetypes)) != NULL) {
+ /* Build a list of attributestype to learn from the remote definitions */
+ attr_syntax_read_lock();
+ at2learn_list = schema_list_attr2learn(attr_syntax_get_global_at(), remote_at_list, REPL_SCHEMA_AS_SUPPLIER);
+ attr_syntax_unlock_read();
+
+ /* For each of them copy the value to set */
+ for (at2learn = at2learn_list; at2learn != NULL; at2learn = at2learn->next) {
+ struct berval *bv;
+ bv = attributetypes[at2learn->index]; /* takes the berval from the selected mod */
+ at2learn->new_value = (char *) slapi_ch_malloc(bv->bv_len + 1);
+ memcpy(at2learn->new_value, bv->bv_val, bv->bv_len);
+ at2learn->new_value[bv->bv_len] = '\0';
+ slapi_log_error(SLAPI_LOG_REPL, "schema", "supplier takes attributetypes: %s\n", at2learn->new_value);
+ }
+
+ /* Free the remote schema list */
+ schema_atlist_free(remote_at_list);
+ } else {
+ /* If we can not build an attributes list from the mods, just skip
+ * it and look for objectclasses
+ */
+ slapi_log_error(SLAPI_LOG_FATAL, "schema",
+ "Not able to build an attributes list from the consumer schema");
+ }
+ schema_dse_unlock();
+ *new_oc = oc2learn_list;
+ *new_at = at2learn_list;
+
+}
+
+/* This functions is called by a supplier when it detects new definitions (objectclasses/attributetypes)
+ * or extension of existing definitions in a consumer schema.
+ * This function, build lists of definitions to "learn" and add those definitions in the schema and 99user.ldif
+ */
+void
+supplier_learn_new_definitions(struct berval **objectclasses, struct berval **attributetypes)
+{
+ struct schema_mods_indexes *oc_list = NULL;
+ struct schema_mods_indexes *at_list = NULL;
+
+ supplier_get_new_definitions(objectclasses, attributetypes, &oc_list, &at_list);
+ if (at_list) {
+ modify_schema_apply_new_definitions("attributetypes", at_list);
+ }
+ if (oc_list) {
+ modify_schema_apply_new_definitions("objectclasses", oc_list);
+ }
+ /* No need to hold the lock for these list that are local */
+ modify_schema_free_new_definitions(at_list);
+ modify_schema_free_new_definitions(oc_list);
+}
9 years, 5 months
Branch '389-ds-base-1.2.11' - ldap/servers
by Mark Reynolds
ldap/servers/slapd/back-ldbm/ldbm_delete.c | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
New commits:
commit 00a7594e6fd3d1ff5b6fb5100aa5e1bac5e1eada
Author: Mark Reynolds <mreynolds(a)redhat.com>
Date: Mon Apr 21 13:06:52 2014 -0400
Ticket 47771 - Move parentsdn initialization to avoid crash
Bug Description: It's possible to call slapi_sdn_done() before parentsdn is
initialized - which will crash the server.
Fix Description: Initialize parentsdn at the top of the function.
https://fedorahosted.org/389/ticket/47771
Reviewed by: nhosoi(Thanks!)
diff --git a/ldap/servers/slapd/back-ldbm/ldbm_delete.c b/ldap/servers/slapd/back-ldbm/ldbm_delete.c
index f6e6005..c9ce70f 100644
--- a/ldap/servers/slapd/back-ldbm/ldbm_delete.c
+++ b/ldap/servers/slapd/back-ldbm/ldbm_delete.c
@@ -108,6 +108,7 @@ ldbm_back_delete( Slapi_PBlock *pb )
slapi_pblock_get( pb, SLAPI_IS_REPLICATED_OPERATION, &is_replicated_operation );
slapi_sdn_init(&nscpEntrySDN);
+ slapi_sdn_init(&parentsdn);
/* dblayer_txn_init needs to be called before "goto error_return" */
dblayer_txn_init(li,&txn);
@@ -321,7 +322,6 @@ ldbm_back_delete( Slapi_PBlock *pb )
* seems to deadlock the database when dblayer_txn_begin is
* called.
*/
- slapi_sdn_init(&parentsdn);
slapi_sdn_get_backend_parent_ext(sdnp, &parentsdn, pb->pb_backend, is_tombstone_entry);
if ( !slapi_sdn_isempty(&parentsdn) )
{
@@ -361,7 +361,6 @@ ldbm_back_delete( Slapi_PBlock *pb )
}
retval = -1;
CACHE_RETURN(&(inst->inst_cache), &parent);
- slapi_sdn_done(&parentsdn);
goto error_return;
} else {
/* entry locked, move on */
@@ -398,7 +397,6 @@ ldbm_back_delete( Slapi_PBlock *pb )
op, &haschildren);
/* The modify context now contains info needed later */
if (0 != retval) {
- slapi_sdn_done(&parentsdn);
ldap_result_code= LDAP_OPERATIONS_ERROR;
goto error_return;
}
@@ -418,7 +416,6 @@ ldbm_back_delete( Slapi_PBlock *pb )
}
}
}
- slapi_sdn_done(&parentsdn);
if(create_tombstone_entry)
{
@@ -1249,6 +1246,7 @@ diskfull_return:
slapi_ch_free((void**)&errbuf);
slapi_sdn_done(&nscpEntrySDN);
slapi_ch_free_string(&e_uniqueid);
+ slapi_sdn_done(&parentsdn);
if (pb->pb_conn)
{
slapi_log_error (SLAPI_LOG_TRACE, "ldbm_back_delete", "leave conn=%" NSPRIu64 " op=%d\n", pb->pb_conn->c_connid, operation->o_opid);
9 years, 5 months
Branch '389-ds-base-1.3.1' - ldap/servers
by Mark Reynolds
ldap/servers/slapd/back-ldbm/ldbm_delete.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
New commits:
commit 4b986ee0536114a568c30b808663ee590f0c1a86
Author: Mark Reynolds <mreynolds(a)redhat.com>
Date: Mon Apr 21 12:43:09 2014 -0400
Ticket 47771 - Move parentsdn initialization to avoid crash
Bug Description: It's possible to call slapi_sdn_done() before parentsdn is
initialized - which will crash the server.
Fix Description: Initialize parentsdn at the top of the function.
https://fedorahosted.org/389/ticket/47771
Reviewed by: nhosoi(Thanks!)
(cherry picked from commit 991984f1544c096d46a54ed3117542e8c5f3d70c)
diff --git a/ldap/servers/slapd/back-ldbm/ldbm_delete.c b/ldap/servers/slapd/back-ldbm/ldbm_delete.c
index 792d424..4c75fb9 100644
--- a/ldap/servers/slapd/back-ldbm/ldbm_delete.c
+++ b/ldap/servers/slapd/back-ldbm/ldbm_delete.c
@@ -108,6 +108,7 @@ ldbm_back_delete( Slapi_PBlock *pb )
slapi_pblock_get( pb, SLAPI_IS_REPLICATED_OPERATION, &is_replicated_operation );
slapi_sdn_init(&nscpEntrySDN);
+ slapi_sdn_init(&parentsdn);
/* dblayer_txn_init needs to be called before "goto error_return" */
dblayer_txn_init(li,&txn);
@@ -444,7 +445,6 @@ ldbm_back_delete( Slapi_PBlock *pb )
* seems to deadlock the database when dblayer_txn_begin is
* called.
*/
- slapi_sdn_init(&parentsdn);
slapi_sdn_get_backend_parent_ext(sdnp, &parentsdn, pb->pb_backend, is_tombstone_entry);
if ( !slapi_sdn_isempty(&parentsdn) )
{
9 years, 5 months
Branch '389-ds-base-1.3.2' - ldap/servers
by Mark Reynolds
ldap/servers/slapd/back-ldbm/ldbm_delete.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
New commits:
commit bdb69624717bedcebd3e16e35f3c5ece367c3a1c
Author: Mark Reynolds <mreynolds(a)redhat.com>
Date: Mon Apr 21 12:43:09 2014 -0400
Ticket 47771 - Move parentsdn initialization to avoid crash
Bug Description: It's possible to call slapi_sdn_done() before parentsdn is
initialized - which will crash the server.
Fix Description: Initialize parentsdn at the top of the function.
https://fedorahosted.org/389/ticket/47771
Reviewed by: nhosoi(Thanks!)
(cherry picked from commit 991984f1544c096d46a54ed3117542e8c5f3d70c)
diff --git a/ldap/servers/slapd/back-ldbm/ldbm_delete.c b/ldap/servers/slapd/back-ldbm/ldbm_delete.c
index 678580c..ea9473e 100644
--- a/ldap/servers/slapd/back-ldbm/ldbm_delete.c
+++ b/ldap/servers/slapd/back-ldbm/ldbm_delete.c
@@ -108,6 +108,7 @@ ldbm_back_delete( Slapi_PBlock *pb )
slapi_pblock_get( pb, SLAPI_IS_REPLICATED_OPERATION, &is_replicated_operation );
slapi_sdn_init(&nscpEntrySDN);
+ slapi_sdn_init(&parentsdn);
/* dblayer_txn_init needs to be called before "goto error_return" */
dblayer_txn_init(li,&txn);
@@ -444,7 +445,6 @@ ldbm_back_delete( Slapi_PBlock *pb )
* seems to deadlock the database when dblayer_txn_begin is
* called.
*/
- slapi_sdn_init(&parentsdn);
slapi_sdn_get_backend_parent_ext(sdnp, &parentsdn, pb->pb_backend, is_tombstone_entry);
if ( !slapi_sdn_isempty(&parentsdn) )
{
9 years, 5 months
ldap/servers
by Mark Reynolds
ldap/servers/slapd/back-ldbm/ldbm_delete.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
New commits:
commit 991984f1544c096d46a54ed3117542e8c5f3d70c
Author: Mark Reynolds <mreynolds(a)redhat.com>
Date: Mon Apr 21 12:43:09 2014 -0400
Ticket 47771 - Move parentsdn initialization to avoid crash
Bug Description: It's possible to call slapi_sdn_done() before parentsdn is
initialized - which will crash the server.
Fix Description: Initialize parentsdn at the top of the function.
https://fedorahosted.org/389/ticket/47771
Reviewed by: nhosoi(Thanks!)
diff --git a/ldap/servers/slapd/back-ldbm/ldbm_delete.c b/ldap/servers/slapd/back-ldbm/ldbm_delete.c
index 678580c..ea9473e 100644
--- a/ldap/servers/slapd/back-ldbm/ldbm_delete.c
+++ b/ldap/servers/slapd/back-ldbm/ldbm_delete.c
@@ -108,6 +108,7 @@ ldbm_back_delete( Slapi_PBlock *pb )
slapi_pblock_get( pb, SLAPI_IS_REPLICATED_OPERATION, &is_replicated_operation );
slapi_sdn_init(&nscpEntrySDN);
+ slapi_sdn_init(&parentsdn);
/* dblayer_txn_init needs to be called before "goto error_return" */
dblayer_txn_init(li,&txn);
@@ -444,7 +445,6 @@ ldbm_back_delete( Slapi_PBlock *pb )
* seems to deadlock the database when dblayer_txn_begin is
* called.
*/
- slapi_sdn_init(&parentsdn);
slapi_sdn_get_backend_parent_ext(sdnp, &parentsdn, pb->pb_backend, is_tombstone_entry);
if ( !slapi_sdn_isempty(&parentsdn) )
{
9 years, 5 months
Branch '389-ds-base-1.3.1' - ldap/servers
by Mark Reynolds
ldap/servers/plugins/dna/dna.c | 219 +++++++++++++++++++++++++++++++++++++++--
1 file changed, 209 insertions(+), 10 deletions(-)
New commits:
commit eb6ea3642a0e145d3a0d7de4bbca7c05cde7a3d3
Author: Mark Reynolds <mreynolds(a)redhat.com>
Date: Mon Apr 21 11:16:09 2014 -0400
Ticket 47779 - Part of DNA shared configuration is deleted after server restart
Bug Description: After a configuration change, like updating dnaNextVal,
the shared config entries are always rebuilt, but they do
preserve some of the configuration attributes like dnaRemoteBindMethod.
Fix Description: Maintain a global linked list of all the shared config entries,
so when we go to rebuild the shared config entry we can restore
the dnaRemote attributes.
https://fedorahosted.org/389/ticket/47779
Reviewed by: nhosoi(Thanks!)
(cherry picked from commit 97412d6d51abf46caf1d84924982f95ed5f22384)
diff --git a/ldap/servers/plugins/dna/dna.c b/ldap/servers/plugins/dna/dna.c
index 90a66c6..5ef5713 100644
--- a/ldap/servers/plugins/dna/dna.c
+++ b/ldap/servers/plugins/dna/dna.c
@@ -101,6 +101,7 @@
#define DNA_HOSTNAME "dnaHostname"
#define DNA_PORTNUM "dnaPortNum"
#define DNA_SECURE_PORTNUM "dnaSecurePortNum"
+#define DNA_REMOTE_BUFSIZ 15 /* max size for bind method & protocol */
#define DNA_REMOTE_BIND_METHOD "dnaRemoteBindMethod"
#define DNA_REMOTE_CONN_PROT "dnaRemoteConnProtocol"
@@ -191,6 +192,9 @@ struct configEntry {
static PRCList *dna_global_config = NULL;
static Slapi_RWLock *g_dna_cache_lock;
+static struct dnaServer *dna_global_servers = NULL;
+static Slapi_RWLock *g_dna_cache_server_lock;
+
static void *_PluginID = NULL;
static const char *_PluginDN = NULL;
@@ -206,6 +210,7 @@ static char *secureportnum = NULL;
*/
struct dnaServer {
PRCList list;
+ Slapi_DN *sdn;
char *host;
unsigned int port;
unsigned int secureport;
@@ -216,6 +221,7 @@ struct dnaServer {
char *remote_conn_prot;
char *remote_binddn; /* contains pointer to main config binddn */
char *remote_bindpw; /* contains pointer to main config bindpw */
+ struct dnaServer *next; /* used for the global server list */
};
static char *dna_extend_exop_oid_list[] = {
@@ -264,7 +270,7 @@ static void dna_notice_allocation(struct configEntry *config_entry,
PRUint64 new, PRUint64 last);
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 int dna_get_shared_servers(struct configEntry *config_entry, PRCList **servers, int get_all);
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);
@@ -287,6 +293,9 @@ static void dna_create_valcheck_filter(struct configEntry *config_entry, PRUint6
static int dna_isrepl(Slapi_PBlock *pb);
static int dna_get_remote_config_info( struct dnaServer *server, char **bind_dn, char **bind_passwd,
char **bind_method, int *is_ssl, int *port);
+static int dna_load_shared_servers();
+static void dna_delete_global_servers();
+static int dna_get_shared_config_attr_val(struct configEntry *config_entry, char *attr, char *value);
/**
*
@@ -340,6 +349,21 @@ void dna_unlock()
slapi_rwlock_unlock(g_dna_cache_lock);
}
+void dna_server_read_lock()
+{
+ slapi_rwlock_rdlock(g_dna_cache_server_lock);
+}
+
+void dna_server_write_lock()
+{
+ slapi_rwlock_wrlock(g_dna_cache_server_lock);
+}
+
+void dna_server_unlock()
+{
+ slapi_rwlock_unlock(g_dna_cache_server_lock);
+}
+
/**
*
* Get the dna plug-in version
@@ -553,11 +577,16 @@ dna_start(Slapi_PBlock * pb)
}
g_dna_cache_lock = slapi_new_rwlock();
-
if (!g_dna_cache_lock) {
slapi_log_error(SLAPI_LOG_FATAL, DNA_PLUGIN_SUBSYSTEM,
- "dna_start: lock creation failed\n");
+ "dna_start: global config lock creation failed\n");
+ return DNA_FAILURE;
+ }
+ g_dna_cache_server_lock = slapi_new_rwlock();
+ if (!g_dna_cache_server_lock) {
+ slapi_log_error(SLAPI_LOG_FATAL, DNA_PLUGIN_SUBSYSTEM,
+ "dna_start: global server lock creation failed\n");
return DNA_FAILURE;
}
@@ -602,6 +631,16 @@ dna_start(Slapi_PBlock * pb)
}
g_plugin_started = 1;
+
+ /*
+ * Load all shared server configs
+ */
+ if (dna_load_shared_servers() ) {
+ slapi_log_error(SLAPI_LOG_FATAL, DNA_PLUGIN_SUBSYSTEM,
+ "dna_start: shared config server initialization failed.\n");
+ return DNA_FAILURE;
+ }
+
slapi_log_error(SLAPI_LOG_PLUGIN, DNA_PLUGIN_SUBSYSTEM,
"dna: ready for service\n");
slapi_log_error(SLAPI_LOG_TRACE, DNA_PLUGIN_SUBSYSTEM,
@@ -633,6 +672,10 @@ dna_close(Slapi_PBlock * pb)
slapi_ch_free((void **)&dna_global_config);
+ dna_delete_global_servers();
+ slapi_destroy_rwlock(g_dna_cache_server_lock);
+ g_dna_cache_server_lock = NULL;
+
slapi_ch_free_string(&hostname);
slapi_ch_free_string(&portnum);
slapi_ch_free_string(&secureportnum);
@@ -653,6 +696,82 @@ done:
}
/*
+ * Free the global linkedl ist of shared servers
+ */
+static void
+dna_delete_global_servers()
+{
+ struct dnaServer *server, *next;
+
+ if(dna_global_servers){
+ server = dna_global_servers;
+ while(server){
+ next = server->next;
+ dna_free_shared_server(&server);
+ server = next;
+ }
+ dna_global_servers = NULL;
+ }
+}
+
+/*
+ * Look through the global config entries, and build a global
+ * shared server config list.
+ */
+static int
+dna_load_shared_servers()
+{
+ struct configEntry *config_entry = NULL;
+ struct dnaServer *server = NULL, *global_servers = NULL;
+ PRCList *server_list = NULL;
+ PRCList *config_list = NULL;
+ int ret = 0;
+
+ /* First free the existing list. */
+ dna_delete_global_servers();
+
+ /* Now build the new list. */
+ dna_write_lock();
+ if (!PR_CLIST_IS_EMPTY(dna_global_config)) {
+ config_list = PR_LIST_HEAD(dna_global_config);
+ while (config_list != dna_global_config) {
+ PRCList *shared_list = NULL;
+ config_entry = (struct configEntry *) config_list;
+
+ if(dna_get_shared_servers(config_entry,
+ &shared_list,
+ 1 /* get all the servers */))
+ {
+ dna_unlock();
+ return -1;
+ }
+
+ dna_server_write_lock();
+ if (shared_list) {
+ server_list = PR_LIST_HEAD(shared_list);
+ while (server_list != shared_list) {
+ server = (struct dnaServer *)server_list;
+ if(global_servers == NULL){
+ dna_global_servers = global_servers = server;
+ } else {
+ global_servers->next = server;
+ global_servers = server;
+ }
+ server_list = PR_NEXT_LINK(server_list);
+ }
+ slapi_ch_free((void **)&shared_list);
+ }
+ dna_server_unlock();
+
+ config_list = PR_NEXT_LINK(config_list);
+ }
+ }
+ dna_unlock();
+
+ return ret;
+}
+
+/*
* config looks like this
* - cn=myplugin
* --- cn=posix
@@ -1257,6 +1376,7 @@ dna_free_shared_server(struct dnaServer **server)
return;
}
s = *server;
+ slapi_sdn_free(&s->sdn);
slapi_ch_free_string(&s->host);
slapi_ch_free_string(&s->remote_bind_method);
slapi_ch_free_string(&s->remote_conn_prot);
@@ -1359,6 +1479,7 @@ dna_update_config_event(time_t event_time, void *arg)
Slapi_PBlock *dna_pb = NULL;
Slapi_DN *sdn = slapi_sdn_new_normdn_byref(config_entry->shared_cfg_dn);
Slapi_Backend *be = slapi_be_select(sdn);
+
slapi_sdn_free(&sdn);
if (be) {
dna_pb = slapi_pblock_new();
@@ -1445,7 +1566,7 @@ static int dna_fix_maxval(struct configEntry *config_entry,
} else if (!skip_range_request && config_entry->shared_cfg_base) {
/* Find out if there are any other servers to request
* range from. */
- dna_get_shared_servers(config_entry, &servers);
+ dna_get_shared_servers(config_entry, &servers, 0 );
if (servers) {
/* We have other servers we can try to extend
@@ -1535,7 +1656,7 @@ dna_notice_allocation(struct configEntry *config_entry, PRUint64 new,
}
static int
-dna_get_shared_servers(struct configEntry *config_entry, PRCList **servers)
+dna_get_shared_servers(struct configEntry *config_entry, PRCList **servers, int get_all)
{
int ret = LDAP_SUCCESS;
Slapi_PBlock *pb = NULL;
@@ -1585,13 +1706,14 @@ dna_get_shared_servers(struct configEntry *config_entry, PRCList **servers)
/* 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]))) {
+ /* skip our own shared config entry, unless we want all the servers */
+ if (get_all || 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->sdn = slapi_sdn_new_dn_byval(slapi_entry_get_ndn(entries[i]));
server->host = slapi_entry_attr_get_charptr(entries[i],
DNA_HOSTNAME);
server->port = slapi_entry_attr_get_uint(entries[i], DNA_PORTNUM);
@@ -1660,7 +1782,7 @@ dna_get_shared_servers(struct configEntry *config_entry, PRCList **servers)
} else {
/* Find the right slot for this entry. We
* want to order the entries based off of
- * the remaining number of values, higest
+ * the remaining number of values, highest
* to lowest. */
struct dnaServer *sitem;
PRCList* item = PR_LIST_HEAD(*servers);
@@ -1937,6 +2059,36 @@ static int dna_dn_is_config(char *dn)
return ret;
}
+static int
+dna_dn_is_shared_config(Slapi_PBlock *pb, char *dn)
+{
+ struct configEntry *config_entry = NULL;
+ Slapi_Entry *entry = NULL;
+ Slapi_Attr *attr = NULL;
+ PRCList *list = NULL;
+ int ret = 0;
+
+ dna_read_lock();
+ if (!PR_CLIST_IS_EMPTY(dna_global_config)) {
+ list = PR_LIST_HEAD(dna_global_config);
+ while (list != dna_global_config) {
+ config_entry = (struct configEntry *) list;
+ if (slapi_dn_issuffix(dn, config_entry->shared_cfg_base)) {
+ slapi_pblock_get(pb, SLAPI_ENTRY_POST_OP, &entry);
+ /* If the entry has the dnaHost attribute, it is a shared entry */
+ if (slapi_entry_attr_find(entry, DNA_HOSTNAME, &attr) == 0) {
+ ret = 1;
+ break;
+ }
+ }
+ list = PR_NEXT_LINK(list);
+ }
+ }
+ dna_unlock();
+
+ return ret;
+}
+
#define DNA_LDAP_TAG_SK_REVERSE 0x81L
static LDAPControl *dna_build_sort_control(const char *attr)
@@ -2265,6 +2417,39 @@ static int dna_get_next_value(struct configEntry *config_entry,
}
/*
+ * Get a value from the global server list. The dna_server_read_lock()
+ * should be held prior to calling this function.
+ */
+static int
+dna_get_shared_config_attr_val(struct configEntry *config_entry, char *attr, char *value)
+{
+ struct dnaServer *server = NULL;
+ Slapi_DN *server_sdn = NULL;
+ int found = 0;
+
+ server_sdn = slapi_sdn_new_dn_byref(config_entry->shared_cfg_dn);
+ if (dna_global_servers) {
+ server = dna_global_servers;
+ while (server) {
+ if(slapi_sdn_compare(server->sdn, server_sdn) == 0){
+ if(strcmp(attr, DNA_REMOTE_BIND_METHOD) == 0){
+ PR_snprintf(value, DNA_REMOTE_BUFSIZ, "%s", server->remote_bind_method);
+ found = 1;
+ break;
+ } else if(strcmp(attr, DNA_REMOTE_CONN_PROT) == 0){
+ PR_snprintf(value, DNA_REMOTE_BUFSIZ, "%s", server->remote_conn_prot);
+ found = 1;
+ break;
+ }
+ }
+ server = server->next;
+ }
+ }
+ slapi_sdn_free(&server_sdn);
+
+ return found;
+}
+/*
* dna_update_shared_config()
*
* Updates the shared config entry if one is
@@ -2316,8 +2501,9 @@ dna_update_shared_config(struct configEntry * config_entry)
* already exist, we add it. */
if (ret == LDAP_NO_SUCH_OBJECT) {
Slapi_Entry *e = NULL;
- Slapi_DN *sdn =
- slapi_sdn_new_normdn_byref(config_entry->shared_cfg_dn);
+ Slapi_DN *sdn = slapi_sdn_new_normdn_byref(config_entry->shared_cfg_dn);
+ char bind_meth[DNA_REMOTE_BUFSIZ];
+ char conn_prot[DNA_REMOTE_BUFSIZ];
/* Set up the new shared config entry */
e = slapi_entry_alloc();
@@ -2333,6 +2519,16 @@ dna_update_shared_config(struct configEntry * config_entry)
}
slapi_entry_add_string(e, DNA_REMAINING, remaining_vals);
+ /* Grab the remote server settings */
+ dna_server_read_lock();
+ if(dna_get_shared_config_attr_val(config_entry, DNA_REMOTE_BIND_METHOD, bind_meth)) {
+ slapi_entry_add_string(e, DNA_REMOTE_BIND_METHOD, bind_meth);
+ }
+ if(dna_get_shared_config_attr_val(config_entry, DNA_REMOTE_CONN_PROT, conn_prot)){
+ slapi_entry_add_string(e, DNA_REMOTE_CONN_PROT,conn_prot);
+ }
+ dna_server_unlock();
+
/* clear pb for re-use */
slapi_pblock_init(pb);
@@ -3932,6 +4128,9 @@ static int dna_config_check_post_op(Slapi_PBlock * pb)
if (dna_dn_is_config(dn)) {
dna_load_plugin_config(pb, 0);
}
+ if(dna_dn_is_shared_config(pb, dn) == 0){
+ dna_load_shared_servers();
+ }
}
}
9 years, 5 months
Branch '389-ds-base-1.3.2' - ldap/servers
by Mark Reynolds
ldap/servers/plugins/dna/dna.c | 219 +++++++++++++++++++++++++++++++++++++++--
1 file changed, 209 insertions(+), 10 deletions(-)
New commits:
commit 97412d6d51abf46caf1d84924982f95ed5f22384
Author: Mark Reynolds <mreynolds(a)redhat.com>
Date: Mon Apr 21 11:16:09 2014 -0400
Ticket 47779 - Part of DNA shared configuration is deleted after server restart
Bug Description: After a configuration change, like updating dnaNextVal,
the shared config entries are always rebuilt, but they do
preserve some of the configuration attributes like dnaRemoteBindMethod.
Fix Description: Maintain a global linked list of all the shared config entries,
so when we go to rebuild the shared config entry we can restore
the dnaRemote attributes.
https://fedorahosted.org/389/ticket/47779
Reviewed by: nhosoi(Thanks!)
diff --git a/ldap/servers/plugins/dna/dna.c b/ldap/servers/plugins/dna/dna.c
index 9794a4a..f8f2813 100644
--- a/ldap/servers/plugins/dna/dna.c
+++ b/ldap/servers/plugins/dna/dna.c
@@ -101,6 +101,7 @@
#define DNA_HOSTNAME "dnaHostname"
#define DNA_PORTNUM "dnaPortNum"
#define DNA_SECURE_PORTNUM "dnaSecurePortNum"
+#define DNA_REMOTE_BUFSIZ 15 /* max size for bind method & protocol */
#define DNA_REMOTE_BIND_METHOD "dnaRemoteBindMethod"
#define DNA_REMOTE_CONN_PROT "dnaRemoteConnProtocol"
@@ -191,6 +192,9 @@ struct configEntry {
static PRCList *dna_global_config = NULL;
static Slapi_RWLock *g_dna_cache_lock;
+static struct dnaServer *dna_global_servers = NULL;
+static Slapi_RWLock *g_dna_cache_server_lock;
+
static void *_PluginID = NULL;
static const char *_PluginDN = NULL;
@@ -206,6 +210,7 @@ static char *secureportnum = NULL;
*/
struct dnaServer {
PRCList list;
+ Slapi_DN *sdn;
char *host;
unsigned int port;
unsigned int secureport;
@@ -216,6 +221,7 @@ struct dnaServer {
char *remote_conn_prot;
char *remote_binddn; /* contains pointer to main config binddn */
char *remote_bindpw; /* contains pointer to main config bindpw */
+ struct dnaServer *next; /* used for the global server list */
};
static char *dna_extend_exop_oid_list[] = {
@@ -264,7 +270,7 @@ static void dna_notice_allocation(struct configEntry *config_entry,
PRUint64 new, PRUint64 last);
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 int dna_get_shared_servers(struct configEntry *config_entry, PRCList **servers, int get_all);
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);
@@ -287,6 +293,9 @@ static void dna_create_valcheck_filter(struct configEntry *config_entry, PRUint6
static int dna_isrepl(Slapi_PBlock *pb);
static int dna_get_remote_config_info( struct dnaServer *server, char **bind_dn, char **bind_passwd,
char **bind_method, int *is_ssl, int *port);
+static int dna_load_shared_servers();
+static void dna_delete_global_servers();
+static int dna_get_shared_config_attr_val(struct configEntry *config_entry, char *attr, char *value);
/**
*
@@ -340,6 +349,21 @@ void dna_unlock()
slapi_rwlock_unlock(g_dna_cache_lock);
}
+void dna_server_read_lock()
+{
+ slapi_rwlock_rdlock(g_dna_cache_server_lock);
+}
+
+void dna_server_write_lock()
+{
+ slapi_rwlock_wrlock(g_dna_cache_server_lock);
+}
+
+void dna_server_unlock()
+{
+ slapi_rwlock_unlock(g_dna_cache_server_lock);
+}
+
/**
*
* Get the dna plug-in version
@@ -553,11 +577,16 @@ dna_start(Slapi_PBlock * pb)
}
g_dna_cache_lock = slapi_new_rwlock();
-
if (!g_dna_cache_lock) {
slapi_log_error(SLAPI_LOG_FATAL, DNA_PLUGIN_SUBSYSTEM,
- "dna_start: lock creation failed\n");
+ "dna_start: global config lock creation failed\n");
+ return DNA_FAILURE;
+ }
+ g_dna_cache_server_lock = slapi_new_rwlock();
+ if (!g_dna_cache_server_lock) {
+ slapi_log_error(SLAPI_LOG_FATAL, DNA_PLUGIN_SUBSYSTEM,
+ "dna_start: global server lock creation failed\n");
return DNA_FAILURE;
}
@@ -602,6 +631,16 @@ dna_start(Slapi_PBlock * pb)
}
g_plugin_started = 1;
+
+ /*
+ * Load all shared server configs
+ */
+ if (dna_load_shared_servers() ) {
+ slapi_log_error(SLAPI_LOG_FATAL, DNA_PLUGIN_SUBSYSTEM,
+ "dna_start: shared config server initialization failed.\n");
+ return DNA_FAILURE;
+ }
+
slapi_log_error(SLAPI_LOG_PLUGIN, DNA_PLUGIN_SUBSYSTEM,
"dna: ready for service\n");
slapi_log_error(SLAPI_LOG_TRACE, DNA_PLUGIN_SUBSYSTEM,
@@ -633,6 +672,10 @@ dna_close(Slapi_PBlock * pb)
slapi_ch_free((void **)&dna_global_config);
+ dna_delete_global_servers();
+ slapi_destroy_rwlock(g_dna_cache_server_lock);
+ g_dna_cache_server_lock = NULL;
+
slapi_ch_free_string(&hostname);
slapi_ch_free_string(&portnum);
slapi_ch_free_string(&secureportnum);
@@ -653,6 +696,82 @@ done:
}
/*
+ * Free the global linkedl ist of shared servers
+ */
+static void
+dna_delete_global_servers()
+{
+ struct dnaServer *server, *next;
+
+ if(dna_global_servers){
+ server = dna_global_servers;
+ while(server){
+ next = server->next;
+ dna_free_shared_server(&server);
+ server = next;
+ }
+ dna_global_servers = NULL;
+ }
+}
+
+/*
+ * Look through the global config entries, and build a global
+ * shared server config list.
+ */
+static int
+dna_load_shared_servers()
+{
+ struct configEntry *config_entry = NULL;
+ struct dnaServer *server = NULL, *global_servers = NULL;
+ PRCList *server_list = NULL;
+ PRCList *config_list = NULL;
+ int ret = 0;
+
+ /* First free the existing list. */
+ dna_delete_global_servers();
+
+ /* Now build the new list. */
+ dna_write_lock();
+ if (!PR_CLIST_IS_EMPTY(dna_global_config)) {
+ config_list = PR_LIST_HEAD(dna_global_config);
+ while (config_list != dna_global_config) {
+ PRCList *shared_list = NULL;
+ config_entry = (struct configEntry *) config_list;
+
+ if(dna_get_shared_servers(config_entry,
+ &shared_list,
+ 1 /* get all the servers */))
+ {
+ dna_unlock();
+ return -1;
+ }
+
+ dna_server_write_lock();
+ if (shared_list) {
+ server_list = PR_LIST_HEAD(shared_list);
+ while (server_list != shared_list) {
+ server = (struct dnaServer *)server_list;
+ if(global_servers == NULL){
+ dna_global_servers = global_servers = server;
+ } else {
+ global_servers->next = server;
+ global_servers = server;
+ }
+ server_list = PR_NEXT_LINK(server_list);
+ }
+ slapi_ch_free((void **)&shared_list);
+ }
+ dna_server_unlock();
+
+ config_list = PR_NEXT_LINK(config_list);
+ }
+ }
+ dna_unlock();
+
+ return ret;
+}
+
+/*
* config looks like this
* - cn=myplugin
* --- cn=posix
@@ -1257,6 +1376,7 @@ dna_free_shared_server(struct dnaServer **server)
return;
}
s = *server;
+ slapi_sdn_free(&s->sdn);
slapi_ch_free_string(&s->host);
slapi_ch_free_string(&s->remote_bind_method);
slapi_ch_free_string(&s->remote_conn_prot);
@@ -1359,6 +1479,7 @@ dna_update_config_event(time_t event_time, void *arg)
Slapi_PBlock *dna_pb = NULL;
Slapi_DN *sdn = slapi_sdn_new_normdn_byref(config_entry->shared_cfg_dn);
Slapi_Backend *be = slapi_be_select(sdn);
+
slapi_sdn_free(&sdn);
if (be) {
dna_pb = slapi_pblock_new();
@@ -1445,7 +1566,7 @@ static int dna_fix_maxval(struct configEntry *config_entry,
} else if (!skip_range_request && config_entry->shared_cfg_base) {
/* Find out if there are any other servers to request
* range from. */
- dna_get_shared_servers(config_entry, &servers);
+ dna_get_shared_servers(config_entry, &servers, 0 );
if (servers) {
/* We have other servers we can try to extend
@@ -1535,7 +1656,7 @@ dna_notice_allocation(struct configEntry *config_entry, PRUint64 new,
}
static int
-dna_get_shared_servers(struct configEntry *config_entry, PRCList **servers)
+dna_get_shared_servers(struct configEntry *config_entry, PRCList **servers, int get_all)
{
int ret = LDAP_SUCCESS;
Slapi_PBlock *pb = NULL;
@@ -1585,13 +1706,14 @@ dna_get_shared_servers(struct configEntry *config_entry, PRCList **servers)
/* 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]))) {
+ /* skip our own shared config entry, unless we want all the servers */
+ if (get_all || 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->sdn = slapi_sdn_new_dn_byval(slapi_entry_get_ndn(entries[i]));
server->host = slapi_entry_attr_get_charptr(entries[i],
DNA_HOSTNAME);
server->port = slapi_entry_attr_get_uint(entries[i], DNA_PORTNUM);
@@ -1660,7 +1782,7 @@ dna_get_shared_servers(struct configEntry *config_entry, PRCList **servers)
} else {
/* Find the right slot for this entry. We
* want to order the entries based off of
- * the remaining number of values, higest
+ * the remaining number of values, highest
* to lowest. */
struct dnaServer *sitem;
PRCList* item = PR_LIST_HEAD(*servers);
@@ -1937,6 +2059,36 @@ static int dna_dn_is_config(char *dn)
return ret;
}
+static int
+dna_dn_is_shared_config(Slapi_PBlock *pb, char *dn)
+{
+ struct configEntry *config_entry = NULL;
+ Slapi_Entry *entry = NULL;
+ Slapi_Attr *attr = NULL;
+ PRCList *list = NULL;
+ int ret = 0;
+
+ dna_read_lock();
+ if (!PR_CLIST_IS_EMPTY(dna_global_config)) {
+ list = PR_LIST_HEAD(dna_global_config);
+ while (list != dna_global_config) {
+ config_entry = (struct configEntry *) list;
+ if (slapi_dn_issuffix(dn, config_entry->shared_cfg_base)) {
+ slapi_pblock_get(pb, SLAPI_ENTRY_POST_OP, &entry);
+ /* If the entry has the dnaHost attribute, it is a shared entry */
+ if (slapi_entry_attr_find(entry, DNA_HOSTNAME, &attr) == 0) {
+ ret = 1;
+ break;
+ }
+ }
+ list = PR_NEXT_LINK(list);
+ }
+ }
+ dna_unlock();
+
+ return ret;
+}
+
#define DNA_LDAP_TAG_SK_REVERSE 0x81L
static LDAPControl *dna_build_sort_control(const char *attr)
@@ -2265,6 +2417,39 @@ static int dna_get_next_value(struct configEntry *config_entry,
}
/*
+ * Get a value from the global server list. The dna_server_read_lock()
+ * should be held prior to calling this function.
+ */
+static int
+dna_get_shared_config_attr_val(struct configEntry *config_entry, char *attr, char *value)
+{
+ struct dnaServer *server = NULL;
+ Slapi_DN *server_sdn = NULL;
+ int found = 0;
+
+ server_sdn = slapi_sdn_new_dn_byref(config_entry->shared_cfg_dn);
+ if (dna_global_servers) {
+ server = dna_global_servers;
+ while (server) {
+ if(slapi_sdn_compare(server->sdn, server_sdn) == 0){
+ if(strcmp(attr, DNA_REMOTE_BIND_METHOD) == 0){
+ PR_snprintf(value, DNA_REMOTE_BUFSIZ, "%s", server->remote_bind_method);
+ found = 1;
+ break;
+ } else if(strcmp(attr, DNA_REMOTE_CONN_PROT) == 0){
+ PR_snprintf(value, DNA_REMOTE_BUFSIZ, "%s", server->remote_conn_prot);
+ found = 1;
+ break;
+ }
+ }
+ server = server->next;
+ }
+ }
+ slapi_sdn_free(&server_sdn);
+
+ return found;
+}
+/*
* dna_update_shared_config()
*
* Updates the shared config entry if one is
@@ -2316,8 +2501,9 @@ dna_update_shared_config(struct configEntry * config_entry)
* already exist, we add it. */
if (ret == LDAP_NO_SUCH_OBJECT) {
Slapi_Entry *e = NULL;
- Slapi_DN *sdn =
- slapi_sdn_new_normdn_byref(config_entry->shared_cfg_dn);
+ Slapi_DN *sdn = slapi_sdn_new_normdn_byref(config_entry->shared_cfg_dn);
+ char bind_meth[DNA_REMOTE_BUFSIZ];
+ char conn_prot[DNA_REMOTE_BUFSIZ];
/* Set up the new shared config entry */
e = slapi_entry_alloc();
@@ -2333,6 +2519,16 @@ dna_update_shared_config(struct configEntry * config_entry)
}
slapi_entry_add_string(e, DNA_REMAINING, remaining_vals);
+ /* Grab the remote server settings */
+ dna_server_read_lock();
+ if(dna_get_shared_config_attr_val(config_entry, DNA_REMOTE_BIND_METHOD, bind_meth)) {
+ slapi_entry_add_string(e, DNA_REMOTE_BIND_METHOD, bind_meth);
+ }
+ if(dna_get_shared_config_attr_val(config_entry, DNA_REMOTE_CONN_PROT, conn_prot)){
+ slapi_entry_add_string(e, DNA_REMOTE_CONN_PROT,conn_prot);
+ }
+ dna_server_unlock();
+
/* clear pb for re-use */
slapi_pblock_init(pb);
@@ -3932,6 +4128,9 @@ static int dna_config_check_post_op(Slapi_PBlock * pb)
if (dna_dn_is_config(dn)) {
dna_load_plugin_config(pb, 0);
}
+ if(dna_dn_is_shared_config(pb, dn) == 0){
+ dna_load_shared_servers();
+ }
}
}
9 years, 5 months
ldap/servers
by Mark Reynolds
ldap/servers/plugins/dna/dna.c | 218 +++++++++++++++++++++++++++++++++++++++--
1 file changed, 208 insertions(+), 10 deletions(-)
New commits:
commit 06a3d0a1a2d925e314e9d50c1093a122c6ec788c
Author: Mark Reynolds <mreynolds(a)redhat.com>
Date: Mon Apr 21 11:16:09 2014 -0400
Ticket 47779 - Part of DNA shared configuration is deleted after server restart
Bug Description: After a configuration change, like updating dnaNextVal,
the shared config entries are always rebuilt, but they do
preserve some of the configuration attributes like dnaRemoteBindMethod.
Fix Description: Maintain a global linked list of all the shared config entries,
so when we go to rebuild the shared config entry we can restore
the dnaRemote attributes.
https://fedorahosted.org/389/ticket/47779
Reviewed by: nhosoi(Thanks!)
diff --git a/ldap/servers/plugins/dna/dna.c b/ldap/servers/plugins/dna/dna.c
index cf88597..e9453a4 100644
--- a/ldap/servers/plugins/dna/dna.c
+++ b/ldap/servers/plugins/dna/dna.c
@@ -101,6 +101,7 @@
#define DNA_HOSTNAME "dnaHostname"
#define DNA_PORTNUM "dnaPortNum"
#define DNA_SECURE_PORTNUM "dnaSecurePortNum"
+#define DNA_REMOTE_BUFSIZ 15 /* max size for bind method & protocol */
#define DNA_REMOTE_BIND_METHOD "dnaRemoteBindMethod"
#define DNA_REMOTE_CONN_PROT "dnaRemoteConnProtocol"
@@ -191,6 +192,9 @@ struct configEntry {
static PRCList *dna_global_config = NULL;
static Slapi_RWLock *g_dna_cache_lock;
+static struct dnaServer *dna_global_servers = NULL;
+static Slapi_RWLock *g_dna_cache_server_lock;
+
static void *_PluginID = NULL;
static const char *_PluginDN = NULL;
@@ -204,6 +208,7 @@ static char *secureportnum = NULL;
*/
struct dnaServer {
PRCList list;
+ Slapi_DN *sdn;
char *host;
unsigned int port;
unsigned int secureport;
@@ -214,6 +219,7 @@ struct dnaServer {
char *remote_conn_prot;
char *remote_binddn; /* contains pointer to main config binddn */
char *remote_bindpw; /* contains pointer to main config bindpw */
+ struct dnaServer *next; /* used for the global server list */
};
static char *dna_extend_exop_oid_list[] = {
@@ -262,7 +268,7 @@ static void dna_notice_allocation(struct configEntry *config_entry,
PRUint64 new, PRUint64 last);
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 int dna_get_shared_servers(struct configEntry *config_entry, PRCList **servers, int get_all);
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);
@@ -285,6 +291,9 @@ static void dna_create_valcheck_filter(struct configEntry *config_entry, PRUint6
static int dna_isrepl(Slapi_PBlock *pb);
static int dna_get_remote_config_info( struct dnaServer *server, char **bind_dn, char **bind_passwd,
char **bind_method, int *is_ssl, int *port);
+static int dna_load_shared_servers();
+static void dna_delete_global_servers();
+static int dna_get_shared_config_attr_val(struct configEntry *config_entry, char *attr, char *value);
/**
*
@@ -338,6 +347,21 @@ void dna_unlock()
slapi_rwlock_unlock(g_dna_cache_lock);
}
+void dna_server_read_lock()
+{
+ slapi_rwlock_rdlock(g_dna_cache_server_lock);
+}
+
+void dna_server_write_lock()
+{
+ slapi_rwlock_wrlock(g_dna_cache_server_lock);
+}
+
+void dna_server_unlock()
+{
+ slapi_rwlock_unlock(g_dna_cache_server_lock);
+}
+
/**
*
* Get the dna plug-in version
@@ -546,11 +570,16 @@ dna_start(Slapi_PBlock * pb)
"--> dna_start\n");
g_dna_cache_lock = slapi_new_rwlock();
-
if (!g_dna_cache_lock) {
slapi_log_error(SLAPI_LOG_FATAL, DNA_PLUGIN_SUBSYSTEM,
- "dna_start: lock creation failed\n");
+ "dna_start: global config lock creation failed\n");
+ return DNA_FAILURE;
+ }
+ g_dna_cache_server_lock = slapi_new_rwlock();
+ if (!g_dna_cache_server_lock) {
+ slapi_log_error(SLAPI_LOG_FATAL, DNA_PLUGIN_SUBSYSTEM,
+ "dna_start: global server lock creation failed\n");
return DNA_FAILURE;
}
@@ -594,6 +623,15 @@ dna_start(Slapi_PBlock * pb)
return DNA_FAILURE;
}
+ /*
+ * Load all shared server configs
+ */
+ if (dna_load_shared_servers() ) {
+ slapi_log_error(SLAPI_LOG_FATAL, DNA_PLUGIN_SUBSYSTEM,
+ "dna_start: shared config server initialization failed.\n");
+ return DNA_FAILURE;
+ }
+
slapi_log_error(SLAPI_LOG_PLUGIN, DNA_PLUGIN_SUBSYSTEM,
"dna: ready for service\n");
slapi_log_error(SLAPI_LOG_TRACE, DNA_PLUGIN_SUBSYSTEM,
@@ -618,6 +656,10 @@ dna_close(Slapi_PBlock * pb)
slapi_destroy_rwlock(g_dna_cache_lock);
g_dna_cache_lock = NULL;
+ dna_delete_global_servers();
+ slapi_destroy_rwlock(g_dna_cache_server_lock);
+ g_dna_cache_server_lock = NULL;
+
slapi_ch_free_string(&hostname);
slapi_ch_free_string(&portnum);
slapi_ch_free_string(&secureportnum);
@@ -629,6 +671,82 @@ dna_close(Slapi_PBlock * pb)
}
/*
+ * Free the global linkedl ist of shared servers
+ */
+static void
+dna_delete_global_servers()
+{
+ struct dnaServer *server, *next;
+
+ if(dna_global_servers){
+ server = dna_global_servers;
+ while(server){
+ next = server->next;
+ dna_free_shared_server(&server);
+ server = next;
+ }
+ dna_global_servers = NULL;
+ }
+}
+
+/*
+ * Look through the global config entries, and build a global
+ * shared server config list.
+ */
+static int
+dna_load_shared_servers()
+{
+ struct configEntry *config_entry = NULL;
+ struct dnaServer *server = NULL, *global_servers = NULL;
+ PRCList *server_list = NULL;
+ PRCList *config_list = NULL;
+ int ret = 0;
+
+ /* First free the existing list. */
+ dna_delete_global_servers();
+
+ /* Now build the new list. */
+ dna_write_lock();
+ if (!PR_CLIST_IS_EMPTY(dna_global_config)) {
+ config_list = PR_LIST_HEAD(dna_global_config);
+ while (config_list != dna_global_config) {
+ PRCList *shared_list = NULL;
+ config_entry = (struct configEntry *) config_list;
+
+ if(dna_get_shared_servers(config_entry,
+ &shared_list,
+ 1 /* get all the servers */))
+ {
+ dna_unlock();
+ return -1;
+ }
+
+ dna_server_write_lock();
+ if (shared_list) {
+ server_list = PR_LIST_HEAD(shared_list);
+ while (server_list != shared_list) {
+ server = (struct dnaServer *)server_list;
+ if(global_servers == NULL){
+ dna_global_servers = global_servers = server;
+ } else {
+ global_servers->next = server;
+ global_servers = server;
+ }
+ server_list = PR_NEXT_LINK(server_list);
+ }
+ slapi_ch_free((void **)&shared_list);
+ }
+ dna_server_unlock();
+
+ config_list = PR_NEXT_LINK(config_list);
+ }
+ }
+ dna_unlock();
+
+ return ret;
+}
+
+/*
* config looks like this
* - cn=myplugin
* --- cn=posix
@@ -1233,6 +1351,7 @@ dna_free_shared_server(struct dnaServer **server)
return;
}
s = *server;
+ slapi_sdn_free(&s->sdn);
slapi_ch_free_string(&s->host);
slapi_ch_free_string(&s->remote_bind_method);
slapi_ch_free_string(&s->remote_conn_prot);
@@ -1330,6 +1449,7 @@ dna_update_config_event(time_t event_time, void *arg)
Slapi_PBlock *dna_pb = NULL;
Slapi_DN *sdn = slapi_sdn_new_normdn_byref(config_entry->shared_cfg_dn);
Slapi_Backend *be = slapi_be_select(sdn);
+
slapi_sdn_free(&sdn);
if (be) {
dna_pb = slapi_pblock_new();
@@ -1416,7 +1536,7 @@ static int dna_fix_maxval(struct configEntry *config_entry,
} else if (!skip_range_request && config_entry->shared_cfg_base) {
/* Find out if there are any other servers to request
* range from. */
- dna_get_shared_servers(config_entry, &servers);
+ dna_get_shared_servers(config_entry, &servers, 0 );
if (servers) {
/* We have other servers we can try to extend
@@ -1506,7 +1626,7 @@ dna_notice_allocation(struct configEntry *config_entry, PRUint64 new,
}
static int
-dna_get_shared_servers(struct configEntry *config_entry, PRCList **servers)
+dna_get_shared_servers(struct configEntry *config_entry, PRCList **servers, int get_all)
{
int ret = LDAP_SUCCESS;
Slapi_PBlock *pb = NULL;
@@ -1556,13 +1676,14 @@ dna_get_shared_servers(struct configEntry *config_entry, PRCList **servers)
/* 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]))) {
+ /* skip our own shared config entry, unless we want all the servers */
+ if (get_all || 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->sdn = slapi_sdn_new_dn_byval(slapi_entry_get_ndn(entries[i]));
server->host = slapi_entry_attr_get_charptr(entries[i],
DNA_HOSTNAME);
server->port = slapi_entry_attr_get_uint(entries[i], DNA_PORTNUM);
@@ -1631,7 +1752,7 @@ dna_get_shared_servers(struct configEntry *config_entry, PRCList **servers)
} else {
/* Find the right slot for this entry. We
* want to order the entries based off of
- * the remaining number of values, higest
+ * the remaining number of values, highest
* to lowest. */
struct dnaServer *sitem;
PRCList* item = PR_LIST_HEAD(*servers);
@@ -1908,6 +2029,36 @@ static int dna_dn_is_config(char *dn)
return ret;
}
+static int
+dna_dn_is_shared_config(Slapi_PBlock *pb, char *dn)
+{
+ struct configEntry *config_entry = NULL;
+ Slapi_Entry *entry = NULL;
+ Slapi_Attr *attr = NULL;
+ PRCList *list = NULL;
+ int ret = 0;
+
+ dna_read_lock();
+ if (!PR_CLIST_IS_EMPTY(dna_global_config)) {
+ list = PR_LIST_HEAD(dna_global_config);
+ while (list != dna_global_config) {
+ config_entry = (struct configEntry *) list;
+ if (slapi_dn_issuffix(dn, config_entry->shared_cfg_base)) {
+ slapi_pblock_get(pb, SLAPI_ENTRY_POST_OP, &entry);
+ /* If the entry has the dnaHost attribute, it is a shared entry */
+ if (slapi_entry_attr_find(entry, DNA_HOSTNAME, &attr) == 0) {
+ ret = 1;
+ break;
+ }
+ }
+ list = PR_NEXT_LINK(list);
+ }
+ }
+ dna_unlock();
+
+ return ret;
+}
+
#define DNA_LDAP_TAG_SK_REVERSE 0x81L
static LDAPControl *dna_build_sort_control(const char *attr)
@@ -2236,6 +2387,39 @@ static int dna_get_next_value(struct configEntry *config_entry,
}
/*
+ * Get a value from the global server list. The dna_server_read_lock()
+ * should be held prior to calling this function.
+ */
+static int
+dna_get_shared_config_attr_val(struct configEntry *config_entry, char *attr, char *value)
+{
+ struct dnaServer *server = NULL;
+ Slapi_DN *server_sdn = NULL;
+ int found = 0;
+
+ server_sdn = slapi_sdn_new_dn_byref(config_entry->shared_cfg_dn);
+ if (dna_global_servers) {
+ server = dna_global_servers;
+ while (server) {
+ if(slapi_sdn_compare(server->sdn, server_sdn) == 0){
+ if(strcmp(attr, DNA_REMOTE_BIND_METHOD) == 0){
+ PR_snprintf(value, DNA_REMOTE_BUFSIZ, "%s", server->remote_bind_method);
+ found = 1;
+ break;
+ } else if(strcmp(attr, DNA_REMOTE_CONN_PROT) == 0){
+ PR_snprintf(value, DNA_REMOTE_BUFSIZ, "%s", server->remote_conn_prot);
+ found = 1;
+ break;
+ }
+ }
+ server = server->next;
+ }
+ }
+ slapi_sdn_free(&server_sdn);
+
+ return found;
+}
+/*
* dna_update_shared_config()
*
* Updates the shared config entry if one is
@@ -2287,8 +2471,9 @@ dna_update_shared_config(struct configEntry * config_entry)
* already exist, we add it. */
if (ret == LDAP_NO_SUCH_OBJECT) {
Slapi_Entry *e = NULL;
- Slapi_DN *sdn =
- slapi_sdn_new_normdn_byref(config_entry->shared_cfg_dn);
+ Slapi_DN *sdn = slapi_sdn_new_normdn_byref(config_entry->shared_cfg_dn);
+ char bind_meth[DNA_REMOTE_BUFSIZ];
+ char conn_prot[DNA_REMOTE_BUFSIZ];
/* Set up the new shared config entry */
e = slapi_entry_alloc();
@@ -2304,6 +2489,16 @@ dna_update_shared_config(struct configEntry * config_entry)
}
slapi_entry_add_string(e, DNA_REMAINING, remaining_vals);
+ /* Grab the remote server settings */
+ dna_server_read_lock();
+ if(dna_get_shared_config_attr_val(config_entry, DNA_REMOTE_BIND_METHOD, bind_meth)) {
+ slapi_entry_add_string(e, DNA_REMOTE_BIND_METHOD, bind_meth);
+ }
+ if(dna_get_shared_config_attr_val(config_entry, DNA_REMOTE_CONN_PROT, conn_prot)){
+ slapi_entry_add_string(e, DNA_REMOTE_CONN_PROT,conn_prot);
+ }
+ dna_server_unlock();
+
/* clear pb for re-use */
slapi_pblock_init(pb);
@@ -3893,6 +4088,9 @@ static int dna_config_check_post_op(Slapi_PBlock * pb)
if (dna_dn_is_config(dn)) {
dna_load_plugin_config(pb, 0);
}
+ if(dna_dn_is_shared_config(pb, dn) == 0){
+ dna_load_shared_servers();
+ }
}
}
9 years, 5 months