ACC SHELL
/**
* File:
* modules/BootGRUB.ycp
*
* Module:
* Bootloader installation and configuration
*
* Summary:
* Module containing specific functions for GRUB configuration
* and installation
*
* Authors:
* Jiri Srain <jsrain@suse.cz>
* Joachim Plack <jplack@suse.de>
* Olaf Dabrunz <od@suse.de>
* Philipp Thomas <pth@suse.de>
*
* $Id: BootGRUB.ycp 58279 2009-08-04 16:01:51Z juhliarik $
*
*/
{
module "BootGRUB";
textdomain "bootloader";
import "Arch";
import "BootCommon";
import "BootStorage";
import "Kernel";
import "Mode";
import "Stage";
import "Storage";
import "StorageDevices";
import "Pkg";
import "HTML";
// private variables
/**
* Shall proposal merge menus?
*/
global symbol merge_level = `main;
/**
* The variable indicate if client bootloader_preupdate
* successful update device map
* If success true else false
*/
global boolean update_device_map_done = false;
// variables for temporary data
/**
* Disks order for ordering widget purproses
*/
global list<string> disks_order = nil;
// includes
include "bootloader/grub/misc.ycp";
include "bootloader/routines/popups.ycp";
include "bootloader/grub/dialogs.ycp";
// end of mandatory functions
//----------------------------------------------------------------------------
// wrapper function to adjust to new grub name sceme
map<string,any> CreateLinuxSection (string title) {
return BootCommon::CreateLinuxSection (title);
}
/**
* Check for additional kernels which could go to the proposed settings
* @return a list of kernels to propose
*/
global list<map<string,string> > CheckAdditionalKernels () {
list<string> files = (list<string>)SCR::Read (.target.dir, "/boot");
string binary = Kernel::GetBinary ();
list<string> kernels = filter (string k, files, {
return substring (k, 0, size (binary)) == binary;
});
kernels = filter (string k, kernels, {
return k != ""
&& k != binary
&& regexpmatch (k, sformat ("^%1-.+$", binary));
});
if (contains (kernels, binary))
{
string defaultv = (string)SCR::Read (.target.symlink,
sformat ("/boot/%1", binary));
defaultv = ""; // FIXME remove this line
kernels = filter (string k, kernels, {
return k != defaultv;
});
}
list<map<string,string> > ret = maplist (string k, kernels, {
string version = regexpsub (k, sformat ("^%1-(.+)$", binary), "\\1");
map<string,string> info = $[
"version" : "Kernel-" + version,
"image" : sformat ("/boot/%1", k)
];
if (contains (files, sformat ("initrd-%1", version)))
{
info["initrd"] = sformat ("/boot/initrd-%1", version);
}
return info;
});
y2milestone ("Additional sections to propose: %1", ret);
return ret;
}
/**
* Propose sections to bootloader menu
* modifies internal structures
*/
//FIXME really needs refactor, it is really huge function
global define void CreateSections () {
y2debug ("Creating GRUB sections from scratch");
list<map<string,any> > out = [];
out = add (out, CreateLinuxSection ("linux"));
if (BootCommon::XenPresent ())
out = add (out, CreateLinuxSection ("xen"));
list<string> others_ignore = [];
string linux_fallback_text = "Linux other";
integer fallback_num = 1;
// get a list of bootable (= "aa55" at the end of block 0) primary
// partitions classified by partition type:
//
// "/dev/sda3 Linux other 1"
// "/dev/sda4 Linux other 2"
// "/dev/sda2 dos 1"
// "/dev/sda3 windows 1"
// "/dev/sda3 OS/2 Boot Manager 1"
// "/dev/sda1 Vendor diagnostic"
// ...
list<string> others = (list<string>)Storage::GetForeignPrimary();
y2milestone ("Other primaries: %1", others);
// get list of all Linux Partitions on all real disks
list<map> other_l = (list<map>)Storage::GetOtherLinuxPartitions();
y2milestone ("Other linux parts: %1", other_l);
list<string> destroyed_partitions
= BootStorage::getPartitionList (`destroyed, "grub");
string tmpdir = (string)SCR::Read (.target.tmpdir) + "/bldetect/";
// load additional modules for filesystems used by other linux partitions,
// if needed
if (merge_level != `none && other_l != nil && size (other_l) > 0
&& 0 == SCR::Execute (.target.bash,
sformat ("test -d %1 || /bin/mkdir %1", tmpdir))
)
{
y2milestone ("Detecting other Linux parts");
list filesystems = maplist (map p, other_l,
``(p["used_fs"]:(any)""));
filesystems = toset (filter (any f, filesystems, ``(f != "")));
filesystems = filter (any f, filesystems, ``(f != `ext2));
y2debug ("Have to modprobe %1", filesystems);
foreach (any f, filesystems, {
map fsmods = $[
`ext2 : "",
`ext3 : "ext3",
`reiser : "reiserfs",
`xfs : "xfs",
`jfs : "jfs"
];
string modname = fsmods[f]:"";
y2debug ("Module name is %1", modname);
if (modname != "")
{
integer r = (integer)SCR::Execute (.target.bash,
sformat ("/sbin/modprobe %1", modname));
y2debug ("result of loading %1 is %2", modname, r);
}
});
foreach (map o, other_l, {
// Here is the general logic:
//
// not mountable => no entry
// mountable bootable has_menu_lst name_found => chainloader ("openSUSE 10.2 (/dev/sda2)")
// mountable bootable not name_found => chainloader ("Linux other 1 (/dev/sda2)")
// mountable not bootable has_menu_lst name_found => configfile ("openSUSE 10.2 (/dev/sda2)")
// mountable not bootable has_menu_lst not name_found => configfile ("Linux other 1 (/dev/sda2)")
// mountable not bootable not has_menu_lst => no entry
boolean bootable = false;
boolean has_menu_lst = false;
string menu_lst = "";
boolean name_found = false;
string new_sect_name_prefix = "";
BootCommon::InitializeLibrary (false, "grub");
string dev = o["device"]:"";
if (dev != "" && 0 == SCR::Execute (.target.bash,
sformat ("/bin/mount %1 %2", dev, tmpdir))
)
{
// mountable: true
y2milestone ("Mounted %1", dev);
list<string> filenames = [];
foreach (string fn, [
// not needed since there is a symlink in /boot directory
// named boot pointing to the /boot directory
// this caused bug #23346 - the file was found twice
// tmpdir + "grub/menu.lst",
tmpdir + "boot/grub/menu.lst"
], {
if (-1 != (integer)SCR::Read (.target.size, fn))
filenames = add (filenames, fn);
});
y2milestone ("Found files %1", filenames);
// bootable: ?
bootable = BootCommon::IsPartitionBootable(dev);
// has_menu_lst: ?
// name_found: ?
//
// look for a readable menu.lst and try to extract a section
// name from a qualifying section
foreach (string f, filenames, {
if (name_found)
return;
y2debug ("Checking file %1", f);
string fc = (string)SCR::Read (.target.string, f);
if (fc == nil)
return;
has_menu_lst = true;
menu_lst = substring(f, size(tmpdir));
if ( substring(menu_lst, 0, 1) != "/" )
menu_lst = "/" + menu_lst;
string dm = (string)SCR::Read (.target.string,
regexpsub (f, "(.*)menu.lst$",
"\\1device.map"));
y2debug ("Device map file name: %1",
regexpsub (f, "(.*)menu.lst$",
"\\1device.map"));
y2debug ("Device map contents: %1", dm);
// pass the menu.lst and device.map to the parser
map<string,string> files = $[
"/boot/grub/menu.lst" : fc,
];
if (dm == nil)
return;
files["/boot/grub/device.map"] = dm;
BootCommon::InitializeLibrary (false, "grub");
BootCommon::SetFilesContents (files);
list<map<string,any> > sects
= BootCommon::GetSections ();
y2debug ("Found sections %1", sects);
// bnc #448010 grub doesn't add another installed Linux in installation
map<string,string> globs = BootCommon::GetGlobal ();
string default_sec_name = globs["default"]:"";
// look only for "default" == "initial" entries, not all entries
sects = filter (map<string,any> s, sects, {
return ((s["initial"]:nil != nil) || (s["name"]:nil == default_sec_name));
});
// now find the name of the first non-broken "initial" section and get its name
foreach (map<string,any> s, sects, ``{
if (name_found)
return;
boolean _use = true;
// this is a heuristic: if the mounted partition or the
// root partition referenced in the section is "new,
// deleted or formatted", then do not use the section
list<string> devs = [dev];
string _d = s["root"]:"";
if (_d != nil && _d != "")
devs = add (devs, _d);
devs = (list<string>)filter (string d, devs,
``(d != ""
&& d != nil
&& d != "/dev/null" && d != "false"));
devs = toset (devs);
devs = maplist (string d, devs, {
return BootCommon::UpdateDevice (d);
});
foreach (string _d, devs, {
if (contains (destroyed_partitions, _d))
_use = false;
});
if (_use)
{
// no need to translate here...
new_sect_name_prefix = s["name"]:"";
name_found = true;
}
});
});
// now evaluate the collected information:
//
// mountable bootable has_menu_lst name_found => chainloader ("openSUSE 10.2 (/dev/sda2)")
// mountable bootable not name_found => chainloader ("Linux other 1 (/dev/sda2)")
// mountable not bootable has_menu_lst name_found => configfile ("openSUSE 10.2 (/dev/sda2)")
// mountable not bootable has_menu_lst not name_found => configfile ("Linux other 1 (/dev/sda2)")
// mountable not bootable not has_menu_lst => no entry FIXME: should this be handled with a chainloader entry anyway, in case it becomes bootable later?
if (bootable || has_menu_lst)
{
// set up the new section entry
map<string,any> new_sect = $[
"root" : BootStorage::Dev2MountByDev(dev),
"__changed" : true,
"__auto" : true,
"__devs" : [dev]
];
if (bootable)
{
// mountable bootable => chainloader (label: to be decided)
new_sect["noverifyroot"] = "true";
new_sect["chainloader"] = BootStorage::Dev2MountByDev(dev);
new_sect["blockoffset"] = "1";
new_sect["type"] = "other";
}
else
{
// mountable not bootable => configfile (label: to be decided)
//new_sect["configfile"] = sformat("(%1)%2", dev, menu_lst);
new_sect["configfile"] = menu_lst;
new_sect["type"] = "menu";
}
if (name_found)
{
// mountable name_found => ("openSUSE 10.2 (/dev/sda2)")
new_sect["name"] = sformat(" %1 (%2)", new_sect_name_prefix, dev);
}
else
{
// mountable not name_found => ("Linux other 1 (/dev/sda2)")
new_sect["name"] = sformat("%1 %2 (%3)", linux_fallback_text, fallback_num, dev);
fallback_num = fallback_num+1;
}
new_sect["original_name"] = new_sect["name"]:"";
out = add (out, new_sect);
// ignore this partition when going through the list of
// "other", non-linux partitions in the next loop below
others_ignore = add (others_ignore, dev);
}
SCR::Execute (.target.bash, sformat (
"/bin/umount %1", dev));
}
});
SCR::Execute (.target.bash, sformat ("/bin/rmdir %1", tmpdir));
}
// Go through a list of "bootable" (aa55) primary partitions that may be
// "foreign", check that it is not one of our current boot partitions and
// if this is not a special Thinkpad or "Vendor diagnostics" partition, add
// a chainloader entry for it.
if (others != nil && (size (others) > 0)) {
foreach (string o, others, {
list parts = splitstring (o, " ");
while (parts[0]:" " == "")
parts = remove (parts, 0);
string dev = parts[0]:"";
y2milestone ("Checking other partition %1", dev);
if (! contains (others_ignore, dev)) {
parts = remove (parts, 0);
string label = mergestring ((list<string>)parts, " ");
// don't add rewritten location (#19990)
// do not check for a bootable partition boot record: partition
// may become bootable later
if (dev != "" && label != ""
&& !contains(BootCommon::GetBootloaderDevices(), dev)
&& (
BootCommon::AddFirmwareToBootloader (
BootCommon::mbrDisk)
|| (label != "Vendor diagnostics" // <- should probably be '&&' => not Thinkpad MBR AND not "Vendor diagnostics" partition type
&& label != "Vendor diagnostic")
)
)
{
map<string,any> m = $[
"name" : BootCommon::translateSectionTitle (label),
"type" : "other",
"original_name" : label,
"chainloader" : BootStorage::Dev2MountByDev(dev),
"__changed" : true,
"__auto" : true,
"__devs" : [dev],
];
out = add (out, m);
}
}
});
}
if (grub_InstallingToFloppy ())
{
out = add (out, $[
"name" : BootCommon::translateSectionTitle("hard disk"),
"original_name" : "hard_disk",
"type" : "other",
"chainloader" : BootStorage::Dev2MountByDev(BootCommon::mbrDisk),
"__changed" : true,
"__auto" : true,
"__devs" : [],
]);
}
else if (StorageDevices::FloppyPresent())
{
out = add (out, $[
"name" : BootCommon::translateSectionTitle("floppy"),
"original_name" : "floppy",
"type" : "other",
"chainloader" : "/dev/fd0",
"__changed" : true,
"__auto" : true,
"__devs" : [],
]);
}
out = add (out, CreateLinuxSection ("failsafe"));
out = add (out, CreateLinuxSection ("memtest86"));
if (Mode::normal ())
{
foreach (map<string,string> additional,
CheckAdditionalKernels (),
{
string type = additional["version"]:"";
type = sformat ("%1", type);
map<string,any> s = CreateLinuxSection (type);
s["image"] = additional["image"]:"";
if (haskey (additional, "initrd"))
s["initrd"] = additional["initrd"]:"";
s["original_name"] = "linux";
out = add (out, s);
});
}
out = filter (map<string,any> s, out, {return s != $[] && s != nil;});
BootCommon::sections = out;
}
/**
* Propose global options of bootloader
*/
global map<string,string> StandardGlobals () {
return $[
"activate": "true",
"default" : BootCommon::sections[0, "name"]:"",
"timeout" : "8",
"gfxmenu" : "/boot/message",
];
}
// general functions
/**
* 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) {
BootCommon::InitializeLibrary (reread, "grub");
if (reread) {
BootCommon::ReadFiles (avoid_reading_device_map);
}
grub_DetectDisks ();
boolean ret = BootCommon::Read (false, avoid_reading_device_map);
// refresh device map if not read
if (BootStorage::device_mapping == nil
|| size (BootStorage::device_mapping) == 0)
{
BootStorage::ProposeDeviceMap ();
}
// FATE#305403: Bootloader beep configuration
// read status of acoustic signals
if (Mode::normal())
{
GfxMenu::ReadStatusAcousticSignal();
string md_value = BootStorage::addMDSettingsToGlobals();
string pB_md_value = BootCommon::globals["boot_md_mbr"]:"";
if (md_value != pB_md_value)
{
if (pB_md_value != "")
{
list <string> disks = splitstring(pB_md_value,",");
disks = filter(string v, disks, {return (v != "");});
if (size(disks) == 2)
{
BootCommon::enable_md_array_redundancy = true;
md_value = "";
}
y2milestone("disks from md array (perl Bootloader): %1", disks);
}
if (md_value != "")
{
BootCommon::enable_md_array_redundancy = false;
BootCommon::globals["boot_md_mbr"] = md_value;
y2milestone("Add md array to globals: %1", BootCommon::globals);
}
}
}
return ret;
}
/**
* Reset bootloader settings
* @param init boolean true to repropose also device map
*/
global define void Reset (boolean init) {
if (Mode::autoinst ())
return;
BootCommon::Reset (init);
}
/**
* Propose bootloader settings
*/
global define void Propose () {
y2debug ("Started propose: Glob: %1, Sec: %2",
BootCommon::globals, BootCommon::sections);
// if NOT was_proposed (i.e. blPropose() has not been called yet), then
// - set up BootPartitionDevice, RootPartitionDevice
// - if empty, set up mbrDisk
// - if loader_device is empty or the device is not a boot device, go
// to grub_ConfigureLocation() and
// - propose
// - select one loader device in the boot_* keys of the globals map
// - activate (when needed, but try to be nice to Windows)
// - do not touch these, except when /boot partition is selected_location:
// - activate_changed (set to true)
// - repl_mbr (set to true when we need to update existing GRUB, lilo MBRs,
// or when it looks like there is no code in the MBR at all,
// but NOT if this is a "Generic" (DOS) MBR, some unknown code
// or a Thinkpad MBR)
//
// always propose:
// - device_mapping (from "bios_id"s delivered by Storage, then let
// devices with unknown "bios_id"s drop into the
// gaps of this mapping or append at the end)
//
// if '/' and '/boot' were changed and selected_location is set and not
// "custom", ask user with popup whether it is OK to change the
// location and change it (with grub_DetectDisks() and grub_ConfigureLocation()
// (see above))
grub_LocationProposal ();
// Note that the Propose() function is called every time before
// Summary() is called.
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 do in AI mode if sections exist");
}
else
BootCommon::FixSections (BootGRUB::CreateSections);
}
if (BootCommon::globals == nil || size (BootCommon::globals) == 0)
{
BootCommon::globals = StandardGlobals();
}
else
{
if (Mode::autoinst ())
{
// TODO whatever will be needed
y2debug ("nothing to do in AI mode if globals are defined");
}
// decided to merge in default values for missing keys, EVEN in AI mode (!)
// this is primarily done to allow for the LocationProposal() to
// run and set keys in globals before we check them for existing
// keys here; but rather than checking for an empty globals map
// before LocationProposal() and using the result here, we figured
// that augmenting the globals is not such a bad idea even for the
// AI case...
y2milestone ("merging defaults to missing keys in globals");
// Merge default globals where not yet set
BootCommon::globals = (map<string, string>) union(
StandardGlobals(),
BootCommon::globals
);
// this currently does nothing more than fixing the "default" key,
// if that points to a section that does not exist anymore
BootCommon::FixGlobals ();
}
// check if windows is on second disk and add remap if it is necessary
// FATE #301994: Correct device mapping in case windows is installed on the second HD
BootCommon::sections = checkWindowsSection(BootCommon::sections);
if (Mode::installation())
BootCommon::UpdateProposalFromClient();
BootCommon::isTrustedGrub ();
y2milestone ("Proposed sections: %1", BootCommon::sections);
y2milestone ("Proposed globals: %1", BootCommon::globals);
}
/**
* 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)
{
// reduce device map to 8 devices
// FATE #303548 - Grub: limit device.map to devices detected by BIOS Int 13
ReduceDeviceMapTo8();
// now really save the settings
boolean ret = BootCommon::Save (clean, init, flush);
//importMetaData();
return ret;
}
/** FATE#303643 Enable one-click changes in bootloader proposal
*
*
*/
string urlLocationSummary()
{
y2milestone("Prepare url summary for GRUB");
string ret = "";
string line = "";
list<string> locations = [];
line = "<ul>\n<li>";
if (BootCommon::globals["boot_mbr"]:"" == "true")
line = line +
_("Boot from MBR is enabled (<a href=\"disable_boot_mbr\">disable</a>") + ")</li>\n";
else
line = line +
_("Boot from MBR is disabled (<a href=\"enable_boot_mbr\">enable</a>") + ")</li>\n";
locations = add(locations, line);
line ="<li>";
boolean set_boot_boot = false;
if ((BootCommon::globals["boot_boot"]:"" == "true") &&
(BootStorage::BootPartitionDevice != BootStorage::RootPartitionDevice))
{
set_boot_boot = true;
line = line +
_("Boot from /boot partition is enabled (<a href=\"disable_boot_boot\">disable</a>") + ")</li></ul>";
}
else if ((BootCommon::globals["boot_boot"]:"" == "false") &&
(BootStorage::BootPartitionDevice != BootStorage::RootPartitionDevice))
{
set_boot_boot = true;
line = line +
_("Boot from /boot partition is disabled (<a href=\"enable_boot_boot\">enable</a>") + ")</li></ul>";
}
if (line != "<li>")
{
locations = add(locations, line);
line ="<li>";
}
if ((BootCommon::globals["boot_root"]:"" == "true") && (!set_boot_boot))
line = line +
_("Boot from \"/\" partition is enabled (<a href=\"disable_boot_root\">disable</a>") + ")</li></ul>";
else if ((BootCommon::globals["boot_root"]:"" == "false") && (!set_boot_boot))
line = line +
_("Boot from \"/\" partition is disabled (<a href=\"enable_boot_root\">enable</a>") + ")</li></ul>";
if (!set_boot_boot)
locations = add(locations, line);
if (size(locations) > 0) {
// FIXME: should we translate all devices to names and add MBR suffixes?
ret = sformat (_("Change Location: %1"), mergestring (locations, " "));
}
return ret;
}
/**
* Display bootloader summary
* @return a list of summary lines
*/
global define list<string> Summary () {
list<string> ret = [];
string lt = BootCommon::getLoaderType (false);
string ln = BootCommon::getLoaderName (lt, `summary);
if (lt == "none") {
ret = [ HTML::Colorize (ln, "red") ];
}
// summary text, %1 is bootloader name (eg. LILO)
ret = add (ret, sformat (_("Boot Loader Type: %1"), ln));
// summary text, location is location description (eg. /dev/hda)
list<string> locations = [];
string line = "";
if (BootCommon::globals["boot_boot"]:"" == "true")
locations = add(locations, BootStorage::BootPartitionDevice + _(" (\"/boot\")"));
if (BootCommon::globals["boot_extended"]:"" == "true")
locations = add(locations, BootStorage::ExtendedPartitionDevice + _(" (extended)"));
if (BootCommon::globals["boot_root"]:"" == "true")
locations = add(locations, BootStorage::RootPartitionDevice + _(" (\"/\")"));
if (BootCommon::globals["boot_mbr"]:"" == "true")
locations = add(locations, BootCommon::mbrDisk + _(" (MBR)"));
if (haskey (BootCommon::globals, "boot_custom"))
locations = add(locations, BootCommon::globals["boot_custom"]:"");
if (size(locations) > 0) {
// FIXME: should we translate all devices to names and add MBR suffixes?
ret = add (ret, sformat (_("Status Location: %1"), mergestring (locations, ", ")));
}
// it is necessary different summary for autoyast and installation
// other mode than autoyast on running system
if (!Mode::config())
{
//ret = add(ret, _("Change Location:"));
ret = add(ret, urlLocationSummary());
}
// summary text. %1 is list of bootloader sections
list<string> sects = [];
foreach (map<string,any> s, BootCommon::sections, {
string title = s["name"]:"";
// section name "suffix" for default section
string def = title == BootCommon::globals["default"]:"" ? _(" (default)") : "";
sects = add (sects, String::EscapeTags (sformat ("+ %1%2", title, def)));
});
ret = add (ret, sformat (_("Sections:<br>%1"),
mergestring (sects, "<br>")));
if (size(locations) == 0) {
// summary text
ret = add (ret, _("Do not install boot loader; just create configuration files"));
}
string order_sum = BootCommon::DiskOrderSummary ();
if (order_sum != nil)
ret = add (ret, order_sum);
return ret;
}
/**
* Update read settings to new version of configuration files
*/
global define void Update () {
// update device map would be done in bootloader_preupdate
// run update device only if it was not called or if update device
// failed in bootloader_preupdate
if (!update_device_map_done)
BootCommon::UpdateDeviceMap ();
// During update, for libata device name migration ("/dev/hda1" ->
// "/dev/sda1") and somesuch, we need to re-read and parse the rest of the
// configuration file contents after internally updating the device map in
// perl-Bootloader. This way, the device names are consistent with the
// partitioning information we have set up in perl-Bootloader with
// SetDiskInfo(), and device names in other config files can be translated
// to Unix device names (#328448, this hits sections that are not
// (re-)created by yast-Bootloader or later by perl-Bootloader anyway).
BootCommon::SetDeviceMap (BootStorage::device_mapping);
Read (true, true);
BootCommon::UpdateSections ();
BootCommon::UpdateGlobals ();
}
/**
* Write bootloader settings to disk
* @return boolean true on success
*/
global define boolean Write () ``{
boolean ret = BootCommon::UpdateBootloader ();
if (BootCommon::location_changed || BootCommon::InstallingToFloppy ()) {
// bnc #461613 - Unable to boot after making changes to boot loader
// bnc #357290 - module rewrites grub generic code when leaving with no changes, which may corrupt grub
grub_updateMBR ();
if (BootCommon::InstallingToFloppy ()) {
if (! saveToFLoppyPopup ()) {
y2error ("Preparing floppy disk failed.");
ret = false;
}
}
boolean grub_ret = BootCommon::InitializeBootloader ();
if (grub_ret == nil)
grub_ret = false;
y2milestone ("GRUB return value: %1", grub_ret);
if (BootCommon::InstallingToFloppy ()) {
BootCommon::updateTimeoutPopupForFloppy
(BootCommon::getLoaderName ("grub", `summary));
}
ret = ret && grub_ret;
ret = ret && BootCommon::PostUpdateMBR ();
}
return ret;
}
global map<string,symbol()> Dialogs () {
return $[
"installation" : i386InstallDetailsDialog,
"loader" : i386LoaderDetailsDialog,
];
}
/**
* Boot passed section once on next reboot.
* @param section string section to boot
* @return boolean true on success
*/
global define boolean FlagOnetimeBoot (string section)
{
map result = (map)SCR::Execute (.target.bash_output, sformat (
"/usr/sbin/grubonce \"%1\"", BootCommon::Section2Index(section)));
y2milestone ("grubonce returned %1", result);
return (result["exit"]:-1 == 0);
}
list <string> grub_section_types()
{
return ["image", "xen", "menu", "other"];
}
/**
* Return map of provided functions
* @return a map of functions (eg. $["write"::Write])
*/
global map<string, any> GetFunctions () {
return $[
"read" : Read,
"reset" : Reset,
"propose" : Propose,
"save" : Save,
"summary" : Summary,
"update" : Update,
"write" : Write,
"widgets" : grubWidgets,
"dialogs" : Dialogs,
"section_types" : grub_section_types,
"flagonetimeboot" : FlagOnetimeBoot,
];
}
/**
* Initializer of GRUB bootloader
*/
global define void Initializer () {
y2milestone ("Called GRUB initializer");
BootCommon::current_bootloader_attribs = $[
"alias_keys" : [],
"propose" : true,
"read" : true,
"scratch" : true,
"additional_entries" : [`item (`id (`propose_deep),
// menubutton item, keep as short as possible
_("Propose and &Merge with Existing GRUB Menus"))],
"restore_mbr" : true,
"key_only_once" : false,
"bootloader_on_disk" : true,
];
BootCommon::InitializeLibrary (false, "grub");
}
/**
* Constructor
*/
global define void BootGRUB () {
BootCommon::bootloader_attribs["grub"] = $[
"required_packages" : ["grub"],
"loader_name" : "GRUB",
"initializer" : BootGRUB::Initializer,
];
}
/** bnc#494630 GRUB configuration in installation workflow fail with more than 8 disks on software raid
* Return all disks for checking in device map
*
* @param string boot disk
* @return list<string> disk devices
*/
list <string> ReturnAllDisks(string boot_disk)
{
list <string> ret = [];
map tm = Storage::GetTargetMap ();
map b_disk = tm[boot_disk]:$[];
if (b_disk["type"]:nil == `CT_MD)
{
string boot_partition = BootCommon::getBootPartition();
list b_disk_partitions = b_disk["partitions"]:[];
foreach (map p, (list<map>)b_disk_partitions,
{
if (p["device"]:"" == boot_partition)
{
if ((size(p["devices"]:[]) > 0) && (p["type"]:nil == `sw_raid))
{
foreach(string dev, p["devices"]:[],
{
map p_dev = Storage::GetDiskPartition (dev);
string disk_dev = p_dev["disk"]:"";
if (disk_dev != "")
ret = add(ret, disk_dev);
else
y2error("Real disk was not found for device: %1", dev);
});
} else {
if (p["type"]:nil == `sw_raid)
y2error("soft raid partition: %1 doesn't include any devices: %2",
boot_partition, p["devices"]:[]);
else
y2error("Disk is not soft-raid %1", b_disk);
ret = add(ret, boot_disk);
}
}
});
} else {
y2milestone("Boot disk is not on MD-RAID");
ret = add(ret, boot_disk);
}
y2milestone("Devices for checking if they are in device map: %1", ret);
return ret;
}
/** bnc#494630 GRUB configuration in installation workflow fail with more than 8 disks on software raid
* Function check if boot disk is in device map
*
* @return boolean true if boot device is not in device map
*/
global boolean CheckDeviceMap()
{
// FATE #303548 - Grub: limit device.map to devices detected by BIOS
boolean ret = false;
string boot_disk = BootCommon::getBootDisk();
list <string> disks = ReturnAllDisks(boot_disk);
if (size(disks) > 0)
{
foreach(string disk, disks,
{
ret = ret || checkBootDeviceInDeviceMap(disk, BootStorage::Dev2MountByDev(disk));
});
}
return ret;
}
} // EOF
/*
* Local variables:
* mode: ycp
* mode: font-lock
* mode: auto-fill
* indent-level: 4
* fill-column: 78
* End:
*/
ACC SHELL 2018