| Viewing file:  manager.py (9.25 KB)      -rw-r--r-- Select action/file-type:
 
  (+) |  (+) |  (+) | Code (+) | Session (+) |  (+) | SDB (+) |  (+) |  (+) |  (+) |  (+) |  (+) | 
 
# -*- coding: utf-8 -*-
 # Copyright © Cloud Linux GmbH & Cloud Linux Software, Inc 2010-2018 All Rights Reserved
 #
 # Licensed under CLOUD LINUX LICENSE AGREEMENT
 # http://cloudlinux.com/docs/LICENSE.TXT
 #
 
 import logging
 import os
 import pwd
 import shutil
 import subprocess
 from collections import namedtuple
 
 from packaging.version import Version
 
 from clcommon.utils import get_file_lines, write_file_lines, mod_makedirs
 from clcommon.public_hooks import CLOUDLINUX_HOOKS, CONTACT_SUPPORT_MESSAGE_FOOTER
 from clcommon.cpapi import get_cp_description
 
 BIN_DIR = os.path.join(CLOUDLINUX_HOOKS, 'directadmin/')
 
 
 Hook = namedtuple('Hook', ['path', 'hook'])
 HookPath = namedtuple('HookPath', ['min_version', 'max_version', 'path'])
 
 HOOKS = { # None means +inf or -int depending on positions
 Hook(BIN_DIR + 'user_create_post', (HookPath(None, '1.60', 'user_create_post.sh'),
 HookPath('1.60', None, 'user_create_post/CL_user_create_post.sh'))),
 Hook(BIN_DIR + 'user_destroy_post', (HookPath(None, '1.60', 'user_destroy_post.sh'),
 HookPath('1.60', None, 'user_destroy_post/CL_user_destroy_post.sh'))),
 Hook(BIN_DIR + 'user_destroy_pre', (HookPath(None, '1.60', 'user_destroy_pre.sh'),
 HookPath('1.60', None, 'user_destroy_pre/CL_user_destroy_pre.sh'))),
 Hook(BIN_DIR + 'user_restore_post', (HookPath(None, '1.60', 'user_restore_post.sh'),
 HookPath('1.60', None, 'user_restore_post/CL_user_restore_post.sh'))),
 Hook(BIN_DIR + 'domain_change_post', (HookPath(None, '1.60', 'domain_change_post.sh'),
 HookPath('1.60', None, 'domain_change_post/CL_domain_change_post.sh'))),
 }
 DA_HOOK_DEST_DIR = '/usr/local/directadmin/scripts/custom'
 if get_cp_description() is None:
 DA_VERSION = '1.0'  # in case we can't determine version we set default old
 else:
 DA_VERSION = get_cp_description()["version"]
 
 logger = logging.getLogger(__name__)
 
 
 def _folder_hooks_compatibility():
 """
 Check that DA has compatibility with folder-based hooks
 :return: Bool
 """
 return Version(DA_VERSION) >= Version('1.60')
 
 
 def _get_hook_from_structure(hook):
 """
 hook = HookPath stucture
 return suitable hook name depending on version
 if return None - suitable hook not found
 """
 for version_hook in hook:
 if version_hook.min_version is None:
 min_version = True
 else: # if DA bigger than the min val
 min_version = Version(DA_VERSION) >= Version(version_hook.min_version)
 if version_hook.max_version is None:
 max_version = True
 else: # if DA lesser that the max val
 max_version = Version(DA_VERSION) < Version(version_hook.max_version)
 if min_version and max_version:
 return version_hook.path
 return None
 
 
 def create_da_hook(da_hook_filename, command, da_hook_default_dir=DA_HOOK_DEST_DIR):
 """
 Creates DA hook
 Example args:
 da_hook_filename = user_create_post.sh
 da_hook_src = /usr/share/cagefs-plugins/hooks/directadmin/user_create_post.sh - command
 
 :param string da_hook_filename: How to name that hook in DA panel
 :param command: what we should run on hook
 :param string da_hook_default_dir:
 :return: None
 """
 
 hook_fullname = os.path.join(da_hook_default_dir, da_hook_filename)
 
 logger.debug('Registering %s action hook', hook_fullname)
 try:
 da_user = pwd.getpwnam('diradmin')
 except (KeyError,) as e:
 logger.error("failed to find 'diradmin' user: %s", str(e))
 return
 
 da_user_uid = da_user.pw_uid
 da_user_gid = da_user.pw_gid
 
 try:
 # if hook file already in system
 if os.path.isfile(hook_fullname):
 # get hook content
 content = get_file_lines(hook_fullname)
 content = [line for line in content if line != '\n']
 # Flags for check if hook installed and if hook on bash
 hook_installed = False
 hook_on_bash = False
 
 for line in content:
 # check if hook installed
 if line.find(command) != -1:
 hook_installed = True
 break
 # check if hook on bash
 if line.startswith('#!/') and (line.find('/sh') != -1 or line.find('/bash') != -1):
 hook_on_bash = True
 
 # if hook not installed
 if not hook_installed:
 # if hook on bash
 if hook_on_bash:
 # add command for cagefs hook
 content.append('\n' + command + '\n')
 write_file_lines(hook_fullname, content, 'w')
 else:  # if hook not on bash
 try:
 # backup old hook filename
 dirname, fname = os.path.split(hook_fullname)
 old_hook_backup = os.path.join(dirname, 'old_'+fname)
 
 # copy backup old hook
 shutil.copyfile(hook_fullname, old_hook_backup)
 os.chmod(old_hook_backup, 0o700)
 os.chown(old_hook_backup, da_user_uid, da_user_gid)
 
 # replace old hook by new hook on bash
 write_file_lines(hook_fullname,
 '#!/bin/bash\n' + command + '\n' + old_hook_backup + '\n', 'w')
 os.chmod(hook_fullname, 0o700)
 os.chown(hook_fullname, da_user_uid, da_user_gid)
 except (OSError, shutil.Error) as e:
 logger.error('Failed to create hook for DirectAdmin: %s: %s. %s',
 hook_fullname, str(e), CONTACT_SUPPORT_MESSAGE_FOOTER)
 except (OSError, IOError) as e:
 logger.error('Failed to install hook for DirectAdmin: %s. %s',
 str(e), CONTACT_SUPPORT_MESSAGE_FOOTER)
 
 # Install hook if it's not installed
 if not os.path.isfile(hook_fullname):
 try:
 if not os.path.isdir(os.path.dirname(hook_fullname)):
 mod_makedirs(os.path.dirname(hook_fullname), 0o700)
 os.chmod(os.path.dirname(hook_fullname), 0o700)
 os.chown(os.path.dirname(hook_fullname), da_user_uid, da_user_gid)
 write_file_lines(hook_fullname, '#!/bin/bash\n' + command + '\n', 'w')
 os.chmod(hook_fullname, 0o700)
 os.chown(hook_fullname, da_user_uid, da_user_gid)
 except (OSError, IOError) as e:
 logger.error('Failed to install hook for DirectAdmin: %s. %s',
 str(e), CONTACT_SUPPORT_MESSAGE_FOOTER)
 
 
 # DO NOT CHANGE THIS METHOD
 # STILL USED IN LVEMANAGER & CAGEFS
 def remove_da_hook(da_hook_filename, command, da_hook_default_dir=DA_HOOK_DEST_DIR):
 """
 Removes DA hook
 da_hook_default_dir = DA_HOOK_DEST_DIR = /usr/local/directadmin/scripts/custom
 da_hook_filename = user_create_post.sh
 command = /usr/share/cagefs-plugins/hooks/directadmin/user_create_post.sh
 
 :param string da_hook_filename: How to name that hook in DA panel
 :param command: what we should run on hook
 :param da_hook_default_dir: default dir for hooks
 :return: None
 """
 logger.debug('Unregistering %s action hook', da_hook_filename)
 
 hook_fullname = os.path.join(da_hook_default_dir, da_hook_filename)
 
 # check if hook exist in system
 if not os.path.isfile(hook_fullname):
 logger.info('Hook %s is not installed; skip', hook_fullname)
 return
 
 try:
 content = get_file_lines(hook_fullname)
 new_content = []
 for line in content:
 # check for hook execution command in hook
 if line != '\n' and line.find(command) == -1:
 new_content.append(line)
 # write changes to hook
 write_file_lines(hook_fullname, new_content, 'w')
 except IOError as e:
 logger.error('Failed to remove hook for DirectAdmin: %s. %s',
 str(e), CONTACT_SUPPORT_MESSAGE_FOOTER)
 
 
 def install_hooks():
 if not _folder_hooks_compatibility():
 subprocess.run(
 f"touch {DA_HOOK_DEST_DIR}/.old_hooks_present",
 shell=True,
 executable="/bin/bash",
 check=False,
 )
 else:
 if os.path.isfile(f"{DA_HOOK_DEST_DIR}/.old_hooks_present"):
 remove_hooks()
 
 for hook_structure in HOOKS:
 hook_name = _get_hook_from_structure(hook_structure.hook)
 if hook_name is None:
 logger.error('Failed to install hook for DirectAdmin: %s. %s',
 "Can't find suitable version", CONTACT_SUPPORT_MESSAGE_FOOTER)
 continue
 create_da_hook(hook_name, hook_structure.path)
 
 
 def remove_hooks():
 if _folder_hooks_compatibility and os.path.isfile(f"{DA_HOOK_DEST_DIR}/.old_hooks_present"):
 subprocess.run(
 f"rm -f {DA_HOOK_DEST_DIR}/.old_hooks_present",
 shell=True,
 executable="/bin/bash",
 check=False,
 )
 for hook_structure in HOOKS:
 hook_names = [version_hook.path for version_hook in hook_structure.hook]
 for name in hook_names:
 remove_da_hook(name, hook_structure.path)
 
 |