Compare commits
8 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cd5135cf3a | ||
|
|
26f66fc9f3 | ||
|
|
e3abfab8ae | ||
|
|
6a9fe72ef8 | ||
|
|
0e19d9ccb9 | ||
|
|
c134ea11db | ||
|
|
0fbd2343c9 | ||
|
|
8a64e863ed |
12 changed files with 60 additions and 699 deletions
|
|
@ -1,4 +1,4 @@
|
|||
From 5de98cdc10bd333d0695c00d57e137d699f90f1c Mon Sep 17 00:00:00 2001
|
||||
From 1c9c535888b9a850095794787d67900b04924a76 Mon Sep 17 00:00:00 2001
|
||||
From: tbordaz <tbordaz@redhat.com>
|
||||
Date: Wed, 7 Jan 2026 11:21:12 +0100
|
||||
Subject: [PATCH] Issue 7096 - During replication online total init the
|
||||
|
|
@ -1,551 +0,0 @@
|
|||
From 045fe1a6899b7e4588be7101e81bb78995a713b1 Mon Sep 17 00:00:00 2001
|
||||
From: Simon Pichugin <spichugi@redhat.com>
|
||||
Date: Tue, 16 Dec 2025 15:48:35 -0800
|
||||
Subject: [PATCH] Issue 7150 - Compressed access log rotations skipped,
|
||||
accesslog-list out of sync (#7151)
|
||||
|
||||
Description: Accept `.gz`-suffixed rotated log filenames when
|
||||
rebuilding rotation info and checking previous logs, preventing
|
||||
compressed rotations from being dropped from the internal list.
|
||||
|
||||
Add regression tests to stress log rotation with compression,
|
||||
verify `nsslapd-accesslog-list` stays in sync, and guard against
|
||||
crashes when flushing buffered logs during rotation.
|
||||
Minor doc fix in test.
|
||||
|
||||
Fixes: https://github.com/389ds/389-ds-base/issues/7150
|
||||
|
||||
Reviewed by: @progier389 (Thanks!)
|
||||
---
|
||||
.../suites/logging/log_flush_rotation_test.py | 341 +++++++++++++++++-
|
||||
ldap/servers/slapd/log.c | 99 +++--
|
||||
2 files changed, 402 insertions(+), 38 deletions(-)
|
||||
|
||||
diff --git a/dirsrvtests/tests/suites/logging/log_flush_rotation_test.py b/dirsrvtests/tests/suites/logging/log_flush_rotation_test.py
|
||||
index b33a622e1..864ba9c5d 100644
|
||||
--- a/dirsrvtests/tests/suites/logging/log_flush_rotation_test.py
|
||||
+++ b/dirsrvtests/tests/suites/logging/log_flush_rotation_test.py
|
||||
@@ -6,6 +6,7 @@
|
||||
# See LICENSE for details.
|
||||
# --- END COPYRIGHT BLOCK ---
|
||||
#
|
||||
+import glob
|
||||
import os
|
||||
import logging
|
||||
import time
|
||||
@@ -13,14 +14,351 @@ import pytest
|
||||
from lib389._constants import DEFAULT_SUFFIX, PW_DM
|
||||
from lib389.tasks import ImportTask
|
||||
from lib389.idm.user import UserAccounts
|
||||
+from lib389.idm.domain import Domain
|
||||
+from lib389.idm.directorymanager import DirectoryManager
|
||||
from lib389.topologies import topology_st as topo
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
+def remove_rotated_access_logs(inst):
|
||||
+ """
|
||||
+ Remove all rotated access log files to start fresh for each test.
|
||||
+ This prevents log files from previous tests affecting current test results.
|
||||
+ """
|
||||
+ log_dir = inst.get_log_dir()
|
||||
+ patterns = [
|
||||
+ f'{log_dir}/access.2*', # Uncompressed rotated logs
|
||||
+ f'{log_dir}/access.*.gz', # Compressed rotated logs
|
||||
+ ]
|
||||
+ for pattern in patterns:
|
||||
+ for log_file in glob.glob(pattern):
|
||||
+ try:
|
||||
+ os.remove(log_file)
|
||||
+ log.info(f"Removed old log file: {log_file}")
|
||||
+ except OSError as e:
|
||||
+ log.warning(f"Could not remove {log_file}: {e}")
|
||||
+
|
||||
+
|
||||
+def reset_access_log_config(inst):
|
||||
+ """
|
||||
+ Reset access log configuration to default values.
|
||||
+ """
|
||||
+ inst.config.set('nsslapd-accesslog-compress', 'off')
|
||||
+ inst.config.set('nsslapd-accesslog-maxlogsize', '100')
|
||||
+ inst.config.set('nsslapd-accesslog-maxlogsperdir', '10')
|
||||
+ inst.config.set('nsslapd-accesslog-logrotationsync-enabled', 'off')
|
||||
+ inst.config.set('nsslapd-accesslog-logbuffering', 'on')
|
||||
+ inst.config.set('nsslapd-accesslog-logexpirationtime', '-1')
|
||||
+ inst.config.set('nsslapd-accesslog-logminfreediskspace', '5')
|
||||
+
|
||||
+
|
||||
+def generate_heavy_load(inst, suffix, iterations=50):
|
||||
+ """
|
||||
+ Generate heavy LDAP load to fill access log quickly.
|
||||
+ Performs multiple operations: searches, modifies, binds to populate logs.
|
||||
+ """
|
||||
+ for i in range(iterations):
|
||||
+ suffix.replace('description', f'iteration_{i}')
|
||||
+ suffix.get_attr_val('description')
|
||||
+
|
||||
+
|
||||
+def count_access_logs(log_dir, compressed_only=False):
|
||||
+ """
|
||||
+ Count access log files in the log directory.
|
||||
+ Returns count of rotated access logs (not including the active 'access' file).
|
||||
+ """
|
||||
+ if compressed_only:
|
||||
+ pattern = f'{log_dir}/access.*.gz'
|
||||
+ else:
|
||||
+ pattern = f'{log_dir}/access.2*'
|
||||
+ log_files = glob.glob(pattern)
|
||||
+ return len(log_files)
|
||||
+
|
||||
+
|
||||
+def test_log_pileup_with_compression(topo):
|
||||
+ """Test that log rotation properly deletes old logs when compression is enabled.
|
||||
+
|
||||
+ :id: fa1bfce8-b6d3-4520-a0a8-bead14fa5838
|
||||
+ :setup: Standalone Instance
|
||||
+ :steps:
|
||||
+ 1. Clean up existing rotated logs and reset configuration
|
||||
+ 2. Enable access log compression
|
||||
+ 3. Set strict log limits (small maxlogsperdir)
|
||||
+ 4. Disable log expiration to test count-based deletion
|
||||
+ 5. Generate heavy load to create many log rotations
|
||||
+ 6. Verify log count does not exceed maxlogsperdir limit
|
||||
+ :expectedresults:
|
||||
+ 1. Success
|
||||
+ 2. Success
|
||||
+ 3. Success
|
||||
+ 4. Success
|
||||
+ 5. Success
|
||||
+ 6. Log count should be at or below maxlogsperdir + small buffer
|
||||
+ """
|
||||
+
|
||||
+ inst = topo.standalone
|
||||
+ suffix = Domain(inst, DEFAULT_SUFFIX)
|
||||
+ log_dir = inst.get_log_dir()
|
||||
+
|
||||
+ # Clean up before test
|
||||
+ remove_rotated_access_logs(inst)
|
||||
+ reset_access_log_config(inst)
|
||||
+ inst.restart()
|
||||
+
|
||||
+ max_logs = 5
|
||||
+ inst.config.set('nsslapd-accesslog-compress', 'on')
|
||||
+ inst.config.set('nsslapd-accesslog-maxlogsperdir', str(max_logs))
|
||||
+ inst.config.set('nsslapd-accesslog-maxlogsize', '1') # 1MB to trigger rotation
|
||||
+ inst.config.set('nsslapd-accesslog-logrotationsync-enabled', 'off')
|
||||
+ inst.config.set('nsslapd-accesslog-logbuffering', 'off')
|
||||
+
|
||||
+ inst.config.set('nsslapd-accesslog-logexpirationtime', '-1')
|
||||
+
|
||||
+ inst.config.set('nsslapd-accesslog-logminfreediskspace', '5')
|
||||
+
|
||||
+ inst.restart()
|
||||
+ time.sleep(2)
|
||||
+
|
||||
+ target_logs = max_logs * 3
|
||||
+ for i in range(target_logs):
|
||||
+ log.info(f"Generating load for log rotation {i+1}/{target_logs}")
|
||||
+ generate_heavy_load(inst, suffix, iterations=150)
|
||||
+ time.sleep(1) # Wait for rotation
|
||||
+
|
||||
+ time.sleep(3)
|
||||
+
|
||||
+ logs_on_disk = count_access_logs(log_dir)
|
||||
+ log.info(f"Configured maxlogsperdir: {max_logs}")
|
||||
+ log.info(f"Actual rotated logs on disk: {logs_on_disk}")
|
||||
+
|
||||
+ all_access_logs = glob.glob(f'{log_dir}/access*')
|
||||
+ log.info(f"All access log files: {all_access_logs}")
|
||||
+
|
||||
+ max_allowed = max_logs + 2
|
||||
+ assert logs_on_disk <= max_allowed, (
|
||||
+ f"Log rotation failed to delete old files! "
|
||||
+ f"Expected at most {max_allowed} rotated logs (maxlogsperdir={max_logs} + 2 buffer), "
|
||||
+ f"but found {logs_on_disk}. The server has lost track of the file list."
|
||||
+ )
|
||||
+
|
||||
+
|
||||
+@pytest.mark.parametrize("compress_enabled", ["on", "off"])
|
||||
+def test_accesslog_list_mismatch(topo, compress_enabled):
|
||||
+ """Test that nsslapd-accesslog-list stays synchronized with actual log files.
|
||||
+
|
||||
+ :id: 0a8a46a6-cae7-43bd-8b64-5e3481480cd3
|
||||
+ :parametrized: yes
|
||||
+ :setup: Standalone Instance
|
||||
+ :steps:
|
||||
+ 1. Clean up existing rotated logs and reset configuration
|
||||
+ 2. Configure log rotation with compression enabled/disabled
|
||||
+ 3. Generate activity to trigger multiple rotations
|
||||
+ 4. Get the nsslapd-accesslog-list attribute
|
||||
+ 5. Compare with actual files on disk
|
||||
+ 6. Verify they match (accounting for .gz extension when enabled)
|
||||
+ :expectedresults:
|
||||
+ 1. Success
|
||||
+ 2. Success
|
||||
+ 3. Success
|
||||
+ 4. Success
|
||||
+ 5. Success
|
||||
+ 6. The list attribute should match actual files on disk
|
||||
+ """
|
||||
+
|
||||
+ inst = topo.standalone
|
||||
+ suffix = Domain(inst, DEFAULT_SUFFIX)
|
||||
+ log_dir = inst.get_log_dir()
|
||||
+ compression_on = compress_enabled == "on"
|
||||
+
|
||||
+ # Clean up before test
|
||||
+ remove_rotated_access_logs(inst)
|
||||
+ reset_access_log_config(inst)
|
||||
+ inst.restart()
|
||||
+
|
||||
+ inst.config.set('nsslapd-accesslog-compress', compress_enabled)
|
||||
+ inst.config.set('nsslapd-accesslog-maxlogsize', '1')
|
||||
+ inst.config.set('nsslapd-accesslog-maxlogsperdir', '10')
|
||||
+ inst.config.set('nsslapd-accesslog-logrotationsync-enabled', 'off')
|
||||
+ inst.config.set('nsslapd-accesslog-logbuffering', 'off')
|
||||
+ inst.config.set('nsslapd-accesslog-logexpirationtime', '-1')
|
||||
+
|
||||
+ inst.restart()
|
||||
+ time.sleep(2)
|
||||
+
|
||||
+ for i in range(15):
|
||||
+ suffix_note = "(no compression)" if not compression_on else ""
|
||||
+ log.info(f"Generating load for rotation {i+1}/15 {suffix_note}")
|
||||
+ generate_heavy_load(inst, suffix, iterations=150)
|
||||
+ time.sleep(1)
|
||||
+
|
||||
+ time.sleep(3)
|
||||
+
|
||||
+ accesslog_list = inst.config.get_attr_vals_utf8('nsslapd-accesslog-list')
|
||||
+ log.info(f"nsslapd-accesslog-list entries (compress={compress_enabled}): {len(accesslog_list)}")
|
||||
+ log.info(f"nsslapd-accesslog-list (compress={compress_enabled}): {accesslog_list}")
|
||||
+
|
||||
+ disk_files = glob.glob(f'{log_dir}/access.2*')
|
||||
+ log.info(f"Actual files on disk (compress={compress_enabled}): {len(disk_files)}")
|
||||
+ log.info(f"Disk files (compress={compress_enabled}): {disk_files}")
|
||||
+
|
||||
+ disk_files_for_compare = set()
|
||||
+ for fpath in disk_files:
|
||||
+ if compression_on and fpath.endswith('.gz'):
|
||||
+ disk_files_for_compare.add(fpath[:-3])
|
||||
+ else:
|
||||
+ disk_files_for_compare.add(fpath)
|
||||
+
|
||||
+ list_files_set = set(accesslog_list)
|
||||
+ missing_from_disk = list_files_set - disk_files_for_compare
|
||||
+ extra_on_disk = disk_files_for_compare - list_files_set
|
||||
+
|
||||
+ if missing_from_disk:
|
||||
+ log.error(
|
||||
+ f"[compress={compress_enabled}] Files in list but NOT on disk: {missing_from_disk}"
|
||||
+ )
|
||||
+ if extra_on_disk:
|
||||
+ log.warning(
|
||||
+ f"[compress={compress_enabled}] Files on disk but NOT in list: {extra_on_disk}"
|
||||
+ )
|
||||
+
|
||||
+ assert not missing_from_disk, (
|
||||
+ f"nsslapd-accesslog-list mismatch (compress={compress_enabled})! "
|
||||
+ f"Files listed but missing from disk: {missing_from_disk}. "
|
||||
+ f"This indicates the server's internal list is out of sync with actual files."
|
||||
+ )
|
||||
+
|
||||
+ if len(extra_on_disk) > 2:
|
||||
+ log.warning(
|
||||
+ f"Potential log tracking issue (compress={compress_enabled}): "
|
||||
+ f"{len(extra_on_disk)} files on disk are not tracked in the accesslog-list: "
|
||||
+ f"{extra_on_disk}"
|
||||
+ )
|
||||
+
|
||||
+
|
||||
+def test_accesslog_list_mixed_compression(topo):
|
||||
+ """Test that nsslapd-accesslog-list correctly tracks both compressed and uncompressed logs.
|
||||
+
|
||||
+ :id: 11b088cd-23be-407d-ad16-4ce2e12da09e
|
||||
+ :setup: Standalone Instance
|
||||
+ :steps:
|
||||
+ 1. Clean up existing rotated logs and reset configuration
|
||||
+ 2. Create rotated logs with compression OFF
|
||||
+ 3. Enable compression and create more rotated logs
|
||||
+ 4. Get the nsslapd-accesslog-list attribute
|
||||
+ 5. Compare with actual files on disk
|
||||
+ 6. Verify all files are correctly tracked (uncompressed and compressed)
|
||||
+ :expectedresults:
|
||||
+ 1. Success
|
||||
+ 2. Success - uncompressed rotated logs created
|
||||
+ 3. Success - compressed rotated logs created
|
||||
+ 4. Success
|
||||
+ 5. Success
|
||||
+ 6. The list should contain base filenames (without .gz) that
|
||||
+ correspond to files on disk (either as-is or with .gz suffix)
|
||||
+ """
|
||||
+
|
||||
+ inst = topo.standalone
|
||||
+ suffix = Domain(inst, DEFAULT_SUFFIX)
|
||||
+ log_dir = inst.get_log_dir()
|
||||
+
|
||||
+ # Clean up before test
|
||||
+ remove_rotated_access_logs(inst)
|
||||
+ reset_access_log_config(inst)
|
||||
+ inst.restart()
|
||||
+
|
||||
+ inst.config.set('nsslapd-accesslog-compress', 'off')
|
||||
+ inst.config.set('nsslapd-accesslog-maxlogsize', '1')
|
||||
+ inst.config.set('nsslapd-accesslog-maxlogsperdir', '20')
|
||||
+ inst.config.set('nsslapd-accesslog-logrotationsync-enabled', 'off')
|
||||
+ inst.config.set('nsslapd-accesslog-logbuffering', 'off')
|
||||
+ inst.config.set('nsslapd-accesslog-logexpirationtime', '-1')
|
||||
+
|
||||
+ inst.restart()
|
||||
+ time.sleep(2)
|
||||
+
|
||||
+ for i in range(15):
|
||||
+ log.info(f"Generating load for uncompressed rotation {i+1}/15")
|
||||
+ generate_heavy_load(inst, suffix, iterations=150)
|
||||
+ time.sleep(1)
|
||||
+
|
||||
+ time.sleep(2)
|
||||
+
|
||||
+ # Check what we have so far
|
||||
+ uncompressed_files = glob.glob(f'{log_dir}/access.2*')
|
||||
+ log.info(f"Files on disk after uncompressed phase: {uncompressed_files}")
|
||||
+
|
||||
+ inst.config.set('nsslapd-accesslog-compress', 'on')
|
||||
+ inst.restart()
|
||||
+ time.sleep(2)
|
||||
+
|
||||
+ for i in range(15):
|
||||
+ log.info(f"Generating load for compressed rotation {i+1}/15")
|
||||
+ generate_heavy_load(inst, suffix, iterations=150)
|
||||
+ time.sleep(1)
|
||||
+
|
||||
+ time.sleep(3)
|
||||
+
|
||||
+ accesslog_list = inst.config.get_attr_vals_utf8('nsslapd-accesslog-list')
|
||||
+
|
||||
+ disk_files = glob.glob(f'{log_dir}/access.2*')
|
||||
+
|
||||
+ log.info(f"nsslapd-accesslog-list entries: {len(accesslog_list)}")
|
||||
+ log.info(f"nsslapd-accesslog-list: {sorted(accesslog_list)}")
|
||||
+ log.info(f"Actual files on disk: {len(disk_files)}")
|
||||
+ log.info(f"Disk files: {sorted(disk_files)}")
|
||||
+
|
||||
+ compressed_on_disk = [f for f in disk_files if f.endswith('.gz')]
|
||||
+ uncompressed_on_disk = [f for f in disk_files if not f.endswith('.gz')]
|
||||
+ log.info(f"Compressed files on disk: {compressed_on_disk}")
|
||||
+ log.info(f"Uncompressed files on disk: {uncompressed_on_disk}")
|
||||
+
|
||||
+ list_files_set = set(accesslog_list)
|
||||
+
|
||||
+ disk_files_base = set()
|
||||
+ for fpath in disk_files:
|
||||
+ if fpath.endswith('.gz'):
|
||||
+ disk_files_base.add(fpath[:-3]) # Strip .gz
|
||||
+ else:
|
||||
+ disk_files_base.add(fpath)
|
||||
+
|
||||
+ missing_from_disk = list_files_set - disk_files_base
|
||||
+
|
||||
+ extra_on_disk = disk_files_base - list_files_set
|
||||
+
|
||||
+ if missing_from_disk:
|
||||
+ log.error(f"Files in list but NOT on disk: {missing_from_disk}")
|
||||
+ if extra_on_disk:
|
||||
+ log.warning(f"Files on disk but NOT in list: {extra_on_disk}")
|
||||
+
|
||||
+ assert not missing_from_disk, (
|
||||
+ f"nsslapd-accesslog-list contains stale entries! "
|
||||
+ f"Files in list but not on disk (as base or .gz): {missing_from_disk}"
|
||||
+ )
|
||||
+
|
||||
+ for list_file in accesslog_list:
|
||||
+ exists_uncompressed = os.path.exists(list_file)
|
||||
+ exists_compressed = os.path.exists(list_file + '.gz')
|
||||
+ assert exists_uncompressed or exists_compressed, (
|
||||
+ f"File in accesslog-list does not exist on disk: {list_file} "
|
||||
+ f"(checked both {list_file} and {list_file}.gz)"
|
||||
+ )
|
||||
+ if exists_compressed and not exists_uncompressed:
|
||||
+ log.info(f" {list_file} -> exists as .gz (compressed)")
|
||||
+ elif exists_uncompressed:
|
||||
+ log.info(f" {list_file} -> exists (uncompressed)")
|
||||
+
|
||||
+ if len(extra_on_disk) > 1:
|
||||
+ log.warning(
|
||||
+ f"Some files on disk are not tracked in accesslog-list: {extra_on_disk}"
|
||||
+ )
|
||||
+
|
||||
+ log.info("Mixed compression test completed successfully")
|
||||
+
|
||||
+
|
||||
def test_log_flush_and_rotation_crash(topo):
|
||||
- """Make sure server does not crash whening flushing a buffer and rotating
|
||||
+ """Make sure server does not crash when flushing a buffer and rotating
|
||||
the log at the same time
|
||||
|
||||
:id: d4b0af2f-48b2-45f5-ae8b-f06f692c3133
|
||||
@@ -36,6 +374,7 @@ def test_log_flush_and_rotation_crash(topo):
|
||||
3. Success
|
||||
4. Success
|
||||
"""
|
||||
+ # NOTE: This test is placed last as it may affect the suffix state.
|
||||
|
||||
inst = topo.standalone
|
||||
|
||||
diff --git a/ldap/servers/slapd/log.c b/ldap/servers/slapd/log.c
|
||||
index 27bb4bc15..ea744ac1e 100644
|
||||
--- a/ldap/servers/slapd/log.c
|
||||
+++ b/ldap/servers/slapd/log.c
|
||||
@@ -137,6 +137,7 @@ static void vslapd_log_emergency_error(LOGFD fp, const char *msg, int locked);
|
||||
static int get_syslog_loglevel(int loglevel);
|
||||
static void log_external_libs_debug_openldap_print(char *buffer);
|
||||
static int log__fix_rotationinfof(char *pathname);
|
||||
+static int log__validate_rotated_logname(const char *timestamp_str, PRBool *is_compressed);
|
||||
|
||||
static int
|
||||
get_syslog_loglevel(int loglevel)
|
||||
@@ -375,7 +376,7 @@ g_log_init()
|
||||
loginfo.log_security_fdes = NULL;
|
||||
loginfo.log_security_file = NULL;
|
||||
loginfo.log_securityinfo_file = NULL;
|
||||
- loginfo.log_numof_access_logs = 1;
|
||||
+ loginfo.log_numof_security_logs = 1;
|
||||
loginfo.log_security_logchain = NULL;
|
||||
loginfo.log_security_buffer = log_create_buffer(LOG_BUFFER_MAXSIZE);
|
||||
loginfo.log_security_compress = cfg->securitylog_compress;
|
||||
@@ -3422,7 +3423,7 @@ log__open_accesslogfile(int logfile_state, int locked)
|
||||
}
|
||||
} else if (loginfo.log_access_compress) {
|
||||
if (compress_log_file(newfile, loginfo.log_access_mode) != 0) {
|
||||
- slapi_log_err(SLAPI_LOG_ERR, "log__open_auditfaillogfile",
|
||||
+ slapi_log_err(SLAPI_LOG_ERR, "log__open_accesslogfile",
|
||||
"failed to compress rotated access log (%s)\n",
|
||||
newfile);
|
||||
} else {
|
||||
@@ -4825,6 +4826,50 @@ log__delete_rotated_logs()
|
||||
loginfo.log_error_logchain = NULL;
|
||||
}
|
||||
|
||||
+/*
|
||||
+ * log__validate_rotated_logname
|
||||
+ *
|
||||
+ * Validates that a log filename timestamp suffix matches the expected format:
|
||||
+ * YYYYMMDD-HHMMSS (15 chars) or YYYYMMDD-HHMMSS.gz (18 chars) for compressed files.
|
||||
+ * Uses regex pattern: ^[0-9]{8}-[0-9]{6}(\.gz)?$
|
||||
+ *
|
||||
+ * \param timestamp_str The timestamp portion of the log filename (after the first '.')
|
||||
+ * \param is_compressed Output parameter set to PR_TRUE if the file has .gz suffix
|
||||
+ * \return 1 if valid, 0 if invalid
|
||||
+ */
|
||||
+static int
|
||||
+log__validate_rotated_logname(const char *timestamp_str, PRBool *is_compressed)
|
||||
+{
|
||||
+ Slapi_Regex *re = NULL;
|
||||
+ char *re_error = NULL;
|
||||
+ int rc = 0;
|
||||
+
|
||||
+ /* Match YYYYMMDD-HHMMSS with optional .gz suffix */
|
||||
+ static const char *pattern = "^[0-9]{8}-[0-9]{6}(\\.gz)?$";
|
||||
+
|
||||
+ *is_compressed = PR_FALSE;
|
||||
+
|
||||
+ re = slapi_re_comp(pattern, &re_error);
|
||||
+ if (re == NULL) {
|
||||
+ slapi_log_err(SLAPI_LOG_ERR, "log__validate_rotated_logname",
|
||||
+ "Failed to compile regex: %s\n", re_error ? re_error : "unknown error");
|
||||
+ slapi_ch_free_string(&re_error);
|
||||
+ return 0;
|
||||
+ }
|
||||
+
|
||||
+ rc = slapi_re_exec_nt(re, timestamp_str);
|
||||
+ if (rc == 1) {
|
||||
+ /* Check if compressed by looking for .gz suffix */
|
||||
+ size_t len = strlen(timestamp_str);
|
||||
+ if (len >= 3 && strcmp(timestamp_str + len - 3, ".gz") == 0) {
|
||||
+ *is_compressed = PR_TRUE;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ slapi_re_free(re);
|
||||
+ return rc == 1 ? 1 : 0;
|
||||
+}
|
||||
+
|
||||
#define ERRORSLOG 1
|
||||
#define ACCESSLOG 2
|
||||
#define AUDITLOG 3
|
||||
@@ -4907,31 +4952,19 @@ log__fix_rotationinfof(char *pathname)
|
||||
}
|
||||
} else if (0 == strncmp(log_type, dirent->name, strlen(log_type)) &&
|
||||
(p = strchr(dirent->name, '.')) != NULL &&
|
||||
- NULL != strchr(p, '-')) /* e.g., errors.20051123-165135 */
|
||||
+ NULL != strchr(p, '-')) /* e.g., errors.20051123-165135 or errors.20051123-165135.gz */
|
||||
{
|
||||
struct logfileinfo *logp;
|
||||
- char *q;
|
||||
- int ignoreit = 0;
|
||||
-
|
||||
- for (q = ++p; q && *q; q++) {
|
||||
- if (*q != '-' &&
|
||||
- *q != '.' && /* .gz */
|
||||
- *q != 'g' &&
|
||||
- *q != 'z' &&
|
||||
- !isdigit(*q))
|
||||
- {
|
||||
- ignoreit = 1;
|
||||
- }
|
||||
- }
|
||||
- if (ignoreit || (q - p != 15)) {
|
||||
+ PRBool is_compressed = PR_FALSE;
|
||||
+
|
||||
+ /* Skip the '.' to get the timestamp portion */
|
||||
+ p++;
|
||||
+ if (!log__validate_rotated_logname(p, &is_compressed)) {
|
||||
continue;
|
||||
}
|
||||
logp = (struct logfileinfo *)slapi_ch_malloc(sizeof(struct logfileinfo));
|
||||
logp->l_ctime = log_reverse_convert_time(p);
|
||||
- logp->l_compressed = PR_FALSE;
|
||||
- if (strcmp(p + strlen(p) - 3, ".gz") == 0) {
|
||||
- logp->l_compressed = PR_TRUE;
|
||||
- }
|
||||
+ logp->l_compressed = is_compressed;
|
||||
PR_snprintf(rotated_log, rotated_log_len, "%s/%s",
|
||||
logsdir, dirent->name);
|
||||
|
||||
@@ -5098,23 +5131,15 @@ log__check_prevlogs(FILE *fp, char *pathname)
|
||||
for (dirent = PR_ReadDir(dirptr, dirflags); dirent;
|
||||
dirent = PR_ReadDir(dirptr, dirflags)) {
|
||||
if (0 == strncmp(log_type, dirent->name, strlen(log_type)) &&
|
||||
- (p = strrchr(dirent->name, '.')) != NULL &&
|
||||
- NULL != strchr(p, '-')) { /* e.g., errors.20051123-165135 */
|
||||
- char *q;
|
||||
- int ignoreit = 0;
|
||||
-
|
||||
- for (q = ++p; q && *q; q++) {
|
||||
- if (*q != '-' &&
|
||||
- *q != '.' && /* .gz */
|
||||
- *q != 'g' &&
|
||||
- *q != 'z' &&
|
||||
- !isdigit(*q))
|
||||
- {
|
||||
- ignoreit = 1;
|
||||
- }
|
||||
- }
|
||||
- if (ignoreit || (q - p != 15))
|
||||
+ (p = strchr(dirent->name, '.')) != NULL &&
|
||||
+ NULL != strchr(p, '-')) { /* e.g., errors.20051123-165135 or errors.20051123-165135.gz */
|
||||
+ PRBool is_compressed = PR_FALSE;
|
||||
+
|
||||
+ /* Skip the '.' to get the timestamp portion */
|
||||
+ p++;
|
||||
+ if (!log__validate_rotated_logname(p, &is_compressed)) {
|
||||
continue;
|
||||
+ }
|
||||
|
||||
fseek(fp, 0, SEEK_SET);
|
||||
buf[BUFSIZ - 1] = '\0';
|
||||
--
|
||||
2.52.0
|
||||
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
From 6f3bf5a48d504646751be9e91293487eec972ed8 Mon Sep 17 00:00:00 2001
|
||||
From 446bc42e7b64a8496c2c3fe486f86bba318bed5e Mon Sep 17 00:00:00 2001
|
||||
From: Mark Reynolds <mreynolds@redhat.com>
|
||||
Date: Wed, 7 Jan 2026 16:55:27 -0500
|
||||
Subject: [PATCH] Issue - Revise paged result search locking
|
||||
|
|
@ -684,10 +684,10 @@ index 941ab97e3..0d6c4a1aa 100644
|
|||
int rc = -1;
|
||||
Connection *conn = NULL;
|
||||
diff --git a/ldap/servers/slapd/proto-slap.h b/ldap/servers/slapd/proto-slap.h
|
||||
index 6af6583a5..8445670d2 100644
|
||||
index 765c12bf5..455d6d718 100644
|
||||
--- a/ldap/servers/slapd/proto-slap.h
|
||||
+++ b/ldap/servers/slapd/proto-slap.h
|
||||
@@ -1611,20 +1611,22 @@ pthread_mutex_t *pageresult_lock_get_addr(Connection *conn);
|
||||
@@ -1614,20 +1614,22 @@ pthread_mutex_t *pageresult_lock_get_addr(Connection *conn);
|
||||
int pagedresults_parse_control_value(Slapi_PBlock *pb, struct berval *psbvp, ber_int_t *pagesize, int *index, Slapi_Backend *be);
|
||||
void pagedresults_set_response_control(Slapi_PBlock *pb, int iscritical, ber_int_t estimate, int curr_search_count, int index);
|
||||
Slapi_Backend *pagedresults_get_current_be(Connection *conn, int index);
|
||||
|
|
@ -717,7 +717,7 @@ index 6af6583a5..8445670d2 100644
|
|||
int pagedresults_get_unindexed(Connection *conn, Operation *op, int index);
|
||||
int pagedresults_set_unindexed(Connection *conn, Operation *op, int index);
|
||||
int pagedresults_get_sort_result_code(Connection *conn, Operation *op, int index);
|
||||
@@ -1636,15 +1638,13 @@ int pagedresults_cleanup(Connection *conn, int needlock);
|
||||
@@ -1639,15 +1641,13 @@ int pagedresults_cleanup(Connection *conn, int needlock);
|
||||
int pagedresults_is_timedout_nolock(Connection *conn);
|
||||
int pagedresults_reset_timedout_nolock(Connection *conn);
|
||||
int pagedresults_in_use_nolock(Connection *conn);
|
||||
|
|
@ -738,7 +738,7 @@ index 6af6583a5..8445670d2 100644
|
|||
/*
|
||||
* sort.c
|
||||
diff --git a/ldap/servers/slapd/slap.h b/ldap/servers/slapd/slap.h
|
||||
index 49cfb4210..abb0d2e47 100644
|
||||
index 11c5602e3..d494931c2 100644
|
||||
--- a/ldap/servers/slapd/slap.h
|
||||
+++ b/ldap/servers/slapd/slap.h
|
||||
@@ -89,6 +89,10 @@ static char ptokPBE[34] = "Internal (Software) Token ";
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
From 3b133a5ed6fa89939a569fe6130b325726f4e50c Mon Sep 17 00:00:00 2001
|
||||
From: Stanislav Levin <slev@altlinux.org>
|
||||
Date: Fri, 19 Dec 2025 14:52:48 +0300
|
||||
Subject: [PATCH] Sync lib389 version to 3.1.4 (#7161)
|
||||
|
||||
Prepared with:
|
||||
$ python3 validate_version.py --update
|
||||
ERROR: Version mismatch detected!
|
||||
Main project version: 3.1.4
|
||||
lib389 version: 3.1.3
|
||||
SUCCESS: Updated lib389 version to 3.1.4 in pyproject.toml
|
||||
|
||||
Fixes: https://github.com/389ds/389-ds-base/issues/7160
|
||||
|
||||
Reviewed by: @progier (Thanks!)
|
||||
|
||||
Signed-off-by: Stanislav Levin <slev@altlinux.org>
|
||||
---
|
||||
src/lib389/pyproject.toml | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/src/lib389/pyproject.toml b/src/lib389/pyproject.toml
|
||||
index 1cd840713..85c0c5141 100644
|
||||
--- a/src/lib389/pyproject.toml
|
||||
+++ b/src/lib389/pyproject.toml
|
||||
@@ -16,7 +16,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "lib389"
|
||||
-version = "3.1.3" # Should match the main 389-ds-base version
|
||||
+version = "3.1.4" # Should match the main 389-ds-base version
|
||||
description = "A library for accessing, testing, and configuring the 389 Directory Server"
|
||||
readme = "README.md"
|
||||
license = {text = "GPL-3.0-or-later"}
|
||||
--
|
||||
2.52.0
|
||||
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
From cde999edf7246d9dcec4a13950e2c0895165a16e Mon Sep 17 00:00:00 2001
|
||||
From 4936f953fa3b0726c2b178f135cd78dcac7463ba Mon Sep 17 00:00:00 2001
|
||||
From: Simon Pichugin <spichugi@redhat.com>
|
||||
Date: Thu, 8 Jan 2026 10:02:39 -0800
|
||||
Subject: [PATCH] Issue 7108 - Fix shutdown crash in entry cache destruction
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
From 9adaeba848a5b0fbe5d3a6148736f6c2ae940c35 Mon Sep 17 00:00:00 2001
|
||||
From: progier389 <progier@redhat.com>
|
||||
Date: Mon, 5 Jan 2026 14:38:38 +0100
|
||||
Subject: [PATCH] Issue 7166 - db_config_set asserts because of dynamic list
|
||||
(#7167)
|
||||
|
||||
Avoid assertion in db_config_set when args does not contains dynamic list attributes
|
||||
|
||||
Issue: #7166
|
||||
|
||||
Reviewed by: @tbordaz (Thanks!)
|
||||
|
||||
(cherry picked from commit 5f15223280002803a932187c22b10beaeaa74bc2)
|
||||
---
|
||||
src/lib389/lib389/cli_conf/backend.py | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/src/lib389/lib389/cli_conf/backend.py b/src/lib389/lib389/cli_conf/backend.py
|
||||
index 677b37fcb..d0ec4bd9e 100644
|
||||
--- a/src/lib389/lib389/cli_conf/backend.py
|
||||
+++ b/src/lib389/lib389/cli_conf/backend.py
|
||||
@@ -544,7 +544,7 @@ def db_config_set(inst, basedn, log, args):
|
||||
did_something = False
|
||||
replace_list = []
|
||||
|
||||
- if args.enable_dynamic_lists and args.disable_dynamic_lists:
|
||||
+ if getattr(args,'enable_dynamic_lists', None) and getattr(args, 'disable_dynamic_lists', None):
|
||||
raise ValueError("You can not enable and disable dynamic lists at the same time")
|
||||
|
||||
for attr, value in list(attrs.items()):
|
||||
--
|
||||
2.52.0
|
||||
|
||||
|
|
@ -1,57 +0,0 @@
|
|||
From 7693d5335c498de1dbb783042cc4acec0138e44d Mon Sep 17 00:00:00 2001
|
||||
From: Simon Pichugin <spichugi@redhat.com>
|
||||
Date: Mon, 5 Jan 2026 18:32:52 -0800
|
||||
Subject: [PATCH] Issue 7160 - Add lib389 version sync check to configure
|
||||
(#7165)
|
||||
|
||||
Description: Add version validation during configure that ensures
|
||||
lib389 version in pyproject.toml matches the main project version
|
||||
in VERSION.sh. Configure fails with clear error message and fix
|
||||
instructions when versions mismatch, preventing inconsistent releases.
|
||||
|
||||
Fixes: https://github.com/389ds/389-ds-base/issues/7160
|
||||
|
||||
Reviewed by: @progier389 (Thanks!)
|
||||
---
|
||||
configure.ac | 25 +++++++++++++++++++++++++
|
||||
1 file changed, 25 insertions(+)
|
||||
|
||||
diff --git a/configure.ac b/configure.ac
|
||||
index e94f72647..7fd061a8c 100644
|
||||
--- a/configure.ac
|
||||
+++ b/configure.ac
|
||||
@@ -7,6 +7,31 @@ AC_CONFIG_HEADERS([config.h])
|
||||
# include the version information
|
||||
. $srcdir/VERSION.sh
|
||||
AC_MSG_NOTICE(This is configure for $PACKAGE_TARNAME $PACKAGE_VERSION)
|
||||
+
|
||||
+# Validate lib389 version matches main project version
|
||||
+AC_MSG_CHECKING([lib389 version sync])
|
||||
+lib389_pyproject="$srcdir/src/lib389/pyproject.toml"
|
||||
+if test -f "$lib389_pyproject"; then
|
||||
+ lib389_version=$(grep -E '^version\s*=' "$lib389_pyproject" | sed 's/.*"\(.*\)".*/\1/')
|
||||
+ if test "x$lib389_version" != "x$RPM_VERSION"; then
|
||||
+ AC_MSG_RESULT([MISMATCH])
|
||||
+ AC_MSG_ERROR([
|
||||
+lib389 version mismatch detected!
|
||||
+ Main project version (VERSION.sh): $RPM_VERSION
|
||||
+ lib389 version (pyproject.toml): $lib389_version
|
||||
+
|
||||
+To fix this, run:
|
||||
+ cd $srcdir/src/lib389 && python3 validate_version.py --update
|
||||
+
|
||||
+lib389 version MUST match the main project version before release.
|
||||
+])
|
||||
+ else
|
||||
+ AC_MSG_RESULT([ok ($lib389_version)])
|
||||
+ fi
|
||||
+else
|
||||
+ AC_MSG_RESULT([MISSING])
|
||||
+ AC_MSG_ERROR([lib389 pyproject.toml not found at $lib389_pyproject - source tree is incomplete])
|
||||
+fi
|
||||
AM_INIT_AUTOMAKE([1.9 foreign subdir-objects dist-bzip2 no-dist-gzip no-define tar-pax])
|
||||
AC_SUBST([RPM_VERSION])
|
||||
AC_SUBST([RPM_RELEASE])
|
||||
--
|
||||
2.52.0
|
||||
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
From 062aa6eab12d00adffa4e46d58722f6c0e5eeac1 Mon Sep 17 00:00:00 2001
|
||||
From 742c12e0247ab64e87da000a4de2f3e5c99044ab Mon Sep 17 00:00:00 2001
|
||||
From: Viktor Ashirov <vashirov@redhat.com>
|
||||
Date: Fri, 9 Jan 2026 11:39:50 +0100
|
||||
Subject: [PATCH] Issue 7172 - Index ordering mismatch after upgrade (#7173)
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
From 6bce8f6e8c985289c4ac1a4f051c291283c0a1ec Mon Sep 17 00:00:00 2001
|
||||
From f5de84e309d5a4435198c9cc9b31b5722979f1ff Mon Sep 17 00:00:00 2001
|
||||
From: Viktor Ashirov <vashirov@redhat.com>
|
||||
Date: Mon, 12 Jan 2026 10:58:02 +0100
|
||||
Subject: [PATCH 9/9] Issue 7172 - (2nd) Index ordering mismatch after upgrade
|
||||
Subject: [PATCH 5/5] Issue 7172 - (2nd) Index ordering mismatch after upgrade
|
||||
(#7180)
|
||||
|
||||
Commit 742c12e0247ab64e87da000a4de2f3e5c99044ab introduced a regression
|
||||
|
|
@ -75,7 +75,7 @@ ExcludeArch: i686
|
|||
|
||||
Summary: 389 Directory Server (%{variant})
|
||||
Name: 389-ds-base
|
||||
Version: 3.1.4
|
||||
Version: 3.2.0
|
||||
Release: %{autorelease -n %{?with_asan:-e asan}}%{?dist}
|
||||
License: GPL-3.0-or-later WITH GPL-3.0-389-ds-base-exception AND (0BSD OR Apache-2.0 OR MIT) AND (Apache-2.0 OR Apache-2.0 WITH LLVM-exception OR MIT) AND (Apache-2.0 OR BSL-1.0) AND (Apache-2.0 OR LGPL-2.1-or-later OR MIT) AND (Apache-2.0 OR MIT OR Zlib) AND (Apache-2.0 OR MIT) AND (CC-BY-4.0 AND MIT) AND (MIT OR Apache-2.0) AND Unicode-3.0 AND (MIT OR CC0-1.0) AND (MIT OR Unlicense) AND 0BSD AND Apache-2.0 AND BSD-2-Clause AND BSD-3-Clause AND ISC AND MIT AND MIT AND ISC AND MPL-2.0 AND PSF-2.0 AND Zlib
|
||||
URL: https://www.port389.org
|
||||
|
|
@ -504,21 +504,18 @@ Source0: https://github.com/389ds/%{name}/releases/download/%{name}-%{v
|
|||
Source2: %{name}-devel.README
|
||||
%if %{with bundle_jemalloc}
|
||||
Source3: https://github.com/jemalloc/%{jemalloc_name}/releases/download/%{jemalloc_ver}/%{jemalloc_name}-%{jemalloc_ver}.tar.bz2
|
||||
Source6: jemalloc-5.3.0_throw_bad_alloc.patch
|
||||
%endif
|
||||
Source4: 389-ds-base.sysusers
|
||||
%if %{with bundle_libdb}
|
||||
Source5: https://fedorapeople.org/groups/389ds/libdb-5.3.28-59.tar.bz2
|
||||
%endif
|
||||
|
||||
Patch: 0001-Issue-7150-Compressed-access-log-rotations-skipped-a.patch
|
||||
Patch: 0002-Sync-lib389-version-to-3.1.4-7161.patch
|
||||
Patch: 0003-Issue-7166-db_config_set-asserts-because-of-dynamic-.patch
|
||||
Patch: 0004-Issue-7160-Add-lib389-version-sync-check-to-configur.patch
|
||||
Patch: 0005-Issue-7096-During-replication-online-total-init-the-.patch
|
||||
Patch: 0006-Issue-Revise-paged-result-search-locking.patch
|
||||
Patch: 0007-Issue-7108-Fix-shutdown-crash-in-entry-cache-destruc.patch
|
||||
Patch: 0008-Issue-7172-Index-ordering-mismatch-after-upgrade-717.patch
|
||||
Patch: 0009-Issue-7172-2nd-Index-ordering-mismatch-after-upgrade.patch
|
||||
Patch: 0001-Issue-7096-During-replication-online-total-init-the-.patch
|
||||
Patch: 0002-Issue-Revise-paged-result-search-locking.patch
|
||||
Patch: 0003-Issue-7108-Fix-shutdown-crash-in-entry-cache-destruc.patch
|
||||
Patch: 0004-Issue-7172-Index-ordering-mismatch-after-upgrade-717.patch
|
||||
Patch: 0005-Issue-7172-2nd-Index-ordering-mismatch-after-upgrade.patch
|
||||
|
||||
%description
|
||||
389 Directory Server is an LDAPv3 compliant server. The base package includes
|
||||
|
|
@ -715,6 +712,7 @@ COCKPIT_FLAGS="--disable-cockpit"
|
|||
|
||||
# Build jemalloc
|
||||
pushd ../%{jemalloc_name}-%{jemalloc_ver}
|
||||
patch -p1 -F3 < %{SOURCE6}
|
||||
%configure \
|
||||
--libdir=%{_libdir}/%{pkgname}/lib \
|
||||
--bindir=%{_libdir}/%{pkgname}/bin \
|
||||
|
|
|
|||
41
jemalloc-5.3.0_throw_bad_alloc.patch
Normal file
41
jemalloc-5.3.0_throw_bad_alloc.patch
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
#commit 3de0c24859f4413bf03448249078169bb50bda0f
|
||||
#Author: divanorama <divanorama@gmail.com>
|
||||
#Date: Thu Sep 29 23:35:59 2022 +0200
|
||||
#
|
||||
# Disable builtin malloc in tests
|
||||
#
|
||||
# With `--with-jemalloc-prefix=` and without `-fno-builtin` or `-O1` both clang and gcc may optimize out `malloc` calls
|
||||
# whose result is unused. Comparing result to NULL also doesn't necessarily count as being used.
|
||||
#
|
||||
# This won't be a problem in most client programs as this only concerns really unused pointers, but in
|
||||
# tests it's important to actually execute allocations.
|
||||
# `-fno-builtin` should disable this optimization for both gcc and clang, and applying it only to tests code shouldn't hopefully be an issue.
|
||||
# Another alternative is to force "use" of result but that'd require more changes and may miss some other optimization-related issues.
|
||||
#
|
||||
# This should resolve https://github.com/jemalloc/jemalloc/issues/2091
|
||||
#
|
||||
#diff --git a/Makefile.in b/Makefile.in
|
||||
#index 6809fb29..a964f07e 100644
|
||||
#--- a/Makefile.in
|
||||
#+++ b/Makefile.in
|
||||
#@@ -458,6 +458,8 @@ $(TESTS_OBJS): $(objroot)test/%.$(O): $(srcroot)test/%.c
|
||||
# $(TESTS_CPP_OBJS): $(objroot)test/%.$(O): $(srcroot)test/%.cpp
|
||||
# $(TESTS_OBJS): CPPFLAGS += -I$(srcroot)test/include -I$(objroot)test/include
|
||||
# $(TESTS_CPP_OBJS): CPPFLAGS += -I$(srcroot)test/include -I$(objroot)test/include
|
||||
#+$(TESTS_OBJS): CFLAGS += -fno-builtin
|
||||
#+$(TESTS_CPP_OBJS): CPPFLAGS += -fno-builtin
|
||||
# ifneq ($(IMPORTLIB),$(SO))
|
||||
# $(CPP_OBJS) $(C_SYM_OBJS) $(C_OBJS) $(C_JET_SYM_OBJS) $(C_JET_OBJS): CPPFLAGS += -DDLLEXPORT
|
||||
# endif
|
||||
diff --git a/src/jemalloc_cpp.cpp b/src/jemalloc_cpp.cpp
|
||||
index fffd6aee..5a682991 100644
|
||||
--- a/src/jemalloc_cpp.cpp
|
||||
+++ b/src/jemalloc_cpp.cpp
|
||||
@@ -93,7 +93,7 @@ handleOOM(std::size_t size, bool nothrow) {
|
||||
}
|
||||
|
||||
if (ptr == nullptr && !nothrow)
|
||||
- std::__throw_bad_alloc();
|
||||
+ throw std::bad_alloc();
|
||||
return ptr;
|
||||
}
|
||||
2
sources
2
sources
|
|
@ -1,3 +1,3 @@
|
|||
SHA512 (jemalloc-5.3.0.tar.bz2) = 22907bb052096e2caffb6e4e23548aecc5cc9283dce476896a2b1127eee64170e3562fa2e7db9571298814a7a2c7df6e8d1fbe152bd3f3b0c1abec22a2de34b1
|
||||
SHA512 (libdb-5.3.28-59.tar.bz2) = 731a434fa2e6487ebb05c458b0437456eb9f7991284beb08cb3e21931e23bdeddddbc95bfabe3a2f9f029fe69cd33a2d4f0f5ce6a9811e9c3b940cb6fde4bf79
|
||||
SHA512 (389-ds-base-3.1.4.tar.bz2) = 17de77a02c848dbb8d364e7bab529726b4c32e466f47d5c2a5bba8d8b55e2a56e2b743a2efa4f820c935b39f770a621146a42443e4f171f8b14c68968155ee2c
|
||||
SHA512 (389-ds-base-3.2.0.tar.bz2) = 9ff6aa56b30863c619f4f324344dca72cc883236bfe8d94520e8469d9e306f54b373ee2504eda18dcb0ecda33f915a3e64a6f3cdaa93a69b74d901caa48545e1
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue