File manager - Edit - /usr/share/l.v.e-manager/install-lvemanager-plugin.py
Back
#!/opt/cloudlinux/venv/bin/python3 -bb # -*- coding: utf-8 -*- # Copyright © Cloud Linux GmbH & Cloud Linux Software, Inc 2010-2019 All Rights Reserved # # Licensed under CLOUD LINUX LICENSE AGREEMENT # http://cloudlinux.com/docs/LICENSE.TXT from __future__ import print_function from __future__ import division from __future__ import absolute_import import subprocess from future.moves import configparser as ConfigParser import os import sys import getopt import shutil import datetime import re import uuid import json from pathlib import Path from builtins import map import cldetectlib as detect from lvemanager.config_parser import SectionlessConfigParser from copy_directory import CopyDirectory from exec_command import exec_command, exec_command_check from crontab import add_cron, remove_cron from feature_manager import cpanel_fix_feature_manager from clcommon.utils import create_symlink from clcommon.const import Feature from clcommon.cpapi import is_panel_feature_supported from clcommon.public_hooks.bundle.cpanel import ( install_hooks, remove_hooks, Hook ) from clcommon.utils import mod_makedirs from lvemanager import PKG_VERSION as LVEMANAGER_VERSION from lvemanager import PKG_RELEASE as LVEMANAGER_RELEASE from lvemanager.sudoers import ( add_unix_user_to_sudoers ) from clcommon.ui_config import UIConfig LVEMANAGER_CRON = "lvemanager-cron" CL_SELECTOR_PHP_CONF_PATH = '/etc/cl.selector/php.conf' SOURCE_PATH = "/usr/share/l.v.e-manager/" ROOT_CPANEL_DIR = "/usr/local/cpanel/whostmgr/docroot/" CPANEL_CUSTOM_PATH = '/var/cpanel/perl/Cpanel/ResourceUsage/Custom/' ROOT_IWORX_DIR = "/usr/local/interworx/plugins/" ROOT_ISPMGR_DIR = "/usr/local/ispmgr/" ROOT_DA_DIR = "/usr/local/directadmin/plugins/" CPANEL_PAPER_LATERN_HOME = "/usr/local/cpanel/base/frontend/paper_lantern/home/" CPANEL_PATCHES_PATH = SOURCE_PATH + 'cpanel/patches/' CPANEL_ORIGINAL_FILES_DIR = '/usr/local/cpanel/base/3rdparty/cloudlinux/original/' SPA_RESOURCE_DIR = SOURCE_PATH + "commons/spa-resources/" UI_CONFIG_PATH = '/usr/share/l.v.e-manager/lvemanager-config.json' CLOUDLINUX_SERVER_FLAGS_SOURCE_DIR = SOURCE_PATH + 'cl-server-flags/' CLOUDLINUX_SERVER_FLAGS_DESTINATION_DIR = '/opt/cloudlinux/flags/available-flags.d/' # cPanel parametres CPANEL_ICON_BASE_DIR = '/usr/local/cpanel/base/frontend/' CRON_TASKS = [ ("*/5", "*", "*", "*", "*", "root", "/usr/bin/flock -n /var/run/cloudlinux_cl-quota.cronlock /usr/bin/cl-quota -YC") ] CPANEL_CRON_TASKS = [ ("*/5", "*", "*", "*", "*", "root", "/usr/bin/flock -n /var/run/cloudlinux_lve_manager.cronlock /usr/share/l.v.e-manager/cpanel/hooks/l.v.e-manager_postupcp_hook.sh --cronjob"), ("*/5", "*", "*", "*", "*", "root", "/usr/bin/flock -n /var/run/detect_edition_changes.cronlock /usr/share/l.v.e-manager/detect_edition_changes.py > /dev/null 2>&1"), ] if is_panel_feature_supported(Feature.LVE): CPANEL_CRON_TASKS.append(("*/5", "*", "*", "*", "*", "root", "/usr/bin/flock -n /var/run/cloudlinux_cl-syncpkg.cronlock /usr/bin/cl-syncpkgs")) CPANEL_SELECTOR_UNPARK_HOOK = SOURCE_PATH + 'commons/lib/selectorhooks/selectorunparkhook.py' CPANEL_SELECTOR_UNPARK_HOOK_SCRIPT = '/scripts/selectorunparkhook.py' CPANEL_OLD_NODEJS_UNPARK_HOOK_SCRIPT = '/scripts/nodejsunparkhook.py' _CPANEL_HOOKS = ( Hook(CPANEL_SELECTOR_UNPARK_HOOK_SCRIPT, 'Whostmgr', 'Domain::unpark', 'post'), ) _CPANEL_LEGACY_HOOKS_TO_REMOVE = ( Hook(CPANEL_OLD_NODEJS_UNPARK_HOOK_SCRIPT, 'Whostmgr', 'Domain::unpark', 'post'), ) # cpanel scripts to install&uninstall end-user plugins CPANEL_PLUGIN_INSTALL_SCRIPT = "/usr/local/cpanel/scripts/install_plugin" CPANEL_PLUGIN_UNINSTALL_SCRIPT = "/usr/local/cpanel/scripts/uninstall_plugin" # list of configs ready to format with theme CPANEL_DYNAMICUI_CONFIGS = [ "/usr/local/cpanel/base/frontend/{}/dynamicui/dynamicui_lvepythonsel.conf", "/usr/local/cpanel/base/frontend/{}/dynamicui/dynamicui_lverubysel.conf", "/usr/local/cpanel/base/frontend/{}/dynamicui/dynamicui_lvephpsel.conf", "/usr/local/cpanel/base/frontend/{}/dynamicui/dynamicui_lvenodejssel.conf", "/usr/local/cpanel/base/frontend/{}/dynamicui/dynamicui_lveresusage.conf", "/usr/local/cpanel/base/frontend/{}/dynamicui/dynamicui_lvewpos.conf" ] # DirectAdmin parametres DA_HOOK_DEST_DIR = '/usr/local/directadmin/scripts/custom' DA_CREATE_USER_HOOK_FILENAME = 'user_create_post.sh' DA_REMOVE_USER_HOOK_FILENAME = 'user_destroy_post.sh' DA_CREATE_USER_HOOK_CMD_OLD = '/usr/bin/da-addsudoer "$username" add_user' DA_REMOVE_USER_HOOK_CMD_OLD = '/usr/bin/da-removesudoer "$username" lve_user' # https://www.directadmin.com/features.php?id=448 DA_CHANGE_DOMAIN_HOOK_CMD = '/usr/bin/da_transit_app "$username" "$domain" "$newdomain"' DA_CHANGE_DOMAIN_HOOK_FILE = 'domain_change_post.sh' DA_CREATE_USER_HOOK_CMD = '/usr/bin/da_add_admin "$username"' DA_REMOVE_USER_HOOK_CMD = '/usr/bin/da_remove_admin "$username"' DA_SUDOERS_USER = 'lvemanager' # Plesk parameters ROOT_PLESK_DIR = "/usr/local/psa/admin/" PLESK_MIGRATOR_DIR = '/usr/local/psa/var/modules/panel-migrator/events/subscription-migration-finished/' PLESK_MIGRATOR_FILE = "PHP_settings_update.py" PLESK_MIGRATOR_HOOK = "/usr/share/l.v.e-manager/hooks/plesk/" + PLESK_MIGRATOR_FILE PANEL_CONFIG = '/opt/cpvendor/etc/integration.ini' # Universal hooks location UNIVERSAL_HOOK_PATH_DNF = '/etc/dnf/universal-hooks/multi_pkgs/transaction' UNIVERSAL_HOOK_PATH_YUM = '/etc/yum/universal-hooks/multi_pkgs/posttrans' UNIVERSAL_HOOK_PATH_APT = '/etc/apt/universal-hooks/multi_pkgs/Post-Invoke' # CloudLinux Summary hooks CL_SUMMARY_HOOK = '/usr/share/l.v.e-manager/hooks/universal/hook_update_cl_summary.py' CL_PACKAGES_WITH_SUMMARY_HOOKS = [ 'alt-php*', 'alt-nodejs*', 'alt-python*', 'alt-ruby*', '*passenger*', 'cagefs', '*lsapi', 'governor-mysql' ] def _open_parent_dir_nofollow(dst_path): """Open the parent directory of *dst_path* with O_DIRECTORY|O_NOFOLLOW. Returns ``(parent_fd, basename)``. Caller MUST ``os.close(parent_fd)``. Rationale (F-42): plugin.conf writes under ``/usr/local/directadmin/plugins/*/`` run from the RPM %post scriptlet as root, but the parent directories are chowned ``diradmin:diradmin`` during install (see ``install_plugin`` line ~659 and ``da_install_user_plugins`` lines ~1239/1249/1259/1268), so a diradmin-privileged attacker can rename either the leaf ``plugin.conf`` or an entire plugin subdirectory into a symlink between installs. Pinning the parent via O_DIRECTORY|O_NOFOLLOW and then operating on the leaf with ``dir_fd=parent_fd`` and O_NOFOLLOW defeats both the leaf-symlink and one-level-up parent-symlink swap. """ parent_dir = os.path.dirname(dst_path) or '.' basename = os.path.basename(dst_path) flags = os.O_RDONLY | os.O_DIRECTORY | os.O_NOFOLLOW | os.O_CLOEXEC parent_fd = os.open(parent_dir, flags) return parent_fd, basename def _read_text_nofollow_at(parent_fd, basename): """Read *basename* under *parent_fd* using O_RDONLY|O_NOFOLLOW|O_CLOEXEC. Raises ``FileNotFoundError`` if the leaf does not exist; raises ``OSError`` with ``errno=ELOOP`` if the leaf is a symlink. """ fd = os.open(basename, os.O_RDONLY | os.O_NOFOLLOW | os.O_CLOEXEC, dir_fd=parent_fd) with os.fdopen(fd, 'r') as f: return f.read() def _write_text_nofollow_at(parent_fd, basename, text, mode=0o644): """Write *text* to *basename* under *parent_fd* using ``O_WRONLY|O_CREAT|O_TRUNC|O_NOFOLLOW|O_CLOEXEC``. O_NOFOLLOW on the leaf refuses to follow a symlink (raises ``OSError`` with ``errno=ELOOP``); O_TRUNC keeps the same-inode overwrite semantics as the previous ``shutil.copy`` / ``open(path, 'w')`` sites. """ flags = (os.O_WRONLY | os.O_CREAT | os.O_TRUNC | os.O_NOFOLLOW | os.O_CLOEXEC) fd = os.open(basename, flags, mode, dir_fd=parent_fd) with os.fdopen(fd, 'w') as f: f.write(text) def touch(fname): try: os.utime(fname, None) except OSError: open(fname, 'a').close() def getItem(txt1, txt2, op): try: i1 = int(txt1) except ValueError: i1 = -1 try: i2 = int(txt2) except ValueError: i2 = -1 if i1 == -1 or i2 == -1: if op == 0: return txt1>txt2 else: return txt1<txt2 else: if op == 0: return i1>i2 else: return i1<i2 #Compare version of types xx.xx.xxx... and yyy.yy.yy.y.. #if xxx and yyy is numbers, than comapre as numbers #else - comapre as strings def verCompare (base, test): base = base.split(".") test = test.split(".") if(len(base)>len(test)): ln = len(test) else: ln = len(base) for i in range(ln): if getItem(base[i],test[i],0): return 1 if getItem(base[i],test[i],1): return -1 if len(base)==len(test): return 0 elif len(base)>len(test): return 1 else: return 0 def usage(): print('') print('Use following syntax to manage CloudLinux Manager install utility:') print(sys.argv[0]+" [OPTIONS]") print('Options:') print(" -i | --install : install lvemanager plugin") print(" --patch-paper-lantern : deprecated; use --postupcp") print(" --postupcp : always run after updating cPanel") print(" -d | --delete : delete lvemanager plugin") print(" -u | --update : update lvemanager plugin") def exit_with_error(message, status=1): sys.stderr.write("Error: %s\n" % (str(message,))) sys.exit(status) def add_sudoers(user): try: from clsudo import Clsudo sudo = Clsudo() sudo.add_user(user) except Exception as e: exit_with_error(e) def install_cpanel_icons(plugin_name): try: base_dir = SOURCE_PATH + "cpanel/" + plugin_name + "/icon/" themes = os.listdir(base_dir) for theme in themes: icons = os.listdir(base_dir + theme) for icon_file in icons: if os.path.isfile(base_dir + theme + "/" + icon_file): # print 'copy ' +base_dir + theme + "/" + icon_file + ' to ' + CPANEL_ICON_BASE_DIR + theme + '/branding/' if verCompare (cp.version, "11.46.0") >= 0: if os.path.isdir(CPANEL_ICON_BASE_DIR + theme + '/styled/basic/icons/'): shutil.copy(base_dir + theme + "/" + icon_file, CPANEL_ICON_BASE_DIR + theme + '/styled/basic/icons/') else: if os.path.isdir(CPANEL_ICON_BASE_DIR + theme + '/branding/'): shutil.copy(base_dir + theme + "/" + icon_file, CPANEL_ICON_BASE_DIR + theme + '/branding/') except (OSError, IOError): pass #skip all errors; def list_patches(reverse=False): if not os.path.exists(CPANEL_PATCHES_PATH): return [] comparator = lambda first, second: int(first.split('_', 1)[0]) - int(second.split('_', 1)[0]) lst = os.listdir(CPANEL_PATCHES_PATH) lst = [x for x in lst if x.endswith('.patch')] lst.sort(cmp=comparator, reverse=reverse) return lst def check_if_backup_older_than_month(file_): """ :param file_: file name :return: True if file older then month. False if can't parse date or not older than month """ try: file_datetime = datetime.datetime.strptime(file_.split("_")[-2], "%Y-%m-%d") except (ValueError, IndexError): return False if file_datetime < datetime.datetime.now() - datetime.timedelta(days=30): return True else: return False def delete_old_papper_latern_backups(dir_with_backups=CPANEL_PAPER_LATERN_HOME): filelist = os.listdir(dir_with_backups) filelist = [x for x in filelist if (x.startswith("stats_display_name.html.tt.orig_") or x.startswith("stats_bar.html.tt.orig_")) and check_if_backup_older_than_month(x)] for f in filelist: try: os.remove(os.path.join(dir_with_backups, f)) except OSError: pass print("Deleted " + str(len(filelist)) + \ " old backup files ( for stats_display_name.html.tt and stats_bar.html.tt) in directory " + dir_with_backups) def cpanel_use_patches(): if verCompare(cp.version, "69.9032") >= 0: return delete_old_papper_latern_backups() patches = list_patches() for patch_file_name in patches: full_patch_path = os.path.join(CPANEL_PATCHES_PATH, patch_file_name) if os.path.exists(full_patch_path): exec_command("patch -t -F3 -b -z .orig_$(date +%%F_%%R) -r- -p0 -d / -N -i %s" % full_patch_path) def cpanel_rollback_patches(): if verCompare(cp.version, "69.9032") >= 0: return patches = list_patches(reverse=True) for patch_file_name in patches: full_patch_path = os.path.join(CPANEL_PATCHES_PATH, patch_file_name) if os.path.exists(full_patch_path): exec_command("patch -R -F3 -t -b -z .orig_$(date +%%F_\%%R) -r- -p0 -d / -N -i %s" % full_patch_path) def restore_cpanel_statsbar(): subprocess.run('/bin/bash /usr/share/l.v.e-manager/cpanel/patches/restore-statsbar.sh', shell=True, executable='/bin/bash') def add_lve_limits_to_stats_bar(): if verCompare(cp.version, "69.9032") >= 0: restore_cpanel_statsbar() if not os.path.exists(CPANEL_CUSTOM_PATH): os.makedirs(CPANEL_CUSTOM_PATH) shutil.copyfile(SOURCE_PATH + 'cpanel/stats_bar/Cloudlinux.pm', CPANEL_CUSTOM_PATH + 'Cloudlinux.pm') def grep(line, f): stream = open(f) for line_ in stream: if line in line_: stream.close() return True stream.close() return False def patch_cpanel_config(): exec_command(SOURCE_PATH + 'cpanel/utils/dynamicuictl.py --verbose --sync-conf=all --no-rebuild-sprites') # CLOS-5790: the SPA moved to Angular 22 and the vendored jQuery 1.11.3 / Bootstrap JS were # dropped from commons/spa-resources. CopyDirectory only copies, it never prunes, so on an # upgrade these files keep sitting in the panels' docroots - web-served, not owned by the # package, and still reported by vulnerability scanners. Delete them explicitly, the same way # LVEMAN-1479 handles clinfo.php below. RETIRED_SPA_RESOURCE_FILES = ( 'jquery.min.js', 'bootstrap.min.js', 'common.js', ) # cpanel/utils/install_module.pl copies /usr/share/l.v.e-manager/cpanel/spa/assets/ # into /usr/local/cpanel/whostmgr/docroot/3rdparty/cloudlinux/, so its js/ lands in a # directory the package does not own either. These three names went away with the # Angular 22 migration; tooltip.min.js is the Bootstrap 3 tooltip plugin. The four # *_config.js files that still ship in that directory must stay. RETIRED_CPANEL_SPA_FILES = ( 'tooltip.min.js', 'testPOST.js', 'common.js', ) def _unlink_nofollow_walk(base_dir, relative_path): """Unlink *relative_path* under *base_dir* without traversing any symlink. Every directory component of *relative_path* is opened with ``O_DIRECTORY|O_NOFOLLOW`` relative to the previous component's fd, so a symlink planted at *any* intermediate component is refused rather than followed. Same threat as F-42 (see ``_open_parent_dir_nofollow``), but the paths here are several levels deep, so pinning only the immediate parent would still leave ``images``, ``assets`` and the plugin directory itself swappable. *base_dir* must therefore be a root-owned anchor. Returns ``True`` when the leaf was removed. """ components = [part for part in relative_path.split('/') if part not in ('', '.')] if not components or '..' in components: return False # A trailing slash makes the kernel skip O_NOFOLLOW on the final component. anchor = base_dir.rstrip('/') or '/' dir_flags = os.O_RDONLY | os.O_DIRECTORY | os.O_NOFOLLOW | os.O_CLOEXEC open_fds = [] try: open_fds.append(os.open(anchor, dir_flags)) for component in components[:-1]: open_fds.append(os.open(component, dir_flags, dir_fd=open_fds[-1])) os.unlink(components[-1], dir_fd=open_fds[-1]) return True except OSError: return False finally: for fd in reversed(open_fds): try: os.close(fd) except OSError: pass def remove_retired_spa_resources(base_dir, assets_subdir, filenames=RETIRED_SPA_RESOURCE_FILES): """Remove vendored JS that no longer ships, from a tree we previously copied into. *base_dir* is a root-owned anchor; *assets_subdir* is the path below it that received ``commons/spa-resources``. The DirectAdmin targets are chowned ``diradmin:diradmin`` by this same script, so the removal must not follow symlinks planted there between installs. """ for basename in filenames: relative_path = '/'.join((assets_subdir.strip('/'), 'js', basename)) if _unlink_nofollow_walk(base_dir, relative_path): print('Removed retired vendored asset ' + os.path.join(base_dir.rstrip('/'), relative_path)) def plesk_commons(): cpdir = CopyDirectory(SPA_RESOURCE_DIR, ROOT_PLESK_DIR + 'htdocs/modules/plesk-lvemanager') cpdir.process() # LVEMAN-1479: remove /usr/local/psa/admin/htdocs/modules/plesk-lvemanager/misc/clinfo.php try: os.unlink(ROOT_PLESK_DIR + 'htdocs/modules/plesk-lvemanager/misc/clinfo.php') except OSError: pass remove_retired_spa_resources(ROOT_PLESK_DIR, 'htdocs/modules/plesk-lvemanager') def cpanel_commons(): CopyDirectory(SPA_RESOURCE_DIR, ROOT_CPANEL_DIR + '3rdparty/cloudlinux/assets').process() remove_retired_spa_resources(ROOT_CPANEL_DIR, '3rdparty/cloudlinux/assets') # install_module.pl mirrors cpanel/spa/assets/ one level higher, so the retired # files from that tree sit in 3rdparty/cloudlinux/js/ rather than .../assets/js/. remove_retired_spa_resources(ROOT_CPANEL_DIR, '3rdparty/cloudlinux', filenames=RETIRED_CPANEL_SPA_FILES) create_symlink(ROOT_CPANEL_DIR + '3rdparty/cloudlinux', ROOT_CPANEL_DIR + 'cgi/cloudlinux') def da_commons(): CopyDirectory(SPA_RESOURCE_DIR, ROOT_DA_DIR + 'lvemanager_spa/images/assets').process() CopyDirectory(SOURCE_PATH + 'directadmin/assets', ROOT_DA_DIR + 'lvemanager_spa/images/assets').process() DA_PLUGIN_NAMES = ['nodejs_selector', 'python_selector', 'phpselector', 'resource_usage'] for plugin_name in DA_PLUGIN_NAMES: CopyDirectory(ROOT_DA_DIR + 'lvemanager_spa/images/assets', ROOT_DA_DIR + plugin_name + '/images/assets')\ .process() # Clean after the fan-out: the plugin directories were populated from the shared assets # dir, so both sides can still hold the retired files from an earlier version. remove_retired_spa_resources(ROOT_DA_DIR, 'lvemanager_spa/images/assets') for plugin_name in DA_PLUGIN_NAMES: remove_retired_spa_resources(ROOT_DA_DIR, plugin_name + '/images/assets') def install_commons(): panels = { 'Plesk': plesk_commons, 'cPanel': cpanel_commons, 'DirectAdmin': da_commons } try: panels[cp.name]() except KeyError as e: pass def migrate_ui_configs(panel_name): """ Migrate all of the UI configs from the old config file to the new lvemanager-config.json file. :param panel_name: name of the panel """ plesk_conf_file = '/usr/local/psa/admin/plib/modules/lvemanager.conf' if (not os.path.exists(UI_CONFIG_PATH) and panel_name in ['cPanel', 'DirectAdmin', 'Plesk']) \ or (os.path.exists(plesk_conf_file) and panel_name == 'Plesk'): print ('Migrating {} UI configs to new UI config file...'.format(panel_name)) configs = { 'cPanel': {'path': '/var/cpanel/cpanel.config', 'params': ['lve_showinodeusage', 'lve_hideuserstat', 'lve_hideextensions', 'lve_enablerubyapp', 'lve_enablepythonapp', 'lve_hide_selector']}, 'DirectAdmin': {'path': '/usr/local/directadmin/plugins/phpselector/plugin.conf', 'params': ['active']}, 'Plesk': {'path': plesk_conf_file, 'params': ['enable_selector']} } parsed_config = {} if os.path.exists(configs[panel_name]['path']): try: f = open(configs[panel_name]['path']) config_lines = f.readlines() f.close() except (OSError, IOError) as e: print(e) else: config_lines = '' try: for idx in range(0, len(config_lines)): line_parts = config_lines[idx].strip().split('=') if len(line_parts) != 2: continue if line_parts[0] in configs[panel_name]['params']: try: value = line_parts[1] if not isinstance(value, bool): if value.lower() in ['y', '1', 'on', 'true', 'yes']: value = True else: value = False parsed_config[line_parts[0]] = value except ValueError: parsed_config[line_parts[0]] = False except (KeyError, IndexError) as e: print(e) result_config = dict() if panel_name == 'cPanel': result_config['inodeLimits'] = {'showUserInodesUsage': parsed_config.get('lve_showinodeusage')} result_config['uiSettings'] = {'hideLVEUserStat': parsed_config.get('lve_hideuserstat'), 'hidePHPextensions': parsed_config.get('lve_hideextensions'), 'hideRubyApp': not parsed_config.get('lve_enablerubyapp'), 'hidePythonApp': not parsed_config.get('lve_enablepythonapp'), 'hidePhpApp': parsed_config.get('lve_hide_selector')} elif panel_name == 'DirectAdmin': result_config = UIConfig().get_config() result_config['uiSettings']['hidePhpApp'] = False if parsed_config.get('active') else True else: result_config = UIConfig().get_config() result_config['uiSettings']['hidePhpApp'] = False if parsed_config.get('enable_selector') else True if os.path.exists(configs[panel_name]['path']): os.remove(configs[panel_name]['path']) with open(UI_CONFIG_PATH, 'w') as new_file: json.dump({'ui_config': result_config}, new_file) def check_and_rename_ui_setting(old_key, new_key): """ Read and rename key in uiSettings from /usr/share/l.v.e-manager/lvemanager-config.json if required """ if not os.path.exists(UI_CONFIG_PATH): return with open(UI_CONFIG_PATH, 'r') as json_file: data = json.load(json_file) if old_key in data.get("ui_config", {}).get("uiSettings", {}): data["ui_config"]["uiSettings"][new_key] = data["ui_config"]["uiSettings"].pop(old_key) with open(UI_CONFIG_PATH, 'w') as json_file: json.dump(data, json_file) def fix_php_conf(): """ for details please see LVEMAN-1215: fix syntax in /etc/cl.selector/php.conf for old installations of lvemanager """ if not os.path.isfile(CL_SELECTOR_PHP_CONF_PATH): return replace_values = [ { 's_from': '\nComment = If PHP is in the safe mode and a script tries to access some files, files from t' \ 'his directory will bypass security (UID/GID) checks. Th\ne directory must also be in include_p' \ 'ath. For example: /dir/inc\n', 's_to': '\nComment = If PHP is in the safe mode and a script tries to access some files, files from thi' \ 's directory will bypass security (UID/GID) checks. The directory must also be in include_path. F' \ 'or example: /dir/inc\n' }, { 's_from': '\nRange = ~E_ALL,E_ALL & ~E_NOTICE,E_ALL\n', 's_to': '\nRange = ~E_ALL,E_ALL & ~E_NOTICE,E_ALL,E_ALL & ~E_DEPRECATED & ~E_STRICT\n' } ] with open(CL_SELECTOR_PHP_CONF_PATH, 'r') as f: php_conf = f.read() for item in replace_values: if item['s_from'] in php_conf: php_conf = php_conf.replace(item['s_from'], item['s_to']) with open(CL_SELECTOR_PHP_CONF_PATH, 'w') as f: f.write(php_conf) def create_selector_config_dir(selector_name): """ Create config dir for NodeJS/Python/etc selector in easy mountable location and create symlink from /etc to that location for details see CAG-797: mount NodeJS Selector config directory into CageFS and CAG-828: Add python-selector.json to cagefs :param selector_name: name of selector: nodejs, python, etc """ selector_cfg_dir = os.path.join(SOURCE_PATH, 'cl.{}'.format(selector_name)) # symlink for ease of use the selector config and avoiding problem with cagefs selector_cfg_symlink = '/etc/cl.{}'.format(selector_name) if not os.path.isdir(selector_cfg_dir): mod_makedirs(selector_cfg_dir, 0o755) create_symlink(selector_cfg_dir, selector_cfg_symlink) def get_universal_hook_path(package) -> Path: """ Get path to universal hooks directory based on the package name pattern """ dir_name = package.replace('*', '__WILDCARD__') if Path('/etc/apt/').exists(): return Path(UNIVERSAL_HOOK_PATH_APT, dir_name) elif Path('/etc/dnf/').exists(): return Path(UNIVERSAL_HOOK_PATH_DNF, dir_name) return Path(UNIVERSAL_HOOK_PATH_YUM, dir_name) def install_cl_summary_universal_hooks(): """ Install cloudlinux-summary hooks. Needed to update statistics on install packages from CLI """ for package in CL_PACKAGES_WITH_SUMMARY_HOOKS: hook_dir_path = get_universal_hook_path(package) hook_dir_path.mkdir(parents=True, exist_ok=True) hook_name = Path(CL_SUMMARY_HOOK).name hook_full_path = Path(hook_dir_path, hook_name) if not hook_full_path.exists(): hook_full_path.symlink_to(CL_SUMMARY_HOOK) def uninstall_cl_summary_universal_hooks(): """ Uninstall cloudlinux-summary hooks """ for package in CL_PACKAGES_WITH_SUMMARY_HOOKS: hook_dir_path = get_universal_hook_path(package) hook_name = Path(CL_SUMMARY_HOOK).name hook_full_path = Path(hook_dir_path, hook_name) if hook_full_path.is_symlink(): hook_full_path.unlink() def install_plugin(tp): print('Starting {} script...\n'.format(tp)) # install into crontab remove_cron(LVEMANAGER_CRON) remove_cron('lvemanager-selector') if is_panel_feature_supported(Feature.RUBY_SELECTOR): add_cron('lvemanager-selector', 'r', 'r', '*', '*', '*', 'root', '/usr/bin/flock -n /var/run/cloudlinux_cache_ruby.cronlock ' + SOURCE_PATH + 'utils/cache_rubygems.py') if is_panel_feature_supported(Feature.PHP_SELECTOR): add_cron('lvemanager-selector', '*/5', '*', '*', '*', '*', 'root', '/usr/bin/flock -n /var/run/cloudlinux_cache_php.cronlock ' + SOURCE_PATH + 'utils/cache_phpdata.py') for task in CRON_TASKS: add_cron(LVEMANAGER_CRON, *task, check_command=False) fix_php_conf() create_selector_config_dir('nodejs') create_selector_config_dir('python') # install only. rewrite all existing file update_php_config() # Remember version and release. remember_version() # Detect the first installation. create_first_installation_flags() # Add cloudlinux-server-flags add_cl_server_flags() # migrating ui configs migrate_ui_configs(cp.name) # rename hideWPOSApp to hideAccelerateWPApp if old config check_and_rename_ui_setting('hideWPOSApp', 'hideAccelerateWPApp') # install cloudlinux-summary hooks install_cl_summary_universal_hooks() if cp.name == "Plesk" and verCompare (cp.version, "10") >= 0: # Checking the install version 2.0 if (LVEMANAGER_VERSION == '2.0' and not os.path.exists(ROOT_PLESK_DIR + 'htdocs/modules/plesk-lvemanager/lvemanager_spa')) \ or (LVEMANAGER_VERSION == '1.0' and os.path.exists(ROOT_PLESK_DIR + 'htdocs/modules/plesk-lvemanager/lvemanager_spa')): delete_plugin() add_sudoers("psaadm") exec_command(SOURCE_PATH+"utils/generate_locale_files_for_plesk.py") # Prepare static files for SPA cpdir = CopyDirectory(SPA_RESOURCE_DIR+'static/', SOURCE_PATH+'plesk/htdocs/modules/plesk-lvemanager/static/') cpdir.process() cpdir = CopyDirectory(SOURCE_PATH+"plesk", ROOT_PLESK_DIR) cpdir.process() if os.path.exists(SOURCE_PATH+"utils/install-plesk-plugin.sh"): exec_command(SOURCE_PATH+"utils/install-plesk-plugin.sh") if os.path.exists(SOURCE_PATH+"utils/plesk-cgi.sh"): if not os.path.exists("/usr/share/l.v.e-manager/plesk/script/"): os.makedirs("/usr/share/l.v.e-manager/plesk/script/") shutil.copyfile(SOURCE_PATH+"utils/plesk-cgi.sh", "/usr/share/l.v.e-manager/plesk/script/plesk-cgi.sh") if os.path.exists(SOURCE_PATH+"utils/installsupergrp.sh"): exec_command(SOURCE_PATH+"utils/installsupergrp.sh psaadm") register_plesk_plugin() # install migration hook for Plesk 12.5+ if verCompare(cp.version, "12.5") >= 0: # Plesk 12.5+ if not os.path.exists(PLESK_MIGRATOR_DIR): os.makedirs(PLESK_MIGRATOR_DIR) print("%s --> %s" % (PLESK_MIGRATOR_HOOK, PLESK_MIGRATOR_DIR+PLESK_MIGRATOR_FILE)) shutil.copy(PLESK_MIGRATOR_HOOK, PLESK_MIGRATOR_DIR) # CLOS-3894: disable nginxServePhp for PHP Selector domains _plesk_sync_nginx_serve_php() elif cp.name == "cPanel": # Checking the install version 2.0 if (LVEMANAGER_VERSION == '2.0' and not os.path.exists(ROOT_CPANEL_DIR + '3rdparty/cloudlinux/assets/lvemanager_spa') and not os.path.exists('/var/lve/lvemanager_is_not_installed')) \ or (LVEMANAGER_VERSION == '1.0' and os.path.exists(ROOT_CPANEL_DIR + '3rdparty/cloudlinux/assets/lvemanager_spa')): delete_plugin() # Prepare static files for SPA cpdir = CopyDirectory(SPA_RESOURCE_DIR+'static/', SOURCE_PATH+'cpanel/spa/assets/static/') cpdir.process() if verCompare(cp.version, "59.9999") < 0: print("Warning!!! You use old version of CPanel. Some functional may not work correctly.") # fix JSON-styled configs exec_command(SOURCE_PATH + 'cpanel/utils/dynamicuictl.py --fix-dynamicui') cpdir = CopyDirectory(SOURCE_PATH + "cpanel/images", ROOT_CPANEL_DIR + "images/") cpdir.process() # Reinstall hooks from scratch to prevent garbage in cases where some # files were renamed or removed if os.path.exists(SOURCE_PATH + "cpanel/hooks/cpanel-delete-hooks"): exec_command(SOURCE_PATH + "cpanel/hooks/cpanel-delete-hooks") if os.path.exists(SOURCE_PATH + "cpanel/hooks/cpanel-install-hooks"): exec_command(SOURCE_PATH + "cpanel/hooks/cpanel-install-hooks") # delete old hooks that are now moved to cllib if os.path.exists(SOURCE_PATH + "cpanel/hooks/cpanel-delete-unused-hooks"): exec_command(SOURCE_PATH + "cpanel/hooks/cpanel-delete-unused-hooks") cpanel_install_user_plugins_from_tar() cpanel_use_patches() patch_cpanel_config() if os.path.exists(SOURCE_PATH+"cpanel/utils/install_module.pl"): action_key = ( ( tp == "install" and '-i' ) or '-u' ) exec_command("perl "+SOURCE_PATH+"cpanel/utils/install_module.pl " + action_key) for task in CPANEL_CRON_TASKS: add_cron(LVEMANAGER_CRON, *task, check_command=False) subprocess.run("/usr/local/cpanel/bin/sprite_generator --all", shell=True, executable='/bin/bash') # delete old hooks cause it is not deleted automatically during update remove_hooks(_CPANEL_LEGACY_HOOKS_TO_REMOVE) if os.path.isfile(CPANEL_SELECTOR_UNPARK_HOOK): shutil.copy(CPANEL_SELECTOR_UNPARK_HOOK, CPANEL_SELECTOR_UNPARK_HOOK_SCRIPT) install_hooks(_CPANEL_HOOKS) register_cpanel_plugin() exec_command(SOURCE_PATH + 'utils/dynamicui.py --silent --fix-display-plugin') exec_command(SOURCE_PATH + 'cpanel/cpanel_hooks_manage.py -i') elif cp.name == "DirectAdmin": if (LVEMANAGER_VERSION == '2.0' and not os.path.exists(ROOT_DA_DIR + 'lvemanager_spa/lvemanager_spa')) \ or (LVEMANAGER_VERSION == '1.0' and os.path.exists(ROOT_DA_DIR + 'lvemanager_spa/lvemanager_spa')): delete_plugin() if (not os.path.isdir(ROOT_DA_DIR)): os.mkdir(ROOT_DA_DIR) exec_command("chown -R diradmin:diradmin "+ROOT_DA_DIR) exec_command("chmod -R 755 "+ROOT_DA_DIR) CopyDirectory(SOURCE_PATH+"directadmin/lvemanager_spa/", ROOT_DA_DIR+"lvemanager_spa/").process() update_da_plugin_conf(SOURCE_PATH+"directadmin/lvemanager_spa/plugin.conf", ROOT_DA_DIR+"lvemanager_spa/plugin.conf") # Scanner note (F-43, CLOS-5557, 2026-07-17): `chown -R` here (and at # lines 660, 1239, 1249, 1259, 1268) flagged as re-ownable via a # diradmin-planted symlink. Refuted: GNU coreutils `chown -R` defaults # to FTS_PHYSICAL / AT_SYMLINK_NOFOLLOW (coreutils/src/chown-core.c) -- # symlinks encountered mid-walk are `lchown`'d, not followed. # Verified on coreutils >=8.22 (CL7/8/9/10, Ubuntu 20+). exec_command("chown -R diradmin:diradmin "+ROOT_DA_DIR+"lvemanager_spa") exec_command("chmod -R 755 "+ROOT_DA_DIR+"lvemanager_spa/admin") exec_command("chmod -R 755 " + ROOT_DA_DIR + "lvemanager_spa/reseller") exec_command("chmod -R 755 "+ROOT_DA_DIR+"lvemanager_spa/user") if not is_panel_feature_supported(Feature.RESELLER_LIMITS): # We do not need to show plugin for reseller if reseller_limits not supported if os.path.exists(ROOT_DA_DIR+"lvemanager_spa/reseller"): shutil.rmtree(ROOT_DA_DIR+"lvemanager_spa/reseller") da_install_user_plugins() # Clear old versions files clear_da_old_files() # Fix possibly sysctl options out of sync - LVEMAN-867 exec_command("/sbin/sysctl -p") # Install DA create user hook install_da_hook() # Add all DA admins to sudoers and clsupergid group from clcommon.cpapi import admins for user in admins(): try: # Add user to sudoers/clsupergid groups and add sudoer group to /etc/sudoers add_unix_user_to_sudoers(user) except Exception as e: print("Unable to add unix user %s to sudoers group; error: %s\n" % (user, str(e))) # Create user for run reseller plugin create_da_lvemanager_user() else: from clcommon import cpapi for user in cpapi.admins(): try: # Add user to sudoers/clsupergid groups and add sudoer group to /etc/sudoers add_unix_user_to_sudoers(user) except Exception as e: print("Unable to add unix user %s to sudoers group; error: %s\n" % (user, str(e))) # convert locales files for SPA (from yaml to json) exec_command(SOURCE_PATH+"utils/sync_locales.py --panel='"+cp.name+"'") install_commons() # Install to custom panel if exists custom_panel_features() print('Finished {} script...\n'.format(tp)) # Install DA create user hook def install_da_hook(): from clcommon.public_hooks.bundle.directadmin import remove_da_hook # Remove old hooks remove_da_hook(DA_CREATE_USER_HOOK_FILENAME, DA_CREATE_USER_HOOK_CMD_OLD) remove_da_hook(DA_REMOVE_USER_HOOK_FILENAME, DA_REMOVE_USER_HOOK_CMD_OLD) remove_da_hook(DA_CREATE_USER_HOOK_FILENAME, DA_CREATE_USER_HOOK_CMD) remove_da_hook(DA_REMOVE_USER_HOOK_FILENAME, DA_REMOVE_USER_HOOK_CMD) remove_da_hook(DA_CHANGE_DOMAIN_HOOK_FILE, DA_CHANGE_DOMAIN_HOOK_CMD) def clear_da_old_files(): if os.path.exists(ROOT_DA_DIR+"new_lvemanager"): shutil.rmtree(ROOT_DA_DIR+"new_lvemanager") def create_da_lvemanager_user(): PLUGIN_FILE = ROOT_DA_DIR + 'lvemanager_spa/plugin.conf' remove_da_custom_lvemanager_users() new_user_name = prepare_da_system_lvemanager_user() if new_user_name != DA_SUDOERS_USER: # F-42: mirror the update_da_plugin_conf hardening. lvemanager_spa/ # is chowned diradmin during install_plugin (line ~669), so diradmin # can rename plugin.conf to a symlink or swap lvemanager_spa itself # for a symlink. Pin the parent via O_DIRECTORY|O_NOFOLLOW and use # dir_fd + O_NOFOLLOW on the leaf to defeat both swaps. Errors # propagate to match the previous fail-loud behaviour of this # function (a missing PLUGIN_FILE was a hard install error before). parent_fd, basename = _open_parent_dir_nofollow(PLUGIN_FILE) try: file_str = _read_text_nofollow_at(parent_fd, basename) file_str = file_str.replace( 'reseller_run_as=lvemanager', 'reseller_run_as=' + new_user_name) _write_text_nofollow_at(parent_fd, basename, file_str) finally: os.close(parent_fd) def remove_da_custom_lvemanager_users(): import pwd lvemanager_users = [p[0] for p in pwd.getpwall() if p[0].startswith('lvemanager_')] for username in lvemanager_users: exec_command('userdel {}'.format(username)) def prepare_da_system_lvemanager_user(): new_user_name = DA_SUDOERS_USER has_panel_user = os.path.exists('/usr/local/directadmin/data/users/lvemanager') has_nonsystem_user = False if exec_command_check('id {}'.format(DA_SUDOERS_USER)): user_info = exec_command('id {}'.format(DA_SUDOERS_USER)) if 'clsudoers' in user_info[0]: print("Remove old lvemanager user") exec_command('userdel {}'.format(DA_SUDOERS_USER)) else: has_nonsystem_user = True if has_panel_user or has_nonsystem_user: if not exec_command_check('id {}'.format(DA_SUDOERS_USER)): # return panel user exec_command('adduser --home /home/lvemanager lvemanager') # system username for plugin new_user_name = '{}_{}'.format(DA_SUDOERS_USER, uuid.uuid4().hex)[:32] print('Warning!!! Non-system user "lvemanager" exists. Will be used {} as system user.'.format(new_user_name)) print("Adding {} user".format(new_user_name)) groups = ['clsupergid', 'clsudoers'] if is_panel_feature_supported(Feature.LVE) else ['clsudoers'] exec_command( 'adduser --system -s /bin/false --groups {} --home /usr/share/l.v.e-manager {}'.format( ','.join(groups), new_user_name)) return new_user_name def delete_plugin(spa_install=True): delete_first_installation_flags() # Uninstall cloudlinux-summary hooks uninstall_cl_summary_universal_hooks() # remove crontab files if not spa_install: remove_cron('lvemanager-selector') remove_cron(LVEMANAGER_CRON) # remove installed files if cp.name == "Plesk" and verCompare (cp.version, "10") >= 0: exec_command('/usr/local/psa/bin/extension --uninstall plesk-lvemanager') if os.path.exists(PLESK_MIGRATOR_DIR+PLESK_MIGRATOR_FILE): os.remove(PLESK_MIGRATOR_DIR+PLESK_MIGRATOR_FILE) elif cp.name == "cPanel": exec_command(SOURCE_PATH+"cpanel/hooks/cpanel-delete-hooks") remove_hooks(_CPANEL_HOOKS) if verCompare(cp.version, "65.9999") >=0: exec_command("sed -i -e '/^#WHMADDON:lvemanager:CloudLinux Manager/d' "+CPANEL_ORIGINAL_FILES_DIR+"CloudLinux.cgi") if os.path.exists(SOURCE_PATH+"cpanel/utils/install_module.pl"): exec_command("perl "+SOURCE_PATH+"cpanel/utils/install_module.pl -d") if os.path.exists(ROOT_CPANEL_DIR+"images/lve"): shutil.rmtree(ROOT_CPANEL_DIR+"images/lve") # if os.path.exists(ROOT_CPANEL_DIR+"images/CloudLinux_logo.png"): # os.remove(ROOT_CPANEL_DIR+"images/CloudLinux_logo.png") if os.path.exists(ROOT_CPANEL_DIR+"images/spa"): shutil.rmtree(ROOT_CPANEL_DIR+"images/spa") # delete end-user plugins (lveversion) cpanel_uninstall_user_plugin_by_tar(SOURCE_PATH + "cpanel/lveversion/user-plugins.tar.bz2") # Deleting new resource usage plugin cpanel_uninstall_user_plugin_by_tar(SOURCE_PATH + "cpanel/resource_usage/plugin.tar.bz2") # Remove links from themes cpanel_remove_links_from_themes() subprocess.run("/usr/local/cpanel/bin/rebuild_sprites --force", shell=True, executable='/bin/bash') if verCompare(cp.version, "69.9032") >= 0: custom_path = CPANEL_CUSTOM_PATH + 'Cloudlinux.pm' if os.path.exists(custom_path): os.unlink(custom_path) # rollback patches cpanel_rollback_patches() exec_command(SOURCE_PATH + 'cpanel/cpanel_hooks_manage.py -d') elif cp.name == "InterWorx": if os.path.exists(ROOT_IWORX_DIR+"lvemanager"): shutil.rmtree(ROOT_IWORX_DIR+"lvemanager") elif cp.name == "ISPManager": if verCompare(cp.version, "5") == -1: # ISP Manager 4 if os.path.exists(ROOT_ISPMGR_DIR+"addon/lvemanager"): shutil.rmtree(ROOT_ISPMGR_DIR+"addon/lvemanager") if (os.path.exists("/usr/local/ispmgr/addon/lvemanager.php")): os.remove("/usr/local/ispmgr/addon/lvemanager.php") if (os.path.exists("/usr/local/ispmgr/addon/LVEPackage.php")): os.remove("/usr/local/ispmgr/addon/LVEPackage.php") if (os.path.exists("/usr/local/ispmgr/addon/LVEPackageUser.php")): os.remove("/usr/local/ispmgr/addon/LVEPackageUser.php") if (os.path.exists("/usr/local/ispmgr/etc/ispmgr_mod_lvemanager.xml")): os.remove("/usr/local/ispmgr/etc/ispmgr_mod_lvemanager.xml") if (os.path.exists("/usr/local/ispmgr/etc/ispmgr_mod_lvepreset.xml")): os.remove("/usr/local/ispmgr/etc/ispmgr_mod_lvepreset.xml") if (os.path.exists("/usr/local/ispmgr/skins/mobile/mb-lve.png")): os.remove("/usr/local/ispmgr/skins/mobile/mb-lve.png") if (os.path.exists("/usr/local/ispmgr/skins/mobile/mb-resource.png")): os.remove("/usr/local/ispmgr/skins/mobile/mb-resource.png") if (os.path.exists("/usr/local/ispmgr/skins/mobile/m-lve.png")): os.remove("/usr/local/ispmgr/skins/mobile/m-lve.png") if (os.path.exists("/usr/local/ispmgr/skins/mobile/m-resource.png")): os.remove("/usr/local/ispmgr/skins/mobile/m-resource.png") if (os.path.exists("/usr/local/ispmgr/skins/mobile/refresh_auto.png")): os.remove("/usr/local/ispmgr/skins/mobile/refresh_auto.png") if (os.path.exists("/usr/local/ispmgr/skins/mobile/refresh.png")): os.remove("/usr/local/ispmgr/skins/mobile/refresh.png") if (os.path.exists("/usr/local/ispmgr/skins/sirius/mb-lve.png")): os.remove("/usr/local/ispmgr/skins/sirius/mb-lve.png") if (os.path.exists("/usr/local/ispmgr/skins/sirius/mb-resource.png")): os.remove("/usr/local/ispmgr/skins/sirius/mb-resource.png") if (os.path.exists("/usr/local/ispmgr/skins/sirius/m-lve.png")): os.remove("/usr/local/ispmgr/skins/sirius/m-lve.png") if (os.path.exists("/usr/local/ispmgr/skins/sirius/m-resource.png")): os.remove("/usr/local/ispmgr/skins/sirius/m-resource.png") if (os.path.exists("/usr/local/ispmgr/skins/sirius/refresh_auto.png")): os.remove("/usr/local/ispmgr/skins/sirius/refresh_auto.png") if (os.path.exists("/usr/local/ispmgr/skins/sirius/refresh.png")): os.remove("/usr/local/ispmgr/skins/sirius/refresh.png") # Clear ISP Manager's cache and restart it shutil.rmtree('/usr/local/ispmgr/var/.xmlcache/ispmgr', True) exec_command("/usr/bin/killall ispmgr") exec_command(SOURCE_PATH+"utils/delete-isp-plugin.sh") elif cp.name == "DirectAdmin": # Remove dirs if os.path.exists(ROOT_DA_DIR+"new_lvemanager"): shutil.rmtree(ROOT_DA_DIR+"new_lvemanager") if os.path.exists(ROOT_DA_DIR+"resource_usage"): shutil.rmtree(ROOT_DA_DIR+"resource_usage") if os.path.exists(ROOT_DA_DIR+"phpselector"): shutil.rmtree(ROOT_DA_DIR+"phpselector") if os.path.exists(ROOT_DA_DIR+"selector"): shutil.rmtree(ROOT_DA_DIR+"selector") if os.path.exists(ROOT_DA_DIR+"lvemanager_spa"): shutil.rmtree(ROOT_DA_DIR+"lvemanager_spa") if os.path.exists(ROOT_DA_DIR + "nodejs_selector"): shutil.rmtree(ROOT_DA_DIR + "nodejs_selector") if os.path.exists(ROOT_DA_DIR + "python_selector"): shutil.rmtree(ROOT_DA_DIR + "python_selector") else: print("Current panel unsupported. Panel name: "+cp.name+" version: "+cp.version) if os.path.exists(SOURCE_PATH+"utils/downgrades/clear-reseller.py"): subprocess.run(SOURCE_PATH+"utils/downgrades/clear-reseller.py --unregister", shell=True, executable='/bin/bash') if os.path.exists(SOURCE_PATH+"utils/downgrades/clear-new-resource-usage.py"): subprocess.run(SOURCE_PATH+"utils/downgrades/clear-new-resource-usage.py", shell=True, executable='/bin/bash') if os.path.exists(SOURCE_PATH + "utils/downgrades/migrate-ui-configs.py"): subprocess.run(SOURCE_PATH + "utils/downgrades/migrate-ui-configs.py", shell=True, executable='/bin/bash') def update_php_config(): """ Adds defaults for error_reporting option (to avoid empty value) and tries to keep all user changes untouched """ data = {} data_index = [] if not os.path.isfile(CL_SELECTOR_PHP_CONF_PATH): return fd = open(CL_SELECTOR_PHP_CONF_PATH,'r') directive = None for line in fd: if '=' not in line: continue if line.startswith('Directive'): directive = line[ line.find('=')+1 : ].strip() data_index.append(directive) data[directive] = {} if not 'data' in data[directive]: data[directive]['data'] = {} if not 'index' in data[directive]: data[directive]['index'] = [] continue if not directive: continue ( key, value ) = list(map( ( lambda x: x.strip() ), line.split('=', 1) )) data[directive]['index'].append(key.lower()) data[directive]['data'][ key.lower() ] = value fd.close() if 'error_reporting' not in data: return if 'default' in data['error_reporting']['data'] and data['error_reporting']['data']['default'] != "": return if 'default' not in data['error_reporting']['data']: data['error_reporting']['index'].insert(0,'default') data['error_reporting']['data']['default'] = 'E_ALL & -E_NOTICE' fd = open( CL_SELECTOR_PHP_CONF_PATH, 'w' ) for directive in data_index: fd.write( "Directive = %s\n" % directive ) for item in data[directive]['index']: fd.write( "%s = %s\n" % ( item.capitalize().ljust(9), data[directive]['data'][item]) ) fd.write("\n") fd.close() def cp_supported(): if cp.name == "Plesk" and verCompare (cp.version, "10") >= 0: return True if cp.name in ("cPanel", "DirectAdmin"): return True return False class ControlPanel: name = '' varsion = '' def __init__(self, name, version): self.name = name self.version = version def postupcp(): cpanel_install_user_plugins_from_tar() cpanel_use_patches() add_lve_limits_to_stats_bar() install_cpanel_icons('resourceusage') install_cpanel_icons('lveversion') exec_command(SOURCE_PATH + 'cpanel/utils/dynamicuictl.py --verbose --sync-conf=all') def get_release_number(): """ Get release number from release name """ matchOb = re.match('^\d+(\.\d+)?', LVEMANAGER_RELEASE, re.I) if matchOb: release = matchOb.group() else: release = LVEMANAGER_RELEASE return release def register_cpanel_plugin(): if verCompare(cp.version, "63.9999") >=0: if verCompare(cp.version, '65.9999') >= 0: cpanel_config = 'cpanel66' else: cpanel_config = 'cpanel64' else: cpanel_config = 'native' print('\n'.join(exec_command(SOURCE_PATH + 'utils/install-cpanel-plugin.sh unregister'))) print('\n'.join(exec_command(SOURCE_PATH + 'utils/install-cpanel-plugin.sh register %s' % cpanel_config))) def _plesk_sync_nginx_serve_php(): """ Disable nginxServePhp for PHP Selector compatible domains (CLOS-3894). Non-fatal: log warnings but do not fail the installation. """ sync_script = SOURCE_PATH + 'utils/plesk_nginx_php_sync.py' if not os.path.exists(sync_script): return try: subprocess.check_call( [sys.executable, sync_script], timeout=300, ) except Exception as e: print("Warning: plesk_nginx_php_sync failed: %s" % e, file=sys.stderr) def register_plesk_plugin(): fd = open(ROOT_PLESK_DIR+"plib/modules/plesk-lvemanager/meta.xml", 'w') release_number = get_release_number() text = """<?xml version="1.0" encoding="utf-8"?> <module> <id>plesk-lvemanager</id> <name>CloudLinux Manager</name> <description>CloudLinux Manager ({vr})</description> <url>http://cloudlinux.com/</url> <version>{version}</version> <release>{release}</release> <vendor>Cloud Linux</vendor> <icon>/images/modules/plesk-lvemanager/addon_CloudLinux_logo.png</icon> </module>""" fd.write(text.format( vr='%s-%s' % (LVEMANAGER_VERSION, release_number), version=LVEMANAGER_VERSION, release=release_number) ) fd.close() exec_command('/usr/local/psa/bin/extension --register plesk-lvemanager') def remember_version(): """ Remember version and release. """ version_info = "{}-{}".format(LVEMANAGER_VERSION, get_release_number()) version_file_path = "{}version".format(SOURCE_PATH) with open(version_file_path, 'w') as version_file: version_file.write(version_info) def update_da_plugin_conf(src_path, dst_path): # F-42: refuse to follow symlinks at dst_path or its immediate parent. # The parent plugin subdirectories under /usr/local/directadmin/plugins/ # are chowned diradmin:diradmin (see install_plugin / da_install_user_plugins); # this function runs as root from the RPM %post scriptlet, so a plain # ``config.read`` / ``shutil.copy`` / ``open(dst, 'w')`` could be redirected # by a diradmin-planted symlink at either the leaf or the containing # plugin subdirectory. ``_open_parent_dir_nofollow`` + dir_fd + O_NOFOLLOW # on the leaf closes both primitives. from io import StringIO try: parent_fd, basename = _open_parent_dir_nofollow(dst_path) except OSError as e: print("Warning: Cann't open parent dir of {}: {}".format(dst_path, e)) return try: # Read existing config (if any) under the pinned parent. ELOOP means # a symlink was planted at the leaf — refuse the whole update rather # than silently fall through to a fresh-template write. active_value = None installed_value = None try: existing_text = _read_text_nofollow_at(parent_fd, basename) except FileNotFoundError: existing_text = None except OSError as e: print("Warning: Cann't open {}: {}".format(dst_path, e)) return if existing_text is not None: existing = SectionlessConfigParser(strict=False) try: existing.readfp(StringIO(existing_text)) except Exception: print("Warning: Cann't open {}".format(dst_path)) return active_value = existing.get( '__config__', 'active', fallback=None) installed_value = existing.get( '__config__', 'installed', fallback=None) # Load the fresh template from src_path. src_path lives under the # RPM-owned /usr/share/l.v.e-manager/ tree so a plain open is safe. config = SectionlessConfigParser(strict=False) try: with open(src_path, 'r') as srcf: config.readfp(srcf) except Exception: print("Warning: Cann't update {}".format(dst_path)) return # Preserve old values previously stored in config. Other values will # be inherited from the default template (matches prior behaviour). if active_value: config.set('__config__', 'active', active_value) if installed_value: config.set('__config__', 'installed', installed_value) buf = StringIO() config.write(buf) try: _write_text_nofollow_at(parent_fd, basename, buf.getvalue()) except OSError as e: print("Warning: Cann't save {}: {}".format(dst_path, e)) return finally: os.close(parent_fd) FIRST_INSTALL_FLAG = '/var/lve/wizard/is_first_installation.flag' CLDEPLOY_IGNORE_FLAG = '/var/lve/wizard/ignore_cldeploy.flag' LVEMANAGER_RPM_FIRST_INSTALL_FLAG = '/var/lve/lvemanager_is_not_installed' def create_first_installation_flags(): """ Detect the first installation of CloudLinux Manager and create flags (files) """ if (os.path.exists(LVEMANAGER_RPM_FIRST_INSTALL_FLAG) or (not os.path.isfile('/var/log/cldeploy.log') and not os.path.isfile(CLDEPLOY_IGNORE_FLAG))): if not os.path.exists(os.path.dirname(FIRST_INSTALL_FLAG)): os.makedirs(os.path.dirname(FIRST_INSTALL_FLAG)) touch(FIRST_INSTALL_FLAG) touch(CLDEPLOY_IGNORE_FLAG) try: os.unlink(LVEMANAGER_RPM_FIRST_INSTALL_FLAG) except OSError: pass def delete_first_installation_flags(): """ Delete first installation flags """ for path in (FIRST_INSTALL_FLAG, CLDEPLOY_IGNORE_FLAG, LVEMANAGER_RPM_FIRST_INSTALL_FLAG): try: os.unlink(path) except OSError: pass def add_cl_server_flags(): """ Create CloudLinux server flags """ CopyDirectory(CLOUDLINUX_SERVER_FLAGS_SOURCE_DIR, CLOUDLINUX_SERVER_FLAGS_DESTINATION_DIR).process() def custom_panel_features(): """ Copy lvemanager files to directory defined in panel config file install and run python server if required """ if not os.path.isfile(PANEL_CONFIG): return parser = ConfigParser.SafeConfigParser(interpolation=None, strict=False) parser.read(PANEL_CONFIG) try: no_panel_base_path = parser.get('lvemanager_config', 'base_path') if no_panel_base_path: print('Copy files for other panels to ' + no_panel_base_path) CopyDirectory(SOURCE_PATH + 'panelless-version/lvemanager', no_panel_base_path).process() CopyDirectory(SPA_RESOURCE_DIR, no_panel_base_path + '/assets').process() remove_retired_spa_resources(no_panel_base_path, 'assets') except ConfigParser.NoSectionError: print('WARNING: Cannot copy files for no panel version') try: run_service = parser.get('lvemanager_config', 'run_service') if run_service and int(run_service): print('Enable and start lvemanager') if os.path.exists('/usr/bin/systemctl'): exec_command('systemctl enable lvemanager') exec_command('systemctl restart lvemanager') else: exec_command('chkconfig lvemanager on') exec_command('service lvemanager restart') except ConfigParser.NoSectionError: print('WARNING: Cannot copy files for no panel version') try: base_path = parser.get('lvemanager_config', 'base_path') vendor_php = parser.get('lvemanager_config', 'vendor_php') if os.path.isfile(vendor_php): shutil.copy2(vendor_php, base_path) except ConfigParser.NoSectionError: print('WARNING: Cannot copy vendor.php file for no panel version') except ConfigParser.NoOptionError: pass def cpanel_install_user_plugins_from_tar(): # Install PHP Selector for end-user if is_panel_feature_supported(Feature.PHP_SELECTOR): selector_install_json_path_user_plugins = SOURCE_PATH + "cpanel/lveversion/user-plugins/selector/" selector_plugin_tar_user_plugins = SOURCE_PATH + "cpanel/lveversion/user-plugins.tar.bz2" cpanel_install_user_plugin_from_tar(selector_install_json_path_user_plugins, selector_plugin_tar_user_plugins) cpanel_fix_feature_manager(['cpanel/lveversion/user-plugins/selector/install.json']) # install NodeJS Selector if is_panel_feature_supported(Feature.NODEJS_SELECTOR): nodejs_install_json_path_user_plugins = SOURCE_PATH + "cpanel/lveversion/user-plugins/nodejssel/" nodejs_plugin_tar_user_plugins = SOURCE_PATH + "cpanel/lveversion/user-plugins.tar.bz2" cpanel_install_user_plugin_from_tar(nodejs_install_json_path_user_plugins, nodejs_plugin_tar_user_plugins) cpanel_fix_feature_manager(['cpanel/lveversion/user-plugins/nodejssel/install.json']) # install Python Selector if is_panel_feature_supported(Feature.PYTHON_SELECTOR): python_install_json_path_user_plugins = SOURCE_PATH + "cpanel/lveversion/user-plugins/pythonsel/" python_plugin_tar_user_plugins = SOURCE_PATH + "cpanel/lveversion/user-plugins.tar.bz2" cpanel_install_user_plugin_from_tar(python_install_json_path_user_plugins, python_plugin_tar_user_plugins) cpanel_fix_feature_manager(['cpanel/lveversion/user-plugins/pythonsel/install.json']) # install Ruby Selector if is_panel_feature_supported(Feature.RUBY_SELECTOR): ruby_install_json_path_user_plugins = SOURCE_PATH + "cpanel/lveversion/user-plugins/rubysel/" ruby_plugin_tar_user_plugins = SOURCE_PATH + "cpanel/lveversion/user-plugins.tar.bz2" cpanel_install_user_plugin_from_tar(ruby_install_json_path_user_plugins, ruby_plugin_tar_user_plugins) cpanel_fix_feature_manager(['cpanel/lveversion/user-plugins/rubysel/install.json']) # Install Resource usage install_json_path_resource_usage = SOURCE_PATH + "cpanel/resource_usage/plugin/" plugin_tar_resource_usage = SOURCE_PATH + "cpanel/resource_usage/plugin.tar.bz2" cpanel_install_user_plugin_from_tar(install_json_path_resource_usage, plugin_tar_resource_usage) if is_panel_feature_supported(Feature.LVE): add_lve_limits_to_stats_bar() cpanel_fix_feature_manager(['cpanel/resource_usage/plugin/install.json']) def cpanel_install_user_plugin_from_tar(install_json_path, plugin_tar): """ Install plugin for end-user using script /usr/local/cpanel/scripts/install_plugin. To install we need to call the script like /usr/local/cpanel/scripts/install_plugin plugin.tar.bz2 --theme theme. The action should be performed for each theme. :param install_json_path: string (path where located install.json for the plugin) :param plugin_tar: string (path to plugin .tar.bz2 file) :return: """ if os.path.exists(install_json_path): exec_command("/bin/tar -cjf {} -C {} .".format(plugin_tar, install_json_path)) if os.path.exists(plugin_tar) and os.path.exists(CPANEL_PLUGIN_INSTALL_SCRIPT): for theme in cpanel_get_theme_list(): exec_command("{} {} --theme {}".format(CPANEL_PLUGIN_INSTALL_SCRIPT, plugin_tar, theme)) def cpanel_uninstall_user_plugin_by_tar(plugin_tar): """ Uninstall plugin for end-user using script /usr/local/cpanel/scripts/uninstall_plugin. To remove we need to call the script like /usr/local/cpanel/scripts/uninstall_plugin plugin.tar.bz2 --theme theme. To fully remove we need to call this script for each available theme. :param plugin_tar: string (path to plugin .tar.bz2 file) :return: None """ if os.path.exists(plugin_tar) and os.path.exists(CPANEL_PLUGIN_UNINSTALL_SCRIPT): for theme in cpanel_get_theme_list(): exec_command("{} {} --theme {}".format(CPANEL_PLUGIN_UNINSTALL_SCRIPT, plugin_tar, theme)) def cpanel_get_theme_list(): """ Get list of themes for cpanel. Each folder in /usr/local/cpanel/base/frontend/ represent the theme :return: list """ if os.path.isdir(CPANEL_ICON_BASE_DIR): return next(os.walk(CPANEL_ICON_BASE_DIR), (None, None, []))[1] def cpanel_remove_links_from_themes(): """ Remove links for each theme and each plugin :return: None """ for theme in cpanel_get_theme_list(): for config in CPANEL_DYNAMICUI_CONFIGS: config_file = config.format(theme) if os.path.exists(config_file): exec_command("rm {}".format(config_file)) def da_install_user_plugins(): """ Install only end-user plugins for DirectAdmin """ # PHP Selector if is_panel_feature_supported(Feature.PHP_SELECTOR): CopyDirectory(SOURCE_PATH+"directadmin/phpselector/", ROOT_DA_DIR+"phpselector/").process() update_da_plugin_conf(SOURCE_PATH + "directadmin/phpselector/plugin.conf", ROOT_DA_DIR + "phpselector/plugin.conf") exec_command("chown -R diradmin:diradmin "+ROOT_DA_DIR+"phpselector") exec_command("chmod -R 755 "+ROOT_DA_DIR+"phpselector") exec_command("chmod 644 "+ROOT_DA_DIR+"phpselector/plugin.conf") # Resource usage CopyDirectory(SOURCE_PATH+"directadmin/resource_usage/", ROOT_DA_DIR+"resource_usage/").process() if os.path.isfile(ROOT_DA_DIR+"new_lvemanager/plugin.conf"): shutil.copy(ROOT_DA_DIR + "new_lvemanager/plugin.conf", ROOT_DA_DIR + "resource_usage/plugin.conf") update_da_plugin_conf(SOURCE_PATH + "directadmin/resource_usage/plugin.conf", ROOT_DA_DIR + "resource_usage/plugin.conf") exec_command("chown -R diradmin:diradmin "+ROOT_DA_DIR+"resource_usage") exec_command("chmod -R 755 "+ROOT_DA_DIR+"resource_usage") exec_command("chmod -R 755 "+ROOT_DA_DIR+"resource_usage/user") exec_command("chmod 644 "+ROOT_DA_DIR+"resource_usage/plugin.conf") # Node.js Selector if is_panel_feature_supported(Feature.NODEJS_SELECTOR): CopyDirectory(SOURCE_PATH+"directadmin/nodejs_selector/", ROOT_DA_DIR+"nodejs_selector/").process() update_da_plugin_conf(SOURCE_PATH + "directadmin/nodejs_selector/plugin.conf", ROOT_DA_DIR + "nodejs_selector/plugin.conf") exec_command("chown -R diradmin:diradmin "+ROOT_DA_DIR+"nodejs_selector") exec_command("chmod -R 755 "+ROOT_DA_DIR+"nodejs_selector/user") exec_command("chmod 644 "+ROOT_DA_DIR+"nodejs_selector/plugin.conf") # Python Selector if is_panel_feature_supported(Feature.PYTHON_SELECTOR): CopyDirectory(SOURCE_PATH + "directadmin/python_selector/", ROOT_DA_DIR + "python_selector/").process() update_da_plugin_conf(SOURCE_PATH + "directadmin/python_selector/plugin.conf", ROOT_DA_DIR + "python_selector/plugin.conf") exec_command("chown -R diradmin:diradmin "+ROOT_DA_DIR+"python_selector") exec_command("chmod -R 755 "+ROOT_DA_DIR+"python_selector/user") exec_command("chmod 644 "+ROOT_DA_DIR+"python_selector/plugin.conf") if __name__ == "__main__": detect.getCP() cp = ControlPanel(detect.CP_NAME, detect.CP_VERSION) try: opts, args = getopt.getopt(sys.argv[1:], "hidu", ["help", "postupcp", "install", "delete", "update"]) except getopt.GetoptError as err: # print help information and exit: print(str(err)) # will print something like "option -a not recognized" usage() sys.exit(2) for o, a in opts: if o in ("-h", "--help"): usage() sys.exit() elif o in ("-i", "--install"): install_plugin("install") elif o in ("-d", "--delete"): delete_plugin(spa_install=False) elif o in ("-u", "--update"): install_plugin("update") elif o in ("--postupcp",): postupcp() else: usage() sys.exit(2)
| ver. 1.4 |
Github
|
.
| PHP 8.1.34 | Generation time: 0.07 |
proxy
|
phpinfo
|
Settings