From c7995ecf031c8218f87bb9c3cf1c9a1d23df4a40 Mon Sep 17 00:00:00 2001 From: master3395 Date: Sun, 12 Apr 2026 02:54:04 +0200 Subject: [PATCH] Fix missing /usr/local/CyberCP/bin/python for cron and IncBackups Add plogical/cyberpanel_python.py with resolve_cyberpanel_python() and ensure_cyberpanel_bin_python_shim() (symlink to system Python when venv binary is absent). Call shim before writing root crontab on install/upgrade, and from IncBackups/IncScheduler.py so existing jobs self-heal. IncBackups views use resolved interpreter for backupUtilities. Upgrade._python_for_manage delegates to resolve_cyberpanel_python(). --- IncBackups/IncScheduler.py | 20 +++++++---- IncBackups/views.py | 3 +- install/install.py | 8 +++++ plogical/cyberpanel_python.py | 67 +++++++++++++++++++++++++++++++++++ plogical/upgrade.py | 19 +++++++--- 5 files changed, 105 insertions(+), 12 deletions(-) create mode 100644 plogical/cyberpanel_python.py diff --git a/IncBackups/IncScheduler.py b/IncBackups/IncScheduler.py index 4c4619b94..e3e946c81 100644 --- a/IncBackups/IncScheduler.py +++ b/IncBackups/IncScheduler.py @@ -1,17 +1,25 @@ import argparse import sys -sys.path.append('/usr/local/CyberCP') + +sys.path.append("/usr/local/CyberCP") +from plogical.cyberpanel_python import ensure_cyberpanel_bin_python_shim, resolve_cyberpanel_python from plogical.processUtilities import ProcessUtilities -def main(): - parser = argparse.ArgumentParser(description='CyberPanel Installer') - parser.add_argument('function', help='Specific a function to call!') +def main(): + try: + ensure_cyberpanel_bin_python_shim() + except BaseException: + pass + + parser = argparse.ArgumentParser(description="CyberPanel incremental backup cron wrapper") + parser.add_argument("function", help="Function name to pass to plogical/IncScheduler.py") args = parser.parse_args() - command = f"/usr/local/CyberCP/bin/python /usr/local/CyberCP/plogical/IncScheduler.py '{args.function}'" + py = resolve_cyberpanel_python() + command = "%s /usr/local/CyberCP/plogical/IncScheduler.py '%s'" % (py, args.function) ProcessUtilities.normalExecutioner(command) if __name__ == "__main__": - main() \ No newline at end of file + main() diff --git a/IncBackups/views.py b/IncBackups/views.py index 3b9a6096a..3c990ac65 100644 --- a/IncBackups/views.py +++ b/IncBackups/views.py @@ -108,7 +108,8 @@ def add_destination(request): final_json = json.dumps(final_dic) return HttpResponse(final_json) - python_path = Path('/usr/local/CyberCP/bin/python') + from plogical.cyberpanel_python import resolve_cyberpanel_python + python_path = Path(resolve_cyberpanel_python()) backup_utils = Path(vhu.cyberPanel) / "plogical/backupUtilities.py" exec_args = "submitDestinationCreation --ipAddress %s --password %s --port %s --user %s" % \ diff --git a/install/install.py b/install/install.py index f79e5e109..f142545a3 100644 --- a/install/install.py +++ b/install/install.py @@ -5498,6 +5498,14 @@ user_query = SELECT email as user, password, 'vmail' as uid, 'vmail' as gid, '/h else: cronPath = '/var/spool/cron/crontabs/root' + try: + if '/usr/local/CyberCP' not in sys.path: + sys.path.insert(0, '/usr/local/CyberCP') + from plogical.cyberpanel_python import ensure_cyberpanel_bin_python_shim + ensure_cyberpanel_bin_python_shim() + except BaseException: + pass + cronFile = open(cronPath, "w") # Randomize acme.sh and renew.py cron schedules to avoid traffic spikes to Let's Encrypt diff --git a/plogical/cyberpanel_python.py b/plogical/cyberpanel_python.py new file mode 100644 index 000000000..a9e12ba40 --- /dev/null +++ b/plogical/cyberpanel_python.py @@ -0,0 +1,67 @@ +# -*- coding: utf-8 -*- +""" +Resolve the Python interpreter for CyberPanel CLI and cron jobs. + +Some installs omit /usr/local/CyberCP/bin/python (no venv). We symlink that path +to a working interpreter when missing so existing crontab lines keep working. +""" +from __future__ import annotations + +import os + +_CANDIDATES = ( + "/usr/local/CyberPanel/bin/python", + "/usr/local/CyberCP/bin/python", + "/usr/bin/python3", + "/usr/local/bin/python3", +) + + +def resolve_cyberpanel_python() -> str: + """Return first existing executable candidate, else /usr/bin/python3.""" + for path in _CANDIDATES: + if path == "/usr/local/CyberCP/bin/python": + continue + if path and os.path.isfile(path) and os.access(path, os.X_OK): + return path + return "/usr/bin/python3" + + +def ensure_cyberpanel_bin_python_shim() -> None: + """ + If /usr/local/CyberCP/bin/python is missing or a broken symlink, replace with + symlink to resolve_cyberpanel_python(). Non-destructive for a real venv file. + """ + bin_dir = "/usr/local/CyberCP/bin" + legacy = os.path.join(bin_dir, "python") + try: + os.makedirs(bin_dir, exist_ok=True) + except OSError: + return + + if os.path.isfile(legacy) and os.access(legacy, os.X_OK) and not os.path.islink(legacy): + return + + if os.path.islink(legacy): + real = os.path.realpath(legacy) + if os.path.isfile(real) and os.access(real, os.X_OK): + return + try: + os.unlink(legacy) + except OSError: + return + + if os.path.lexists(legacy): + try: + os.remove(legacy) + except OSError: + try: + os.unlink(legacy) + except OSError: + return + + target = resolve_cyberpanel_python() + try: + os.symlink(target, legacy) + except OSError: + pass diff --git a/plogical/upgrade.py b/plogical/upgrade.py index 729bdb668..988512be3 100644 --- a/plogical/upgrade.py +++ b/plogical/upgrade.py @@ -3850,11 +3850,9 @@ passdb { @staticmethod def _python_for_manage(): - """Resolve Python for manage.py (avoid FileNotFoundError when /usr/local/CyberPanel/bin/python missing).""" - for path in ('/usr/local/CyberPanel/bin/python', '/usr/local/CyberCP/bin/python', '/usr/bin/python3', '/usr/local/bin/python3'): - if path and os.path.isfile(path) and os.access(path, os.X_OK): - return path - return '/usr/bin/python3' + """Resolve Python for manage.py (avoid FileNotFoundError when venv python missing).""" + from plogical.cyberpanel_python import resolve_cyberpanel_python + return resolve_cyberpanel_python() @staticmethod def GeneralMigrations(): @@ -6231,6 +6229,12 @@ vmail if os.path.exists(cronPath): data = open(cronPath, 'r').read() + try: + from plogical.cyberpanel_python import ensure_cyberpanel_bin_python_shim + ensure_cyberpanel_bin_python_shim() + except BaseException: + pass + if data.find('findBWUsage') == -1: # Randomize acme.sh and renew.py cron schedules to avoid traffic spikes to Let's Encrypt # Each installation gets a random day (0-6 Sun-Sat), hour, and minute to spread load @@ -6290,6 +6294,11 @@ vmail else: + try: + from plogical.cyberpanel_python import ensure_cyberpanel_bin_python_shim + ensure_cyberpanel_bin_python_shim() + except BaseException: + pass # Randomize acme.sh and renew.py cron schedules to avoid traffic spikes to Let's Encrypt # Each installation gets a random day (0-6 Sun-Sat), hour, and minute to spread load acme_hour = random.randint(0, 23)