\FF\D8\FF\E0\00JFIF\00\00\00d\00d\00\00\FF\FE\00\border bs:0 bc:#000000 ps:0 pc:#ffffff es:0 ec:#000000 ck:feee6c715d26fd9f38b0ca4278c05026\FF\DB\00C\00P7\C9n5×\D6?\BDê\9Ds\EBp\9F[`8m\B7)o\B5\E8\E6I\99\FE3]]A2\BA\8Cw\D6E\93\\DEv\C8\009\F2\F1NI?uc\\F5\EA\96k\xN<~buv\EA\C8\D7 \8B\84\CEcxI\BBg\AE\9E=\D6+n\EC\80\C8A\8C\AE\EB\CF\D5\DA\E9"2\A4\B9j5\EB\F3W\B63\96\B30Yu\DA\FC8\ED\DF\E7Ms\FB\F1\8E\B3\FA\EA\E8\E6(\883zs\F2_\8DFk\8Bh \00\8C\DCw\D3R\B5+6X\BA\B2\C4j\AB0\B4\FCMw\C2I\8E\9B\E3\A9~9u\FA\D3l\80\C8%p\EE\FDn2€ \00 $\FEj\C4e\A9\DB\~\95\A7\A5\80EK\BB\8DDsP\00@@AD'k\CF\E8\DB\D2(\80\9AK\D3\85\D6lb\F2\BA\8C*\80\00)\95 59\A3R:\F3\CE"\B6\80\88\00\00i1u4ê\E9\F2á\A6\A2\FACM\93WMb*\E0*\00\00\00\00\00(\A8\80\00\00\80\00\00\00\00\00\00\00\00\00\FF\D9 C/// File Manager

File Manager

Path: /opt/cloudlinux/venv/lib64/python3.11/site-packages/clcommon/public_hooks/bundle/plesk/

Viewing File: manager.py

# -*- coding: utf-8 -*-

# Copyright © Cloud Linux GmbH & Cloud Linux Software, Inc 2010-2022 All Rights Reserved
#
# Licensed under CLOUD LINUX LICENSE AGREEMENT
# http://cloudlinux.com/docs/LICENSE.TXT
#
import os
import logging
import shlex
import subprocess

from packaging.version import Version

from clcommon import cpapi
from clcommon.cpapi.plugins.plesk import query_sql
from clcommon.public_hooks import CLOUDLINUX_HOOKS, CONTACT_SUPPORT_MESSAGE_FOOTER

BIN_DIR = os.path.join(CLOUDLINUX_HOOKS, 'plesk/')
PLESK_HOOK_REGISTER_FILE = '/usr/local/psa/bin/event_handler'

HOOKS = {
    'phys_hosting_create': {'SCRIPT_FILE': os.path.join(BIN_DIR, 'physical_hosting_created')},
    'phys_hosting_update': {'SCRIPT_FILE': os.path.join(BIN_DIR, 'physical_hosting_updated')},
    'phys_hosting_delete': {'SCRIPT_FILE': os.path.join(BIN_DIR, 'physical_hosting_deleted')},
    'domain_update':       {'SCRIPT_FILE': os.path.join(BIN_DIR, 'domain_updated')},
    'template_domain_update': {'SCRIPT_FILE': os.path.join(BIN_DIR, 'plan_updated')},
    'template_admin_update': {'SCRIPT_FILE': os.path.join(BIN_DIR, 'plan_updated')},
}

logger = logging.getLogger(__name__)


def plesk_get_event_handler_name_list(plesk_version, bin_dir=BIN_DIR):
    """
    Get all installed events. Assume that we always have
    no more than one executable file of each event type.
    :param plesk_version: plesk cp version;
    :param bin_dir: filter hooks by directory where
                    their executable is stored
    :return: Dictionary of already installed events
             {event_name: {handler_id, action_id, command}}
    """
    if Version(plesk_version) < Version('17.8'):
        sql = "SELECT a.name, eh.id, eh.action_id, eh.command " \
              "FROM psa.event_handlers eh " \
              "INNER JOIN psa.actions a ON a.id=eh.action_id"
    else:
        sql = "SELECT action_name, id, 0, command " \
              "FROM psa.event_handlers"
    # An empty or relative bin_dir is never a legitimate hook directory;
    # treat it as "no handlers match" instead of matching everything.
    if not bin_dir or not os.path.isabs(bin_dir):
        return {}
    rows = query_sql(sql)
    result = {}
    for row in rows:
        # row[0] - action_name
        # row[1] - handler id
        # row[2] - action id (deprecated in PLESK 17.8 )
        # row[3] - command
        # take only hooks whose executable lives directly in bin_dir
        if not row[3]:
            continue
        try:
            parts = shlex.split(row[3])
        except ValueError:
            continue
        if not parts:
            # whitespace-only or comment-only command yields no tokens
            continue
        handler_exe = parts[0]
        if os.path.dirname(os.path.normpath(handler_exe)) \
                == os.path.normpath(bin_dir):
            result[row[0]] = {
                'handler_id': int(row[1]),
                'action_id': int(row[2]),
                'command': row[3]
            }
    return result


def plesk_hook_install(event_id, script_file):
    """
    Plesk single hook install into /usr/local/bin
    :param event_id: Event ID that must be created
    :param script_file: Command for event
    """
    if not os.path.isfile(script_file):
        logger.error("File '%s' does not exist. "
                     "Maybe some rpm package is malformed. %s",
                     script_file, CONTACT_SUPPORT_MESSAGE_FOOTER)
        return

    try:
        output = subprocess.check_output([
            PLESK_HOOK_REGISTER_FILE, '--create',
            '-command', script_file, '-priority', '50',
            '-user', 'root', '-event', str(event_id)
        ], stderr=subprocess.STDOUT, text=True)
    except (OSError, subprocess.CalledProcessError) as e:
        if isinstance(e, subprocess.CalledProcessError):
            message = e.output.rstrip('\n')
        else:
            message = str(e)
        logger.error('failed to register Plesk hook %s: %s. %s',
                     script_file, message, CONTACT_SUPPORT_MESSAGE_FOOTER)
    else:
        logger.info('Register hook ended successfully; tool output: `%s`', output.rstrip())


def plesk_hook_remove(handler_id):
    """
    Remove Plesk hook
    :param handler_id: Handler ID of created event
    :return: Nothing
    """
    try:
        output = subprocess.check_output([
            PLESK_HOOK_REGISTER_FILE, '--delete', str(handler_id)
        ], stderr=subprocess.STDOUT, text=True)
    except (OSError, subprocess.CalledProcessError) as e:
        if isinstance(e, subprocess.CalledProcessError):
            message = e.output.rstrip('\n')
        else:
            message = str(e)
        logger.error('Failed to unregister Plesk hook %s. '
                     'Plesk reported following error: %s. %s',
                     handler_id, message, CONTACT_SUPPORT_MESSAGE_FOOTER)
    else:
        logger.info('Unregister hook ended successfully; tool output: `%s`', output.rstrip())


def define_hook_ids():
    """
    Defines IDs of hooks by name (extends existing PLESK_HOOKS)
    """
    hook_names_str = ','.join([f"'{i}'" for i in HOOKS])
    sql = f"SELECT id, name FROM psa.actions WHERE name IN({hook_names_str})"
    rows = query_sql(sql)
    if not rows:
        return
    for row in rows:
        HOOKS[row[1]]['EVENT_ID'] = int(row[0])


def install_hooks():
    """
    Plesk hooks install into /usr/local/bin
    """
    if not os.path.isfile(PLESK_HOOK_REGISTER_FILE):
        logger.warning('%s does not exist; skip installing hooks', PLESK_HOOK_REGISTER_FILE)
        return

    panel_data = cpapi.get_cp_description()
    plesk_version = panel_data['version']

    if Version(plesk_version) < Version('17.8'):
        define_hook_ids()

    installed_events = plesk_get_event_handler_name_list(plesk_version)
    for hook_name, hook_data in HOOKS.items():
        # Checking of not installed events
        if hook_name in installed_events:
            logger.info('Hook %s is already registered in plesk; skip', hook_name)
            continue
        logger.debug('Registering %s action hook', hook_name)
        if Version(plesk_version) < Version('17.8'):
            plesk_hook_install(hook_data['EVENT_ID'], hook_data['SCRIPT_FILE'])
        else:
            plesk_hook_install(hook_name, hook_data['SCRIPT_FILE'])


def remove_hooks():
    """
    All Plesk hooks remove
    """
    if not os.path.isfile(PLESK_HOOK_REGISTER_FILE):
        logger.warning('%s does not exist; skip installing hooks', PLESK_HOOK_REGISTER_FILE)
        return

    panel_data = cpapi.get_cp_description()
    plesk_version = panel_data['version']

    installed_events = plesk_get_event_handler_name_list(plesk_version)

    for hook in HOOKS:
        if hook not in installed_events:
            logger.info('Hook %s is not registered in plesk; skip', hook)
            continue
        logger.debug('Unregistering %s action hook', hook)
        plesk_hook_remove(installed_events[hook]['handler_id'])


# DOT NOT CHANGE OR REMOVE: USED IN CAGEFS
def remove_hook_by_str_id_and_location(event_name, bin_dir):
    """
    Remove hooks of given
    :param event_name: string id of hook type,
                       like 'phys_hosting_create'
    :param bin_dir: directory where we should search
                    for hooks subscribed to given event name
    :return: Nothing
    """
    if not os.path.isfile(PLESK_HOOK_REGISTER_FILE):
        return

    panel_data = cpapi.get_cp_description()
    plesk_version = panel_data['version']

    installed_events = plesk_get_event_handler_name_list(plesk_version, bin_dir)

    if event_name not in installed_events:
        return
    plesk_hook_remove(installed_events[event_name]['handler_id'])