ACC SHELL

Path : /usr/share/YaST2/include/bootloader/routines/
File Upload :
Current File : //usr/share/YaST2/include/bootloader/routines/lilolike.ycp

/**
 * File:
 *      include/bootloader/routines/lilolike.ycp
 *
 * Module:
 *      Bootloader installation and configuration
 *
 * Summary:
 *      Functions common for lilo-like bootloaders only
 *
 * Authors:
 *      Jiri Srain <jsrain@suse.cz>
 *      Olaf Dabrunz <od@suse.de>
 *
 * $Id: lilolike.ycp 62030 2010-05-19 07:58:43Z juhliarik $
 *
 */
{

textdomain "bootloader";

import "Arch";
import "Mode";
import "Storage";
import "StorageDevices";
import "BootArch";
import "Map";

global string DiskOrderSummary ();
global void DetectDisks ();
global boolean DisksChanged ();

include "bootloader/routines/i386.ycp";

/**
 * Check whether disk settings were changed since last checking
 * @return boolean true if needs to recheck
 */
global boolean DisksChanged () {
    if (Mode::config ())
	return false;
    map mp = Storage::GetMountPoints();
    string actual_root = mp["/", 0]:"";
    string actual_boot = mp["/boot", 0]:actual_root;

    // don't change configuration if '/' and '/boot' were not changed
    // and location is "floppy", "mbr" or "boot"
    if (actual_boot == BootStorage::BootPartitionDevice
	&& actual_root == BootStorage::RootPartitionDevice
            && selected_location != "custom"
            && selected_location != ""
	    && selected_location != nil)
    {
	return false;
    }

    list all_partitions = BootStorage::getPartitionList(`boot, getLoaderType(false));

    if (!contains(all_partitions, BootCommon::loader_device))
    {
	y2milestone ("Location should be set again");
	return true;
    }
    return false;
}

/**
 * FindMbrDisk()
 * try to find the system's mbr device
 * @return string   mbr device
 */
global string FindMBRDisk() {
    // check the disks order, first has MBR
    list<string> order = BootStorage::DisksOrder ();
    if (size (order) > 0)
    {
	string ret = order[0]:"";
	y2milestone ("First disk in the order: %1, using for MBR", ret);
	return ret;
    }

    // OK, order empty, use the disk with boot partition
    map mp = Storage::GetMountPoints();
    string boot_disk = mp["/boot",2]:(mp["/",2]:"");
    y2milestone ("Disk with boot partition: %1, using for MBR", boot_disk);
    return boot_disk;
}



/**
 * function check all partitions and it tries to find /boot partition
 * if it is MD Raid and soft-riad return correct device for analyse MBR
 * @param list<map> list of partitions
 * @return string device for analyse MBR
 */
define string soft_MDraid_boot_disk(list<map> partitions)
{
   string result = "";
   string boot_device = "";
   if ((BootStorage::BootPartitionDevice != nil) && (BootStorage::BootPartitionDevice != ""))
       boot_device = BootStorage::BootPartitionDevice;
   else
       boot_device = BootStorage::RootPartitionDevice;

   foreach(map p, partitions, {
	if (p["device"]:"" == boot_device)
	{
	   if ((p["type"]:nil == `sw_raid) && (p["fstype"]:"" == "MD Raid"))
	   {
	      string device_1 = p["devices",0]:"";
	      y2debug("device_1: %1", device_1);
	      map dp = Storage::GetDiskPartition (device_1);
	      y2debug("dp: %1", dp);
	      result = dp["disk"]:"";
	   }
	}
   });
   y2milestone("Device for analyse MBR from soft-raid (MD-Raid only): %1", result);
   return result;
}



    /**
     * ConfigureLocation()
     * Where to install the bootloader.
     * Returns the type of device where to install: one of "boot", "root", "mbr", "mbr_md"
     * Also sets internal global variable selected_location to this.
     *
     *
     * @return string type of location proposed to bootloader
     */
     // FIXME: replace with grub_ConfigureLocation() when lilo et al. have
     // changed to stop using selected_location and loader_device.
    string ConfigureLocation() {
        selected_location = "mbr";             // default to mbr
        loader_device   = BootCommon::mbrDisk;
	// check whether the /boot partition
	//  - is primary:			    is_logical	-> false
	//  - is on the first disk (with the MBR):  disk_is_mbr -> true
	map<string,any> tm = Storage::GetTargetMap ();
	map dp = Storage::GetDiskPartition (BootStorage::BootPartitionDevice);
	string disk = dp["disk"]:"";
	boolean disk_is_mbr = disk == mbrDisk;
	map dm = tm[disk]:$[];
	list<map> partitions = dm["partitions"]:[];
	boolean is_logical = false;
	string extended = nil;
	list<string> needed_devices = [ BootStorage::BootPartitionDevice ];
	map<string,integer> md_info = Md2Partitions (BootStorage::BootPartitionDevice);
	if (md_info != nil && size (md_info) > 0)
	{
	    disk_is_mbr = false;
	    needed_devices = maplist (string d, integer b, md_info, {
		map pdp = Storage::GetDiskPartition (d);
		string p_disk = pdp["disk"]:"";
		if (p_disk == mbrDisk)
		    disk_is_mbr = true;
		return d;
	    });
	}
	y2milestone ("Boot partition devices: %1", needed_devices);
	foreach (map p, partitions, {
	    if (p["type"]:nil == `extended)
	    {
		extended = (string)p["device"]:nil;
	    }
	    else if (contains (needed_devices, p["device"]:"")
	        && p["type"]:nil == `logical)
	    {
		is_logical = true;
	    }
	});
	y2milestone ("/boot is on 1st disk: %1", disk_is_mbr);
	y2milestone ("/boot is in logical partition: %1", is_logical);
	y2milestone ("The extended partition: %1", extended);

	// keep_mbr, if the MBR contains special code that needs to be kept,
	//           like Thinkpad boot code (and ATM only Thinkpad boot code
	//           is recognized)
	boolean keep_mbr = KeepMBR (loader_device);

	integer exit = 0;
	// if is primary, store bootloader there
	if (disk_is_mbr && ! is_logical)
	{
	    selected_location = "boot";
	    loader_device = BootStorage::BootPartitionDevice;
	    activate = true;
	    activate_changed = true;

            // check if there is raid and if it soft-raid select correct device for analyse MBR
            // bnc #398356
	    if (size (needed_devices) > 1)
	       disk = soft_MDraid_boot_disk(partitions);
	    if (disk == "")
	       disk = dp["disk"]:"";
	    string out = examineMBR(disk);

	    repl_mbr = (out != "vista") && (! keep_mbr);

	}
	else if (size (needed_devices) > 1)
	{
	    loader_device = "mbr_md";
	    selected_location = "mbr_md";
	}

	if (keep_mbr)
	{
	    if (is_logical && extended != nil)
		loader_device = extended;
	    else
		loader_device = BootStorage::BootPartitionDevice;
	    selected_location = "boot";
	}
	if (! contains (BootStorage::getPartitionList (`boot, getLoaderType(false)), loader_device))
	{
	    selected_location = "mbr";             // default to mbr
	    loader_device   = BootCommon::mbrDisk;
	}

        y2milestone ("ConfigureLocation (%1 on %2)",
	    selected_location, loader_device);

	// set active flag
	if (selected_location == "mbr")
	{
	    // we are installing into MBR:
	    // if there is an active partition, then we do not need to activate
	    // one (otherwise we do)
            activate = size (Storage::GetBootPartition (mbrDisk)) == 0;
	}
	else
	{
	    // if not installing to MBR, always activate
	    activate = true;
	}

        return selected_location;
    }

    /**
      * Detect /boot and / (root) partition devices
      * If loader_device is empty or the device is not available as a boot
      * partition, also calls ConfigureLocation to configure loader_device, set
      * selected_location and set the activate flag if needed
      * all these settings are stored in internal variables
      */
    global void DetectDisks () {
	// #151501: AutoYaST needs to know the activate flag and the
	// loader_device; jsrain also said this code is probably a bug:
	// commenting out, but this may need to be changed and made dependent
	// on a "clone" flag (i.e. make the choice to provide minimal (i.e. let
	// YaST do partial proposals on the target system) or maximal (i.e.
	// stay as closely as possible to this system) info in the AutoYaST XML
	// file)
	// if (Mode::config ())
	//    return;
        map mp = Storage::GetMountPoints();

        list mountdata_boot = mp["/boot"]:(mp["/"]:[]);
	list mountdata_root = mp["/"]:[];

        y2milestone( "mountPoints %1", mp );
        y2milestone( "mountdata_boot %1", mountdata_boot );

        BootStorage::RootPartitionDevice = mp["/", 0]:"";

        if (BootStorage::RootPartitionDevice == "")
        {
            y2error ("No mountpoint for / !!");
        }

        // if /boot changed, re-configure location
        BootStorage::BootPartitionDevice
            = mountdata_boot[0]:BootStorage::RootPartitionDevice;

	if (BootCommon::mbrDisk == "" || BootCommon::mbrDisk == nil)
	{
	    // mbr detection.
	    BootCommon::mbrDisk = FindMBRDisk();
	}

	if (loader_device == nil || loader_device == ""
	    || ! contains (BootStorage::getPartitionList (`boot, getLoaderType(false)), loader_device))
            ConfigureLocation ();

    }

    /**
      * Converts the md device to the list of devices building it
      * @param md_device string md device
      * @return a map of devices (from device name to BIOS ID or nil if
      *   not detected) building the md device
      */
    global define map<string, integer> Md2Partitions (string md_device) {
	map<string,integer> ret = $[];
	map<string,any> tm = (map<string,map>)Storage::GetTargetMap();
	foreach (string disk, any descr_a, tm, ``{
	    map<string,any> descr = (map<string,any>)descr_a;
	    string bios_id_str = descr["bios_id"]:"";
	    integer bios_id = 256; // maximum + 1 (means: no bios_id found)
	    if (bios_id_str != "")
		bios_id = tointeger (bios_id);
	    list<map<string,any> > partitions = (list<map<string,any> >)
		descr["partitions"]:[];
	    foreach (map<string,any> partition, partitions, ``{
		if (partition["used_by_device"]:"" == md_device)
		{
		    string d = (string)(partition["device"]:"");
		    ret[d] = bios_id;
		}
	    });
	});
	y2milestone ("Partitions building %1: %2", md_device, ret);
	return ret;
    }

    /**
      * Converts the md device to the first of its members
      * @param md_device string md device
      * @return string one of devices building the md array
      */
    global define string Md2Partition (string md_device) {
	map<string,integer> devices = Md2Partitions (md_device);
	if (size (devices) == 0)
	    return md_device;
	integer minimal = 129; // maximum + 2
	string found = "";
	foreach (string k, integer v, devices, {
	    if (v < minimal)
	    {
		found = k;
		minimal = v;
	    }
	});
	return found;
    }


/**
 * Run delayed updates
 *
 * This is used by perl-Bootloader when it cannot remove sections from the
 * bootloader configuration from the postuninstall-script of the kernel. It
 * writes a command to a delayed update script that is then called here to
 * remove these sections.
 *
 * The script is deleted after execution.
 */
global void RunDelayedUpdates () {
    string scriptname = "/boot/perl-BL_delayed_exec";
    string cmd = sformat("test -x %1 && { cat %1 ; %1 ; }", scriptname);

    y2milestone ("running delayed update command: %1", cmd);
    map out = (map)SCR::Execute (.target.bash_output, cmd);
    y2milestone ("command returned %1", out);

    cmd = sformat("rm -f %1", scriptname);
    out = (map)SCR::Execute (.target.bash_output, cmd);
}


/**
 * fallback list for kernel flavors (adapted from Kernel.ycp), used if we have
 * no better information
 * order is from special to general, but prefer "default" in favor of "xen"
 */
// FIXME: handle "rt" and "vanilla"?
// bnc #400526 there is not xenpae anymore...
list<string> generic_fallback_flavors = [
	"s390", "iseries64", "ppc64", "bigsmp", "default", "xen",
];

/**
 * Fix global section of lilo-like bootloader
 *
 * This currently only tries to fix the "default" key if necessary. It is when
 * the referenced section does not exist anymore or during a system update when
 * a special comment in the bootloader configuration tells us that we have to
 * update the "default" key. An empty "default" value is not changed, because
 * this means that no default is wanted.
 *
 * If we need to fix the "default" key we take the following steps:
 *
 *  - If we are fixing the configuration at the end of an update and the
 *    special key "former_default_image_flavor" exists, try to set the default
 *    to the first "linux.*" section with an image of this flavor (preferring
 *    "linux" entries over possibly older "linux-.*" entries).
 *
 *  - Otherwise go through a list of fallback kernel flavours and use the first
 *    "linux.*" section that contains a matching image (preferring "linux"
 *    entries over possibly older "linux-.*" entries).
 *
 *  - Otherwise, simply use the first section as the default section.
 */
global void FixGlobals () {
    string defaultv = globals["default"]:"";
    string first = "";

    y2milestone ("fixing default section");

    // nothing to do if default is empty
    if (defaultv == "")
	return;

    // does default section exist?
    boolean exists = false;
    foreach (map<string,any> s, sections, {
	string label = s["name"]:"";
	if (label == defaultv)
	    exists = true;
	if (first == "")
	    first = label;
    });

    if (exists &&
	(! Mode::update() || globals["former_default_image_flavor"]:nil == nil))
	return;

    // need to fix "default"
    boolean old_entry_found = false;
    string found_name = "";
    list<string> fallback_flavors = generic_fallback_flavors;

    if (Mode::update() && globals["former_default_image_flavor"]:nil != nil) {
	fallback_flavors = prepend(fallback_flavors, globals["former_default_image_flavor"]:"");

	// former_default_image_flavor is removed at the end of the update
	globals = remove (globals, "former_default_image_flavor");
    }

    y2milestone ("looking for image flavors %1", fallback_flavors);
    foreach (string flavor, fallback_flavors, {
	if (found_name != "")
	    return;

	foreach (map<string,any> s, sections, {
	    string label = s["name"]:"";
	    if (regexpmatch (s["original_name"]:"", "^linux(-.*)?$") ||
		regexpmatch (s["image"]:"", "^.*-" + flavor + "$")) {
		// found, if we have not yet found a match, or the previously
		// found one was for an "old" entry and we now found a "new"
		// one
		if (found_name == "" ||
		    (old_entry_found && regexpmatch (s["original_name"]:"", "^linux$"))) {
		    found_name = label;
		    if (regexpmatch (s["original_name"]:"", "^linux-.*$"))
			old_entry_found = true;
		    else
			old_entry_found = false;
		}
	    }
	});
    });

    if (found_name != "")
	globals["default"] = found_name;
    else
	globals["default"] = first;

    y2milestone ("setting new default section to: %1", globals["default"]:nil);
}


string getLargestSwapPartition () {
    map<string, integer> swap_sizes = getSwapPartitions ();
    list<string> swap_parts = (list<string>)
        maplist (string name, integer size, swap_sizes, ``(name));
    swap_parts = sort (string a, string b, swap_parts,
		       ``(swap_sizes[a]:0 > swap_sizes[b]:0)
		       );
    return swap_parts[0]:"";
}


/**
 * Fix section of lilo-like bootloader
 */
global void FixSections (void() create_sections) {
    list<string> parts = BootStorage::getPartitionList(`parts_old, getLoaderType(false));
    if (partitioning_last_change
	    != Storage::GetTargetChangeTime()
	&& BootCommon::files_edited)
    {
	displayFilesEditedPopup ();
	files_edited_warned = true;
	return;
    }

    // save old sections and propose new ones in global "sections"
    // (the updated list of old sections will become the new section list in
    // the end)
    list<map<string,any> > old_sect_list = sections;

    create_sections ();

    // new_sect is a map with elements containing: "type" -> section
    map<string,map<string,any> > new_sect = listmap (map<string,any> s,
	sections,
    {
	string label = s["name"]:"";
	string type = s["original_name"]:label;
	return $[type: s];
    });

    // remember a list with all the section "types" in the old section list
    // (needed later in this function to find newly created sections)
    list<string> old_section_types = maplist (map<string,any> s, old_sect_list,
    {
	return s["original_name"]:"";
    });

    // in the old list of sections:
    //	- only keep sections that the user created (no "__auto", or false) or
    //	  changed ("__changed") in the UI
    //  - replace unchanged sections with ones we proposed just now (if
    //    available)
    //  - also notify user when devices for a "changed by user" section are
    //    unavailable or would now be proposed differently (and mark section as
    //    "created by user")
    old_sect_list = maplist (map<string,any> s, old_sect_list, {
	string label = s["name"]:"";
	string type = s["original_name"]:label;
	if (! s["__auto"]:false)
	{
	    y2milestone ("Leaving section %1", label);
	    return s;
	}
	else if (! s["__changed"]:false)
	{
	    y2milestone ("Recreating section %1, new is %2",
		label, new_sect[type]:$[]);
	    return new_sect[type]:$[];
	}
	else
	{
	    // section was created by us, then changed by the user:
	    //	- keep it, except if no newly created section of same type can
	    //	  be found (which probably means we have a bug, because
	    //	  "__auto" says we created the old section as well)
	    //  - maybe notify user to check it (and then mark it as a "user
	    //    defined section")
	    y2milestone ("Warning on section %1", label);
	    boolean cont = true;
	    // if "non-standard" section name and a used device is not
	    // available anymore, notify user
	    if (type != "linux" && type != "failsafe"
		&& type != "memtest86")
	    {
		foreach (string n, s["__devs"]:[], {
		    if (! contains (parts, n))
		    {
			cont = false;
		    }
		});
	    }
	    // find section of same type in newly created sections;
	    // if not found (which should not happen, since according to the
	    // "__auto" key we created it) delete this section
	    map<string,any> new_this_section = new_sect[type]:$[];
	    if (new_this_section == $[]) {
		y2warning("Warning, could not find freshly proposed section" +
		    "corresponding to section %1, deleting it",
		    s["name"]:"");
		return $[];
	    }
	    // if the devices for this section and the freshly created one of
	    // the same type are different, notify user
	    list new_devs = toset(new_this_section["__devs"]:[]);
	    list old_devs = toset(s["__devs"]:[]);
	    if (size (new_devs) != size (old_devs))
		cont = false;
	    else
	    {
                foreach (any d, old_devs, ``{
		    if (! contains (new_devs, d))
			cont = false;
                });
	    }
	    // display info popup for this section;
	    // also, next time we come through this function, consider this
	    // section as a section created by the user (and leave it as it is)
	    if (! cont)
	    {
		s["__auto"] = false;
		displayDiskChangePopup (label);
	    }
	    return s;
	}
    });

    // in newly created sections, fix "resume" parameter in append line if
    // necessary
    y2milestone ("Checking for sections using the resume parameter");
    sections = maplist (map<string,any> s, BootCommon::sections, ``{
	string append = s["append"]:"";
	string resume = getKernelParamFromLine (append, "resume");
	if (resume != "" && resume != nil
	    && ! haskey (getSwapPartitions (), resume))
	// points to unexistent swap partition
	{
	    // bnc# 335526 - Installing memtest with lilo screws up installation
	    if (search(s["original_name"]:"", "memtest") == nil)
	    {
	    	append = setKernelParamToLine (append, "resume", BootStorage::Dev2MountByDev(getLargestSwapPartition ()));
	    	s["append"] = append;
	    }
	}
	return s;
    });

    // now add sections from newly created ones that were unknown before in the
    // old section list, if not already removed by the user (#170469)
    foreach (map<string,any> s, sections, {
	string label = s["name"]:"";
	string type = s["original_name"]:label;
	if (! contains (old_section_types, type) &&
	    ! contains (removed_sections, type))
	{
	    y2milestone ("Adding new section \"%1\": %2",
		label, s);
	    old_sect_list = add(old_sect_list, s);
	    return s;
	}
    });

    // Strange (I must have misread the code here):
    // if a newly created section uses one or more deleted devices, and a
    // section of that type does not exist anymore in the old section list, add
    // it to the old section list
    y2milestone ("Checking for sections needing some of %1", del_parts);
    list<string> to_remove = [];
    foreach (map<string,any> s, sections, {
	list<string> devs = s["__devs"]:[];
	string label = s["name"]:"";
	y2milestone ("Section %1 needs %2", label, devs);
	boolean to_add = false;
	foreach (string d, devs, {
	   if (contains (del_parts, d))
	    {
		to_add = true;
	    }
	});
	if (to_add)
	{
	    map old_sect = listmap (map<string,any> os, old_sect_list, {
		return $[label: os];
	    });

	    if (label != "" && ! haskey(old_sect, label))
	    {
		y2milestone ("Adding %1", s);
		to_remove = add (to_remove, label);
		old_sect_list = add (old_sect_list, s);
	    }
	}
    });

    // FIXME: BUG: looks like a bug to remove a list of labels from the list of
    // deleted devices
    del_parts = (list<string>)difflist (del_parts, to_remove);

    // cleanup: throw away empty sections
    old_sect_list = filter (map<string,any> s, old_sect_list, {
	return s != $[];
    });

    // save old, updated section list as proposed section list
    sections = old_sect_list;
}

/**
 * Update in section old devices to new kernel format
 * @param section section to modify
 */
void UpdateDeviceForSection(map<string,any> section){
  if (section["type"]:"" == "image" || section["type"]:"" == "xen")
  {
     //check if we update correct image and if update is needed
     if (BootCommon::UpdateDevice(section["root"]:"nonexisted")
        == BootStorage::RootPartitionDevice
        && section["root"]:""!=(BootCommon::UpdateDevice(section["root"]:"")))
     {
	y2milestone ("Updating device of section %1", section["name"]:"name");
        section["root"]= BootCommon::UpdateDevice(section["root"]:"");
        string resume
	    = BootCommon::getKernelParamFromLine (
                section["append"]:"", "resume");
	if (resume != "false")
	{
	  y2milestone ("Updating resume device of section %1", section["name"]:"name");
	  resume = BootCommon::UpdateDevice (resume);
	  section["append"] = BootCommon::setKernelParamToLine (
	    section["append"]:"", "resume", BootStorage::Dev2MountByDev(resume));
	}
        section["__modified"] = "1";
     }
  }
}

/**
 * Update sections of bootloader menu (removes obsolete thinks and
 * place for ugly hacks (but doesn't efect zypper dup))
 * modifies internal structures
 * label update is done in perl-Bootloader during updating kernel
 * Purpose is don't break anything, but expect that previous state is
 * working
 */
global void UpdateSections ()
{
  foreach (map<string,any> section, BootCommon::sections,{
    //remap device names from hd* to sd*
    UpdateDeviceForSection(section);
  });
}

/*global void UpdateSections (boolean replace,
    map<string,any>(string) create_linux_section)
{
    list<map<string,any> > out = BootCommon::sections;
    list<string> recreated = [];
    boolean linux_resume_added = false;

    map<string,any> default_sect = create_linux_section ("linux");
    string default_kernel = default_sect["image"]:"";
    string default_initrd = default_sect["initrd"]:"";
    string default_name = default_sect["name"]:"";

    list<string> sections_to_recreate = ["linux", "failsafe", "memtest86"];
    if (getLoaderType (false) == "grub")
    {
	sections_to_recreate = add (sections_to_recreate, "xen");
    }

    // if replace == true,  replace all sections that have a type in
    //                      sections_to_recreate with a newly created version
    // if replace == false, only adjust "append" line of "linux" section
    //
    // at the end of the loop, if one of the sections_to_recreate does not
    // exist, create it
    foreach (string t, sections_to_recreate, {
	map<string,any> m = create_linux_section (t);
	boolean f_changed = false;
	out = maplist (map<string,any> s, out, {
	    string label = s["name"]:"";
	    string sect_type = s["original_name"]:"";
	    if (sect_type == "")
		sect_type = label;
	    if (sect_type == t)
	    {
		f_changed = true;
		if (replace && m != $[])
		{
		    y2milestone ("Recreated section %1: %2", label, m);
		    recreated = add (recreated, label);
		    return m;
		}
		else if (t == "linux")
		{
		    string append = s["append"]:"";
		    string resume = BootCommon::getKernelParamFromLine (
			append, "resume");
		    if (! haskey (BootCommon::getSwapPartitions (), resume))
		    {
			append = setKernelParamToLine (append,
			    "resume", BootStorage::Dev2MountByDev(getLargestSwapPartition ()));
			s["append"] = append;
			linux_resume_added = true;
		    }
		    return s;
                }
		else
		    return s;
	    }
	    else
	    {
		return s;
	    }
	});
	// if we did NOT change or replace the old section (meaning: there was
	// none), but create_linux_section() gave us a new section, then
	// prepend or append the section created by create_linux_section()
        if (! f_changed && m != $[])
	{
	    y2milestone ("Recreated section: %1", m);
	    recreated = add (recreated, m["name"]:"");
            if (t == "linux")
		out = prepend (out, m);
            else
		out = add (out, m);
	}
    });
    y2milestone ("All recreated sections: %1", recreated);

    // for grub and replace-mode only:
    // convert sections that boot "other" installations (another Linux, Windows
    // etc.) to chainloader and configfile sections
    //
    // ! contains (sections_to_recreate, original_name)         AND
    // original_name == name (e.g. "openSUSE 10.3 (/dev/sdb1)") AND
    // haskey("image")						AND
    // haskey("initrd")
    //
    //  => if bootable, convert to "chainloader"
    //       otherwise, convert to "configfile" and hope for the best
    //
    if (getLoaderType (false) == "grub" && replace)
    {
	y2milestone ("Converting sections for other installation to use chainloader/configfile entries...");
	out = maplist (map<string,any> s, out, {
	    string label = s["name"]:"";
	    string sect_type = s["original_name"]:"";
	    y2milestone ("label: %1, sect_type: %2", label, sect_type);
	    if (!contains(sections_to_recreate, sect_type) &&
		 sect_type == label &&
		 haskey(s, "image") &&
		 haskey(s, "initrd"))
	     {
		string dev = (string) s["root"]:nil;

		// create new entry
		s = $[
		    "name":		label,
		    "original_name":	sect_type,
		    "root":		BootStorage::Dev2MountByDev(dev)
		];

		if (dev != nil && IsPartitionBootable(dev))
		{
		    //     bootable =>  chainloader
		    s["noverifyroot"] = "true";
		    s["chainloader"] = BootStorage::Dev2MountByDev(dev);
		    s["blockoffset"] = "1";
		    s["type"] = "other";
		    y2milestone ("Updating section \"%1\" booting other installation to use chainloader entry", s["name"]:"");
		}
		else
		{
		    // not bootable =>  configfile
		    // let's hope the config file can be found (should work at
		    // least for all SUSE installations using grub)
		    s["configfile"] = "/boot/grub/menu.lst";
		    s["type"] = "menu";
		    y2milestone ("Updating section \"%1\" booting other installation to use configfile entry", s["name"]:"");
		}
		return s;
	    }
	    else
		return s;
	});
    }


    // now adjust these keys in sections that need it:
    //  - kernel
    //  - initrd
    //  - name
    //  - device (e.g. for SATA: /dev/hda -> /dev/sda)
    //  - append
    out = maplist (map<string,any> s, out, {
	string label = s["name"]:"";
	string type = s["original_name"]:label;
	// FIXME: do we still need to remove strings from kernel or initrd names?
	//        if yes: add "image"
	//        if no: remove loop
	//        probably need to do this depending on the version of the old
	//        installation
	foreach (string key, ["image", "initrd"], {
	    string value = s[key]:"";
	    if (regexpmatch (value, "^.*\.shipped.*$"))
	    {
		value = regexpsub (value,
		    "^(.*)\.shipped(.*)$", "\\1\\2");
	    }
	    else if (regexpmatch (value, "^.*\.suse.*$"))
	    {
		value = regexpsub (value,
		    "^(.*)\.suse(.*)$", "\\1\\2");
	    }
	    // This was broken (was: s["key"] = value;)
	    s[key] = value;
	});
	// If we did not replace the sections anyway, adjust the section titles:
	// Does this section
	//  - use the default kernel of a linux section \ i.e. it uses the updated kernel
	//  - use the default initrd of a linux section /
	//  - contain the name of the first "linux" section read from disk in
	//    its name
	// then, update the section name
	if (!replace
	    && s["image"]:"" == default_kernel
	    && s["initrd"]:"" == default_initrd
	    && issubstring (s["name"]:"", read_default_section_name)
	    && read_default_section_name != ""
	    && read_default_section_name != default_name)
	{
	    // idea of this:
	    // orig_name == "linux":    "Linux"		    -> "<new name>"
	    // orig_name == "failsafe": "Failsafe -- Linux" -> "Failsafe -- <new name>"
	    y2milestone ("Updating label of section %1...", s["name"]:"");
	    string old_name = s["name"]:"";
	    integer i1 = search (old_name, read_default_section_name);
	    integer i2 = i1 + size (read_default_section_name);
	    s["name"] = substring (old_name, 0, i1) + default_name
		+ substring (old_name, i2);
	    y2milestone ("... to %1", s["name"]:"");
	}

	// Update device names in sections for certain section types, if the
	// section has not been recreated anyway
	// FIXME: never update device names twice!
	foreach (string key, ["root", "chainloader"], {
	    // FIXME: for some reason, this used to update "linux" and
	    // "failsafe" even if they have been recreated; but "other"
	    // sections were never updated -- since there are no calls to
	    // UpdateSections() with replace == false, ATM we disable updating
	    // "linux" and "failsafe", and add "other", seed
	    // BootCommon::update_section_types
	    if ((contains (update_section_types, type)
		    && ! contains (recreated, label))
		|| key == "chainloader")
	    {
		string device = (string) s[key]:nil;
		y2milestone ("Checking for update: device %1 of key %2, section %3", device, key, label);
		if (device != nil)
		{
		    y2milestone ("Updating root/other device of section %1",
			label);
		    device = BootCommon::UpdateDevice (device);
		    s[key] = device;
		}
	    }
	});
	if (type == "linux" && haskey (s, "append"))
	{
	    string option = s["append"]:"";
	    foreach (string o, ListAdditionalKernelParams (), {
		option = setKernelParamToLine (option, o, "false");
	    });
	    option = option + " " + GetAdditionalKernelParams ();
	    if (getKernelParamFromLine (option, "splash") == "false")
		option = setKernelParamToLine
		    (option, "splash", "silent");
	    s["append"] = option;
	}
	else if (haskey (s, "append")
	    && contains (BootCommon::update_section_types, type)
	   && ! contains (recreated, label))
	{
	    string option = s["append"]:"";
	    if (type != "linux" || ! linux_resume_added)
	    {
		string resume
		    = BootCommon::getKernelParamFromLine (
			option, "resume");
		if (resume != "false")
		{
		    y2milestone ("Updating resume device of section %1", label);
		    resume = BootCommon::UpdateDevice (resume);
		    option = BootCommon::setKernelParamToLine (
			option, "resume", BootStorage::Dev2MountByDev(resume));
		}
	    }
	    s["append"] = option;
	}
	return s;
    });
    sections = out;
}*/

/**
 * Update global options of bootloader
 * modifies internal sreuctures
 */
global void UpdateGlobals () {
    if (BootCommon::globals["timeout"]:"" == "")
        BootCommon::globals["timeout"] = "8";
    list<string> s1_devs
	= splitstring (BootCommon::globals["stage1_dev"]:"", ",");
    s1_devs = maplist (string d, s1_devs, {
	return UpdateDevice (d);
    });
    BootCommon::globals["stage1_dev"] = mergestring (s1_devs, ",");

    // bnc #380509 if autoyast profile includes gfxmenu == none
    // it will be deleted
    if (BootCommon::globals["gfxmenu"]:"" != "none")
        BootCommon::globals["gfxmenu"] = "/boot/message";

    // now that the label for the "linux" section is not "linux" anymore, but
    // some product dependent string that can change with an update ("SLES_10"
    // -> "SLES_10_SP1"), we need to update the label in the "default" line for
    // LILO and GRUB (although the latter only needs it to correctly transform
    // back to the section number)
    // FIXME: is this needed/wanted for ELILO as well?
    if ( contains (["lilo", "grub"], getLoaderType (false)))
	FixGlobals ();
}

/**
 * Update the device map according to changed device names
 * Read device map and store it in internal structures
 */
global void UpdateDeviceMap () {
    BootStorage::device_mapping = mapmap (string unix, string fw, BootStorage::device_mapping, {
	y2milestone ("Updating device in devmap entry %1 -> %2",
	    unix, fw);
	unix = BootCommon::UpdateDevice (unix);
	return $[ unix : fw ];
    });
    y2milestone ("Updated device map: %1", BootStorage::device_mapping);
}

/**
 * Filter sections, remove those pointing to unexistent image
 * @param path_prefix string prefix to be added to kernel path
 * @param relative_path_prefix prefix to be added to relative kernel
 *  paths (without leading slash)
 */
global void RemoveUnexistentSections (string path_prefix,
    string relative_path_prefix)
{
    string defaultv = globals["default"]:"";
    string first = nil;
    BootCommon::sections = filter (map<string,any> s, BootCommon::sections, {
	string label = s["name"]:"";

	string type = s["original_name"]:"";
        if (label == "")
	{
	    y2warning ("Removing section with empty title");
	    if (label == defaultv)
		defaultv = nil;
	    return false;
	}
	// FIXME the following check makes sense for all sections`
	if (! contains (["linux", "failsafe", "memtest86", "xen"], type))
	{
	    if (first == nil)
		first = label;
	    return true;
	}
	string kernel = s["image"]:"";
	if (kernel == "")
	{
	    if (first == nil)
		first = label;
	    return true;
	}
	if (substring (kernel, 0, 1) == "/")
	{
	    kernel = path_prefix + kernel;
	}
	else
	{
	    if (relative_path_prefix == "")
		return true;
	    kernel = relative_path_prefix + kernel;
	}
	if (SCR::Read (.target.size, kernel) == -1)
	{
	    y2warning ("Removing section %1 with unexistent kernel %2",
		label, kernel);
	    if (label == defaultv)
		defaultv = nil;
	    return false;
	}
	if (first == nil)
	    first = label;
	return true;
    });
    if (defaultv == nil)
	defaultv = first;
    globals["default"] = defaultv;
}

/**
  * Update append option if some parameters were changed
  */
global void UpdateAppend () {
    sections = maplist (map<string,any> s, sections, {
	string type = s["original_name"]:"";
        if ((type == "linux" || type == "global")
	    && s["append"]:nil != nil
	    && Stage::initial ())
	{
	    s["append"] = UpdateKernelParams (s["append"]:"");
	}
        return s;
    });
    if (haskey (globals, "append"))
    {
	globals["append"] = UpdateKernelParams (globals["append"]:"");
    }
}

/**
 * Update the gfxboot/message/... line if exists
 */
global void UpdateGfxMenu () {
    string message = globals["gfxmenu"]:"";
    if ((message != "") && (search(message, "(") == nil))
    {
	if (-1 == SCR::Read (.target.size, message))
	{
	    globals = remove (globals, "gfxmenu");
	}
    }

}



/**
 * Get the summary of disks order for the proposal
 * @return string a line for the summary (or nil if not intended to be shown)
 */
global string DiskOrderSummary () {
    list<string> order = BootStorage::DisksOrder ();
    string ret = nil;
    if (size (order) > 1)
    {
	ret = sformat (
	    // part of summary, %1 is a list of hard disks device names
	    _("Order of Hard Disks: %1"),
	    mergestring (order, ", "));
    }
    return ret;
}

/**
 * Convert XEN boot section to normal linux section
 * if intalling in domU (bnc #436899)
 *
 * @return boolean true if XEN section is converted to linux section
 */

boolean ConvertXENinDomU ()
{

	boolean ret = false;
	if (!Arch::is_xenU())
	{
		y2milestone("Don't convert XEN section - it is not running in domU");
		return ret;
	}

	// tmp sections
	list<map<string,any> > tmp_sections = [];

	foreach(map<string,any> sec, BootCommon::sections,
	{
		// bnc#604401 Xen para-virtualized guest boots native kernel
		// set XEN boot section to default
		if (sec["type"]:"" == "xen" || sec["type"]:"" == "image")
		{
			if (search(tolower(sec["image"]:""), "xen") != nil)
			{
				y2milestone("Set \"xen\" image: %1 to default", sec["name"]:"");
				BootCommon::globals["default"] = sec["name"]:"";
			}
		}
		if (sec["type"]:"" != "xen")
		{
			tmp_sections = add(tmp_sections, sec);

		} else {
			// convert XEN section to linux section
			y2milestone("Converting XEN section in domU: %1", sec);
			sec["type"] = "image";
			sec["original_name"] = "linux";
			if (haskey(sec,"xen"))
				sec = remove(sec, "xen");
			if (haskey(sec,"xen_append"))
				sec = remove(sec, "xen_append");
			if (haskey(sec,"lines_cache_id"))
				sec = remove(sec, "lines_cache_id");

			y2milestone("Converted XEN section in domU: %1", sec);

			tmp_sections = add(tmp_sections, sec);

			ret = true;
		}
		
	});
	
	BootCommon::sections = tmp_sections;
	return ret;
}



} // EOF

/*
 * Local variables:
 *     mode: ycp
 *     mode: font-lock
 *     mode: auto-fill
 *     indent-level: 4
 *     fill-column: 78
 * End:
 */

ACC SHELL 2018