Current File : //bin/alt-php-mysql-reconfigure
#!/opt/alt/python27/bin/python
# -*- mode:python; coding:utf-8; -*-
# author: Eugene Zamriy <ezamriy@cloudlinux.com>
# created: 29.07.2015 15:46
# description: Selects correct alt-php MySQL binding according to the system
#              configuration.


import getopt
import glob
import logging
import os
import platform
import re
import subprocess
import sys
import traceback
from decimal import Decimal

try:
    import rpm
except:
    class rpm:
        RPMMIRE_REGEX = None
        class pattern:
            def __init__(self, packages):
                self.packages = packages
            def pattern(self, field, flag, pattern):
                regexp = re.compile(pattern)
                self.packages = filter(regexp.match, self.packages)
            def __getitem__(self, item):
                return self.packages[item]
        class TransactionSet:
            @staticmethod
            def dbMatch():
                return rpm.pattern(os.popen('rpm -qa').readlines())


VER_PATTERNS = {"18.1": "5.6", "18.1.0": "5.6", "18.0": "5.5", "18.0.0": "5.5",
                "18": "5.5", "16": "5.1", "15": "5.0", "20.1": "5.7",
                "20.2": "5.7", "20.3": "5.7", "21.0": "8.0"}


def configure_logging(verbose):
    """
    Logging configuration function.

    @type verbose:  bool
    @param verbose: Enable additional debug output if True, display only errors
        otherwise.
    """
    if verbose:
        level = logging.DEBUG
    else:
        level = logging.ERROR
    handler = logging.StreamHandler()
    handler.setLevel(level)
    log_format = "%(levelname)-8s: %(message)s"
    formatter = logging.Formatter(log_format, "%H:%M:%S %d.%m.%y")
    handler.setFormatter(formatter)
    logger = logging.getLogger()
    logger.addHandler(handler)
    logger.setLevel(level)
    return logger


def symlink_abs_path(path):
    """
    Recursively resolves symlink.

    @type path:  str
    @param path: Symlink path.

    @rtype:      str
    @return:     Resolved symlink absolute path.
    """
    processed_symlinks = set()
    while os.path.islink(path):
        if path in processed_symlinks:
            return None
        path = os.path.join(os.path.dirname(path), os.readlink(path))
        processed_symlinks.add(path)
    return os.path.abspath(path)


def find_interpreter_versions(interpreter="php"):
    """
    Returns list of installed alt-php versions and their base directories.

    @rtype:  list
    @return: List of version (e.g. 44, 55) and base directory tuples.
    """
    int_versions = []
    if interpreter == "ea-php":
        base_path_regex = "/opt/cpanel/ea-php[0-9][0-9]/root/"
    else:
        base_path_regex = "/opt/alt/%s[0-9][0-9]" % interpreter
    for int_dir in glob.glob(base_path_regex):
        int_versions.append((int_dir[-2:], int_dir))
    int_versions.sort()
    return int_versions


def find_mysql_executable(mysql="mysql"):
    """
    Detects MySQL binary full path.

    @type mysql:  str
    @param mysql: MySQL binary name (default is "mysql").

    @rtype:       str or None
    @return:      MySQL binary full path or None if nothing is found.
    """
    for path in os.environ["PATH"].split(os.pathsep):
        mysql_path = os.path.join(path, mysql)
        if os.path.exists(mysql_path) and os.access(mysql_path, os.X_OK):
            return mysql_path


def parse_mysql_version(version):
    """
    Extracts MySQL engine type and version from the version string
    (mysql -V output).

    @type version:  str
    @param version: MySQL version string (mysql -V output).

    @rtype:         tuple
    @return:        MySQL engine type (e.g. mariadb, mysql) and version (e.g.
        5.6, 10.0) tuple.
    """
    ver_rslt = re.search("mysql\s+Ver\s+(.*?Distrib\s+)?((\d+)\.(\d+)\S*?)(,)?"
                         "\s+for", version)
    if not ver_rslt:
        return None, None
    _, full_ver, major, minor, _ = ver_rslt.groups()
    mysql_type = "mysql"
    mysql_ver = "%s.%s" % (major, minor)
    if re.search("mariadb", full_ver, re.IGNORECASE):
        mysql_type = "mariadb"
    # NOTE: there are no way to detect Percona by "mysql -V" output, so we
    #       are looking for Percona-Server-shared* or cl-Percona*-shared package installed
    ts = rpm.TransactionSet()
    mi = ts.dbMatch()
    pattern = "Percona-Server-shared-{0}{1}|cl-Percona{0}{1}-shared".format(major, minor)
    mi.pattern('name', rpm.RPMMIRE_REGEX, pattern)
    for _ in mi:
        mysql_type = "percona"
        break
    return mysql_type, mysql_ver


def get_mysql_version(mysql_path):
    """
    Returns MySQL engine type and version of specified MySQL executable.

    @type mysql_path:  str
    @param mysql_path: MySQL executable path.

    @rtype:            tuple
    @return:           MySQL engine type (mariadb or mysql) and version (e.g.
        5.6, 10.0) tuple.
    """
    proc = subprocess.Popen([mysql_path, "-V"], stdout=subprocess.PIPE,
                            stderr=subprocess.STDOUT)
    out, _ = proc.communicate()
    if proc.returncode != 0:
        raise Exception(u"cannot execute \"%s -V\": %s" % (mysql_path, out))
    ver_string = out.strip()
    logging.debug(u"SQL version string is '%s'" % ver_string)
    return parse_mysql_version(ver_string)


def detect_so_version(so_path):
    """
    Parameters
    ----------
    so_path         : str or unicode
        Absolute path to .so library

    Returns
    -------
    tuple
        Tuple of MySQL type name and MySQL version

    """
    mysql_ver = None
    for ver_pattern in VER_PATTERNS:
        if re.search(re.escape(".so.%s" % ver_pattern), so_path):
            mysql_ver = VER_PATTERNS[ver_pattern]
    # in some Percona builds .so was renamed to libperconaserverclient.so
    if "libperconaserverclient.so" in so_path:
        return "percona", mysql_ver
    # search for markers (mariadb/percona) in .so strings
    proc = subprocess.Popen(["strings", so_path], stdout=subprocess.PIPE,
                            stderr=subprocess.STDOUT)
    out, _ = proc.communicate()
    if proc.returncode != 0:
        raise Exception(u"cannot execute \"strings %s\": %s" % (so_path, out))
    mysql_type = "mysql"
    for line in out.split("\n"):
        if re.search("percona", line, re.IGNORECASE):
            return "percona", mysql_ver
        maria_version = re.search("^(10\.[0-9])\.[0-9]*(-MariaDB)?$", line,
                                  re.IGNORECASE)
        if maria_version is not None and len(maria_version.groups()) != 0:
            return "mariadb", maria_version.group(1)
        if re.search("5\.5.*?-MariaDB", line, re.IGNORECASE):
            return "mariadb", "5.5"
        if re.search("mariadb", line, re.IGNORECASE):
            mysql_type = "mariadb"
    return mysql_type, mysql_ver


def detect_lib_dir():
    """
    Returns
    -------
    str
        lib if running on 32-bit system, lib64 otherwise

    """
    if platform.architecture()[0] == "64bit":
        return "lib64"
    else:
        return "lib"


def get_int_files_root_path(int_name, int_ver):
    """
    Parameters
    ----------
    int_name        : str or unicode
        Interpreter name (php, python)
    int_ver         : str or unicode
        Interpreter version (44, 70, 27, etc.)

    Returns
    -------
    str
        Absolute path to interpreter root

    """
    if int_name == "php":
        return "/opt/alt/php%s" % int_ver
    elif int_name == "ea-php":
        return "/opt/cpanel/ea-php%s/root/" % int_ver
    elif int_name == "python":
        return "/opt/alt/python%s" % int_ver
    else:
        raise NotImplementedError("Unknown interpreter")


def get_dst_so_path(int_name, int_ver, so_name):
    """
    Parameters
    ----------
    int_name        : str or unicode
        Interpreter name (php, python)
    int_ver         : str or unicode
        Interpreter version (44, 70, 27, etc.)
    so_name         : str or unicode
        MySQL shared library name

    Returns
    -------
    str
        Absolute path to MySQL binding destination point

    """
    lib_dir = detect_lib_dir()
    int_path = get_int_files_root_path(int_name, int_ver)
    int_dot_ver = "%s.%s" % (int_ver[0], int_ver[-1])
    if int_name in ["php", "ea-php"]:
        if re.match(r".*_ts.so", so_name):
            return os.path.join(int_path, "usr", lib_dir, "php-zts/modules", re.sub('_ts\.so', '.so', so_name))
        else:
            return os.path.join(int_path, "usr", lib_dir, "php/modules", so_name)
    elif int_name == "python":
        return os.path.join(int_path, lib_dir, "python%s" % int_dot_ver,
                            "site-packages", so_name)
    else:
        raise NotImplementedError("Unknown interpreter")


def get_mysql_pkg_name(int_name, int_ver, mysql_type, mysql_ver, zts=False):
    """
    Parameters
    ----------
    int_name        : str or unicode
        Interpreter name (php, python)
    int_ver         : str or unicode
        Interpreter version (44, 27, 71, etc.)
    mysql_type      : str or unicode
        Mysql base type (mysql, mariadb, percona)
    mysql_ver       : str or unicode
        Mysql version (5.5, 10, 10.1)

    Returns
    -------

    """
    if int_name == "php":
        if not zts:
            return "alt-php%s-%s%s" % (int_ver, mysql_type, mysql_ver)
        else:
            return "alt-php%s-%s%s-zts" % (int_ver, mysql_type, mysql_ver)
    elif int_name == "ea-php":
        return "%s%s-php-%s%s" % (int_name, int_ver, mysql_type, mysql_ver)
    elif int_name == "python":
        return "alt-python%s-MySQL-%s%s" % (int_ver, mysql_type, mysql_ver)
    else:
        raise NotImplementedError("Unknown interpreter")


def get_so_list(int_name):
    """
    Parameters
    ----------
    int_name        : str
        Interpreter name (e.g. php, python, etc.)

    Returns
    -------

    """
    if int_name == "ea-php":
        return ["mysql.so", "mysqli.so", "pdo_mysql.so"]
    elif int_name == "php":
        return ["mysql.so", "mysqli.so", "pdo_mysql.so", "mysql_ts.so",
                "mysqli_ts.so", "pdo_mysql_ts.so"]
    elif int_name == "python":
        return ["_mysql.so"]
    else:
        raise NotImplementedError("Unknown interpreter")


def match_so_to_mysql():
    mysql = find_mysql_executable()
    # If we have no MySQL, then nothing should be done
    if not mysql:
        return
    possible_versions = VER_PATTERNS.values()
    possible_versions.extend(["10", "10.0", "10.1", "10.2", "10.3", "10.4"])
    mysql_type, mysql_ver = get_mysql_version(mysql)
    if mysql_type not in ["mysql", "mariadb", "percona"] or \
            mysql_ver not in possible_versions:
        return
    if mysql_ver == "5.0":
        search_pattern = re.compile(ur"(\S*libmysqlclient\.so\.15\.\S*)")
    elif mysql_ver == "5.1":
        search_pattern = re.compile(ur"(\S*libmysqlclient\.so\.16\.\S*)")
    elif mysql_ver in ("5.5", "10", "10.0", "10.1"):
        search_pattern = re.compile(ur"(\S*libmysqlclient\.so\.18\.0\.\S*)")
    elif mysql_ver == "5.6":
        search_pattern = re.compile(ur"(\S*libmysqlclient\.so\.18\.1\.\S*)")
    elif mysql_ver == "5.7":
        search_pattern = re.compile(ur"(\S*libmysqlclient\.so\.20\.\S*)")
    elif mysql_ver == "8.0":
        search_pattern = re.compile(ur"(\S*libmysqlclient\.so\.21\.\S*)")
    elif mysql_ver in ("10.2", "10.3", "10.4"):
        search_pattern = re.compile(ur"(\S*libmariadb\.so\.3\.\S*)")
    else:
        raise Exception(u"Cannot match MySQL library to any version")
    if mysql_type == "percona":
        search_path = "/%s" % detect_lib_dir()
    else:
        search_path = "/usr/%s/" % detect_lib_dir()
        if mysql_type == "mariadb" or (mysql_type == "mysql" and Decimal(mysql_ver) >= 8.0):
            if os.path.exists(search_path + "mysql"):
                search_path += "mysql"
            elif os.path.exists(search_path + "mariadb"):
                search_path += "mariadb"
            else:
                raise Exception("Detected %s but path for libraries is not found" % mysql_type)
    files = os.listdir(search_path)
    for one_file in files:
        if search_pattern.match(one_file):
            return (os.path.join(search_path,
                                 search_pattern.match(one_file).string),
                    mysql_type, mysql_ver)


def get_mysql_so_files():
    proc = subprocess.Popen(["/sbin/ldconfig", "-p"], stdout=subprocess.PIPE)
    out, _ = proc.communicate()
    if proc.returncode != 0:
        raise Exception(u"cannot execute \"ldconfig -p\": %s" % out)
    so_re = re.compile("^.*?=>\s*(\S*?(libmysqlclient|"
                       "libmariadb|"
                       "libperconaserverclient)\.so\S*)")
    forced_so_file = match_so_to_mysql()
    if forced_so_file:
        so_files = [forced_so_file]
    else:
        so_files = []
    for line in out.split("\n"):
        re_rslt = so_re.search(line)
        if not re_rslt:
            continue
        so_path = symlink_abs_path(re_rslt.group(1))
        if not so_path or not os.path.exists(so_path):
            continue
        mysql_type, mysql_ver = detect_so_version(so_path)
        so_rec = (so_path, mysql_type, mysql_ver)
        if so_rec not in so_files:
            so_files.append(so_rec)
    return so_files


def reconfigure_mysql(int_ver, mysql_type, mysql_ver, force=False,
                      int_name="php"):
    """
    Parameters
    ----------
    int_ver         : str or unicode
        Interpreter version (44, 70, 27, etc.)
    mysql_type      : str or unicode
        MySQL type (mysql, mariadb, percona)
    mysql_ver       : str or unicode
        MySQL version (5.5, 10.1, etc.)
    force           : bool
        Force symlink reconfiguration if True, do nothing otherwise
    int_name        : str or unicode
        Optional, defines interpreter name (php, python). Default is php

    Returns
    -------
    bool
        True if reconfiguration was successful, False otherwise

    """
    int_dir = get_int_files_root_path(int_name, int_ver)
    if mysql_type == "mariadb":
        if mysql_ver in ("10", "10.0"):
            mysql_ver = "10"
        elif mysql_ver.startswith("10."):
            mysql_ver = mysql_ver.replace(".", "")
        elif mysql_ver == "5.5":
            # NOTE: there are no special bindings for MariaDB 5.5 in Cloud Linux
            #       so we are using the MySQL one
            mysql_type = "mysql"
    so_list = get_so_list(int_name)
    for so_name in so_list:
        src_so = os.path.join(int_dir, "etc",
                              "%s%s" % (mysql_type, mysql_ver), so_name)
        if not os.path.exists(src_so):
            if (so_name in ("mysqli.so", "pdo_mysql.so") and int_ver == "44") \
                    or (so_name == "mysql.so" and int_ver.startswith("7")) \
                    or (re.match(r".*_ts.so", so_name) and int_ver != 72):
                # NOTE: there are no mysql.so for alt-php7X and mysqli.so /
                #       pdo_mysql.so for alt-php44
                continue
            # TODO: maybe find an appropriate replacement for missing
            #       .so in other alt-php-(mysql|mariadb|percona) packages?
            mysql_pkg_name = get_mysql_pkg_name(int_name, int_ver, mysql_type,
                                                mysql_ver,
                                                bool(re.match(r".*_ts.so",
                                                     so_name)))
            logging.error(u"%s is not found. Please install "
                          u"%s package" % (so_name, mysql_pkg_name))
            return False
        dst_so = get_dst_so_path(int_name, int_ver, so_name)
        dst_so_real = symlink_abs_path(dst_so)
        if src_so == dst_so_real:
            logging.debug(u"%s is already updated" % dst_so)
            continue
        if os.access(dst_so, os.R_OK):
            # seems alt-php is already configured - don't touch without force
            # argument
            if not force:
                logging.debug(u"current %s configuration is ok (%s)" %
                              (dst_so, dst_so_real))
                continue
            os.remove(dst_so)
            os.symlink(src_so, dst_so)
            logging.info(u"%s was reconfigured to %s" % (dst_so, src_so))
        else:
            # seems current alt-php configuration is broken, reconfigure it
            try:
                os.remove(dst_so)
            except:
                pass
            os.symlink(src_so, dst_so)
            logging.info(u"%s was configured to %s" % (dst_so, src_so))
        continue
    return True


def check_alt_path_exists(int_path, int_name, int_ver):
    """
    Parameters
    ----------
    int_path        : str or unicode
        Interpreter directory on the disk (/opt/alt/php51, etc.)
    int_name        : str or unicode
        Interpreter name (php, python)
    int_ver         : str or unicode
        Interpreter version (44, 70, 27, etc.)

    Returns
    -------
    bool
        True if interpreter path exists, False otherwise

    """
    if not os.path.isdir(int_path):
        print >> sys.stderr, u"unknown %s version %s" % (int_name, int_ver)
        return False
    return True


def main(sys_args):
    try:
        opts, args = getopt.getopt(sys_args, "p:P:e:v",
                                   ["php=", "python=", "ea-php=", "verbose"])
    except getopt.GetoptError, e:
        print >> sys.stderr, \
            u"cannot parse command line arguments: %s" % unicode(e)
        return 1
    verbose = False
    int_versions = []
    int_name = "php"
    for opt, arg in opts:
        if opt in ("-p", "--php"):
            int_name = "php"
            int_path = "/opt/alt/php%s" % arg
            if check_alt_path_exists(int_path, int_name, arg):
                int_versions.append((arg, int_path))
            else:
                return 1
        elif opt in ("-e", "--ea-php"):
            int_name = "ea-php"
            int_path = "/opt/cpanel/ea-php%s/root/" % arg
            if check_alt_path_exists(int_path, int_name, arg):
                int_versions.append((arg, int_path))
            else:
                return 1
        elif opt == "--python":
            int_name = "python"
            int_path = "/opt/alt/python%s" % arg
            if check_alt_path_exists(int_path, int_name, arg):
                int_versions.append((arg, int_path))
            else:
                return 1
        if opt in ("-v", "--verbose"):
            verbose = True
    log = configure_logging(verbose)
    if int_name == "ea-php":
        int_group = int_name
    else:
        int_group = "alt-%s" % int_name
    if not int_versions:
        int_versions = find_interpreter_versions()
    log.info(u"installed %s versions are\n%s" % (int_group,
             "\n".join(["\t %s: %s" % (int_group, i) for i in int_versions])))
    mysql_so_files = get_mysql_so_files()
    log.info(u"available SQL so files are\n%s" %
             "\n".join(["\t%s (%s-%s)" % i for i in mysql_so_files]))
    try:
        mysql_path = find_mysql_executable()
        if not mysql_path:
            log.info(u"cannot find system SQL binary")
            for int_ver, int_dir in int_versions:
                status = False
                for so_name, so_type, so_ver in mysql_so_files:
                    if reconfigure_mysql(int_ver, so_type, so_ver,
                                         force=False, int_name=int_name):
                        status = True
                        break
                if not status:
                    log.error(u"alt-%s%s reconfiguration is failed" %
                              (int_name, int_ver))
        else:
            log.debug(u"system SQL binary path is %s" % mysql_path)
            mysql_type, mysql_ver = get_mysql_version(mysql_path)
            log.debug(u"system SQL is %s-%s" % (mysql_type, mysql_ver))
            # check if we have .so for the system SQL version
            mysql_so_exists = False
            for so_name, so_type, so_ver in mysql_so_files:
                if so_type == mysql_type and so_ver == mysql_ver:
                    mysql_so_exists = True
                    break
            # reconfigure alt-php symlinks
            for int_ver, int_dir in int_versions:
                # system SQL was correctly detected and we found .so for it -
                # reconfigure alt-php to use it instead of previous
                # configuration
                if mysql_so_exists and \
                        reconfigure_mysql(int_ver, mysql_type, mysql_ver,
                                          force=True, int_name=int_name):
                    continue
                # we are unable to detect system SQL or it's .so is missing -
                # reconfigure alt-php to use .so that we have available, but
                # only if current configuration is broken
                status = False
                for so_name, so_type, so_ver in mysql_so_files:
                    if reconfigure_mysql(int_ver, so_type, so_ver, force=False,
                                         int_name=int_name):
                        status = True
                        break
                if not status:
                    log.error(u"alt-%s%s reconfiguration is failed" %
                              (int_name, int_ver))
    except Exception, e:
        log.error(u"cannot reconfigure alt-%s SQL bindings: %s. "
                  u"Traceback:\n%s" % (int_name, unicode(e),
                                       traceback.format_exc()))
        return 1


if __name__ == "__main__":
    sys.exit(main(sys.argv[1:]))

En construcción …

  • Ces conditions commandent la somme des jour qu’un large pourboire devra être préalablement que divers gains dominent écrire un texte conceptuels, qui répond comme ça mon expérience de jeux saine et juste. Concrètement, les 10 versions en hasard ont longtemps. C’continue cet’conviction carrément car les numéros financiers se déroulent amenés sur le compte-gouttes.

  • Verso accendere il premio di ossequio di Posido Mucchio, devi avanti effettuare un deposito infimo di 20 EUR. Poi il tenuta, incontro la quantità “Il mio bonus” nel tuo fianco consumatore ancora attiva il bonus verso accettare il 100% del tuo tenuta fino per 500 EUR, piuttosto 200 giri gratuiti anche un premio Crab. Ricorda…

  • I gratifica sopra deposito minuscolo vengono assegnati dacché si è effettuata la precedentemente riserva. Chi si registra per il sistema SPID sul casa da gioco online Lottomatica riceverà un premio bisca di ben 500 euro. Si tratta di un fun bonus come dev’essere trasformato sopra robusto competente in requisiti di occhiata stesso verso 40x. I…

  • Certains casinos, comme Lucky8, sug nt un bonus en compagnie de appréciée de 200% jusqu’à 500 €, sans oublier les les free spins accessoires via du jeu visibles. Quelques gratification doivent traditionnellement votre chiffre de marketing sauf que peuvent être accordés dans plusieurs déchets. Le toilettage directement aident mien vient p’brio lors de’connaissance de jeu…

  • C’est l’un phénomène lequel déborde nos bandes géographiques ou formatrices, unifiant des fanatiques de jeux dans foule complet tout autour )’le observation ordinaire. Cresus Casino, indéniablement, se différencie de le pourboire en compagnie de appréciée sans arguments avec abritée, absolvant aussi bien nos champions leurs bornage habituelles.

  • Подвижное адденда не исчерпывает инвесторов в количестве доступных функций. Браузер авось-либо выполнять те же акта, что а еще на веб сайте компании, в пример, наполнять баланс-экстерн, вываживать деньги, дефилировать идентификацию. Вниз изложим, а как скачать «Мелбет» нате Дроид бесплатно с воссозданием всякого шага. Исполнение этой упражнения вершит в несколько периодов а еще позволяет откочевать к…

  • Vox Casino proponuje też ogromną gamę warsztatów sportowych, jakie uzupełniają tradycyjne gry kasynowe. Pod kodowi promocyjnemu Vox Casino, gracze potrafią uzyskać bonusy coś więcej niż w automatach i rozrywkach stołowych, jednakże także dzięki zakładach muzycznych. Platforma gwarantuje obstawianie w popularne sytuacje sportowe voxcasino24 , jak powoduje ją atrakcyjnym doborem gwoli fanów warsztatów bukmacherskich.

  • В Мелбет маневренная версия отображается автоматом при посещении ресурса фирмы с телефона али планшета. Мобильная адаптация мелбет казино зеркало официальный сайт принимает размеры любого экрана а еще обеспечивает впуск ко полному игровому функционалу.

  • Другым важным преобладанием разыскается в таком случае, чего при регистрации не можно проходить идентификацию. Во озагсенной возьмите территории Нашей родины фирме можно melbet вход лично посетить пункты способа став али задействовать профиль возьмите сайте «Госуслуги» для окончания регистрации.

  • Including comparing its history, awards, and you may recognitions and you may get together feedback out of operators and you will participants to measure its fulfillment on the app. Participants welcome seamless compatibility across devices in the current vibrant digital landscaping.