Compare commits
No commits in common. "c8-stream-1.4" and "a9" have entirely different histories.
c8-stream-
...
a9
|
@ -1,3 +1,2 @@
|
|||
bd9aab32d9cbf9231058d585479813f3420dc872 SOURCES/389-ds-base-1.4.3.39.tar.bz2
|
||||
09d78ce7b3e2f3d5d28c889cabd56720a573ade3 SOURCES/389-ds-base-2.3.6.tar.bz2
|
||||
1c8f2d0dfbf39fa8cd86363bf3314351ab21f8d4 SOURCES/jemalloc-5.3.0.tar.bz2
|
||||
978b7c5e4a9e5784fddb23ba1abe4dc5a071589f SOURCES/vendor-1.4.3.39-1.tar.gz
|
||||
|
|
|
@ -1,3 +1,2 @@
|
|||
SOURCES/389-ds-base-1.4.3.39.tar.bz2
|
||||
SOURCES/389-ds-base-2.3.6.tar.bz2
|
||||
SOURCES/jemalloc-5.3.0.tar.bz2
|
||||
SOURCES/vendor-1.4.3.39-1.tar.gz
|
||||
|
|
|
@ -1,83 +0,0 @@
|
|||
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
|
||||
|
|
@ -0,0 +1,119 @@
|
|||
From d9784b09531b19f6541602a31cfd49c9878ef2ca Mon Sep 17 00:00:00 2001
|
||||
From: Simon Pichugin <spichugi@redhat.com>
|
||||
Date: Thu, 31 Aug 2023 11:19:05 -0700
|
||||
Subject: [PATCH] Issue 5848 - Fix condition and add a CI test (#5916)
|
||||
|
||||
Description: Add a "positive" test for the issue and fix the condition
|
||||
to make sure that 65535 and no --replica-id are correctly accepted.
|
||||
|
||||
Related: https://github.com/389ds/389-ds-base/issues/5848
|
||||
|
||||
Reviewed by: @mreynolds389 @tbordaz (Thanks!)
|
||||
---
|
||||
dirsrvtests/tests/suites/clu/dsconf_test.py | 34 ++++++++++++++++++++-
|
||||
src/lib389/lib389/cli_conf/replication.py | 23 ++++++++------
|
||||
2 files changed, 47 insertions(+), 10 deletions(-)
|
||||
|
||||
diff --git a/dirsrvtests/tests/suites/clu/dsconf_test.py b/dirsrvtests/tests/suites/clu/dsconf_test.py
|
||||
index eb3c426c7..4f7da0b58 100644
|
||||
--- a/dirsrvtests/tests/suites/clu/dsconf_test.py
|
||||
+++ b/dirsrvtests/tests/suites/clu/dsconf_test.py
|
||||
@@ -99,7 +99,7 @@ def test_dsconf_with_ldaps(topology_st, enable_config, config_type):
|
||||
|
||||
|
||||
@pytest.mark.parametrize('instance_role', ('consumer', 'hub'))
|
||||
-def test_check_replica_id_rejected (instance_role):
|
||||
+def test_check_replica_id_rejected_hub_consumer(instance_role):
|
||||
"""Test dsconf CLI does not accept replica-id parameter for comsumer and hubs
|
||||
|
||||
:id: 274b47f8-111a-11ee-8321-98fa9ba19b65
|
||||
@@ -129,3 +129,35 @@ def test_check_replica_id_rejected (instance_role):
|
||||
log.info(f'output message : {msg}')
|
||||
assert "Replication successfully enabled for" not in msg, f"Test Failed: --replica-id option is accepted....It shouldn't for {instance_role}"
|
||||
log.info(f"Test PASSED: --replica-id option is NOT accepted for {instance_role}.")
|
||||
+
|
||||
+
|
||||
+@pytest.mark.parametrize('instance_role, replica_id',
|
||||
+ [('consumer', None), ('hub', None), ('consumer', "65535"), ('hub', "65535")])
|
||||
+def test_check_replica_id_accepted_hub_consumer(topology_st, instance_role, replica_id):
|
||||
+ """Test dsconf CLI accepts 65535 replica-id parameter for comsumer and hubs
|
||||
+
|
||||
+ :id: e0a1a1e6-11c1-40e6-92fe-cb550fb2170d
|
||||
+ :parametrized: yes
|
||||
+ :customerscenario: True
|
||||
+ :setup: Create DS instance
|
||||
+ :steps:
|
||||
+ 1. Create ldap instance
|
||||
+ 2. Use dsconf cli to create replica and don't specify replica id for a consumer or hub
|
||||
+ 3. Use dsconf cli to create replica and specify replica id for a consumer or hub
|
||||
+ :expectedresults:
|
||||
+ 1. Success
|
||||
+ 2. Success
|
||||
+ 3. Success
|
||||
+ """
|
||||
+ print("DN_DM {}".format(DN_DM))
|
||||
+ cmdline = ['/usr/sbin/dsconf', 'standalone1', '-D', DN_DM, '-w', 'password', 'replication', 'enable', '--suffix', DEFAULT_SUFFIX, '--role', instance_role]
|
||||
+ if replica_id is not None:
|
||||
+ cmdline.append(f'--replica-id={replica_id}')
|
||||
+ log.info(f'Command used : {cmdline}')
|
||||
+ proc = subprocess.Popen(cmdline, stdout=subprocess.PIPE)
|
||||
+
|
||||
+ msg = proc.communicate()
|
||||
+ msg = msg[0].decode('utf-8')
|
||||
+ log.info(f'output message : {msg}')
|
||||
+ assert "Replication successfully enabled for" in msg
|
||||
+ log.info(f"Test PASSED: --replica-id option is accepted for {instance_role}.")
|
||||
diff --git a/src/lib389/lib389/cli_conf/replication.py b/src/lib389/lib389/cli_conf/replication.py
|
||||
index a75774ca0..2e2803ced 100644
|
||||
--- a/src/lib389/lib389/cli_conf/replication.py
|
||||
+++ b/src/lib389/lib389/cli_conf/replication.py
|
||||
@@ -154,6 +154,17 @@ def enable_replication(inst, basedn, log, args):
|
||||
# error - unknown type
|
||||
raise ValueError(f"Unknown replication role ({role}), you must use \"supplier\", \"hub\", or \"consumer\"")
|
||||
|
||||
+ if args.replica_id is not None:
|
||||
+ # is it a number?
|
||||
+ try:
|
||||
+ rid_num = int(rid)
|
||||
+ except ValueError:
|
||||
+ raise ValueError("--replica-id expects a number between 1 and 65535")
|
||||
+
|
||||
+ # Is it in range?
|
||||
+ if rid_num < 1 or rid_num > 65535:
|
||||
+ raise ValueError("--replica-id expects a number between 1 and 65535")
|
||||
+
|
||||
# Start the propeties and update them as needed
|
||||
repl_properties = {
|
||||
'cn': 'replica',
|
||||
@@ -170,15 +181,9 @@ def enable_replication(inst, basedn, log, args):
|
||||
# Error, supplier needs a rid TODO
|
||||
raise ValueError('You must specify the replica ID (--replica-id) when enabling a \"supplier\" replica')
|
||||
|
||||
- # is it a number?
|
||||
- try:
|
||||
- rid_num = int(rid)
|
||||
- except ValueError:
|
||||
- raise ValueError("--replica-id expects a number between 1 and 65534")
|
||||
-
|
||||
# Is it in range?
|
||||
if rid_num < 1 or rid_num > 65534:
|
||||
- raise ValueError("--replica-id expects a number between 1 and 65534")
|
||||
+ raise ValueError("--replica-id expects a number between 1 and 65534 for supplier role")
|
||||
|
||||
# rid is good add it to the props
|
||||
repl_properties['nsDS5ReplicaId'] = args.replica_id
|
||||
@@ -186,9 +191,9 @@ def enable_replication(inst, basedn, log, args):
|
||||
# Validate consumer and hub settings
|
||||
elif role == "consumer" or role == "hub":
|
||||
# Check Replica ID
|
||||
- if args.replica_id is not None or args.replica_id != 65535:
|
||||
+ if args.replica_id is not None and rid_num != 65535:
|
||||
# Error, Replica ID cannot be specified for consumer and hub roles
|
||||
- raise ValueError('Replica ID cannot be specified for consumer and hub roles')
|
||||
+ raise ValueError('Replica ID other than 65535 cannot be specified for consumer and hub roles')
|
||||
|
||||
# Bind DN or Bind DN Group?
|
||||
if args.bind_group_dn:
|
||||
--
|
||||
2.41.0
|
||||
|
|
@ -1,119 +0,0 @@
|
|||
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
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
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
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
From b2194c95f9d02965b2ad3d0dcf333e74881347d0 Mon Sep 17 00:00:00 2001
|
||||
From: James Chapman <jachapma@redhat.com>
|
||||
Date: Thu, 31 Aug 2023 14:05:31 +0000
|
||||
Subject: [PATCH] Issue 5909 - Multi listener hang with 20k connections (#5917)
|
||||
|
||||
Bug Description: A fix for connection sub-table to freelist mapping results
|
||||
in an uninitialised head of the sub-table linked list.
|
||||
|
||||
Fix Description: During connection table creation, initialise all elements but
|
||||
skip the list head during the mapping phase.
|
||||
|
||||
Fixes: https//github.com/389ds/389-ds-base/issues/5909
|
||||
|
||||
Reviewed by: @progier389 @tbordaz (Thank you)
|
||||
---
|
||||
ldap/servers/slapd/conntable.c | 10 ++++++----
|
||||
1 file changed, 6 insertions(+), 4 deletions(-)
|
||||
|
||||
diff --git a/ldap/servers/slapd/conntable.c b/ldap/servers/slapd/conntable.c
|
||||
index b1c66cf421..11e997432b 100644
|
||||
--- a/ldap/servers/slapd/conntable.c
|
||||
+++ b/ldap/servers/slapd/conntable.c
|
||||
@@ -172,9 +172,9 @@ connection_table_new(int table_size)
|
||||
/* all connections start out invalid */
|
||||
ct->fd[ct_list][i].fd = SLAPD_INVALID_SOCKET;
|
||||
|
||||
- /* The connection table has a double linked list running through it.
|
||||
+ /* The connection sub-tables have a double linked list running through them.
|
||||
* This is used to find out which connections should be looked at
|
||||
- * in the poll loop. Slot 0 in the table is always the head of
|
||||
+ * in the poll loop. Slot 0 in each sub-table is always the head of
|
||||
* the linked list. Each slot has a c_next and c_prev which are
|
||||
* pointers back into the array of connection slots. */
|
||||
ct->c[ct_list][i].c_next = NULL;
|
||||
@@ -196,8 +196,10 @@ connection_table_new(int table_size)
|
||||
/* Ready to rock, mark as such. */
|
||||
ct->c[ct_list][i].c_state = CONN_STATE_INIT;
|
||||
|
||||
- /* Map multiple ct lists to a single freelist. */
|
||||
- ct->c_freelist[free_idx++] = &(ct->c[ct_list][i]);
|
||||
+ /* Map multiple ct lists to a single freelist, but skip slot 0 of each list. */
|
||||
+ if (i != 0) {
|
||||
+ ct->c_freelist[free_idx++] = &(ct->c[ct_list][i]);
|
||||
+ }
|
||||
}
|
||||
}
|
||||
|
|
@ -1,147 +0,0 @@
|
|||
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,626 @@
|
|||
From 26716f0edefe4690d9fd9143d07156e01321020c Mon Sep 17 00:00:00 2001
|
||||
From: progier389 <progier@redhat.com>
|
||||
Date: Thu, 28 Sep 2023 12:15:25 +0200
|
||||
Subject: [PATCH] issue 5924 - ASAN server build crash when looping
|
||||
opening/closing connections (#5926)
|
||||
|
||||
* issue 5924 - ASAN server build crash when looping opening/closing connections
|
||||
Issue: Got a crash due to:
|
||||
1. Failure to get a connection slot because connection freelist is misshandled.
|
||||
2. A confusion between listening and acceptedfd descriptor leaded to
|
||||
close the listening descriptor while handing the error.
|
||||
|
||||
Solution:
|
||||
Rename clearly the file descriptor variables
|
||||
Close the accepted file descriptor in error handler
|
||||
Rewrite the freelist management so that first connection chosen is the last released one.
|
||||
(Code is simpler, this fix the end of list issue, and it avoid to spread the open connection over too much memory)
|
||||
|
||||
Issue: #5924
|
||||
|
||||
Reviewed by: @Firstyear, @vashirov, @droideck (Thanks !)
|
||||
|
||||
(cherry picked from commit 02d333251419ff3c4d0384595e9fe7ded5bcd8fc)
|
||||
---
|
||||
dirsrvtests/tests/suites/basic/basic_test.py | 287 +++++++++++++++++++
|
||||
ldap/servers/slapd/conntable.c | 100 +++----
|
||||
ldap/servers/slapd/daemon.c | 35 ++-
|
||||
ldap/servers/slapd/fe.h | 1 -
|
||||
4 files changed, 351 insertions(+), 72 deletions(-)
|
||||
|
||||
diff --git a/dirsrvtests/tests/suites/basic/basic_test.py b/dirsrvtests/tests/suites/basic/basic_test.py
|
||||
index fac0f7371e..f4525e1848 100644
|
||||
--- a/dirsrvtests/tests/suites/basic/basic_test.py
|
||||
+++ b/dirsrvtests/tests/suites/basic/basic_test.py
|
||||
@@ -28,7 +28,15 @@
|
||||
from lib389.backend import Backends
|
||||
from lib389.idm.domain import Domain
|
||||
from lib389.nss_ssl import NssSsl
|
||||
+from lib389._constants import *
|
||||
+from lib389 import DirSrv
|
||||
+from lib389.instance.setup import SetupDs
|
||||
+from lib389.instance.options import General2Base, Slapd2Base
|
||||
import os
|
||||
+import random
|
||||
+import ldap
|
||||
+import time
|
||||
+import subprocess
|
||||
|
||||
|
||||
pytestmark = pytest.mark.tier0
|
||||
@@ -36,6 +44,7 @@
|
||||
default_paths = Paths()
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
+DEBUGGING = os.getenv("DEBUGGING", default=False)
|
||||
|
||||
# Globals
|
||||
USER1_DN = 'uid=user1,' + DEFAULT_SUFFIX
|
||||
@@ -51,6 +60,190 @@
|
||||
'vendorName',
|
||||
'vendorVersion')
|
||||
|
||||
+# This MAX_FDS value left about 22 connections available with bdb
|
||||
+# (should have more connections with lmdb)
|
||||
+MAX_FDS = 150
|
||||
+
|
||||
+
|
||||
+
|
||||
+default_paths = Paths()
|
||||
+
|
||||
+
|
||||
+
|
||||
+log = logging.getLogger(__name__)
|
||||
+DEBUGGING = os.getenv("DEBUGGING", default=False)
|
||||
+
|
||||
+
|
||||
+class CustomSetup():
|
||||
+ DEFAULT_GENERAL = { 'config_version': 2,
|
||||
+ 'full_machine_name': 'localhost.localdomain',
|
||||
+ 'strict_host_checking': False,
|
||||
+ # Not setting 'systemd' because it is not used.
|
||||
+ # (that is the global default.inf setting that matters)
|
||||
+ }
|
||||
+ DEFAULT_SLAPD = { 'root_password': PW_DM,
|
||||
+ 'defaults': INSTALL_LATEST_CONFIG,
|
||||
+ }
|
||||
+ DEFAULT_BACKENDS = [ {
|
||||
+ 'cn': 'userroot',
|
||||
+ 'nsslapd-suffix': DEFAULT_SUFFIX,
|
||||
+ 'sample_entries': 'yes',
|
||||
+ BACKEND_SAMPLE_ENTRIES: INSTALL_LATEST_CONFIG,
|
||||
+ }, ]
|
||||
+
|
||||
+ WRAPPER_FORMAT = '''#!/bin/sh
|
||||
+{wrapper_options}
|
||||
+exec {nsslapd} -D {cfgdir} -i {pidfile}
|
||||
+'''
|
||||
+
|
||||
+
|
||||
+ class CustomDirSrv(DirSrv):
|
||||
+ def __init__(self, verbose=False, external_log=log):
|
||||
+ super().__init__(verbose=verbose, external_log=external_log)
|
||||
+ self.wrapper = None # placeholder for the wrapper file name
|
||||
+
|
||||
+ def _reset_systemd(self):
|
||||
+ self.systemd_override = False
|
||||
+
|
||||
+ def status(self):
|
||||
+ self._reset_systemd()
|
||||
+ return super().status()
|
||||
+
|
||||
+ def start(self, timeout=120, *args):
|
||||
+ if self.status():
|
||||
+ return
|
||||
+ tmp_env = os.environ
|
||||
+ # Unset PYTHONPATH to avoid mixing old CLI tools and new lib389
|
||||
+ if "PYTHONPATH" in tmp_env:
|
||||
+ del tmp_env["PYTHONPATH"]
|
||||
+ try:
|
||||
+ subprocess.check_call([
|
||||
+ '/usr/bin/sh',
|
||||
+ self.wrapper
|
||||
+ ], env=tmp_env, stderr=subprocess.STDOUT)
|
||||
+ except subprocess.CalledProcessError as e:
|
||||
+ log.fatal("%s failed! Error (%s) %s" % (self.wrapper, e.returncode, e.output))
|
||||
+ raise e from None
|
||||
+ for count in range(timeout):
|
||||
+ if self.status():
|
||||
+ return
|
||||
+ time.sleep(1)
|
||||
+ raise TimeoutException('Failed to start ns-slpad')
|
||||
+
|
||||
+ def stop(self, timeout=120):
|
||||
+ self._reset_systemd()
|
||||
+ super().stop(timeout=timeout)
|
||||
+
|
||||
+
|
||||
+ def _search_be(belist, beinfo):
|
||||
+ for be in belist:
|
||||
+ if be['cn'] == beinfo['cn']:
|
||||
+ return be
|
||||
+ return None
|
||||
+
|
||||
+ def __init__(self, serverid, general=None, slapd=None, backends=None, log=log):
|
||||
+ verbose = log.level > logging.DEBUG
|
||||
+ self.log = log
|
||||
+ self.serverid = serverid
|
||||
+ self.verbose = verbose
|
||||
+ self.wrapper = f'/tmp/ds_{serverid}_wrapper.sh'
|
||||
+ if serverid.startswith('slapd-'):
|
||||
+ self.instname = server.id
|
||||
+ else:
|
||||
+ self.instname = 'slapd-'+serverid
|
||||
+ self.ldapi = None
|
||||
+ self.pid_file = None
|
||||
+ self.inst = None
|
||||
+
|
||||
+ # Lets prepare the options
|
||||
+ general_options = General2Base(log)
|
||||
+ for d in (CustomSetup.DEFAULT_GENERAL, general):
|
||||
+ if d:
|
||||
+ for key,val in d.items():
|
||||
+ general_options.set(key,val)
|
||||
+ log.debug('[general]: %s' % general_options._options)
|
||||
+ self.general = general_options
|
||||
+
|
||||
+ slapd_options = Slapd2Base(self.log)
|
||||
+ slapd_options.set('instance_name', serverid)
|
||||
+ for d in (CustomSetup.DEFAULT_SLAPD, slapd):
|
||||
+ if d:
|
||||
+ for key,val in d.items():
|
||||
+ slapd_options.set(key,val)
|
||||
+ log.debug('[slapd]: %s' % slapd_options._options)
|
||||
+ self.slapd = slapd_options
|
||||
+
|
||||
+ backend_options = []
|
||||
+ for backend_list in (CustomSetup.DEFAULT_BACKENDS, backends):
|
||||
+ if not backend_list:
|
||||
+ continue
|
||||
+ for backend in backend_list:
|
||||
+ target_be = CustomSetup._search_be(backend_options, backend)
|
||||
+ if not target_be:
|
||||
+ target_be = {}
|
||||
+ backend_options.append(target_be)
|
||||
+ for key,val in backend.items():
|
||||
+ target_be[key] = val
|
||||
+ log.debug('[backends]: %s' % backend_options)
|
||||
+ self.backends = backend_options
|
||||
+
|
||||
+ def _to_dirsrv_args(self):
|
||||
+ args = {}
|
||||
+ slapd = self.slapd.collect()
|
||||
+ general = self.general.collect()
|
||||
+ args["SER_HOST"] = general['full_machine_name']
|
||||
+ args["SER_PORT"] = slapd['port']
|
||||
+ args["SER_SECURE_PORT"] = slapd['secure_port']
|
||||
+ args["SER_SERVERID_PROP"] = self.serverid
|
||||
+ return args
|
||||
+
|
||||
+ def create_instance(self):
|
||||
+ sds = SetupDs(verbose=self.verbose, dryrun=False, log=self.log)
|
||||
+ self.general.verify()
|
||||
+ general = self.general.collect()
|
||||
+ self.slapd.verify()
|
||||
+ slapd = self.slapd.collect()
|
||||
+ sds.create_from_args(general, slapd, self.backends, None)
|
||||
+ self.ldapi = get_ldapurl_from_serverid(self.serverid)[0]
|
||||
+ args = self._to_dirsrv_args()
|
||||
+ log.debug('DirSrv.allocate args = %s' % str(args))
|
||||
+ log.debug('ldapi = %s' % str(self.ldapi))
|
||||
+ root_dn = slapd['root_dn']
|
||||
+ root_password = slapd['root_password']
|
||||
+ inst = DirSrv(verbose=self.verbose, external_log=self.log)
|
||||
+ inst.local_simple_allocate(self.serverid, ldapuri=self.ldapi, binddn=root_dn, password=root_password)
|
||||
+ self.pid_file = inst.pid_file()
|
||||
+ # inst.setup_ldapi()
|
||||
+ log.debug('DirSrv = %s' % str(inst.__dict__))
|
||||
+ inst.open()
|
||||
+ inst.stop()
|
||||
+ inst = CustomSetup.CustomDirSrv(verbose=self.verbose, external_log=self.log)
|
||||
+ inst.local_simple_allocate(self.serverid, ldapuri=self.ldapi, binddn=root_dn, password=root_password)
|
||||
+ self.inst = inst
|
||||
+ return inst
|
||||
+
|
||||
+ def create_wrapper(self, maxfds=None):
|
||||
+ self.inst.wrapper = self.wrapper
|
||||
+ slapd = self.slapd.collect()
|
||||
+ sbin_dir = slapd['sbin_dir']
|
||||
+ config_dir = slapd['config_dir']
|
||||
+ fmtvalues = {
|
||||
+ 'nsslapd': f'{sbin_dir}/ns-slapd',
|
||||
+ 'cfgdir': config_dir.format(instance_name=self.instname),
|
||||
+ 'pidfile': self.pid_file,
|
||||
+ 'wrapper_options': ''
|
||||
+ }
|
||||
+ if maxfds:
|
||||
+ fmtvalues['wrapper_options']=f'ulimit -n {maxfds}\nulimit -H -n {maxfds}'
|
||||
+ with open(self.wrapper, 'w') as f:
|
||||
+ f.write(CustomSetup.WRAPPER_FORMAT.format(**fmtvalues))
|
||||
+
|
||||
+ def cleanup(self):
|
||||
+ self.inst.stop()
|
||||
+ self.inst.delete()
|
||||
+ if os.path.exists(self.wrapper):
|
||||
+ os.remove(self.wrapper)
|
||||
+
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
def _reset_attr(request, topology_st):
|
||||
@@ -2222,6 +2415,100 @@ def test_dscreate_with_different_rdn(dscreate_test_rdn_value):
|
||||
assert True
|
||||
|
||||
|
||||
+@pytest.fixture(scope="module")
|
||||
+def dscreate_custom_instance(request):
|
||||
+ topo = CustomSetup('custom')
|
||||
+
|
||||
+ def fin():
|
||||
+ topo.cleanup()
|
||||
+
|
||||
+ request.addfinalizer(fin)
|
||||
+ topo.create_instance()
|
||||
+ # Return CustomSetup object associated with
|
||||
+ # a stopped instance named "custom"
|
||||
+ return topo
|
||||
+
|
||||
+ obj.create_wrapper(maxfds=150)
|
||||
+ log.info("Starting wrapper")
|
||||
+ inst.start()
|
||||
+ log.info("Server is started.")
|
||||
+ log.info("Open connection")
|
||||
+ inst.open()
|
||||
+
|
||||
+
|
||||
+@pytest.fixture(scope="module", params=set(range(1,5)))
|
||||
+def dscreate_with_numlistener(request, dscreate_custom_instance):
|
||||
+ numlisteners = request.param
|
||||
+ dscreate_custom_instance.create_wrapper(maxfds=MAX_FDS)
|
||||
+ inst = dscreate_custom_instance.inst
|
||||
+ inst.stop()
|
||||
+ dse_ldif = DSEldif(inst)
|
||||
+ dse_ldif.replace('cn=config', 'nsslapd-numlisteners', str(numlisteners))
|
||||
+ inst.start()
|
||||
+ inst.open()
|
||||
+ return inst
|
||||
+
|
||||
+
|
||||
+@pytest.mark.skipif(ds_is_older('2.2.0.0'),
|
||||
+ reason="This test is only required with multiple listener support.")
|
||||
+def test_conn_limits(dscreate_with_numlistener):
|
||||
+ """Check the connections limits for various number of listeners.
|
||||
+
|
||||
+ :id: 7be2eb5c-4d8f-11ee-ae3d-482ae39447e5
|
||||
+ :parametrized: yes
|
||||
+ :setup: Setup an instance then set nsslapd-numlisteners and maximum file descriptors
|
||||
+ :steps:
|
||||
+ 1. Loops on:
|
||||
+ Open new connection and perform search until timeout expires
|
||||
+ 2. Close one of the previously open connections
|
||||
+ 3. Loops MAX_FDS times on:
|
||||
+ - opening a new connection
|
||||
+ - perform a search
|
||||
+ - close the connection
|
||||
+ 4. Close all open connections
|
||||
+ 5. Remove the instance
|
||||
+ :expectedresults:
|
||||
+ 1. Should get a timeout (because the server has no more any connections)
|
||||
+ 2. Should success
|
||||
+ 3. Should success (otherwise issue #5924 has probably been hit)
|
||||
+ 4. Should success
|
||||
+ 5. Should success
|
||||
+ """
|
||||
+ inst = dscreate_with_numlistener
|
||||
+
|
||||
+ conns = []
|
||||
+ timeout_occured = False
|
||||
+ for i in range(MAX_FDS):
|
||||
+ try:
|
||||
+ ldc = ldap.initialize(f'ldap://localhost:{inst.port}')
|
||||
+ ldc.set_option(ldap.OPT_TIMEOUT, 5)
|
||||
+ ldc.search_s(DEFAULT_SUFFIX, ldap.SCOPE_SUBTREE, "(uid=demo)")
|
||||
+ conns.append(ldc)
|
||||
+ except ldap.TIMEOUT:
|
||||
+ timeout_occured = True
|
||||
+ break
|
||||
+ # Should not be able to open MAX_FDS connections (some file descriptor are
|
||||
+ # reserved (for example for the listening socket )
|
||||
+ assert timeout_occured
|
||||
+
|
||||
+ conn = random.choice(conns)
|
||||
+ conn.unbind()
|
||||
+ conns.remove(conn)
|
||||
+
|
||||
+ # Should loop enough time so trigger issue #5924 if it is not fixed.
|
||||
+ for i in range(MAX_FDS):
|
||||
+ ldc = ldap.initialize(f'ldap://localhost:{inst.port}')
|
||||
+ # Set a timeout long enough so that the test fails if server is unresponsive
|
||||
+ ldc.set_option(ldap.OPT_TIMEOUT, 60)
|
||||
+ ldc.search_s(DEFAULT_SUFFIX, ldap.SCOPE_SUBTREE, "(uid=demo)")
|
||||
+ ldc.unbind()
|
||||
+
|
||||
+ # Close all open connections
|
||||
+ for c in conns:
|
||||
+ c.unbind()
|
||||
+
|
||||
+ # Step 6 is done in teardown phase by dscreate_instance finalizer
|
||||
+
|
||||
if __name__ == '__main__':
|
||||
# Run isolated
|
||||
# -s for DEBUG mode
|
||||
diff --git a/ldap/servers/slapd/conntable.c b/ldap/servers/slapd/conntable.c
|
||||
index 11e997432b..1ca60e0e5f 100644
|
||||
--- a/ldap/servers/slapd/conntable.c
|
||||
+++ b/ldap/servers/slapd/conntable.c
|
||||
@@ -48,69 +48,59 @@
|
||||
* under the connection table lock. This should move the allocation algorithm from O(n) worst case
|
||||
* to O(1) worst case as we always recieve and empty slot *or* ct full. It also reduces lock/atomic
|
||||
* contention on the CPU to improve things.
|
||||
+ * Lastly a change was done: Instead of having a sliding windows that tend to get a never used
|
||||
+ * slot for each new connection, it nows reuse last freed one. That has several benefits:
|
||||
+ * - Fix a bug because the last free list slots may not be alloced.
|
||||
+ * - Avoid to grow the memory footprint when there is no much load
|
||||
+ * - Simplify the code (as only a single is needed. )
|
||||
*
|
||||
- * The freelist is a ringbuffer of pointers to the connection table. On a small scale it looks like:
|
||||
+ * The freelist is a stack of pointers to the connection table.
|
||||
+ * It is NULL terminated. On a small scale it looks like:
|
||||
*
|
||||
* |--------------------------------------------|
|
||||
- * | slot 1 | slot 2 | slot 3 | slot 4 | slot 5 |
|
||||
- * | _ ptr | _ ptr | _ ptr | _ ptr | _ ptr |
|
||||
+ * | slot 0 | slot 1 | slot 2 | slot 3 | slot 4 |
|
||||
+ * | _ ptr | _ ptr | _ ptr | _ ptr | NULL |
|
||||
* |--------------------------------------------|
|
||||
- * ^ ^- conn_next
|
||||
- * |
|
||||
- * \-- conn_free
|
||||
+ * ^- conn_next
|
||||
*
|
||||
- * As we allocate, we shift conn_next through the list, yielding the ptr that was stored (and
|
||||
- * setting it to NULL as we proceed)
|
||||
+ * To allocate, we pop one of the stored connection ptr out of the stack (yield the ptr, set
|
||||
+ * its slot to NULL then increase conn_next)
|
||||
*
|
||||
* |--------------------------------------------|
|
||||
- * | slot 1 | slot 2 | slot 3 | slot 4 | slot 5 |
|
||||
- * | _NULL | _NULL | _ ptr | _ ptr | _ ptr |
|
||||
+ * | slot 0 | slot 1 | slot 2 | slot 3 | slot 4 |
|
||||
+ * | _NULL | _NULL | _ ptr | _ ptr | NULL |
|
||||
* |--------------------------------------------|
|
||||
- * ^ ^- conn_next
|
||||
- * |
|
||||
- * \-- conn_free
|
||||
+ * ^- conn_next
|
||||
*
|
||||
- * When a connection is "freed" we return it to conn_free, which is then also slid up.
|
||||
+ * When a connection is "freed" we push it back in the stack after decreasing conn_next
|
||||
*
|
||||
* |--------------------------------------------|
|
||||
- * | slot 1 | slot 2 | slot 3 | slot 4 | slot 5 |
|
||||
- * | _ ptr | _NULL | _ ptr | _ ptr | _ ptr |
|
||||
+ * | slot 0 | slot 1 | slot 2 | slot 3 | slot 4 |
|
||||
+ * | _NULL | _ ptr | _ ptr | _ ptr | NULL |
|
||||
* |--------------------------------------------|
|
||||
- * ^ ^- conn_next
|
||||
- * |
|
||||
- * \-- conn_free
|
||||
+ * ^- conn_next
|
||||
*
|
||||
- * If all connections are exhausted, conn_next will == conn_next, as conn_next must have proceeded
|
||||
- * to the end of the ring, and then wrapped back allocating all previous until we meet with conn_free.
|
||||
+ * If all connections are exhausted, freelist[conn_next] is NULL
|
||||
*
|
||||
* |--------------------------------------------|
|
||||
- * | slot 1 | slot 2 | slot 3 | slot 4 | slot 5 |
|
||||
+ * | slot 0 | slot 1 | slot 2 | slot 3 | slot 4 |
|
||||
* | _NULL | _NULL | _NULL | _NULL | _NULL |
|
||||
* |--------------------------------------------|
|
||||
- * ^ ^- conn_next
|
||||
- * |
|
||||
- * \-- conn_free
|
||||
+ * ^- conn_next
|
||||
*
|
||||
* This means allocations of conns will keep failing until a connection is returned.
|
||||
*
|
||||
* |--------------------------------------------|
|
||||
- * | slot 1 | slot 2 | slot 3 | slot 4 | slot 5 |
|
||||
- * | _NULL | _ ptr | _NULL | _NULL | _NULL |
|
||||
+ * | slot 0 | slot 1 | slot 2 | slot 3 | slot 4 |
|
||||
+ * | _NULL | _NULL | _NULL | _ ptr | NULL |
|
||||
* |--------------------------------------------|
|
||||
- * ^- conn_next ^
|
||||
- * |
|
||||
- * \-- conn_free
|
||||
+ * ^- conn_next
|
||||
*
|
||||
* And now conn_next can begin to allocate again.
|
||||
*
|
||||
*
|
||||
* -- invariants
|
||||
- * * when conn_free is slid back to meet conn_next, there can be no situation where another
|
||||
- * connection is returned, as none must allocated -if they were allocated, conn_free would have
|
||||
- * moved_along.
|
||||
- * * the ring buffer must be as large as conntable.
|
||||
- * * We do not check conn_next == conn_free (that's the starting state), but we check if the
|
||||
- * slot at conn_next is NULL, which must imply that conn_free has nothing to return.
|
||||
+ * * the stack must be as large as conntable.
|
||||
* * connection_table_move_connection_out_of_active_list is the only function able to return a
|
||||
* connection to the freelist, as it is the function that is called when the event system has
|
||||
* determined all IO's are complete, or unable to complete. This function is what prepares the
|
||||
@@ -136,11 +126,9 @@ connection_table_new(int table_size)
|
||||
ct->c = (Connection **)slapi_ch_calloc(1, ct->size * sizeof(Connection *));
|
||||
ct->fd = (struct POLL_STRUCT **)slapi_ch_calloc(1, ct->list_num * sizeof(struct POLL_STRUCT*));
|
||||
ct->table_mutex = PR_NewLock();
|
||||
- /* Allocate the freelist */
|
||||
- ct->c_freelist = (Connection **)slapi_ch_calloc(1, ct->size * sizeof(Connection *));
|
||||
- /* NEVER use slot 0, this is a list pointer */
|
||||
- ct->conn_next_offset = 1;
|
||||
- ct->conn_free_offset = 1;
|
||||
+ /* Allocate the freelist (a slot for each connection plus another slot for the final NULL pointer) */
|
||||
+ ct->c_freelist = (Connection **)slapi_ch_calloc(1, (ct->size+1) * sizeof(Connection *));
|
||||
+ ct->conn_next_offset = 0;
|
||||
|
||||
slapi_log_err(SLAPI_LOG_INFO, "connection_table_new", "Number of connection sub-tables %d, each containing %d slots.\n",
|
||||
ct->list_num, ct->list_size);
|
||||
@@ -273,22 +261,22 @@ connection_table_get_connection(Connection_Table *ct, int sd)
|
||||
{
|
||||
PR_Lock(ct->table_mutex);
|
||||
|
||||
- PR_ASSERT(ct->conn_next_offset != 0);
|
||||
Connection *c = ct->c_freelist[ct->conn_next_offset];
|
||||
if (c != NULL) {
|
||||
/* We allocated it, so now NULL the slot and move forward. */
|
||||
- ct->c_freelist[ct->conn_next_offset] = NULL;
|
||||
- /* Handle overflow. */
|
||||
- ct->conn_next_offset = (ct->conn_next_offset + 1) % ct->size;
|
||||
- if (ct->conn_next_offset == 0) {
|
||||
- /* Never use slot 0 */
|
||||
- ct->conn_next_offset += 1;
|
||||
- }
|
||||
+ PR_ASSERT(ct->conn_next_offset>=0 && ct->conn_next_offset<ct->size);
|
||||
+ ct->c_freelist[ct->conn_next_offset++] = NULL;
|
||||
PR_Unlock(ct->table_mutex);
|
||||
} else {
|
||||
/* couldn't find a Connection, table must be full */
|
||||
- slapi_log_err(SLAPI_LOG_CONNS, "connection_table_get_connection", "Max open connections reached\n");
|
||||
PR_Unlock(ct->table_mutex);
|
||||
+ static time_t last_err_msg_time = 0;
|
||||
+ time_t curtime = slapi_current_utc_time();
|
||||
+ /* Logs the message only once per seconds */
|
||||
+ if (curtime != last_err_msg_time) {
|
||||
+ slapi_log_err(SLAPI_LOG_ERR, "connection_table_get_connection", "Max open connections reached\n");
|
||||
+ last_err_msg_time = curtime;
|
||||
+ }
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -461,14 +449,10 @@ connection_table_move_connection_out_of_active_list(Connection_Table *ct, Connec
|
||||
|
||||
/* Finally, place the connection back into the freelist for use */
|
||||
PR_ASSERT(c->c_refcnt == 0);
|
||||
- PR_ASSERT(ct->conn_free_offset != 0);
|
||||
- PR_ASSERT(ct->c_freelist[ct->conn_free_offset] == NULL);
|
||||
- ct->c_freelist[ct->conn_free_offset] = c;
|
||||
- ct->conn_free_offset = (ct->conn_free_offset + 1) % ct->size;
|
||||
- if (ct->conn_free_offset == 0) {
|
||||
- /* Never use slot 0 */
|
||||
- ct->conn_free_offset += 1;
|
||||
- }
|
||||
+ PR_ASSERT(ct->conn_next_offset != 0);
|
||||
+ ct->conn_next_offset--;
|
||||
+ PR_ASSERT(ct->c_freelist[ct->conn_next_offset] == NULL);
|
||||
+ ct->c_freelist[ct->conn_next_offset] = c;
|
||||
|
||||
PR_Unlock(ct->table_mutex);
|
||||
|
||||
diff --git a/ldap/servers/slapd/daemon.c b/ldap/servers/slapd/daemon.c
|
||||
index e8b979acaf..10aabed6d3 100644
|
||||
--- a/ldap/servers/slapd/daemon.c
|
||||
+++ b/ldap/servers/slapd/daemon.c
|
||||
@@ -135,19 +135,25 @@ get_pid_file(void)
|
||||
}
|
||||
|
||||
static int
|
||||
-accept_and_configure(int s __attribute__((unused)), PRFileDesc *pr_acceptfd, PRNetAddr *pr_netaddr, int addrlen __attribute__((unused)), int secure, int local, PRFileDesc **pr_clonefd)
|
||||
+accept_and_configure(int s __attribute__((unused)), PRFileDesc *listenfd, PRNetAddr *pr_netaddr, int addrlen __attribute__((unused)), int secure, int local, PRFileDesc **pr_accepted_fd)
|
||||
{
|
||||
int ns = 0;
|
||||
PRIntervalTime pr_timeout = PR_MillisecondsToInterval(slapd_accept_wakeup_timer);
|
||||
|
||||
- (*pr_clonefd) = PR_Accept(pr_acceptfd, pr_netaddr, pr_timeout);
|
||||
- if (!(*pr_clonefd)) {
|
||||
+ (*pr_accepted_fd) = PR_Accept(listenfd, pr_netaddr, pr_timeout);
|
||||
+ if (!(*pr_accepted_fd)) {
|
||||
PRErrorCode prerr = PR_GetError();
|
||||
- slapi_log_err(SLAPI_LOG_ERR, "accept_and_configure", "PR_Accept() failed, " SLAPI_COMPONENT_NAME_NSPR " error %d (%s)\n",
|
||||
- prerr, slapd_pr_strerror(prerr));
|
||||
+ static time_t last_err_msg_time = 0;
|
||||
+ time_t curtime = slapi_current_utc_time();
|
||||
+ /* Logs the message only once per seconds */
|
||||
+ if (curtime != last_err_msg_time) {
|
||||
+ slapi_log_err(SLAPI_LOG_ERR, "accept_and_configure", "PR_Accept() failed, " SLAPI_COMPONENT_NAME_NSPR " error %d (%s)\n",
|
||||
+ prerr, slapd_pr_strerror(prerr));
|
||||
+ last_err_msg_time = curtime;
|
||||
+ }
|
||||
return (SLAPD_INVALID_SOCKET);
|
||||
}
|
||||
- ns = configure_pr_socket(pr_clonefd, secure, local);
|
||||
+ ns = configure_pr_socket(pr_accepted_fd, secure, local);
|
||||
|
||||
return ns;
|
||||
}
|
||||
@@ -155,7 +161,7 @@ accept_and_configure(int s __attribute__((unused)), PRFileDesc *pr_acceptfd, PRN
|
||||
/*
|
||||
* This is the shiny new re-born daemon function, without all the hair
|
||||
*/
|
||||
-static int handle_new_connection(Connection_Table *ct, int tcps, PRFileDesc *pr_acceptfd, int secure, int local, Connection **newconn);
|
||||
+static int handle_new_connection(Connection_Table *ct, int tcps, PRFileDesc *listenfd, int secure, int local, Connection **newconn);
|
||||
static void handle_pr_read_ready(Connection_Table *ct, int list_id, PRIntn num_poll);
|
||||
static int clear_signal(struct POLL_STRUCT *fds, int list_id);
|
||||
static void unfurl_banners(Connection_Table *ct, daemon_ports_t *ports, PRFileDesc **n_tcps, PRFileDesc **s_tcps, PRFileDesc **i_unix);
|
||||
@@ -831,6 +837,7 @@ accept_thread(void *vports)
|
||||
}
|
||||
/* Need a sleep delay here. */
|
||||
PR_Sleep(pr_timeout);
|
||||
+ last_accept_new_connections = accept_new_connections;
|
||||
continue;
|
||||
} else {
|
||||
/* Log that we are now listening again */
|
||||
@@ -1846,28 +1853,30 @@ handle_closed_connection(Connection *conn)
|
||||
* this function returns the connection table list the new connection is in
|
||||
*/
|
||||
static int
|
||||
-handle_new_connection(Connection_Table *ct, int tcps, PRFileDesc *pr_acceptfd, int secure, int local, Connection **newconn)
|
||||
+handle_new_connection(Connection_Table *ct, int tcps, PRFileDesc *listenfd, int secure, int local, Connection **newconn)
|
||||
{
|
||||
int ns = 0;
|
||||
Connection *conn = NULL;
|
||||
/* struct sockaddr_in from;*/
|
||||
PRNetAddr from = {{0}};
|
||||
- PRFileDesc *pr_clonefd = NULL;
|
||||
+ PRFileDesc *pr_accepted_fd = NULL;
|
||||
slapdFrontendConfig_t *fecfg = getFrontendConfig();
|
||||
ber_len_t maxbersize;
|
||||
|
||||
if (newconn) {
|
||||
*newconn = NULL;
|
||||
}
|
||||
- if ((ns = accept_and_configure(tcps, pr_acceptfd, &from,
|
||||
- sizeof(from), secure, local, &pr_clonefd)) == SLAPD_INVALID_SOCKET) {
|
||||
+ if ((ns = accept_and_configure(tcps, listenfd, &from,
|
||||
+ sizeof(from), secure, local, &pr_accepted_fd)) == SLAPD_INVALID_SOCKET) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* get a new Connection from the Connection Table */
|
||||
conn = connection_table_get_connection(ct, ns);
|
||||
if (conn == NULL) {
|
||||
- PR_Close(pr_acceptfd);
|
||||
+ if (pr_accepted_fd) {
|
||||
+ PR_Close(pr_accepted_fd);
|
||||
+ }
|
||||
return -1;
|
||||
}
|
||||
pthread_mutex_lock(&(conn->c_mutex));
|
||||
@@ -1879,7 +1888,7 @@ handle_new_connection(Connection_Table *ct, int tcps, PRFileDesc *pr_acceptfd, i
|
||||
conn->c_idletimeout = fecfg->idletimeout;
|
||||
conn->c_idletimeout_handle = idletimeout_reslimit_handle;
|
||||
conn->c_sd = ns;
|
||||
- conn->c_prfd = pr_clonefd;
|
||||
+ conn->c_prfd = pr_accepted_fd;
|
||||
conn->c_flags &= ~CONN_FLAG_CLOSING;
|
||||
|
||||
/* Set per connection static config */
|
||||
diff --git a/ldap/servers/slapd/fe.h b/ldap/servers/slapd/fe.h
|
||||
index 64c6645e7b..95dfeeb89b 100644
|
||||
--- a/ldap/servers/slapd/fe.h
|
||||
+++ b/ldap/servers/slapd/fe.h
|
||||
@@ -88,7 +88,6 @@ struct connection_table
|
||||
/* An array of free connections awaiting allocation. */;
|
||||
Connection **c_freelist;
|
||||
size_t conn_next_offset;
|
||||
- size_t conn_free_offset;
|
||||
struct POLL_STRUCT **fd;
|
||||
PRLock *table_mutex;
|
||||
};
|
|
@ -1,840 +0,0 @@
|
|||
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
|
||||
|
|
@ -0,0 +1,385 @@
|
|||
From dc091325814d9ac74d238c7e14cac8b9112b3271 Mon Sep 17 00:00:00 2001
|
||||
From: progier389 <progier@redhat.com>
|
||||
Date: Fri, 17 Nov 2023 14:41:51 +0100
|
||||
Subject: [PATCH] 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 | 107 ++++++++++++++++--
|
||||
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, 150 insertions(+), 21 deletions(-)
|
||||
|
||||
diff --git a/dirsrvtests/tests/suites/paged_results/paged_results_test.py b/dirsrvtests/tests/suites/paged_results/paged_results_test.py
|
||||
index d490c4af20..cdafa834ab 100644
|
||||
--- a/dirsrvtests/tests/suites/paged_results/paged_results_test.py
|
||||
+++ b/dirsrvtests/tests/suites/paged_results/paged_results_test.py
|
||||
@@ -7,7 +7,8 @@
|
||||
# --- END COPYRIGHT BLOCK ---
|
||||
#
|
||||
import socket
|
||||
-from random import sample
|
||||
+from random import sample, randrange
|
||||
+
|
||||
import pytest
|
||||
from ldap.controls import SimplePagedResultsControl, GetEffectiveRightsControl
|
||||
from lib389.tasks import *
|
||||
@@ -16,6 +17,10 @@
|
||||
from lib389._constants import DN_LDBM, DN_DM, DEFAULT_SUFFIX
|
||||
from lib389._controls import SSSRequestControl
|
||||
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
|
||||
@@ -42,11 +47,56 @@
|
||||
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"""
|
||||
@@ -71,8 +121,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)
|
||||
|
||||
@@ -175,7 +227,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.
|
||||
@@ -195,9 +247,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
|
||||
@@ -217,6 +276,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
|
||||
@@ -1191,6 +1251,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 26a2e7bf83..964d28836f 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 a842d4249f..f77043afa9 100644
|
||||
--- a/ldap/servers/slapd/opshared.c
|
||||
+++ b/ldap/servers/slapd/opshared.c
|
||||
@@ -921,9 +921,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 fc15f6bec9..9959c927e0 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 c7389fe2ec..e8adbc254b 100644
|
||||
--- a/ldap/servers/slapd/proto-slap.h
|
||||
+++ b/ldap/servers/slapd/proto-slap.h
|
||||
@@ -1614,7 +1614,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 7590ec4424..6a941dbe75 100644
|
||||
--- a/src/lib389/lib389/__init__.py
|
||||
+++ b/src/lib389/lib389/__init__.py
|
||||
@@ -1048,6 +1048,24 @@ def close(self):
|
||||
|
||||
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
|
||||
@@ -1071,7 +1089,13 @@ def start(self, timeout=120, post_open=True):
|
||||
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.
|
||||
@@ -1095,6 +1119,7 @@ def start(self, timeout=120, post_open=True):
|
||||
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')
|
|
@ -0,0 +1,76 @@
|
|||
From 4883a69dcf2764146bbe54c5432752b79f8d1a1e Mon Sep 17 00:00:00 2001
|
||||
From: Pierre Rogier <progier@redhat.com>
|
||||
Date: Mon, 20 Nov 2023 16:17:39 +0100
|
||||
Subject: [PATCH] Issue 5984 - Crash when paged result search are abandoned -
|
||||
fix2
|
||||
|
||||
---
|
||||
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 964d28836f..2dd1ee3205 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 6a941dbe75..7590ec4424 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')
|
|
@ -0,0 +1,3 @@
|
|||
#Type Name ID GECOS Home directory Shell
|
||||
g dirsrv 389
|
||||
u dirsrv 389:389 "user for 389-ds-base" /usr/share/dirsrv/ /sbin/nologin
|
|
@ -1,933 +0,0 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "addr2line"
|
||||
version = "0.21.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb"
|
||||
dependencies = [
|
||||
"gimli",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "adler"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
||||
|
||||
[[package]]
|
||||
name = "ahash"
|
||||
version = "0.7.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a824f2aa7e75a0c98c5a504fceb80649e9c35265d44525b5f94de4771a395cd"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
"once_cell",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ansi_term"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "atty"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||
|
||||
[[package]]
|
||||
name = "backtrace"
|
||||
version = "0.3.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837"
|
||||
dependencies = [
|
||||
"addr2line",
|
||||
"cc",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"miniz_oxide",
|
||||
"object",
|
||||
"rustc-demangle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||
|
||||
[[package]]
|
||||
name = "cbindgen"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9daec6140ab4dcd38c3dd57e580b59a621172a526ac79f1527af760a55afeafd"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"log",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"syn 1.0.109",
|
||||
"tempfile",
|
||||
"toml",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0"
|
||||
dependencies = [
|
||||
"jobserver",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "2.34.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c"
|
||||
dependencies = [
|
||||
"ansi_term",
|
||||
"atty",
|
||||
"bitflags 1.3.2",
|
||||
"strsim",
|
||||
"textwrap",
|
||||
"unicode-width",
|
||||
"vec_map",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "concread"
|
||||
version = "0.2.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dcc9816f5ac93ebd51c37f7f9a6bf2b40dfcd42978ad2aea5d542016e9244cf6"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"crossbeam",
|
||||
"crossbeam-epoch",
|
||||
"crossbeam-utils",
|
||||
"lru",
|
||||
"parking_lot",
|
||||
"rand",
|
||||
"smallvec",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam"
|
||||
version = "0.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1137cd7e7fc0fb5d3c5a8678be38ec56e819125d8d7907411fe24ccb943faca8"
|
||||
dependencies = [
|
||||
"crossbeam-channel",
|
||||
"crossbeam-deque",
|
||||
"crossbeam-epoch",
|
||||
"crossbeam-queue",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-channel"
|
||||
version = "0.5.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "176dc175b78f56c0f321911d9c8eb2b77a78a4860b9c19db83835fea1a46649b"
|
||||
dependencies = [
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-deque"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d"
|
||||
dependencies = [
|
||||
"crossbeam-epoch",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-epoch"
|
||||
version = "0.9.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
|
||||
dependencies = [
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-queue"
|
||||
version = "0.3.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35"
|
||||
dependencies = [
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.8.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345"
|
||||
|
||||
[[package]]
|
||||
name = "entryuuid"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"paste",
|
||||
"slapi_r_plugin",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "entryuuid_syntax"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"paste",
|
||||
"slapi_r_plugin",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.3.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5"
|
||||
|
||||
[[package]]
|
||||
name = "fernet"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "93804560e638370a8be6d59ce71ed803e55e230abdbf42598e666b41adda9b1f"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"byteorder",
|
||||
"getrandom",
|
||||
"openssl",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "foreign-types"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
|
||||
dependencies = [
|
||||
"foreign-types-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "foreign-types-shared"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gimli"
|
||||
version = "0.28.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253"
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.12.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.1.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "instant"
|
||||
version = "0.1.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
|
||||
|
||||
[[package]]
|
||||
name = "jobserver"
|
||||
version = "0.1.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8c37f63953c4c63420ed5fd3d6d398c719489b9f872b9fa683262f8edd363c7d"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.152"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7"
|
||||
|
||||
[[package]]
|
||||
name = "librnsslapd"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"cbindgen",
|
||||
"libc",
|
||||
"slapd",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "librslapd"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"cbindgen",
|
||||
"concread",
|
||||
"libc",
|
||||
"slapd",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.4.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
|
||||
|
||||
[[package]]
|
||||
name = "lru"
|
||||
version = "0.7.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e999beba7b6e8345721bd280141ed958096a2e4abdf74f67ff4ce49b4b54e47a"
|
||||
dependencies = [
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149"
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7"
|
||||
dependencies = [
|
||||
"adler",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "object"
|
||||
version = "0.32.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.19.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
|
||||
|
||||
[[package]]
|
||||
name = "openssl"
|
||||
version = "0.10.62"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8cde4d2d9200ad5909f8dac647e29482e07c3a35de8a13fce7c9c7747ad9f671"
|
||||
dependencies = [
|
||||
"bitflags 2.4.1",
|
||||
"cfg-if",
|
||||
"foreign-types",
|
||||
"libc",
|
||||
"once_cell",
|
||||
"openssl-macros",
|
||||
"openssl-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openssl-macros"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.48",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openssl-sys"
|
||||
version = "0.9.98"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c1665caf8ab2dc9aef43d1c0023bd904633a6a05cb30b0ad59bec2ae986e57a7"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"pkg-config",
|
||||
"vcpkg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99"
|
||||
dependencies = [
|
||||
"instant",
|
||||
"lock_api",
|
||||
"parking_lot_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot_core"
|
||||
version = "0.8.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"instant",
|
||||
"libc",
|
||||
"redox_syscall 0.2.16",
|
||||
"smallvec",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "paste"
|
||||
version = "0.1.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "45ca20c77d80be666aef2b45486da86238fabe33e38306bd3118fe4af33fa880"
|
||||
dependencies = [
|
||||
"paste-impl",
|
||||
"proc-macro-hack",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "paste-impl"
|
||||
version = "0.1.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d95a7db200b97ef370c8e6de0088252f7e0dfff7d047a28528e47456c0fc98b6"
|
||||
dependencies = [
|
||||
"proc-macro-hack",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58"
|
||||
|
||||
[[package]]
|
||||
name = "pkg-config"
|
||||
version = "0.3.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "69d3587f8a9e599cc7ec2c00e331f71c4e69a5f9a4b8a6efd5b07466b9736f9a"
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-hack"
|
||||
version = "0.5.20+deprecated"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.76"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pwdchan"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"cc",
|
||||
"libc",
|
||||
"openssl",
|
||||
"paste",
|
||||
"slapi_r_plugin",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"rand_chacha",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.2.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rsds"
|
||||
version = "0.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "rustc-demangle"
|
||||
version = "0.1.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.38.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "322394588aaf33c24007e8bb3238ee3e4c5c09c084ab32bc73890b99ff326bca"
|
||||
dependencies = [
|
||||
"bitflags 2.4.1",
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c"
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.195"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "63261df402c67811e9ac6def069e4786148c4563f4b50fd4bf30aa370d626b02"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.195"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.48",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.111"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "176e46fa42316f18edd598015a5166857fc835ec732f5215eac6b7bdbf0a84f4"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "slapd"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"fernet",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "slapi_r_plugin"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"paste",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2593d31f82ead8df961d8bd23a64c2ccf2eb5dd34b0a34bfb4dd54011c72009e"
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.109"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.48"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "01ce4141aa927a6d1bd34a041795abd0db1cccba5d5f24b009f694bdf3a1f3fa"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"fastrand",
|
||||
"redox_syscall 0.4.1",
|
||||
"rustix",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "textwrap"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
|
||||
dependencies = [
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.35.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c89b4efa943be685f629b149f53829423f8f5531ea21249408e8e2f8671ec104"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
"pin-project-lite",
|
||||
"tokio-macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-macros"
|
||||
version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.48",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.5.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.1.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85"
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "vcpkg"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
|
||||
|
||||
[[package]]
|
||||
name = "vec_map"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu",
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
|
||||
|
||||
[[package]]
|
||||
name = "zeroize"
|
||||
version = "1.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d"
|
||||
dependencies = [
|
||||
"zeroize_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zeroize_derive"
|
||||
version = "1.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.48",
|
||||
]
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue