#!/usr/bin/env python

# SPDX-License-Identifier: GPL-2.0-only

from __future__ import print_function

import sys
import os
import os.path
import signal
import subprocess

# user interface:
import tui
import tui.init
import tui.progress

import install
import init_constants
import netutil
import diskutil
import disktools
import util
from netinterface import *
from xcp import logger

from version import *

# Attempt to configure the network:
def configureNetworking(ui, device, config):
    if ui:
        ui.progress.showMessageDialog(
            "Preparing for installation",
            "Attempting to configure networking..."
            )

    if device == 'all':
        config = 'dhcp'
    mode, rest = config.split(":", 1) if config and ":" in config else (config, None)
    config_dict = {'gateway': None, 'dns': None, 'domain': None, 'vlan': None}
    if rest:
        for el in rest.split(';'):
            k, v = el.split('=', 1)
            config_dict[k] = v
    if mode == 'static':
        if config_dict['dns'] is not None:
            config_dict['dns'] = config_dict['dns'].split(',')
        assert 'ip' in config_dict and 'netmask' in config_dict
    if config_dict['vlan']:
        if not netutil.valid_vlan(config_dict['vlan']):
            raise RuntimeError("Invalid VLAN value for installer network")
        config_dict['vlan'] = int(config_dict['vlan'])

    nethw = netutil.scanConfiguration()
    netcfg = {}
    for i in nethw:
        if (device == i or device == nethw[i].hwaddr) and mode == 'static':
            netcfg[i] = NetInterface(NetInterface.Static, nethw[i].hwaddr,
                                     config_dict['ip'], config_dict['netmask'],
                                     config_dict['gateway'], config_dict['dns'],
                                     config_dict['domain'], config_dict['vlan'])
        else:
            netcfg[i] = NetInterface(NetInterface.DHCP, nethw[i].hwaddr,
                                     vlan=config_dict['vlan'])

    netutil.writeNetInterfaceFiles(netcfg)
    netutil.writeResolverFile(netcfg, '/etc/resolv.conf')

    iface_to_start = []
    if device == 'all':
        iface_to_start.extend(list(netcfg.keys()))
    elif device.startswith('eth'):
        if device in nethw:
            iface_to_start.append(device)
    else:
        # MAC address
        matching_list = [x for x in nethw.values() if x.hwaddr == device]
        if len(matching_list) == 1:
            devname = matching_list[0].name
            iface_to_start.append(devname)

    for i in iface_to_start:
        netutil.ifup(netcfg[i].getInterfaceName(i))
        netcfg[i].waitUntilUp(i)

    if ui:
        ui.progress.clearModelessDialog()

def sig_term(x, y):
    logger.log("Killed by another instance, terminating")
    os.system('/usr/bin/clear')
    sys.exit(0)

def main(args):
    # log to tty3
    logger.openLog('/dev/tty3')
    logger.openLog('/tmp/install-log', level=logger.logging.DEBUG)

    tty = None
    signal.signal(signal.SIGTERM, sig_term)
    try:
        tty = os.path.basename(os.readlink('/proc/self/fd/0'))
        pidfile = open('/var/run/installer-%s.pid' % tty, 'w')
        print(os.getpid(), file=pidfile)
        pidfile.close()
    except:
        pass

    ui = tui
    interactive = True
    answer_device = 'all'
    answer_config = 'dhcp'
    init_network = False
    reboot = False
    answerfile_address = None
    answerfile_script = None
    mpath = False
    use_ibft = False
    netdev_map = []

    try:
        installer_version = subprocess.check_output(
            ['rpm', '-q', '--qf', '%{VERSION}-%{RELEASE}', 'host-installer'])
    except Exception:
        installer_version = 'unknown'

    logger.log("%s Setup - Version %s" % (PRODUCT_BRAND or PLATFORM_NAME, PRODUCT_VERSION_TEXT))
    logger.log("Installer Version %s" % (installer_version,))
    logger.log("Command line args: %s" % str(args))
    for (opt, val) in args.items():
        if opt == "--answerfile":
            answerfile_address = val
            interactive = False
            if not val.startswith('file://'):
                init_network = True
        elif opt == "--rt_answerfile":
            answerfile_address = val
            interactive = False
            if not val.startswith('file://'):
                init_network = True
            ui = None
            logger.openLog(sys.stdout)
        elif opt == "--answerfile_generator":
            answerfile_script = val
            interactive = False
            if not val.startswith('file://'):
                init_network = True
        elif opt in ['--answerfile_device', '--network_device']:
            answer_device = val.lower()
            init_network = True
        elif opt == '--network_config':
            answer_config = val.lower()
        elif opt == "--reboot":
            reboot = True
        elif opt == "--device_mapper_multipath":
            if val.lower() in [ "disabled", "false", "0", "no" ]:
                mpath = False
            elif val.lower() in [ "enabled", "true", "1", "yes", "force"]:
                mpath = True
        elif opt == "--use_ibft":
            use_ibft = True
        elif opt == "--map_netdev":
            netdev_map = val

    # start the user interface:
    if ui:
        # switch to ISO 8859-1 mode so line drawing characters work as expected on
        # vt100 terminals.
        print("\033%@")

        logger.log("Starting 'init' user interface on %s" % tty)
        ui.init_ui()

    # let the user choose what they would like to do:
    if interactive:
        # choose keymap
        kmap = ui.init.get_keymap()

        if tty:
            try:
                # terminate any additional instances of the installer
                for p in os.listdir('/var/run'):
                    if p != 'installer-%s.pid' % tty and p.startswith('installer-'):
                        f = open('/var/run/'+p)
                        pid = int(f.readline())
                        f.close()
                        logger.log("Killing installer with pid %d" % pid)
                        os.kill(pid, signal.SIGTERM)
            except:
                pass

        args['--keymap'] = kmap
        logger.log("Loading keymap %s" % kmap)
        util.runCmd2(["/bin/loadkeys", kmap])

    # Always sanitise netdevs - it generates
    # data for later in the install
    # CA-60620 - dont try and run remap_netdevs in the codepath where we are
    # running several concurrent instances.  It causes fun with competing
    # /sbin/ip renames
    netutil.remap_netdevs(netdev_map)

    # Attaches iSCSI disks listed in iSCSI Boot Firmware Tables.  This may
    # reserve NICs and so should be called before netutil.scanConfiguration
    if use_ibft:
        try:
            diskutil.process_ibft(ui, interactive)
        except Exception as e:
            logger.logException(e)
            if ui:
                ui.exn_error_dialog("install-log", False, interactive)
                return reboot
            raise

    # ensure partitions/disks are not locked by LVM
    # this should be done before attempting to enable multipath
    lvm = disktools.LVMTool()
    lvm.deactivateAll()
    del lvm

    # Ensure multipath devices are created unless installer is being
    # run with the "--device_mapper_multipath=disabled" option
    if mpath:
        diskutil.mpath_enable()

    if ui:
        netutil.setAllLinksUp()
    if init_network:
        configureNetworking(ui, answer_device, answer_config)

    logger.log("Starting installation/upgrade/restore")

    rc = install.go(ui, args, answerfile_address, answerfile_script)

    if ui:
        ui.end_ui()

    # Bring down multipath devices to ensure they're flushed
    if mpath:
        diskutil.mpath_disable()

    # Log out of any iSCSI disks
    if use_ibft:
        diskutil.release_ibft_disks()

    # stop logging to tty3:
    logger.closeLogs()

    return reboot


if __name__ == "__main__":
    reboot = main(util.splitArgs(sys.argv[1:], ('--console', '--map_netdev', '--mount')))
    if reboot:
        os.system("reboot")
