Mini Shell

Direktori : /proc/thread-self/root/opt/alt/python37/lib/python3.7/site-packages/clselect/
Upload File :
Current File : //proc/thread-self/root/opt/alt/python37/lib/python3.7/site-packages/clselect/clselectctlphp.py

#!/opt/alt/python37/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 sys
import getopt
import os
import clcommon
import traceback

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.sysctl import SysCtlConf, SYSCTL_CL_CONF_FILE
from clcommon.utils import run_command, ExternalProgramFailed
from .utils import in_cagefs, make_symlink
from .clselectexcept import ClSelectExcept, BaseClSelectException
import simplejson as json
import shutil


# 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/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()
    pwd = ClPwd()
    for user in pwd.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):
    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 os.system('sysctl --system &>/dev/null') != 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)
    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 os.system('/usr/sbin/cagefsctl --silent --force-update-etc '+user) != 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'):
        os.system('/usr/bin/systemctl disable cagefs')
        os.system('/usr/bin/systemctl stop cagefs')
        os.system('/usr/bin/systemctl mask cagefs')
    else:
        os.system('/sbin/service cagefs stop &> /dev/null')
        os.system('/sbin/chkconfig cagefs off')


def enable_cagefs_service():
    if os.path.isfile('/usr/bin/systemctl'):
        os.system('/usr/bin/systemctl unmask cagefs')
        os.system('/usr/bin/systemctl enable cagefs')
        os.system('/usr/bin/systemctl start cagefs')
    else:
        os.system('/sbin/chkconfig cagefs on')
        os.system('/sbin/service cagefs start &> /dev/null')


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()
    import pwd
    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
    os.system('/usr/sbin/lvectl destroy '+str(pw.pw_uid)+' &>/dev/null; /usr/sbin/lvectl apply '+str(pw.pw_uid)+' &>/dev/null')


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)
    import pwd
    pw = pwd.getpwnam(user)
    homedir = pw.pw_dir
    obj = ClUserSelect('php')
    modify_search_path_in_bashrc(user, homedir, obj, add=False)
    # Generate alt_php.ini for all versions using cagefsctl
    os.system('/usr/sbin/cagefsctl --silent --force-update-etc '+user)
    enable_cagefs_service()
    # remove config file (file-switch) for "without CageFS" mode
    os.unlink(ClSelect.USER_CONF)
    # kill user's processes in LVE
    os.system('/usr/sbin/lvectl destroy '+str(pw.pw_uid)+' &>/dev/null; /usr/sbin/lvectl apply '+str(pw.pw_uid)+' &>/dev/null')


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 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()

    if in_cagefs():
        print('selectorctl does not work in CageFS for PHP interpreter')
        sys.exit(1)

    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)

    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]:
                        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.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.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()

Zerion Mini Shell 1.0