diff --git a/cyberpanel.sh b/cyberpanel.sh index 4adde917a..aff29677c 100644 --- a/cyberpanel.sh +++ b/cyberpanel.sh @@ -968,67 +968,9 @@ except Exception as e: if [ -f "$installer_py" ]; then print_status "Using install/install.py directly for installation (non-interactive mode)" - # CRITICAL: Patch install.py to exclude MariaDB-server from dnf/yum commands - if [ -n "$MARIADB_VERSION" ] && [ "$major_ver" -lt 12 ] 2>/dev/null; then - print_status "Patching install.py to exclude MariaDB-server from installation commands..." - - # Create backup - cp "$installer_py" "${installer_py}.backup" 2>/dev/null || true - - # Patch install.py to add --exclude=MariaDB-server* to dnf/yum install commands - python3 -c " -import re -import sys - -try: - with open('$installer_py', 'r') as f: - content = f.read() - - original_content = content - - # Pattern: Add --exclude=MariaDB-server* to dnf/yum install commands that install mariadb-server - def add_exclude(match): - cmd = match.group(0) - # Check if --exclude is already present - if '--exclude=MariaDB-server' in cmd: - return cmd - # Add --exclude=MariaDB-server* after install and flags, before packages - return re.sub(r'((?:dnf|yum)\s+install\s+(?:-[^\s]+\s+)*)', r'\1--exclude=MariaDB-server* ', cmd, flags=re.IGNORECASE) - - # Find all dnf/yum install commands that mention mariadb-server - content = re.sub( - r'(?:dnf|yum)\s+install[^;]*?mariadb-server[^;]*', - add_exclude, - content, - flags=re.IGNORECASE | re.MULTILINE - ) - - # Also handle MariaDB-server (capitalized) and in Python strings - content = re.sub( - r'(\"|\')(?:dnf|yum)\s+install[^\"]*?mariadb-server[^\"]*(\"|\')', - lambda m: m.group(1) + re.sub(r'((?:dnf|yum)\s+install\s+(?:-[^\s]+\s+)*)', r'\1--exclude=MariaDB-server* ', m.group(0)[1:-1], flags=re.IGNORECASE) + m.group(2), - content, - flags=re.IGNORECASE | re.MULTILINE - ) - - # Only write if content changed - if content != original_content: - with open('$installer_py', 'w') as f: - f.write(content) - print('install.py patched successfully') - else: - print('No changes needed in install.py') - -except Exception as e: - print(f'Error patching install.py: {e}') - sys.exit(1) -" 2>/dev/null && print_status "install.py patched successfully" || { - # Fallback: Simple sed-based patching if Python fails - sed -i 's/\(dnf\|yum\) install\([^;]*\)mariadb-server/\1 install\2--exclude=MariaDB-server* mariadb-server/gi' "$installer_py" 2>/dev/null - sed -i 's/\(dnf\|yum\) install\([^;]*\)MariaDB-server/\1 install\2--exclude=MariaDB-server* MariaDB-server/gi' "$installer_py" 2>/dev/null - print_status "install.py patched (fallback method)" - } - fi + # NOTE: We do NOT patch install.py to add --exclude=MariaDB-server* to dnf install. + # That would block the initial MariaDB-server install. install.py now clears dnf exclude + # before installing MariaDB and uses official MariaDB-server packages. # If MariaDB 10.x is installed, disable repositories right before running installer if [ -n "$MARIADB_VERSION" ] && [ -f /tmp/cyberpanel_repo_monitor.pid ]; then @@ -1421,11 +1363,61 @@ EOF # Give services a moment to start sleep 3 + # Ensure both 8090 (CyberPanel) and 7080 (LiteSpeed/OLS) are accessible + echo " • Ensuring ports 8090 and 7080 are accessible..." + port_check() { + local port=$1 + command -v ss >/dev/null 2>&1 && ss -tlnp 2>/dev/null | grep -q ":$port " && return 0 + command -v netstat >/dev/null 2>&1 && netstat -tlnp 2>/dev/null | grep -q ":$port " && return 0 + return 1 + } + max_attempts=18 + attempt=0 + while [ $attempt -lt $max_attempts ]; do + need_restart=false + systemctl is-active --quiet mariadb || { systemctl start mariadb 2>/dev/null; need_restart=true; } + systemctl is-active --quiet lsws 2>/dev/null || { [ -x /usr/local/lsws/bin/lswsctrl ] && systemctl start lsws 2>/dev/null; need_restart=true; } + systemctl is-active --quiet lscpd 2>/dev/null || { systemctl start lscpd 2>/dev/null; need_restart=true; } + [ "$need_restart" = true ] && sleep 5 + if port_check 8090 && port_check 7080; then + echo " ✓ Port 8090 (CyberPanel) and 7080 (OpenLiteSpeed) are listening" + break + fi + attempt=$((attempt + 1)) + [ $attempt -lt $max_attempts ] && sleep 5 + done + if ! port_check 8090 || ! port_check 7080; then + systemctl start lscpd 2>/dev/null + systemctl start lsws 2>/dev/null + sleep 10 + if port_check 8090 && port_check 7080; then + echo " ✓ Port 8090 and 7080 are now listening" + else + echo " ⚠ One or both ports not yet listening. Run: systemctl start mariadb lsws lscpd" + fi + fi + echo " ✓ Post-installation configurations completed" } +# Helper: check if a port is listening +_port_listening() { + local port=$1 + command -v ss >/dev/null 2>&1 && ss -tlnp 2>/dev/null | grep -q ":$port " && return 0 + command -v netstat >/dev/null 2>&1 && netstat -tlnp 2>/dev/null | grep -q ":$port " && return 0 + return 1 +} + # Function to show status summary show_status_summary() { + # Last-chance: try to start services so 8090 and 7080 are accessible + if ! _port_listening 8090 || ! _port_listening 7080; then + systemctl start mariadb 2>/dev/null || true + systemctl start lsws 2>/dev/null || true + systemctl start lscpd 2>/dev/null || true + sleep 8 + fi + echo "===============================================================================================================" echo " FINAL STATUS CHECK" echo "===============================================================================================================" @@ -1453,6 +1445,22 @@ show_status_summary() { echo " ✓ CyberPanel Application - Running" else echo " ✗ CyberPanel Application - Not Running (may take a moment to start)" + all_services_running=false + fi + + echo "" + echo "Port Accessibility:" + if _port_listening 8090; then + echo " ✓ Port 8090 (CyberPanel) - Accessible" + else + echo " ✗ Port 8090 (CyberPanel) - Not listening (run: systemctl start lscpd)" + all_services_running=false + fi + if _port_listening 7080; then + echo " ✓ Port 7080 (OpenLiteSpeed) - Accessible" + else + echo " ✗ Port 7080 (OpenLiteSpeed) - Not listening (run: systemctl start lsws)" + all_services_running=false fi # Get the actual password that was set @@ -1481,7 +1489,7 @@ show_status_summary() { echo "===============================================================================================================" if [ "$all_services_running" = true ]; then - echo "✓ Installation completed successfully!" + echo "✓ Installation completed successfully! Ports 8090 and 7080 are accessible." else echo "⚠ Installation completed with warnings. Some services may need attention." fi diff --git a/install/install.py b/install/install.py index 66b6d0280..eecb7e113 100644 --- a/install/install.py +++ b/install/install.py @@ -1906,11 +1906,34 @@ module cyberpanel_ols { mariadb_ver = getattr(preFlightsChecks, 'mariadb_version', '11.8') command = f'curl -LsS https://downloads.mariadb.com/MariaDB/mariadb_repo_setup | bash -s -- --mariadb-server-version={mariadb_ver}' self.call(command, self.distro, command, command, 1, 1, os.EX_OSERR, True) - # Use --nobest for 10.11 and 11.8 on el9 to avoid MariaDB-client dependency resolution issues + # Allow MariaDB-server to be installed: remove from dnf exclude if present (e.g. from previous run or cyberpanel.sh) + dnf_conf = '/etc/dnf/dnf.conf' + if os.path.exists(dnf_conf): + try: + with open(dnf_conf, 'r') as f: + dnf_content = f.read() + if 'MariaDB-server' in (dnf_content or '') and 'exclude=' in (dnf_content or ''): + # Remove MariaDB-server and MariaDB-server* from exclude= line(s) + def strip_mariadb_exclude(match): + line = match.group(0) + rest = re.sub(r'\bMariaDB-server\*?\s*', '', line).strip() + if rest == 'exclude=' or rest == 'exclude': + return '' + return rest.rstrip() + '\n' + new_content = re.sub(r'exclude=[^\n]*', strip_mariadb_exclude, dnf_content) + new_content = re.sub(r'\n\n+', '\n', new_content) + if new_content != dnf_content: + with open(dnf_conf, 'w') as f: + f.write(new_content) + self.stdOut("Temporarily removed MariaDB-server from dnf exclude for installation", 1) + except Exception as e: + self.stdOut(f"Warning: Could not adjust dnf exclude: {e}", 1) + # Install from official MariaDB repo (capitalized package names); --nobest for 10.11/11.8 on el9 + mariadb_packages = 'MariaDB-server MariaDB-client MariaDB-backup MariaDB-devel' if mariadb_ver in ('10.11', '11.8'): - command = 'dnf install -y --nobest mariadb-server mariadb-devel mariadb-client-utils' + command = f'dnf install -y --nobest {mariadb_packages}' else: - command = 'dnf install mariadb-server mariadb-devel mariadb-client-utils -y' + command = f'dnf install -y {mariadb_packages}' self.call(command, self.distro, command, command, 1, 1, os.EX_OSERR, True) # Verify MariaDB was installed successfully before proceeding @@ -3267,7 +3290,36 @@ password="%s" logging.InstallLog.writeToFile("settings.py updated!") - # self.setupVirtualEnv(self.distro) + # Create Python venv at /usr/local/CyberCP if missing (install.py run from temp dir does not run venvsetup.sh) + if not os.path.exists("/usr/local/CyberCP/bin/python"): + logging.InstallLog.writeToFile("Creating Python virtual environment at /usr/local/CyberCP...") + preFlightsChecks.stdOut("Creating Python virtual environment...") + try: + r = subprocess.run( + [sys.executable or "python3", "-m", "venv", "/usr/local/CyberCP"], + timeout=120, capture_output=True, text=True, cwd="/usr/local/CyberCP" + ) + if r.returncode != 0: + logging.InstallLog.writeToFile("venv create stderr: " + (r.stderr or "")[:500]) + if r.returncode == 0 and os.path.exists("/usr/local/CyberCP/bin/pip"): + req_file = "/usr/local/CyberCP/requirments.txt" + if not os.path.exists(req_file): + req_file = "/usr/local/CyberCP/requirements.txt" + if os.path.exists(req_file): + subprocess.run( + ["/usr/local/CyberCP/bin/pip", "install", "-r", req_file, "--quiet"], + timeout=600, cwd="/usr/local/CyberCP", capture_output=True + ) + else: + subprocess.run( + ["/usr/local/CyberCP/bin/pip", "install", "Django", "PyMySQL", "requests", "cryptography", "psutil", "--quiet"], + timeout=180, cwd="/usr/local/CyberCP", capture_output=True + ) + if os.path.exists("/usr/local/CyberCP/bin/python"): + logging.InstallLog.writeToFile("Virtual environment created successfully") + preFlightsChecks.stdOut("Virtual environment created", 1) + except Exception as e: + logging.InstallLog.writeToFile("Venv create warning: " + str(e)) # Now run Django migrations since we're in /usr/local/CyberCP and database exists os.chdir("/usr/local/CyberCP") @@ -3303,46 +3355,53 @@ password="%s" logging.InstallLog.writeToFile("Migration cleanup completed") - # Ensure virtual environment is properly set up - logging.InstallLog.writeToFile("Ensuring virtual environment is properly set up...") + # Ensure virtual environment or system Python is available + logging.InstallLog.writeToFile("Ensuring Python is available for migrations...") if not self.ensureVirtualEnvironmentSetup(): - logging.InstallLog.writeToFile("ERROR: Virtual environment setup failed!", 0) - preFlightsChecks.stdOut("ERROR: Virtual environment setup failed!", 0) - return False + logging.InstallLog.writeToFile("WARNING: No venv found; will try system Python", 1) - # Find the correct Python virtual environment path (prefer CyberCP - app install path) + # Find Python: prefer venv, then system python3 (avoids FileNotFoundError for /usr/local/CyberPanel/bin/python) python_paths = [ "/usr/local/CyberCP/bin/python", "/usr/local/CyberPanel/bin/python", - "/usr/local/CyberPanel-venv/bin/python" + "/usr/local/CyberPanel-venv/bin/python", + "/usr/bin/python3", + "/usr/local/bin/python3", ] - + if sys.executable and sys.executable not in python_paths: + python_paths.append(sys.executable) + python_path = None for path in python_paths: - if os.path.exists(path): - python_path = path - logging.InstallLog.writeToFile(f"Found Python virtual environment at: {path}") - break - + if path and os.path.exists(path): + try: + r = subprocess.run([path, "--version"], capture_output=True, text=True, timeout=5) + if r.returncode == 0: + python_path = path + logging.InstallLog.writeToFile(f"Using Python at: {path}") + break + except Exception: + continue + if not python_path: - logging.InstallLog.writeToFile("ERROR: No Python virtual environment found!", 0) - preFlightsChecks.stdOut("ERROR: No Python virtual environment found!", 0) + logging.InstallLog.writeToFile("ERROR: No working Python found for migrations!", 0) + preFlightsChecks.stdOut("ERROR: No working Python found!", 0) return False # Create migrations in dependency order - loginSystem first since other apps depend on it logging.InstallLog.writeToFile("Creating migrations for loginSystem first...") command = f"{python_path} manage.py makemigrations loginSystem --noinput" - preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) + preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR, True) # Now create migrations for all other apps logging.InstallLog.writeToFile("Creating migrations for all other apps...") command = f"{python_path} manage.py makemigrations --noinput" - preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) + preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR, True) # Apply all migrations logging.InstallLog.writeToFile("Applying all migrations...") command = f"{python_path} manage.py migrate --noinput" - preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) + preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR, True) logging.InstallLog.writeToFile("Django migrations completed successfully!") preFlightsChecks.stdOut("Django migrations completed successfully!") @@ -3354,7 +3413,7 @@ password="%s" self.downloadCDNLibraries() command = f"{python_path} manage.py collectstatic --noinput --clear" - preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR) + preFlightsChecks.call(command, self.distro, command, command, 1, 1, os.EX_OSERR, True) ## Moving static content to lscpd location command = 'mv static /usr/local/CyberCP/public/' @@ -5308,10 +5367,10 @@ user_query = SELECT email as user, password, 'vmail' as uid, 'vmail' as gid, '/h try: os.remove(f) except OSError: - pass + subprocess.run(["rm", "-f", f], timeout=5, capture_output=True) command = "ssh-keygen -f /root/.ssh/cyberpanel -t rsa -N ''" - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) + preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR, True) except BaseException as msg: logging.InstallLog.writeToFile('[ERROR] ' + str(msg) + " [install_default_keys]") @@ -5701,11 +5760,18 @@ milter_default_action = accept os.chdir(self.cwd) - command = "chmod +x composer.sh" - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) + # Download composer.sh if missing (e.g. when run from temp dir without repo file) + composer_sh = os.path.join(self.cwd, "composer.sh") + if not os.path.exists(composer_sh) or not os.path.isfile(composer_sh): + command = "wget -q https://cyberpanel.sh/composer.sh -O " + composer_sh + preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) - command = "./composer.sh" - preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) + if os.path.exists(composer_sh): + command = "chmod +x " + composer_sh + preFlightsChecks.call(command, self.distro, command, command, 1, 0, os.EX_OSERR) + + command = "bash " + os.path.abspath(composer_sh) + preFlightsChecks.call(command, self.distro, "./composer.sh", command, 1, 0, os.EX_OSERR, True) except OSError as msg: logging.InstallLog.writeToFile('[ERROR] ' + str(msg) + " [setupPHPAndComposer]") diff --git a/install/installCyberPanel.py b/install/installCyberPanel.py index 1e0aed194..7861be4ee 100644 --- a/install/installCyberPanel.py +++ b/install/installCyberPanel.py @@ -922,6 +922,25 @@ gpgcheck=1 command = 'dnf clean all' install_utils.call(command, self.distro, command, command, 1, 1, os.EX_OSERR, True) + # Allow MariaDB-server to be installed: remove from dnf exclude if present + dnf_conf = '/etc/dnf/dnf.conf' + if os.path.exists(dnf_conf): + try: + with open(dnf_conf, 'r') as f: + dnf_content = f.read() + if 'MariaDB-server' in dnf_content and 'exclude=' in dnf_content: + new_content = re.sub( + r'(exclude=[^\n]*)', + lambda m: re.sub(r'\bMariaDB-server\*?\s*', '', m.group(1)).strip(), + dnf_content + ) + if new_content != dnf_content: + with open(dnf_conf, 'w') as f: + f.write(new_content) + install_utils.writeToFile("Removed MariaDB-server from dnf exclude for installation") + except Exception: + pass + # Use --nobest so server+client resolve from same repo (avoids AlmaLinux 9 dependency conflict) command = 'dnf install -y --nobest MariaDB-server MariaDB-client MariaDB-backup' diff --git a/plogical/backupUtilities.py b/plogical/backupUtilities.py index 2f1e11bb1..bfb0eb5a9 100644 --- a/plogical/backupUtilities.py +++ b/plogical/backupUtilities.py @@ -1412,6 +1412,13 @@ class backupUtilities: if os.path.exists('/root/.ssh/cyberpanel.pub'): pass else: + # Remove existing key files so ssh-keygen never prompts "Overwrite (y/n)?" + for f in ('/root/.ssh/cyberpanel', '/root/.ssh/cyberpanel.pub'): + if os.path.exists(f): + try: + os.remove(f) + except OSError: + pass command = "ssh-keygen -f /root/.ssh/cyberpanel -t rsa -N ''" ProcessUtilities.executioner(command, 'root', True)