ACC SHELL

Path : /usr/share/YaST2/modules/
File Upload :
Current File : //usr/share/YaST2/modules/BootCommon.ycp

/**
 * File:
 *      modules/BootCommon.ycp
 *
 * Module:
 *      Bootloader installation and configuration
 *
 * Summary:
 *      Data to be shared between common and bootloader-specific parts of
 *      bootloader configurator/installator, generic versions of bootloader
 *      specific functions
 *
 * Authors:
 *      Jiri Srain <jsrain@suse.cz>
 *      Joachim Plack <jplack@suse.de>
 *      Olaf Dabrunz <od@suse.de>
 *
 * $Id: BootCommon.ycp 62089 2010-06-02 15:08:27Z juhliarik $
 *
 */

{
module "BootCommon";

textdomain "bootloader";

import "Arch";
import "GfxMenu";
import "HTML";
import "Mode";
import "PackageSystem";
import "Storage";
import "String";
import "Pkg";
import "Popup";
import "Package";
import "PackagesProposal";
import "BootStorage";

import "Linuxrc";


// General bootloader settings

/**
 * map of global options and types for new perl-Bootloader interface
 */
global map<string,any> global_options = $[];

/**
 * map of section options and types for new perl-Bootloader interface
 */
global map<string,any> section_options = $[];

/**
 * boolean value indicate if "/" is on logical
 * and primary /boot doesn't exist
 */

global boolean boot_device_on_logical = false;

/**
 * map of global options and values
 */
global map<string,string> globals = $[];

/**
 * list of section
 */
global list<map<string,any> > sections = [];

/**
 * Saved change time from target map - proposal
 */

global integer cached_settings_base_data_change_time = nil;


/**
 * device to save loader stage 1 to
 * NOTE: this variable is being phased out. The boot_* keys in the globals map
 * are now used to remember the selected boot location. Thus, we now have a
 * list of selected loader devices. It can be generated from the information in
 * the boot_* keys and the global variables (Boot|Root|Extended)PartitionDevice
 * and mbrDisk by calling GetBootloaderDevices().
 */
 //FIXME: need remove to read only loader location from perl-Bootloader
global string loader_device = nil;

// proposal helping variables

/**
 * The kind of bootloader location that the user selected last time he went to
 * the dialog. Used as a hint next time a proposal is requested, so the
 * proposal can try to satisfy the user's previous preference.
 * NOTE: this variable is being phased out. The boot_* keys in the globals map
 * will be used to remember the last selected location.
 * Currently, valid values are: mbr, boot, root, floppy, mbr_md, none
 */
 //FIXME: need remove to read only loader location from perl-Bootloader
global string selected_location = nil;



/* These global variables and functions are needed in included files */

/**
  * Parameters of currently used bootloader
  */
global map<string, any> current_bootloader_attribs = $[];

/**
  * Parameters of all bootloaders
  */
global map<string,map<string, any> > bootloader_attribs = $[];

/**
  * Name of currently edited section
  */
global string current_section_name = nil;

/**
 * Index of current section, -1 for new created section
 */
global integer current_section_index = -1;

/**
  * Curtrently edited section -- tmp store
  */
global map<string,any> current_section = $[];


/**
  * Option types for different bootloaders
  */
global map<string,map<string,string> > opt_types = $[];

/**
  * device holding MBR for bootloader
  */
global string mbrDisk = "";

/**
  * was currently edited section changed (== true)
  */
global boolean one_section_changed = false;

/**
  * Backup original MBR before installing bootloader
  */
global boolean backup_mbr = false;

/**
  * Activate bootloader partition during installation?
  */
global boolean activate = false;


/** FATE #303548 - Grub: limit device.map to devices detected by BIOS
  * Ask user if he wants to edit again bootloader settings
  * It is used if device.map is limited and "boot" disk is out of range 
  * The range is the first 8 devices
  */
global boolean ask_user = false;

/**
  * Replace MBR with generic code after bootloader installation?
  */
global boolean repl_mbr = false;

/**
  * Kernel parameters at previous detection
  */
global string kernelCmdLine = "";

/**
  * were settings changed (== true)
  */
global boolean changed = false;


global map<string,any> installed_version = $[];
global map<string,any> update_version = $[];

global map<string,string> edited_files = $[];
// common variables

/**
  * type of bootloader to configure/being configured
  * shall be one of "lilo", "grub", "elilo", "ppc", "zipl"
  */
string loader_type = nil;

// sysconfig variables

// installation proposal help variables

/**
  * List of partitions deleted in primary proposal
  */
global list<string> del_parts = [];

// variables for storing data

// saving mode setting functions

/**
  * map of save mode settings
  */
global map write_settings = $[];

// summary dialog state

/**
  * Show verbose summary output
  */
global boolean verbose = false;

// ui help variables


string additional_failsafe_params = "";


// other variables

/**
  * Settings of other bootloaders used when switching bootloader
  */
global map other_bl = $[];

// bootloader installation variables

/**
  * Was the activate flag changed by user?
  */
global boolean activate_changed = false;
/**
  * Save everything, not only changed settings
  */
global boolean save_all = false;

// state variables

/**
  * was the propose function called (== true)
  */
global boolean was_proposed = false;
/**
  * Were module settings read (== true)
  */
global boolean was_read = false;
/**
  * Was bootloader location changed? (== true)
  */
global boolean location_changed = false;
/**
  * Were configuration files manually edited and chamged?
  */
global boolean files_edited = false;
/**
  * Has been files edited warning already shown?
  */
global boolean files_edited_warned = false;
/**
  * time of last change of partitioning
  */
global integer partitioning_last_change = 0;
/**
 * true if memtest was removed by user (manually) during the installation
 * proposal
 */
 //FIXME correct comment
global list<string> removed_sections = [];

/**
 * The name of the default section as it was read
 */
global string read_default_section_name = "";

/**
 * Types of sections that should be updated (changed device names)
 */
 // FIXME: see FIXME in lilolike.ycp:899
global list<string> update_section_types = [ "floppy", "other" ];
//    = [ "linux", "failsafe", "initrd", "floppy" ];

/**
 * List of all supported bootloaders
 */
global list<string> bootloaders = ["lilo", "grub", "elilo", 
				   "zipl", "ppc"];

/** FATE#305008: Failover boot configurations for md arrays with redundancy
 * if true enable redundancy for md array
 */
global boolean enable_md_array_redundancy = nil;

/** FATE#305557: Enable SELinux for 11.2
 *  if true create /selinux directory
 */
global boolean enable_selinux = false;


global define string getLoaderType (boolean recheck);
global define list<string> getBootloaders ();
global define list<string> Summary ();
global map<string,any> CreateLinuxSection (string title);
global string UpdateSerialConsole (string append, string console);
global string examineMBR (string device);
global boolean ThinkPadMBR (string disk);
global boolean VerifyMDArray ();
/*
 * help message and dscription definitions
 */
include "bootloader/routines/popups.ycp";
include "bootloader/routines/misc.ycp";
// FIXME: there are other archs than i386, this is not 'commmon'
include "bootloader/routines/lilolike.ycp";
include "bootloader/routines/lib_iface.ycp";



// interface to bootloader library




// FIXME 2x functions should not be finally here...
/**
 * Check whether XEN is selected for installation resp. selected
 * @return boolean true of XEN installed/selected
 */
global boolean XenPresent () {
    boolean ret = (! contains (removed_sections, "xen"))
	&& (Mode::test ()
	    || (Mode::normal () && Pkg::IsProvided ("xen")
		&& Pkg::IsProvided ("kernel-xen"))
	    || (! Mode::normal () && Pkg::IsSelected ("xen")
		&& Pkg::IsSelected ("kernel-xen")));

    y2milestone("ret: %1", ret);
    return ret;
}

/**
 * Function check if trusted grub is selected
 * or installed return true if is selected/installed
 * and add trusted_grub to globals
 * @return boolean true if trusted grub is selected/installed
 */

global boolean isTrustedGrub ()
{
    boolean ret = false;
    if (Mode::normal ())
    {
	if (Pkg::IsProvided ("trustedgrub") || Package::Installed("trustedgrub"))
	{
	    ret = true;
	    globals["trusted_grub"] = "true";
	}

    } else {
	if (Pkg::IsSelected ("trustedgrub"))
	{
	    ret = true;
	    globals["trusted_grub"] = "true";
	}
    }
    return ret;
}


/**
 * Get the size of memory for XEN's domain 0
 * @return the memory size in kB
 */
global integer Dom0MemorySize () {
    list<map> memory = (list<map>) SCR::Read(.probe.memory);

    y2milestone("memory: %1", memory);
    integer memory_size = 0;

    foreach(map info, memory, {
            // internal class, main memory
            if (info["class_id"]:0 == 257 && info["sub_class_id"]:0 == 2)
            {
                list<map> minf = info["resource", "phys_mem"]:[];
                foreach(map i, minf, {
                        memory_size = memory_size + i["range"]:0;
                    }
                );
            }
        }
    );
    // size in kB lowered 64 MB for XEN itself
    memory_size = memory_size / 1024 - (64 * 1024);
    y2milestone ("Memory size for XEN domain 0: %1", memory_size);
    return memory_size;
}


/**
 * Create section for linux kernel
 * @param title string the section name to create (untranslated)
 * @return a map describing the section
 */
global map<string,any> CreateLinuxSection (string title) {
    map<string,any> ret = $[
	"name" : translateSectionTitle (title),
	"original_name" : title,
	"type" : "image",
	"__auto" : true,
	"__changed" : true,
    ];

    if (title == "memtest86") {
	if (MemtestPresent ()) {
	    ret["image"] = "/boot/memtest.bin";
	    ret["__devs"] = [BootStorage::BootPartitionDevice];
	    return ret;
	}
	else {
	    return $[];
	}
    }

    string resume = BootArch::ResumeAvailable ()
	? getLargestSwapPartition ()
	: "";
    // try to use label or udev id for device name... FATE #302219
    if ((resume != "") && (resume != nil))
	resume = BootStorage::Dev2MountByDev(resume);


    // FIXME:
    // This only works in the installed system (problem with GetFinalKernel()),
    // in all other cases we use the symlinks.

    string kernel_fn = "";
    string initrd_fn = "";

    if (Mode::normal ()) {
	// Find out the file names of the "real" kernel and initrd files, with
	// version etc. pp. whatever (currently version-flavor) attached.
	// FIXME: also do this for xen and xenpae kernels as found below
	//
	// Note: originally, we wanted to find out the kernel file names during
	// installation proposal when the files are not yet installed. But not
	// all the necessary interfaces work at that time. Now, this variant is
	// only run in the "running system", and could as well look at the
	// installed files.
	//

	// First of all, we have to initialize the RPM database
	Pkg::TargetInit ( "/",	// installed system
		false );	// don't create a new RPM database

	// Then, get the file names in the "selected" kernel package,
	string kernel_package = Kernel::ComputePackage();
	list<string> files = Pkg::PkgGetFilelist( kernel_package, `installed );
	y2milestone ("kernel package %1 has these files: %2", kernel_package, files);

	// then find the first file that matches the arch-dependent kernel file
	// name prefix and the initrd filename prefix.
	string kernel_prefix = "/boot/" + Kernel::GetBinary ();
	string initrd_prefix = "/boot/initrd";

	list<string> files_filtered = filter (string file, files, {
	    return ( substring(file, 0, size(kernel_prefix)) == kernel_prefix );
	});


	// Sort the filtered files, thus the image strings by length, the big ones
	// at the beginning, the small ones at the end of the list.
	// So, the first element of the sorted list files_filtered is the image string
	// containing the version and flavor.
	files_filtered = sort ( string kbig, string ksmall, files_filtered, ``(
		size(kbig) > size(ksmall) ) );

	kernel_fn = (files_filtered[0]:"");

	files_filtered = filter (string file, files, {
	    return substring(file, 0, size(initrd_prefix)) == initrd_prefix ;
	});

	// Sort the filtered files, thus the initrd strings by length, the big ones
	// at the beginning, the small ones at the end of the list.
	// So, the first element of the sorted list files_filtered is the initrd string
	// containing the version and flavor.
	files_filtered = sort ( string ibig, string ismall, files_filtered, ``(
		size(ibig) > size(ismall) ) );

	initrd_fn = (files_filtered[0]:"");

	if ((kernel_fn == "") || (kernel_fn == nil))
	    kernel_fn = "/boot/vmlinuz";

	if ((initrd_fn == "") || (initrd_fn == nil))
	    initrd_fn = "/boot/initrd";

	// read commandline options for kernel
	list<string> cmd = (list<string>) SCR::Read(.proc.cmdline);

	any vga = nil; 

	// trying to find "vga" option
	foreach ( string key, cmd, 
	{
	    if (issubstring(key, "vga="))
		vga = key;
	    y2milestone("key: %1", key);
	});
	y2milestone("vga from command line: %1", vga);
	list <string> mode =[];

	// split vga=value
	if ((vga != nil) && (vga != ""))
	    mode = splitstring(tostring(vga), "=");

	string vgamode =nil;

	// take value if exist
	if ((size(mode)>1) && (mode[0]:"" == "vga"))
     	    vgamode = mode[1]:nil;

	// add value of vga into proposal (if exist)
	if ((vgamode != nil) && (vgamode != ""))
	{
	    ret["vgamode"] = vgamode;
	    y2milestone("vga mode: %1", vgamode);
	}

    } else {
	// the links are shown in the proposal; at the end of an installation,
	// in bootloader_finish, they will be resolved to the real filenames
	kernel_fn = "/boot/" + Kernel::GetBinary ();
	initrd_fn = "/boot/initrd";
    }
    // done: kernel_fn and initrd_fn are the results
    y2milestone ("kernel_fn: %1 initrd_fn: %2", kernel_fn, initrd_fn);

    ret = (map <string, any>)union (ret, $[
	"image" : kernel_fn,
	"initrd" : initrd_fn,
	// try to use label or udev id for device name... FATE #302219
	"root" : BootStorage::Dev2MountByDev(BootStorage::RootPartitionDevice),
	"append" : (title == "failsafe")
	    ? BootArch::FailsafeKernelParams ()
	    : BootArch::DefaultKernelParams (resume),
	"__devs" : [BootStorage::BootPartitionDevice, BootStorage::RootPartitionDevice],
    ]);
    if (BootArch::VgaAvailable () && Kernel::GetVgaType () != "")
    {

	// B#352020 kokso: - Graphical failsafe mode
	//if (title == "failsafe")
	//    ret["vga"] = "normal";
	//else
	ret["vgamode"] = Kernel::GetVgaType ();

	// B#352020 end

    }
    if (title == "xen")
    {
        ret["type"] = "xen";
	ret["xen_append"] = "";

	ret["xen"] = "/boot/xen.gz";
	ret["image"] = "/boot/" + Kernel::GetBinary () + "-xen";
	ret["initrd"] = "/boot/initrd-xen";

    }
    return ret;
}

// generic versions of bootloader-specific functions

/**
  * Export bootloader settings to a map
  * @return bootloader settings
  */
global define map Export () {
    
    map exp = $[
	"global": remapGlobals(globals),
	"sections" : remapSections(sections),
	"device_map" : BootStorage::remapDeviceMap(BootStorage::device_mapping),
    ];
    if ( ! ( loader_type == "grub" ) ) {
	exp["repl_mbr"] = repl_mbr;
	exp["activate"] = activate;
    }

    return exp;
}

/**
  * Import settings from a map
  * @param settings map of bootloader settings
  * @return boolean true on success
  */
global define boolean Import (map settings) {
    globals = settings["global"]:$[];
    sections = settings["sections"]:[];

    // FIXME: for grub, repl_mbr is replaced by globals["generic_mbr"]; same
    // for activate; remove the following when no bootloader uses these
    // variables any more
    if ( ! ( loader_type == "grub" ) ) {
	repl_mbr = settings["repl_mbr"]:false;
	activate = settings["activate"]:false;
    }
    BootStorage::device_mapping = settings["device_map"]:$[];
    return true;
}

/**
 * Read settings from disk
 * @param reread boolean true to force reread settings from system
 * @param avoid_reading_device_map do not read new device map from file, use
 * internal data
 * @return boolean true on success
 */
global boolean Read (boolean reread, boolean avoid_reading_device_map) {
    string bl = getLoaderType (false);
    if (bl == "none")
	return true;
    InitializeLibrary (reread, bl);
    if (reread)
    {
	BootCommon::ReadFiles (avoid_reading_device_map);
    }
    sections = GetSections ();
    globals = GetGlobal ();
    isTrustedGrub ();
    BootStorage::device_mapping = GetDeviceMap ();
    read_default_section_name = "";
    foreach (map<string,any> s, sections, {
	if (s["original_name"]:"" == "linux"
	    && read_default_section_name == "")
	{
	    read_default_section_name = s["name"]:"";
	}
    });

    // convert device names in device map to the kernel device names
    BootStorage::device_mapping = mapmap (string k , string v, BootStorage::device_mapping, {
	    // if we update from version 9 (SLES9), first convert old-style persistent
	    // device names to new-style persistent device names ("p1" -> "-part1")
	    // NOTE: this is idempotent; but other device name translation
	    // (e.g. libata migration) is not, so it will be done later
	    if (Mode::update () && installed_version["major"]:0 == 9) {
		k = Storage::SLES9PersistentDevNames(k);
		y2milestone( "devmap: dev name after SLES9 persistent dev name translation: %1", k);
	    }
	    return $[BootStorage::Dev2MountByDev(k) : v];
    });

    // convert custom boot device names in globals to the kernel device names
    // also, for legacy bootloaders like LILO that still pass device names,
    // convert the stage1_dev
    globals = mapmap (string k , string v, globals, {
	if ( k == "stage1_dev" || regexpmatch(k, "^boot_.*custom$" ) ) {
	    // see comments above
	    if (Mode::update () && installed_version["major"]:0 == 9) {
		v = Storage::SLES9PersistentDevNames(v);
		y2milestone( "globals: dev name after SLES9 persistent dev name translation: %1", v);
	    }
	    return $[k : BootStorage::Dev2MountByDev(v)];
	} else {
	    return $[k : v];
	}
    });


    // convert root device names in sections to kernel device names, if
    // possible
    sections = maplist (map<string,any> s, sections, {
	string rdev = s["root"]:"";
	// see comments above
	if (Mode::update () && installed_version["major"]:0 == 9) {
	    rdev = Storage::SLES9PersistentDevNames(rdev);
	    y2milestone( "sections: dev name after SLES9 persistent dev name translation: %1", rdev);
	}
	// bnc#533782 - after changing filesystem label system doesn't boot
	if (s["append"]:"" != "")
	    s["append"] = remapResume(s["append"]:"",true);
	s["root"] = BootStorage::Dev2MountByDev(rdev);
	return s;
    });
    return true;
}

/**
  * Reset bootloader settings
  * @param init boolean true to repropose also device map
  */
global define void Reset (boolean init) {
    sections = [];
    globals = $[];
    // DetectDisks ();
    repl_mbr = false;
    activate = false;
    activate_changed = false;
    removed_sections = [];
    was_proposed = false;
    if (init)
    {
	if (getLoaderType (false) == "grub")
	   BootStorage::ProposeDeviceMap ();
    }
}

/**
 * Propose bootloader settings
 */
global void Propose () {
    y2error ("No generic propose function available");
}

/** bnc# 346576 - Bootloader configuration doesn't work for serial output
 * Function check if settings need to remove gfxmenu
 *
 * @return boolean - true if gfxmenu needs to be removed
 */

boolean removeGFXMenu ()
{
    if ((globals["trusted_grub"]:"" == "true") && (haskey(globals, "gfxmenu")))
    {
	y2milestone("Remove gfxmenu -> selected trusted grub");
	return true;
    }

    if ((globals["serial"]:nil != "") && (globals["serial"]:nil != nil))
    {
	y2milestone("Remove gfxmenu -> defined serial console");
	return true;
    }

    if (globals["gfxmenu"]:"" == "none")
    {
	y2milestone("Remove gfxmenu -> disabled gfxmenu");
	return true;
    }
    return false;
}

/** bnc #390659 - autoyast bootloader config: empty settings are ignored (memtest)
 *
 * Check if sections inlcude section for memtest
 * if yes delete all unnecessary keys like initrd, vgamode, append...
 */
void checkMemtest ()
{
    list<map<string,any> > out = [];

    foreach(map<string,any> s, sections, 
    {
	if (search(s["image"]:"","memtest") != nil)
	{
	    map<string,any> tmp_s = $[];
	    tmp_s["image"] = s["image"]:"";
	    tmp_s["original_name"] = s["original_name"]:"";
	    tmp_s["name"] = s["name"]:"";
	    tmp_s["__changed"] = s["__changed"]:false;
	    tmp_s["__auto"] = s["__auto"]:false;
	    tmp_s["type"] = s["type"]:"";
	    y2milestone("Updating memtest section from: %1 to: %2", s, tmp_s);
	    out = add(out, tmp_s);
	} else {
	    out = add(out, s);
	}
    });

    sections = out;
}
/**
 * Save all bootloader configuration files to the cache of the PlugLib
 * PlugLib must be initialized properly !!!
 * @param clean boolean true if settings should be cleaned up (checking their
 *  correctness, supposing all files are on the disk)
 * @param init boolean true to init the library
 * @param flush boolean true to flush settings to the disk
 * @return boolean true if success
 */
global boolean Save (boolean clean, boolean init, boolean flush) {
    if (clean)
    {
	BootCommon::RemoveUnexistentSections ("", "");
	BootCommon::UpdateAppend ();
	BootCommon::UpdateGfxMenu ();
    }

    boolean ret = true;

    string bl = getLoaderType (false);

    InitializeLibrary (init, bl);

    if (bl == "none")
	return true;

    if (removeGFXMenu())
	globals = remove(globals, "gfxmenu");

    // bnc#589433 -  Install grub into root (/) partition gives error
    if ((globals["boot_custom"]:nil == "") && (haskey(globals, "boot_custom")))
	globals = remove(globals, "boot_custom");

    // FIXME: give mountby information to perl-Bootloader (or define some
    // better interface), so that perl-Bootloader can use mountby device names
    // for these devices instead. Tracked in bug #248162.

    // convert XEN section to linux section id running in domU
    // bnc #436899
    // bnc #604401 Xen para-virtualized guest boots native kernel
    // I have to call it before updating of BootCommon::globals to my_globals
    // bnc #604401c14
    ConvertXENinDomU ();

    // convert custom boot device names in globals to the device names
    // indicated by "mountby"
    // also, for legacy bootloaders like LILO that still pass device names,
    // convert the stage1_dev
    map<string,string> my_globals = mapmap (string k , string v, globals, {
	if ((k == "stage1_dev") || (regexpmatch(k, "^boot_.*custom$" )))
	    return $[k : BootStorage::Dev2MountByDev(v)];
	else
	    return $[k : v];
    });

    // convert device names in device map to the device names indicated by
    // "mountby"

    y2milestone ("device map before mapping %1", BootStorage::device_mapping);
    map<string,string> my_device_mapping =
	mapmap (string k , string v, BootStorage::device_mapping, {
	    return $[BootStorage::Dev2MountByDev(k) : v];
    });
    y2milestone ("device map after mapping %1", my_device_mapping);

    if (VerifyMDArray())
    {
	if ((enable_md_array_redundancy == false) && (haskey(my_globals, "boot_md_mbr")))
	    my_globals = remove(my_globals, "boot_md_mbr");
	if ((enable_md_array_redundancy) && (!haskey(my_globals, "boot_md_mbr")))
	    my_globals["boot_md_mbr"] = BootStorage::addMDSettingsToGlobals();

    } else {
	if (haskey(globals, "boot_md_mbr"))
	    my_globals = remove(my_globals, "boot_md_mbr");
    }
    // add check if there is memtest and delete from memtest section
    // keys like append, initrd etc...
    checkMemtest ();
    ret = ret && DefineMultipath(BootStorage::multipath_mapping);
    ret = ret && SetDeviceMap (my_device_mapping);
    ret = ret && SetSections (sections);
    ret = ret && SetGlobal (my_globals);
    if (flush)
    {
	ret = ret && CommitSettings ();
    }

    // write settings to /etc/sysconfig/bootloader
    WriteToSysconf(false);	

    return ret;
}
    /**
      * Display bootloader summary
      * @return a list of summary lines
      */
    global define list<string> Summary () {
	if (getLoaderType (false) == "none")
	{
	    return [HTML::Colorize (
		getLoaderName (getLoaderType (false), `summary),
		"red") ];
	}
	map targetMap = Storage::GetTargetMap ();
	map boot_target = targetMap[loader_device]:$[];
	string target_name = "";
	if (boot_target == $[])
	{
	    target_name = loader_device;
	    if (target_name == "mbr_md")
	    {
		list<string> mbrs = maplist (string d, integer id,
		    Md2Partitions (BootStorage::BootPartitionDevice),
		{
		    map p_dev = Storage::GetDiskPartition (d);
		    return p_dev["disk"]:"";
		});
		// summary part, %1 is a list of device names
		target_name = sformat (_("Master boot records of disks %1"),
		    mergestring (mbrs, ", "));
	    }
	}
	else
	{
	    target_name = boot_target["name"]:"disk";
	}
	target_name = AddMbrToDescription (target_name, loader_device);

	list<string> result = [];
	// summary text, %1 is bootloader name (eg. LILO)
	result = add (result, sformat (_("Boot Loader Type: %1"),
		getLoaderName (getLoaderType (false), `summary)));
	// summary text, location is location description (eg. /dev/hda)
	result = add (result, sformat (_("Location: %1"), target_name));
	list<string> sects = [];
	foreach (map<string,any> s, sections, {
	    string title = s["name"]:"";
		// section name "suffix" for default section
	    string def = title == globals["default"]:"" ? _(" (default)") : "";
	    sects = add (sects, String::EscapeTags (sformat ("+ %1%2", title, def)));
	});
	// summary text. %1 is list of bootloader sections
	result = add (result, sformat (_("Sections:<br>%1"),
	    mergestring (sects, "<br>")));
	if (loader_device == "/dev/null")
	    // summary text
	    result = add (result, _("Do not install boot loader; just create 
configuration files"));

	return result;
    }

    /**
      * Update read settings to new version of configuration files
      */
    global define void Update () {
	y2debug ("No generic update function available");
    }

/**
 * Write bootloader settings to disk
 * @return boolean true on success
 */
global define boolean Write () {
    y2error ("No generic write function available");
    return false;
}


// end of generic versions of bootloader-specific functions
//-----------------------------------------------------------------------------
// common functions start

// bootloader type handling functions

/**
  * Set attributes of specified bootloader to variable containing
  * attributes of currently used bootloader, call its initializer
  * @param loader_type string loader type to initialize
  */
global define void setCurrentLoaderAttribs (string loader_type) {
    y2milestone ("Setting attributes for bootloader %1", loader_type);
    // testsuite hack
    if (Mode::test ())
        return;
    if (loader_type == nil)
    {
	y2error ("Setting loader type to nil, this is wrong");
	return;
    }

    // FIXME: this should be blInitializer in switcher.ycp for code cleanness
    // and understandability
    if (bootloader_attribs[loader_type, "initializer"]:nil != nil)
    {
	y2milestone ("Running bootloader initializer");
	void () toEval = (void ()) (bootloader_attribs[loader_type, "initializer"]:nil);
	toEval ();
	y2milestone ("Initializer finished");
    }
    else
    {
	y2error ("No initializer found for >>%1<<", loader_type);
	current_bootloader_attribs = $[];
    }

    current_bootloader_attribs = (map<string, any>) union (
	current_bootloader_attribs,
	(map<string, any>) eval (bootloader_attribs[loader_type]:$[])
    );
}

/**
 * Check whether loader with specified name is supported
 * @param loader string name of loader to check
 * @return string the loader name if supported, "none" otherwise
 */
string SupportedLoader (string loader) {
    if (contains (bootloaders, loader))
	return loader;
    return "none";
}

/**
  * Get currently used bootloader, detect if not set yet
  * @param recheck boolean force checking bootloader
  * @return string botloader type
  */
global define string getLoaderType (boolean recheck) {
    if ((! recheck) && (loader_type != nil))
        return loader_type;
    // read bootloader to use from disk
    if (Mode::update () || Mode::normal () || Mode::repair ())
    {
	loader_type = (string)SCR::Read (.sysconfig.bootloader.LOADER_TYPE);
	if (loader_type != nil && loader_type != "")
	{
	    if (loader_type == "s390")
	        loader_type = "zipl";
	    if (loader_type == "lilo" && Arch::ppc ())
	        loader_type = "ppc";
	    y2milestone ("Sysconfig bootloader is %1, using", loader_type);
	    loader_type = SupportedLoader (loader_type);
	    y2milestone ("Sysconfig bootloader is %1, using", loader_type);
	    setCurrentLoaderAttribs (loader_type);
	    return loader_type;
	}
	if (Mode::update ())
	{
	  // FIXME: this is extremely broken, no arch specifica here !!
	    if (Arch::i386 ())
	    {
		// no sysconfig variable -> old version installed -> use LILO
		loader_type = "lilo";
		loader_type = SupportedLoader (loader_type);
		setCurrentLoaderAttribs (loader_type);
		return loader_type;
	    }
	}
    }
    // detect bootloader
    loader_type = (string)SCR::Read (.probe.boot_arch);
    if (loader_type == "s390")
      loader_type = "zipl";
    y2milestone ("Bootloader detection returned %1", loader_type);
    // lslezak@: Arch::is_xenU() returns true only in PV guest
    if (Arch::is_uml () || Arch::is_xenU())
    {
	// y2milestone ("Not installing any bootloader for UML/Xen PV");
	// loader_type = "none";
	// bnc #380982 - pygrub cannot boot kernel
	// added installation of bootloader
	y2milestone ("It is XEN domU and the bootloader should be installed");

    }
    if ((Arch::i386() || Arch::x86_64()) && Linuxrc::InstallInf("EFI") == "1")
    {
	loader_type = "elilo";
    }

    loader_type = SupportedLoader (loader_type);
    y2milestone ("Detected bootloader %1", loader_type);
    setCurrentLoaderAttribs (loader_type);
    return loader_type;
}


/**
  * set type of bootloader
  * @param bootloader string type of bootloader
  */
global define void setLoaderType (string bootloader) {
    if (bootloader == nil)
    {
        y2milestone ("Resetting the loader type");
        loader_type = nil;
    }
    y2milestone ("Setting bootloader to >>%1<<", bootloader);
    if (bootloader != nil
        && contains(bootloaders, bootloader)
        && ! Mode::test ())
    {
	// added kexec-tools fate# 303395
	// if kexec option is equal 0 or running live installation 
	// doesn't install kexec-tools

	list<string> bootloader_packages =[];
	if ((! Mode::live_installation()) 
	   && (Linuxrc::InstallInf ("kexec_reboot") != "0"))
	{
        	bootloader_packages = bootloader_attribs[bootloader, "kexec-tools", "required_packages"]:[];
		bootloader_packages = add(bootloader_packages, "kexec-tools");
	} else {
        	bootloader_packages = bootloader_attribs[bootloader, "required_packages"]:[];
	}

	
	//FIXME: solve problem with installing bootloader
	// it should be split and designed for each module (bootloader)
	if ((bootloader == "grub") || (bootloader == "lilo") || (bootloader == "elilo"))
	   bootloader_packages = add(bootloader_packages, bootloader);
	else if (bootloader == "ppc")
	   bootloader_packages = add(bootloader_packages, "lilo");
	else if (bootloader == "zipl")
	   bootloader_packages = add(bootloader_packages, "s390-tools");

	if (globals["trusted_grub"]:"" == "true")
	{
		bootloader_packages = bootloader_attribs[bootloader, "trustedgrub", "required_packages"]:[];
		if (contains(bootloader_packages, "grub"))
			bootloader_packages = filter(string key, bootloader_packages, {return (key !="grub"); });

		bootloader_packages = add(bootloader_packages, "trustedgrub");
	}

        // don't configure package manager during autoinstallation preparing
        if (Mode::normal () && (! (Mode::config () || Mode::repair ())))
        {
	    y2milestone("Install packages :%1", bootloader_packages);
	    PackageSystem::InstallAll (bootloader_packages);
        }
        else if (Stage::initial () )
        {
	    boolean pkg_added = false;
	    foreach (string p, bootloader_packages, {
	       y2milestone("Select bootloader package: %1", p);
	       PackagesProposal::AddResolvables ("yast2-bootloader", `package, [p]);
	       pkg_added = true;
		
	    });
        }
    }
    else if (! Mode::test ())
    {
        y2error ("Unknown bootloader");
    }
    loader_type = bootloader;
    if (loader_type != nil)
        setCurrentLoaderAttribs (loader_type);
    y2milestone ("Loader type set");
}

/**
  * List bootloaders available for configured architecture
  * @return a list of bootloaders
  */
global define list<string> getBootloaders () {
    if (Mode::config ())
    {
        return ["grub", "lilo", "elilo", "zipl", "ppc", "default", "none"];
    }
    list<string> ret = [
	getLoaderType (false),
	(string)SCR::Read (.probe.boot_arch)
    ];
    if (Arch::i386 () || Arch::x86_64 ())
    {
        ret = (list<string>)merge (ret, ["lilo", "grub"]);
	if (Arch::x86_64 ())
	    ret = (list<string>)merge (ret, ["elilo"]);
    }
    // in order not to display it twice when "none" is selected
    ret = filter (string l, ret, {
      return l != "none";
    });
    ret = toset (ret);
    ret = add (ret, "none");
    return ret;
}

/**
  * Search for section passed
  * @return integer index number
  */

global define integer Section2Index (string section_name)
{
    integer index = -1;
    integer sectnum = -1;

    foreach (map<string,any> s, BootCommon::sections, {
            index = index + 1;
	if (s["name"]:"" == section_name)
	    sectnum = index;
    });

    y2milestone ("ret: %1", sectnum);
    return sectnum;
}

/** FATE#305008: Failover boot configurations for md arrays with redundancy
 * Verify if proposal includes md array with 2 diferent disks
 *
 * @return boolean true if there is md array based on 2 disks
 */
global boolean VerifyMDArray ()
{
    boolean ret = false;
    if (haskey(globals, "boot_md_mbr"))
    {
	string md_array =  globals["boot_md_mbr"]:"";
	list<string> disks = splitstring(md_array,",");
	disks = filter(string v, disks, {return (v !="");});
	if (size(disks) == 2)
	{
	    y2milestone("boot_md_mbr includes 2 disks: %1", disks);
	    ret = true;
	}

    }
    return ret;
}

}

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

ACC SHELL 2018