D7net
Home
Console
Upload
information
Create File
Create Folder
About
Tools
:
/
proc
/
self
/
root
/
opt
/
cloudlinux
/
venv
/
lib
/
python3.11
/
site-packages
/
clselect
/
Filename :
clselectctlphp.py
back
Copy
#!/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 absolute_import from __future__ import division import subprocess import sys import getopt import os import clcommon import traceback import simplejson as json import shutil import pwd from builtins import map from .clselect import ClSelect from .clextselect import ClExtSelect, depend_modules_dict from .cluserselect import ClUserSelect from .cluserextselect import ClUserExtSelect from .cluseroptselect import ClUserOptSelect from .clselectprint import clprint from clcommon import ClPwd from clcommon.const import Feature from clcommon.cpapi import is_panel_feature_supported from clcommon.sysctl import SysCtlConf, SYSCTL_CL_CONF_FILE from clcommon.utils import run_command, ExternalProgramFailed from clcommon.lib.cledition import is_ubuntu from .utils import in_cagefs, make_symlink from .clselectexcept import ClSelectExcept, BaseClSelectException # This is the oldest version with all bad things API_0 = 0 # This version replaces hardcoded interpreter name in utility output with more # generic key names that will be correctly understood by cloudlinux-selector API_1 = 1 # Path to cagefs command CAGEFSCTL_COMMAND = '/usr/sbin/cagefsctl' def usage(): print(' -v | --version : Specify alternative version') print(' -u | --user : Username') print(' -l | --list : List alternatives for interpreter') print(' -G | --list-extensions : List global set of extensions for alternative') print(' -S | --summary : List summary of alternatives') print(' -s | --user-summary : List user summary of alternatives') print(' -C | --current : Show currently selected alternative') print(' -c | --user-current : Show currently selected alternative for a user') print(' -B | --set-current : Set alternative as global default') print(' -b | --set-user-current : Set alternative as user default') print(' -Y | --enable-alternative : Enable alternative globally') print(' -N | --disable-alternative : Disable alternative globally') print(' -E | --enable-extensions : Enable comma-separated list of extensions globally for a version') print(' -D | --disable-extensions : Disable comma-separated list of extensions globally for a version') print(' -R | --replace-extensions : Replace extensions with comma-separated list of extensions ') print(' for a version globally') print(' -e | --enable-user-extensions : Enable comma-separated list of extensions for a user') print(' -d | --disable-user-extensions : Disable comma-separated list of extensions for a user') print(' -r | --replace-user-extensions : Replace user extensions with comma-separated list of extensions') print(' -t | --reset-user-extensions : Replace user extensions with version default extensions') print(' -g | --list-user-extensions : List enabled extensions for a user. With key --all shows all extensions') print(' -a | --all : Show all extensions') print(' -p | --print-summary : If specified along with setting an alternative prints user summary') print(' -V | --show-native-version : Shows native version while showing summary or selected version') print(' -L | --list-users : List users who use a specified alternative') print(' -T | --change-to-version : Changes to a specified version all users who have a certain version') print(' -k | --add-options : Add comma-separated list options for a user') print(' -m | --replace-options : Replace user options with comma-separated list of options') print(' -x | --delete-options : Delete comma-separated list options for a user') print(' -Q | --base64 : Expects data as comma-separated base64-encoded string') print(' -q | --quiet : Suppress errors messages for wrong input') print(' -P | --print-options : Prints user options. By default prints as plain text') print(' --print-options-safe : Prints user options. By default prints as plain text (safe strings)') print(' --apply-symlinks-rules : Recreate symlinks to php extensions for all users ') print(' based on /etc/cl.selector/symlinks.rules file') print(' --exclude-pid-list : Specify list of PIDs of processes that should not be signaled by SIGHUP') print(' -j | --json : Print data as JSON') print(' -w | --csv : Print data as CSV') print(' -W | --perl : Print data as perl structure') print(' --api-version : Integer, representing specific api version to use. ') print(' Defaults to {}'.format(API_0)) print(' -z | --reset-options : Deletes all user custom options. Range can be narrowed with user ') print(' or version options') print(' --update-backup : write settings to backup') print(' --apply-global-php-ini : use with 0, 1 or 2 arguments from the list: error_log, date.timezone') print(' without arguments applies all global php options including two above') print(' --setup-without-cagefs : setup PHP Selector without CageFS') print(' --revert-to-cagefs : revert to default setup of PHP Selector (with CageFS)') print(' --for-all-users : applies specified action for all users in CageFS. ') print(' Available only for enable/disable user extensions') def print_error_and_exit(message, prefix=None): """ Prints to stderr @param message: string """ fmt = "%s\n" if prefix: fmt = "%s:%s\n" % (prefix, '%s') sys.stderr.write(fmt % message) sys.exit(1) def check_args_presence(): """ Checks presence of command line arguments and exits with usage info if missing """ if len(sys.argv) == 1: print_error_and_exit( "Command line arguments expected. " "For help use '-h' or '--help' options") def get_name_modifier(version): """ """ BASE_ALT_DIR = '/opt/alt/php' ver = version.replace('.','') name_modifier_file = BASE_ALT_DIR + ver + '/name_modifier' name_modifier = '' if os.path.isfile(name_modifier_file): try: name_modifier = open(name_modifier_file,'r').readline().strip() except (OSError, IOError): return '' return name_modifier def letter_to_status(letter): if letter == '-': return 'disabled' elif letter == 'e': return 'enabled' def format_summary(data, format='text', api_version=API_0): if api_version == API_0: available_versions_key = 'PHPConfiguration' default_version_key = 'defaultPHPversion' else: available_versions_key = 'available_versions' default_version_key = 'default_version' states = ['e', 'd', 's'] text_lines = [] json_dict = { available_versions_key: [], } for alt in data: row_length = len(alt[1]) fmt = ' '.join(['%s'] * (row_length+1)) row_data = [alt[0]] row_data.extend(list(map( (lambda i: ((alt[1][i] and states[i]) or '-')), range(row_length)))) # pylint: disable=range-builtin-not-iterating name_modifier = get_name_modifier(alt[0]) if name_modifier != "": fmt = ' '.join(['%s'] * (row_length+2)) row_data.append(name_modifier) if format == 'text': text_lines.append(fmt % tuple(row_data)) if 'd' in row_data: json_dict[default_version_key] = row_data[0] json_dict[available_versions_key].append({ 'version': row_data[0], 'status': letter_to_status(row_data[1]), 'name_modifier': name_modifier, }) if format == 'json': return json.dumps(json_dict) elif format == 'text': return '\n'.join(text_lines) def print_summary(data, format='text', api_version=API_0): """ Prints alternatives summary """ data = format_summary(data, format, api_version) print(data) def check_params(config, param_list): """ Check that config has param_list and this params not None """ for param in param_list: if param not in config or config[param] == None: print_error_and_exit("Error: %s must be specified" % param) def ext_letter_to_status(letter): if letter == '~': return 'build-in' elif letter == '+': return 'enabled' else: return 'disabled' def fill_descriptions(tmp_list): descr_file = '/etc/cl.selector.conf.d/phpextdesc.txt' if in_cagefs() else '/etc/cl.selector/phpextdesc.txt' with open(descr_file) as f: desct_content = f.readlines() full_desct_dict = {} for line in desct_content: line_parts = line.split('=') full_desct_dict[line_parts[0]] = line_parts[1].strip() for item in tmp_list: try: item['description'] = full_desct_dict[item['name']] except KeyError: # skip extention without description pass return tmp_list def print_json_status_ok(): result_dict = {'status': 'ok'} print(json.dumps(result_dict)) def get_cpanel_user(): """ Return user (name of cpanel account) for PHP Selector without CageFS feature """ if os.path.isfile(ClSelect.USER_CONF): with open(ClSelect.USER_CONF, 'r') as f: return f.read().strip() for user in ClPwd().get_user_dict(): if os.path.exists('/var/cpanel/users/'+user): return user return None def set_cpanel_user(user): """ Set user (name of cpanel account) for PHP Selector without CageFS feature """ with open(ClSelect.USER_CONF, 'w') as f: f.write(user) os.chmod(ClSelect.USER_CONF, 0o644) def switch_linksafe(enable=False): if not is_panel_feature_supported(Feature.LVE): return new_conf = '/etc/sysctl.d/cloudlinux-linksafe.conf' if os.path.isfile(new_conf): conf = new_conf else: conf = SYSCTL_CL_CONF_FILE sysctl_cfg = SysCtlConf(config_file=conf) sysctl_cfg.set('fs.protected_symlinks_create', str(int(enable))) sysctl_cfg.set('fs.protected_hardlinks_create', str(int(enable))) if subprocess.call('sysctl --system &>/dev/null', shell=True, executable='/bin/bash') != 0: print('Error while executing: sysctl --system') sys.exit(1) def modify_search_path_in_bashrc(user, homedir, obj=None, add=True): """ Add path to PATH variable in ~/.bashrc :param user: name of user :type user: string :param homedir: path to home directory :type homedir: string :param obj: instance of ClUserSelect class :type obj: instance of ClUserSelect class :param add: add path to .bashrc when True, remove otherwise :type obj: bool """ if obj is None: obj = ClUserSelect('php') cur_user = obj._change_uid(user) # On Ubuntu we are using ~/.profile # On CL - ~/.bashrc if is_ubuntu(): bashrc = homedir + '/.profile' else: bashrc = homedir + '/.bashrc' line = 'PATH=$HOME/'+ClUserSelect.SELECTOR2_DIR+':$HOME/.cl.selector:$PATH' with open(bashrc, 'r') as f: found = line+'\n' in f if add: if not found: with open(bashrc, 'a') as f: f.write('\n'+line+'\n') else: if found: clcommon.utils.delete_line_from_file(bashrc, line) obj._restore_uid(cur_user) def restore_settings_from_backup(user, homedir, uid, alt_dirs, obj=None): """ Restore (apply) settings for PHP Selector from backup :param user: name of user :type user: string :param homedir: path to user's home directory :type homedir: string :param uid: user's uid :type uid: int :param alt_dirs: list of alt-php directories like ['php51', 'php52'] :type alt_dirs: list :param obj: instance of ClUserSelect class :type obj: ClUserSelect object """ def cleanup(): # Delete unneeded files shutil.rmtree(os.path.join('/var/cagefs', str(uid)[-2:]), True) shutil.rmtree('/usr/share/cagefs/etc', True) shutil.rmtree('/usr/share/cagefs/etc.new', True) if obj is None: obj = ClUserSelect('php') base_dest_path = homedir + '/.cl.selector' cleanup() # Generate alt_php.ini for all versions using cagefsctl if subprocess.call('/usr/sbin/cagefsctl --silent --force-update-etc ' + user, shell=True, executable='/bin/bash') != 0: sys.exit(1) cur_user = obj._change_uid(user) # Copy generated alt_php.ini files to new location in user's home directory base_src_path = os.path.join('/var/cagefs', str(uid)[-2:], user, 'etc', 'cl.php.d') for alt_dir in alt_dirs: src_path = base_src_path + '/alt-' + alt_dir + '/alt_php.ini' dest_path = base_dest_path + '/alt_' + alt_dir + '.ini' shutil.copy(src_path, dest_path) # Select php version from backup (or default when backup does not exist) obj.set_version_from_backup(user) shutil.rmtree(homedir+'/.cagefs', True) obj._restore_uid(cur_user) cleanup() def disable_cagefs_service(): if os.path.isfile('/usr/bin/systemctl'): subprocess.run('/usr/bin/systemctl disable cagefs', shell=True, executable='/bin/bash') subprocess.run('/usr/bin/systemctl stop cagefs', shell=True, executable='/bin/bash') subprocess.run('/usr/bin/systemctl mask cagefs', shell=True, executable='/bin/bash') else: subprocess.run('/sbin/service cagefs stop &> /dev/null', shell=True, executable='/bin/bash') subprocess.run('/sbin/chkconfig cagefs off', shell=True, executable='/bin/bash') def enable_cagefs_service(): if os.path.isfile('/usr/bin/systemctl'): subprocess.run('/usr/bin/systemctl unmask cagefs', shell=True, executable='/bin/bash') subprocess.run('/usr/bin/systemctl enable cagefs', shell=True, executable='/bin/bash') subprocess.run('/usr/bin/systemctl start cagefs', shell=True, executable='/bin/bash') else: subprocess.run('/sbin/chkconfig cagefs on', shell=True, executable='/bin/bash') subprocess.run('/sbin/service cagefs start &> /dev/null', shell=True, executable='/bin/bash') def setup_without_cagefs(args): """ Setup PHP Selector without CageFS """ sys.path.append('/usr/share/cagefs') try: import cagefslib except ImportError: print('Error: CageFS is not installed') sys.exit(1) # alt-php versions are installed ? alt_dirs = cagefslib.get_alt_dirs() if not alt_dirs: print('alt-php not found') sys.exit(1) # detect cpanel user if args: user = args[0] set_cpanel_user(user) else: user = get_cpanel_user() if not user: print('Error: failed to detect cpanel account. Please specify name of an account as argument:') print('selectorctl --setup-without-cagefs USER') sys.exit(1) if not os.path.exists(ClSelect.USER_CONF): set_cpanel_user(user) # disable linksafe protection switch_linksafe() pw = pwd.getpwnam(user) homedir = pw.pw_dir # create symlinks to user's alt_php.ini files for alt_dir in alt_dirs: alt_path = '/opt/alt/' + alt_dir + '/link/conf/alt_php.ini' user_path = homedir + '/.cl.selector/alt_' + alt_dir + '.ini' make_symlink(user_path, alt_path) obj = ClUserSelect('php') restore_settings_from_backup(user, homedir, pw.pw_uid, alt_dirs, obj) obj.create_selector_symlinks(user) modify_search_path_in_bashrc(user, homedir, obj) disable_cagefs_service() # kill user's processes in LVE subprocess.run('/usr/sbin/lvectl destroy ' + str(pw.pw_uid) + ' &>/dev/null; /usr/sbin/lvectl apply ' + str(pw.pw_uid) + ' &>/dev/null', shell=True, executable='/bin/bash') def revert_to_cagefs(): """ Revert to default PHP Selector setup with CageFS """ if not os.path.exists(ClSelect.USER_CONF): print('PHP Selector is in default mode already ("with CageFS" mode)') sys.exit(1) sys.path.append('/usr/share/cagefs') try: import cagefslib except ImportError: print('Error: CageFS is not installed') sys.exit(1) # alt-php versions are installed ? alt_dirs = cagefslib.get_alt_dirs() if not alt_dirs: print('alt-php not found') sys.exit(1) switch_linksafe(enable=True) # delete symlinks to user's alt_php.ini files for alt_dir in alt_dirs: alt_path = '/opt/alt/' + alt_dir + '/link/conf/alt_php.ini' if os.path.islink(alt_path): os.unlink(alt_path) user = get_cpanel_user() if not user: print('Error: failed to detect user') sys.exit(1) pw = pwd.getpwnam(user) homedir = pw.pw_dir obj = ClUserSelect('php') modify_search_path_in_bashrc(user, homedir, obj, add=False) # remove config file (file-switch) for "without CageFS" mode os.unlink(ClSelect.USER_CONF) # Generate alt_php.ini for all versions using cagefsctl subprocess.run('/usr/sbin/cagefsctl --silent --force-update-etc ' + user, shell=True, executable='/bin/bash') enable_cagefs_service() # kill user's processes in LVE subprocess.run('/usr/sbin/lvectl destroy ' + str(pw.pw_uid) + ' &>/dev/null; /usr/sbin/lvectl apply ' + str(pw.pw_uid) + ' &>/dev/null', shell=True, executable='/bin/bash') def apply_global_php_ini(args): """ Apply "global" php.ini settings to all alt-php versions :param args: list of command line parameters (names of php.ini options) :type args: list """ sys.path.append('/usr/share/cagefs') try: import cagefslib import cagefsreconfigure except ImportError: print('Error: CageFS is not installed') sys.exit(1) # alt-php versions are installed ? if cagefslib.get_alt_versions(): cagefsreconfigure.replace_alt_settings(options=args) def _check_depencies_and_print_message(print_format, print_message): if len(depend_modules_dict): # Warning - blocked modules present modules_list = list() for module_name, dep_module in depend_modules_dict.items(): modules_list.append(" '%s' is required for '%s'" % (module_name, dep_module)) clprint.print_diag(print_format, {'status': 'WARN', 'message': print_message + ','.join(modules_list)}) def get_extensions(interpreter, version, fmt='text'): ext_list = ClExtSelect(interpreter).list_extensions(version) return parse_extensions(ext_list, version, fmt) def parse_extensions(ext_list, version, fmt): json_list = [] for ext in ext_list: action = '~' if ext[1] is True: action = '+' elif ext[1] is False: action = '-' if fmt == 'text': json_list.append((action, ext[0])) else: json_list.append({'name': ext[0], 'description': '', 'state': ext_letter_to_status(action)}) json_list = fill_descriptions(json_list) if fmt == 'json': result_dict = {'version': version, 'extensions': json_list} return result_dict return json_list def get_cagefs_users(): """ Return list of users that are in CageFS If CageFS is not installed or initialized throws exception and prints it :return: """ not_installed_msg = 'No such file or directory' not_initialized_msg = 'CageFS is not initialized' try: users = run_command([CAGEFSCTL_COMMAND, '--list-enabled']).strip() if users == '': return [] return users.split('\n')[1:] # First element shows number of users except ExternalProgramFailed as e: if not_installed_msg in str(e): print_error_and_exit('ERROR: CageFS not installed.') elif not_initialized_msg in str(e): print_error_and_exit('Error: CageFS is not initialized. ' 'Use "/usr/sbin/cagefsctl --init" to initialize CageFS') print_error_and_exit(e) def verify_options_in_cagefs(options, supported_options): """ Verifies if the provided options are supported in CageFS and appends the current user option if user-related options are absent """ input_options = {opt for opt, _ in options} if not input_options & set(supported_options): print_error_and_exit('Error: This command is not supported in CageFS') if '--user' not in input_options and '-u' not in input_options: current_user_name = pwd.getpwuid(os.geteuid()).pw_name options.append(('--user', current_user_name)) def main(): config = {} config['interpreter'] = 'php' config['version'] = None config['show-all'] = False config['print-summary'] = False config['show-native-version'] = False config['decoder'] = 'plain' config['quiet'] = False config['format'] = 'text' config['api-version'] = API_0 actions = {} exclude_pid_list = [] check_args_presence() try: opts, args = getopt.getopt( sys.argv[1:], 'hi:lSsCcB:Y:N:E:D:R:v:Gu:b:ge:d:r:atpVLT:k:m:x:QqPjwWz', ['help', 'setup-without-cagefs', 'revert-to-cagefs', 'interpreter=', 'list', 'summary', 'user-summary', 'current', 'user-current', 'set-current=', 'enable-alternative=', 'disable-alternative=', 'enable-extensions=', 'disable-extensions=', 'replace-extensions=', 'version=', 'list-extensions', 'user=', 'set-user-current=', 'list-user-extensions', 'enable-user-extensions=', 'disable-user-extensions=', 'replace-user-extensions=', 'all', 'reset-user-extensions', 'print-summary', 'show-native-version', 'list-users', 'change-to-version=', 'add-options=', 'replace-options=', 'delete-options=', 'base64', 'apply-symlinks-rules', 'quiet', 'print-options', 'print-options-safe', 'json', 'csv', 'perl', 'api-version=', 'reset-options', 'update-backup', 'apply-global-php-ini', 'exclude-pid-list=', 'for-all-users' ]) except getopt.GetoptError: usage() sys.exit(1) opts_supported_in_cagefs = [ '-l', '--list', '-s', '--user-summary', '-c', '--user-current', '-b', '--set-user-current', '-e', '--enable-user-extensions', '-d', '--disable-user-extensions', '-r', '--replace-user-extensions', '-t', '--reset-user-extensions', '-g', '--list-user-extensions', '-k', '--add-options', '-m', '--replace-options', '-x', '--delete-options', '-P', '--print-options', '--print-options-safe', '-z', '--reset-options', ] if in_cagefs(): verify_options_in_cagefs(opts, opts_supported_in_cagefs) for o, a in opts: if o in ['-h', '--help']: usage() sys.exit(0) elif o in ['--exclude-pid-list']: for pid in a.split(','): try: exclude_pid_list.append(int(pid)) except ValueError: continue elif o in ['--apply-symlinks-rules']: actions['apply-symlinks-rules'] = True elif o in ['--setup-without-cagefs']: setup_without_cagefs(args) sys.exit(0) elif o in ['--revert-to-cagefs']: revert_to_cagefs() sys.exit(0) elif o in ("--apply-global-php-ini",): apply_global_php_ini(args) sys.exit(0) elif o in ['-l', '--list']: actions['list-alternatives'] = True elif o in ['-S', '--summary']: actions['show-summary'] = True elif o in ['-s', '--user-summary']: actions['show-user-summary'] = True elif o in ['-C', '--current']: actions['show-current'] = True elif o in ['-c', '--user-current']: actions['show-user-current'] = True elif o in ['-a', '--all']: config['show-all'] = True elif o in ['-v', '--version']: config['version'] = a elif o in ['-u', '--user']: config['user'] = a clpwd = ClPwd() if ClSelect.work_without_cagefs(): uid = clpwd.get_uid(a) else: users = a.split(',') user_list = list() try: if len(users) == 1: uid = clpwd.get_uid(a) if os.geteuid() == 0: for user in clpwd.get_names(uid): ClUserSelect().cagefs_copy_etc(user) else: for user in users: if user not in user_list: user_list += clpwd.get_names(clpwd.get_uid(user)) config['user'] = ','.join(user_list) except ClPwd.NoSuchUserException as e: sys.stderr.write(str(e)+'\n') sys.exit(1) elif o in ['-B', '--set-current']: actions['set-current'] = a elif o in ['-b', '--set-user-current']: actions['set-user-current'] = a elif o in ['-Y', '--enable-alternative']: actions['enable-alternative'] = a elif o in ['-N', '--disable-alternative']: actions['disable-alternative'] = a elif o in ['-G', '--list-extensions']: actions['list-extensions'] = True elif o in ['-g', '--list-user-extensions']: actions['list-user-extensions'] = True elif o in ['-E', '--enable-extensions']: actions['enable-extensions'] = a elif o in ['-D', '--disable-extensions']: actions['disable-extensions'] = a elif o in ['-R', '--replace-extensions']: actions['replace-extensions'] = a elif o in ['-e', '--enable-user-extensions']: actions['enable-user-extensions'] = a elif o in ['-d', '--disable-user-extensions']: actions['disable-user-extensions'] = a elif o in ['-r', '--replace-user-extensions']: actions['replace-user-extensions'] = a elif o in ['-t', '--reset-user-extensions']: actions['reset-user-extensions'] = True elif o in ['-p', '--print-summary']: config['print-summary'] = True elif o in ['-V', '--show-native-version']: config['show-native-version'] = True elif o in ['-L', '--list-users']: actions['list-users'] = True elif o in ['-T', '--change-to-version']: actions['change-to-version'] = a elif o in ['-k', '--add-options']: actions['add-options'] = a elif o in ['-m', '--replace-options']: actions['replace-options'] = a elif o in ['-x', '--delete-options']: actions['delete-options'] = a elif o in ['-Q', '--base64']: config['decoder'] = 'base64' elif o in ['-q', '--quiet']: config['quiet'] = True elif o in ['-P', '--print-options']: actions['print-options'] = True elif o in ['--print-options-safe']: actions['print-options-safe'] = True elif o in ['-j', '--json']: config['format'] = 'json' elif o in ['-w', '--csv']: config['format'] = 'csv' elif o in ['--api-version']: config['api-version'] = int(a) elif o in ['-W', '--perl']: config['format'] = 'perl' elif o in ['-z', '--reset-options']: actions['reset-options'] = True elif o in ['--update-backup']: actions['update-backup'] = True elif o in ['--for-all-users']: if 'user' in config: print_error_and_exit("--for-all-users and --user options are mutually" " exclusive options and cannot be used simultaneously." "\nUse --for-all-user OR --user instead.") users = get_cagefs_users() if not users: print_error_and_exit("No changes were made: there are no users with cagefs enabled ") clpwd = ClPwd() user_list = list() for user in users: if user not in user_list: user_list += clpwd.get_names(clpwd.get_uid(user)) config['user'] = ','.join(user_list) if len(actions) != 1: if len(actions) == 0 and config['show-native-version']: try: print(ClSelect(config['interpreter']).get_native_version()[0]) except TypeError: pass else: print_error_and_exit("Wrong set of options", 'ERROR') try: # check if we are able to do anything before actually parsing options # this two exceptions check for native version inside # this is done because selectorctl is called in alt-php spec in cycle # and we should avoid printing lot of messages there if 'set-user-current' not in actions and \ 'show-user-current' not in actions: ClSelect().check_requirements() if 'list-alternatives' in actions: if config["format"] != "json": for alt in ClSelect(config['interpreter']).list_alternatives(): print("%s\t%s\t%s" % (alt)) else: alternatives_dict = {'status': 'ok', 'data': []} for alt in ClSelect(config['interpreter']).list_alternatives(): alternatives_dict['data'].append({'short': alt[0], 'full': alt[1], 'path': alt[2]}) print(json.dumps(alternatives_dict)) elif 'show-summary' in actions: data = ClSelect(config['interpreter']).get_summary( config['show-native-version']) print_summary(data, config['format'], config['api-version']) elif 'show-current' in actions: print("%s\t%s\t%s" % ClSelect(config['interpreter']).get_version( config['show-native-version'])) elif 'set-current' in actions: ClSelect(config['interpreter']).set_version(actions['set-current']) if config['format'] == 'json': print_json_status_ok() elif 'enable-alternative' in actions: ClSelect(config['interpreter']).enable_version(actions['enable-alternative']) if config['format'] == 'json': print_json_status_ok() elif 'disable-alternative' in actions: ClSelect(config['interpreter']).disable_version(actions['disable-alternative']) if config['format'] == 'json': print_json_status_ok() elif 'list-extensions' in actions: check_params(config, ('interpreter','version')) ext_list = get_extensions(config['interpreter'], config['version'], config['format']) if config['format'] == 'text': for item in ext_list: print("%s %s" % item) elif config['format'] == 'json': print(json.dumps(ext_list)) elif 'enable-extensions' in actions: check_params(config, ('interpreter', 'version')) ClExtSelect(config['interpreter']).enable_extensions( config['version'], list(map((lambda i: i.strip()), actions['enable-extensions'].split(',')))) elif 'disable-extensions' in actions: check_params(config, ('interpreter', 'version')) ClExtSelect(config['interpreter']).disable_extensions( config['version'], list(map((lambda i: i.strip()), actions['disable-extensions'].split(',')))) if len(depend_modules_dict): # Warning - blocked modules present _check_depencies_and_print_message(config['format'], 'Modules left by dependencies:') elif 'replace-extensions' in actions: check_params(config, ('interpreter', 'version')) ClExtSelect(config['interpreter']).replace_extensions( config['version'], list(map((lambda i: i.strip()), actions['replace-extensions'].split(',')))) if len(depend_modules_dict): # Warning - blocked modules present _check_depencies_and_print_message(config['format'], 'Modules left/added by dependencies:') elif config['format'] == 'json': print_json_status_ok() elif 'show-user-summary' in actions: check_params(config, ('interpreter', 'user')) data = ClUserSelect(config['interpreter'], exclude_pid_list).get_summary( config['user'], config['show-native-version']) print_summary(data) elif 'show-user-current' in actions: check_params(config, ('interpreter', 'user')) print("%s\t%s\t%s" % ClUserSelect( config['interpreter'], exclude_pid_list).get_version( config['user'], config['show-native-version'])) elif 'apply-symlinks-rules' in actions: check_params(config, ('interpreter',)) ClUserSelect(config['interpreter'], exclude_pid_list).apply_symlinks_rules() elif 'set-user-current' in actions: check_params(config, ('interpreter', 'user')) # hack for alt-php spec where we read and re-apply # php version for each user in system # in order not to bump deps, we just silently # ignore version set requests for 'native' # (which should be set for all users on server # because web ui does not work) try: ClSelect().check_requirements() except ClSelectExcept.NativeNotInstalled: if actions['set-user-current'] != 'native': raise exit(0) # we intentionally use first user cause set_version has workaround for multiple same uid users # LVEMAN-1670 user = clpwd.get_names(uid)[0] c = ClUserSelect(config['interpreter'], exclude_pid_list) data = c.set_version(user, actions['set-user-current'], config['print-summary'], config['show-native-version']) c.clean_crui_images(clpwd.get_names(uid)) if config['print-summary']: print_summary(data) elif 'list-user-extensions' in actions: check_params(config, ('interpreter', 'user')) if config['show-all']: for ext in ClUserExtSelect(config['interpreter'], exclude_pid_list).list_all_extensions( config['user'], config['version']): action = '-' if ext[1] is None: action = '~' elif ext[1] is True: action = '+' print("%s %s" % (action, ext[0])) else: for ext in ClUserExtSelect(config['interpreter'], exclude_pid_list).list_enabled_extensions( config['user'], config['version']): print(ext[0]) elif 'enable-user-extensions' in actions: check_params(config, ('interpreter', 'version', 'user')) ClSelect.check_multiphp_system_default_version() users = config['user'].split(',') for user in users: c = ClUserExtSelect(config['interpreter'], exclude_pid_list) c.bulk_enable_extensions( user=user, version=config['version'], ext_list=list(map((lambda i: i.strip()), actions['enable-user-extensions'].split(','))), check_ext=True) c.clean_crui_images(users) elif 'disable-user-extensions' in actions: check_params(config, ('interpreter', 'version', 'user')) ClSelect.check_multiphp_system_default_version() users = config['user'].split(',') for user in users: c = ClUserExtSelect(config['interpreter'], exclude_pid_list) c.bulk_disable_extensions( user, config['version'], list(map((lambda i: i.strip()), actions['disable-user-extensions'].split(',')))) c.clean_crui_images(users) elif 'replace-user-extensions' in actions: check_params(config, ('interpreter', 'version', 'user')) ClSelect.check_multiphp_system_default_version() user = clpwd.get_names(uid)[0] c = ClUserExtSelect(config['interpreter'], exclude_pid_list) c.replace_extensions( user, config['version'], list(map((lambda i: i.strip()), actions['replace-user-extensions'].split(',')))) c.clean_crui_images(clpwd.get_names(uid)) elif 'reset-user-extensions' in actions: check_params(config, ('interpreter', 'version', 'user')) ClSelect.check_multiphp_system_default_version() user = clpwd.get_names(uid)[0] c = ClUserExtSelect(config['interpreter'], exclude_pid_list) extensions = c.reset_extensions(user, config['version']) c.clean_crui_images(clpwd.get_names(uid)) print(','.join(extensions)) elif 'list-users' in actions: check_params(config, ('interpreter', 'version')) users = ClUserSelect(config['interpreter'], exclude_pid_list).list_users( config['version']) print(','.join(users)) elif 'change-to-version' in actions: check_params(config, ('interpreter', 'version')) ClUserSelect(config['interpreter'], exclude_pid_list).change_to_version( actions['change-to-version'], config['version']) elif 'add-options' in actions: check_params(config, ('interpreter', 'version', 'user')) ClSelect.check_multiphp_system_default_version() user = clpwd.get_names(uid)[0] c = ClUserOptSelect(config['interpreter'], exclude_pid_list) c.insert_options( user, config['version'], actions['add-options'], config['decoder'], True, config['quiet']) c.clean_crui_images(clpwd.get_names(uid)) if config['format'] == 'json': clprint.print_data(config['format'], {}) elif 'replace-options' in actions: check_params(config, ('interpreter', 'version', 'user')) ClSelect.check_multiphp_system_default_version() user = clpwd.get_names(uid)[0] c = ClUserOptSelect(config['interpreter'], exclude_pid_list) c.insert_options( user, config['version'], actions['replace-options'], config['decoder'], False, config['quiet']) c.clean_crui_images(clpwd.get_names(uid)) if config['format'] == 'json': clprint.print_data(config['format'], {}) elif 'delete-options' in actions: check_params(config, ('interpreter', 'version', 'user')) ClSelect.check_multiphp_system_default_version() user = clpwd.get_names(uid)[0] c = ClUserOptSelect(config['interpreter'], exclude_pid_list) c.delete_options( user, config['version'], actions['delete-options'], config['decoder'], config['quiet']) c.clean_crui_images(clpwd.get_names(uid)) if config['format'] == 'json': clprint.print_data(config['format'], {}) elif 'reset-options' in actions: user = None version = None if 'user' in config: user = config['user'].split(',') if config['version']: version = config['version'].split(',') c = ClUserOptSelect(config['interpreter'], exclude_pid_list) c.reset_options(user, version) c.clean_crui_images(user) if config['format'] == 'json': clprint.print_data(config['format'], {}) elif 'print-options' in actions: check_params(config, ('interpreter', 'user')) clprint.print_data(config['format'], ClUserOptSelect(config['interpreter'], exclude_pid_list).get_options( config['user'], config['version'])) elif 'print-options-safe' in actions: check_params(config, ('interpreter', 'user')) clprint.print_data( config['format'], ClUserOptSelect(config['interpreter'], exclude_pid_list).get_options( config['user'], config['version'] ), escape=True ) elif 'update-backup' in actions: clpwd = ClPwd() for user in clpwd.get_user_dict().keys(): try: ClUserSelect()._check_user_in_cagefs(user) ClUserSelect()._backup_settings(user) ClUserOptSelect().backup_php_options(user) except ClSelectExcept.NotCageFSUser: pass #SKIP user with disabled cagefs except ClSelectExcept.UnableToSaveData as e: if not config['quiet']: clprint.print_diag( config['format'], {'status': 'ERROR', 'message': str(e)}) pass #SKIP user with errors except ClSelectExcept.NativeNotInstalled as e: clprint.print_diag(config['format'], { 'status': 'WARNING', 'message': str(e), 'details': e.details, 'context': e.context }) sys.exit(1) except BaseClSelectException as e: clprint.print_diag(config['format'], { 'status': 'ERROR', 'message': str(e), 'details': e.details, 'context': e.context }) sys.exit(1) except (KeyError, UnboundLocalError): print_error_and_exit("Incomplete or incorrect set of arguments") except Exception as e: msg = traceback.format_exc() clprint.print_diag( config['format'], {'status': 'ERROR', 'message': msg}) if __name__ == '__main__': main()