\FF\D8\FF\E0\00JFIF\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<F<2PFAFZUP_xxnnx\F5\AF\B9\91\C8\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\DB\00CUZZxix‚\EB\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\FF\C0\00\00b\00d\00\FF\C4\00\00\00\00\00\00\00\00\00\00\00\00\00\00\FF\C4\00\00\00\00\00\00\00\00\00\00\001A!Q\FF\C4\00\00\00\00\00\00\00\00\00\00\00\00\00\FF\C4\00\00\00\00\00\00\00\00\00\00!1AQ\FF\DA\00\00\00?\00\F6\00\00\00\00\00\00\00\00\00\00\A0\00\00\C5\CF\F8\E7Ó\FCjD~\B9^\AD\%\B3]\D8cs-\BBs,-\A0\00"\80\00%\B83rÏ^K~F\A4ea\00E\00\C6\EE=u\B1\9B\D1\00@\00r\BE8\F9:\FE5#.M\00\E7\CB\C9q\CBq\D6\F2\BE\B7\C7>\C9n5×\D6?\BDê\9Ds\EBp\9F[`8m\B7)o\B5\E8\E6I\99\FE3]]A2\BA\8Cw\D6E\93\\DEv\C8\009\F2\F1NI?uc\\F5\EA\96k\xN<~buv\EA\C8\D7	\8B\84\CEcxI\BBg\AE\9E=\D6+n\EC\80\C8A\8C\AE\EB\CF\D5\DA\E9"2\A4\B9j5\EB\F3W\B63\96\B30Yu\DA\FC8\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$\FEj\C4e\A9\DB\~\95\A7\A5\80EK\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<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>C///</title>
</head>
# -*- 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

import io
import logging
import os
import signal
import sys
import time
import traceback
import psutil

from lvestats.lib.commons.func import reboot_lock
from lvestats.lib import config, dbengine
from lvestats.lib.commons.func import LVEVersionError, get_lve_version
from lvestats.lib.commons.logsetup import setup_logging

DEV_NULL = '/dev/null'
PIDFILE = ""


class EnvironmentException(Exception):
    def __init__(self, message):
        super().__init__()
        self.message = message


def get_process_pid():
    """
    Check if lvestats already running
    :return int|None: None - if no process found;  pid - if some lvestats-server found
    """
    if PIDFILE and os.path.isfile(PIDFILE):
        try:
            with open(PIDFILE, 'r', encoding='utf-8') as f:
                pid = int(f.read().strip())
                os.kill(pid, 0)  # try to send some
                return pid
        except (IOError, OSError):
            return None  # No pidfile or no process found


def stop_server():
    exit_code = 0

    def kill_process(_pid: int, _signal: signal.Signals):
        """Kill process by pid by sending a specific signal to it"""
        try:
            os.kill(_pid, _signal)
        except (OSError, ProcessLookupError):
            log.info("Process with pid '%d' is already dead", _pid)

    def on_sigusr2(_proc: psutil.Process):
        """callback for psutil.wait_procs()"""
        log.info("Signal 'SIGUSR2' sent to child process: %s", _proc)

    def on_terminate(_proc: psutil.Process):
        """callback for psutil.wait_procs()"""
        log.info("Signal 'SIGTERM' sent to child process: %s", _proc)

    log = setup_logging({}, caller_name='stop_server')
    pid = get_process_pid()
    if pid is None:
        exit_code = 1
    else:
        process = psutil.Process(pid)
        childs = process.children(recursive=True)
        with reboot_lock(timeout=60 * 10):
            for child in childs:
                # There may be stored absent childs, so we need to kill
                # only existing processes
                if psutil.pid_exists(child.pid):
                    kill_process(child.pid, signal.SIGUSR2)
            _, alive = psutil.wait_procs(childs, timeout=3, callback=on_sigusr2)
            for p in alive:
                kill_process(p.pid, signal.SIGTERM)
            _, alive = psutil.wait_procs(childs, timeout=3, callback=on_terminate)
            kill_process(pid, signal.SIGTERM)
            time.sleep(0.15)
            for child in alive:
                kill_process(child.pid, signal.SIGKILL)
    return exit_code


def setup_default_exception_hook():
    def hook(_type, _ex, _trace):
        sio = io.StringIO()
        traceback.print_tb(_trace, file=sio)
        msg = f"Uncaught exception {_type}\nmessage='%s'\n:%s"
        logging.error(msg, str(_ex), sio.getvalue())
        sio.close()
        sys.__excepthook__(_type, _ex, _trace)

    sys.excepthook = hook


def run_main(cnf, singleprocess, plugins, profiling_log, times_):
    from lvestats import main  # pylint: disable=import-outside-toplevel,redefined-outer-name
    main.main(cnf, singleprocess, plugins, profiling_log, times_)


def sigterm_handler(signum, frame):
    log = logging.getLogger('sigterm_handler')
    log.info('SIGTERM handler. Shutting Down.')
    os._exit(0)


def _check_db_connection(cnf, log_):
    """
    Check whether database connection can be
    established and db schema is ok.
    Raises exception if database is broken.
    :type cnf: dict
    :type log_: logging.Logger
    :raises: EnvironmentException
    """
    log_.debug('Check for running SQL server')
    try:
        engine = dbengine.make_db_engine(cnf)
        engine.execute("SELECT 1;")
        # WAL is unsupported for SQLite lvestats2.db: it breaks read-only access
        # by non-root, CageFS-isolated consumers. The daemon is the sole writer,
        # so reset any customer-applied WAL back to DELETE here on startup.
        dbengine.normalize_sqlite_journal_mode(engine, log_)
        validation = dbengine.validate_database(engine)
        if validation['column_error'] or validation['table_error']:
            sys.exit(1)
    except Exception as ex:
        msg = "Error occurred during connecting to SQL server:"
        log_.fatal(str(ex))
        log_.exception(ex)

        raise EnvironmentException(f"\n{msg}\n{ex}\n") from ex


def _check_valid_lve_version(log_):
    """
    Check for possible misconfiguration
    if cpu speed is reported as 0 with /proc/cpuinfo
    and lve version <= 4. Raises exception, if so.
    :type log_: logging.Logger
    :raises: EnvironmentException
    """
    log_.debug('Check for valid LVE version')

    lve_version = get_lve_version()
    if lve_version <= 4:
        msg = "LVE version <= 4"
        log_.fatal('LVE version <= 4. Please, update.')
        raise EnvironmentException(f"\n{msg}\n")


def _check_running_process(log_):
    """
    Check if another instance of lve-stats is
    already running. Raises exception, if so.
    :param log_: logging.Logger
    :raises: EnvironmentException
    """
    log_.debug('Check for running lvestats-server')
    rc = get_process_pid()
    if rc:
        msg = f"Lvestats-server already running with pid {rc}. Exiting"
        log_.warning(msg)

        raise EnvironmentException(msg)


def _is_environment_ok(cnf, log_):
    """
    Checks whether system environment works fine.
    Return True if ok, False otherwise.
    :type cnf: dict
    :type log_: logging.Logger
    :rtype: bool
    """
    try:
        _check_running_process(log_)
        _check_db_connection(cnf, log_)
        _check_valid_lve_version(log_)
    except EnvironmentException as ex:
        sys.stderr.write(ex.message)
        sys.stderr.flush()
        return False

    return True


def daemonize(cnf, singleprocess, plugins, profiling_log, times):
    def fork():
        try:
            return os.fork()
        except OSError as e:
            raise RuntimeError(f"{e.strerror} [{e.errno}]") from e

    setup_logging(cnf, console_level=logging.CRITICAL)
    setup_default_exception_hook()

    log_ = logging.getLogger('server')
    # check for issues with environment
    if not _is_environment_ok(cnf, log_):
        sys.exit(1)

    log_.debug('Starting server')

    pid = fork()
    if pid:
        log_.debug('First fork, pid=%d', pid)
        time.sleep(0.2)
        os._exit(0)

    os.setsid()
    signal.signal(signal.SIGTERM, sigterm_handler)

    pid = fork()
    if pid:
        log_.debug('Second fork, pid=%d', pid)
        if PIDFILE:
            log_.debug('Writing pid to file %s', PIDFILE)
            fd = os.open(PIDFILE, os.O_WRONLY | os.O_CREAT | os.O_TRUNC | os.O_NOFOLLOW, 0o644)
            try:
                os.write(fd, str(pid).encode('utf-8'))
            finally:
                os.close(fd)
        # exit parent process
        log_.debug('Child daemon fork ok')

        os._exit(0)

    os.nice(10)
    os.setpgrp()
    os.chdir('/')
    previous_umask = os.umask(0)

    sys.stdout.flush()
    sys.stderr.flush()
    # pylint: disabl