HEX
Server: LiteSpeed
System: Linux php-prod-1.spaceapp.ru 5.15.0-157-generic #167-Ubuntu SMP Wed Sep 17 21:35:53 UTC 2025 x86_64
User: xnsbb3110 (1041)
PHP: 8.1.33
Disabled: NONE
Upload Files
File: //proc/self/root/usr/local/CyberCP/public/imunifyav/sbin/installation.py
#!/usr/bin/env python

import atexit
import base64
import json
import os
import socket
import subprocess
import sys
import time


# this code is copy-pasted because execute.py is
# copied into /usr/bin directory in .spec
# also present in execute.py
class Status:
    INSTALLING = "installing"
    OK = "running"
    NOT_INSTALLED = "not_installed"
    FAILED_TO_INSTALL = "failed_to_install"
    STOPPED = "stopped"
    SOCKET_INACCESSIBLE = "socket_inaccessible"


class ImunifyPluginName:
    IMUNIFY_360 = "360"
    IMUNIFY_AV = "AV"


class ImunifyPluginDeployScript:
    IMUNIFY_360 = "i360deploy.sh"
    IMUNIFY_AV = "imav-deploy.sh"


class ImunifyPluginLogs:
    IMUNIFY_360 = "/var/log/i360deploy.log"
    IMUNIFY_AV = "/var/log/imav-deploy.log"


def get_status():
    proc = subprocess.Popen(["ps", "ax"], stdout=subprocess.PIPE)
    output = proc.stdout.read()
    is_i360_running = ImunifyPluginDeployScript.IMUNIFY_360.encode() in output
    is_imav_running = ImunifyPluginDeployScript.IMUNIFY_AV.encode() in output
    if is_i360_running or is_imav_running:
        return Status.INSTALLING
    else:
        sock = None
        try:
            sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
            try:
                sock.connect("/var/run/defence360agent/simple_rpc.sock")
            except socket.error as e:
                if e.errno == 13:
                    return Status.SOCKET_INACCESSIBLE
                raise e
            return Status.OK
        except Exception:  # noqa
            if os.path.exists("/usr/bin/imunify360-agent"):
                return Status.STOPPED
            else:
                if os.path.exists(
                    "/usr/local/psa/var/modules/imunify360/installation.log"
                ):
                    return Status.FAILED_TO_INSTALL
                return Status.NOT_INSTALLED
        finally:
            if sock is not None:
                sock.close()


var_dir = "/usr/local/psa/var/modules/imunify360"
file_prefix = var_dir + "/installation."
socket_path = file_prefix + "sock"


def print_response(data):
    json.dump(data, sys.stdout)
    sys.stdout.write("\n")


class Daemon:
    """
    A generic daemon class.

    Usage: subclass the Daemon class and override the run() method
    """

    def __init__(self):
        self.stdin = "/dev/null"
        self.stdout = "/dev/null"
        self.stderr = "/dev/null"
        self.pidfile = file_prefix + "pid"
        if not os.path.exists(var_dir):
            os.makedirs(var_dir)
        self.log_file = open(file_prefix + "log", "w")

    def daemonize(self):
        """
        do the UNIX double-fork magic, see Stevens' "Advanced
        Programming in the UNIX Environment" for details (ISBN 0201563177)
        http://www.erlenstar.demon.co.uk/unix/faq_2.html#SEC16
        """
        try:
            pid = os.fork()
            if pid > 0:
                # https://unix.stackexchange.com/questions/447898/why-does-a-program-with-fork-sometimes-print-its-output-multiple-times
                # plesk collects stdout from all forked processes
                time.sleep(1)
                print_response(
                    dict(
                        result="success",
                        data=None,
                        messages=[],
                        status=get_status(),
                    )
                )
                # exit first parent
                sys.exit(0)
        except OSError as e:
            sys.stderr.write(
                "fork #1 failed: %d (%s)\n" % (e.errno, e.strerror)
            )
            sys.exit(1)
        self.log_file.write("Installation daemon forked first time\n")
        # buffer will be duplicated
        self.log_file.flush()
        # decouple from parent environment
        os.chdir("/")
        os.setsid()
        os.umask(0)

        # do second fork
        try:
            pid = os.fork()
            if pid > 0:
                # exit from second parent
                sys.exit(0)
        except OSError as e:
            sys.stderr.write(
                "fork #2 failed: %d (%s)\n" % (e.errno, e.strerror)
            )
            sys.exit(1)
        self.log_file.write("Installation daemon forked second time\n")

        # redirect standard file descriptors
        sys.stdout.flush()
        sys.stderr.flush()
        si = open(self.stdin, mode="rb")
        so = open(self.stdout, mode="a+b")
        se = open(self.stderr, mode="a+b", buffering=0)
        os.dup2(si.fileno(), sys.stdin.fileno())
        os.dup2(so.fileno(), sys.stdout.fileno())
        os.dup2(se.fileno(), sys.stderr.fileno())

        # write pidfile
        atexit.register(self.delpid)
        pid = str(os.getpid())
        open(self.pidfile, "w+").write("%s\n" % pid)
        self.log_file.write("Installation daemon initialized\n")

    def delpid(self):
        os.remove(self.pidfile)
        self.log_file.close()

    def start(self, params):
        """
        Start the daemon
        """
        # Check for a pidfile to see if the daemon already runs
        try:
            pf = open(self.pidfile, "r")
            pid = int(pf.read().strip())
            pf.close()
        except IOError:
            pid = None

        if pid:
            message = "pidfile %s already exist. Daemon already running?\n"
            sys.stderr.write(message % self.pidfile)
            sys.exit(0)
        self.log_file.write(
            "Installation daemon created pid file " + str(self.pidfile) + "\n"
        )
        # daemonization will duplicate buffer
        self.log_file.flush()

        # Start the daemon
        self.daemonize()
        self.run(params)

    def run(self, params):
        deploy_scripts_mapping = {
            ImunifyPluginName.IMUNIFY_AV: ImunifyPluginDeployScript.IMUNIFY_AV,
            ImunifyPluginName.IMUNIFY_360: ImunifyPluginDeployScript.IMUNIFY_360,
        }
        deploy_script = deploy_scripts_mapping.get(
            params.get("plugin_name"), ImunifyPluginDeployScript.IMUNIFY_AV
        )
        license_key = params.get("license_key", None)
        self.log_file.write("Starting {}\n".format(deploy_script))
        deploy_script_dir = "/usr/local/psa/admin/sbin/modules/imunify360"
        command = [
            os.path.join(deploy_script_dir, deploy_script),
            "-y",
            "--force",
        ]
        if license_key:
            command.extend(["--key", license_key])
        # flush to make i360deploy.sh write AFTER logs of this file
        self.log_file.flush()

        def preexec_fn():
            os.setsid()
            # need to set working directory, since deployscript can update
            # itself only if located inside working directory
            os.chdir(deploy_script_dir)

        deploy_env = os.environ.copy()
        deploy_env["I360_FROM_PLESK_EXTENSION"] = "1"

        p = subprocess.Popen(
            command,
            preexec_fn=preexec_fn,
            stdout=self.log_file.fileno(),
            stderr=self.log_file.fileno(),
            universal_newlines=True,
            env=deploy_env,
        )

        p.wait(60 * 60)


def run_deploy_in_systemd(params):
    systemd_run_command = "/usr/bin/systemd-run"
    if os.path.exists(systemd_run_command):
        log_file = open(file_prefix + "log", "w")
        log_file.write("Installation by systemd-run initialized\n")
        # for SELinux we have to temporary change selinux mode to permissive
        p = subprocess.Popen(
            [systemd_run_command, "--version"], stdout=subprocess.PIPE
        )
        out, _ = p.communicate()
        systemd_version = out.decode().split()[1]
        log_file.write("systemd version = %s\n" % systemd_version)

        prefix = [systemd_run_command]

        if systemd_version < "235":
            log_mapping = {
                ImunifyPluginName.IMUNIFY_AV: ImunifyPluginLogs.IMUNIFY_AV,
                ImunifyPluginName.IMUNIFY_360: ImunifyPluginLogs.IMUNIFY_360,
            }

            log_file.write(
                "We can't redirect log, look at %s\n"
                % log_mapping.get(
                    params.get("plugin_name"), ImunifyPluginLogs.IMUNIFY_AV
                )
            )
        else:
            prefix += ["-P"]

        unenforce = False
        if os.path.exists("/etc/selinux/config"):
            p = subprocess.Popen(["getenforce"], stdout=subprocess.PIPE)
            out, _ = p.communicate()
            log_file.write("semode = %s" % out.decode())
            if b"Enforcing" in out:
                res = subprocess.call(["setenforce", "0"])
                unenforce = True
                log_file.write("selinux mode changed %r\n" % res)

        prefix = prefix + [
            "-p",
            "SendSIGKILL=no",
            "--slice=imunify_install",
            "--setenv=I360_FROM_PLESK_EXTENSION=1",
            "--",
        ]
        deploy_scripts_mapping = {
            ImunifyPluginName.IMUNIFY_AV: ImunifyPluginDeployScript.IMUNIFY_AV,
            ImunifyPluginName.IMUNIFY_360: ImunifyPluginDeployScript.IMUNIFY_360,
        }
        deploy_script = deploy_scripts_mapping.get(
            params.get("plugin_name"), ImunifyPluginDeployScript.IMUNIFY_AV
        )
        log_file.write("Starting {}\n".format(deploy_script))
        license_key = params.get("license_key", None)
        deploy_script_dir = "/usr/local/psa/admin/sbin/modules/imunify360"
        command = prefix + [
            os.path.join(deploy_script_dir, deploy_script),
            "-y",
            "--force",
        ]
        if license_key:
            command.extend(["--key", license_key])
        log_file.flush()

        def preexec_fn():
            os.setsid()
            # need to set working directory, since deployscript can update
            # itself only if located inside working directory
            os.chdir(deploy_script_dir)

        subprocess.Popen(
            command,
            preexec_fn=preexec_fn,
            stdout=log_file.fileno(),
            stderr=log_file.fileno(),
            universal_newlines=True,
        )
        time.sleep(3)
        if unenforce:
            res = subprocess.call(["setenforce", "1"])
            log_file.write("selinux mode return-back %r\n" % res)
            log_file.flush()
        print_response(
            dict(
                result="success",
                data=None,
                messages=[],
                status=get_status(),
            )
        )
        return True
    else:
        return False


def _get_chunk(offset, limit):
    try:
        with open(file_prefix + "log", "r") as f:
            f.seek(offset)
            # can not use select
            for i in range(10):
                chunk = f.read(limit)
                if chunk == "":
                    time.sleep(1)
                else:
                    return chunk
    except:  # noqa
        pass
    return ""


def status(offset, limit):
    chunk = _get_chunk(offset, limit)
    print_response(
        dict(
            status=get_status(),
            result="success",
            messages=[],
            data=dict(
                items=dict(
                    log=chunk,
                    offset=offset + len(chunk),
                )
            ),
        )
    )


if __name__ == "__main__":
    request = json.loads(base64.b64decode(sys.argv[1]))
    method = request["method"]
    if method == ["start"]:
        if not run_deploy_in_systemd(request["params"]):
            Daemon().start(request["params"])
    elif method == ["status"]:
        status(request["params"]["offset"], request["params"]["limit"])
    else:
        sys.stderr.write("Could not parse input " + str(sys.argv))