/
opt
/
cloudlinux
/
venv
/
lib
/
python3.11
/
site-packages
/
xray
/
reconfiguration
/
Upload Filee
HOME
# -*- coding: utf-8 -*- # Copyright © Cloud Linux GmbH & Cloud Linux Software, Inc 2010-2021 All Rights Reserved # # Licensed under CLOUD LINUX LICENSE AGREEMENT # http://cloudlinux.com/docs/LICENSE.TXT import logging import os import re import pwd from contextlib import suppress from glob import iglob from typing import Tuple from secureio import disable_quota from xray.internal.utils import user_context logger = logging.getLogger() GLOBAL_INI_MARKER = '/opt/cloudlinux/flags/enabled-flags.d/xray-ini-global-mode.flag' # directories that don't matter for us # php is either outdated or just internal _EXCLUDE_DIR_PATHS = ( 'php44', 'php51', 'php52', 'php53', 'php\d+-imunify', 'php-internal' ) # global set of ini locations where php # usually loads configuration files from # some of them may be missing, like /opt/plesk # which only exists on plesk _INI_LOCATIONS = ( '/opt/alt/php[0-9][0-9]/link/conf', '/opt/cpanel/ea-php[0-9][0-9]/root/etc/php.d', '/opt/plesk/php/[0-9].[0-9]/etc/php.d', '/usr/local/php[0-9][0-9]/lib/php.conf.d', '/usr/share/cagefs/.cpanel.multiphp/opt/cpanel/ea-php[0-9][0-9]/root/etc/php.d', '/usr/share/cagefs-skeleton/usr/local/php[0-9][0-9]/lib/php.conf.d' ) # same as above, but ini which are writeable by users _INI_USER_LOCATIONS = ( dict(path='/var/cagefs/*/*/etc/cl.php.d/alt-php[0-9][0-9]', user=lambda path: pwd.getpwnam(path.split('/')[4])), ) def _is_excluded_path(dir_path: str) -> list: """ Check if given path is in exclude list. """ res = [substring for substring in _EXCLUDE_DIR_PATHS if re.search(substring, dir_path)] return res def _iter_existing_ini_locations() -> Tuple[Tuple[int, int], str]: """ Generator of existing paths (matching known wildcard locations) for additional ini files Returns tuple of (uid, gid) and path. """ for location in _INI_LOCATIONS: for dir_path in iglob(location): if _is_excluded_path(dir_path): continue yield (0, 0), dir_path for location in _INI_USER_LOCATIONS: for dir_path in iglob(location['path']): if _is_excluded_path(dir_path): continue try: pw_record = location['user'](dir_path) except: logger.info('Unable to get information about user ' 'owning %s directory (maybe he`s already terminated?), ' 'skip updating', dir_path) continue else: yield (pw_record.pw_uid, pw_record.pw_gid), dir_path def _create_single_ini(uid: int, gid: int, ini_path: str): # write counter of tasks so during mode switch # we can still safely cleanup ini files not # bound to any exiting tasks ini_content = ';xray.tasks=0\nextension=xray.so' path = os.path.join(ini_path, 'xray.ini') if os.path.exists(path): return with user_context(uid, gid), \ disable_quota(), \ open(path, 'w') as ini: logger.info('Generating %s file...', path) ini.write(ini_content) def is_global_ini_mode(): return os.path.exists(GLOBAL_INI_MARKER) def create_global_ini_mode_marker(): open(GLOBAL_INI_MARKER, 'w').close() def remove_global_ini_mode_marker(): with suppress(FileNotFoundError): os.remove(GLOBAL_INI_MARKER) def create_ini_files() -> None: """ Place xray.ini into each existing Additional ini path, including cagefs ones. """ logger.info('Generating xray.ini files...') for (uid, gid), ini_path in _iter_existing_ini_locations(): try: _create_single_ini(uid, gid, ini_path) except PermissionError: logger.warning('Unable to update file %s, ' 'possible permission misconfiguration', ini_path) continue except Exception as e: logger.warning('Unexpected error happened during file processing: ' '"%s", error: "%s"', ini_path, str(e), exc_info=True) continue logger.info('Finished!') def remove_ini_files() -> None: """ Remove all gathered clos_ssa.ini files """ logger.info('Removing clos_ssa.ini files...') for (uid, gid), clos_ini_dir in _iter_existing_ini_locations(): ini_file = os.path.join(clos_ini_dir, 'xray.ini') try: with user_context(uid, gid), open(ini_file) as f: contents = f.read() # unlink file only if there are no linked tasks # case with minus sign covers negative values if "xray.tasks=0" in contents or \ "xray.tasks=-" in contents: os.unlink(ini_file) except FileNotFoundError: continue except Exception as e: logger.warning('Unable to remove file: "%s", error: "%s"', ini_file, str(e)) continue logger.info('Finished!')