home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Chip 2007 January, February, March & April
/
Chip-Cover-CD-2007-02.iso
/
boot
/
i386
/
root
/
usr
/
share
/
YaST2
/
include
/
bootloader
/
routines
/
misc.ycp
< prev
next >
Wrap
Text File
|
2006-11-29
|
53KB
|
1,762 lines
/**
* File:
* include/bootloader/routines/misc-common.ycp
*
* Module:
* Bootloader installation and configuration
*
* Summary:
* Miscelaneous functions for bootloader configuration and installation
*
* Authors:
* Jiri Srain <jsrain@suse.cz>
* Olaf Dabrunz <od@suse.de>
*
* $Id: misc.ycp 34298 2006-11-13 12:02:16Z jplack $
*
* WARNING:
* To be included to BootCommon.ycp only, requires function
* getLoaderType to avoid include-import cycle
* Use import "BootCommon" instead.
*/
{
textdomain "bootloader";
import "Mode";
import "Stage";
import "Storage";
import "StorageDevices";
import "Report";
import "Kernel";
import "Misc";
import "ProductFeatures";
import "Directory";
// bootloader attributes handling functions
global define map<string, integer> Md2Partitions (string md_device);
global define void DetectDisks ();
/**
* Get packages needed by specified bootloader
* maybe should be moved to bootloader specific modules
* @param bootloader string name of bootloader
* @return a list of required packages names
*/
global define list<string> getBootloaderPackages (string bootloader) ``{
return bootloader_attribs[bootloader, "required_packages"]:[];
}
/**
* return printable name of bootloader
* @param bootloader string bootloader type internal string
* @param mode symbol `combo or `summary (because of capitalization)
* @return string printable bootloader name
*/
global define string getLoaderName (string bootloader, symbol mode) ``{
if (bootloader == "none")
{
return mode == `summary
// summary string
? _("Do not install any boot loader")
// combo box item
: _("Do Not Install Any Boot Loader");
}
if (bootloader == "default")
{
return mode == `summary
// summary string
? _("Install the default boot loader")
// combo box item
: _("Install Default Boot Loader");
}
string fallback_name = mode == `summary
// summary string
? _("Boot loader")
// combo box item
: _("Boot Loader");
// fallback bootloader name, keep short
return bootloader_attribs[bootloader, "loader_name"]:fallback_name;
}
/**
* Get value of specified boolean bootloader attribute
* @param attrib string attribute name
* @return boolean value of attribute
*/
global define boolean getBooleanAttrib (string attrib) ``{
return current_bootloader_attribs[attrib]:false;
}
/**
* Get value of specified bootloader attribute
* @param attrib string attribute name
* @param defaultv any default value of the attribute (if not found)
* @return any value of attribute
*/
global define any getAnyTypeAttrib (string attrib, any defaultv) ``{
return current_bootloader_attribs[attrib]:defaultv;
}
/**
* Get index of a section specified by name
* @param name string section name
* @return integer index of the section or nil if not found
*/
global integer getSectionIndex (string name) {
integer index = -1;
integer sectnum = nil;
foreach (map<string,any> s, sections, {
index = index + 1;
if (s["name"]:"" == name)
sectnum = index;
});
return sectnum;
}
/**
* Get map where to store kernel parameters
* @return map describing where to store which kernel parameter
*/
global define map getKernelParamKeys () ``{
return current_bootloader_attribs["kernel_params"]:$[];
}
// other misc functions
/**
* Generates unused section label for new section
* @return string label for new section
*/
global string getFreeSectionLabel () {
integer index = 1;
list<string> existing = [];
foreach (map<string,any> s, sections, {
existing = add (existing, s["name"]:"");
list<string> aliases = splitstring (s["aliases"]:"", ";");
foreach (string a, aliases, {
existing = add (existing, a);
});
});
while (true)
{
string title = sformat ("section %1", index);
if (! contains (existing, title))
return title;
index = index + 1;
}
}
/**
* Get the list of installed floppy drives
* @return a list of floppy devices
*/
global define list<string> getFloppyDevices () ``{
if (floppy_devices == nil)
{
list<map> floppies = (list<map>) SCR::Read (.probe.floppy);
floppies = filter (map f, floppies,
``(f["model"]:"Floppy Disk" == "Floppy Disk"));
floppy_devices = maplist (map f, floppies,
``(f["dev_name"]:""));
floppy_devices = filter (string f, floppy_devices, ``(f != ""));
}
return floppy_devices;
}
/**
* Update the text of countdown widget
* @param bootloader string printable name of used bootloader
*/
global define void updateTimeoutPopupForFloppy (string bootloader) ``{
if (Mode::normal ())
return;
string confirm_boot_msg = Misc::boot_msg;
// data saved to floppy disk
string msg = sformat (
// popup, %1 is bootloader name
_("The %1 boot sector has been written to the floppy disk."),
bootloader);
msg = msg + "\n";
// always hard boot
// If LILO was written on floppy disk and we need
// to do a hard reboot (because a different kernel
// was installed), tell the user to leave the floppy
// inserted.
msg = msg
// popup - continuing
+ _("Leave the floppy disk in the drive.");
if ( size (confirm_boot_msg) > 0 )
{
msg = msg + "\n" + confirm_boot_msg;
}
Misc::boot_msg = msg;
}
/**
* List of all supported bootloaders
*/
global list<string> bootloaders = ["lilo", "grub", "elilo", "milo", "silo",
"aboot", "zipl", "ppc", "mips"];
/**
* converts array of string to path
* @param strs list of string
* @return path whose components are taken from strs
*/
global define path list2path(list<string> strs) ``{
path pth = .;
foreach(string e, strs, ``{
pth = add(pth, e);
});
return pth;
}
/**
* Converts a "/dev/disk/by-" device name to the corresponding kernel
* device name, if a mapping for this name can be found in the map from
* yast2-storage. If the given device name is not a "/dev/disk/by-" device
* name, it is left unchanged. Also, if the information about the device
* name cannot be found in the target map from yast2-storage, the device
* name is left unchanged.
*
* @param dev string device name
* @return string kernel device name
*/
global define string MountByDev2Dev(string dev) ``{
y2milestone ("MountByDev2Dev: %1", dev);
if (!regexpmatch(dev, "^/dev/disk/by-"))
return dev;
map<string,map> devices = (map<string,map>)Storage::GetTargetMap();
// make a map: "type/identifier" -> kernel_device_name
// e.g.: "id/scsi-SATA_ST3120813AS_3LS09W21-part5" -> "/dev/sda2"
map<string,string> partitions = $[];
foreach (string k, map v, devices, ``{
foreach (map p, (list<map>)(v["partitions"]:[]), ``{
if ( haskey(p, "uuid") )
{
partitions["uuid/" + p["uuid"]:""] = p["device"]:dev;
}
if ( haskey(p, "label") )
{
partitions["label/" + p["label"]:""] = p["device"]:dev;
}
if ( haskey(p, "udev_id") )
{
partitions["id/" + p["udev_id", 0]:""] = p["device"]:dev;
}
if ( haskey(p, "udev_path") )
{
partitions["path/" + p["udev_path"]:""] = p["device"]:dev;
}
});
});
y2milestone ("MountByDev2Dev: partitions %1", partitions);
// ident is "id/<id>", "path/<path>", "uuid/<uuid>" or "label/<label>"
string ident = regexpsub(dev, "^/dev/disk/by-(.*)", "\\1");
y2milestone ("MountByDev2Dev: ident %1", ident);
// if exists, get the kernel name, otherwise leave unchanged
string ret = partitions[ident]:dev;
y2milestone ("MountByDev2Dev: ret %1", ret);
return ret;
}
/**
* Converts a device name to the corresponding device name it should be
* mounted by, according to the "mountby" setting for the device from
* yast2-storage.
*
* @param dev string device name
* @return string device name according to "mountby"
*/
global define string Dev2MountByDev(string dev) ``{
y2milestone ("Dev2MountByDev: %1", dev);
map<string,map> devices = (map<string,map>)Storage::GetTargetMap();
// make a map: "/dev/hda1" -> info_map_for_this_partition
map<string,map> partitions = $[];
foreach (string k, map v, devices, ``{
foreach (map p, (list<map>)(v["partitions"]:[]), ``{
partitions[p["device"]:""] = p;
});
});
y2debug ("Dev2MountByDev: partitions %1", partitions);
string ret = dev;
symbol mountby = `device;
mountby = (symbol) partitions[dev, "mountby"]:nil;
if ( mountby == `uuid )
{
ret = sformat ("/dev/disk/by-uuid/%1", partitions[dev, "uuid"]:"");
} else if ( mountby == `label )
{
ret = sformat ("/dev/disk/by-label/%1", partitions[dev, "label"]:"");
} else if ( mountby == `id )
{
ret = sformat ("/dev/disk/by-id/%1", partitions[dev, "udev_id", 0]:"");
} else if ( mountby == `path )
{
ret = sformat ("/dev/disk/by-path/%1", partitions[dev, "udev_path"]:"");
}
y2milestone ("Dev2MountByDev: ret %1", ret);
return ret;
}
/**
* Returns list of partitions with "mount by" hints. Goes through the list
* of partitions passed as a parameter and creates a list of partitions with
* hints according to the current partitioning requested from
* yast2-storage. To be used in a combobox or menu.
*
* @param parts_to_get list<string> partitions to list
* @return a list of strings containing a partition name and a hint (if applicable)
*/
global define list<string> getHintedPartitionList(list<string> parts_to_get) ``{
y2milestone ("getHintedPartitionList: %1", parts_to_get);
map<string,map> devices = (map<string,map>)Storage::GetTargetMap();
// make a map: "/dev/hda1" -> info_map_for_this_partition
map<string,map> partitions = $[];
foreach (string k, map v, devices, ``{
foreach (map p, (list<map>)(v["partitions"]:[]), ``{
partitions[p["device"]:""] = p;
});
});
y2milestone ("getHintedPartitionList: partitions %1", partitions);
symbol mountby = `device;
list<string> ret = maplist (string dev, parts_to_get, ``{
mountby = (symbol) partitions[dev, "mountby"]:nil;
if ( mountby == `uuid )
{
return sformat ("%1 (mount by UUID: %2)", dev,
partitions[dev, "uuid"]:nil != nil ?
partitions[dev, "uuid"]:"" :
"<UUID to be created later during format>");
} else if ( mountby == `label )
{
return sformat ("%1 (mount by LABEL: %2)", dev, partitions[dev, "label"]:"");
} else if ( mountby == `id )
{
return sformat ("%1 (mount by ID: %2)", dev, partitions[dev, "udev_id", 0]:"");
} else if ( mountby == `path )
{
return sformat ("%1 (mount by PATH: %2)", dev, partitions[dev, "udev_path"]:"");
} else if ( mountby == nil || mountby == `device )
{
return dev;
}
});
y2milestone ("getHintedPartitionList: ret %1", ret);
return ret;
}
/**
* Returns list of partitions. Requests current partitioning from
* yast2-storage and creates list of partition for combobox, menu or other
* purpose.
* @param type symbol
* `boot - for bootloader installation
* `root - for kernel root
* `boot_other - for bootable partitions of other systems
* `all - all partitions
* `parts_old - all partitions, except those what will be created
* during isntallation
* `deleted - all partitions deleted in current proposal
* `kept - all partitions that won't be deleted, new created or formatted
* `destroyed - all partition which are new, deleted or formatted
* @return a list of strings
*/
global define list<string> getPartitionList(symbol type) ``{
y2milestone ("getPartitionList: %1", type);
map<string,map> devices = (map<string,map>)Storage::GetTargetMap();
list<map> partitions = [];
foreach (string k, map v, devices, ``{
partitions = (list<map>)merge (partitions, (list<map>)(v["partitions"]:[]));
});
list<string> floppies = getFloppyDevices ();
devices = filter (string k, map v, devices,
``(v["type"]:`CT_UNKNOWN != `CT_LVM));
if (type == `boot || type == `boot_other)
{
devices = filter (string k, map v, devices,
``(v["type"]:`CT_UNKNOWN == `CT_DISK));
}
list<string> all_disks = (list<string>) maplist (string k, map v, devices, ``(k));
if (type == `deleted)
return maplist (map x, filter (map p, partitions, ``(p["delete"]:false)),
``{return x["device"]:"";
});
else if (type == `destroyed)
{
return maplist (map x, filter (map p, partitions, {
return p["delete"]:false || p["format"]:false
|| p["create"]:false;
}),
{
return x["device"]:"";
});
}
partitions = filter (map p, partitions, ``(! p["delete"]:false));
list<string> ret = all_disks;
if (type == `boot_other || type == `root || type == `parts_old
|| type == `kept)
ret = [];
if (type == `boot)
{
partitions = filter (map p, partitions,
``(p["type"]:`primary == `primary
|| p["type"]:`primary == `extended
|| p["type"]:`primary == `logical
|| p["type"]:`primary == `sw_raid));
// FIXME this checking is performed on 3 places, one function should
// be developed for it
partitions = filter (map p, partitions, {
symbol fs = (symbol)(p["used_fs"]:p["detected_fs"]:nil);
if (fs == `xfs)
return false;
return true;
});
}
else if (type == `root)
{
partitions = filter (map p, partitions,
``(p["type"]:`primary != `extended));
}
else if (type == `parts_old)
{
partitions = filter (map p, partitions, ``(! p["create"]:false));
}
else if (type == `kept)
{
partitions = filter (map p, partitions, {
return ! (p["create"]:false || p["format"]:false);
});
}
if (type != `all && type != `parts_old && type != `kept)
{
partitions = filter (map p, partitions,
``(p["fstype"]:"" != "Linux swap"));
}
if (type == `boot)
{
partitions = filter (map p, partitions,
``(p["fstype"]:"" == "Linux native"
|| p["fstype"]:"" == "Extended"
|| p["fstype"]:"" == "Linux RAID"
|| p["fstype"]:"" == "MD Raid"));
}
list<string>partition_names
= maplist (map p, partitions, ``((string)(p["device"]:"")));
partition_names = filter (string p, partition_names, ``(p != ""));
partition_names = (list<string>)merge (partition_names, floppies);
ret = (list<string>)union (ret, partition_names);
ret = (list<string>)toset (ret);
return (list<string>)ret;
}
/**
* returns true if char is blank (newline, tab or space)
* @param s single char string
* @return boolean blank/non blank
*/
global define boolean isBlank(string s) ``{
if (s == "\n" || s == "\t" || s == " ")
{
return true;
}
return false;
}
/**
* removes trailing and leading blank chars from string.
* eg: " as df " -> "as df"
* @param str string source string
* @return string stripped string
*/
global define string strip(string str)``{
//emtpy string
if (size(str) == 0) return "";
integer bound = size(str);
integer first = 0;
integer last = size(str) - 1;
// find first non-blank char
while(first < bound && isBlank(substring(str, first, 1)))
{
first = first + 1;
}
while(last >= 0 && isBlank(substring(str, last, 1)))
{
last = last - 1;
}
if (last >= first)
{
return substring(str, first, last - first + 1);
}
return "";
}
/**
* replaces all occurences of 'from' to 'to' in src
* @param src input string
* @param from string to be replaced
* @param to string to be replaced by
* @return string modified string
*/
global define string replaceAll(string src, string from, string to) ``{
if (from == "")
{
return src;
}
list tokens = [];
integer p = search(src, from);
while(p != nil)
{
tokens = add(tokens, substring(src, 0, p));
src = substring(src, p + size(from), size(src) - (p+size(from)));
p = search(src, from);
}
tokens = add(tokens, src);
return mergestring((list<string>)tokens, to);
}
/**
* convert string values from agent representation to module representation
* 1) if string is quoted, quotes are removed
* 2) if string contains escaped quotes, they're unescaped
* @param val value to transform
* @return any transformed value
*/
global define any mod2ui(any val) ``{
if (is(val, string))
{
string v = sformat("%1", val);
v = strip(v);
// remove leading and trailing quotes
if (substring(v, 0, 1) == "\""
&& substring(v, size(v)-1, 1) == "\"")
{
v = substring(v, 1, size(v)-2);
}
// unescape backslashes
v = replaceAll(v, "\\\\", "\\");
// unescape quotes
v = replaceAll(v, "\\\"", "\"");
return v;
}
return val;
}
/**
* returns list difference A \ B (items that are in A and are not in B)
* @param a list A
* @param b list B
* @return list see above
*/
global define list difflist(list a, list b) ``{
return filter(any e, a, ``(!contains(b, e)));
}
/**
* reverse of mod2ui
* @param val value to transform
* @return any transformed value
*/
global define any ui2mod(any val) ``{
if (is(val, string))
{
string v = sformat("%1", val);
v = strip(v);
// if string contains backslashes, escape them
v = replaceAll(v, "\\", "\\\\");
// if string contains quotes, escape them
v = replaceAll(v, "\"", "\\\"");
// if string contains spaces or equal sign or is empty, quote it
if ((size(splitstring(v, " ")) >= 2 || size(v) == 0
|| findfirstof(v, "=") != nil)
&&(getLoaderType (false) != "grub"))
{
v = "\"" + v + "\"";
}
return v;
}
return val;
}
/**
* returns modified list where items index1 and index2 are swapped.
* if indices are out of bounds, unmodified list is returned.
* @param input list
* @param index1 index of the first element
* @param index2 index og the second element
* @return list modified list
*/
global define list swapItems(list input, integer index1, integer index2) ``{
if (index1 >= size(input) || index2 >= size(input)
|| index1 < 0 || index2 < 0)
{
return input;
}
list output = [];
integer pos = -1;
output = maplist(any e, input, {
pos = pos + 1;
if (pos == index1)
{
return (any)(input[index2]:(any)$[]);
}
else if (pos == index2)
{
return (any)(input[index1]:(any)$[]);
}
return e;
});
return output;
}
/**
* translate filename path (eg. /boot/kernel) to list of device
* and relative path
* @param fullpth string fileststem path (eg. /boot/vmlinuz)
* @return a list containing device and relative path,
* eg. ["/dev/hda1", "/vmlinuz"]
*/
global define list<string> splitPath (string fullpth) ``{
// UGHLY HACK because of testsuites
map<string,list> mountpoints = $[];
if (Mode::test ())
mountpoints = $["/" : ["/dev/hda2"], "/boot" : ["/dev/hda1"]];
else
mountpoints = (map<string,list>)Storage::GetMountPoints();
string dev = "";
string mp = "";
integer max = 0;
//
// FIXME: this is broken code, implement a proper prefix match!! see below
foreach (string k, list v, mountpoints, ``{
if (k != "swap" && issubstring (fullpth, k) && size (k) > max)
{
max = size (k);
dev = v[0]:"";
mp = k;
}
});
if (mp == "")
return [];
// FIXME: pth will be wrong for fullpth=='(hd0,1)/boot/vmlinux' !!
string pth = substring (fullpth, size (mp));
if (substring (pth, 0, 1) != "/")
pth = "/" + pth;
return [dev, pth];
}
/**
* translate list of device and relative path
* to filename path (eg. /boot/kernel)
* @param devpth list of two strings, first for device name, second for
* relative path (eg. ["/dev/hda1", "/vmlinuz"])
* @return string fileststem path (eg. /boot/vmlinuz)
*/
global define string mergePath (list<string> devpth) ``{
// UGHLY HACK because of testsuites
map<string,list> mountpoints = $[];
if (Mode::test ())
mountpoints = $["/" : ["/dev/hda2"], "/boot" : ["/dev/hda1"]];
else
mountpoints = (map<string,list>)Storage::GetMountPoints();
string ret = "/dev/null";
if (substring (devpth[1]:"", 0, 1) != "/")
devpth[1] = "/" + devpth[1]:"";
foreach (string k, list v, mountpoints, ``{
if (k != "swap" && v[0]:"" == devpth[0]:"")
{
if (substring (k, size (k) - 1, 1) == "/")
k = substring (k, 0, size (k) - 1);
ret = k + devpth[1]:"";
}
});
return ret;
}
/**
* If device is part of RAID (md), then return first of its members
* otherwise return the same as argument
* @param device string device of the RAID
* @return string first member of the RAID
*/
global define string getDeviceOfRaid (string device) ``{
// get list of all partitions (not marked to be deleted)
map<string,map> tm = (map<string,map>)Storage::GetTargetMap ();
list<map> partitions = [];
foreach ( string dev, map disk, tm, ``{
if( Storage::IsRealDisk( disk ) )
{
list<map> l = (list<map>)filter( map p, disk["partitions"]:[],
``(p["delete"]:false==false) );
partitions = (list<map>)merge (partitions, l);
}
});
// filter partitions to relevant list according to raid name
list<map> md_list = filter (map e, partitions, ``(e["used_by"]:"" == substring(device,5)));
// get the devices
list<string> dev_list = maplist (map e, md_list, ``(e["device"]:""));
dev_list = filter (string d, dev_list, ``(d != ""));
if (size (dev_list) > 0)
{
dev_list = sort (dev_list);
return dev_list[0]:"";
}
return device;
}
/**
* Get bootloader device for specified location
* @return string device name
*/
global define string GetBootloaderDevice () ``{
if (BootCommon::selected_location == "mbr")
return BootCommon::mbrDisk;
if (BootCommon::selected_location == "boot")
return BootCommon::BootPartitionDevice;
if (BootCommon::selected_location == "root")
return BootCommon::RootPartitionDevice;
if (BootCommon::selected_location == "floppy")
return StorageDevices::FloppyDevice;
if (BootCommon::selected_location == "mbr_md")
return "mbr_md";
if (BootCommon::selected_location == "none")
return "/dev/null";
return BootCommon::loader_device;
}
/**
* Check if installation to floppy is performed
* @return true if installing bootloader to floppy
*/
global boolean InstallingToFloppy () {
boolean ret = false;
if (loader_device == StorageDevices::FloppyDevice)
ret = true;
else if (contains (getFloppyDevices (), loader_device))
ret = true;
y2milestone ("Installing to floppy: %1", ret);
return ret;
}
/**
*
* @return true if installing bootloader to floppy
*/
global define boolean installingToFloppy () ``{
if (BootCommon::loader_device == StorageDevices::FloppyDevice)
return true;
if (contains (getFloppyDevices (), BootCommon::loader_device))
return true;
return false;
}
/**
* Get the list of particular kernel parameters
* @param line string the whole kernel command line
* @return a list of the kernel parameters split each separaterlly
*/
global list<string> ListKernelParamsInLine (string line) {
// FIXME this function is really similar to code in Kernel.ycp
list<string> cmdlist = [];
integer parse_index = 0;
boolean in_quotes = false;
boolean after_backslash = false;
string current_param = "";
while (parse_index < size (line))
{
string current_char = substring (line, parse_index, 1);
if (current_char == "\"" && ! after_backslash)
in_quotes = ! in_quotes;
if (current_char == " " && ! in_quotes)
{
cmdlist = add (cmdlist, current_param);
current_param = "";
}
else
current_param = current_param + current_char;
if (current_char == "\\")
after_backslash = true;
else
after_backslash = false;
parse_index = parse_index + 1;
}
cmdlist = add (cmdlist, current_param);
cmdlist = maplist (string c, cmdlist, {
if (regexpmatch (c, "^[^=]+="))
c = regexpsub (c, "^([^=]+)=", "\\1");
return c;
});
return cmdlist;
}
/**
* get kernel parameter from kernel command line
* @param line string original line
* @param key string parameter key
* @return string value, "false" if not present,
* "true" if present key without value
*/
global define string getKernelParamFromLine (string line, string key) ``{
// FIXME this doesn't work with quotes and spaces
string res = "false";
list<string> params = splitstring (line, " ");
params = filter (string p, params, ``(p != ""));
foreach (string p, params, ``{
list<string> l = (list<string>) filter (string e, splitstring (p, "="), ``(e != " " && e != ""));
if (l[0]:"" == key)
res = l[1]:"true";
});
return res;
}
/**
* set kernel parameter to GRUB command line
* @param line string original line
* @param key string parameter key
* @param value string value, "false" to remove key,
* "true" to add key without value
* @return string new kernel command line
*/
global define string setKernelParamToLine
(string line, string key, string value)
``{
// FIXME this doesn't work with quotes and spaces
list<string> params = splitstring (line, " ");
params = filter (string p, params, ``(p != ""));
boolean done = false;
// count occurences of every parameter
map<string,integer> occurences = $[];
foreach (string p, params, ``{
list<string> l = filter (string e, splitstring (p, "="), ``(e != " " && e != ""));
string k = l[0]:"";
occurences[k] = occurences[k]:0 + 1;
});
params = maplist (string p, params, ``{
list<string> l = filter (string e, splitstring (p, "="), ``(e != " " && e != ""));
string k = l[0]:"";
if (k == key)
{
if (value == "false")
{
return "";
}
else if (occurences[k]:0 <= 1)
{
done = true;
if (value == "true")
{
return key;
}
else if (value != "false")
{
return sformat ("%1=%2", key, value);
}
}
else
{
occurences[k] = occurences[k]:0 - 1;
return "";
}
}
return p;
});
if (! done)
{
if (value == "true")
{
params = add (params, key);
}
else if (value != "false")
{
params = add (params, sformat ("%1=%2", key, value));
}
}
params = filter (string p, params, ``(p != ""));
line = mergestring (params, " ");
return line;
}
/**
* remove blanks from section name and replace them with _
* when not using GRUB
* @param original string
* @return string fixed string
*/
global define string removeBlanks (string original) {
if (getLoaderType (false) == "grub")
return original;
// do not allow empty labels
while (size (original) > 1 && substring (original, 0, 1) == " ")
{
original = substring (original, 1);
}
while (size (original) > 1
&& substring (original, size (original) -1, 1) == " ")
{
original = substring (original, 0, size (original) - 1);
}
if (size (original) > 15)
original = substring (original, 0, 15);
return replaceAll (original, " ", "_");
}
/**
* Filter list to only entries which are present as keys in map
* @param order list (ordered) of settings
* @param widgets map of present widgets
* @param index of selected option
* @return map containing new index of selected option and list of options
* where options that aren't ordered precede ordered options
*/
global define map filterOrder (list order, map widgets, integer index) ``{
list ordering = [];
list unordering = [];
integer new_index = -1;
integer order_count = 0;
integer sorted_cound = 0;
foreach (map o, (list<map>)order, ``{
boolean ord_option = widgets[o["key"]:"", "table", "ordering"]:true;
if (substring (o["key"]:"", 0, 2) == "__")
ord_option = false;
if (ord_option)
{
ordering = add (ordering, o);
order_count = order_count + 1;
}
else
{
unordering = add (unordering, o);
}
if (index == 0)
{
new_index = order_count - 1;
}
index = index - 1;
});
new_index = new_index + size (unordering);
return $[
"items" : merge (unordering, ordering),
"index" : new_index,
];
}
/*
* convert any value to an integer and return 0 for nonsense
*/
define integer myToInteger(any num_any) {
if (num_any == nil)
return 0;
if (is (num_any, integer))
return (integer)num_any;
if (is (num_any, string))
return (num_any == "") ? 0 : tointeger((string)num_any);
return 0;
}
/**
* Get partition which should be activated if doing it during bl inst.
* @param boot_partition string the partition holding /boot subtree
* @param loader_device string the device to install bootloader to
* @return a map $[ "dev" : string, "mbr": string, "num": any]
* containing device (eg. "/dev/hda4"), disk (eg. "/dev/hda") and
* partition number (eg. 4)
*/
global define map<string,any> getPartitionToActivate (string boot_partition,
string loader_device)
{
map p_dev = Storage::GetDiskPartition (loader_device);
integer num = myToInteger( p_dev["nr"]:nil );
string mbr_dev = p_dev["disk"]:"";
// if bootloader is installed to /dev/md*
// FIXME: use ::storage to detect md devices, not by name!
if (substring (loader_device, 0, 7) == "/dev/md")
{
map<string,integer> md = Md2Partitions (BootCommon::loader_device);
integer min = 128; // max. is 127
string device = "";
foreach (string d, integer id, md, {
if (id < min)
{
min = id;
device = d;
}
});
if (device != "")
{
map p_dev = Storage::GetDiskPartition (loader_device);
num = myToInteger( p_dev["nr"]:nil );
mbr_dev = p_dev["disk"]:"";
}
}
// if bootloader in MBR, activate /boot partition
// (partiall fix of #20637)
else if (num == 0)
{
p_dev = Storage::GetDiskPartition (boot_partition);
num = myToInteger( p_dev["nr"]:nil );
mbr_dev = p_dev["disk"]:"";
if (size (Md2Partitions (boot_partition)) > 1)
{
foreach (string k, integer v, Md2Partitions (boot_partition),{
if (search (k, loader_device) == 0)
{
p_dev = Storage::GetDiskPartition (k);
num = myToInteger( p_dev["nr"]:nil );
mbr_dev = p_dev["disk"]:"";
}
});
}
}
if (num != 0)
{
if (num > 4)
{
y2milestone ("Bootloader partition type is logical");
map tm = Storage::GetTargetMap ();
list<map> partitions = tm[mbr_dev, "partitions"]:[];
foreach (map p, partitions, ``{
if (p["type"]:nil == `extended)
{
num = p["nr"]:num;
y2milestone ("Using extended partition %1 instead",
num);
}
});
}
}
map<string,any> ret = $[
"num" : num,
"mbr" : mbr_dev,
"dev" : Storage::GetDeviceName (mbr_dev, num),
];
return ret;
}
/**
* Get a list of partitions to activate if user wants to activate
* boot partition
* @return a list of partitions to activate
*/
global define list<map<string, any> > getPartitionsToActivate () {
map<string,integer> md = $[];
if (BootCommon::loader_device == "mbr_md")
{
md = Md2Partitions (BootCommon::BootPartitionDevice);
}
else
{
md = Md2Partitions (BootCommon::loader_device);
}
list<string> partitions = maplist (string k, integer v, md, ``(k));
if (size (partitions) == 0)
{
partitions = [BootCommon::loader_device];
}
list<map<string,any> > ret = maplist (string partition, partitions, {
return getPartitionToActivate (
BootCommon::BootPartitionDevice,
partition);
});
return toset (ret);
}
/**
* Get the list of MBR disks that should be rewritten by generic code
* if user wants to do so
* @return a list of device names to be rewritten
*/
global define list<string> getMbrsToRewrite () {
list<string> ret = [BootCommon::mbrDisk];
map<string,integer> md = $[];
if (BootCommon::loader_device == "mbr_md")
{
md = Md2Partitions (BootCommon::BootPartitionDevice);
}
else
{
md = Md2Partitions (BootCommon::loader_device);
}
list<string> mbrs = maplist (string d, integer b, md, {
d = getPartitionToActivate (
BootCommon::BootPartitionDevice, d
)["mbr"]:mbrDisk;
return d;
});
if (contains (mbrs, BootCommon::mbrDisk))
{
ret = (list<string>)merge (ret, mbrs);
}
return toset (ret);
}
/**
* Get last change time of file
* @param filename string name of file
* @return string last change date as YYYY-MM-DD-HH-MM-SS
*/
global define string getFileChangeDate (string filename) ``{
map stat = (map) SCR::Read (.target.stat, filename);
integer ctime = stat["ctime"]:0;
string command = sformat (
"date --date='1970-01-01 00:00:00 %1 seconds' +\"%%Y-%%m-%%d-%%H-%%M-%%S\"",
ctime);
map out = (map) SCR::Execute (.target.bash_output, command);
string c_time = out["stdout"]:"";
y2debug ("File %1: last change %2", filename, c_time);
return c_time;
}
/**
* Save current MBR to /boot/backup_mbr
* Also save to /var/lib/YaST2/backup_boot_sectors/%device, if some
* existing, rename it
* @param device string name of device
*/
global define void saveMBR (string device) ``{
string device_file = mergestring (splitstring (device, "/"), "_");
string device_file_path = "/var/lib/YaST2/backup_boot_sectors/"
+ device_file;
SCR::Execute (.target.bash,
"test -d /var/lib/YaST2/backup_boot_sectors || mkdir /var/lib/YaST2/backup_boot_sectors");
if (SCR::Read (.target.size, device_file_path) > 0)
{
list<string> contents = (list<string>) SCR::Read (.target.dir, "/var/lib/YaST2/backup_boot_sectors");
contents = filter (string c, contents, ``(regexpmatch (c, sformat (
"%1-.*-.*-.*-.*-.*-.*", device_file))));
contents = sort (contents);
integer index = 0;
integer siz = size (contents);
while (index + 10 < siz)
{
SCR::Execute (.target.remove,
sformat ("/var/lib/YaST2/backup_boot_sectors/%1", contents[index]:""));
index = index + 1;
}
string change_date = getFileChangeDate (device_file_path);
SCR::Execute (.target.bash, sformat (
"/bin/mv %1 %1-%2",
device_file_path, change_date));
}
SCR::Execute (.target.bash, sformat (
"/bin/dd if=%1 of=%2 bs=512 count=1 2>&1",
device, device_file_path));
if (device == mbrDisk)
{
SCR::Execute (.target.bash, sformat (
"/bin/dd if=%1 of=%2 bs=512 count=1 2>&1",
device, "/boot/backup_mbr"));
}
}
/**
* Update contents of MBR (active partition and booting code)
* FIXME move tis function to lilolike.ycp
* @return boolean true on success
*/
global define boolean updateMBR () ``{
// FIXME: do the real thing in perl_Bootloader
if (getLoaderType (false) == "grub") {
activate = ( globals["activate"]:"false" == "true" );
repl_mbr = ( globals["generic_mbr"]:"false" == "true" );
}
y2milestone ("Updating disk system area, activate partition: %1, replace MBR: %2", activate, repl_mbr);
if (backup_mbr)
{
y2milestone ("Doing MBR backup: MBR Disk: %1, loader device: %2",
BootCommon::mbrDisk, BootCommon::loader_device);
list<string> disks_to_rewrite = (list<string>)toset (merge (
getMbrsToRewrite (),
[BootCommon::mbrDisk, BootCommon::loader_device]));
y2milestone ("Creating backup of boot sectors of %1",
disks_to_rewrite);
foreach (string d, disks_to_rewrite, {
saveMBR (d);
});
}
boolean ret = true;
if (repl_mbr && BootCommon::loader_device != mbrDisk)
{
if (! Stage::initial ())
{
PackageSystem::Install ("master-boot-code");
}
y2milestone("Updating code in MBR: MBR Disk: %1, loader device: %2",
BootCommon::mbrDisk, BootCommon::loader_device);
list<string> disks_to_rewrite = getMbrsToRewrite ();
foreach (string d, disks_to_rewrite, {
y2milestone ("Copying generic MBR code to %1", d);
string command = sformat (
"/bin/dd bs=446 count=1 if=%1 of=%2",
"/usr/lib/boot/master-boot-code",
d);
y2milestone ("Running command %1", command);
map out = (map)SCR::Execute (.target.bash_output, command);
integer exit = out["exit"]:0;
y2milestone ("Command output: %1", out);
ret = ret && (0 == exit);
});
}
if (activate)
{
foreach (map m_activate, getPartitionsToActivate (), {
any num = m_activate["num"]:0;
string mbr_dev = m_activate["mbr"]:"";
if (num != 0 && mbr_dev != "")
{
// if primary partition
if ((! is (num, integer)) || num <= 4)
{
y2milestone ("Activating partition %1 on %2", num, mbr_dev);
// FIXME: this is the most rotten code since molded sliced bread
// move to bootloader/Core/GRUB.pm or similar
// TESTME: make sure that parted does not destroy BSD
// slices (#suse24740): cf. section 5.1 of "info parted":
// Parted only supports the BSD disk label system.
// Parted is unlikely to support the partition slice
// system in the future because the semantics are rather
// strange, and don't work like "normal" partition tables
// do.
// string command = sformat
// ("/usr/sbin/parted -s %1 set %2 boot on", mbr_dev, num);
// As a workaround for #167602, moved back to
// /sbin/activate, because it does not cause the kernel to
// forget about an activated extended partition (it changes
// the data on disk without using any ioctl).
// FIXME: investigate proper handling of the activate flag
// (kernel ioctls in parted etc.) and fix parted
string command = sformat
("/usr/sbin/parted -s %1 set %2 boot on", mbr_dev, num);
y2milestone ("Running command %1", command);
map out = (map)SCR::Execute (.target.bash_output, command);
integer exit = out["exit"]:0;
y2milestone ("Command output: %1", out);
ret = ret && (0 == exit);
command = sformat ("/usr/sbin/fix_chs %1 %2",
mbr_dev,
num);
y2milestone ("Running command %1", command);
out = (map)SCR::Execute (.target.bash_output, command);
exit = out["exit"]:0;
y2milestone ("Command output: %1", out);
ret = ret && (0 == exit);
}
}
else
{
y2error ("Cannot activate %1", m_activate);
}
});
}
return ret;
}
/**
* Rewrite current MBR with /var/lib/YaST2/backup_boot_sectors/%device
* Warning!!! don't use for bootsectors, 446 bytes of sector are written
* @param device string device to rewrite MBR to
* @return boolean true on success
*/
global define boolean restoreMBR (string device) ``{
string device_file = mergestring (splitstring (device, "/"), "_");
if (SCR::Read (.target.size, sformat ("/var/lib/YaST2/backup_boot_sectors/%1", device_file))
<= 0)
{
Report::Error ("Can't restore MBR. No saved MBR found");
return false;
}
integer ret = (integer) SCR::Execute (.target.bash, sformat (
"/bin/dd of=%1 if=/var/lib/YaST2/backup_boot_sectors/%2 bs=446 count=1",
device, device_file));
return (ret == 0);
}
/**
* Translate device name to major/minor number
* @param device string
* @return string major*256+minor hexadecimal without leading 0x, in case
* of any fail return unchanged device node
*/
global define string dev2majmin (string device) ``{
map exec_ret = (map) SCR::Execute (.target.bash_output, sformat (
"ls -ln %1", device));
if (exec_ret["exit"]:1 != 0 && Stage::initial ())
{
exec_ret = (map) WFM::Execute (.local.bash_output, sformat (
"ls -ln %1", device));
}
if (exec_ret["exit"]:1 != 0)
return device;
string ls = exec_ret["stdout"]:"";
list<string> parts = splitstring (ls, " ");
parts = filter (string p, parts, ``(p != ""));
string majs = parts[4]:"";
majs = substring (majs, 0, size (majs) -1);
string mins = parts[5]:"";
if (majs == "" || majs == nil || mins == "" || mins == nil)
return device;
integer maj = tointeger (majs);
integer min = tointeger (mins);
majs = tohexstring (maj);
majs = substring (majs, 2);
while (size (majs) < 2)
majs = "0" + majs;
mins = tohexstring (min);
mins = substring (mins, 2);
while (size (mins) < 2)
mins = "0" + mins;
y2debug ("Translated %1 to %2:%3", device, majs, mins);
string ret = sformat ("%1%2", majs, mins);
return ret;
}
/**
* Translate device name to major/minor number if the device is not
* "usual"
* @param device string
* @return string the original device parameter if it is "usual",
* same as dev2majmin otherwise
*/
global define string dev2majminIfNonStandard (string device) ``{
// don't use major/minor numbers for LVM and MD (seems to be able
// to make system unbootable
if (Storage::CheckForLvmRootFs() || Storage::CheckForEvmsRootFs ()
|| Storage::CheckForMdRootFs())
{
return device;
}
// FIXME: this is broken, detection by name is deprecated
if (regexpmatch (device, "^/dev/hd.+$")
|| regexpmatch (device, "^/dev/sd.+$")
|| regexpmatch (device, "^/dev/ataraid/.+$"))
{
return device;
}
return dev2majmin (device);
}
/**
* Convert any to boolean
* @param v any value
* @return boolean retyped value
*/
global define boolean tobool (any v) ``{
if (v == true)
return true;
else
return false;
}
/**
* Update kernel parameters if some were added in Kernel module
* @param orig original kernel parameters or kernel command line
* @return kernel command line or parameters with added new parameters
*/
global define string UpdateKernelParams (string orig) ``{
list<string> new = splitstring (Kernel::GetCmdLine (), " ");
list<string> old = splitstring (orig, " ");
list<string> added = (list<string>)BootCommon::difflist (new,
splitstring (BootCommon::kernelCmdLine, " "));
added = (list<string>)BootCommon::difflist (added, old);
old = (list<string>) merge (old, added);
if (Stage::initial ())
{// move showopts apic to the end
boolean showopts = false;
boolean apic = false;
if (contains (old, "showopts"))
showopts = true;
if (contains (old, "apic"))
apic = true;
old = filter (string o, old, ``(o != "apic" && o != "showopts"));
if (showopts)
old = add (old, "showopts");
if (apic)
old = add (old, "apic");
}
return mergestring (old, " ");
}
/**
* Check whether settings were changed and if yes, ask for exit
* without saving
* @return true if shall exit
*/
global define boolean confirmAbort () ``{
if ((! changed) || confirmAbortPopup ())
return true;
else
return false;
}
/**
* Get map of swap partitions
* @return a map where key is partition name and value its size
*/
global define map<string, integer> getSwapPartitions () ``{
map<string, map> tm = (map<string,map>)Storage::GetTargetMap ();
boolean installation = Mode::installation ();
map<string, integer> ret = $[];
foreach (string k, map v, tm, ``{
integer cyl_size = v["cyl_size"]:0;
list<map<string,any> > partitions = v["partitions"]:[];
partitions = filter(map<string,any> p, partitions, {
return p["mount"]:"" == "swap" && ! p["delete"]:false;
});
foreach (map<string, any> s, partitions, ``{
string dev = (string)(s["device"]:"");
ret[dev] = (integer)(s["region", 1]:0) * cyl_size;
});
});
y2milestone ("Available swap partitions: %1", ret);
return ret;
}
/**
* Get the name of the largest available swap partition
* @return string name of the largest partition
*/
global define 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]:"";
}
/**
* Create translated name of a section
* @param orig string original section name
* @return translated section name
*/
global define string translateSectionTitle (string orig) ``{
return GfxMenu::translateSectionTitle(orig,
getLoaderType(false));
}
/**
* Check if device is MBR of a disk
* @param device string device to check
* @return boolean true if is MBR
*/
global define boolean IsMbr (string device) ``{
if (regexpmatch (device, "^\/dev\/[abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ]+$"))
return true;
if (regexpmatch (device, "^\/dev\/[abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ]+\/.*d[0-9]+$"))
return true;
return false;
}
/**
* Add '(MBR)' to the disk description if it is a MBR of some partition
* @param descr string disk description
* @param device string disk device
* @return string updated description
*/
global define string AddMbrToDescription (string descr, string device) ``{
return IsMbr (device)
? sformat ("%1 (MBR)", descr)
: descr;
}
/**
* Update the Kernel::vgaType value to the saved one if not defined
*/
global define void UpdateInstallationKernelParameters () ``{
map<string,any> saved_params = $[];
if (! Stage::initial ())
{
saved_params = (map<string,any>)SCR::Read (
.target.ycp, "/var/lib/YaST2/bootloader.ycp");
}
if (Kernel::GetVgaType () == "")
{
string vgaType = (string)(saved_params["vga"]:"");
if (vgaType != nil && vgaType != "")
Kernel::SetVgaType (vgaType);
}
if (! Stage::initial ())
{
Kernel::SetCmdLine (saved_params["installation_kernel_params"]:"");
}
else
{
if (SCR::Read (.etc.install_inf.NoPCMCIA) == "1")
{
Kernel::SetCmdLine (Kernel::GetCmdLine () + " NOPCMCIA");
}
}
}
/**
* Get additional kernel parameters
* @return additional kernel parameters
*/
global define string GetAdditionalFailsafeParams () ``{
if (Stage::initial ())
{
additional_failsafe_params =
SCR::Read (.etc.install_inf.NoPCMCIA) == "1"
? " NOPCMCIA "
: "";
}
else
{
map<string,any> saved_params = (map<string,any>)SCR::Read (
.target.ycp, "/var/lib/YaST2/bootloader.ycp");
additional_failsafe_params
= saved_params["additional_failsafe_params"]:"";
}
return additional_failsafe_params;
}
/**
* Get additional kernel parameters from control file
* @return string additional kernel parameters
*/
global string GetAdditionalKernelParams () {
return ProductFeatures::GetStringFeature (
"globals",
"additional_kernel_parameters");
}
/**
* Get additional kernel parameters splitted to a list
* @return a list of additional kernel parameters
*/
global list<string> ListAdditionalKernelParams () {
return ListKernelParamsInLine (GetAdditionalKernelParams ());
}
/**
* Update graphical bootloader to contain help text of current language
* And make the selected installation language default
* @return boolean true on success
*/
global define boolean UpdateGfxMenuContents () {
return GfxMenu::UpdateGfxMenuContents(getLoaderType(false));
}
/**
* Transform the selected linux section to section for previous kernel
* @param section a list representing the bootloader section
* @return a list representing the updated section
*/
global map<string,any> Linux2Previous (map<string,any> section) {
boolean intern_found = false;
section["name"] = translateSectionTitle ("previous");
section["original_name"] = "previous";
section["kernel"] = regexpsub (
section["kernel"]:"",
sformat ("^(.*)%1[^ ]*(.*)$", Kernel::GetBinary ()),
sformat ("\\1%1.previous\\2", Kernel::GetBinary()));
if (haskey (section, "initrd"))
{
section["initrd"] = regexpsub (
section["initrd"]:"",
"^(.*)initrd[^ ]*(.*)$",
"\\1initrd.previous\\2");
}
return section;
}
/**
* Update device name according to changes in kernel (eg. SATA)
* @param device string the original device name
* @return string updated device
*/
global string UpdateDevice (string device) {
if (Mode::test ())
{
map mapping = $[
"/dev/hda" : "/dev/sda",
"/dev/hdb" : "/dev/sdb",
];
map d = Storage::GetDiskPartition( device );
if( haskey( mapping, d["disk"]:"" ))
{
if (d["nr"]:nil == nil || d["nr"]:nil == 0)
{
device = mapping[d["disk"]:""]:"";
}
else
{
device = Storage::GetDeviceName(
mapping[d["disk"]:""]:"",
d["nr"]:nil);
}
}
}
else
{
list<string> devices = Storage::GetTranslatedDevices (
installed_version,
update_version,
[ device ]);
device = devices[0]:device;
}
return device;
}
/**
* Check if memtest86 is present
* @return boolean true if memtest86 section is to be proposed
*/
global boolean MemtestPresent () {
return (! contains (removed_sections, "memtest"))
&& (Mode::test ()
|| (Mode::normal () && Pkg::IsProvided ("memtest86"))
|| (! Mode::normal () && Pkg::IsSelected ("memtest86")));
}
/**
* 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" : version,
"kernel" : sformat ("/boot/%1", k)
];
if (contains (files, sformat ("initrd-%1", version)))
{
info["initrd"] = sformat ("/boot/initrd-%1", version);
}
return info;
});
y2milestone ("Additional sectinos to propose: %1", ret);
return ret;
}
/**
* Check if the bootloader can be installed at all with current configuration
* @return boolean true if it can
*/
global boolean BootloaderInstallable () {
if (Mode::config ())
return true;
if (Arch::i386 () || Arch::x86_64 ())
// the only relevant is the partition holding the /boot filesystem
{
DetectDisks ();
y2milestone ("Boot partition device: %1",
BootCommon::BootPartitionDevice);
map dev = Storage::GetDiskPartition( BootCommon::BootPartitionDevice);
y2milestone ("Disk info: %1", dev);
// MD, but not mirroring is OK
// FIXME: type detection by name deprecated
if (dev["disk"]:"" == "/dev/md")
{
map tm = Storage::GetTargetMap ();
map md = tm["/dev/md"]:$[];
list<map> parts = md["partitions"]:[];
map info = $[];
foreach (map p, parts, {
if (p["device"]:"" == BootPartitionDevice)
info = p;
});
if (tolower (info["raid_type"]:"") != "raid1")
{
y2milestone ("Cannot install bootloader on RAID (not mirror)");
return false;
}
}
// EVMS
// FIXME: type detection by name deprecated
else if (search (BootPartitionDevice, "/dev/evms/") == 0)
{
y2milestone ("Cannot install bootloader on EVMS");
return false;
}
// LVM
else if (! is( dev["nr"]:(any)0, integer ))
{
y2milestone ("Cannot install bootloader on LVM");
return false;
}
return true;
}
else
{
return true;
}
}
} //end of include