|
|
26521d |
From 669d0b288ca55a144fd1f5ba30199d2d2bb82061 Mon Sep 17 00:00:00 2001
|
|
|
26521d |
From: Mark Reynolds <mreynolds@redhat.com>
|
|
|
26521d |
Date: Thu, 7 Mar 2019 15:38:25 -0500
|
|
|
26521d |
Subject: [PATCH] Ticket 50260 - backend txn plugins can corrupt entry cache
|
|
|
26521d |
|
|
|
26521d |
Bug Description: If a nested backend txn plugin fails, any updates
|
|
|
26521d |
it made that went into the entry cache still persist
|
|
|
26521d |
after the database transaction is aborted.
|
|
|
26521d |
|
|
|
26521d |
Fix Description: In order to be sure the entry cache is not corrupted
|
|
|
26521d |
after a backend txn plugin failure we need to flush
|
|
|
26521d |
all the cache entries that were added to the cache
|
|
|
26521d |
after the parent operation was started.
|
|
|
26521d |
|
|
|
26521d |
To do this we record the start time the original operation,
|
|
|
26521d |
(or parent operation), and we record the time any entry
|
|
|
26521d |
is added to the cache. Then on failure we do a comparision
|
|
|
26521d |
and remove the entry from the cache if it's not in use.
|
|
|
26521d |
If it is in use we add a "invalid" flag which triggers
|
|
|
26521d |
the entry to be removed when the cache entry is returned
|
|
|
26521d |
by the owner.
|
|
|
26521d |
|
|
|
26521d |
https://pagure.io/389-ds-base/issue/50260
|
|
|
26521d |
|
|
|
26521d |
CI tested and ASAN approved.
|
|
|
26521d |
|
|
|
26521d |
Reviewed by: firstyear, tbordaz, and lkrispen (Thanks!!!)
|
|
|
26521d |
|
|
|
26521d |
(cherry picked from commit 7ba8a80cfbaed9f6d727f98ed8c284943b3295e1)
|
|
|
26521d |
---
|
|
|
26521d |
dirsrvtests/tests/suites/betxns/betxn_test.py | 114 ++++++++++++++++--
|
|
|
26521d |
ldap/servers/slapd/back-ldbm/back-ldbm.h | 68 ++++++-----
|
|
|
26521d |
ldap/servers/slapd/back-ldbm/backentry.c | 3 +-
|
|
|
26521d |
ldap/servers/slapd/back-ldbm/cache.c | 112 ++++++++++++++++-
|
|
|
26521d |
ldap/servers/slapd/back-ldbm/ldbm_add.c | 14 +++
|
|
|
26521d |
ldap/servers/slapd/back-ldbm/ldbm_delete.c | 14 +++
|
|
|
26521d |
ldap/servers/slapd/back-ldbm/ldbm_modify.c | 14 +++
|
|
|
26521d |
ldap/servers/slapd/back-ldbm/ldbm_modrdn.c | 32 +++--
|
|
|
26521d |
.../servers/slapd/back-ldbm/proto-back-ldbm.h | 1 +
|
|
|
26521d |
ldap/servers/slapd/slapi-plugin.h | 6 +
|
|
|
26521d |
10 files changed, 321 insertions(+), 57 deletions(-)
|
|
|
26521d |
|
|
|
26521d |
diff --git a/dirsrvtests/tests/suites/betxns/betxn_test.py b/dirsrvtests/tests/suites/betxns/betxn_test.py
|
|
|
26521d |
index 48181a9ea..f03fb93cc 100644
|
|
|
26521d |
--- a/dirsrvtests/tests/suites/betxns/betxn_test.py
|
|
|
26521d |
+++ b/dirsrvtests/tests/suites/betxns/betxn_test.py
|
|
|
26521d |
@@ -7,12 +7,10 @@
|
|
|
26521d |
# --- END COPYRIGHT BLOCK ---
|
|
|
26521d |
#
|
|
|
26521d |
import pytest
|
|
|
26521d |
-import six
|
|
|
26521d |
import ldap
|
|
|
26521d |
from lib389.tasks import *
|
|
|
26521d |
from lib389.utils import *
|
|
|
26521d |
from lib389.topologies import topology_st
|
|
|
26521d |
-
|
|
|
26521d |
from lib389._constants import DEFAULT_SUFFIX, PLUGIN_7_BIT_CHECK, PLUGIN_ATTR_UNIQUENESS, PLUGIN_MEMBER_OF
|
|
|
26521d |
|
|
|
26521d |
logging.getLogger(__name__).setLevel(logging.DEBUG)
|
|
|
26521d |
@@ -249,8 +247,8 @@ def test_betxn_memberof(topology_st, dynamic_plugins):
|
|
|
26521d |
log.info('test_betxn_memberof: PASSED')
|
|
|
26521d |
|
|
|
26521d |
|
|
|
26521d |
-def test_betxn_modrdn_memberof(topology_st):
|
|
|
26521d |
- """Test modrdn operartions and memberOf
|
|
|
26521d |
+def test_betxn_modrdn_memberof_cache_corruption(topology_st):
|
|
|
26521d |
+ """Test modrdn operations and memberOf
|
|
|
26521d |
|
|
|
26521d |
:id: 70d0b96e-b693-4bf7-bbf5-102a66ac5994
|
|
|
26521d |
|
|
|
26521d |
@@ -285,18 +283,18 @@ def test_betxn_modrdn_memberof(topology_st):
|
|
|
26521d |
|
|
|
26521d |
# Create user and add it to group
|
|
|
26521d |
users = UserAccounts(topology_st.standalone, basedn=DEFAULT_SUFFIX)
|
|
|
26521d |
- user = users.create(properties=TEST_USER_PROPERTIES)
|
|
|
26521d |
+ user = users.ensure_state(properties=TEST_USER_PROPERTIES)
|
|
|
26521d |
if not ds_is_older('1.3.7'):
|
|
|
26521d |
user.remove('objectClass', 'nsMemberOf')
|
|
|
26521d |
|
|
|
26521d |
group.add_member(user.dn)
|
|
|
26521d |
|
|
|
26521d |
# Attempt modrdn that should fail, but the original entry should stay in the cache
|
|
|
26521d |
- with pytest.raises(ldap.OBJECTCLASS_VIOLATION):
|
|
|
26521d |
+ with pytest.raises(ldap.OBJECT_CLASS_VIOLATION):
|
|
|
26521d |
group.rename('cn=group_to_people', newsuperior=peoplebase)
|
|
|
26521d |
|
|
|
26521d |
# Should fail, but not with NO_SUCH_OBJECT as the original entry should still be in the cache
|
|
|
26521d |
- with pytest.raises(ldap.OBJECTCLASS_VIOLATION):
|
|
|
26521d |
+ with pytest.raises(ldap.OBJECT_CLASS_VIOLATION):
|
|
|
26521d |
group.rename('cn=group_to_people', newsuperior=peoplebase)
|
|
|
26521d |
|
|
|
26521d |
#
|
|
|
26521d |
@@ -305,6 +303,108 @@ def test_betxn_modrdn_memberof(topology_st):
|
|
|
26521d |
log.info('test_betxn_modrdn_memberof: PASSED')
|
|
|
26521d |
|
|
|
26521d |
|
|
|
26521d |
+def test_ri_and_mep_cache_corruption(topology_st):
|
|
|
26521d |
+ """Test RI plugin aborts change after MEP plugin fails.
|
|
|
26521d |
+ This is really testing the entry cache for corruption
|
|
|
26521d |
+
|
|
|
26521d |
+ :id: 70d0b96e-b693-4bf7-bbf5-102a66ac5995
|
|
|
26521d |
+
|
|
|
26521d |
+ :setup: Standalone instance
|
|
|
26521d |
+
|
|
|
26521d |
+ :steps: 1. Enable and configure mep and ri plugins
|
|
|
26521d |
+ 2. Add user and add it to a group
|
|
|
26521d |
+ 3. Disable MEP plugin and remove MEP group
|
|
|
26521d |
+ 4. Delete user
|
|
|
26521d |
+ 5. Check that user is still a member of the group
|
|
|
26521d |
+
|
|
|
26521d |
+ :expectedresults:
|
|
|
26521d |
+ 1. Success
|
|
|
26521d |
+ 2. Success
|
|
|
26521d |
+ 3. Success
|
|
|
26521d |
+ 4. It fails with NO_SUCH_OBJECT
|
|
|
26521d |
+ 5. Success
|
|
|
26521d |
+
|
|
|
26521d |
+ """
|
|
|
26521d |
+ # Start plugins
|
|
|
26521d |
+ topology_st.standalone.config.set('nsslapd-dynamic-plugins', 'on')
|
|
|
26521d |
+ mep_plugin = ManagedEntriesPlugin(topology_st.standalone)
|
|
|
26521d |
+ mep_plugin.enable()
|
|
|
26521d |
+ ri_plugin = ReferentialIntegrityPlugin(topology_st.standalone)
|
|
|
26521d |
+ ri_plugin.enable()
|
|
|
26521d |
+
|
|
|
26521d |
+ # Add our org units
|
|
|
26521d |
+ ous = OrganizationalUnits(topology_st.standalone, DEFAULT_SUFFIX)
|
|
|
26521d |
+ ou_people = ous.create(properties={'ou': 'managed_people'})
|
|
|
26521d |
+ ou_groups = ous.create(properties={'ou': 'managed_groups'})
|
|
|
26521d |
+
|
|
|
26521d |
+ # Configure MEP
|
|
|
26521d |
+ mep_templates = MEPTemplates(topology_st.standalone, DEFAULT_SUFFIX)
|
|
|
26521d |
+ mep_template1 = mep_templates.create(properties={
|
|
|
26521d |
+ 'cn': 'MEP template',
|
|
|
26521d |
+ 'mepRDNAttr': 'cn',
|
|
|
26521d |
+ 'mepStaticAttr': 'objectclass: posixGroup|objectclass: extensibleObject'.split('|'),
|
|
|
26521d |
+ 'mepMappedAttr': 'cn: $cn|uid: $cn|gidNumber: $uidNumber'.split('|')
|
|
|
26521d |
+ })
|
|
|
26521d |
+ mep_configs = MEPConfigs(topology_st.standalone)
|
|
|
26521d |
+ mep_configs.create(properties={'cn': 'config',
|
|
|
26521d |
+ 'originScope': ou_people.dn,
|
|
|
26521d |
+ 'originFilter': 'objectclass=posixAccount',
|
|
|
26521d |
+ 'managedBase': ou_groups.dn,
|
|
|
26521d |
+ 'managedTemplate': mep_template1.dn})
|
|
|
26521d |
+
|
|
|
26521d |
+ # Add an entry that meets the MEP scope
|
|
|
26521d |
+ users = UserAccounts(topology_st.standalone, DEFAULT_SUFFIX,
|
|
|
26521d |
+ rdn='ou={}'.format(ou_people.rdn))
|
|
|
26521d |
+ user = users.create(properties={
|
|
|
26521d |
+ 'uid': 'test-user1',
|
|
|
26521d |
+ 'cn': 'test-user',
|
|
|
26521d |
+ 'sn': 'test-user',
|
|
|
26521d |
+ 'uidNumber': '10011',
|
|
|
26521d |
+ 'gidNumber': '20011',
|
|
|
26521d |
+ 'homeDirectory': '/home/test-user1'
|
|
|
26521d |
+ })
|
|
|
26521d |
+
|
|
|
26521d |
+ # Add group
|
|
|
26521d |
+ groups = Groups(topology_st.standalone, DEFAULT_SUFFIX)
|
|
|
26521d |
+ user_group = groups.ensure_state(properties={'cn': 'group', 'member': user.dn})
|
|
|
26521d |
+
|
|
|
26521d |
+ # Check if a managed group entry was created
|
|
|
26521d |
+ mep_group = Group(topology_st.standalone, dn='cn={},{}'.format(user.rdn, ou_groups.dn))
|
|
|
26521d |
+ if not mep_group.exists():
|
|
|
26521d |
+ log.fatal("MEP group was not created for the user")
|
|
|
26521d |
+ assert False
|
|
|
26521d |
+
|
|
|
26521d |
+ # Mess with MEP so it fails
|
|
|
26521d |
+ mep_plugin.disable()
|
|
|
26521d |
+ mep_group.delete()
|
|
|
26521d |
+ mep_plugin.enable()
|
|
|
26521d |
+
|
|
|
26521d |
+ # Add another group for verify entry cache is not corrupted
|
|
|
26521d |
+ test_group = groups.create(properties={'cn': 'test_group'})
|
|
|
26521d |
+
|
|
|
26521d |
+ # Delete user, should fail, and user should still be a member
|
|
|
26521d |
+ with pytest.raises(ldap.NO_SUCH_OBJECT):
|
|
|
26521d |
+ user.delete()
|
|
|
26521d |
+
|
|
|
26521d |
+ # Verify membership is intact
|
|
|
26521d |
+ if not user_group.is_member(user.dn):
|
|
|
26521d |
+ log.fatal("Member was incorrectly removed from the group!! Or so it seems")
|
|
|
26521d |
+
|
|
|
26521d |
+ # Restart server and test again in case this was a cache issue
|
|
|
26521d |
+ topology_st.standalone.restart()
|
|
|
26521d |
+ if user_group.is_member(user.dn):
|
|
|
26521d |
+ log.info("The entry cache was corrupted")
|
|
|
26521d |
+ assert False
|
|
|
26521d |
+
|
|
|
26521d |
+ assert False
|
|
|
26521d |
+
|
|
|
26521d |
+ # Verify test group is still found in entry cache by deleting it
|
|
|
26521d |
+ test_group.delete()
|
|
|
26521d |
+
|
|
|
26521d |
+ # Success
|
|
|
26521d |
+ log.info("Test PASSED")
|
|
|
26521d |
+
|
|
|
26521d |
+
|
|
|
26521d |
if __name__ == '__main__':
|
|
|
26521d |
# Run isolated
|
|
|
26521d |
# -s for DEBUG mode
|
|
|
26521d |
diff --git a/ldap/servers/slapd/back-ldbm/back-ldbm.h b/ldap/servers/slapd/back-ldbm/back-ldbm.h
|
|
|
26521d |
index 4727961a9..6cac605c0 100644
|
|
|
26521d |
--- a/ldap/servers/slapd/back-ldbm/back-ldbm.h
|
|
|
26521d |
+++ b/ldap/servers/slapd/back-ldbm/back-ldbm.h
|
|
|
26521d |
@@ -312,48 +312,52 @@ typedef struct
|
|
|
26521d |
|
|
|
26521d |
struct backcommon
|
|
|
26521d |
{
|
|
|
26521d |
- int ep_type; /* to distinguish backdn from backentry */
|
|
|
26521d |
- struct backcommon *ep_lrunext; /* for the cache */
|
|
|
26521d |
- struct backcommon *ep_lruprev; /* for the cache */
|
|
|
26521d |
- ID ep_id; /* entry id */
|
|
|
26521d |
- char ep_state; /* state in the cache */
|
|
|
26521d |
-#define ENTRY_STATE_DELETED 0x1 /* entry is marked as deleted */
|
|
|
26521d |
-#define ENTRY_STATE_CREATING 0x2 /* entry is being created; don't touch it */
|
|
|
26521d |
-#define ENTRY_STATE_NOTINCACHE 0x4 /* cache_add failed; not in the cache */
|
|
|
26521d |
- int ep_refcnt; /* entry reference cnt */
|
|
|
26521d |
- size_t ep_size; /* for cache tracking */
|
|
|
26521d |
+ int32_t ep_type; /* to distinguish backdn from backentry */
|
|
|
26521d |
+ struct backcommon *ep_lrunext; /* for the cache */
|
|
|
26521d |
+ struct backcommon *ep_lruprev; /* for the cache */
|
|
|
26521d |
+ ID ep_id; /* entry id */
|
|
|
26521d |
+ uint8_t ep_state; /* state in the cache */
|
|
|
26521d |
+#define ENTRY_STATE_DELETED 0x1 /* entry is marked as deleted */
|
|
|
26521d |
+#define ENTRY_STATE_CREATING 0x2 /* entry is being created; don't touch it */
|
|
|
26521d |
+#define ENTRY_STATE_NOTINCACHE 0x4 /* cache_add failed; not in the cache */
|
|
|
26521d |
+#define ENTRY_STATE_INVALID 0x8 /* cache entry is invalid and needs to be removed */
|
|
|
26521d |
+ int32_t ep_refcnt; /* entry reference cnt */
|
|
|
26521d |
+ size_t ep_size; /* for cache tracking */
|
|
|
26521d |
+ struct timespec ep_create_time; /* the time the entry was added to the cache */
|
|
|
26521d |
};
|
|
|
26521d |
|
|
|
26521d |
-/* From ep_type through ep_size MUST be identical to backcommon */
|
|
|
26521d |
+/* From ep_type through ep_create_time MUST be identical to backcommon */
|
|
|
26521d |
struct backentry
|
|
|
26521d |
{
|
|
|
26521d |
- int ep_type; /* to distinguish backdn from backentry */
|
|
|
26521d |
- struct backcommon *ep_lrunext; /* for the cache */
|
|
|
26521d |
- struct backcommon *ep_lruprev; /* for the cache */
|
|
|
26521d |
- ID ep_id; /* entry id */
|
|
|
26521d |
- char ep_state; /* state in the cache */
|
|
|
26521d |
- int ep_refcnt; /* entry reference cnt */
|
|
|
26521d |
- size_t ep_size; /* for cache tracking */
|
|
|
26521d |
- Slapi_Entry *ep_entry; /* real entry */
|
|
|
26521d |
+ int32_t ep_type; /* to distinguish backdn from backentry */
|
|
|
26521d |
+ struct backcommon *ep_lrunext; /* for the cache */
|
|
|
26521d |
+ struct backcommon *ep_lruprev; /* for the cache */
|
|
|
26521d |
+ ID ep_id; /* entry id */
|
|
|
26521d |
+ uint8_t ep_state; /* state in the cache */
|
|
|
26521d |
+ int32_t ep_refcnt; /* entry reference cnt */
|
|
|
26521d |
+ size_t ep_size; /* for cache tracking */
|
|
|
26521d |
+ struct timespec ep_create_time; /* the time the entry was added to the cache */
|
|
|
26521d |
+ Slapi_Entry *ep_entry; /* real entry */
|
|
|
26521d |
Slapi_Entry *ep_vlventry;
|
|
|
26521d |
- void *ep_dn_link; /* linkage for the 3 hash */
|
|
|
26521d |
- void *ep_id_link; /* tables used for */
|
|
|
26521d |
- void *ep_uuid_link; /* looking up entries */
|
|
|
26521d |
- PRMonitor *ep_mutexp; /* protection for mods; make it reentrant */
|
|
|
26521d |
+ void *ep_dn_link; /* linkage for the 3 hash */
|
|
|
26521d |
+ void *ep_id_link; /* tables used for */
|
|
|
26521d |
+ void *ep_uuid_link; /* looking up entries */
|
|
|
26521d |
+ PRMonitor *ep_mutexp; /* protection for mods; make it reentrant */
|
|
|
26521d |
};
|
|
|
26521d |
|
|
|
26521d |
-/* From ep_type through ep_size MUST be identical to backcommon */
|
|
|
26521d |
+/* From ep_type through ep_create_time MUST be identical to backcommon */
|
|
|
26521d |
struct backdn
|
|
|
26521d |
{
|
|
|
26521d |
- int ep_type; /* to distinguish backdn from backentry */
|
|
|
26521d |
- struct backcommon *ep_lrunext; /* for the cache */
|
|
|
26521d |
- struct backcommon *ep_lruprev; /* for the cache */
|
|
|
26521d |
- ID ep_id; /* entry id */
|
|
|
26521d |
- char ep_state; /* state in the cache; share ENTRY_STATE_* */
|
|
|
26521d |
- int ep_refcnt; /* entry reference cnt */
|
|
|
26521d |
- size_t ep_size; /* for cache tracking */
|
|
|
26521d |
+ int32_t ep_type; /* to distinguish backdn from backentry */
|
|
|
26521d |
+ struct backcommon *ep_lrunext; /* for the cache */
|
|
|
26521d |
+ struct backcommon *ep_lruprev; /* for the cache */
|
|
|
26521d |
+ ID ep_id; /* entry id */
|
|
|
26521d |
+ uint8_t ep_state; /* state in the cache; share ENTRY_STATE_* */
|
|
|
26521d |
+ int32_t ep_refcnt; /* entry reference cnt */
|
|
|
26521d |
+ size_t ep_size; /* for cache tracking */
|
|
|
26521d |
+ struct timespec ep_create_time; /* the time the entry was added to the cache */
|
|
|
26521d |
Slapi_DN *dn_sdn;
|
|
|
26521d |
- void *dn_id_link; /* for hash table */
|
|
|
26521d |
+ void *dn_id_link; /* for hash table */
|
|
|
26521d |
};
|
|
|
26521d |
|
|
|
26521d |
/* for the in-core cache of entries */
|
|
|
26521d |
diff --git a/ldap/servers/slapd/back-ldbm/backentry.c b/ldap/servers/slapd/back-ldbm/backentry.c
|
|
|
26521d |
index f2fe780db..972842bcb 100644
|
|
|
26521d |
--- a/ldap/servers/slapd/back-ldbm/backentry.c
|
|
|
26521d |
+++ b/ldap/servers/slapd/back-ldbm/backentry.c
|
|
|
26521d |
@@ -23,7 +23,8 @@ backentry_free(struct backentry **bep)
|
|
|
26521d |
return;
|
|
|
26521d |
}
|
|
|
26521d |
ep = *bep;
|
|
|
26521d |
- PR_ASSERT(ep->ep_state & (ENTRY_STATE_DELETED | ENTRY_STATE_NOTINCACHE));
|
|
|
26521d |
+
|
|
|
26521d |
+ PR_ASSERT(ep->ep_state & (ENTRY_STATE_DELETED | ENTRY_STATE_NOTINCACHE | ENTRY_STATE_INVALID));
|
|
|
26521d |
if (ep->ep_entry != NULL) {
|
|
|
26521d |
slapi_entry_free(ep->ep_entry);
|
|
|
26521d |
}
|
|
|
26521d |
diff --git a/ldap/servers/slapd/back-ldbm/cache.c b/ldap/servers/slapd/back-ldbm/cache.c
|
|
|
26521d |
index 86e1f7b39..458d7912f 100644
|
|
|
26521d |
--- a/ldap/servers/slapd/back-ldbm/cache.c
|
|
|
26521d |
+++ b/ldap/servers/slapd/back-ldbm/cache.c
|
|
|
26521d |
@@ -56,11 +56,14 @@
|
|
|
26521d |
#define LOG(...)
|
|
|
26521d |
#endif
|
|
|
26521d |
|
|
|
26521d |
-#define LRU_DETACH(cache, e) lru_detach((cache), (void *)(e))
|
|
|
26521d |
+typedef enum {
|
|
|
26521d |
+ ENTRY_CACHE,
|
|
|
26521d |
+ DN_CACHE,
|
|
|
26521d |
+} CacheType;
|
|
|
26521d |
|
|
|
26521d |
+#define LRU_DETACH(cache, e) lru_detach((cache), (void *)(e))
|
|
|
26521d |
#define CACHE_LRU_HEAD(cache, type) ((type)((cache)->c_lruhead))
|
|
|
26521d |
#define CACHE_LRU_TAIL(cache, type) ((type)((cache)->c_lrutail))
|
|
|
26521d |
-
|
|
|
26521d |
#define BACK_LRU_NEXT(entry, type) ((type)((entry)->ep_lrunext))
|
|
|
26521d |
#define BACK_LRU_PREV(entry, type) ((type)((entry)->ep_lruprev))
|
|
|
26521d |
|
|
|
26521d |
@@ -185,6 +188,7 @@ new_hash(u_long size, u_long offset, HashFn hfn, HashTestFn tfn)
|
|
|
26521d |
int
|
|
|
26521d |
add_hash(Hashtable *ht, void *key, uint32_t keylen, void *entry, void **alt)
|
|
|
26521d |
{
|
|
|
26521d |
+ struct backcommon *back_entry = (struct backcommon *)entry;
|
|
|
26521d |
u_long val, slot;
|
|
|
26521d |
void *e;
|
|
|
26521d |
|
|
|
26521d |
@@ -202,6 +206,7 @@ add_hash(Hashtable *ht, void *key, uint32_t keylen, void *entry, void **alt)
|
|
|
26521d |
e = HASH_NEXT(ht, e);
|
|
|
26521d |
}
|
|
|
26521d |
/* ok, it's not already there, so add it */
|
|
|
26521d |
+ back_entry->ep_create_time = slapi_current_rel_time_hr();
|
|
|
26521d |
HASH_NEXT(ht, entry) = ht->slot[slot];
|
|
|
26521d |
ht->slot[slot] = entry;
|
|
|
26521d |
return 1;
|
|
|
26521d |
@@ -492,6 +497,89 @@ cache_make_hashes(struct cache *cache, int type)
|
|
|
26521d |
}
|
|
|
26521d |
}
|
|
|
26521d |
|
|
|
26521d |
+/*
|
|
|
26521d |
+ * Helper function for flush_hash() to calculate if the entry should be
|
|
|
26521d |
+ * removed from the cache.
|
|
|
26521d |
+ */
|
|
|
26521d |
+static int32_t
|
|
|
26521d |
+flush_remove_entry(struct timespec *entry_time, struct timespec *start_time)
|
|
|
26521d |
+{
|
|
|
26521d |
+ struct timespec diff;
|
|
|
26521d |
+
|
|
|
26521d |
+ slapi_timespec_diff(entry_time, start_time, &diff);
|
|
|
26521d |
+ if (diff.tv_sec >= 0) {
|
|
|
26521d |
+ return 1;
|
|
|
26521d |
+ } else {
|
|
|
26521d |
+ return 0;
|
|
|
26521d |
+ }
|
|
|
26521d |
+}
|
|
|
26521d |
+
|
|
|
26521d |
+/*
|
|
|
26521d |
+ * Flush all the cache entries that were added after the "start time"
|
|
|
26521d |
+ * This is called when a backend transaction plugin fails, and we need
|
|
|
26521d |
+ * to remove all the possible invalid entries in the cache.
|
|
|
26521d |
+ *
|
|
|
26521d |
+ * If the ref count is 0, we can straight up remove it from the cache, but
|
|
|
26521d |
+ * if the ref count is greater than 1, then the entry is currently in use.
|
|
|
26521d |
+ * In the later case we set the entry state to ENTRY_STATE_INVALID, and
|
|
|
26521d |
+ * when the owning thread cache_returns() the cache entry is automatically
|
|
|
26521d |
+ * removed so another thread can not use/lock the invalid cache entry.
|
|
|
26521d |
+ */
|
|
|
26521d |
+static void
|
|
|
26521d |
+flush_hash(struct cache *cache, struct timespec *start_time, int32_t type)
|
|
|
26521d |
+{
|
|
|
26521d |
+ void *e, *laste = NULL;
|
|
|
26521d |
+ Hashtable *ht = cache->c_idtable;
|
|
|
26521d |
+
|
|
|
26521d |
+ cache_lock(cache);
|
|
|
26521d |
+
|
|
|
26521d |
+ for (size_t i = 0; i < ht->size; i++) {
|
|
|
26521d |
+ e = ht->slot[i];
|
|
|
26521d |
+ while (e) {
|
|
|
26521d |
+ struct backcommon *entry = (struct backcommon *)e;
|
|
|
26521d |
+ uint64_t remove_it = 0;
|
|
|
26521d |
+ if (flush_remove_entry(&entry->ep_create_time, start_time)) {
|
|
|
26521d |
+ /* Mark the entry to be removed */
|
|
|
26521d |
+ slapi_log_err(SLAPI_LOG_CACHE, "flush_hash", "[%s] Removing entry id (%d)\n",
|
|
|
26521d |
+ type ? "DN CACHE" : "ENTRY CACHE", entry->ep_id);
|
|
|
26521d |
+ remove_it = 1;
|
|
|
26521d |
+ }
|
|
|
26521d |
+ laste = e;
|
|
|
26521d |
+ e = HASH_NEXT(ht, e);
|
|
|
26521d |
+
|
|
|
26521d |
+ if (remove_it) {
|
|
|
26521d |
+ /* since we have the cache lock we know we can trust refcnt */
|
|
|
26521d |
+ entry->ep_state |= ENTRY_STATE_INVALID;
|
|
|
26521d |
+ if (entry->ep_refcnt == 0) {
|
|
|
26521d |
+ entry->ep_refcnt++;
|
|
|
26521d |
+ lru_delete(cache, laste);
|
|
|
26521d |
+ if (type == ENTRY_CACHE) {
|
|
|
26521d |
+ entrycache_remove_int(cache, laste);
|
|
|
26521d |
+ entrycache_return(cache, (struct backentry **)&laste);
|
|
|
26521d |
+ } else {
|
|
|
26521d |
+ dncache_remove_int(cache, laste);
|
|
|
26521d |
+ dncache_return(cache, (struct backdn **)&laste);
|
|
|
26521d |
+ }
|
|
|
26521d |
+ } else {
|
|
|
26521d |
+ /* Entry flagged for removal */
|
|
|
26521d |
+ slapi_log_err(SLAPI_LOG_CACHE, "flush_hash",
|
|
|
26521d |
+ "[%s] Flagging entry to be removed later: id (%d) refcnt: %d\n",
|
|
|
26521d |
+ type ? "DN CACHE" : "ENTRY CACHE", entry->ep_id, entry->ep_refcnt);
|
|
|
26521d |
+ }
|
|
|
26521d |
+ }
|
|
|
26521d |
+ }
|
|
|
26521d |
+ }
|
|
|
26521d |
+
|
|
|
26521d |
+ cache_unlock(cache);
|
|
|
26521d |
+}
|
|
|
26521d |
+
|
|
|
26521d |
+void
|
|
|
26521d |
+revert_cache(ldbm_instance *inst, struct timespec *start_time)
|
|
|
26521d |
+{
|
|
|
26521d |
+ flush_hash(&inst->inst_cache, start_time, ENTRY_CACHE);
|
|
|
26521d |
+ flush_hash(&inst->inst_dncache, start_time, DN_CACHE);
|
|
|
26521d |
+}
|
|
|
26521d |
+
|
|
|
26521d |
/* initialize the cache */
|
|
|
26521d |
int
|
|
|
26521d |
cache_init(struct cache *cache, uint64_t maxsize, long maxentries, int type)
|
|
|
26521d |
@@ -1142,7 +1230,7 @@ entrycache_return(struct cache *cache, struct backentry **bep)
|
|
|
26521d |
} else {
|
|
|
26521d |
ASSERT(e->ep_refcnt > 0);
|
|
|
26521d |
if (!--e->ep_refcnt) {
|
|
|
26521d |
- if (e->ep_state & ENTRY_STATE_DELETED) {
|
|
|
26521d |
+ if (e->ep_state & (ENTRY_STATE_DELETED | ENTRY_STATE_INVALID)) {
|
|
|
26521d |
const char *ndn = slapi_sdn_get_ndn(backentry_get_sdn(e));
|
|
|
26521d |
if (ndn) {
|
|
|
26521d |
/*
|
|
|
26521d |
@@ -1154,6 +1242,13 @@ entrycache_return(struct cache *cache, struct backentry **bep)
|
|
|
26521d |
LOG("entrycache_return -Failed to remove %s from dn table\n", ndn);
|
|
|
26521d |
}
|
|
|
26521d |
}
|
|
|
26521d |
+ if (e->ep_state & ENTRY_STATE_INVALID) {
|
|
|
26521d |
+ /* Remove it from the hash table before we free the back entry */
|
|
|
26521d |
+ slapi_log_err(SLAPI_LOG_CACHE, "entrycache_return",
|
|
|
26521d |
+ "Finally flushing invalid entry: %d (%s)\n",
|
|
|
26521d |
+ e->ep_id, backentry_get_ndn(e));
|
|
|
26521d |
+ entrycache_remove_int(cache, e);
|
|
|
26521d |
+ }
|
|
|
26521d |
backentry_free(bep);
|
|
|
26521d |
} else {
|
|
|
26521d |
lru_add(cache, e);
|
|
|
26521d |
@@ -1535,7 +1630,7 @@ cache_lock_entry(struct cache *cache, struct backentry *e)
|
|
|
26521d |
|
|
|
26521d |
/* make sure entry hasn't been deleted now */
|
|
|
26521d |
cache_lock(cache);
|
|
|
26521d |
- if (e->ep_state & (ENTRY_STATE_DELETED | ENTRY_STATE_NOTINCACHE)) {
|
|
|
26521d |
+ if (e->ep_state & (ENTRY_STATE_DELETED | ENTRY_STATE_NOTINCACHE | ENTRY_STATE_INVALID)) {
|
|
|
26521d |
cache_unlock(cache);
|
|
|
26521d |
PR_ExitMonitor(e->ep_mutexp);
|
|
|
26521d |
LOG("<= cache_lock_entry (DELETED)\n");
|
|
|
26521d |
@@ -1696,7 +1791,14 @@ dncache_return(struct cache *cache, struct backdn **bdn)
|
|
|
26521d |
} else {
|
|
|
26521d |
ASSERT((*bdn)->ep_refcnt > 0);
|
|
|
26521d |
if (!--(*bdn)->ep_refcnt) {
|
|
|
26521d |
- if ((*bdn)->ep_state & ENTRY_STATE_DELETED) {
|
|
|
26521d |
+ if ((*bdn)->ep_state & (ENTRY_STATE_DELETED | ENTRY_STATE_INVALID)) {
|
|
|
26521d |
+ if ((*bdn)->ep_state & ENTRY_STATE_INVALID) {
|
|
|
26521d |
+ /* Remove it from the hash table before we free the back dn */
|
|
|
26521d |
+ slapi_log_err(SLAPI_LOG_CACHE, "dncache_return",
|
|
|
26521d |
+ "Finally flushing invalid entry: %d (%s)\n",
|
|
|
26521d |
+ (*bdn)->ep_id, slapi_sdn_get_dn((*bdn)->dn_sdn));
|
|
|
26521d |
+ dncache_remove_int(cache, (*bdn));
|
|
|
26521d |
+ }
|
|
|
26521d |
backdn_free(bdn);
|
|
|
26521d |
} else {
|
|
|
26521d |
lru_add(cache, (void *)*bdn);
|
|
|
26521d |
diff --git a/ldap/servers/slapd/back-ldbm/ldbm_add.c b/ldap/servers/slapd/back-ldbm/ldbm_add.c
|
|
|
26521d |
index 32c8e71ff..aa5b59aea 100644
|
|
|
26521d |
--- a/ldap/servers/slapd/back-ldbm/ldbm_add.c
|
|
|
26521d |
+++ b/ldap/servers/slapd/back-ldbm/ldbm_add.c
|
|
|
26521d |
@@ -97,6 +97,8 @@ ldbm_back_add(Slapi_PBlock *pb)
|
|
|
26521d |
PRUint64 conn_id;
|
|
|
26521d |
int op_id;
|
|
|
26521d |
int result_sent = 0;
|
|
|
26521d |
+ int32_t parent_op = 0;
|
|
|
26521d |
+ struct timespec parent_time;
|
|
|
26521d |
|
|
|
26521d |
if (slapi_pblock_get(pb, SLAPI_CONN_ID, &conn_id) < 0) {
|
|
|
26521d |
conn_id = 0; /* connection is NULL */
|
|
|
26521d |
@@ -147,6 +149,13 @@ ldbm_back_add(Slapi_PBlock *pb)
|
|
|
26521d |
slapi_entry_delete_values(e, numsubordinates, NULL);
|
|
|
26521d |
|
|
|
26521d |
dblayer_txn_init(li, &txn);
|
|
|
26521d |
+
|
|
|
26521d |
+ if (txn.back_txn_txn == NULL) {
|
|
|
26521d |
+ /* This is the parent operation, get the time */
|
|
|
26521d |
+ parent_op = 1;
|
|
|
26521d |
+ parent_time = slapi_current_rel_time_hr();
|
|
|
26521d |
+ }
|
|
|
26521d |
+
|
|
|
26521d |
/* the calls to perform searches require the parent txn if any
|
|
|
26521d |
so set txn to the parent_txn until we begin the child transaction */
|
|
|
26521d |
if (parent_txn) {
|
|
|
26521d |
@@ -1212,6 +1221,11 @@ ldbm_back_add(Slapi_PBlock *pb)
|
|
|
26521d |
slapi_pblock_set(pb, SLAPI_PLUGIN_OPRETURN, ldap_result_code ? &ldap_result_code : &retval);
|
|
|
26521d |
}
|
|
|
26521d |
slapi_pblock_get(pb, SLAPI_PB_RESULT_TEXT, &ldap_result_message);
|
|
|
26521d |
+
|
|
|
26521d |
+ /* Revert the caches if this is the parent operation */
|
|
|
26521d |
+ if (parent_op) {
|
|
|
26521d |
+ revert_cache(inst, &parent_time);
|
|
|
26521d |
+ }
|
|
|
26521d |
goto error_return;
|
|
|
26521d |
}
|
|
|
26521d |
|
|
|
26521d |
diff --git a/ldap/servers/slapd/back-ldbm/ldbm_delete.c b/ldap/servers/slapd/back-ldbm/ldbm_delete.c
|
|
|
26521d |
index f5f6c1e3a..3f687eb91 100644
|
|
|
26521d |
--- a/ldap/servers/slapd/back-ldbm/ldbm_delete.c
|
|
|
26521d |
+++ b/ldap/servers/slapd/back-ldbm/ldbm_delete.c
|
|
|
26521d |
@@ -79,6 +79,8 @@ ldbm_back_delete(Slapi_PBlock *pb)
|
|
|
26521d |
ID tomb_ep_id = 0;
|
|
|
26521d |
int result_sent = 0;
|
|
|
26521d |
Connection *pb_conn;
|
|
|
26521d |
+ int32_t parent_op = 0;
|
|
|
26521d |
+ struct timespec parent_time;
|
|
|
26521d |
|
|
|
26521d |
if (slapi_pblock_get(pb, SLAPI_CONN_ID, &conn_id) < 0) {
|
|
|
26521d |
conn_id = 0; /* connection is NULL */
|
|
|
26521d |
@@ -100,6 +102,13 @@ ldbm_back_delete(Slapi_PBlock *pb)
|
|
|
26521d |
dblayer_txn_init(li, &txn);
|
|
|
26521d |
/* the calls to perform searches require the parent txn if any
|
|
|
26521d |
so set txn to the parent_txn until we begin the child transaction */
|
|
|
26521d |
+
|
|
|
26521d |
+ if (txn.back_txn_txn == NULL) {
|
|
|
26521d |
+ /* This is the parent operation, get the time */
|
|
|
26521d |
+ parent_op = 1;
|
|
|
26521d |
+ parent_time = slapi_current_rel_time_hr();
|
|
|
26521d |
+ }
|
|
|
26521d |
+
|
|
|
26521d |
if (parent_txn) {
|
|
|
26521d |
txn.back_txn_txn = parent_txn;
|
|
|
26521d |
} else {
|
|
|
26521d |
@@ -1270,6 +1279,11 @@ replace_entry:
|
|
|
26521d |
slapi_pblock_set(pb, SLAPI_PLUGIN_OPRETURN, &retval);
|
|
|
26521d |
}
|
|
|
26521d |
slapi_pblock_get(pb, SLAPI_PB_RESULT_TEXT, &ldap_result_message);
|
|
|
26521d |
+
|
|
|
26521d |
+ /* Revert the caches if this is the parent operation */
|
|
|
26521d |
+ if (parent_op) {
|
|
|
26521d |
+ revert_cache(inst, &parent_time);
|
|
|
26521d |
+ }
|
|
|
26521d |
goto error_return;
|
|
|
26521d |
}
|
|
|
26521d |
if (parent_found) {
|
|
|
26521d |
diff --git a/ldap/servers/slapd/back-ldbm/ldbm_modify.c b/ldap/servers/slapd/back-ldbm/ldbm_modify.c
|
|
|
26521d |
index cc4319e5f..b90b3e0f0 100644
|
|
|
26521d |
--- a/ldap/servers/slapd/back-ldbm/ldbm_modify.c
|
|
|
26521d |
+++ b/ldap/servers/slapd/back-ldbm/ldbm_modify.c
|
|
|
26521d |
@@ -412,6 +412,8 @@ ldbm_back_modify(Slapi_PBlock *pb)
|
|
|
26521d |
int fixup_tombstone = 0;
|
|
|
26521d |
int ec_locked = 0;
|
|
|
26521d |
int result_sent = 0;
|
|
|
26521d |
+ int32_t parent_op = 0;
|
|
|
26521d |
+ struct timespec parent_time;
|
|
|
26521d |
|
|
|
26521d |
slapi_pblock_get(pb, SLAPI_BACKEND, &be);
|
|
|
26521d |
slapi_pblock_get(pb, SLAPI_PLUGIN_PRIVATE, &li;;
|
|
|
26521d |
@@ -426,6 +428,13 @@ ldbm_back_modify(Slapi_PBlock *pb)
|
|
|
26521d |
dblayer_txn_init(li, &txn); /* must do this before first goto error_return */
|
|
|
26521d |
/* the calls to perform searches require the parent txn if any
|
|
|
26521d |
so set txn to the parent_txn until we begin the child transaction */
|
|
|
26521d |
+
|
|
|
26521d |
+ if (txn.back_txn_txn == NULL) {
|
|
|
26521d |
+ /* This is the parent operation, get the time */
|
|
|
26521d |
+ parent_op = 1;
|
|
|
26521d |
+ parent_time = slapi_current_rel_time_hr();
|
|
|
26521d |
+ }
|
|
|
26521d |
+
|
|
|
26521d |
if (parent_txn) {
|
|
|
26521d |
txn.back_txn_txn = parent_txn;
|
|
|
26521d |
} else {
|
|
|
26521d |
@@ -864,6 +873,11 @@ ldbm_back_modify(Slapi_PBlock *pb)
|
|
|
26521d |
slapi_pblock_set(pb, SLAPI_PLUGIN_OPRETURN, ldap_result_code ? &ldap_result_code : &retval);
|
|
|
26521d |
}
|
|
|
26521d |
slapi_pblock_get(pb, SLAPI_PB_RESULT_TEXT, &ldap_result_message);
|
|
|
26521d |
+
|
|
|
26521d |
+ /* Revert the caches if this is the parent operation */
|
|
|
26521d |
+ if (parent_op) {
|
|
|
26521d |
+ revert_cache(inst, &parent_time);
|
|
|
26521d |
+ }
|
|
|
26521d |
goto error_return;
|
|
|
26521d |
}
|
|
|
26521d |
retval = plugin_call_mmr_plugin_postop(pb, NULL,SLAPI_PLUGIN_BE_TXN_POST_MODIFY_FN);
|
|
|
26521d |
diff --git a/ldap/servers/slapd/back-ldbm/ldbm_modrdn.c b/ldap/servers/slapd/back-ldbm/ldbm_modrdn.c
|
|
|
26521d |
index e4d0337d4..73e50ebcc 100644
|
|
|
26521d |
--- a/ldap/servers/slapd/back-ldbm/ldbm_modrdn.c
|
|
|
26521d |
+++ b/ldap/servers/slapd/back-ldbm/ldbm_modrdn.c
|
|
|
26521d |
@@ -97,6 +97,8 @@ ldbm_back_modrdn(Slapi_PBlock *pb)
|
|
|
26521d |
int op_id;
|
|
|
26521d |
int result_sent = 0;
|
|
|
26521d |
Connection *pb_conn = NULL;
|
|
|
26521d |
+ int32_t parent_op = 0;
|
|
|
26521d |
+ struct timespec parent_time;
|
|
|
26521d |
|
|
|
26521d |
if (slapi_pblock_get(pb, SLAPI_CONN_ID, &conn_id) < 0) {
|
|
|
26521d |
conn_id = 0; /* connection is NULL */
|
|
|
26521d |
@@ -134,6 +136,13 @@ ldbm_back_modrdn(Slapi_PBlock *pb)
|
|
|
26521d |
|
|
|
26521d |
/* dblayer_txn_init needs to be called before "goto error_return" */
|
|
|
26521d |
dblayer_txn_init(li, &txn);
|
|
|
26521d |
+
|
|
|
26521d |
+ if (txn.back_txn_txn == NULL) {
|
|
|
26521d |
+ /* This is the parent operation, get the time */
|
|
|
26521d |
+ parent_op = 1;
|
|
|
26521d |
+ parent_time = slapi_current_rel_time_hr();
|
|
|
26521d |
+ }
|
|
|
26521d |
+
|
|
|
26521d |
/* the calls to perform searches require the parent txn if any
|
|
|
26521d |
so set txn to the parent_txn until we begin the child transaction */
|
|
|
26521d |
if (parent_txn) {
|
|
|
26521d |
@@ -1208,6 +1217,11 @@ ldbm_back_modrdn(Slapi_PBlock *pb)
|
|
|
26521d |
slapi_pblock_set(pb, SLAPI_PLUGIN_OPRETURN, ldap_result_code ? &ldap_result_code : &retval);
|
|
|
26521d |
}
|
|
|
26521d |
slapi_pblock_get(pb, SLAPI_PB_RESULT_TEXT, &ldap_result_message);
|
|
|
26521d |
+
|
|
|
26521d |
+ /* Revert the caches if this is the parent operation */
|
|
|
26521d |
+ if (parent_op) {
|
|
|
26521d |
+ revert_cache(inst, &parent_time);
|
|
|
26521d |
+ }
|
|
|
26521d |
goto error_return;
|
|
|
26521d |
}
|
|
|
26521d |
retval = plugin_call_mmr_plugin_postop(pb, NULL,SLAPI_PLUGIN_BE_TXN_POST_MODRDN_FN);
|
|
|
26521d |
@@ -1353,8 +1367,13 @@ error_return:
|
|
|
26521d |
slapi_pblock_set(pb, SLAPI_PLUGIN_OPRETURN, ldap_result_code ? &ldap_result_code : &retval);
|
|
|
26521d |
}
|
|
|
26521d |
slapi_pblock_get(pb, SLAPI_PB_RESULT_TEXT, &ldap_result_message);
|
|
|
26521d |
+
|
|
|
26521d |
+ /* Revert the caches if this is the parent operation */
|
|
|
26521d |
+ if (parent_op) {
|
|
|
26521d |
+ revert_cache(inst, &parent_time);
|
|
|
26521d |
+ }
|
|
|
26521d |
}
|
|
|
26521d |
- retval = plugin_call_mmr_plugin_postop(pb, NULL,SLAPI_PLUGIN_BE_TXN_POST_MODRDN_FN);
|
|
|
26521d |
+ retval = plugin_call_mmr_plugin_postop(pb, NULL,SLAPI_PLUGIN_BE_TXN_POST_MODRDN_FN);
|
|
|
26521d |
|
|
|
26521d |
/* Release SERIAL LOCK */
|
|
|
26521d |
dblayer_txn_abort(be, &txn); /* abort crashes in case disk full */
|
|
|
26521d |
@@ -1411,17 +1430,6 @@ common_return:
|
|
|
26521d |
"operation failed, the target entry is cleared from dncache (%s)\n", slapi_entry_get_dn(ec->ep_entry));
|
|
|
26521d |
CACHE_REMOVE(&inst->inst_dncache, bdn);
|
|
|
26521d |
CACHE_RETURN(&inst->inst_dncache, &bdn;;
|
|
|
26521d |
- /*
|
|
|
26521d |
- * If the new/invalid entry (ec) is in the cache, that means we need to
|
|
|
26521d |
- * swap it out with the original entry (e) --> to undo the swap that
|
|
|
26521d |
- * modrdn_rename_entry_update_indexes() did.
|
|
|
26521d |
- */
|
|
|
26521d |
- if (cache_is_in_cache(&inst->inst_cache, ec)) {
|
|
|
26521d |
- if (cache_replace(&inst->inst_cache, ec, e) != 0) {
|
|
|
26521d |
- slapi_log_err(SLAPI_LOG_ALERT, "ldbm_back_modrdn",
|
|
|
26521d |
- "failed to replace cache entry after error\n");
|
|
|
26521d |
- }
|
|
|
26521d |
- }
|
|
|
26521d |
}
|
|
|
26521d |
|
|
|
26521d |
if (ec && inst) {
|
|
|
26521d |
diff --git a/ldap/servers/slapd/back-ldbm/proto-back-ldbm.h b/ldap/servers/slapd/back-ldbm/proto-back-ldbm.h
|
|
|
26521d |
index b56f6ef26..e68765bd4 100644
|
|
|
26521d |
--- a/ldap/servers/slapd/back-ldbm/proto-back-ldbm.h
|
|
|
26521d |
+++ b/ldap/servers/slapd/back-ldbm/proto-back-ldbm.h
|
|
|
26521d |
@@ -55,6 +55,7 @@ void cache_unlock_entry(struct cache *cache, struct backentry *e);
|
|
|
26521d |
int cache_replace(struct cache *cache, void *oldptr, void *newptr);
|
|
|
26521d |
int cache_has_otherref(struct cache *cache, void *bep);
|
|
|
26521d |
int cache_is_in_cache(struct cache *cache, void *ptr);
|
|
|
26521d |
+void revert_cache(ldbm_instance *inst, struct timespec *start_time);
|
|
|
26521d |
|
|
|
26521d |
#ifdef CACHE_DEBUG
|
|
|
26521d |
void check_entry_cache(struct cache *cache, struct backentry *e);
|
|
|
26521d |
diff --git a/ldap/servers/slapd/slapi-plugin.h b/ldap/servers/slapd/slapi-plugin.h
|
|
|
26521d |
index 54c195eef..0bc3a6fab 100644
|
|
|
26521d |
--- a/ldap/servers/slapd/slapi-plugin.h
|
|
|
26521d |
+++ b/ldap/servers/slapd/slapi-plugin.h
|
|
|
26521d |
@@ -6765,6 +6765,12 @@ time_t slapi_current_time(void) __attribute__((deprecated));
|
|
|
26521d |
* \return timespec of the current relative system time.
|
|
|
26521d |
*/
|
|
|
26521d |
struct timespec slapi_current_time_hr(void);
|
|
|
26521d |
+/**
|
|
|
26521d |
+ * Returns the current system time as a hr clock
|
|
|
26521d |
+ *
|
|
|
26521d |
+ * \return timespec of the current monotonic time.
|
|
|
26521d |
+ */
|
|
|
26521d |
+struct timespec slapi_current_rel_time_hr(void);
|
|
|
26521d |
/**
|
|
|
26521d |
* Returns the current system time as a hr clock in UTC timezone.
|
|
|
26521d |
* This clock adjusts with ntp steps, and should NOT be
|
|
|
26521d |
--
|
|
|
26521d |
2.17.2
|
|
|
26521d |
|