Compare commits

..

2 Commits

Author SHA1 Message Date
Anthony 4505179db6 Improved debugging and error checking using xmlstarlet 2025-03-28 01:20:04 +08:00
Anthony 8533225d00 Added xmlstarlet installation 2025-03-28 01:09:16 +08:00
2 changed files with 501 additions and 83 deletions

View File

@ -24,7 +24,53 @@ ERROR_LOG="${LOG_DIR}/ssl-remover-error.log"
DEBUG_LOG="${LOG_DIR}/ssl-remover-debug.log"
VERBOSE=0
# Validate configuration file exists and is readable
if [[ ! -f "$CONF_FILE" ]]; then
echo "❌ ERROR: Configuration file '$CONF_FILE' does not exist"
exit 1
fi
if [[ ! -r "$CONF_FILE" ]]; then
echo "❌ ERROR: Configuration file '$CONF_FILE' is not readable"
exit 1
fi
# Validate required tools are available
for tool in xmlstarlet xmllint certbot; do
if ! command -v "$tool" >/dev/null 2>&1; then
echo "❌ ERROR: Required tool '$tool' is not installed"
exit 1
fi
done
# === Functions ===
check_command() {
local cmd="$1"
local pkg="$2"
if ! command -v "$cmd" &>/dev/null; then
log "⚠ Required command '$cmd' not found. Attempting to install package '$pkg'..."
# Try dnf first (AlmaLinux)
if command -v dnf &>/dev/null; then
if sudo dnf install -y "$pkg"; then
log "✔ Successfully installed '$pkg' using dnf."
return 0
fi
fi
# Fallback to yum (CentOS)
if command -v yum &>/dev/null; then
if sudo yum install -y "$pkg"; then
log "✔ Successfully installed '$pkg' using yum."
return 0
fi
fi
log "❌ ERROR: Failed to install '$pkg' using either dnf or yum. Exiting."
exit 1
fi
}
setup_logging() {
# Create log directory if it doesn't exist
sudo mkdir -p "$LOG_DIR" || { echo "❌ ERROR: Cannot create log directory '$LOG_DIR'. Check permissions."; exit 1; }
@ -116,11 +162,15 @@ backup_config() {
local timestamp=$(date +%Y%m%d%H%M%S)
local backup_file="${BACKUP_DIR}/httpd_config.pre-removal-${timestamp}.xml"
mkdir -p "$BACKUP_DIR"
# Create backup directory with proper permissions
sudo mkdir -p "$BACKUP_DIR" || { log_error "Failed to create backup directory '$BACKUP_DIR'"; exit 2; }
sudo chown -R "$(whoami)":"$(id -gn)" "$BACKUP_DIR"
sudo chmod 755 "$BACKUP_DIR"
if cp "$CONF_FILE" "$backup_file"; then
log "Config backup saved to $backup_file"
else
log "❌ ERROR: Failed to create backup '$backup_file'. Exiting."
log_error "Failed to create backup '$backup_file'. Exiting."
exit 2
fi
}
@ -129,21 +179,68 @@ remove_certificate() {
local domain="$1"
log "Checking for certificate for domain '$domain'..."
if certbot certificates | grep -q "Domains: $domain"; then
log "Found certificate for '$domain'. Proceeding with removal..."
if certbot delete --cert-name "$domain" --non-interactive; then
# Remove all certificate files
rm -rf "/etc/letsencrypt/live/$domain"*
rm -rf "/etc/letsencrypt/archive/$domain"*
log_success "Certificate successfully removed for '$domain'"
log_verbose "Removed certificate files from /etc/letsencrypt/live/$domain and /etc/letsencrypt/archive/$domain"
else
log_error "Failed to remove certificate for '$domain'"
return 1
fi
else
log "No certificate found for '$domain'. Skipping removal."
# Validate domain format first
if ! validate_domain "$domain"; then
log_error "Invalid domain format: '$domain'"
return 1
fi
# Check if certbot is available and working
if ! certbot --version >/dev/null 2>&1; then
log_error "certbot is not available or not working properly"
return 1
fi
# Check for certificate existence with better error handling
if ! certbot certificates | grep -q "Domains: $domain"; then
log "No certificate found for '$domain'. Skipping removal."
return 0
fi
log "Found certificate for '$domain'. Proceeding with removal..."
# Create backup of certificate files before removal
local cert_backup_dir="${BACKUP_DIR}/certificates/${domain}_$(date +%Y%m%d%H%M%S)"
sudo mkdir -p "$cert_backup_dir"
if [[ -d "/etc/letsencrypt/live/$domain" ]]; then
sudo cp -r "/etc/letsencrypt/live/$domain" "$cert_backup_dir/"
fi
if [[ -d "/etc/letsencrypt/archive/$domain" ]]; then
sudo cp -r "/etc/letsencrypt/archive/$domain" "$cert_backup_dir/"
fi
# Attempt to remove certificate
if ! certbot delete --cert-name "$domain" --non-interactive; then
log_error "Failed to remove certificate for '$domain'"
return 1
fi
# Verify certificate removal
if certbot certificates | grep -q "Domains: $domain"; then
log_error "Certificate removal verification failed for '$domain'"
return 1
fi
# Remove certificate files with verification
local files_removed=0
if [[ -d "/etc/letsencrypt/live/$domain" ]]; then
sudo rm -rf "/etc/letsencrypt/live/$domain"*
((files_removed++))
fi
if [[ -d "/etc/letsencrypt/archive/$domain" ]]; then
sudo rm -rf "/etc/letsencrypt/archive/$domain"*
((files_removed++))
fi
if [[ $files_removed -eq 0 ]]; then
log "No certificate files found to remove for '$domain'"
else
log_success "Certificate successfully removed for '$domain'"
log_verbose "Removed certificate files from /etc/letsencrypt/live/$domain and /etc/letsencrypt/archive/$domain"
fi
return 0
}
cleanup_listeners() {
@ -151,6 +248,17 @@ cleanup_listeners() {
local listener_name="HTTPS-$domain"
log "Starting cleanup for listener '$listener_name'..."
# Validate domain format
if ! validate_domain "$domain"; then
log_error "Invalid domain format: '$domain'"
return 1
fi
# Create backup directory with proper permissions
sudo mkdir -p "$BACKUP_DIR" || { log_error "Failed to create backup directory '$BACKUP_DIR'"; return 1; }
sudo chown -R "$(whoami)":"$(id -gn)" "$BACKUP_DIR"
sudo chmod 755 "$BACKUP_DIR"
# First backup the config
local backup_file="${BACKUP_DIR}/httpd_config.pre-removal-$(date +%Y%m%d%H%M%S).xml"
cp "$CONF_FILE" "$backup_file" || {
@ -159,15 +267,43 @@ cleanup_listeners() {
}
log_verbose "Created backup at: $backup_file"
# Remove the entire listener element with all its children
sudo xmlstarlet ed -L \
-d "//listenerList/listener[name='$listener_name']" \
"$CONF_FILE" || {
log_error "Failed to remove listener '$listener_name'"
cp "$backup_file" "$CONF_FILE"
return 1
}
log_verbose "Removed listener element: $listener_name"
# Verify listener exists before attempting removal
if ! awk '
/<listener>/ { in_listener=1; buffer="" }
in_listener { buffer = buffer $0 ORS }
/<\/listener>/ {
if (in_listener && buffer ~ /'"$domain"'/) {
print buffer
}
in_listener=0
}' "$CONF_FILE" >/dev/null 2>&1; then
log "Listener for '$domain' not found in configuration. Skipping removal."
rm -f "$backup_file"
return 0
fi
# Remove the listener block using xmlstarlet
if ! sudo xmlstarlet ed --inplace -d "//listener[contains(., '$domain')]" "$CONF_FILE"; then
log_error "Failed to remove listener for '$domain'"
cp "$backup_file" "$CONF_FILE"
return 1
fi
log_verbose "Removed listener block for domain: $domain"
# Verify the listener was actually removed
if awk '
/<listener>/ { in_listener=1; buffer="" }
in_listener { buffer = buffer $0 ORS }
/<\/listener>/ {
if (in_listener && buffer ~ /'"$domain"'/) {
print buffer
}
in_listener=0
}' "$CONF_FILE" >/dev/null 2>&1; then
log_error "Listener for '$domain' still exists after removal attempt"
cp "$backup_file" "$CONF_FILE"
return 1
fi
# Validate XML after removal
if ! xmllint --noout "$CONF_FILE" 2>/dev/null; then
@ -177,36 +313,162 @@ cleanup_listeners() {
fi
log_verbose "XML validation passed after listener removal"
# Clean up any orphaned vhostMapList elements
sudo xmlstarlet ed -L \
-d "//vhostMapList[not(parent::listener)]" \
"$CONF_FILE" || {
log_error "Failed to clean up orphaned vhostMapList"
cp "$backup_file" "$CONF_FILE"
return 1
}
log_verbose "Cleaned up orphaned vhostMapList elements"
# Set proper permissions on the config file
sudo chown litespeed:litespeed "$CONF_FILE"
sudo chmod 644 "$CONF_FILE"
# Validate XML after cleanup
if ! xmllint --noout "$CONF_FILE" 2>/dev/null; then
log_error "Invalid XML structure after cleanup. Restoring backup..."
cp "$backup_file" "$CONF_FILE"
log_success "Successfully removed listener for '$domain'"
rm -f "$backup_file"
return 0
}
verify_cleanup_state() {
local domain="$1"
local listener_name="HTTPS-$domain"
# Check certificate state
local cert_exists=0
if certbot certificates | grep -q "Domains: $domain"; then
cert_exists=1
fi
# Check listener state - improved check
local listener_exists=0
if xmlstarlet sel -t -c "//listenerList/listener[name='$listener_name']" "$CONF_FILE" >/dev/null 2>&1; then
listener_exists=1
fi
# Log current state
log_verbose "Current state for domain '$domain':"
log_verbose "Certificate exists: $cert_exists"
log_verbose "Listener exists: $listener_exists"
# Return state code
echo "$cert_exists$listener_exists"
}
validate_xml_structure() {
local file="$1"
local validation_errors=0
# Check for basic XML structure with detailed error reporting
if ! xmlstarlet val --err --well-formed "$file" 2> >(while IFS= read -r line; do
log_error "XML Structure Error: $line"
validation_errors=$((validation_errors + 1))
done); then
log_error "Invalid XML structure in $file"
validation_errors=$((validation_errors + 1))
fi
# Check for required root elements
if ! xmlstarlet sel -Q -t -v "//httpServerConfig" "$file" >/dev/null 2>&1; then
log_error "Missing httpServerConfig root element in $file"
validation_errors=$((validation_errors + 1))
fi
# Check for required sections with detailed reporting
local required_sections=("listenerList" "virtualHostList")
for section in "${required_sections[@]}"; do
if ! xmlstarlet sel -Q -t -v "//$section" "$file" >/dev/null 2>&1; then
log_error "Missing required section '$section' in $file"
# Additional check for malformed section
if xmlstarlet sel -Q -t -c "//*[contains(name(), '$section')]" "$file" >/dev/null 2>&1; then
log_error "Found malformed <$section> element (possibly with incorrect case or attributes)"
fi
validation_errors=$((validation_errors + 1))
fi
done
# Check for orphaned elements with detailed reporting
if xmlstarlet sel -Q -t -v "//vhostMapList[not(parent::listener)]" "$file" >/dev/null 2>&1; then
log_error "Found orphaned vhostMapList elements in $file"
# Report the orphaned elements
xmlstarlet sel -Q -t -m "//vhostMapList[not(parent::listener)]" -v "concat('Orphaned vhostMapList: ', .)" -n "$file" 2>/dev/null | while read -r line; do
log_error "$line"
done
validation_errors=$((validation_errors + 1))
fi
# Check for orphaned listeners
if xmlstarlet sel -Q -t -v "//listener[not(parent::listenerList)]" "$file" >/dev/null 2>&1; then
log_error "Found orphaned listener elements in $file"
# Report the orphaned listeners
xmlstarlet sel -Q -t -m "//listener[not(parent::listenerList)]" -v "concat('Orphaned Listener: ', name)" -n "$file" 2>/dev/null | while read -r line; do
log_error "$line"
done
validation_errors=$((validation_errors + 1))
fi
if [[ $validation_errors -eq 0 ]]; then
log_success "XML structure validation passed for '$file'"
return 0
else
log_error "Found $validation_errors XML structure issue(s) in '$file'"
return 1
fi
log_verbose "XML validation passed after cleanup"
}
log_success "Successfully removed listener '$listener_name' and cleaned up XML structure"
rm -f "$backup_file"
cleanup_xml() {
local file="$1"
local temp_file="${file}.tmp"
# Create temporary copy
cp "$file" "$temp_file" || {
log_error "Failed to create temporary file for XML cleanup"
return 1
}
# Remove orphaned elements
if ! xmlstarlet ed -L \
-d "//vhostMapList[not(parent::listener)]" \
-d "//listener[not(.//vhostMapList)]" \
"$temp_file"; then
log_error "Failed to clean up orphaned elements"
rm -f "$temp_file"
return 1
fi
# Validate cleaned XML
if ! validate_xml_structure "$temp_file"; then
log_error "Invalid XML structure after cleanup"
rm -f "$temp_file"
return 1
fi
# Move temporary file to actual config file
if ! mv "$temp_file" "$file"; then
log_error "Failed to update configuration file"
rm -f "$temp_file"
return 1
fi
# Set proper permissions
sudo chown litespeed:litespeed "$file"
sudo chmod 644 "$file"
return 0
}
validate_xml() {
log "Validating XML configuration..."
if ! sudo xmllint --noout "$CONF_FILE" 2>/dev/null; then
log "❌ ERROR: Invalid XML configuration after cleanup. Check backups."
# First check with xmlstarlet with detailed error reporting
if ! xmlstarlet val --err --well-formed "$CONF_FILE" 2> >(while IFS= read -r line; do
log_error "XML Validation Error: $line"
done); then
log "❌ ERROR: XML configuration is not well-formed according to xmlstarlet. Check backups."
return 1
fi
log "✔ XML configuration is valid."
# Then check with xmllint for additional validation
if ! xmllint --noout "$CONF_FILE" 2> >(while IFS= read -r line; do
log_error "XML Validation Error (xmllint): $line"
done); then
log "❌ ERROR: XML configuration is invalid according to xmllint. Check backups."
return 1
fi
log "✔ XML configuration is valid (verified by both xmlstarlet and xmllint)."
return 0
}
@ -229,6 +491,12 @@ main() {
setup_logging
log "Starting SSL Removal Process"
# Check and install required dependencies
log_verbose "Checking dependencies..."
check_command "xmlstarlet" "xmlstarlet"
check_command "xmllint" "libxml2-utils"
check_command "certbot" "certbot"
# Parse parameters
while [[ $# -gt 0 ]]; do
case "$1" in
@ -268,18 +536,51 @@ main() {
continue
fi
# Remove certificate first
if ! remove_certificate "$domain"; then
log "⚠ Warning: Failed to remove certificate for '$domain'. Continuing with cleanup..."
fi
# Clean up listeners and configurations
if ! cleanup_listeners "$domain"; then
log_error "Failed to clean up listeners for '$domain'"
continue
fi
# Check current state
local state=$(verify_cleanup_state "$domain")
log_verbose "Current state code: $state"
case "$state" in
"00") # No certificate, no listener
log "Domain '$domain' is already clean. No action needed."
continue
;;
"01") # No certificate, listener exists
log "Found orphaned listener for '$domain'. Removing..."
if ! cleanup_listeners "$domain"; then
log_error "Failed to remove orphaned listener for '$domain'"
continue
fi
;;
"10") # Certificate exists, no listener
log "Found orphaned certificate for '$domain'. Removing..."
if ! remove_certificate "$domain"; then
log_error "Failed to remove orphaned certificate for '$domain'"
continue
fi
;;
"11") # Both certificate and listener exist
log "Found both certificate and listener for '$domain'. Removing both..."
if ! remove_certificate "$domain"; then
log "⚠ Warning: Failed to remove certificate for '$domain'. Continuing with listener cleanup..."
fi
if ! cleanup_listeners "$domain"; then
log_error "Failed to clean up listeners for '$domain'"
continue
fi
;;
esac
done
# Validate and clean up XML structure
if ! validate_xml_structure "$CONF_FILE"; then
log "Cleaning up XML structure..."
if ! cleanup_xml "$CONF_FILE"; then
log_error "Failed to clean up XML structure"
exit 1
fi
fi
# Validate final XML configuration
if validate_xml; then
restart_litespeed

View File

@ -103,15 +103,28 @@ log_success() {
check_command() {
local cmd="$1"
local apt_pkg="$2"
local pkg="$2"
if ! command -v "$cmd" &>/dev/null; then
log "⚠ Required command '$cmd' not found. Attempting to install package '$apt_pkg'..."
if sudo yum install -y "$apt_pkg"; then
log "✔ Successfully installed '$apt_pkg'."
else
log "❌ ERROR: Failed to install '$apt_pkg'. Exiting."
exit 1
log "⚠ Required command '$cmd' not found. Attempting to install package '$pkg'..."
# Try dnf first (AlmaLinux)
if command -v dnf &>/dev/null; then
if sudo dnf install -y "$pkg"; then
log "✔ Successfully installed '$pkg' using dnf."
return 0
fi
fi
# Fallback to yum (CentOS)
if command -v yum &>/dev/null; then
if sudo yum install -y "$pkg"; then
log "✔ Successfully installed '$pkg' using yum."
return 0
fi
fi
log "❌ ERROR: Failed to install '$pkg' using either dnf or yum. Exiting."
exit 1
fi
}
@ -290,6 +303,115 @@ apply_and_restart() {
fi
}
validate_xml_structure() {
local file_to_check="$1"
log_verbose "Performing comprehensive XML validation on '$file_to_check'..."
local validation_errors=0
# 1. Check if file is well-formed with detailed error reporting
if ! xmlstarlet val --err --well-formed "$file_to_check" 2> >(while IFS= read -r line; do
log_error "XML Validation Error: $line"
validation_errors=$((validation_errors + 1))
done); then
log_error "File '$file_to_check' is not well-formed XML"
validation_errors=$((validation_errors + 1))
fi
# 2. Check for required root elements
if ! xmlstarlet sel -Q -t -c "//httpServerConfig" "$file_to_check" 2>/dev/null; then
log_error "Missing required root element <httpServerConfig>"
validation_errors=$((validation_errors + 1))
fi
# 3. Check for required sections with detailed error reporting
local required_sections=("listenerList" "virtualHostList" "extProcessorList")
for section in "${required_sections[@]}"; do
if ! xmlstarlet sel -Q -t -c "//$section" "$file_to_check" 2>/dev/null; then
log_error "Missing required section <$section>"
# Additional check for malformed section
if xmlstarlet sel -Q -t -c "//*[contains(name(), '$section')]" "$file_to_check" 2>/dev/null; then
log_error "Found malformed <$section> element (possibly with incorrect case or attributes)"
fi
validation_errors=$((validation_errors + 1))
fi
done
# 4. Check for orphaned elements with detailed reporting
if xmlstarlet sel -Q -t -c "//listener[not(parent::listenerList)]" "$file_to_check" 2>/dev/null; then
log_error "Found orphaned <listener> elements"
# Report the orphaned listener names
xmlstarlet sel -Q -t -m "//listener[not(parent::listenerList)]" -v "concat('Orphaned Listener: ', name)" -n "$file_to_check" 2>/dev/null | while read -r line; do
log_error "$line"
done
validation_errors=$((validation_errors + 1))
fi
if xmlstarlet sel -Q -t -c "//virtualHost[not(parent::virtualHostList)]" "$file_to_check" 2>/dev/null; then
log_error "Found orphaned <virtualHost> elements"
# Report the orphaned virtualHost names
xmlstarlet sel -Q -t -m "//virtualHost[not(parent::virtualHostList)]" -v "concat('Orphaned VirtualHost: ', name)" -n "$file_to_check" 2>/dev/null | while read -r line; do
log_error "$line"
done
validation_errors=$((validation_errors + 1))
fi
# 5. Validate listener structure with detailed reporting
xmlstarlet sel -Q -t -m "//listener" -v "concat('Checking listener: ', name)" -n "$file_to_check" 2>/dev/null | while read -r listener; do
if [[ -n "$listener" ]]; then
log_verbose "$listener"
# Check if listener has required attributes
if ! xmlstarlet sel -Q -t -c "//listener[name='${listener#Checking listener: }']/address" "$file_to_check" 2>/dev/null; then
log_error "Listener '${listener#Checking listener: }' is missing required <address> element"
validation_errors=$((validation_errors + 1))
fi
# Additional check for SSL listeners
if [[ "${listener#Checking listener: }" == HTTPS-* ]]; then
if ! xmlstarlet sel -Q -t -c "//listener[name='${listener#Checking listener: }']/certFile" "$file_to_check" 2>/dev/null; then
log_error "HTTPS Listener '${listener#Checking listener: }' is missing required <certFile> element"
validation_errors=$((validation_errors + 1))
fi
if ! xmlstarlet sel -Q -t -c "//listener[name='${listener#Checking listener: }']/keyFile" "$file_to_check" 2>/dev/null; then
log_error "HTTPS Listener '${listener#Checking listener: }' is missing required <keyFile> element"
validation_errors=$((validation_errors + 1))
fi
fi
fi
done
# Return result
if [[ "$validation_errors" -eq 0 ]]; then
log_success "XML structure validation passed for '$file_to_check'"
return 0
else
log_error "Found $validation_errors XML structure issue(s) in '$file_to_check'"
return 1
fi
}
cleanup_xml() {
local file_to_clean="$1"
log "Performing XML cleanup on '$file_to_clean'..."
# 1. Remove empty nodes
xmlstarlet ed -L -d '//*[not(node())]' "$file_to_clean" || log "Warning: Failed to remove empty nodes"
# 2. Remove orphaned elements
xmlstarlet ed -L -d '//listener[not(parent::listenerList)]' "$file_to_clean" || log "Warning: Failed to remove orphaned listeners"
xmlstarlet ed -L -d '//virtualHost[not(parent::virtualHostList)]' "$file_to_clean" || log "Warning: Failed to remove orphaned virtualHosts"
# 3. Clean up whitespace
xmlstarlet ed -L -d '//text()[normalize-space()=""]' "$file_to_clean" || log "Warning: Failed to clean whitespace"
# 4. Validate after cleanup
if ! validate_xml_structure "$file_to_clean"; then
log_error "XML validation failed after cleanup"
return 1
fi
log "XML cleanup completed for '$file_to_clean'"
return 0
}
# === Main Script Logic ===
main() {
# Setup logging first
@ -311,30 +433,28 @@ main() {
log "Backing up '$CONF_FILE' to '$BACKUP_FILE'..."
sudo cp -a "$CONF_FILE" "$BACKUP_FILE"
# Initial Validation
log_verbose "Running initial xmllint validation on '$CONF_FILE'..."
if sudo xmllint --noout "$CONF_FILE" 2>/dev/null; then
log_success "Initial Check: '$CONF_FILE' is valid XML"
INITIAL_FILE_VALID=1
# Initial Validation using xmlstarlet
log_verbose "Running initial xmlstarlet validation on '$CONF_FILE'..."
if xmlstarlet val --well-formed "$CONF_FILE" 2>/dev/null; then
log_success "Initial Check: '$CONF_FILE' is well-formed XML"
if validate_xml_structure "$CONF_FILE"; then
log_success "Initial Check: '$CONF_FILE' has valid structure"
INITIAL_FILE_VALID=1
else
log_error "Initial Check: '$CONF_FILE' has structural issues"
fi
else
log_error "Initial Check: '$CONF_FILE' is invalid XML. Will attempt recovery/cleanup"
log_error "Initial Check: '$CONF_FILE' is not well-formed XML"
fi
# Recovery Attempt
if [[ "$INITIAL_FILE_VALID" -eq 0 ]]; then
log "Attempting automatic recovery with 'xmllint --recover'..."
sudo rm -f "$RECOVERY_TMP_FILE"
if xmllint --recover "$CONF_FILE" 2>/dev/null > "$RECOVERY_TMP_FILE"; then
if sudo xmllint --noout "$RECOVERY_TMP_FILE" 2>/dev/null; then
log_success "'xmllint --recover' produced structurally valid XML output"
RECOVERY_PRODUCED_VALID_XML=1
else
log_error "'xmllint --recover' ran but the output is STILL invalid XML. Discarding recovery"
sudo rm -f "$RECOVERY_TMP_FILE"
fi
log "Attempting XML cleanup..."
if cleanup_xml "$CONF_FILE"; then
log_success "XML cleanup completed successfully"
INITIAL_FILE_VALID=1
else
log_error "'xmllint --recover' command failed or produced errors. Discarding recovery"
sudo rm -f "$RECOVERY_TMP_FILE"
log_error "XML cleanup failed"
fi
fi
@ -348,11 +468,8 @@ main() {
if [[ "$INITIAL_FILE_VALID" -eq 1 ]]; then
FINAL_CONFIG_SOURCE_FILE="$CONF_FILE"
FINAL_CONFIG_SOURCE_DESC="original"
elif [[ "$RECOVERY_PRODUCED_VALID_XML" -eq 1 ]]; then
FINAL_CONFIG_SOURCE_FILE="$RECOVERY_TMP_FILE"
FINAL_CONFIG_SOURCE_DESC="recovered"
else
log_error "Neither original nor recovered file is valid. Falling back to default or minimal config"
log_error "Neither original nor cleaned file is valid. Falling back to default or minimal config"
FINAL_EXIT_CODE=2
fi