Merge pull request #1622 from master3395/v2.5.5-dev
Implement dynamic PHP version detection and configuration updates
This commit is contained in:
commit
ffaa0ca63d
|
|
@ -175,6 +175,52 @@ curl --silent https://cyberpanel.sh/misc/faq.sh | sudo -u nobody bash | less -r
|
|||
exit
|
||||
}
|
||||
|
||||
detect_default_php() {
|
||||
# Detect default PHP version dynamically
|
||||
# Priority: Check symlink, then find highest available version
|
||||
|
||||
local php_version=""
|
||||
local php_version_formatted=""
|
||||
|
||||
# Method 1: Check default PHP symlink
|
||||
if [[ -L /usr/local/lscp/fcgi-bin/lsphp ]]; then
|
||||
local default_php_path=$(readlink -f /usr/local/lscp/fcgi-bin/lsphp 2>/dev/null)
|
||||
if [[ -n "$default_php_path" ]]; then
|
||||
# Extract version from path like /usr/local/lsws/lsphp82/bin/lsphp
|
||||
# Use sed for better portability (works on all systems)
|
||||
php_version=$(echo "$default_php_path" | sed -n 's|.*/lsphp\([0-9][0-9]\)/.*|\1|p')
|
||||
if [[ -n "$php_version" ]] && [[ ${#php_version} -ge 2 ]]; then
|
||||
php_version_formatted="${php_version:0:1}.${php_version:1}"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# Method 2: Find highest available PHP version (fallback)
|
||||
if [[ -z "$php_version" ]]; then
|
||||
# Priority: 85, 84, 83, 82, 81, 80, 74, 73, 72 (newest to oldest, supporting 7.4-8.5)
|
||||
local php_versions=('85' '84' '83' '82' '81' '80' '74' '73' '72')
|
||||
for ver in "${php_versions[@]}"; do
|
||||
if [[ -d "/usr/local/lsws/lsphp${ver}" ]] && [[ -f "/usr/local/lsws/lsphp${ver}/bin/lsphp" ]]; then
|
||||
php_version="$ver"
|
||||
if [[ ${#ver} -ge 2 ]]; then
|
||||
php_version_formatted="${ver:0:1}.${ver:1}"
|
||||
fi
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
# Fallback to PHP 7.4 if nothing found (backwards compatibility)
|
||||
if [[ -z "$php_version" ]]; then
|
||||
if [[ -d "/usr/local/lsws/lsphp74" ]]; then
|
||||
php_version="74"
|
||||
php_version_formatted="7.4"
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "$php_version|$php_version_formatted"
|
||||
}
|
||||
|
||||
addons() {
|
||||
echo -e "\nPlease choose:"
|
||||
echo -e "\n1. Install Memcached extension for PHP."
|
||||
|
|
@ -205,29 +251,49 @@ addons() {
|
|||
}
|
||||
|
||||
phpmyadmin_limits() {
|
||||
echo -e "This will change following parameters for PHP 7.3:"
|
||||
# Detect default PHP version dynamically
|
||||
local php_info=$(detect_default_php)
|
||||
local php_version=$(echo "$php_info" | cut -d'|' -f1)
|
||||
local php_version_formatted=$(echo "$php_info" | cut -d'|' -f2)
|
||||
|
||||
if [[ -z "$php_version" ]] || [[ -z "$php_version_formatted" ]]; then
|
||||
echo -e "\nError: Could not detect PHP version. Please ensure PHP is installed."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo -e "This will change following parameters for PHP ${php_version_formatted}:"
|
||||
echo -e "Post Max Size from default 8M to 500M"
|
||||
echo -e "Upload Max Filesize from default 2M to 500M"
|
||||
echo -e "Memory Limit from default 128M to 768M"
|
||||
echo -e "Max Execution Time from default 30 to 600"
|
||||
echo -e "\nPlease note this will also apply to all sites use PHP 7.3"
|
||||
echo -e "\nPlease note this will also apply to all sites using PHP ${php_version_formatted}"
|
||||
printf "%s" "Please confirm to proceed: [Y/n]: "
|
||||
read TMP_YN
|
||||
if [[ $TMP_YN == "Y" ]] || [[ $TMP_YN == "y" ]] ; then
|
||||
|
||||
local php_ini_path=""
|
||||
|
||||
# Determine php.ini path based on OS and PHP version
|
||||
if [[ "$SERVER_OS" == "CentOS" ]] || [[ "$SERVER_OS" == "openEuler" ]] ; then
|
||||
php_ini_path="/usr/local/lsws/lsphp73/etc/php.ini"
|
||||
fi
|
||||
|
||||
if [[ "$SERVER_OS" == "Ubuntu" ]] ; then
|
||||
php_ini_path="/usr/local/lsws/lsphp73/etc/php/7.3/litespeed/php.ini"
|
||||
fi
|
||||
sed -i 's|post_max_size = 8M|post_max_size = 500M|g' $php_ini_path
|
||||
sed -i 's|upload_max_filesize = 2M|upload_max_filesize = 500M |g' $php_ini_path
|
||||
sed -i 's|memory_limit = 128M|memory_limit = 768M|g' $php_ini_path
|
||||
sed -i 's|max_execution_time = 30|max_execution_time = 600|g' $php_ini_path
|
||||
systemctl restart lscpd
|
||||
echo "Change applied..."
|
||||
php_ini_path="/usr/local/lsws/lsphp${php_version}/etc/php.ini"
|
||||
elif [[ "$SERVER_OS" == "Ubuntu" ]] ; then
|
||||
php_ini_path="/usr/local/lsws/lsphp${php_version}/etc/php/${php_version_formatted}/litespeed/php.ini"
|
||||
fi
|
||||
|
||||
# Verify php.ini file exists
|
||||
if [[ ! -f "$php_ini_path" ]]; then
|
||||
echo -e "\nError: PHP configuration file not found at: $php_ini_path"
|
||||
echo -e "Please verify PHP ${php_version_formatted} is properly installed."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Apply changes
|
||||
sed -i 's|post_max_size = 8M|post_max_size = 500M|g' "$php_ini_path"
|
||||
sed -i 's|upload_max_filesize = 2M|upload_max_filesize = 500M |g' "$php_ini_path"
|
||||
sed -i 's|memory_limit = 128M|memory_limit = 768M|g' "$php_ini_path"
|
||||
sed -i 's|max_execution_time = 30|max_execution_time = 600|g' "$php_ini_path"
|
||||
systemctl restart lscpd
|
||||
echo "Change applied to PHP ${php_version_formatted} configuration..."
|
||||
else
|
||||
echo -e "Please enter Y or n."
|
||||
exit
|
||||
|
|
@ -235,14 +301,21 @@ phpmyadmin_limits() {
|
|||
}
|
||||
|
||||
install_php_redis() {
|
||||
# Install Redis extension for PHP 7.4-8.5 (backwards compatible)
|
||||
if [[ $SERVER_OS == "CentOS" ]] ; then
|
||||
yum install -y lsphp74-redis lsphp73-redis lsphp72-redis lsphp71-redis lsphp70-redis lsphp56-redis lsphp55-redis lsphp54-redis
|
||||
yum install -y lsphp85-redis lsphp84-redis lsphp83-redis lsphp82-redis lsphp81-redis lsphp80-redis lsphp74-redis lsphp73-redis lsphp72-redis lsphp71-redis lsphp70-redis lsphp56-redis lsphp55-redis lsphp54-redis 2>/dev/null || \
|
||||
yum install -y lsphp84-redis lsphp83-redis lsphp82-redis lsphp81-redis lsphp80-redis lsphp74-redis lsphp73-redis lsphp72-redis lsphp71-redis lsphp70-redis lsphp56-redis lsphp55-redis lsphp54-redis 2>/dev/null || \
|
||||
yum install -y lsphp83-redis lsphp82-redis lsphp81-redis lsphp80-redis lsphp74-redis lsphp73-redis lsphp72-redis lsphp71-redis lsphp70-redis lsphp56-redis lsphp55-redis lsphp54-redis
|
||||
fi
|
||||
if [[ $SERVER_OS == "Ubuntu" ]] ; then
|
||||
DEBIAN_FRONTEND=noninteractive apt install -y lsphp74-redis lsphp73-redis lsphp72-redis lsphp71-redis lsphp70-redis
|
||||
DEBIAN_FRONTEND=noninteractive apt install -y lsphp85-redis lsphp84-redis lsphp83-redis lsphp82-redis lsphp81-redis lsphp80-redis lsphp74-redis lsphp73-redis lsphp72-redis lsphp71-redis lsphp70-redis 2>/dev/null || \
|
||||
DEBIAN_FRONTEND=noninteractive apt install -y lsphp84-redis lsphp83-redis lsphp82-redis lsphp81-redis lsphp80-redis lsphp74-redis lsphp73-redis lsphp72-redis lsphp71-redis lsphp70-redis 2>/dev/null || \
|
||||
DEBIAN_FRONTEND=noninteractive apt install -y lsphp83-redis lsphp82-redis lsphp81-redis lsphp80-redis lsphp74-redis lsphp73-redis lsphp72-redis lsphp71-redis lsphp70-redis
|
||||
fi
|
||||
if [[ $SERVER_OS == "openEuler" ]] ; then
|
||||
dnf install -y lsphp74-redis lsphp73-redis lsphp72-redis lsphp71-redis
|
||||
dnf install -y lsphp85-redis lsphp84-redis lsphp83-redis lsphp82-redis lsphp81-redis lsphp80-redis lsphp74-redis lsphp73-redis lsphp72-redis lsphp71-redis 2>/dev/null || \
|
||||
dnf install -y lsphp84-redis lsphp83-redis lsphp82-redis lsphp81-redis lsphp80-redis lsphp74-redis lsphp73-redis lsphp72-redis lsphp71-redis 2>/dev/null || \
|
||||
dnf install -y lsphp83-redis lsphp82-redis lsphp81-redis lsphp80-redis lsphp74-redis lsphp73-redis lsphp72-redis lsphp71-redis
|
||||
fi
|
||||
echo -e "\nRedis extension for PHP has been installed..."
|
||||
exit
|
||||
|
|
@ -359,14 +432,21 @@ read TMP_YN
|
|||
}
|
||||
|
||||
install_php_memcached() {
|
||||
# Install Memcached extension for PHP 7.4-8.5 (backwards compatible)
|
||||
if [[ $SERVER_OS == "CentOS" ]] ; then
|
||||
yum install -y lsphp74-memcached lsphp73-memcached lsphp72-memcached lsphp71-memcached lsphp70-memcached lsphp56-pecl-memcached lsphp55-pecl-memcached lsphp54-pecl-memcached
|
||||
yum install -y lsphp85-memcached lsphp84-memcached lsphp83-memcached lsphp82-memcached lsphp81-memcached lsphp80-memcached lsphp74-memcached lsphp73-memcached lsphp72-memcached lsphp71-memcached lsphp70-memcached lsphp56-pecl-memcached lsphp55-pecl-memcached lsphp54-pecl-memcached 2>/dev/null || \
|
||||
yum install -y lsphp84-memcached lsphp83-memcached lsphp82-memcached lsphp81-memcached lsphp80-memcached lsphp74-memcached lsphp73-memcached lsphp72-memcached lsphp71-memcached lsphp70-memcached lsphp56-pecl-memcached lsphp55-pecl-memcached lsphp54-pecl-memcached 2>/dev/null || \
|
||||
yum install -y lsphp83-memcached lsphp82-memcached lsphp81-memcached lsphp80-memcached lsphp74-memcached lsphp73-memcached lsphp72-memcached lsphp71-memcached lsphp70-memcached lsphp56-pecl-memcached lsphp55-pecl-memcached lsphp54-pecl-memcached
|
||||
fi
|
||||
if [[ $SERVER_OS == "Ubuntu" ]] ; then
|
||||
DEBIAN_FRONTEND=noninteractive apt install -y lsphp74-memcached lsphp73-memcached lsphp72-memcached lsphp71-memcached lsphp70-memcached
|
||||
DEBIAN_FRONTEND=noninteractive apt install -y lsphp85-memcached lsphp84-memcached lsphp83-memcached lsphp82-memcached lsphp81-memcached lsphp80-memcached lsphp74-memcached lsphp73-memcached lsphp72-memcached lsphp71-memcached lsphp70-memcached 2>/dev/null || \
|
||||
DEBIAN_FRONTEND=noninteractive apt install -y lsphp84-memcached lsphp83-memcached lsphp82-memcached lsphp81-memcached lsphp80-memcached lsphp74-memcached lsphp73-memcached lsphp72-memcached lsphp71-memcached lsphp70-memcached 2>/dev/null || \
|
||||
DEBIAN_FRONTEND=noninteractive apt install -y lsphp83-memcached lsphp82-memcached lsphp81-memcached lsphp80-memcached lsphp74-memcached lsphp73-memcached lsphp72-memcached lsphp71-memcached lsphp70-memcached
|
||||
fi
|
||||
if [[ $SERVER_OS == "openEuler" ]] ; then
|
||||
dnf install -y lsphp74-memcached lsphp73-memcached lsphp72-memcached lsphp71-memcached
|
||||
dnf install -y lsphp85-memcached lsphp84-memcached lsphp83-memcached lsphp82-memcached lsphp81-memcached lsphp80-memcached lsphp74-memcached lsphp73-memcached lsphp72-memcached lsphp71-memcached 2>/dev/null || \
|
||||
dnf install -y lsphp84-memcached lsphp83-memcached lsphp82-memcached lsphp81-memcached lsphp80-memcached lsphp74-memcached lsphp73-memcached lsphp72-memcached lsphp71-memcached 2>/dev/null || \
|
||||
dnf install -y lsphp83-memcached lsphp82-memcached lsphp81-memcached lsphp80-memcached lsphp74-memcached lsphp73-memcached lsphp72-memcached lsphp71-memcached
|
||||
fi
|
||||
echo -e "\nMemcached extension for PHP has been installed..."
|
||||
exit
|
||||
|
|
|
|||
|
|
@ -829,13 +829,196 @@ class preFlightsChecks:
|
|||
if any(distro in content for distro in ['red hat', 'almalinux', 'rocky', 'cloudlinux', 'centos']):
|
||||
return 'rhel9'
|
||||
|
||||
# Default to rhel9 if can't detect (safer default for newer systems)
|
||||
self.stdOut("WARNING: Could not detect platform, defaulting to rhel9", 1)
|
||||
return 'rhel9'
|
||||
# Default to rhel8 if can't detect (safer default - rhel9 binaries may require GLIBC 2.35)
|
||||
self.stdOut("WARNING: Could not detect platform, defaulting to rhel8", 1)
|
||||
return 'rhel8'
|
||||
|
||||
except Exception as msg:
|
||||
self.stdOut(f"ERROR detecting platform: {msg}, defaulting to rhel9", 0)
|
||||
return 'rhel9'
|
||||
self.stdOut(f"ERROR detecting platform: {msg}, defaulting to rhel8", 0)
|
||||
return 'rhel8'
|
||||
|
||||
def getSystemGLIBCVersion(self):
|
||||
"""Get the system's GLIBC version"""
|
||||
try:
|
||||
import subprocess
|
||||
# Try to get GLIBC version from ldd
|
||||
result = subprocess.run(['ldd', '--version'], capture_output=True, text=True, timeout=5)
|
||||
if result.returncode == 0:
|
||||
# ldd --version output format: "ldd (GNU libc) 2.34"
|
||||
for line in result.stdout.split('\n'):
|
||||
if 'GNU libc' in line or 'glibc' in line.lower():
|
||||
import re
|
||||
version_match = re.search(r'(\d+)\.(\d+)', line)
|
||||
if version_match:
|
||||
major = int(version_match.group(1))
|
||||
minor = int(version_match.group(2))
|
||||
return (major, minor)
|
||||
|
||||
# Fallback: try to read from libc.so.6
|
||||
try:
|
||||
result = subprocess.run(['/lib64/libc.so.6'], capture_output=True, text=True, timeout=5)
|
||||
if result.returncode != 0 and 'version' in result.stderr.lower():
|
||||
import re
|
||||
version_match = re.search(r'(\d+)\.(\d+)', result.stderr)
|
||||
if version_match:
|
||||
major = int(version_match.group(1))
|
||||
minor = int(version_match.group(2))
|
||||
return (major, minor)
|
||||
except:
|
||||
pass
|
||||
|
||||
# If we can't detect, assume a safe minimum
|
||||
self.stdOut("WARNING: Could not detect GLIBC version, assuming 2.34", 1)
|
||||
return (2, 34)
|
||||
except Exception as msg:
|
||||
self.stdOut(f"WARNING: Error detecting GLIBC version: {msg}, assuming 2.34", 0)
|
||||
return (2, 34)
|
||||
|
||||
def checkBinaryGLIBCRequirements(self, binary_path):
|
||||
"""Check GLIBC version requirements of a binary file"""
|
||||
try:
|
||||
import subprocess
|
||||
import re
|
||||
|
||||
# Use objdump to check GLIBC version requirements
|
||||
# objdump -T shows dynamic symbols and their GLIBC version requirements
|
||||
result = subprocess.run(['objdump', '-T', binary_path], capture_output=True, text=True, timeout=10)
|
||||
|
||||
if result.returncode != 0:
|
||||
# objdump might not be available, try readelf
|
||||
result = subprocess.run(['readelf', '-d', binary_path], capture_output=True, text=True, timeout=10)
|
||||
if result.returncode != 0:
|
||||
self.stdOut("WARNING: Could not check binary GLIBC requirements (objdump/readelf not available)", 1)
|
||||
return None
|
||||
|
||||
# Look for GLIBC version requirements in the output
|
||||
# Format: GLIBC_2.35, GLIBC_2.34, etc.
|
||||
max_version = None
|
||||
for line in result.stdout.split('\n') + result.stderr.split('\n'):
|
||||
# Look for GLIBC version symbols
|
||||
matches = re.findall(r'GLIBC_(\d+)\.(\d+)', line)
|
||||
for match in matches:
|
||||
major = int(match[0])
|
||||
minor = int(match[1])
|
||||
if max_version is None or (major, minor) > max_version:
|
||||
max_version = (major, minor)
|
||||
|
||||
return max_version
|
||||
|
||||
except FileNotFoundError:
|
||||
# objdump/readelf not available
|
||||
self.stdOut("WARNING: objdump/readelf not available, skipping GLIBC check", 1)
|
||||
return None
|
||||
except Exception as msg:
|
||||
self.stdOut(f"WARNING: Error checking binary GLIBC requirements: {msg}", 0)
|
||||
return None
|
||||
|
||||
def verifyBinaryCompatibility(self, binary_path):
|
||||
"""Verify that a binary is compatible with the system's GLIBC version"""
|
||||
try:
|
||||
system_glibc = self.getSystemGLIBCVersion()
|
||||
binary_glibc = self.checkBinaryGLIBCRequirements(binary_path)
|
||||
|
||||
if binary_glibc is None:
|
||||
# Can't check, but we can try a test run
|
||||
self.stdOut("Cannot verify GLIBC requirements, performing test run...", 1)
|
||||
return self.testBinaryExecution(binary_path)
|
||||
|
||||
self.stdOut(f"System GLIBC: {system_glibc[0]}.{system_glibc[1]}", 1)
|
||||
self.stdOut(f"Binary requires GLIBC: {binary_glibc[0]}.{binary_glibc[1]}", 1)
|
||||
|
||||
# Check if binary requires newer GLIBC than system has
|
||||
if binary_glibc > system_glibc:
|
||||
self.stdOut(f"ERROR: Binary requires GLIBC {binary_glibc[0]}.{binary_glibc[1]}, but system has {system_glibc[0]}.{system_glibc[1]}", 1)
|
||||
return False
|
||||
|
||||
self.stdOut("GLIBC compatibility check passed", 1)
|
||||
return True
|
||||
|
||||
except Exception as msg:
|
||||
self.stdOut(f"WARNING: Error verifying binary compatibility: {msg}", 0)
|
||||
# If we can't verify, try test execution
|
||||
return self.testBinaryExecution(binary_path)
|
||||
|
||||
def testBinaryExecution(self, binary_path):
|
||||
"""Test if binary can execute (checks GLIBC compatibility indirectly)"""
|
||||
try:
|
||||
import subprocess
|
||||
# Try to run the binary with --version or -v flag
|
||||
# This will fail immediately if GLIBC is incompatible
|
||||
# The error format is: "./binary: /lib64/libc.so.6: version `GLIBC_X.Y' not found"
|
||||
result = subprocess.run([binary_path, '--version'], capture_output=True, text=True, timeout=5)
|
||||
|
||||
# Check both stdout and stderr for GLIBC errors
|
||||
output = result.stdout + result.stderr
|
||||
|
||||
# Look for GLIBC version not found errors
|
||||
if 'GLIBC' in output and ('not found' in output or 'version' in output.lower()):
|
||||
# Extract the required GLIBC version from error message
|
||||
import re
|
||||
glibc_match = re.search(r"GLIBC_(\d+)\.(\d+)'?\s+not found", output)
|
||||
if glibc_match:
|
||||
required_major = int(glibc_match.group(1))
|
||||
required_minor = int(glibc_match.group(2))
|
||||
self.stdOut(f"ERROR: Binary requires GLIBC {required_major}.{required_minor} which is not available", 1)
|
||||
else:
|
||||
self.stdOut(f"ERROR: Binary GLIBC compatibility test failed: {output[:200]}", 1)
|
||||
return False
|
||||
|
||||
# If binary executed (even with non-zero return code for --version), it's compatible
|
||||
if result.returncode == 0 or len(result.stdout) > 0:
|
||||
return True
|
||||
|
||||
# If we get here, binary might not support --version, try -v
|
||||
result = subprocess.run([binary_path, '-v'], capture_output=True, text=True, timeout=5)
|
||||
output = result.stdout + result.stderr
|
||||
|
||||
if 'GLIBC' in output and ('not found' in output or 'version' in output.lower()):
|
||||
import re
|
||||
glibc_match = re.search(r"GLIBC_(\d+)\.(\d+)'?\s+not found", output)
|
||||
if glibc_match:
|
||||
required_major = int(glibc_match.group(1))
|
||||
required_minor = int(glibc_match.group(2))
|
||||
self.stdOut(f"ERROR: Binary requires GLIBC {required_major}.{required_minor} which is not available", 1)
|
||||
else:
|
||||
self.stdOut(f"ERROR: Binary GLIBC compatibility test failed: {output[:200]}", 1)
|
||||
return False
|
||||
|
||||
# If no GLIBC error and we got some output, assume compatible
|
||||
if len(output) > 0:
|
||||
return True
|
||||
|
||||
# If binary doesn't support --version/-v, try to check if it's executable
|
||||
# by checking file type
|
||||
try:
|
||||
result = subprocess.run(['file', binary_path], capture_output=True, text=True, timeout=5)
|
||||
if 'ELF' in result.stdout and 'executable' in result.stdout:
|
||||
self.stdOut("WARNING: Cannot test binary execution, but file appears valid", 1)
|
||||
return True
|
||||
except:
|
||||
pass
|
||||
|
||||
# Conservative approach: if we can't verify, assume incompatible to be safe
|
||||
self.stdOut("WARNING: Could not verify binary execution, skipping installation for safety", 1)
|
||||
return False
|
||||
|
||||
except subprocess.TimeoutExpired:
|
||||
self.stdOut("WARNING: Binary test timed out, assuming compatible", 1)
|
||||
return True
|
||||
except FileNotFoundError as e:
|
||||
# Binary file not found or command not found
|
||||
self.stdOut(f"ERROR: Binary test failed - file or command not found: {e}", 1)
|
||||
return False
|
||||
except Exception as msg:
|
||||
# Check if it's a GLIBC error in the exception itself
|
||||
error_str = str(msg)
|
||||
if 'GLIBC' in error_str and 'not found' in error_str:
|
||||
self.stdOut(f"ERROR: Binary GLIBC compatibility test failed: {error_str}", 1)
|
||||
return False
|
||||
|
||||
self.stdOut(f"WARNING: Could not test binary execution: {msg}", 0)
|
||||
# Conservative approach: if we can't test, assume incompatible to be safe
|
||||
return False
|
||||
|
||||
def downloadCustomBinary(self, url, destination, expected_sha256=None):
|
||||
"""Download custom binary file with optional checksum verification"""
|
||||
|
|
@ -964,6 +1147,23 @@ class preFlightsChecks:
|
|||
self.stdOut("Continuing with standard OLS", 1)
|
||||
return True # Not fatal, continue with standard OLS
|
||||
|
||||
# CRITICAL: Verify GLIBC compatibility before installation
|
||||
self.stdOut("Verifying GLIBC compatibility...", 1)
|
||||
if not self.verifyBinaryCompatibility(tmp_binary):
|
||||
self.stdOut("=" * 50, 1)
|
||||
self.stdOut("ERROR: Binary GLIBC requirements incompatible with system", 1)
|
||||
self.stdOut("This binary would cause OpenLiteSpeed to fail to start", 1)
|
||||
self.stdOut("Skipping custom binary installation to preserve system stability", 1)
|
||||
self.stdOut("Standard OLS binary from package manager will be used", 1)
|
||||
self.stdOut("=" * 50, 1)
|
||||
# Clean up downloaded binary
|
||||
try:
|
||||
if os.path.exists(tmp_binary):
|
||||
os.remove(tmp_binary)
|
||||
except:
|
||||
pass
|
||||
return True # Not fatal, continue with standard OLS
|
||||
|
||||
# Download module with checksum verification (if available)
|
||||
module_downloaded = False
|
||||
if MODULE_URL and MODULE_SHA256:
|
||||
|
|
@ -979,11 +1179,31 @@ class preFlightsChecks:
|
|||
self.stdOut("Installing custom binaries...", 1)
|
||||
|
||||
try:
|
||||
# Make binary executable before moving
|
||||
os.chmod(tmp_binary, 0o755)
|
||||
|
||||
# Final compatibility test before installation
|
||||
if not self.testBinaryExecution(tmp_binary):
|
||||
self.stdOut("ERROR: Final binary compatibility test failed", 1)
|
||||
self.stdOut("Skipping installation to prevent OpenLiteSpeed failure", 1)
|
||||
try:
|
||||
if os.path.exists(tmp_binary):
|
||||
os.remove(tmp_binary)
|
||||
except:
|
||||
pass
|
||||
return True # Not fatal, continue with standard OLS
|
||||
|
||||
shutil.move(tmp_binary, OLS_BINARY_PATH)
|
||||
os.chmod(OLS_BINARY_PATH, 0o755)
|
||||
self.stdOut("Installed OpenLiteSpeed binary", 1)
|
||||
except Exception as e:
|
||||
self.stdOut(f"ERROR: Failed to install binary: {e}", 1)
|
||||
# Try to restore backup if installation failed
|
||||
try:
|
||||
if os.path.exists(f"{backup_dir}/openlitespeed.backup"):
|
||||
shutil.copy2(f"{backup_dir}/openlitespeed.backup", OLS_BINARY_PATH)
|
||||
self.stdOut("Restored original binary from backup", 1)
|
||||
except:
|
||||
pass
|
||||
return False
|
||||
|
||||
# Install module (if downloaded)
|
||||
|
|
|
|||
|
|
@ -674,13 +674,200 @@ class Upgrade:
|
|||
if any(distro in content for distro in ['red hat', 'almalinux', 'rocky', 'cloudlinux', 'centos']):
|
||||
return 'rhel9'
|
||||
|
||||
# Default to rhel9 if can't detect (safer default for newer systems)
|
||||
Upgrade.stdOut("WARNING: Could not detect platform, defaulting to rhel9", 0)
|
||||
return 'rhel9'
|
||||
# Default to rhel8 if can't detect (safer default - rhel9 binaries may require GLIBC 2.35)
|
||||
Upgrade.stdOut("WARNING: Could not detect platform, defaulting to rhel8", 0)
|
||||
return 'rhel8'
|
||||
|
||||
except Exception as msg:
|
||||
Upgrade.stdOut(f"ERROR detecting platform: {msg}, defaulting to rhel9", 0)
|
||||
return 'rhel9'
|
||||
Upgrade.stdOut(f"ERROR detecting platform: {msg}, defaulting to rhel8", 0)
|
||||
return 'rhel8'
|
||||
|
||||
@staticmethod
|
||||
def getSystemGLIBCVersion():
|
||||
"""Get the system's GLIBC version"""
|
||||
try:
|
||||
import subprocess
|
||||
# Try to get GLIBC version from ldd
|
||||
result = subprocess.run(['ldd', '--version'], capture_output=True, text=True, timeout=5)
|
||||
if result.returncode == 0:
|
||||
# ldd --version output format: "ldd (GNU libc) 2.34"
|
||||
for line in result.stdout.split('\n'):
|
||||
if 'GNU libc' in line or 'glibc' in line.lower():
|
||||
import re
|
||||
version_match = re.search(r'(\d+)\.(\d+)', line)
|
||||
if version_match:
|
||||
major = int(version_match.group(1))
|
||||
minor = int(version_match.group(2))
|
||||
return (major, minor)
|
||||
|
||||
# Fallback: try to read from libc.so.6
|
||||
try:
|
||||
result = subprocess.run(['/lib64/libc.so.6'], capture_output=True, text=True, timeout=5)
|
||||
if result.returncode != 0 and 'version' in result.stderr.lower():
|
||||
import re
|
||||
version_match = re.search(r'(\d+)\.(\d+)', result.stderr)
|
||||
if version_match:
|
||||
major = int(version_match.group(1))
|
||||
minor = int(version_match.group(2))
|
||||
return (major, minor)
|
||||
except:
|
||||
pass
|
||||
|
||||
# If we can't detect, assume a safe minimum
|
||||
Upgrade.stdOut("WARNING: Could not detect GLIBC version, assuming 2.34", 0)
|
||||
return (2, 34)
|
||||
except Exception as msg:
|
||||
Upgrade.stdOut(f"WARNING: Error detecting GLIBC version: {msg}, assuming 2.34", 0)
|
||||
return (2, 34)
|
||||
|
||||
@staticmethod
|
||||
def checkBinaryGLIBCRequirements(binary_path):
|
||||
"""Check GLIBC version requirements of a binary file"""
|
||||
try:
|
||||
import subprocess
|
||||
import re
|
||||
|
||||
# Use objdump to check GLIBC version requirements
|
||||
# objdump -T shows dynamic symbols and their GLIBC version requirements
|
||||
result = subprocess.run(['objdump', '-T', binary_path], capture_output=True, text=True, timeout=10)
|
||||
|
||||
if result.returncode != 0:
|
||||
# objdump might not be available, try readelf
|
||||
result = subprocess.run(['readelf', '-d', binary_path], capture_output=True, text=True, timeout=10)
|
||||
if result.returncode != 0:
|
||||
Upgrade.stdOut("WARNING: Could not check binary GLIBC requirements (objdump/readelf not available)", 0)
|
||||
return None
|
||||
|
||||
# Look for GLIBC version requirements in the output
|
||||
# Format: GLIBC_2.35, GLIBC_2.34, etc.
|
||||
max_version = None
|
||||
for line in result.stdout.split('\n') + result.stderr.split('\n'):
|
||||
# Look for GLIBC version symbols
|
||||
matches = re.findall(r'GLIBC_(\d+)\.(\d+)', line)
|
||||
for match in matches:
|
||||
major = int(match[0])
|
||||
minor = int(match[1])
|
||||
if max_version is None or (major, minor) > max_version:
|
||||
max_version = (major, minor)
|
||||
|
||||
return max_version
|
||||
|
||||
except FileNotFoundError:
|
||||
# objdump/readelf not available
|
||||
Upgrade.stdOut("WARNING: objdump/readelf not available, skipping GLIBC check", 0)
|
||||
return None
|
||||
except Exception as msg:
|
||||
Upgrade.stdOut(f"WARNING: Error checking binary GLIBC requirements: {msg}", 0)
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def verifyBinaryCompatibility(binary_path):
|
||||
"""Verify that a binary is compatible with the system's GLIBC version"""
|
||||
try:
|
||||
system_glibc = Upgrade.getSystemGLIBCVersion()
|
||||
binary_glibc = Upgrade.checkBinaryGLIBCRequirements(binary_path)
|
||||
|
||||
if binary_glibc is None:
|
||||
# Can't check, but we can try a test run
|
||||
Upgrade.stdOut("Cannot verify GLIBC requirements, performing test run...", 0)
|
||||
return Upgrade.testBinaryExecution(binary_path)
|
||||
|
||||
Upgrade.stdOut(f"System GLIBC: {system_glibc[0]}.{system_glibc[1]}", 0)
|
||||
Upgrade.stdOut(f"Binary requires GLIBC: {binary_glibc[0]}.{binary_glibc[1]}", 0)
|
||||
|
||||
# Check if binary requires newer GLIBC than system has
|
||||
if binary_glibc > system_glibc:
|
||||
Upgrade.stdOut(f"ERROR: Binary requires GLIBC {binary_glibc[0]}.{binary_glibc[1]}, but system has {system_glibc[0]}.{system_glibc[1]}", 0)
|
||||
return False
|
||||
|
||||
Upgrade.stdOut("GLIBC compatibility check passed", 0)
|
||||
return True
|
||||
|
||||
except Exception as msg:
|
||||
Upgrade.stdOut(f"WARNING: Error verifying binary compatibility: {msg}", 0)
|
||||
# If we can't verify, try test execution
|
||||
return Upgrade.testBinaryExecution(binary_path)
|
||||
|
||||
@staticmethod
|
||||
def testBinaryExecution(binary_path):
|
||||
"""Test if binary can execute (checks GLIBC compatibility indirectly)"""
|
||||
try:
|
||||
import subprocess
|
||||
# Try to run the binary with --version or -v flag
|
||||
# This will fail immediately if GLIBC is incompatible
|
||||
# The error format is: "./binary: /lib64/libc.so.6: version `GLIBC_X.Y' not found"
|
||||
result = subprocess.run([binary_path, '--version'], capture_output=True, text=True, timeout=5)
|
||||
|
||||
# Check both stdout and stderr for GLIBC errors
|
||||
output = result.stdout + result.stderr
|
||||
|
||||
# Look for GLIBC version not found errors
|
||||
if 'GLIBC' in output and ('not found' in output or 'version' in output.lower()):
|
||||
# Extract the required GLIBC version from error message
|
||||
import re
|
||||
glibc_match = re.search(r"GLIBC_(\d+)\.(\d+)'?\s+not found", output)
|
||||
if glibc_match:
|
||||
required_major = int(glibc_match.group(1))
|
||||
required_minor = int(glibc_match.group(2))
|
||||
Upgrade.stdOut(f"ERROR: Binary requires GLIBC {required_major}.{required_minor} which is not available", 0)
|
||||
else:
|
||||
Upgrade.stdOut(f"ERROR: Binary GLIBC compatibility test failed: {output[:200]}", 0)
|
||||
return False
|
||||
|
||||
# If binary executed (even with non-zero return code for --version), it's compatible
|
||||
if result.returncode == 0 or len(result.stdout) > 0:
|
||||
return True
|
||||
|
||||
# If we get here, binary might not support --version, try -v
|
||||
result = subprocess.run([binary_path, '-v'], capture_output=True, text=True, timeout=5)
|
||||
output = result.stdout + result.stderr
|
||||
|
||||
if 'GLIBC' in output and ('not found' in output or 'version' in output.lower()):
|
||||
import re
|
||||
glibc_match = re.search(r"GLIBC_(\d+)\.(\d+)'?\s+not found", output)
|
||||
if glibc_match:
|
||||
required_major = int(glibc_match.group(1))
|
||||
required_minor = int(glibc_match.group(2))
|
||||
Upgrade.stdOut(f"ERROR: Binary requires GLIBC {required_major}.{required_minor} which is not available", 0)
|
||||
else:
|
||||
Upgrade.stdOut(f"ERROR: Binary GLIBC compatibility test failed: {output[:200]}", 0)
|
||||
return False
|
||||
|
||||
# If no GLIBC error and we got some output, assume compatible
|
||||
if len(output) > 0:
|
||||
return True
|
||||
|
||||
# If binary doesn't support --version/-v, try to check if it's executable
|
||||
# by checking file type
|
||||
try:
|
||||
result = subprocess.run(['file', binary_path], capture_output=True, text=True, timeout=5)
|
||||
if 'ELF' in result.stdout and 'executable' in result.stdout:
|
||||
Upgrade.stdOut("WARNING: Cannot test binary execution, but file appears valid", 0)
|
||||
return True
|
||||
except:
|
||||
pass
|
||||
|
||||
# Conservative approach: if we can't verify, assume incompatible to be safe
|
||||
Upgrade.stdOut("WARNING: Could not verify binary execution, skipping installation for safety", 0)
|
||||
return False
|
||||
|
||||
except subprocess.TimeoutExpired:
|
||||
Upgrade.stdOut("WARNING: Binary test timed out, assuming compatible", 0)
|
||||
return True
|
||||
except FileNotFoundError as e:
|
||||
# Binary file not found or command not found
|
||||
Upgrade.stdOut(f"ERROR: Binary test failed - file or command not found: {e}", 0)
|
||||
return False
|
||||
except Exception as msg:
|
||||
# Check if it's a GLIBC error in the exception itself
|
||||
error_str = str(msg)
|
||||
if 'GLIBC' in error_str and 'not found' in error_str:
|
||||
Upgrade.stdOut(f"ERROR: Binary GLIBC compatibility test failed: {error_str}", 0)
|
||||
return False
|
||||
|
||||
Upgrade.stdOut(f"WARNING: Could not test binary execution: {msg}", 0)
|
||||
# Conservative approach: if we can't test, assume incompatible to be safe
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def downloadCustomBinary(url, destination, expected_sha256=None):
|
||||
|
|
@ -811,6 +998,23 @@ class Upgrade:
|
|||
Upgrade.stdOut("Continuing with standard OLS", 0)
|
||||
return True # Not fatal, continue with standard OLS
|
||||
|
||||
# CRITICAL: Verify GLIBC compatibility before installation
|
||||
Upgrade.stdOut("Verifying GLIBC compatibility...", 0)
|
||||
if not Upgrade.verifyBinaryCompatibility(tmp_binary):
|
||||
Upgrade.stdOut("=" * 50, 0)
|
||||
Upgrade.stdOut("ERROR: Binary GLIBC requirements incompatible with system", 0)
|
||||
Upgrade.stdOut("This binary would cause OpenLiteSpeed to fail to start", 0)
|
||||
Upgrade.stdOut("Skipping custom binary installation to preserve system stability", 0)
|
||||
Upgrade.stdOut("Standard OLS binary from package manager will be used", 0)
|
||||
Upgrade.stdOut("=" * 50, 0)
|
||||
# Clean up downloaded binary
|
||||
try:
|
||||
if os.path.exists(tmp_binary):
|
||||
os.remove(tmp_binary)
|
||||
except:
|
||||
pass
|
||||
return True # Not fatal, continue with standard OLS
|
||||
|
||||
# Download module with checksum verification (if available)
|
||||
module_downloaded = False
|
||||
if MODULE_URL and MODULE_SHA256:
|
||||
|
|
@ -826,11 +1030,31 @@ class Upgrade:
|
|||
Upgrade.stdOut("Installing custom binaries...", 0)
|
||||
|
||||
try:
|
||||
# Make binary executable before moving
|
||||
os.chmod(tmp_binary, 0o755)
|
||||
|
||||
# Final compatibility test before installation
|
||||
if not Upgrade.testBinaryExecution(tmp_binary):
|
||||
Upgrade.stdOut("ERROR: Final binary compatibility test failed", 0)
|
||||
Upgrade.stdOut("Skipping installation to prevent OpenLiteSpeed failure", 0)
|
||||
try:
|
||||
if os.path.exists(tmp_binary):
|
||||
os.remove(tmp_binary)
|
||||
except:
|
||||
pass
|
||||
return True # Not fatal, continue with standard OLS
|
||||
|
||||
shutil.move(tmp_binary, OLS_BINARY_PATH)
|
||||
os.chmod(OLS_BINARY_PATH, 0o755)
|
||||
Upgrade.stdOut("Installed OpenLiteSpeed binary", 0)
|
||||
except Exception as e:
|
||||
Upgrade.stdOut(f"ERROR: Failed to install binary: {e}", 0)
|
||||
# Try to restore backup if installation failed
|
||||
try:
|
||||
if os.path.exists(f"{backup_dir}/openlitespeed.backup"):
|
||||
shutil.copy2(f"{backup_dir}/openlitespeed.backup", OLS_BINARY_PATH)
|
||||
Upgrade.stdOut("Restored original binary from backup", 0)
|
||||
except:
|
||||
pass
|
||||
return False
|
||||
|
||||
# Install module (if downloaded)
|
||||
|
|
|
|||
Loading…
Reference in New Issue