c8-stream-1.4
imports/c8-stream-1.4/389-ds-base-1.4.3.39-3.module+el8.10.0+21535+f663b039
parent
37ba1e3d57
commit
99fa9f4cac
@ -1,3 +1,3 @@
|
|||||||
99591fa118addaa5e8eef3445092d0b2d12758ba SOURCES/389-ds-base-1.4.3.37.tar.bz2
|
bd9aab32d9cbf9231058d585479813f3420dc872 SOURCES/389-ds-base-1.4.3.39.tar.bz2
|
||||||
1c8f2d0dfbf39fa8cd86363bf3314351ab21f8d4 SOURCES/jemalloc-5.3.0.tar.bz2
|
1c8f2d0dfbf39fa8cd86363bf3314351ab21f8d4 SOURCES/jemalloc-5.3.0.tar.bz2
|
||||||
deb3cdc888660e573fe4be992398f35393c2d1d3 SOURCES/vendor-1.4.3.37-1.tar.gz
|
978b7c5e4a9e5784fddb23ba1abe4dc5a071589f SOURCES/vendor-1.4.3.39-1.tar.gz
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
SOURCES/389-ds-base-1.4.3.37.tar.bz2
|
SOURCES/389-ds-base-1.4.3.39.tar.bz2
|
||||||
SOURCES/jemalloc-5.3.0.tar.bz2
|
SOURCES/jemalloc-5.3.0.tar.bz2
|
||||||
SOURCES/vendor-1.4.3.37-1.tar.gz
|
SOURCES/vendor-1.4.3.39-1.tar.gz
|
||||||
|
@ -0,0 +1,83 @@
|
|||||||
|
From 7d1bc439a07c51b5f4f37405b6b27a1990b8cb28 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Simon Pichugin <spichugi@redhat.com>
|
||||||
|
Date: Tue, 27 Feb 2024 16:30:47 -0800
|
||||||
|
Subject: [PATCH] Issue 3527 - Support HAProxy and Instance on the same machine
|
||||||
|
configuration (#6107)
|
||||||
|
|
||||||
|
Description: Improve how we handle HAProxy connections to work better when
|
||||||
|
the DS and HAProxy are on the same machine.
|
||||||
|
Ensure the client and header destination IPs are checked against the trusted IP list.
|
||||||
|
|
||||||
|
Additionally, this change will also allow configuration having
|
||||||
|
HAProxy is listening on a different subnet than the one used to forward the request.
|
||||||
|
|
||||||
|
Related: https://github.com/389ds/389-ds-base/issues/3527
|
||||||
|
|
||||||
|
Reviewed by: @progier389, @jchapma (Thanks!)
|
||||||
|
---
|
||||||
|
ldap/servers/slapd/connection.c | 35 +++++++++++++++++++++++++--------
|
||||||
|
1 file changed, 27 insertions(+), 8 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/ldap/servers/slapd/connection.c b/ldap/servers/slapd/connection.c
|
||||||
|
index d28a39bf7..10a8cc577 100644
|
||||||
|
--- a/ldap/servers/slapd/connection.c
|
||||||
|
+++ b/ldap/servers/slapd/connection.c
|
||||||
|
@@ -1187,6 +1187,8 @@ connection_read_operation(Connection *conn, Operation *op, ber_tag_t *tag, int *
|
||||||
|
char str_ip[INET6_ADDRSTRLEN + 1] = {0};
|
||||||
|
char str_haproxy_ip[INET6_ADDRSTRLEN + 1] = {0};
|
||||||
|
char str_haproxy_destip[INET6_ADDRSTRLEN + 1] = {0};
|
||||||
|
+ int trusted_matches_ip_found = 0;
|
||||||
|
+ int trusted_matches_destip_found = 0;
|
||||||
|
struct berval **bvals = NULL;
|
||||||
|
int proxy_connection = 0;
|
||||||
|
|
||||||
|
@@ -1245,21 +1247,38 @@ connection_read_operation(Connection *conn, Operation *op, ber_tag_t *tag, int *
|
||||||
|
normalize_IPv4(conn->cin_addr, buf_ip, sizeof(buf_ip), str_ip, sizeof(str_ip));
|
||||||
|
normalize_IPv4(&pr_netaddr_dest, buf_haproxy_destip, sizeof(buf_haproxy_destip),
|
||||||
|
str_haproxy_destip, sizeof(str_haproxy_destip));
|
||||||
|
+ size_t ip_len = strlen(buf_ip);
|
||||||
|
+ size_t destip_len = strlen(buf_haproxy_destip);
|
||||||
|
|
||||||
|
/* Now, reset RC and set it to 0 only if a match is found */
|
||||||
|
haproxy_rc = -1;
|
||||||
|
|
||||||
|
- /* Allow only:
|
||||||
|
- * Trusted IP == Original Client IP == HAProxy Header Destination IP */
|
||||||
|
+ /*
|
||||||
|
+ * We need to allow a configuration where DS instance and HAProxy are on the same machine.
|
||||||
|
+ * In this case, we need to check if
|
||||||
|
+ * the HAProxy client IP (which will be a loopback address) matches one of the the trusted IP addresses,
|
||||||
|
+ * while still checking that
|
||||||
|
+ * the HAProxy header destination IP address matches one of the trusted IP addresses.
|
||||||
|
+ * Additionally, this change will also allow configuration having
|
||||||
|
+ * HAProxy listening on a different subnet than one used to forward the request.
|
||||||
|
+ */
|
||||||
|
for (size_t i = 0; bvals[i] != NULL; ++i) {
|
||||||
|
- if ((strlen(bvals[i]->bv_val) == strlen(buf_ip)) &&
|
||||||
|
- (strlen(bvals[i]->bv_val) == strlen(buf_haproxy_destip)) &&
|
||||||
|
- (strncasecmp(bvals[i]->bv_val, buf_ip, strlen(buf_ip)) == 0) &&
|
||||||
|
- (strncasecmp(bvals[i]->bv_val, buf_haproxy_destip, strlen(buf_haproxy_destip)) == 0)) {
|
||||||
|
- haproxy_rc = 0;
|
||||||
|
- break;
|
||||||
|
+ size_t bval_len = strlen(bvals[i]->bv_val);
|
||||||
|
+
|
||||||
|
+ /* Check if the Client IP (HAProxy's machine IP) address matches the trusted IP address */
|
||||||
|
+ if (!trusted_matches_ip_found) {
|
||||||
|
+ trusted_matches_ip_found = (bval_len == ip_len) && (strncasecmp(bvals[i]->bv_val, buf_ip, ip_len) == 0);
|
||||||
|
+ }
|
||||||
|
+ /* Check if the HAProxy header destination IP address matches the trusted IP address */
|
||||||
|
+ if (!trusted_matches_destip_found) {
|
||||||
|
+ trusted_matches_destip_found = (bval_len == destip_len) && (strncasecmp(bvals[i]->bv_val, buf_haproxy_destip, destip_len) == 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
+
|
||||||
|
+ if (trusted_matches_ip_found && trusted_matches_destip_found) {
|
||||||
|
+ haproxy_rc = 0;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
if (haproxy_rc == -1) {
|
||||||
|
slapi_log_err(SLAPI_LOG_CONNS, "connection_read_operation", "HAProxy header received from unknown source.\n");
|
||||||
|
disconnect_server_nomutex(conn, conn->c_connid, -1, SLAPD_DISCONNECT_PROXY_UNKNOWN, EPROTO);
|
||||||
|
--
|
||||||
|
2.43.0
|
||||||
|
|
@ -1,84 +0,0 @@
|
|||||||
From c98a1f0d4ad35778204440a8a5fc497d476621a9 Mon Sep 17 00:00:00 2001
|
|
||||||
From: tbordaz <tbordaz@redhat.com>
|
|
||||||
Date: Wed, 2 Aug 2023 14:44:42 +0200
|
|
||||||
Subject: [PATCH] Issue 5870 - ns-slapd crashes at startup if a backend has no
|
|
||||||
suffix (#5871)
|
|
||||||
|
|
||||||
Bug description:
|
|
||||||
With $5598, the server checks at startup if it exists
|
|
||||||
some referrals entries in the various backends/suffixes.
|
|
||||||
If a backend has no defined suffix (not clear how it
|
|
||||||
occurs except crafting dse.ldif) the checking
|
|
||||||
triggers a sigsev
|
|
||||||
|
|
||||||
Fix description:
|
|
||||||
Check it exists a suffix before using it
|
|
||||||
---
|
|
||||||
.../tests/suites/ds_logs/ds_logs_test.py | 29 +++++++++++++++++++
|
|
||||||
ldap/servers/slapd/backend.c | 3 +-
|
|
||||||
2 files changed, 31 insertions(+), 1 deletion(-)
|
|
||||||
|
|
||||||
diff --git a/dirsrvtests/tests/suites/ds_logs/ds_logs_test.py b/dirsrvtests/tests/suites/ds_logs/ds_logs_test.py
|
|
||||||
index 1a51ca548..812936c62 100644
|
|
||||||
--- a/dirsrvtests/tests/suites/ds_logs/ds_logs_test.py
|
|
||||||
+++ b/dirsrvtests/tests/suites/ds_logs/ds_logs_test.py
|
|
||||||
@@ -11,6 +11,8 @@ import os
|
|
||||||
import time
|
|
||||||
import logging
|
|
||||||
import pytest
|
|
||||||
+import shutil
|
|
||||||
+from lib389.rootdse import RootDSE
|
|
||||||
import subprocess
|
|
||||||
from lib389.referral import Referrals, Referral
|
|
||||||
from lib389.backend import Backend
|
|
||||||
@@ -1398,6 +1400,33 @@ def test_referral_subsuffix(topology_st, request):
|
|
||||||
|
|
||||||
request.addfinalizer(fin)
|
|
||||||
|
|
||||||
+def test_missing_backend_suffix(topology_st, request):
|
|
||||||
+ """Test that the server does not crash if a backend has no suffix
|
|
||||||
+
|
|
||||||
+ :id: 427c9780-4875-4a94-a3e4-afa11be7d1a9
|
|
||||||
+ :setup: Standalone instance
|
|
||||||
+ :steps:
|
|
||||||
+ 1. Stop the instance
|
|
||||||
+ 2. remove 'nsslapd-suffix' from the backend (userRoot)
|
|
||||||
+ 3. start the instance
|
|
||||||
+ 4. Check it started successfully with SRCH on rootDSE
|
|
||||||
+ :expectedresults:
|
|
||||||
+ all steps succeeds
|
|
||||||
+ """
|
|
||||||
+ topology_st.standalone.stop()
|
|
||||||
+ dse_ldif = topology_st.standalone.confdir + '/dse.ldif'
|
|
||||||
+ shutil.copy(dse_ldif, dse_ldif + '.correct')
|
|
||||||
+ os.system('sed -e "/nsslapd-suffix/d" %s > %s' % (dse_ldif + '.correct', dse_ldif))
|
|
||||||
+ topology_st.standalone.start()
|
|
||||||
+ rdse = RootDSE(topology_st.standalone)
|
|
||||||
+
|
|
||||||
+ def fin():
|
|
||||||
+ log.info('Restore dse.ldif')
|
|
||||||
+ topology_st.standalone.stop()
|
|
||||||
+ shutil.copy(dse_ldif + '.correct', dse_ldif)
|
|
||||||
+
|
|
||||||
+ request.addfinalizer(fin)
|
|
||||||
+
|
|
||||||
if __name__ == '__main__':
|
|
||||||
# Run isolated
|
|
||||||
# -s for DEBUG mode
|
|
||||||
diff --git a/ldap/servers/slapd/backend.c b/ldap/servers/slapd/backend.c
|
|
||||||
index ea8ccb88d..498f683b1 100644
|
|
||||||
--- a/ldap/servers/slapd/backend.c
|
|
||||||
+++ b/ldap/servers/slapd/backend.c
|
|
||||||
@@ -222,7 +222,8 @@ slapi_exist_referral(Slapi_Backend *be)
|
|
||||||
suffix = slapi_sdn_get_dn(slapi_be_getsuffix(be, 0));
|
|
||||||
|
|
||||||
/* ignore special backends */
|
|
||||||
- if ((strcmp(suffix, "cn=schema") == 0) ||
|
|
||||||
+ if ((suffix == NULL) ||
|
|
||||||
+ (strcmp(suffix, "cn=schema") == 0) ||
|
|
||||||
(strcmp(suffix, "cn=config") == 0)) {
|
|
||||||
return 0; /* it does not mean anything having a referral in those backends */
|
|
||||||
}
|
|
||||||
--
|
|
||||||
2.41.0
|
|
||||||
|
|
@ -1,389 +0,0 @@
|
|||||||
From 1fefae50842b6bcdd631b59d91688388e90e0a5c Mon Sep 17 00:00:00 2001
|
|
||||||
From: progier389 <progier@redhat.com>
|
|
||||||
Date: Fri, 17 Nov 2023 14:41:51 +0100
|
|
||||||
Subject: [PATCH 1/2] Issue 5984 - Crash when paged result search are abandoned
|
|
||||||
(#5985)
|
|
||||||
|
|
||||||
* Issue 5984 - Crash when paged result search are abandoned
|
|
||||||
|
|
||||||
Problem:
|
|
||||||
Fix #4551 has changed the lock that protects the paged result data
|
|
||||||
within a connection. But the abandon operation attempts to free
|
|
||||||
the paged search result with the connection lock.
|
|
||||||
This leads to race condition and double free causing an heap
|
|
||||||
corruption and a SIGSEGV.
|
|
||||||
|
|
||||||
Solution:
|
|
||||||
- Get a copy of the operation data that needs to be logged.
|
|
||||||
- Unlock the connection mutex (to avoid deadlock risk)
|
|
||||||
- Free the paged result while holding the paged result lock.
|
|
||||||
|
|
||||||
Issue: 5984
|
|
||||||
|
|
||||||
Reviewed by: @tbordaz (Thanks!)
|
|
||||||
|
|
||||||
(cherry picked from commit 06bd0862956672eb76276cab5c1dd906fe5a7eec)
|
|
||||||
---
|
|
||||||
.../paged_results/paged_results_test.py | 110 ++++++++++++++++--
|
|
||||||
ldap/servers/slapd/abandon.c | 23 ++--
|
|
||||||
ldap/servers/slapd/opshared.c | 4 +-
|
|
||||||
ldap/servers/slapd/pagedresults.c | 8 +-
|
|
||||||
ldap/servers/slapd/proto-slap.h | 2 +-
|
|
||||||
src/lib389/lib389/__init__.py | 27 ++++-
|
|
||||||
6 files changed, 152 insertions(+), 22 deletions(-)
|
|
||||||
|
|
||||||
diff --git a/dirsrvtests/tests/suites/paged_results/paged_results_test.py b/dirsrvtests/tests/suites/paged_results/paged_results_test.py
|
|
||||||
index 893d28a07..2fc418d3f 100644
|
|
||||||
--- a/dirsrvtests/tests/suites/paged_results/paged_results_test.py
|
|
||||||
+++ b/dirsrvtests/tests/suites/paged_results/paged_results_test.py
|
|
||||||
@@ -7,7 +7,7 @@
|
|
||||||
# --- END COPYRIGHT BLOCK ---
|
|
||||||
#
|
|
||||||
import socket
|
|
||||||
-from random import sample
|
|
||||||
+from random import sample, randrange
|
|
||||||
|
|
||||||
import pytest
|
|
||||||
from ldap.controls import SimplePagedResultsControl, GetEffectiveRightsControl
|
|
||||||
@@ -17,8 +21,10 @@ from lib389.topologies import topology_st
|
|
||||||
from lib389._constants import DN_LDBM, DN_DM, DEFAULT_SUFFIX, BACKEND_NAME, PASSWORD
|
|
||||||
|
|
||||||
from lib389._controls import SSSRequestControl
|
|
||||||
-
|
|
||||||
-from lib389.idm.user import UserAccounts
|
|
||||||
+from lib389.idm.user import UserAccount, UserAccounts
|
|
||||||
+from lib389.cli_base import FakeArgs
|
|
||||||
+from lib389.config import LDBMConfig
|
|
||||||
+from lib389.dbgen import dbgen_users
|
|
||||||
from lib389.idm.organization import Organization
|
|
||||||
from lib389.idm.organizationalunit import OrganizationalUnit
|
|
||||||
from lib389.backend import Backends
|
|
||||||
@@ -46,11 +52,56 @@ NEW_BACKEND_1 = 'parent_base'
|
|
||||||
NEW_BACKEND_2 = 'child_base'
|
|
||||||
|
|
||||||
OLD_HOSTNAME = socket.gethostname()
|
|
||||||
-socket.sethostname('localhost')
|
|
||||||
+if os.getuid() == 0:
|
|
||||||
+ socket.sethostname('localhost')
|
|
||||||
HOSTNAME = socket.gethostname()
|
|
||||||
IP_ADDRESS = socket.gethostbyname(HOSTNAME)
|
|
||||||
OLD_IP_ADDRESS = socket.gethostbyname(OLD_HOSTNAME)
|
|
||||||
|
|
||||||
+
|
|
||||||
+@pytest.fixture(scope="module")
|
|
||||||
+def create_40k_users(topology_st, request):
|
|
||||||
+ inst = topology_st.standalone
|
|
||||||
+
|
|
||||||
+ # Prepare return value
|
|
||||||
+ retval = FakeArgs()
|
|
||||||
+ retval.inst = inst
|
|
||||||
+ retval.bename = '40k'
|
|
||||||
+ retval.suffix = f'o={retval.bename}'
|
|
||||||
+ retval.ldif_file = f'{inst.get_ldif_dir()}/{retval.bename}.ldif'
|
|
||||||
+
|
|
||||||
+ # Create new backend
|
|
||||||
+ bes = Backends(inst)
|
|
||||||
+ be_1 = bes.create(properties={
|
|
||||||
+ 'cn': retval.bename,
|
|
||||||
+ 'nsslapd-suffix': retval.suffix,
|
|
||||||
+ })
|
|
||||||
+
|
|
||||||
+ # Set paged search lookthrough limit
|
|
||||||
+ ldbmconfig = LDBMConfig(inst)
|
|
||||||
+ ldbmconfig.replace('nsslapd-pagedlookthroughlimit', b'100000')
|
|
||||||
+
|
|
||||||
+ # Create ldif and import it.
|
|
||||||
+ dbgen_users(inst, 40000, retval.ldif_file, retval.suffix)
|
|
||||||
+ # tasks = Tasks(inst)
|
|
||||||
+ # args = {TASK_WAIT: True}
|
|
||||||
+ # tasks.importLDIF(retval.suffix, None, retval.ldif_file, args)
|
|
||||||
+ inst.stop()
|
|
||||||
+ assert inst.ldif2db(retval.bename, None, None, None, retval.ldif_file, None)
|
|
||||||
+ inst.start()
|
|
||||||
+
|
|
||||||
+ # And set an aci allowing anonymous read
|
|
||||||
+ log.info('Adding ACI to allow our test user to search')
|
|
||||||
+ ACI_TARGET = '(targetattr != "userPassword || aci")'
|
|
||||||
+ ACI_ALLOW = '(version 3.0; acl "Enable anonymous access";allow (read, search, compare)'
|
|
||||||
+ ACI_SUBJECT = '(userdn = "ldap:///anyone");)'
|
|
||||||
+ ACI_BODY = ACI_TARGET + ACI_ALLOW + ACI_SUBJECT
|
|
||||||
+ o_1 = Organization(inst, retval.suffix)
|
|
||||||
+ o_1.set('aci', ACI_BODY)
|
|
||||||
+
|
|
||||||
+ return retval
|
|
||||||
+
|
|
||||||
+
|
|
||||||
@pytest.fixture(scope="module")
|
|
||||||
def create_user(topology_st, request):
|
|
||||||
"""User for binding operation"""
|
|
||||||
@@ -75,8 +126,10 @@ def create_user(topology_st, request):
|
|
||||||
|
|
||||||
def fin():
|
|
||||||
log.info('Deleting user simplepaged_test')
|
|
||||||
- user.delete()
|
|
||||||
- socket.sethostname(OLD_HOSTNAME)
|
|
||||||
+ if not DEBUGGING:
|
|
||||||
+ user.delete()
|
|
||||||
+ if os.getuid() == 0:
|
|
||||||
+ socket.sethostname(OLD_HOSTNAME)
|
|
||||||
|
|
||||||
request.addfinalizer(fin)
|
|
||||||
|
|
||||||
@@ -179,7 +232,7 @@ def change_conf_attr(topology_st, suffix, attr_name, attr_value):
|
|
||||||
return attr_value_bck
|
|
||||||
|
|
||||||
|
|
||||||
-def paged_search(conn, suffix, controls, search_flt, searchreq_attrlist):
|
|
||||||
+def paged_search(conn, suffix, controls, search_flt, searchreq_attrlist, abandon_rate=0):
|
|
||||||
"""Search at the DEFAULT_SUFFIX with ldap.SCOPE_SUBTREE
|
|
||||||
using Simple Paged Control(should the first item in the
|
|
||||||
list controls.
|
|
||||||
@@ -199,9 +252,16 @@ def paged_search(conn, suffix, controls, search_flt, searchreq_attrlist):
|
|
||||||
req_pr_ctrl.size,
|
|
||||||
str(controls)))
|
|
||||||
msgid = conn.search_ext(suffix, ldap.SCOPE_SUBTREE, search_flt, searchreq_attrlist, serverctrls=controls)
|
|
||||||
+ log.info('Getting page %d' % (pages,))
|
|
||||||
while True:
|
|
||||||
- log.info('Getting page %d' % (pages,))
|
|
||||||
- rtype, rdata, rmsgid, rctrls = conn.result3(msgid)
|
|
||||||
+ try:
|
|
||||||
+ rtype, rdata, rmsgid, rctrls = conn.result3(msgid, timeout=0.001)
|
|
||||||
+ except ldap.TIMEOUT:
|
|
||||||
+ if pages > 0 and abandon_rate>0 and randrange(100)<abandon_rate:
|
|
||||||
+ conn.abandon(msgid)
|
|
||||||
+ log.info('Paged result search is abandonned.')
|
|
||||||
+ return all_results
|
|
||||||
+ continue
|
|
||||||
log.debug('Data: {}'.format(rdata))
|
|
||||||
all_results.extend(rdata)
|
|
||||||
pages += 1
|
|
||||||
@@ -221,6 +281,7 @@ def paged_search(conn, suffix, controls, search_flt, searchreq_attrlist):
|
|
||||||
break # No more pages available
|
|
||||||
else:
|
|
||||||
break
|
|
||||||
+ log.info('Getting page %d' % (pages,))
|
|
||||||
|
|
||||||
assert not pctrls[0].cookie
|
|
||||||
return all_results
|
|
||||||
@@ -1179,6 +1240,39 @@ def test_maxsimplepaged_per_conn_failure(topology_st, create_user, conf_attr_val
|
|
||||||
del_users(users_list)
|
|
||||||
change_conf_attr(topology_st, DN_CONFIG, 'nsslapd-maxsimplepaged-per-conn', max_per_con_bck)
|
|
||||||
|
|
||||||
+
|
|
||||||
+def test_search_stress_abandon(create_40k_users, create_user):
|
|
||||||
+ """Verify that search with a simple paged results control
|
|
||||||
+ returns all entries it should without errors.
|
|
||||||
+
|
|
||||||
+ :id: e154b24a-83d6-11ee-90d1-482ae39447e5
|
|
||||||
+ :customerscenario: True
|
|
||||||
+ :feature: Simple paged results
|
|
||||||
+ :setup: Standalone instance, test user for binding,
|
|
||||||
+ 40K users in a second backend
|
|
||||||
+ :steps:
|
|
||||||
+ 1. Bind as test user
|
|
||||||
+ 2. Loops a number of times doing:
|
|
||||||
+ - search through added users with a simple paged control
|
|
||||||
+ - randomly abandoning the search after a few ms.
|
|
||||||
+ :expectedresults:
|
|
||||||
+ 1. Bind should be successful
|
|
||||||
+ 2. The loop should complete successfully.
|
|
||||||
+ """
|
|
||||||
+
|
|
||||||
+ abandon_rate = 10
|
|
||||||
+ page_size = 500
|
|
||||||
+ nbloops = 1000
|
|
||||||
+ search_flt = r'(uid=*)'
|
|
||||||
+ searchreq_attrlist = ['dn', 'sn']
|
|
||||||
+ log.info('Set user bind %s ' % create_user)
|
|
||||||
+ conn = create_user.bind(TEST_USER_PWD)
|
|
||||||
+ for idx in range(nbloops):
|
|
||||||
+ req_ctrl = SimplePagedResultsControl(True, size=page_size, cookie='')
|
|
||||||
+ # If the issue #5984 is not fixed the server crashs and the paged search fails with ldap.SERVER_DOWN exception
|
|
||||||
+ paged_search(conn, create_40k_users.suffix, [req_ctrl], search_flt, searchreq_attrlist, abandon_rate=abandon_rate)
|
|
||||||
+
|
|
||||||
+
|
|
||||||
if __name__ == '__main__':
|
|
||||||
# Run isolated
|
|
||||||
# -s for DEBUG mode
|
|
||||||
diff --git a/ldap/servers/slapd/abandon.c b/ldap/servers/slapd/abandon.c
|
|
||||||
index 26a2e7bf8..964d28836 100644
|
|
||||||
--- a/ldap/servers/slapd/abandon.c
|
|
||||||
+++ b/ldap/servers/slapd/abandon.c
|
|
||||||
@@ -38,6 +38,12 @@ do_abandon(Slapi_PBlock *pb)
|
|
||||||
Connection *pb_conn = NULL;
|
|
||||||
Operation *pb_op = NULL;
|
|
||||||
Operation *o;
|
|
||||||
+ /* Keep a copy of some data because o may vanish once conn is unlocked */
|
|
||||||
+ struct {
|
|
||||||
+ struct timespec hr_time_end;
|
|
||||||
+ int nentries;
|
|
||||||
+ int opid;
|
|
||||||
+ } o_copy;
|
|
||||||
|
|
||||||
slapi_pblock_get(pb, SLAPI_OPERATION, &pb_op);
|
|
||||||
slapi_pblock_get(pb, SLAPI_CONNECTION, &pb_conn);
|
|
||||||
@@ -90,8 +96,12 @@ do_abandon(Slapi_PBlock *pb)
|
|
||||||
|
|
||||||
pthread_mutex_lock(&(pb_conn->c_mutex));
|
|
||||||
for (o = pb_conn->c_ops; o != NULL; o = o->o_next) {
|
|
||||||
- if (o->o_msgid == id && o != pb_op)
|
|
||||||
+ if (o->o_msgid == id && o != pb_op) {
|
|
||||||
+ slapi_operation_time_elapsed(o, &o_copy.hr_time_end);
|
|
||||||
+ o_copy.nentries = o->o_results.r.r_search.nentries;
|
|
||||||
+ o_copy.opid = o->o_opid;
|
|
||||||
break;
|
|
||||||
+ }
|
|
||||||
}
|
|
||||||
|
|
||||||
if (o != NULL) {
|
|
||||||
@@ -130,7 +140,8 @@ do_abandon(Slapi_PBlock *pb)
|
|
||||||
slapi_log_err(SLAPI_LOG_TRACE, "do_abandon", "op not found\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
- if (0 == pagedresults_free_one_msgid_nolock(pb_conn, id)) {
|
|
||||||
+ pthread_mutex_unlock(&(pb_conn->c_mutex));
|
|
||||||
+ if (0 == pagedresults_free_one_msgid(pb_conn, id, pageresult_lock_get_addr(pb_conn))) {
|
|
||||||
slapi_log_access(LDAP_DEBUG_STATS, "conn=%" PRIu64
|
|
||||||
" op=%d ABANDON targetop=Simple Paged Results msgid=%d\n",
|
|
||||||
pb_conn->c_connid, pb_op->o_opid, id);
|
|
||||||
@@ -143,15 +154,11 @@ do_abandon(Slapi_PBlock *pb)
|
|
||||||
" targetop=SUPPRESSED-BY-PLUGIN msgid=%d\n",
|
|
||||||
pb_conn->c_connid, pb_op->o_opid, id);
|
|
||||||
} else {
|
|
||||||
- struct timespec o_hr_time_end;
|
|
||||||
- slapi_operation_time_elapsed(o, &o_hr_time_end);
|
|
||||||
slapi_log_access(LDAP_DEBUG_STATS, "conn=%" PRIu64 " op=%d ABANDON"
|
|
||||||
" targetop=%d msgid=%d nentries=%d etime=%" PRId64 ".%010" PRId64 "\n",
|
|
||||||
- pb_conn->c_connid, pb_op->o_opid, o->o_opid, id,
|
|
||||||
- o->o_results.r.r_search.nentries, (int64_t)o_hr_time_end.tv_sec, (int64_t)o_hr_time_end.tv_nsec);
|
|
||||||
+ pb_conn->c_connid, pb_op->o_opid, o_copy.opid, id,
|
|
||||||
+ o_copy.nentries, (int64_t)o_copy.hr_time_end.tv_sec, (int64_t)o_copy.hr_time_end.tv_nsec);
|
|
||||||
}
|
|
||||||
-
|
|
||||||
- pthread_mutex_unlock(&(pb_conn->c_mutex));
|
|
||||||
/*
|
|
||||||
* Wake up the persistent searches, so they
|
|
||||||
* can notice if they've been abandoned.
|
|
||||||
diff --git a/ldap/servers/slapd/opshared.c b/ldap/servers/slapd/opshared.c
|
|
||||||
index 897b9566e..7ab4117cd 100644
|
|
||||||
--- a/ldap/servers/slapd/opshared.c
|
|
||||||
+++ b/ldap/servers/slapd/opshared.c
|
|
||||||
@@ -922,9 +922,7 @@ op_shared_search(Slapi_PBlock *pb, int send_result)
|
|
||||||
next_be = NULL; /* to break the loop */
|
|
||||||
if (operation->o_status & SLAPI_OP_STATUS_ABANDONED) {
|
|
||||||
/* It turned out this search was abandoned. */
|
|
||||||
- pthread_mutex_lock(pagedresults_mutex);
|
|
||||||
- pagedresults_free_one_msgid_nolock(pb_conn, operation->o_msgid);
|
|
||||||
- pthread_mutex_unlock(pagedresults_mutex);
|
|
||||||
+ pagedresults_free_one_msgid(pb_conn, operation->o_msgid, pagedresults_mutex);
|
|
||||||
/* paged-results-request was abandoned; making an empty cookie. */
|
|
||||||
pagedresults_set_response_control(pb, 0, estimate, -1, pr_idx);
|
|
||||||
send_ldap_result(pb, 0, NULL, "Simple Paged Results Search abandoned", 0, NULL);
|
|
||||||
diff --git a/ldap/servers/slapd/pagedresults.c b/ldap/servers/slapd/pagedresults.c
|
|
||||||
index 01fe3370f..db87e486e 100644
|
|
||||||
--- a/ldap/servers/slapd/pagedresults.c
|
|
||||||
+++ b/ldap/servers/slapd/pagedresults.c
|
|
||||||
@@ -34,6 +34,10 @@ pageresult_lock_cleanup()
|
|
||||||
slapi_ch_free((void**)&lock_hash);
|
|
||||||
}
|
|
||||||
|
|
||||||
+/* Beware to the lock order with c_mutex:
|
|
||||||
+ * c_mutex is sometime locked while holding pageresult_lock
|
|
||||||
+ * ==> Do not lock pageresult_lock when holing c_mutex
|
|
||||||
+ */
|
|
||||||
pthread_mutex_t *
|
|
||||||
pageresult_lock_get_addr(Connection *conn)
|
|
||||||
{
|
|
||||||
@@ -350,7 +354,7 @@ pagedresults_free_one(Connection *conn, Operation *op, int index)
|
|
||||||
* Used for abandoning - pageresult_lock_get_addr(conn) is already locked in do_abandone.
|
|
||||||
*/
|
|
||||||
int
|
|
||||||
-pagedresults_free_one_msgid_nolock(Connection *conn, ber_int_t msgid)
|
|
||||||
+pagedresults_free_one_msgid(Connection *conn, ber_int_t msgid, pthread_mutex_t *mutex)
|
|
||||||
{
|
|
||||||
int rc = -1;
|
|
||||||
int i;
|
|
||||||
@@ -361,6 +365,7 @@ pagedresults_free_one_msgid_nolock(Connection *conn, ber_int_t msgid)
|
|
||||||
} else {
|
|
||||||
slapi_log_err(SLAPI_LOG_TRACE,
|
|
||||||
"pagedresults_free_one_msgid_nolock", "=> msgid=%d\n", msgid);
|
|
||||||
+ pthread_mutex_lock(mutex);
|
|
||||||
for (i = 0; i < conn->c_pagedresults.prl_maxlen; i++) {
|
|
||||||
if (conn->c_pagedresults.prl_list[i].pr_msgid == msgid) {
|
|
||||||
PagedResults *prp = conn->c_pagedresults.prl_list + i;
|
|
||||||
@@ -375,6 +380,7 @@ pagedresults_free_one_msgid_nolock(Connection *conn, ber_int_t msgid)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+ pthread_mutex_unlock(mutex);
|
|
||||||
slapi_log_err(SLAPI_LOG_TRACE,
|
|
||||||
"pagedresults_free_one_msgid_nolock", "<= %d\n", rc);
|
|
||||||
}
|
|
||||||
diff --git a/ldap/servers/slapd/proto-slap.h b/ldap/servers/slapd/proto-slap.h
|
|
||||||
index bb1f8aea2..76b2e3af2 100644
|
|
||||||
--- a/ldap/servers/slapd/proto-slap.h
|
|
||||||
+++ b/ldap/servers/slapd/proto-slap.h
|
|
||||||
@@ -1587,7 +1587,7 @@ int pagedresults_is_timedout_nolock(Connection *conn);
|
|
||||||
int pagedresults_reset_timedout_nolock(Connection *conn);
|
|
||||||
int pagedresults_in_use_nolock(Connection *conn);
|
|
||||||
int pagedresults_free_one(Connection *conn, Operation *op, int index);
|
|
||||||
-int pagedresults_free_one_msgid_nolock(Connection *conn, ber_int_t msgid);
|
|
||||||
+int pagedresults_free_one_msgid(Connection *conn, ber_int_t msgid, pthread_mutex_t *mutex);
|
|
||||||
int op_is_pagedresults(Operation *op);
|
|
||||||
int pagedresults_cleanup_all(Connection *conn, int needlock);
|
|
||||||
void op_set_pagedresults(Operation *op);
|
|
||||||
diff --git a/src/lib389/lib389/__init__.py b/src/lib389/lib389/__init__.py
|
|
||||||
index b8ca7685f..81e41c58b 100644
|
|
||||||
--- a/src/lib389/lib389/__init__.py
|
|
||||||
+++ b/src/lib389/lib389/__init__.py
|
|
||||||
@@ -1040,6 +1040,24 @@ class DirSrv(SimpleLDAPObject, object):
|
|
||||||
|
|
||||||
self.state = DIRSRV_STATE_OFFLINE
|
|
||||||
|
|
||||||
+ def dump_errorlog(self):
|
|
||||||
+ '''
|
|
||||||
+ Its logs all errors messages within the error log that occured
|
|
||||||
+ after the last startup.
|
|
||||||
+ '''
|
|
||||||
+ if os.path.isfile(self.errlog):
|
|
||||||
+ lines = []
|
|
||||||
+ with open(self.errlog, 'r') as file:
|
|
||||||
+ for line in file:
|
|
||||||
+ if "starting up" in line:
|
|
||||||
+ lines = []
|
|
||||||
+ for key in ( 'DEBUG', 'INFO', 'NOTICE', 'WARN' ):
|
|
||||||
+ if key in line:
|
|
||||||
+ lines.append(line)
|
|
||||||
+ break
|
|
||||||
+ for line in lines:
|
|
||||||
+ self.log.error(line)
|
|
||||||
+
|
|
||||||
def start(self, timeout=120, post_open=True):
|
|
||||||
'''
|
|
||||||
It starts an instance and rebind it. Its final state after rebind
|
|
||||||
@@ -1063,7 +1081,13 @@ class DirSrv(SimpleLDAPObject, object):
|
|
||||||
if self.with_systemd():
|
|
||||||
self.log.debug("systemd status -> True")
|
|
||||||
# Do systemd things here ...
|
|
||||||
- subprocess.check_output(["systemctl", "start", "dirsrv@%s" % self.serverid], stderr=subprocess.STDOUT)
|
|
||||||
+ try:
|
|
||||||
+ subprocess.check_output(["systemctl", "start", "dirsrv@%s" % self.serverid], stderr=subprocess.STDOUT)
|
|
||||||
+ except subprocess.CalledProcessError as e:
|
|
||||||
+ self.dump_errorlog()
|
|
||||||
+ self.log.error('Failed to start dirsrv@%s: "%s"' % (self.serverid, e.output.decode()))
|
|
||||||
+ self.log.error(e)
|
|
||||||
+ raise ValueError('Failed to start DS')
|
|
||||||
else:
|
|
||||||
self.log.debug("systemd status -> False")
|
|
||||||
# Start the process.
|
|
||||||
@@ -1087,6 +1111,7 @@ class DirSrv(SimpleLDAPObject, object):
|
|
||||||
self.log.debug("DEBUG: starting with %s" % cmd)
|
|
||||||
output = subprocess.check_output(*cmd, env=env, stderr=subprocess.STDOUT)
|
|
||||||
except subprocess.CalledProcessError as e:
|
|
||||||
+ self.dump_errorlog()
|
|
||||||
self.log.error('Failed to start ns-slapd: "%s"' % e.output.decode())
|
|
||||||
self.log.error(e)
|
|
||||||
raise ValueError('Failed to start DS')
|
|
||||||
--
|
|
||||||
2.41.0
|
|
||||||
|
|
@ -0,0 +1,119 @@
|
|||||||
|
From dddb14210b402f317e566b6387c76a8e659bf7fa Mon Sep 17 00:00:00 2001
|
||||||
|
From: progier389 <progier@redhat.com>
|
||||||
|
Date: Tue, 14 Feb 2023 13:34:10 +0100
|
||||||
|
Subject: [PATCH 1/2] issue 5647 - covscan: memory leak in audit log when
|
||||||
|
adding entries (#5650)
|
||||||
|
|
||||||
|
covscan reported an issue about "vals" variable in auditlog.c:231 and indeed a charray_free is missing.
|
||||||
|
Issue: 5647
|
||||||
|
Reviewed by: @mreynolds389, @droideck
|
||||||
|
---
|
||||||
|
ldap/servers/slapd/auditlog.c | 71 +++++++++++++++++++----------------
|
||||||
|
1 file changed, 38 insertions(+), 33 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/ldap/servers/slapd/auditlog.c b/ldap/servers/slapd/auditlog.c
|
||||||
|
index 68cbc674d..3128e0497 100644
|
||||||
|
--- a/ldap/servers/slapd/auditlog.c
|
||||||
|
+++ b/ldap/servers/slapd/auditlog.c
|
||||||
|
@@ -177,6 +177,40 @@ write_auditfail_log_entry(Slapi_PBlock *pb)
|
||||||
|
slapi_ch_free_string(&audit_config);
|
||||||
|
}
|
||||||
|
|
||||||
|
+/*
|
||||||
|
+ * Write the attribute values to the audit log as "comments"
|
||||||
|
+ *
|
||||||
|
+ * Slapi_Attr *entry - the attribute begin logged.
|
||||||
|
+ * char *attrname - the attribute name.
|
||||||
|
+ * lenstr *l - the audit log buffer
|
||||||
|
+ *
|
||||||
|
+ * Resulting output in the log:
|
||||||
|
+ *
|
||||||
|
+ * #ATTR: VALUE
|
||||||
|
+ * #ATTR: VALUE
|
||||||
|
+ */
|
||||||
|
+static void
|
||||||
|
+log_entry_attr(Slapi_Attr *entry_attr, char *attrname, lenstr *l)
|
||||||
|
+{
|
||||||
|
+ Slapi_Value **vals = attr_get_present_values(entry_attr);
|
||||||
|
+ for(size_t i = 0; vals && vals[i]; i++) {
|
||||||
|
+ char log_val[256] = "";
|
||||||
|
+ const struct berval *bv = slapi_value_get_berval(vals[i]);
|
||||||
|
+ if (bv->bv_len >= 256) {
|
||||||
|
+ strncpy(log_val, bv->bv_val, 252);
|
||||||
|
+ strcpy(log_val+252, "...");
|
||||||
|
+ } else {
|
||||||
|
+ strncpy(log_val, bv->bv_val, bv->bv_len);
|
||||||
|
+ log_val[bv->bv_len] = 0;
|
||||||
|
+ }
|
||||||
|
+ addlenstr(l, "#");
|
||||||
|
+ addlenstr(l, attrname);
|
||||||
|
+ addlenstr(l, ": ");
|
||||||
|
+ addlenstr(l, log_val);
|
||||||
|
+ addlenstr(l, "\n");
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
/*
|
||||||
|
* Write "requested" attributes from the entry to the audit log as "comments"
|
||||||
|
*
|
||||||
|
@@ -212,21 +246,9 @@ add_entry_attrs(Slapi_Entry *entry, lenstr *l)
|
||||||
|
for (req_attr = ldap_utf8strtok_r(display_attrs, ", ", &last); req_attr;
|
||||||
|
req_attr = ldap_utf8strtok_r(NULL, ", ", &last))
|
||||||
|
{
|
||||||
|
- char **vals = slapi_entry_attr_get_charray(entry, req_attr);
|
||||||
|
- for(size_t i = 0; vals && vals[i]; i++) {
|
||||||
|
- char log_val[256] = {0};
|
||||||
|
-
|
||||||
|
- if (strlen(vals[i]) > 256) {
|
||||||
|
- strncpy(log_val, vals[i], 252);
|
||||||
|
- strcat(log_val, "...");
|
||||||
|
- } else {
|
||||||
|
- strcpy(log_val, vals[i]);
|
||||||
|
- }
|
||||||
|
- addlenstr(l, "#");
|
||||||
|
- addlenstr(l, req_attr);
|
||||||
|
- addlenstr(l, ": ");
|
||||||
|
- addlenstr(l, log_val);
|
||||||
|
- addlenstr(l, "\n");
|
||||||
|
+ slapi_entry_attr_find(entry, req_attr, &entry_attr);
|
||||||
|
+ if (entry_attr) {
|
||||||
|
+ log_entry_attr(entry_attr, req_attr, l);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
@@ -234,7 +256,6 @@ add_entry_attrs(Slapi_Entry *entry, lenstr *l)
|
||||||
|
for (; entry_attr; entry_attr = entry_attr->a_next) {
|
||||||
|
Slapi_Value **vals = attr_get_present_values(entry_attr);
|
||||||
|
char *attr = NULL;
|
||||||
|
- const char *val = NULL;
|
||||||
|
|
||||||
|
slapi_attr_get_type(entry_attr, &attr);
|
||||||
|
if (strcmp(attr, PSEUDO_ATTR_UNHASHEDUSERPASSWORD) == 0) {
|
||||||
|
@@ -251,23 +272,7 @@ add_entry_attrs(Slapi_Entry *entry, lenstr *l)
|
||||||
|
addlenstr(l, ": ****************************\n");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
-
|
||||||
|
- for(size_t i = 0; vals && vals[i]; i++) {
|
||||||
|
- char log_val[256] = {0};
|
||||||
|
-
|
||||||
|
- val = slapi_value_get_string(vals[i]);
|
||||||
|
- if (strlen(val) > 256) {
|
||||||
|
- strncpy(log_val, val, 252);
|
||||||
|
- strcat(log_val, "...");
|
||||||
|
- } else {
|
||||||
|
- strcpy(log_val, val);
|
||||||
|
- }
|
||||||
|
- addlenstr(l, "#");
|
||||||
|
- addlenstr(l, attr);
|
||||||
|
- addlenstr(l, ": ");
|
||||||
|
- addlenstr(l, log_val);
|
||||||
|
- addlenstr(l, "\n");
|
||||||
|
- }
|
||||||
|
+ log_entry_attr(entry_attr, attr, l);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
slapi_ch_free_string(&display_attrs);
|
||||||
|
--
|
||||||
|
2.43.0
|
||||||
|
|
@ -0,0 +1,27 @@
|
|||||||
|
From be7c2b82958e91ce08775bf6b5da3c311d3b00e5 Mon Sep 17 00:00:00 2001
|
||||||
|
From: progier389 <progier@redhat.com>
|
||||||
|
Date: Mon, 20 Feb 2023 16:14:05 +0100
|
||||||
|
Subject: [PATCH 2/2] Issue 5647 - Fix unused variable warning from previous
|
||||||
|
commit (#5670)
|
||||||
|
|
||||||
|
* issue 5647 - memory leak in audit log when adding entries
|
||||||
|
* Issue 5647 - Fix unused variable warning from previous commit
|
||||||
|
---
|
||||||
|
ldap/servers/slapd/auditlog.c | 1 -
|
||||||
|
1 file changed, 1 deletion(-)
|
||||||
|
|
||||||
|
diff --git a/ldap/servers/slapd/auditlog.c b/ldap/servers/slapd/auditlog.c
|
||||||
|
index 3128e0497..0597ecc6f 100644
|
||||||
|
--- a/ldap/servers/slapd/auditlog.c
|
||||||
|
+++ b/ldap/servers/slapd/auditlog.c
|
||||||
|
@@ -254,7 +254,6 @@ add_entry_attrs(Slapi_Entry *entry, lenstr *l)
|
||||||
|
} else {
|
||||||
|
/* Return all attributes */
|
||||||
|
for (; entry_attr; entry_attr = entry_attr->a_next) {
|
||||||
|
- Slapi_Value **vals = attr_get_present_values(entry_attr);
|
||||||
|
char *attr = NULL;
|
||||||
|
|
||||||
|
slapi_attr_get_type(entry_attr, &attr);
|
||||||
|
--
|
||||||
|
2.43.0
|
||||||
|
|
@ -1,89 +0,0 @@
|
|||||||
From df7dd8320424f7ab616c9ad8086a6874ff8bf859 Mon Sep 17 00:00:00 2001
|
|
||||||
From: progier389 <progier@redhat.com>
|
|
||||||
Date: Tue, 21 Nov 2023 11:57:44 +0100
|
|
||||||
Subject: [PATCH 2/2] Issue 5984 - Crash when paged result search are abandoned
|
|
||||||
- fix2 (#5987)
|
|
||||||
|
|
||||||
Chasing several rabbits at the same time is a bad idea !
|
|
||||||
and I mixed branches and unwillingly pushed one commit for #5980 in #5984
|
|
||||||
just before the PR #5985 merge ! -:(
|
|
||||||
Hopefully it does not break anything but just logs some useless crap if instance fails to starts.
|
|
||||||
Anyway This commit reverts the change about __init.py
|
|
||||||
and also do a minor code cleanup (removed a trailing space) in abandon.c
|
|
||||||
|
|
||||||
Issue #5984
|
|
||||||
|
|
||||||
Reviewed by: @tbordaz Thanks !
|
|
||||||
---
|
|
||||||
ldap/servers/slapd/abandon.c | 2 +-
|
|
||||||
src/lib389/lib389/__init__.py | 27 +--------------------------
|
|
||||||
2 files changed, 2 insertions(+), 27 deletions(-)
|
|
||||||
|
|
||||||
diff --git a/ldap/servers/slapd/abandon.c b/ldap/servers/slapd/abandon.c
|
|
||||||
index 964d28836..2dd1ee320 100644
|
|
||||||
--- a/ldap/servers/slapd/abandon.c
|
|
||||||
+++ b/ldap/servers/slapd/abandon.c
|
|
||||||
@@ -43,7 +43,7 @@ do_abandon(Slapi_PBlock *pb)
|
|
||||||
struct timespec hr_time_end;
|
|
||||||
int nentries;
|
|
||||||
int opid;
|
|
||||||
- } o_copy;
|
|
||||||
+ } o_copy;
|
|
||||||
|
|
||||||
slapi_pblock_get(pb, SLAPI_OPERATION, &pb_op);
|
|
||||||
slapi_pblock_get(pb, SLAPI_CONNECTION, &pb_conn);
|
|
||||||
diff --git a/src/lib389/lib389/__init__.py b/src/lib389/lib389/__init__.py
|
|
||||||
index 6a941dbe7..7590ec442 100644
|
|
||||||
--- a/src/lib389/lib389/__init__.py
|
|
||||||
+++ b/src/lib389/lib389/__init__.py
|
|
||||||
@@ -1048,24 +1048,6 @@ class DirSrv(SimpleLDAPObject, object):
|
|
||||||
|
|
||||||
self.state = DIRSRV_STATE_OFFLINE
|
|
||||||
|
|
||||||
- def dump_errorlog(self):
|
|
||||||
- '''
|
|
||||||
- Its logs all errors messages within the error log that occured
|
|
||||||
- after the last startup.
|
|
||||||
- '''
|
|
||||||
- if os.path.isfile(self.errlog):
|
|
||||||
- lines = []
|
|
||||||
- with open(self.errlog, 'r') as file:
|
|
||||||
- for line in file:
|
|
||||||
- if "starting up" in line:
|
|
||||||
- lines = []
|
|
||||||
- for key in ( 'DEBUG', 'INFO', 'NOTICE', 'WARN' ):
|
|
||||||
- if key in line:
|
|
||||||
- lines.append(line)
|
|
||||||
- break
|
|
||||||
- for line in lines:
|
|
||||||
- self.log.error(line)
|
|
||||||
-
|
|
||||||
def start(self, timeout=120, post_open=True):
|
|
||||||
'''
|
|
||||||
It starts an instance and rebind it. Its final state after rebind
|
|
||||||
@@ -1089,13 +1071,7 @@ class DirSrv(SimpleLDAPObject, object):
|
|
||||||
if self.with_systemd():
|
|
||||||
self.log.debug("systemd status -> True")
|
|
||||||
# Do systemd things here ...
|
|
||||||
- try:
|
|
||||||
- subprocess.check_output(["systemctl", "start", "dirsrv@%s" % self.serverid], stderr=subprocess.STDOUT)
|
|
||||||
- except subprocess.CalledProcessError as e:
|
|
||||||
- self.dump_errorlog()
|
|
||||||
- self.log.error('Failed to start dirsrv@%s: "%s"' % (self.serverid, e.output.decode()))
|
|
||||||
- self.log.error(e)
|
|
||||||
- raise ValueError('Failed to start DS')
|
|
||||||
+ subprocess.check_output(["systemctl", "start", "dirsrv@%s" % self.serverid], stderr=subprocess.STDOUT)
|
|
||||||
else:
|
|
||||||
self.log.debug("systemd status -> False")
|
|
||||||
# Start the process.
|
|
||||||
@@ -1119,7 +1095,6 @@ class DirSrv(SimpleLDAPObject, object):
|
|
||||||
self.log.debug("DEBUG: starting with %s" % cmd)
|
|
||||||
output = subprocess.check_output(*cmd, env=env, stderr=subprocess.STDOUT)
|
|
||||||
except subprocess.CalledProcessError as e:
|
|
||||||
- self.dump_errorlog()
|
|
||||||
self.log.error('Failed to start ns-slapd: "%s"' % e.output.decode())
|
|
||||||
self.log.error(e)
|
|
||||||
raise ValueError('Failed to start DS')
|
|
||||||
--
|
|
||||||
2.41.0
|
|
||||||
|
|
@ -0,0 +1,147 @@
|
|||||||
|
From 692c4cec6cc5c0086cf58f83bcfa690c766c9887 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Thierry Bordaz <tbordaz@redhat.com>
|
||||||
|
Date: Fri, 2 Feb 2024 14:14:28 +0100
|
||||||
|
Subject: [PATCH] Issue 5407 - sync_repl crashes if enabled while dynamic
|
||||||
|
plugin is enabled (#5411)
|
||||||
|
|
||||||
|
Bug description:
|
||||||
|
When dynamic plugin is enabled, if a MOD enables sync_repl plugin
|
||||||
|
then sync_repl init function registers the postop callback
|
||||||
|
that will be called for the MOD itself while the preop
|
||||||
|
has not been called.
|
||||||
|
postop expects preop to be called and so primary operation
|
||||||
|
to be set. When it is not set it crashes
|
||||||
|
|
||||||
|
Fix description:
|
||||||
|
If the primary operation is not set, just return
|
||||||
|
|
||||||
|
relates: #5407
|
||||||
|
---
|
||||||
|
.../suites/syncrepl_plugin/basic_test.py | 68 +++++++++++++++++++
|
||||||
|
ldap/servers/plugins/sync/sync_persist.c | 23 ++++++-
|
||||||
|
2 files changed, 90 insertions(+), 1 deletion(-)
|
||||||
|
|
||||||
|
diff --git a/dirsrvtests/tests/suites/syncrepl_plugin/basic_test.py b/dirsrvtests/tests/suites/syncrepl_plugin/basic_test.py
|
||||||
|
index eb3770b78..cdf35eeaa 100644
|
||||||
|
--- a/dirsrvtests/tests/suites/syncrepl_plugin/basic_test.py
|
||||||
|
+++ b/dirsrvtests/tests/suites/syncrepl_plugin/basic_test.py
|
||||||
|
@@ -592,6 +592,74 @@ def test_sync_repl_cenotaph(topo_m2, request):
|
||||||
|
|
||||||
|
request.addfinalizer(fin)
|
||||||
|
|
||||||
|
+def test_sync_repl_dynamic_plugin(topology, request):
|
||||||
|
+ """Test sync_repl with dynamic plugin
|
||||||
|
+
|
||||||
|
+ :id: d4f84913-c18a-459f-8525-110f610ca9e6
|
||||||
|
+ :setup: install a standalone instance
|
||||||
|
+ :steps:
|
||||||
|
+ 1. reset instance to standard (no retroCL, no sync_repl, no dynamic plugin)
|
||||||
|
+ 2. Enable dynamic plugin
|
||||||
|
+ 3. Enable retroCL/content_sync
|
||||||
|
+ 4. Establish a sync_repl req
|
||||||
|
+ :expectedresults:
|
||||||
|
+ 1. Should succeeds
|
||||||
|
+ 2. Should succeeds
|
||||||
|
+ 3. Should succeeds
|
||||||
|
+ 4. Should succeeds
|
||||||
|
+ """
|
||||||
|
+
|
||||||
|
+ # Reset the instance in a default config
|
||||||
|
+ # Disable content sync plugin
|
||||||
|
+ topology.standalone.plugins.disable(name=PLUGIN_REPL_SYNC)
|
||||||
|
+
|
||||||
|
+ # Disable retro changelog
|
||||||
|
+ topology.standalone.plugins.disable(name=PLUGIN_RETRO_CHANGELOG)
|
||||||
|
+
|
||||||
|
+ # Disable dynamic plugins
|
||||||
|
+ topology.standalone.modify_s(DN_CONFIG, [(ldap.MOD_REPLACE, 'nsslapd-dynamic-plugins', b'off')])
|
||||||
|
+ topology.standalone.restart()
|
||||||
|
+
|
||||||
|
+ # Now start the test
|
||||||
|
+ # Enable dynamic plugins
|
||||||
|
+ try:
|
||||||
|
+ topology.standalone.modify_s(DN_CONFIG, [(ldap.MOD_REPLACE, 'nsslapd-dynamic-plugins', b'on')])
|
||||||
|
+ except ldap.LDAPError as e:
|
||||||
|
+ log.error('Failed to enable dynamic plugin! {}'.format(e.args[0]['desc']))
|
||||||
|
+ assert False
|
||||||
|
+
|
||||||
|
+ # Enable retro changelog
|
||||||
|
+ topology.standalone.plugins.enable(name=PLUGIN_RETRO_CHANGELOG)
|
||||||
|
+
|
||||||
|
+ # Enbale content sync plugin
|
||||||
|
+ topology.standalone.plugins.enable(name=PLUGIN_REPL_SYNC)
|
||||||
|
+
|
||||||
|
+ # create a sync repl client and wait 5 seconds to be sure it is running
|
||||||
|
+ sync_repl = Sync_persist(topology.standalone)
|
||||||
|
+ sync_repl.start()
|
||||||
|
+ time.sleep(5)
|
||||||
|
+
|
||||||
|
+ # create users
|
||||||
|
+ users = UserAccounts(topology.standalone, DEFAULT_SUFFIX)
|
||||||
|
+ users_set = []
|
||||||
|
+ for i in range(10001, 10004):
|
||||||
|
+ users_set.append(users.create_test_user(uid=i))
|
||||||
|
+
|
||||||
|
+ time.sleep(10)
|
||||||
|
+ # delete users, that automember/memberof will generate nested updates
|
||||||
|
+ for user in users_set:
|
||||||
|
+ user.delete()
|
||||||
|
+ # stop the server to get the sync_repl result set (exit from while loop).
|
||||||
|
+ # Only way I found to acheive that.
|
||||||
|
+ # and wait a bit to let sync_repl thread time to set its result before fetching it.
|
||||||
|
+ topology.standalone.stop()
|
||||||
|
+ sync_repl.get_result()
|
||||||
|
+ sync_repl.join()
|
||||||
|
+ log.info('test_sync_repl_dynamic_plugin: PASS\n')
|
||||||
|
+
|
||||||
|
+ # Success
|
||||||
|
+ log.info('Test complete')
|
||||||
|
+
|
||||||
|
def test_sync_repl_invalid_cookie(topology, request):
|
||||||
|
"""Test sync_repl with invalid cookie
|
||||||
|
|
||||||
|
diff --git a/ldap/servers/plugins/sync/sync_persist.c b/ldap/servers/plugins/sync/sync_persist.c
|
||||||
|
index d2210b64c..283607361 100644
|
||||||
|
--- a/ldap/servers/plugins/sync/sync_persist.c
|
||||||
|
+++ b/ldap/servers/plugins/sync/sync_persist.c
|
||||||
|
@@ -156,6 +156,17 @@ ignore_op_pl(Slapi_PBlock *pb)
|
||||||
|
* This is the same for ident
|
||||||
|
*/
|
||||||
|
prim_op = get_thread_primary_op();
|
||||||
|
+ if (prim_op == NULL) {
|
||||||
|
+ /* This can happen if the PRE_OP (sync_update_persist_betxn_pre_op) was not called.
|
||||||
|
+ * The only known case it happens is with dynamic plugin enabled and an
|
||||||
|
+ * update that enable the sync_repl plugin. In such case sync_repl registers
|
||||||
|
+ * the postop (sync_update_persist_op) that is called while the preop was not called
|
||||||
|
+ */
|
||||||
|
+ slapi_log_err(SLAPI_LOG_PLUGIN, SYNC_PLUGIN_SUBSYSTEM,
|
||||||
|
+ "ignore_op_pl - Operation without primary op set (0x%lx)\n",
|
||||||
|
+ (ulong) op);
|
||||||
|
+ return;
|
||||||
|
+ }
|
||||||
|
ident = sync_persist_get_operation_extension(pb);
|
||||||
|
|
||||||
|
if (ident) {
|
||||||
|
@@ -232,8 +243,18 @@ sync_update_persist_op(Slapi_PBlock *pb, Slapi_Entry *e, Slapi_Entry *eprev, ber
|
||||||
|
|
||||||
|
|
||||||
|
prim_op = get_thread_primary_op();
|
||||||
|
+ if (prim_op == NULL) {
|
||||||
|
+ /* This can happen if the PRE_OP (sync_update_persist_betxn_pre_op) was not called.
|
||||||
|
+ * The only known case it happens is with dynamic plugin enabled and an
|
||||||
|
+ * update that enable the sync_repl plugin. In such case sync_repl registers
|
||||||
|
+ * the postop (sync_update_persist_op) that is called while the preop was not called
|
||||||
|
+ */
|
||||||
|
+ slapi_log_err(SLAPI_LOG_PLUGIN, SYNC_PLUGIN_SUBSYSTEM,
|
||||||
|
+ "sync_update_persist_op - Operation without primary op set (0x%lx)\n",
|
||||||
|
+ (ulong) pb_op);
|
||||||
|
+ return;
|
||||||
|
+ }
|
||||||
|
ident = sync_persist_get_operation_extension(pb);
|
||||||
|
- PR_ASSERT(prim_op);
|
||||||
|
|
||||||
|
if ((ident == NULL) && operation_is_flag_set(pb_op, OP_FLAG_NOOP)) {
|
||||||
|
/* This happens for URP (add cenotaph, fixup rename, tombstone resurrect)
|
||||||
|
--
|
||||||
|
2.43.0
|
||||||
|
|
@ -0,0 +1,840 @@
|
|||||||
|
From 8dc61a176323f0d41df730abd715ccff3034c2be Mon Sep 17 00:00:00 2001
|
||||||
|
From: Mark Reynolds <mreynolds@redhat.com>
|
||||||
|
Date: Sun, 27 Nov 2022 09:37:19 -0500
|
||||||
|
Subject: [PATCH] Issue 5547 - automember plugin improvements
|
||||||
|
|
||||||
|
Description:
|
||||||
|
|
||||||
|
Rebuild task has the following improvements:
|
||||||
|
|
||||||
|
- Only one task allowed at a time
|
||||||
|
- Do not cleanup previous members by default. Add new CLI option to intentionally
|
||||||
|
cleanup memberships before rebuilding from scratch.
|
||||||
|
- Add better task logging to show fixup progress
|
||||||
|
|
||||||
|
To prevent automember from being called in a nested be_txn loop thread storage is
|
||||||
|
used to check and skip these loops.
|
||||||
|
|
||||||
|
relates: https://github.com/389ds/389-ds-base/issues/5547
|
||||||
|
|
||||||
|
Reviewed by: spichugi(Thanks!)
|
||||||
|
---
|
||||||
|
.../automember_plugin/automember_mod_test.py | 43 +++-
|
||||||
|
ldap/servers/plugins/automember/automember.c | 232 ++++++++++++++----
|
||||||
|
ldap/servers/slapd/back-ldbm/ldbm_add.c | 11 +-
|
||||||
|
ldap/servers/slapd/back-ldbm/ldbm_delete.c | 10 +-
|
||||||
|
ldap/servers/slapd/back-ldbm/ldbm_modify.c | 11 +-
|
||||||
|
.../lib389/cli_conf/plugins/automember.py | 10 +-
|
||||||
|
src/lib389/lib389/plugins.py | 7 +-
|
||||||
|
src/lib389/lib389/tasks.py | 9 +-
|
||||||
|
8 files changed, 250 insertions(+), 83 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/dirsrvtests/tests/suites/automember_plugin/automember_mod_test.py b/dirsrvtests/tests/suites/automember_plugin/automember_mod_test.py
|
||||||
|
index 8d25384bf..7a0ed3275 100644
|
||||||
|
--- a/dirsrvtests/tests/suites/automember_plugin/automember_mod_test.py
|
||||||
|
+++ b/dirsrvtests/tests/suites/automember_plugin/automember_mod_test.py
|
||||||
|
@@ -5,12 +5,13 @@
|
||||||
|
# License: GPL (version 3 or any later version).
|
||||||
|
# See LICENSE for details.
|
||||||
|
# --- END COPYRIGHT BLOCK ---
|
||||||
|
-#
|
||||||
|
+import ldap
|
||||||
|
import logging
|
||||||
|
import pytest
|
||||||
|
import os
|
||||||
|
+import time
|
||||||
|
from lib389.utils import ds_is_older
|
||||||
|
-from lib389._constants import *
|
||||||
|
+from lib389._constants import DEFAULT_SUFFIX
|
||||||
|
from lib389.plugins import AutoMembershipPlugin, AutoMembershipDefinitions
|
||||||
|
from lib389.idm.user import UserAccounts
|
||||||
|
from lib389.idm.group import Groups
|
||||||
|
@@ -41,6 +42,11 @@ def automember_fixture(topo, request):
|
||||||
|
user_accts = UserAccounts(topo.standalone, DEFAULT_SUFFIX)
|
||||||
|
user = user_accts.create_test_user()
|
||||||
|
|
||||||
|
+ # Create extra users
|
||||||
|
+ users = UserAccounts(topo.standalone, DEFAULT_SUFFIX)
|
||||||
|
+ for i in range(0, 100):
|
||||||
|
+ users.create_test_user(uid=i)
|
||||||
|
+
|
||||||
|
# Create automember definitions and regex rules
|
||||||
|
automember_prop = {
|
||||||
|
'cn': 'testgroup_definition',
|
||||||
|
@@ -59,7 +65,7 @@ def automember_fixture(topo, request):
|
||||||
|
automemberplugin.enable()
|
||||||
|
topo.standalone.restart()
|
||||||
|
|
||||||
|
- return (user, groups)
|
||||||
|
+ return user, groups
|
||||||
|
|
||||||
|
|
||||||
|
def test_mods(automember_fixture, topo):
|
||||||
|
@@ -72,19 +78,21 @@ def test_mods(automember_fixture, topo):
|
||||||
|
2. Update user that should add it to group[1]
|
||||||
|
3. Update user that should add it to group[2]
|
||||||
|
4. Update user that should add it to group[0]
|
||||||
|
- 5. Test rebuild task correctly moves user to group[1]
|
||||||
|
+ 5. Test rebuild task adds user to group[1]
|
||||||
|
+ 6. Test rebuild task cleanups groups and only adds it to group[1]
|
||||||
|
:expectedresults:
|
||||||
|
1. Success
|
||||||
|
2. Success
|
||||||
|
3. Success
|
||||||
|
4. Success
|
||||||
|
5. Success
|
||||||
|
+ 6. Success
|
||||||
|
"""
|
||||||
|
(user, groups) = automember_fixture
|
||||||
|
|
||||||
|
# Update user which should go into group[0]
|
||||||
|
user.replace('cn', 'whatever')
|
||||||
|
- groups[0].is_member(user.dn)
|
||||||
|
+ assert groups[0].is_member(user.dn)
|
||||||
|
if groups[1].is_member(user.dn):
|
||||||
|
assert False
|
||||||
|
if groups[2].is_member(user.dn):
|
||||||
|
@@ -92,7 +100,7 @@ def test_mods(automember_fixture, topo):
|
||||||
|
|
||||||
|
# Update user0 which should go into group[1]
|
||||||
|
user.replace('cn', 'mark')
|
||||||
|
- groups[1].is_member(user.dn)
|
||||||
|
+ assert groups[1].is_member(user.dn)
|
||||||
|
if groups[0].is_member(user.dn):
|
||||||
|
assert False
|
||||||
|
if groups[2].is_member(user.dn):
|
||||||
|
@@ -100,7 +108,7 @@ def test_mods(automember_fixture, topo):
|
||||||
|
|
||||||
|
# Update user which should go into group[2]
|
||||||
|
user.replace('cn', 'simon')
|
||||||
|
- groups[2].is_member(user.dn)
|
||||||
|
+ assert groups[2].is_member(user.dn)
|
||||||
|
if groups[0].is_member(user.dn):
|
||||||
|
assert False
|
||||||
|
if groups[1].is_member(user.dn):
|
||||||
|
@@ -108,7 +116,7 @@ def test_mods(automember_fixture, topo):
|
||||||
|
|
||||||
|
# Update user which should go back into group[0] (full circle)
|
||||||
|
user.replace('cn', 'whatever')
|
||||||
|
- groups[0].is_member(user.dn)
|
||||||
|
+ assert groups[0].is_member(user.dn)
|
||||||
|
if groups[1].is_member(user.dn):
|
||||||
|
assert False
|
||||||
|
if groups[2].is_member(user.dn):
|
||||||
|
@@ -128,12 +136,24 @@ def test_mods(automember_fixture, topo):
|
||||||
|
automemberplugin.enable()
|
||||||
|
topo.standalone.restart()
|
||||||
|
|
||||||
|
- # Run rebuild task
|
||||||
|
+ # Run rebuild task (no cleanup)
|
||||||
|
task = automemberplugin.fixup(DEFAULT_SUFFIX, "objectclass=posixaccount")
|
||||||
|
+ with pytest.raises(ldap.UNWILLING_TO_PERFORM):
|
||||||
|
+ # test only one fixup task is allowed at a time
|
||||||
|
+ automemberplugin.fixup(DEFAULT_SUFFIX, "objectclass=top")
|
||||||
|
task.wait()
|
||||||
|
|
||||||
|
- # Test membership
|
||||||
|
- groups[1].is_member(user.dn)
|
||||||
|
+ # Test membership (user should still be in groups[0])
|
||||||
|
+ assert groups[1].is_member(user.dn)
|
||||||
|
+ if not groups[0].is_member(user.dn):
|
||||||
|
+ assert False
|
||||||
|
+
|
||||||
|
+ # Run rebuild task with cleanup
|
||||||
|
+ task = automemberplugin.fixup(DEFAULT_SUFFIX, "objectclass=posixaccount", cleanup=True)
|
||||||
|
+ task.wait()
|
||||||
|
+
|
||||||
|
+ # Test membership (user should only be in groups[1])
|
||||||
|
+ assert groups[1].is_member(user.dn)
|
||||||
|
if groups[0].is_member(user.dn):
|
||||||
|
assert False
|
||||||
|
if groups[2].is_member(user.dn):
|
||||||
|
@@ -148,4 +168,3 @@ if __name__ == '__main__':
|
||||||
|
# -s for DEBUG mode
|
||||||
|
CURRENT_FILE = os.path.realpath(__file__)
|
||||||
|
pytest.main(["-s", CURRENT_FILE])
|
||||||
|
-
|
||||||
|
diff --git a/ldap/servers/plugins/automember/automember.c b/ldap/servers/plugins/automember/automember.c
|
||||||
|
index 3494d0343..419adb052 100644
|
||||||
|
--- a/ldap/servers/plugins/automember/automember.c
|
||||||
|
+++ b/ldap/servers/plugins/automember/automember.c
|
||||||
|
@@ -1,5 +1,5 @@
|
||||||
|
/** BEGIN COPYRIGHT BLOCK
|
||||||
|
- * Copyright (C) 2011 Red Hat, Inc.
|
||||||
|
+ * Copyright (C) 2022 Red Hat, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* License: GPL (version 3 or any later version).
|
||||||
|
@@ -14,7 +14,7 @@
|
||||||
|
* Auto Membership Plug-in
|
||||||
|
*/
|
||||||
|
#include "automember.h"
|
||||||
|
-
|
||||||
|
+#include <pthread.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Plug-in globals
|
||||||
|
@@ -22,7 +22,9 @@
|
||||||
|
static PRCList *g_automember_config = NULL;
|
||||||
|
static Slapi_RWLock *g_automember_config_lock = NULL;
|
||||||
|
static uint64_t abort_rebuild_task = 0;
|
||||||
|
-
|
||||||
|
+static pthread_key_t td_automem_block_nested;
|
||||||
|
+static PRBool fixup_running = PR_FALSE;
|
||||||
|
+static PRLock *fixup_lock = NULL;
|
||||||
|
static void *_PluginID = NULL;
|
||||||
|
static Slapi_DN *_PluginDN = NULL;
|
||||||
|
static Slapi_DN *_ConfigAreaDN = NULL;
|
||||||
|
@@ -93,9 +95,43 @@ static void automember_task_export_destructor(Slapi_Task *task);
|
||||||
|
static void automember_task_map_destructor(Slapi_Task *task);
|
||||||
|
|
||||||
|
#define DEFAULT_FILE_MODE PR_IRUSR | PR_IWUSR
|
||||||
|
+#define FIXUP_PROGRESS_LIMIT 1000
|
||||||
|
static uint64_t plugin_do_modify = 0;
|
||||||
|
static uint64_t plugin_is_betxn = 0;
|
||||||
|
|
||||||
|
+/* automember_plugin fixup task and add operations should block other be_txn
|
||||||
|
+ * plugins from calling automember_post_op_mod() */
|
||||||
|
+static int32_t
|
||||||
|
+slapi_td_block_nested_post_op(void)
|
||||||
|
+{
|
||||||
|
+ int32_t val = 12345;
|
||||||
|
+
|
||||||
|
+ if (pthread_setspecific(td_automem_block_nested, (void *)&val) != 0) {
|
||||||
|
+ return PR_FAILURE;
|
||||||
|
+ }
|
||||||
|
+ return PR_SUCCESS;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static int32_t
|
||||||
|
+slapi_td_unblock_nested_post_op(void)
|
||||||
|
+{
|
||||||
|
+ if (pthread_setspecific(td_automem_block_nested, NULL) != 0) {
|
||||||
|
+ return PR_FAILURE;
|
||||||
|
+ }
|
||||||
|
+ return PR_SUCCESS;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static int32_t
|
||||||
|
+slapi_td_is_post_op_nested(void)
|
||||||
|
+{
|
||||||
|
+ int32_t *value = pthread_getspecific(td_automem_block_nested);
|
||||||
|
+
|
||||||
|
+ if (value == NULL) {
|
||||||
|
+ return 0;
|
||||||
|
+ }
|
||||||
|
+ return 1;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
/*
|
||||||
|
* Config cache locking functions
|
||||||
|
*/
|
||||||
|
@@ -317,6 +353,14 @@ automember_start(Slapi_PBlock *pb)
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ if (fixup_lock == NULL) {
|
||||||
|
+ if ((fixup_lock = PR_NewLock()) == NULL) {
|
||||||
|
+ slapi_log_err(SLAPI_LOG_ERR, AUTOMEMBER_PLUGIN_SUBSYSTEM,
|
||||||
|
+ "automember_start - Failed to create fixup lock.\n");
|
||||||
|
+ return -1;
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
/*
|
||||||
|
* Get the plug-in target dn from the system
|
||||||
|
* and store it for future use. */
|
||||||
|
@@ -360,6 +404,11 @@ automember_start(Slapi_PBlock *pb)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
+ if (pthread_key_create(&td_automem_block_nested, NULL) != 0) {
|
||||||
|
+ slapi_log_err(SLAPI_LOG_ERR, AUTOMEMBER_PLUGIN_SUBSYSTEM,
|
||||||
|
+ "automember_start - pthread_key_create failed\n");
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
slapi_log_err(SLAPI_LOG_PLUGIN, AUTOMEMBER_PLUGIN_SUBSYSTEM,
|
||||||
|
"automember_start - ready for service\n");
|
||||||
|
slapi_log_err(SLAPI_LOG_TRACE, AUTOMEMBER_PLUGIN_SUBSYSTEM,
|
||||||
|
@@ -394,6 +443,8 @@ automember_close(Slapi_PBlock *pb __attribute__((unused)))
|
||||||
|
slapi_sdn_free(&_ConfigAreaDN);
|
||||||
|
slapi_destroy_rwlock(g_automember_config_lock);
|
||||||
|
g_automember_config_lock = NULL;
|
||||||
|
+ PR_DestroyLock(fixup_lock);
|
||||||
|
+ fixup_lock = NULL;
|
||||||
|
|
||||||
|
slapi_log_err(SLAPI_LOG_TRACE, AUTOMEMBER_PLUGIN_SUBSYSTEM,
|
||||||
|
"<-- automember_close\n");
|
||||||
|
@@ -1619,7 +1670,6 @@ out:
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
-
|
||||||
|
/*
|
||||||
|
* automember_update_member_value()
|
||||||
|
*
|
||||||
|
@@ -1634,7 +1684,7 @@ automember_update_member_value(Slapi_Entry *member_e, const char *group_dn, char
|
||||||
|
LDAPMod *mods[2];
|
||||||
|
char *vals[2];
|
||||||
|
char *member_value = NULL;
|
||||||
|
- int rc = 0;
|
||||||
|
+ int rc = LDAP_SUCCESS;
|
||||||
|
Slapi_DN *group_sdn;
|
||||||
|
|
||||||
|
/* First thing check that the group still exists */
|
||||||
|
@@ -1653,7 +1703,7 @@ automember_update_member_value(Slapi_Entry *member_e, const char *group_dn, char
|
||||||
|
"automember_update_member_value - group (default or target) can not be retrieved (%s) err=%d\n",
|
||||||
|
group_dn, rc);
|
||||||
|
}
|
||||||
|
- return rc;
|
||||||
|
+ goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If grouping_value is dn, we need to fetch the dn instead. */
|
||||||
|
@@ -1879,6 +1929,13 @@ automember_mod_post_op(Slapi_PBlock *pb)
|
||||||
|
PRCList *list = NULL;
|
||||||
|
int rc = SLAPI_PLUGIN_SUCCESS;
|
||||||
|
|
||||||
|
+ if (slapi_td_is_post_op_nested()) {
|
||||||
|
+ /* don't process op twice in the same thread */
|
||||||
|
+ return rc;
|
||||||
|
+ } else {
|
||||||
|
+ slapi_td_block_nested_post_op();
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
slapi_log_err(SLAPI_LOG_TRACE, AUTOMEMBER_PLUGIN_SUBSYSTEM,
|
||||||
|
"--> automember_mod_post_op\n");
|
||||||
|
|
||||||
|
@@ -2005,6 +2062,7 @@ automember_mod_post_op(Slapi_PBlock *pb)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
+ slapi_td_unblock_nested_post_op();
|
||||||
|
|
||||||
|
slapi_log_err(SLAPI_LOG_TRACE, AUTOMEMBER_PLUGIN_SUBSYSTEM,
|
||||||
|
"<-- automember_mod_post_op (%d)\n", rc);
|
||||||
|
@@ -2024,6 +2082,13 @@ automember_add_post_op(Slapi_PBlock *pb)
|
||||||
|
slapi_log_err(SLAPI_LOG_TRACE, AUTOMEMBER_PLUGIN_SUBSYSTEM,
|
||||||
|
"--> automember_add_post_op\n");
|
||||||
|
|
||||||
|
+ if (slapi_td_is_post_op_nested()) {
|
||||||
|
+ /* don't process op twice in the same thread */
|
||||||
|
+ return rc;
|
||||||
|
+ } else {
|
||||||
|
+ slapi_td_block_nested_post_op();
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
/* Reload config if a config entry was added. */
|
||||||
|
if ((sdn = automember_get_sdn(pb))) {
|
||||||
|
if (automember_dn_is_config(sdn)) {
|
||||||
|
@@ -2039,7 +2104,7 @@ automember_add_post_op(Slapi_PBlock *pb)
|
||||||
|
|
||||||
|
/* If replication, just bail. */
|
||||||
|
if (automember_isrepl(pb)) {
|
||||||
|
- return SLAPI_PLUGIN_SUCCESS;
|
||||||
|
+ goto bail;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get the newly added entry. */
|
||||||
|
@@ -2052,7 +2117,7 @@ automember_add_post_op(Slapi_PBlock *pb)
|
||||||
|
tombstone);
|
||||||
|
slapi_value_free(&tombstone);
|
||||||
|
if (is_tombstone) {
|
||||||
|
- return SLAPI_PLUGIN_SUCCESS;
|
||||||
|
+ goto bail;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if a config entry applies
|
||||||
|
@@ -2063,21 +2128,19 @@ automember_add_post_op(Slapi_PBlock *pb)
|
||||||
|
list = PR_LIST_HEAD(g_automember_config);
|
||||||
|
while (list != g_automember_config) {
|
||||||
|
config = (struct configEntry *)list;
|
||||||
|
-
|
||||||
|
/* Does the entry meet scope and filter requirements? */
|
||||||
|
if (slapi_dn_issuffix(slapi_sdn_get_dn(sdn), config->scope) &&
|
||||||
|
- (slapi_filter_test_simple(e, config->filter) == 0)) {
|
||||||
|
+ (slapi_filter_test_simple(e, config->filter) == 0))
|
||||||
|
+ {
|
||||||
|
/* Find out what membership changes are needed and make them. */
|
||||||
|
if (automember_update_membership(config, e, NULL) == SLAPI_PLUGIN_FAILURE) {
|
||||||
|
rc = SLAPI_PLUGIN_FAILURE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
-
|
||||||
|
list = PR_NEXT_LINK(list);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
-
|
||||||
|
automember_config_unlock();
|
||||||
|
} else {
|
||||||
|
slapi_log_err(SLAPI_LOG_PLUGIN, AUTOMEMBER_PLUGIN_SUBSYSTEM,
|
||||||
|
@@ -2098,6 +2161,7 @@ bail:
|
||||||
|
slapi_pblock_set(pb, SLAPI_RESULT_CODE, &result);
|
||||||
|
slapi_pblock_set(pb, SLAPI_PB_RESULT_TEXT, &errtxt);
|
||||||
|
}
|
||||||
|
+ slapi_td_unblock_nested_post_op();
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
@@ -2138,6 +2202,7 @@ typedef struct _task_data
|
||||||
|
Slapi_DN *base_dn;
|
||||||
|
char *bind_dn;
|
||||||
|
int scope;
|
||||||
|
+ PRBool cleanup;
|
||||||
|
} task_data;
|
||||||
|
|
||||||
|
static void
|
||||||
|
@@ -2270,6 +2335,7 @@ automember_task_abort_thread(void *arg)
|
||||||
|
* basedn: dc=example,dc=com
|
||||||
|
* filter: (uid=*)
|
||||||
|
* scope: sub
|
||||||
|
+ * cleanup: yes/on (default is off)
|
||||||
|
*
|
||||||
|
* basedn and filter are required. If scope is omitted, the default is sub
|
||||||
|
*/
|
||||||
|
@@ -2284,9 +2350,22 @@ automember_task_add(Slapi_PBlock *pb, Slapi_Entry *e, Slapi_Entry *eAfter __attr
|
||||||
|
const char *base_dn;
|
||||||
|
const char *filter;
|
||||||
|
const char *scope;
|
||||||
|
+ const char *cleanup_str;
|
||||||
|
+ PRBool cleanup = PR_FALSE;
|
||||||
|
|
||||||
|
*returncode = LDAP_SUCCESS;
|
||||||
|
|
||||||
|
+ PR_Lock(fixup_lock);
|
||||||
|
+ if (fixup_running) {
|
||||||
|
+ PR_Unlock(fixup_lock);
|
||||||
|
+ *returncode = LDAP_UNWILLING_TO_PERFORM;
|
||||||
|
+ slapi_log_err(SLAPI_LOG_ERR, AUTOMEMBER_PLUGIN_SUBSYSTEM,
|
||||||
|
+ "automember_task_add - there is already a fixup task running\n");
|
||||||
|
+ rv = SLAPI_DSE_CALLBACK_ERROR;
|
||||||
|
+ goto out;
|
||||||
|
+ }
|
||||||
|
+ PR_Unlock(fixup_lock);
|
||||||
|
+
|
||||||
|
/*
|
||||||
|
* Grab the task params
|
||||||
|
*/
|
||||||
|
@@ -2300,6 +2379,12 @@ automember_task_add(Slapi_PBlock *pb, Slapi_Entry *e, Slapi_Entry *eAfter __attr
|
||||||
|
rv = SLAPI_DSE_CALLBACK_ERROR;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
+ if ((cleanup_str = slapi_entry_attr_get_ref(e, "cleanup"))) {
|
||||||
|
+ if (strcasecmp(cleanup_str, "yes") == 0 || strcasecmp(cleanup_str, "on")) {
|
||||||
|
+ cleanup = PR_TRUE;
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
scope = slapi_fetch_attr(e, "scope", "sub");
|
||||||
|
/*
|
||||||
|
* setup our task data
|
||||||
|
@@ -2315,6 +2400,7 @@ automember_task_add(Slapi_PBlock *pb, Slapi_Entry *e, Slapi_Entry *eAfter __attr
|
||||||
|
mytaskdata->bind_dn = slapi_ch_strdup(bind_dn);
|
||||||
|
mytaskdata->base_dn = slapi_sdn_new_dn_byval(base_dn);
|
||||||
|
mytaskdata->filter_str = slapi_ch_strdup(filter);
|
||||||
|
+ mytaskdata->cleanup = cleanup;
|
||||||
|
|
||||||
|
if (scope) {
|
||||||
|
if (strcasecmp(scope, "sub") == 0) {
|
||||||
|
@@ -2334,6 +2420,9 @@ automember_task_add(Slapi_PBlock *pb, Slapi_Entry *e, Slapi_Entry *eAfter __attr
|
||||||
|
task = slapi_plugin_new_task(slapi_entry_get_ndn(e), arg);
|
||||||
|
slapi_task_set_destructor_fn(task, automember_task_destructor);
|
||||||
|
slapi_task_set_data(task, mytaskdata);
|
||||||
|
+ PR_Lock(fixup_lock);
|
||||||
|
+ fixup_running = PR_TRUE;
|
||||||
|
+ PR_Unlock(fixup_lock);
|
||||||
|
/*
|
||||||
|
* Start the task as a separate thread
|
||||||
|
*/
|
||||||
|
@@ -2345,6 +2434,9 @@ automember_task_add(Slapi_PBlock *pb, Slapi_Entry *e, Slapi_Entry *eAfter __attr
|
||||||
|
"automember_task_add - Unable to create task thread!\n");
|
||||||
|
*returncode = LDAP_OPERATIONS_ERROR;
|
||||||
|
slapi_task_finish(task, *returncode);
|
||||||
|
+ PR_Lock(fixup_lock);
|
||||||
|
+ fixup_running = PR_FALSE;
|
||||||
|
+ PR_Unlock(fixup_lock);
|
||||||
|
rv = SLAPI_DSE_CALLBACK_ERROR;
|
||||||
|
} else {
|
||||||
|
rv = SLAPI_DSE_CALLBACK_OK;
|
||||||
|
@@ -2372,6 +2464,9 @@ automember_rebuild_task_thread(void *arg)
|
||||||
|
PRCList *list = NULL;
|
||||||
|
PRCList *include_list = NULL;
|
||||||
|
int result = 0;
|
||||||
|
+ int64_t fixup_progress_count = 0;
|
||||||
|
+ int64_t fixup_progress_elapsed = 0;
|
||||||
|
+ int64_t fixup_start_time = 0;
|
||||||
|
size_t i = 0;
|
||||||
|
|
||||||
|
/* Reset abort flag */
|
||||||
|
@@ -2380,6 +2475,7 @@ automember_rebuild_task_thread(void *arg)
|
||||||
|
if (!task) {
|
||||||
|
return; /* no task */
|
||||||
|
}
|
||||||
|
+
|
||||||
|
slapi_task_inc_refcount(task);
|
||||||
|
slapi_log_err(SLAPI_LOG_PLUGIN, AUTOMEMBER_PLUGIN_SUBSYSTEM,
|
||||||
|
"automember_rebuild_task_thread - Refcount incremented.\n");
|
||||||
|
@@ -2393,9 +2489,11 @@ automember_rebuild_task_thread(void *arg)
|
||||||
|
slapi_task_log_status(task, "Automember rebuild task starting (base dn: (%s) filter (%s)...",
|
||||||
|
slapi_sdn_get_dn(td->base_dn), td->filter_str);
|
||||||
|
/*
|
||||||
|
- * Set the bind dn in the local thread data
|
||||||
|
+ * Set the bind dn in the local thread data, and block post op mods
|
||||||
|
*/
|
||||||
|
slapi_td_set_dn(slapi_ch_strdup(td->bind_dn));
|
||||||
|
+ slapi_td_block_nested_post_op();
|
||||||
|
+ fixup_start_time = slapi_current_rel_time_t();
|
||||||
|
/*
|
||||||
|
* Take the config lock now and search the database
|
||||||
|
*/
|
||||||
|
@@ -2426,6 +2524,21 @@ automember_rebuild_task_thread(void *arg)
|
||||||
|
* Loop over the entries
|
||||||
|
*/
|
||||||
|
for (i = 0; entries && (entries[i] != NULL); i++) {
|
||||||
|
+ fixup_progress_count++;
|
||||||
|
+ if (fixup_progress_count % FIXUP_PROGRESS_LIMIT == 0 ) {
|
||||||
|
+ slapi_task_log_notice(task,
|
||||||
|
+ "Processed %ld entries in %ld seconds (+%ld seconds)",
|
||||||
|
+ fixup_progress_count,
|
||||||
|
+ slapi_current_rel_time_t() - fixup_start_time,
|
||||||
|
+ slapi_current_rel_time_t() - fixup_progress_elapsed);
|
||||||
|
+ slapi_task_log_status(task,
|
||||||
|
+ "Processed %ld entries in %ld seconds (+%ld seconds)",
|
||||||
|
+ fixup_progress_count,
|
||||||
|
+ slapi_current_rel_time_t() - fixup_start_time,
|
||||||
|
+ slapi_current_rel_time_t() - fixup_progress_elapsed);
|
||||||
|
+ slapi_task_inc_progress(task);
|
||||||
|
+ fixup_progress_elapsed = slapi_current_rel_time_t();
|
||||||
|
+ }
|
||||||
|
if (slapi_atomic_load_64(&abort_rebuild_task, __ATOMIC_ACQUIRE) == 1) {
|
||||||
|
/* The task was aborted */
|
||||||
|
slapi_task_log_notice(task, "Automember rebuild task was intentionally aborted");
|
||||||
|
@@ -2443,48 +2556,66 @@ automember_rebuild_task_thread(void *arg)
|
||||||
|
if (slapi_dn_issuffix(slapi_entry_get_dn(entries[i]), config->scope) &&
|
||||||
|
(slapi_filter_test_simple(entries[i], config->filter) == 0))
|
||||||
|
{
|
||||||
|
- /* First clear out all the defaults groups */
|
||||||
|
- for (size_t ii = 0; config->default_groups && config->default_groups[ii]; ii++) {
|
||||||
|
- if ((result = automember_update_member_value(entries[i], config->default_groups[ii],
|
||||||
|
- config->grouping_attr, config->grouping_value, NULL, DEL_MEMBER)))
|
||||||
|
- {
|
||||||
|
- slapi_task_log_notice(task, "Automember rebuild membership task unable to delete "
|
||||||
|
- "member from default group (%s) error (%d)",
|
||||||
|
- config->default_groups[ii], result);
|
||||||
|
- slapi_task_log_status(task, "Automember rebuild membership task unable to delete "
|
||||||
|
- "member from default group (%s) error (%d)",
|
||||||
|
- config->default_groups[ii], result);
|
||||||
|
- slapi_log_err(SLAPI_LOG_ERR, AUTOMEMBER_PLUGIN_SUBSYSTEM,
|
||||||
|
- "automember_rebuild_task_thread - Unable to unable to delete from (%s) error (%d)\n",
|
||||||
|
- config->default_groups[ii], result);
|
||||||
|
- goto out;
|
||||||
|
- }
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- /* Then clear out the non-default group */
|
||||||
|
- if (config->inclusive_rules && !PR_CLIST_IS_EMPTY((PRCList *)config->inclusive_rules)) {
|
||||||
|
- include_list = PR_LIST_HEAD((PRCList *)config->inclusive_rules);
|
||||||
|
- while (include_list != (PRCList *)config->inclusive_rules) {
|
||||||
|
- struct automemberRegexRule *curr_rule = (struct automemberRegexRule *)include_list;
|
||||||
|
- if ((result = automember_update_member_value(entries[i], slapi_sdn_get_dn(curr_rule->target_group_dn),
|
||||||
|
- config->grouping_attr, config->grouping_value, NULL, DEL_MEMBER)))
|
||||||
|
+ if (td->cleanup) {
|
||||||
|
+
|
||||||
|
+ slapi_log_err(SLAPI_LOG_PLUGIN, AUTOMEMBER_PLUGIN_SUBSYSTEM,
|
||||||
|
+ "automember_rebuild_task_thread - Cleaning up groups (config %s)\n",
|
||||||
|
+ config->dn);
|
||||||
|
+ /* First clear out all the defaults groups */
|
||||||
|
+ for (size_t ii = 0; config->default_groups && config->default_groups[ii]; ii++) {
|
||||||
|
+ if ((result = automember_update_member_value(entries[i],
|
||||||
|
+ config->default_groups[ii],
|
||||||
|
+ config->grouping_attr,
|
||||||
|
+ config->grouping_value,
|
||||||
|
+ NULL, DEL_MEMBER)))
|
||||||
|
{
|
||||||
|
slapi_task_log_notice(task, "Automember rebuild membership task unable to delete "
|
||||||
|
- "member from group (%s) error (%d)",
|
||||||
|
- slapi_sdn_get_dn(curr_rule->target_group_dn), result);
|
||||||
|
+ "member from default group (%s) error (%d)",
|
||||||
|
+ config->default_groups[ii], result);
|
||||||
|
slapi_task_log_status(task, "Automember rebuild membership task unable to delete "
|
||||||
|
- "member from group (%s) error (%d)",
|
||||||
|
- slapi_sdn_get_dn(curr_rule->target_group_dn), result);
|
||||||
|
+ "member from default group (%s) error (%d)",
|
||||||
|
+ config->default_groups[ii], result);
|
||||||
|
slapi_log_err(SLAPI_LOG_ERR, AUTOMEMBER_PLUGIN_SUBSYSTEM,
|
||||||
|
"automember_rebuild_task_thread - Unable to unable to delete from (%s) error (%d)\n",
|
||||||
|
- slapi_sdn_get_dn(curr_rule->target_group_dn), result);
|
||||||
|
+ config->default_groups[ii], result);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
- include_list = PR_NEXT_LINK(include_list);
|
||||||
|
}
|
||||||
|
+
|
||||||
|
+ /* Then clear out the non-default group */
|
||||||
|
+ if (config->inclusive_rules && !PR_CLIST_IS_EMPTY((PRCList *)config->inclusive_rules)) {
|
||||||
|
+ include_list = PR_LIST_HEAD((PRCList *)config->inclusive_rules);
|
||||||
|
+ while (include_list != (PRCList *)config->inclusive_rules) {
|
||||||
|
+ struct automemberRegexRule *curr_rule = (struct automemberRegexRule *)include_list;
|
||||||
|
+ if ((result = automember_update_member_value(entries[i],
|
||||||
|
+ slapi_sdn_get_dn(curr_rule->target_group_dn),
|
||||||
|
+ config->grouping_attr,
|
||||||
|
+ config->grouping_value,
|
||||||
|
+ NULL, DEL_MEMBER)))
|
||||||
|
+ {
|
||||||
|
+ slapi_task_log_notice(task, "Automember rebuild membership task unable to delete "
|
||||||
|
+ "member from group (%s) error (%d)",
|
||||||
|
+ slapi_sdn_get_dn(curr_rule->target_group_dn), result);
|
||||||
|
+ slapi_task_log_status(task, "Automember rebuild membership task unable to delete "
|
||||||
|
+ "member from group (%s) error (%d)",
|
||||||
|
+ slapi_sdn_get_dn(curr_rule->target_group_dn), result);
|
||||||
|
+ slapi_log_err(SLAPI_LOG_ERR, AUTOMEMBER_PLUGIN_SUBSYSTEM,
|
||||||
|
+ "automember_rebuild_task_thread - Unable to unable to delete from (%s) error (%d)\n",
|
||||||
|
+ slapi_sdn_get_dn(curr_rule->target_group_dn), result);
|
||||||
|
+ goto out;
|
||||||
|
+ }
|
||||||
|
+ include_list = PR_NEXT_LINK(include_list);
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ slapi_log_err(SLAPI_LOG_PLUGIN, AUTOMEMBER_PLUGIN_SUBSYSTEM,
|
||||||
|
+ "automember_rebuild_task_thread - Finished cleaning up groups (config %s)\n",
|
||||||
|
+ config->dn);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Update the memberships for this entries */
|
||||||
|
+ slapi_log_err(SLAPI_LOG_PLUGIN, AUTOMEMBER_PLUGIN_SUBSYSTEM,
|
||||||
|
+ "automember_rebuild_task_thread - Updating membership (config %s)\n",
|
||||||
|
+ config->dn);
|
||||||
|
if (slapi_is_shutting_down() ||
|
||||||
|
automember_update_membership(config, entries[i], NULL) == SLAPI_PLUGIN_FAILURE)
|
||||||
|
{
|
||||||
|
@@ -2508,15 +2639,22 @@ out:
|
||||||
|
slapi_task_log_notice(task, "Automember rebuild task aborted. Error (%d)", result);
|
||||||
|
slapi_task_log_status(task, "Automember rebuild task aborted. Error (%d)", result);
|
||||||
|
} else {
|
||||||
|
- slapi_task_log_notice(task, "Automember rebuild task finished. Processed (%d) entries.", (int32_t)i);
|
||||||
|
- slapi_task_log_status(task, "Automember rebuild task finished. Processed (%d) entries.", (int32_t)i);
|
||||||
|
+ slapi_task_log_notice(task, "Automember rebuild task finished. Processed (%ld) entries in %ld seconds",
|
||||||
|
+ (int64_t)i, slapi_current_rel_time_t() - fixup_start_time);
|
||||||
|
+ slapi_task_log_status(task, "Automember rebuild task finished. Processed (%ld) entries in %ld seconds",
|
||||||
|
+ (int64_t)i, slapi_current_rel_time_t() - fixup_start_time);
|
||||||
|
}
|
||||||
|
slapi_task_inc_progress(task);
|
||||||
|
slapi_task_finish(task, result);
|
||||||
|
slapi_task_dec_refcount(task);
|
||||||
|
slapi_atomic_store_64(&abort_rebuild_task, 0, __ATOMIC_RELEASE);
|
||||||
|
+ slapi_td_unblock_nested_post_op();
|
||||||
|
+ PR_Lock(fixup_lock);
|
||||||
|
+ fixup_running = PR_FALSE;
|
||||||
|
+ PR_Unlock(fixup_lock);
|
||||||
|
+
|
||||||
|
slapi_log_err(SLAPI_LOG_PLUGIN, AUTOMEMBER_PLUGIN_SUBSYSTEM,
|
||||||
|
- "automember_rebuild_task_thread - Refcount decremented.\n");
|
||||||
|
+ "automember_rebuild_task_thread - task finished, refcount decremented.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
diff --git a/ldap/servers/slapd/back-ldbm/ldbm_add.c b/ldap/servers/slapd/back-ldbm/ldbm_add.c
|
||||||
|
index ba2d73a84..ce4c314a1 100644
|
||||||
|
--- a/ldap/servers/slapd/back-ldbm/ldbm_add.c
|
||||||
|
+++ b/ldap/servers/slapd/back-ldbm/ldbm_add.c
|
||||||
|
@@ -1,6 +1,6 @@
|
||||||
|
/** BEGIN COPYRIGHT BLOCK
|
||||||
|
* Copyright (C) 2001 Sun Microsystems, Inc. Used by permission.
|
||||||
|
- * Copyright (C) 2005 Red Hat, Inc.
|
||||||
|
+ * Copyright (C) 2022 Red Hat, Inc.
|
||||||
|
* Copyright (C) 2009 Hewlett-Packard Development Company, L.P.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
@@ -1264,10 +1264,6 @@ ldbm_back_add(Slapi_PBlock *pb)
|
||||||
|
goto common_return;
|
||||||
|
|
||||||
|
error_return:
|
||||||
|
- /* Revert the caches if this is the parent operation */
|
||||||
|
- if (parent_op && betxn_callback_fails) {
|
||||||
|
- revert_cache(inst, &parent_time);
|
||||||
|
- }
|
||||||
|
if (addingentry_id_assigned) {
|
||||||
|
next_id_return(be, addingentry->ep_id);
|
||||||
|
}
|
||||||
|
@@ -1376,6 +1372,11 @@ diskfull_return:
|
||||||
|
if (!not_an_error) {
|
||||||
|
rc = SLAPI_FAIL_GENERAL;
|
||||||
|
}
|
||||||
|
+
|
||||||
|
+ /* Revert the caches if this is the parent operation */
|
||||||
|
+ if (parent_op && betxn_callback_fails) {
|
||||||
|
+ revert_cache(inst, &parent_time);
|
||||||
|
+ }
|
||||||
|
}
|
||||||
|
|
||||||
|
common_return:
|
||||||
|
diff --git a/ldap/servers/slapd/back-ldbm/ldbm_delete.c b/ldap/servers/slapd/back-ldbm/ldbm_delete.c
|
||||||
|
index de23190c3..27f0ac58a 100644
|
||||||
|
--- a/ldap/servers/slapd/back-ldbm/ldbm_delete.c
|
||||||
|
+++ b/ldap/servers/slapd/back-ldbm/ldbm_delete.c
|
||||||
|
@@ -1407,11 +1407,6 @@ commit_return:
|
||||||
|
goto common_return;
|
||||||
|
|
||||||
|
error_return:
|
||||||
|
- /* Revert the caches if this is the parent operation */
|
||||||
|
- if (parent_op && betxn_callback_fails) {
|
||||||
|
- revert_cache(inst, &parent_time);
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
if (tombstone) {
|
||||||
|
if (cache_is_in_cache(&inst->inst_cache, tombstone)) {
|
||||||
|
tomb_ep_id = tombstone->ep_id; /* Otherwise, tombstone might have been freed. */
|
||||||
|
@@ -1496,6 +1491,11 @@ error_return:
|
||||||
|
conn_id, op_id, parent_modify_c.old_entry, parent_modify_c.new_entry, myrc);
|
||||||
|
}
|
||||||
|
|
||||||
|
+ /* Revert the caches if this is the parent operation */
|
||||||
|
+ if (parent_op && betxn_callback_fails) {
|
||||||
|
+ revert_cache(inst, &parent_time);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
common_return:
|
||||||
|
if (orig_entry) {
|
||||||
|
/* NOTE: #define SLAPI_DELETE_BEPREOP_ENTRY SLAPI_ENTRY_PRE_OP */
|
||||||
|
diff --git a/ldap/servers/slapd/back-ldbm/ldbm_modify.c b/ldap/servers/slapd/back-ldbm/ldbm_modify.c
|
||||||
|
index 537369055..64b293001 100644
|
||||||
|
--- a/ldap/servers/slapd/back-ldbm/ldbm_modify.c
|
||||||
|
+++ b/ldap/servers/slapd/back-ldbm/ldbm_modify.c
|
||||||
|
@@ -1,6 +1,6 @@
|
||||||
|
/** BEGIN COPYRIGHT BLOCK
|
||||||
|
* Copyright (C) 2001 Sun Microsystems, Inc. Used by permission.
|
||||||
|
- * Copyright (C) 2005 Red Hat, Inc.
|
||||||
|
+ * Copyright (C) 2022 Red Hat, Inc.
|
||||||
|
* Copyright (C) 2009 Hewlett-Packard Development Company, L.P.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
@@ -1043,11 +1043,6 @@ ldbm_back_modify(Slapi_PBlock *pb)
|
||||||
|
goto common_return;
|
||||||
|
|
||||||
|
error_return:
|
||||||
|
- /* Revert the caches if this is the parent operation */
|
||||||
|
- if (parent_op && betxn_callback_fails) {
|
||||||
|
- revert_cache(inst, &parent_time);
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
if (postentry != NULL) {
|
||||||
|
slapi_entry_free(postentry);
|
||||||
|
postentry = NULL;
|
||||||
|
@@ -1103,6 +1098,10 @@ error_return:
|
||||||
|
if (!not_an_error) {
|
||||||
|
rc = SLAPI_FAIL_GENERAL;
|
||||||
|
}
|
||||||
|
+ /* Revert the caches if this is the parent operation */
|
||||||
|
+ if (parent_op && betxn_callback_fails) {
|
||||||
|
+ revert_cache(inst, &parent_time);
|
||||||
|
+ }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* if ec is in cache, remove it, then add back e if we still have it */
|
||||||
|
diff --git a/src/lib389/lib389/cli_conf/plugins/automember.py b/src/lib389/lib389/cli_conf/plugins/automember.py
|
||||||
|
index 15b00c633..568586ad8 100644
|
||||||
|
--- a/src/lib389/lib389/cli_conf/plugins/automember.py
|
||||||
|
+++ b/src/lib389/lib389/cli_conf/plugins/automember.py
|
||||||
|
@@ -155,7 +155,7 @@ def fixup(inst, basedn, log, args):
|
||||||
|
log.info('Attempting to add task entry... This will fail if Automembership plug-in is not enabled.')
|
||||||
|
if not plugin.status():
|
||||||
|
log.error("'%s' is disabled. Rebuild membership task can't be executed" % plugin.rdn)
|
||||||
|
- fixup_task = plugin.fixup(args.DN, args.filter)
|
||||||
|
+ fixup_task = plugin.fixup(args.DN, args.filter, args.cleanup)
|
||||||
|
if args.wait:
|
||||||
|
log.info(f'Waiting for fixup task "{fixup_task.dn}" to complete. You can safely exit by pressing Control C ...')
|
||||||
|
fixup_task.wait(timeout=args.timeout)
|
||||||
|
@@ -225,8 +225,8 @@ def create_parser(subparsers):
|
||||||
|
subcommands = automember.add_subparsers(help='action')
|
||||||
|
add_generic_plugin_parsers(subcommands, AutoMembershipPlugin)
|
||||||
|
|
||||||
|
- list = subcommands.add_parser('list', help='List Automembership definitions or regex rules.')
|
||||||
|
- subcommands_list = list.add_subparsers(help='action')
|
||||||
|
+ automember_list = subcommands.add_parser('list', help='List Automembership definitions or regex rules.')
|
||||||
|
+ subcommands_list = automember_list.add_subparsers(help='action')
|
||||||
|
list_definitions = subcommands_list.add_parser('definitions', help='Lists Automembership definitions.')
|
||||||
|
list_definitions.set_defaults(func=definition_list)
|
||||||
|
list_regexes = subcommands_list.add_parser('regexes', help='List Automembership regex rules.')
|
||||||
|
@@ -269,6 +269,8 @@ def create_parser(subparsers):
|
||||||
|
fixup_task.add_argument('-f', '--filter', required=True, help='Sets the LDAP filter for entries to fix up')
|
||||||
|
fixup_task.add_argument('-s', '--scope', required=True, choices=['sub', 'base', 'one'], type=str.lower,
|
||||||
|
help='Sets the LDAP search scope for entries to fix up')
|
||||||
|
+ fixup_task.add_argument('--cleanup', action='store_true',
|
||||||
|
+ help="Clean up previous group memberships before rebuilding")
|
||||||
|
fixup_task.add_argument('--wait', action='store_true',
|
||||||
|
help="Wait for the task to finish, this could take a long time")
|
||||||
|
fixup_task.add_argument('--timeout', default=0, type=int,
|
||||||
|
@@ -279,7 +281,7 @@ def create_parser(subparsers):
|
||||||
|
fixup_status.add_argument('--dn', help="The task entry's DN")
|
||||||
|
fixup_status.add_argument('--show-log', action='store_true', help="Display the task log")
|
||||||
|
fixup_status.add_argument('--watch', action='store_true',
|
||||||
|
- help="Watch the task's status and wait for it to finish")
|
||||||
|
+ help="Watch the task's status and wait for it to finish")
|
||||||
|
|
||||||
|
abort_fixup = subcommands.add_parser('abort-fixup', help='Abort the rebuild membership task.')
|
||||||
|
abort_fixup.set_defaults(func=abort)
|
||||||
|
diff --git a/src/lib389/lib389/plugins.py b/src/lib389/lib389/plugins.py
|
||||||
|
index 52691a44c..a1ad0a45b 100644
|
||||||
|
--- a/src/lib389/lib389/plugins.py
|
||||||
|
+++ b/src/lib389/lib389/plugins.py
|
||||||
|
@@ -1141,13 +1141,15 @@ class AutoMembershipPlugin(Plugin):
|
||||||
|
def __init__(self, instance, dn="cn=Auto Membership Plugin,cn=plugins,cn=config"):
|
||||||
|
super(AutoMembershipPlugin, self).__init__(instance, dn)
|
||||||
|
|
||||||
|
- def fixup(self, basedn, _filter=None):
|
||||||
|
+ def fixup(self, basedn, _filter=None, cleanup=False):
|
||||||
|
"""Create an automember rebuild membership task
|
||||||
|
|
||||||
|
:param basedn: Basedn to fix up
|
||||||
|
:type basedn: str
|
||||||
|
:param _filter: a filter for entries to fix up
|
||||||
|
:type _filter: str
|
||||||
|
+ :param cleanup: cleanup old group memberships
|
||||||
|
+ :type cleanup: boolean
|
||||||
|
|
||||||
|
:returns: an instance of Task(DSLdapObject)
|
||||||
|
"""
|
||||||
|
@@ -1156,6 +1158,9 @@ class AutoMembershipPlugin(Plugin):
|
||||||
|
task_properties = {'basedn': basedn}
|
||||||
|
if _filter is not None:
|
||||||
|
task_properties['filter'] = _filter
|
||||||
|
+ if cleanup:
|
||||||
|
+ task_properties['cleanup'] = "yes"
|
||||||
|
+
|
||||||
|
task.create(properties=task_properties)
|
||||||
|
|
||||||
|
return task
|
||||||
|
diff --git a/src/lib389/lib389/tasks.py b/src/lib389/lib389/tasks.py
|
||||||
|
index 1a16bbb83..193805780 100644
|
||||||
|
--- a/src/lib389/lib389/tasks.py
|
||||||
|
+++ b/src/lib389/lib389/tasks.py
|
||||||
|
@@ -1006,12 +1006,13 @@ class Tasks(object):
|
||||||
|
return exitCode
|
||||||
|
|
||||||
|
def automemberRebuild(self, suffix=DEFAULT_SUFFIX, scope='sub',
|
||||||
|
- filterstr='objectclass=top', args=None):
|
||||||
|
+ filterstr='objectclass=top', cleanup=False, args=None):
|
||||||
|
'''
|
||||||
|
- @param suffix - The suffix the task should examine - defualt is
|
||||||
|
+ @param suffix - The suffix the task should examine - default is
|
||||||
|
"dc=example,dc=com"
|
||||||
|
@param scope - The scope of the search to find entries
|
||||||
|
- @param fitlerstr - THe search filter to find entries
|
||||||
|
+ @param fitlerstr - The search filter to find entries
|
||||||
|
+ @param cleanup - reset/clear the old group mmeberships prior to rebuilding
|
||||||
|
@param args - is a dictionary that contains modifier of the task
|
||||||
|
wait: True/[False] - If True, waits for the completion of
|
||||||
|
the task before to return
|
||||||
|
@@ -1027,6 +1028,8 @@ class Tasks(object):
|
||||||
|
entry.setValues('basedn', suffix)
|
||||||
|
entry.setValues('filter', filterstr)
|
||||||
|
entry.setValues('scope', scope)
|
||||||
|
+ if cleanup:
|
||||||
|
+ entry.setValues('cleanup', 'yes')
|
||||||
|
|
||||||
|
# start the task and possibly wait for task completion
|
||||||
|
try:
|
||||||
|
--
|
||||||
|
2.43.0
|
||||||
|
|
Loading…
Reference in new issue