/**
 * File:
 *      modules/BootLILO.ycp
 *
 * Module:
 *      Bootloader installation and configuration
 *
 * Summary:
 *      Module containing specific functions for LILO configuration
 *      and installation
 *
 * Authors:
 *      Jiri Srain <jsrain@suse.cz>
 *
 * $Id: BootLILO.ycp 21211 2005-02-04 10:08:05Z jsrain $
 *
 */

{
    module "BootLILO";

    textdomain "bootloader";

    import "Mode";
    import "Kernel";
    import "Storage";

    import "BootCommon";
    import "Arch";
    import "Pkg";

    include "bootloader/lilo/widgets.ycp";
    include "bootloader/lilo/helps.ycp";
    include "bootloader/routines/popups.ycp";
    include "bootloader/routines/routines.ycp";

// private variables





// end of mandatory functions
//----------------------------------------------------------------------------

    /**
      * Create linux section
      * @param title strign section title (linux, failsafe, memtest)
      * @return section description structure
      */
    global define list<map> createLinuxSection (string title) ``{
	if (title == "memtest86")
	{
	  if (BootCommon::MemtestPresent ())
	  {
	    return [
		$[ "key" : "image", "value" : "/boot/memtest.bin"],
		$[ "key" : "label", "value" : BootCommon::translateSectionTitle (title)],
		$[ "key" : "__intern__", "value" : $[
		    "auto" : true,
		    "changed" : false,
		    "devs" : [],
		    "type" : "memtest86",
		]],
	    ];
	  }
	  else
	  {
	    return [];
	  }
	}
	string resumestring = "";
	if (BootCommon::getLargestSwapPartition () != "")
	{
	    resumestring = sformat ("resume=%1",
		BootCommon::getLargestSwapPartition ());
	}
	// not using .shipped images any more
	string initrd = title == "wildcard" ? "initrd-*" : "initrd";
	string image = title == "wildcard" ? "vmlinuz-*" : "vmlinuz";
	string append = title == "failsafe"
	    ? "ide=nodma apm=off acpi=off vga=normal noresume selinux=0 barrier=off "
	      + ((Arch::x86_64 ())
		? "iommu=noforce "
		: "nosmp noapic maxcpus=0 ")
	      + BootCommon::GetAdditionalFailsafeParams () + " 3"
	    : sformat ("%1 %2 %3 %4", resumestring, "selinux=0",
		Kernel::GetCmdLine (),
		BootCommon::GetAdditionalKernelParams ());
	if (title != "failsafe")
	{
	    if (BootCommon::getKernelParamFromLine (append, "splash")
		== "false")
	    {
		append = BootCommon::setKernelParamToLine (
		    append, "splash", "silent");
	    }
	}
	string root = BootCommon::dev2majminIfNonStandard (
            BootCommon::RootPartitionDevice);
	if (regexpmatch (root, "^[0-9a-f]{4}$"))
	{
	    root = "0x" + root;
	}
	string type = title;
	if (type == "wildcard")
	    title = "*";
	list<map> ret = [
	    $[ "key" : "label", "value" : BootCommon::translateSectionTitle (title)],
	    $[ "key" : "image", "value" : "/boot/" + image],
	    $[ "key" : "initrd", "value" : "/boot/" + initrd],
	    $[ "key" : "root", "value" : root ],
	    $[ "key" : "append", "value" : append],
	    $[ "key" : "__intern__", "value" : $[
		"auto" : true,
		"changed" : false,
		"devs" : [BootCommon::RootPartitionDevice],
		"type" : type,
	    ]],
	];
	if (Kernel::GetVgaType () != "" && title != "failsafe")
	    ret = add (ret, $["key" : "vga", "value" : Kernel::GetVgaType ()]);
	if (type == "wildcard")
	{
	    ret = prepend (ret, $[
		"key" : "wildcard",
		"value" : "/boot/" + image,
	    ]);
	}
	return ret;
    }

    /**
      * Propose sections to bootloader menu
      * modifies internal sreuctures
      */
    global define void createSections () ``{
	list<list<map> > out = [
	    createLinuxSection ("linux")
	];
	list<string> others = (list<string>)Storage::GetForeignPrimary();
	if (others != nil)
	{
	    if (size (others) > 0)
	    {
		foreach (string o, others, ``{
		    list<string> parts = splitstring (o, " ");
		    while (parts[0]:" " == "")
			parts = remove (parts, 0);
		    string dev = parts[0]:"";
		    parts = remove (parts, 0);
		    string label = mergestring (parts, " ");

                    // don't add rewritten location (#19990)
		    if (dev != "" && label != ""
			&& dev != BootCommon::loader_device)
		    {
			list<map> m = [
			    $["key" : "label",
				"value" : BootCommon::translateSectionTitle (
				BootCommon::removeBlanks (label))],
			    $[ "key" : "other", "value" : dev],
			    $[ "key" : "__intern__", "value" : $[
				"auto" : true,
				"changed" : false,
				"devs" : [dev],
				"type" : label,
			    ]],
			];
			out = add (out, m);
		    }
		});
	    }
	}
        out = add (out, createLinuxSection ("failsafe"));
	out = add (out, createLinuxSection ("memtest86"));
//	out = add (out, createLinuxSection ("wildcard"));
	out = (list<list<map> >)
	    filter (list<map> s, out, ``(s != [] && s != nil));
	BootCommon::sections = out;
    }

    /**
      * Propose global options of bootloader
      * modifies internal structures
      */
    global define void createGlobals () ``{
	list internal_bios = (list)SCR::Read (.probe.bios);
	list<map> bcg = [
	    $[ "key" : "menu-scheme", "value" : "Wb:kw:Wb:Wb"],
	    $[ "key" : "default", "value" : BootCommon::translateSectionTitle ("linux")],
	    $[ "key" : "timeout", "value" : 80],
	    $[ "key" : "message", "value" : "/boot/message"],
	    $[ "key" : "lba32",
	       "value" : internal_bios[0, "lba_support"]:false],
	    $[ "key" : "change-rules", "value" : "change-rules,reset"],
	    $[ "key" : "read-only", "value" :  true],
	    $[ "key" : "prompt", "value" : true],
	];
	if (Storage::DiskmapNeeded())
	{
	    string order = "";
	    // check if bios_id is given for a target
	    map<string,map> targetMap
		= (map<string,map>)Storage::GetTargetMap ();
	    foreach (string target_dev, map target, targetMap, ``{
		string bios_id = target["bios_id"]:"";
		if (bios_id != "")
		{
		    if (order != "")
			order = order + ",";
		    order = order + sformat ("disk=%1,bios=%2", target_dev,
			bios_id);
		}
	    });
	    if (order != "")
		bcg = add (bcg, $[ "key" : "disk", "value" : order]);
	}
	BootCommon::globals = bcg;
    }

    /**
      * Update sections of bootloader menu
      * modifies internal sreuctures
      * @param replace boolean true if old sectinos shall be replaced
      */
    global define void updateSections (boolean replace) ``{
	BootCommon::liloUpdateSections (replace, BootLILO::createLinuxSection);
	return;
    }

    /**
      * Update global options of bootloader
      * modifies internal sreuctures
      */
    global define void updateGlobals () ``{
	BootCommon::globals = maplist (map e, BootCommon::globals, ``{
	    if (e["key"]:"" == "menu-scheme")
		e["value"] = "Wb:kw:Wb:Wb";
	    else if (e["key"]:"" == "timeout")
		e["value"] = 80;
	    else if (e["key"]:"" == "message")
		e["value"] = "/boot/message";
	    else if (e["key"]:"" == "boot")
		e["value"] = BootCommon::UpdateDevice (e["value"]:"");
	    return e;
	});
    }

    /**
      * Fix sections of bootloader menu during proposal regeneration
      * modifies internal sreuctures
      * @param disks_changed boolean should be true if bootloader location
      *   relevant disk locations have changed
      */
    global define void fixSections (boolean disks_changed) ``{
	BootCommon::liloFixSections (BootLILO::createSections);
    }

    /**
      * Fix global options of bootloader during proposal regeneration
      * modifies internal sreuctures
      * @param disks_changed boolean should be true if bootloader location
      *   relevant disk locations have changed
      */
    global define void fixGlobals (boolean disks_changed) ``{
	BootCommon::liloFixGlobals ();
    }

    /**
      * Create strings representing cfg. files for by-hand editation
      * @return map filename -> contents
      */
    global define map<string,string> GetFiles() ``{
	SCR::Execute (.bootloader.agent_init, "lilo");
	SCR::Execute (.bootloader.disk_init, BootCommon::PrepareInitData ());
	BootCommon::Sections2IO ();
	BootCommon::Global2IO ();
	BootCommon::io_structure["global", "stage1_dev", 0]
	    = BootCommon::loader_device;
	map<string,string> ret = (map<string,string>)
	    SCR::Read (.bootloader.tostrings, BootCommon::io_structure);
	BootCommon::edited_files = ret;
	return ret;
    }

    /**
      * Store strings representing cfg. files to internal structures
      * @param files map filename -> contents
      */
    global define void SetFiles (map<string,string> files) ``{
	SCR::Execute (.bootloader.agent_init, "LILO");
	SCR::Execute (.bootloader.disk_init, BootCommon::PrepareInitData ());
	BootCommon::io_structure = (map)
	    SCR::Read (.bootloader.fromstrings, files);
	BootCommon::IO2Sections ();
	BootCommon::IO2Global ();
	BootCommon::loader_device
	    = BootCommon::io_structure["global", "stage1_dev", 0]:"";
	if (BootCommon::edited_files != BootCommon::io_structure)
	{
	    BootCommon::files_edited = true;
	}
    }

// general functions

    /**
      * Propose bootloader settings
      */
    global define void Propose () ``{
	y2debug ("Started propose: Glob: %1, Sec: %2",
	    BootCommon::globals, BootCommon::sections);
	if (! BootCommon::was_proposed)
	{
            BootCommon::DetectDisks ();
	    BootCommon::del_parts = BootCommon::getPartitionList (`deleted);
	    y2milestone ("Partitions planned to be deleted: %1",
		BootCommon::del_parts);
	}

	boolean disks_changed = BootCommon::RefreshDisks();
	if (disks_changed && ! Mode::autoinst ())
	{
	    if (askLocationResetPopup (BootCommon::loader_device))
	    {
		BootCommon::location = "";
		BootCommon::DetectDisks ();
	    }
	}

	BootCommon::loader_device = BootCommon::GetBootloaderDevice ();

	// set activate flag
	if (! BootCommon::activate_changed)
	{
	    BootCommon::activate
	        = size (Storage::GetBootPartition (BootCommon::mbrDisk)) == 0;
	}

	if (BootCommon::sections == nil || size (BootCommon::sections) == 0)
	{
	    createSections ();
	    BootCommon::kernelCmdLine = Kernel::GetCmdLine ();
	}
	else
	{
            if (Mode::autoinst ())
            {
                // TODO whatever will be needed
		y2debug ("nothing to to in AI mode if sections exist");
            }
            else
		fixSections (disks_changed);
	}
	if (BootCommon::globals == nil || size (BootCommon::globals) == 0)
	{
	    createGlobals ();
	}
	else
	{
            if (Mode::autoinst ())
            {
                // TODO whatever will be needed
		y2debug ("nothing to to in AI mode if globals are defined");
            }
            else
		fixGlobals (disks_changed);
	}

	y2milestone ("Proposed sections: %1", BootCommon::sections);
	y2milestone ("Proposed globals: %1", BootCommon::globals);
	BootCommon::addValueToBootloaderSection (
	    `globals, "boot", BootCommon::loader_device);
    }

    /**
      * Read settings from disk
      * @return boolean true on success
      */
    global define boolean Read () ``{
	BootCommon::DetectDisks ();
	boolean ret = BootCommon::Read ();
	return ret;
    }

    /**
      * Save all bootloader configuration files
      * @return boolean true if success
      */
    global define boolean Save () ``{
	// update graphical bootloaer menu
	BootCommon::updateGfxMenu ();

	SCR::Execute (.target.bash, "test -f /etc/lilo.conf && /bin/cp /etc/lilo.conf /etc/lilo.conf.old");
	SCR::Execute (.target.bash, "/usr/bin/touch /etc/lilo.conf");

	BootCommon::liloRemoveUnexistentSections ("", "");
	BootCommon::liloUpdateInitrd ();
	BootCommon::liloUpdateAppend ();
	string message = (string)
	    lookup_value (BootCommon::globals, "message", "");
        if (message != "")
        {
            if (-1 == SCR::Read (.target.size, message))
	    {
                BootCommon::globals = modifySection (
		    BootCommon::globals, "message", nil);
	    }
        }
	boolean ret = BootCommon::Save ();
	SCR::Execute (.target.bash, "/bin/chmod 600 /etc/lilo.conf");
	return ret;
    }

    /**
      * Update read settings to new version of configuration files
      */
    global define void Update () ``{
	updateSections (true);
	updateGlobals ();
	BootCommon::loader_device
	    = BootCommon::UpdateDevice (BootCommon::loader_device);
    }
    /**
      * Write bootloader settings to disk
      * @return boolean true on success
      */
    global define boolean Write () ``{
	BootCommon::updateMBR ();
	boolean ret = true;
	if (BootCommon::installingToFloppy ())
	{
	    if (! saveToFLoppyPopup ())
	    {
		y2error ("Preparing floppy disk failed.");
		ret = false;
	    }
	}
	// create backup copy first
	if (BootCommon::createBackupBS ()
	    && BootCommon::getDeviceOfRaid (BootCommon::BootPartitionDevice)
		!= BootCommon::BootPartitionDevice)
	{
	    y2milestone ("Creating backup copy to bootsector");
	    SCR::Execute (.target.bash, sformat ("/sbin/lilo -b %1",
		BootCommon::BootPartitionDevice));
	}

	string bl_command = "/sbin/lilo >> /var/log/YaST2/y2loglilo 2>&1";
	string bl_logfile = "/var/log/YaST2/y2loglilo";
	boolean command_ret
	    = BootCommon::installBootLoader (bl_command, bl_logfile);
	if (! command_ret)
	    y2error ("Execution of installation command failed");
	ret = ret && command_ret;

	if (command_ret && BootCommon::installingToFloppy ())
        {
            BootCommon::updateTimeoutPopupForFloppy
		(BootCommon::getLoaderName ("lilo"));
        }

	return ret;
    }

    /**
      * Return map of provided functions
      * @return map map of functions (eg. $["write":``(BootLILO::Write ())])
      */
    global define map<string, any> GetFunctions () ``{
	return $[
	    "read"	: BootLILO::Read,
	    "propose"	: BootLILO::Propose,
	    "save"	: BootLILO::Save,
	    "update"	: BootLILO::Update,
	    "write"	: BootLILO::Write,
	    "widgets"	: BootLILO::GetWidgetMaps,
	    "getfiles"	: BootLILO::GetFiles,
	    "setfiles"	: BootLILO::SetFiles,
	];
    }

    /**
      * Initializer of LILO bootloader
      */
    global define void Initializer () ``{
	y2milestone ("Called LILO initializer");
	BootCommon::current_bootloader_attribs = $[
	    "section_title" : "label",
	    "kernel_key" : "image",
	    "default_key" : "default",
	    "default_type" : `label,
	    "timeout_multiplier" : 10,
	    "alias_keys" : ["alias"],
	    "convert_function" : BootCommon::liloConvertSection,
	    "kernel_params" : $[
		"vga" : "vga",
		"root" : "root",
		`other : "append",
	    ],
	    "propose" : true,
	    "read" : true,
	    "scratch" : true,
	    "restore_mbr" : true,
	    "key_only_once" : true,
	    "bootloader_on_disk" : true,
	    "option_list" : BootCommon::GetOptions (),
	];
	BootCommon::cwm_options = (map<string, map<string,any> >)union (
	    BootCommon::GetOptionMaps (),
	    BootLILO::GetOptionMaps ());
	BootCommon::cwm_widgets = (map<string, map<string,any> >)union (
	    BootCommon::GetWidgetMaps (),
	    BootLILO::GetWidgetMaps ());
    }

    /**
      * Constructor
      */
    global define void BootLILO () ``{
	BootCommon::bootloader_attribs["lilo"] = $[
	    "required_packages" : ["lilo"],
	    "loader_name" : "LILO",
	    "initializer" : BootLILO::Initializer,
	];
    }



}
