diff --git a/pyanaconda/bootloader.py b/pyanaconda/bootloader.py index 3748d7f..3b5c21d 100644 --- a/pyanaconda/bootloader.py +++ b/pyanaconda/bootloader.py @@ -1,36 +1,37 @@ +# bootloader.py +# Anaconda's bootloader configuration module. # -# bootloader.py: anaconda bootloader shims +# Copyright (C) 2011 Red Hat, Inc. # -# Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006 Red Hat, Inc. -# All rights reserved. +# This copyrighted material is made available to anyone wishing to use, +# modify, copy, or redistribute it subject to the terms and conditions of +# the GNU General Public License v.2, or (at your option) any later version. +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY expressed or implied, including the implied warranties of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +# Public License for more details. You should have received a copy of the +# GNU General Public License along with this program; if not, write to the +# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301, USA. Any Red Hat trademarks that are incorporated in the +# source code or documentation are not subject to the GNU General Public +# License and may only be used or replicated with the express permission of +# Red Hat, Inc. # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -# Author(s): Erik Troan -# Jeremy Katz +# Red Hat Author(s): David Lehman # -import isys -import parted -import os, sys -import iutil -import string -from flags import flags -from constants import * -from storage.devices import devicePathToName -from storage import getReleaseString -from booty.util import getDiskPart +import os +import re +import struct + +from pyanaconda import platform +from pyanaconda import iutil + +from pyanaconda.product import productName +from pyanaconda.flags import flags +from pyanaconda.storage.devicelibs import mdraid +import pyanaconda.storage.errors +from pyanaconda.isys import sync import gettext _ = lambda x: gettext.ldgettext("anaconda", x) @@ -38,208 +39,1473 @@ _ = lambda x: gettext.ldgettext("anaconda", x) import logging log = logging.getLogger("anaconda") -import booty -from booty import bootloaderInfo, checkbootloader -def isEfiSystemPartition(part): - if not part.active: +def get_boot_block(device, seek_blocks=0): + block = " " * 512 + status = device.status + if not status: + try: + device.setup() + except pyanaconda.storage.errors.StorageError: + return block + fd = os.open(device.path, os.O_RDONLY) + if seek_blocks: + os.lseek(fd, seek_blocks * 512, 0) + block = os.read(fd, 512) + os.close(fd) + if not status: + try: + device.teardown(recursive=True) + except pyanaconda.storage.errors.StorageError: + pass + + return block + +def is_windows_boot_block(block): + return (len(block) >= 512 and + struct.unpack("H", buf[0x1fe: 0x200]) == (0xaa55,)) + + +class BootLoaderError(Exception): + pass + + +class ArgumentList(list): + def _is_match(self, item, value): + try: + _item = item.split("=")[0] + except (ValueError, AttributeError): + _item = item + + try: + _value = value.split("=")[0] + except (ValueError, AttributeError): + _value = value + + return _item == _value + + def __contains__(self, value): + return self.count(value) != 0 + + def count(self, value): + c = 0 + for arg in self: + if self._is_match(arg, value): + c += 1 + + return c + + def index(self, value): + for i in range(len(self)): + if self._is_match(self[i], value): + return i + + raise ValueError("'%s' is not in list" % value) + + +class BootLoaderImage(object): + """ Base class for bootloader images. Suitable for non-linux OS images. """ + def __init__(self, device=None, label=None): + self.label = label + self.device = device + + +class LinuxBootLoaderImage(BootLoaderImage): + def __init__(self, device=None, label=None, short=None, version=None, + kernel=None, initrd=None, args=None): + self.label = label # label string + self.short_label = short # shorter label string + self.device = device # StorageDevice instance + self.version = version # kernel version string + self.kernel = kernel # filename string + self.initrd = initrd # filename string + + if isinstance(args, list): + self.boot_args = ArgumentList(args) + elif isinstance(args, str): + self.boot_args = ArgumentList(args.split()) + + @property + def kernel(self): + filename = self.kernel + if self.version and not filename: + filename = "vmlinuz-%s" % self.version + return filename + + @property + def initrd(self): + filename = self.initrd + if self.version and not filename: + filename = "initramfs-%s.img" % self.version + return filename + + +# This is for bootloader detection loops to look for config files or perhaps +# some kind of bootloader factory. Currently it isn't used. +bootloader_classes = {} + +class BootLoader(object): + """TODO: + - make a base class for Yaboot and SILO to inherit from + - iSeries bootloader? + - writeKS + - resolve overlap between Platform checkFoo methods and + _is_valid_target_device and _device_is_bootable + """ + name = "Generic Bootloader" + arches = [] + packages = [] + config_file = None + config_file_mode = 0600 + can_dual_boot = False + image_label_attr = "label" + + # requirements for bootloader target devices + target_device_types = ["partition"] + target_device_raid_levels = [] + target_device_format_types = [] + target_device_disklabel_types = ["msdos"] + target_device_mountpoints = [] + target_device_min_size = None + target_device_max_size = None + + # requirements for boot devices + boot_device_types = ["partition"] + boot_device_raid_levels = [] + boot_device_format_types = [] + + # linux-specific requirements for boot devices + linux_boot_device_format_types = ["ext4", "ext3", "ext2"] + linux_boot_device_mountpoints = ["/boot", "/"] + linux_boot_device_min_size = 50 + linux_boot_device_max_size = None + + def __init__(self, storage=storage): + if storage.platform.bootloader != self.name: + raise ValueError("wrong bootloader for this platform") + + # pyanaconda.storage.Storage instance + self.storage = storage + + self._drives = [] + self._drive_order = [] + + # timeout in seconds + self._timeout = None + + # XXX abstract crypto? + self.password = None + + # console/serial stuff + self.console = "" + self.console_options = "" + self.set_console() + + # list of BootLoaderImage instances representing bootable OSs + self.linux_images = [] + self.chain_images = [] + + # default image + self._default_image = None + + # the device the bootloader will be installed on + self._target_device = None + + self.kernel_args = [] + + # + # target device access + # + @property + def stage1_device(self): + """ Stage1 target device. """ + if not self._target_device: + self._target_device = self.target_devices[0] + + return self._target_device + + @stage1_device.setter + def stage1_device(self, device): + if not self._is_valid_target_device(device): + raise ValueError("%s is not a valid target device" % device.name) + + self._target_device = device + + @property + def stage2_device(self): + """ Stage2 target device. """ + return self.storage.mountpoints.get("/boot", self.storage.rootDevice) + + # + # drive list access + # + @property + def drive_order(self): + """Potentially partial order for drives.""" + return self._drive_order + + @drive_order.setter + def drive_order(self, order): + self._drive_order = order + self._drives = [] # this will get regenerated on next access + + def _sort_drives(self, drives): + """Sort drives based on the drive order.""" + _drives = drives[:] + for name in reversed(self._drive_order): + try: + idx = [d.name for d in _drives].index(name) + except ValueError: + log.error("bios order specified unknown drive %s" % name) + continue + + if idx == 0: + # if this one's already at the front there's nothing to do + continue + + new_drives = [_drives.pop(idx)] + new_drives.extend(_drives) + _drives = new_drives + + return _drives + + @property + def drives(self): + """Sorted list of available drives.""" + if self._drives: + # only generate the list if it is empty + return self._drives + + drives = [d for d in storage.disks if d.partitioned] + self._drives = self._sort_drives(drives) + return self._drives + + # + # image list access + # + @property + def default(self): + """The default image.""" + if not self._default_image and self.linux_images: + self._default_image = self.linux_images[0] + + return self._default_image + + @default.setter + def default(self, image): + if image not in self.images: + raise ValueError("new default image not in image list") + + self._default_image = image + + @property + def images(self): + """ List of OS images that will be included in the configuration. """ + if not self.linux_images: + default = LinuxBootLoaderImage(device=self.storage.rootDevice, + label=productName, + short="linux") + self.linux_images.append(default) + + all_images = self.linux_images + all_images.extend([i for i in self.chain_images if i.label]) + return all_images + + def clear_images(self): + """Empty out the image list.""" + self.linux_images = [] + self.chain_images = [] + + def add_image(self, image): + """Add a BootLoaderImage instance to the image list.""" + if isinstance(image, LinuxBootLoaderImage): + self.linux_images.append(image) + else: + self.chain_images.append(image) + + def image_label(self, image): + """Return the appropriate image label for this bootloader.""" + return getattr(image, self.image_label_attr) + + def _find_chain_images(self): + """ Collect a list of potential non-linux OS installations. """ + images = [] + + if not self.can_dual_boot: + return + + for partition in self.storage.partitions: + if not partition.exists: + continue + + if self._device_is_bootable(partition): + image = BootLoaderImage(device=partition) + self.chain_images.append(image) + + # + # target/stage1 device access + # + def _is_valid_target_device(self, device): + """ Return True if the device is a valid stage1 target device. + + The criteria for being a valid stage1 target device vary from + platform to platform. On some platforms a disk with an msdos + disklabel is a valid stage1 target, while some platforms require + a special device. Some examples of these special devices are EFI + system partitions on EFI machines, PReP boot partitions on + iSeries, and Apple bootstrap partitions on Mac. """ + if device.type not in self.target_device_types: + return False + + if (self.target_device_min_size is not None and + device.size < self.target_device_min_size): + return False + + if (self.target_device_max_size is not None and + device.size > self.target_device_max_size): + return False + + if not device.exists: + return False + + if not getattr(device, "bootable", True) or \ + (hasattr(device, "partedPartition") and + not device.partedPartition.active): + return False + + if getattr(device.format, "label", None) == "ANACONDA": + return False + + if device.format.type not self.target_device_format_types: + return False + + if device.format.type == "disklabel" or device.type == "partition": + if device.format.type == "disklabel": + label_type = device.format.labelType + else: + label_type = device.disk.format.labelType + + if label_type not in self.target_device_disklabel_types: + return False + + if hasattr(device.format, "mountpoint") and \ + device.format.mountpoint not in self.target_device_mountpoints: + return False + + return True + + @property + def target_devices(self): + """ A list of valid stage1 target devices. + + The list self.target_device_types is ordered, so we return a list + of all valid target devices, sorted by device type, then sorted + according to our drive ordering. + """ + slots = [[] for t in self.target_device_types] + for device in self.storage.devices: + try: + idx = self.target_device_types.index(device.type) + except ValueError: + continue + + if self._is_valid_target_device(device): + slots[idx].append(device) + + devices = [] + for slot in slots: + devices.extend(slot) + + return self._sort_drives(devices) + + # + # boot/stage2 device access + # + + def _device_is_bootable(self, device, linux=False): + """ Return True if the specified device might contain an OS image. """ + if device.type not in self.boot_device_types: + return False + + if device.type == "mdarray" and \ + device.level not in self.boot_device_raid_levels: + # TODO: also check metadata version, as ridiculous as that is + return False + + if not self._bootloader_device_present(): + # XXX is this really a dealbreaker? + return False + + if hasattr(device, "partedPartition") and \ + (not device.bootable or not device.partedPartition.active) and \ + not check_for_windows_boot_block(device): + return False + + if linux: + format_types = self.linux_boot_device_format_types + mountpoint = getattr(device.format, "mountpoint", None) + if mountpoint not in self.linux_boot_device_mountpoints: + return False + else: + format_types = self.boot_device_format_types + + return device.format.type in format_types + + @property + def bootable_chain_devices(self): + """ Potential boot devices containing non-linux operating systems. """ + return [d for d in self.storage.devices if self._device_is_bootable(d)] + + @property + def bootable_linux_devices(self): + """ Potential boot devices containing linux operating systems. """ + return [d for d in self.storage.devices + if self._device_is_bootable(d, linux=True)] + + # + # miscellaneous + # + + @property + def has_windows(self): return False - return (part.disk.type == "gpt" and - part.name == "EFI System Partition" and - part.getFlag(parted.PARTITION_BOOT) and - part.fileSystem.type in ("fat16", "fat32") and - isys.readFSLabel(part.getDeviceNodeName()) != "ANACONDA") -def bootloaderSetupChoices(anaconda): - if anaconda.dir == DISPATCH_BACK: - rc = anaconda.intf.messageWindow(_("Warning"), - _("Filesystems have already been activated. You " - "cannot go back past this point.\n\nWould you like to " - "continue with the installation?"), - type="custom", custom_icon=["error","error"], - custom_buttons=[_("_Exit installer"), _("_Continue")]) + @property + def timeout(self): + """Bootloader timeout in seconds.""" + if self._timeout is not None: + t = self._timeout + elif self.console and self.console.startswith("ttyS"): + t = 5 + else: + t = 20 - if rc == 0: - sys.exit(0) - return DISPATCH_FORWARD + return t - if anaconda.ksdata and anaconda.ksdata.bootloader.driveorder: - anaconda.bootloader.updateDriveList(anaconda.ksdata.bootloader.driveorder) - else: - #We want the selected bootloader drive to be preferred - pref = anaconda.bootloader.drivelist[:1] - anaconda.bootloader.updateDriveList(pref) - - if iutil.isEfi() and not anaconda.bootloader.device: - bootPart = None - partitions = anaconda.storage.partitions - for part in partitions: - if part.partedPartition.active and isEfiSystemPartition(part.partedPartition): - bootPart = part.name - break - if bootPart: - anaconda.bootloader.setDevice(bootPart) - -# iSeries bootloader on upgrades - if iutil.getPPCMachine() == "iSeries" and not anaconda.bootloader.device: - bootPart = None - partitions = anaconda.storage.partitions - for part in partitions: - if part.partedPartition.active and \ - part.partedPartition.getFlag(parted.PARTITION_PREP): - bootPart = part.name - break - if bootPart: - anaconda.bootloader.setDevice(bootPart) - - choices = anaconda.platform.bootloaderChoices(anaconda.bootloader) - if not choices and iutil.getPPCMachine() != "iSeries": - anaconda.dispatch.skipStep("instbootloader") - else: - anaconda.dispatch.skipStep("instbootloader", skip = 0) - - # FIXME: ... - anaconda.bootloader.images.setup(anaconda.storage) - - if anaconda.bootloader.defaultDevice != None and choices: - keys = choices.keys() - # there are only two possible things that can be in the keys - # mbr and boot. boot is ALWAYS present. so if the dev isn't - # listed, it was mbr and we should nicely fall back to boot - if anaconda.bootloader.defaultDevice not in keys: - log.warning("MBR not suitable as boot device; installing to partition") - anaconda.bootloader.defaultDevice = "boot" - anaconda.bootloader.setDevice(choices[anaconda.bootloader.defaultDevice][0]) - elif choices and iutil.isMactel() and choices.has_key("boot"): # haccckkkk - anaconda.bootloader.setDevice(choices["boot"][0]) - elif choices and choices.has_key("mbr"): - anaconda.bootloader.setDevice(choices["mbr"][0]) - elif choices and choices.has_key("boot"): - anaconda.bootloader.setDevice(choices["boot"][0]) - -def fixedMdraidGrubTarget(anaconda, grubTarget): - # handle change made in F12 - before F12 mdX used to mean installation - # into mbrs of mdX members' disks - fixedGrubTarget = grubTarget - (product, version) = getReleaseString(anaconda.rootPath) - try: - if float(version) < 12: - stage1Devs = anaconda.bootloader.getPhysicalDevices(grubTarget) - fixedGrubTarget = getDiskPart(stage1Devs[0])[0] - log.info("Mdraid grub upgrade: %s -> %s" % (grubTarget.name, - fixedGrubTarget.name)) - except ValueError: - log.warning("Can't decide mdraid grub upgrade fix, product: %s, version: %s" % (product, version)) + @timeout.setter + def timeout(self, seconds): + self._timeout = seconds - return fixedGrubTarget + # + # configuration + # -def writeBootloader(anaconda): - def dosync(): - isys.sync() - isys.sync() - isys.sync() + def _set_console(self): + """ Set console options based on boot arguments. """ + if flags.serial: + console = flags.cmdline.get("console", "ttyS0").split(",", 1) + self.console = console[0] + if len(console) > 1: + self.console_options = console[1] + elif flags.virtpconsole: + self.console = re.sub("^/dev/", "", flags.virtpconsole) - if anaconda.bootloader.defaultDevice == -1: - return + def write_config_console(self, config): + """Write console-related configuration lines.""" + pass + + def write_config_password(self, config): + """Write password-related configuration lines.""" + if self.password: + config.write("password=%s\n" % self.password) + + def write_config_header(self, config): + """Write global configuration lines.""" + self.write_config_console() + self.write_config_password() + + def write_config_images(self, config): + """Write image configuration entries.""" + # XXX might this be identical for yaboot and silo? + raise NotImplementedError() + + def write_config_post(self, install_root=""): + try: + os.chmod(install_root + self.config_file, self.config_file_mode) + except OSError as e: + log.error("failed to set config file permissions: %s" % e) + + def write_config(self, install_root=""): + """ Write the bootloader configuration. """ + if not self.config_file: + raise BootLoaderError("no config file defined for this bootloader") - if anaconda.bootloader.doUpgradeOnly: - (bootType, theDev) = checkbootloader.getBootloaderTypeAndBoot(anaconda.rootPath, storage=anaconda.storage) - - anaconda.bootloader.doUpgradeonly = 1 - if bootType == "GRUB": - grubTarget = anaconda.storage.devicetree.getDeviceByPath(theDev) - if grubTarget.type == "mdarray": - grubTarget = fixedMdraidGrubTarget(anaconda, grubTarget) - anaconda.bootloader.useGrubVal = 1 - anaconda.bootloader.setDevice(grubTarget.name) + config_path = os.path.normpath(install_root + self.config_file) + if os.access(config_path, os.R_OK): + os.rename(config_path, config_path + ".rpmsave") + + config = open(config_path, "w") + self.write_config_header(config, install_root=install_root) + self.write_config_images(config) + config.close() + self.write_config_post(install_root=install_root) + + def writeKS(self, f): + """ Write bootloader section of kickstart configuration. """ + raise NotImplementedError() + + def read(self): + """ Read an existing bootloader configuration. """ + raise NotImplementedError() + + # + # installation + # + def write(self, install_root=""): + """ Write the bootloader configuration and install the bootloader. """ + self.write_config(install_root=install_root) + sync() + sync() + sync() + sync() + self.install(install_root=install_root) + + def update(self, install_root=""): + """ Update an existing bootloader configuration. """ + pass + + +bootloader_classes[BootLoader.name] = BootLoader + + +class GRUB(BootLoader): + name = "GRUB" + arches = ["i[3456]86", "x86_64"] + _config_dir = "grub" + _config_file = "grub.conf" + _device_map_file = "device.map" + can_dual_boot = True + + # list of strings representing options for bootloader target device types + target_device_types = ["disk", "partition"] + target_device_raid_levels = [] + target_device_format_types = [] + target_device_disklabel_types = ["msdos", "gpt"] # gpt? + + # list of strings representing options for boot device types + boot_device_types = ["partition", "mdarray"] + boot_device_raid_levels = [mdraid.RAID1] + # XXX hpfs, if reported by blkid/udev, will end up with a type of None + boot_device_format_types = ["vfat", "ntfs", "hpfs"] + + packages = ["grub"] + + def __init__(self, storage): + super(GRUB, self).__init__(storage) + + # + # grub-related conveniences + # + + def grub_device_name(device): + """ Return a grub-friendly representation of device. """ + drive = getattr(device, "disk", device) + name = "(hd%d" % self.drives.index(drive) + if hasattr(device, "disk"): + name += ",%d" % (device.partedPartition.number - 1,) + name += ")" + return name + + @property + def grub_prefix(self): + """ Prefix, if any, to paths in /boot. """ + stage2dev = self.stage2_device + if stage2dev == self.storage.rootDevice: + prefix = "/boot" else: - anaconda.bootloader.doUpgradeOnly = 0 + prefix = "" - w = anaconda.intf.waitWindow(_("Bootloader"), _("Installing bootloader.")) + return prefix - kernelList = [] - otherList = [] - # getDefault needs to return a device, but that's too invasive for now. - rootDev = anaconda.storage.rootDevice + @property + def grub_config_dir(self): + """ Config dir, adjusted for grub's view of the world. """ + return self.grub_prefix + self._config_dir - if not anaconda.bootloader.images.getDefault(): - defaultDev = None - else: - defaultDev = anaconda.storage.devicetree.getDeviceByName(anaconda.bootloader.images.getDefault()) + # + # configuration + # - kernelLabel = None - kernelLongLabel = None + @property + def config_dir(self): + """ Full path to configuration directory. """ + return "/boot" + self._config_dir - for (dev, (label, longlabel, type)) in anaconda.bootloader.images.getImages().items(): - if (rootDev is None and kernelLabel is None) or (dev == rootDev.name): - kernelLabel = label - kernelLongLabel = longlabel - elif (not defaultDev and not dev) or (defaultDev and dev == defaultDev.name): - otherList = [(label, longlabel, dev)] + otherList - else: - otherList.append((label, longlabel, dev)) + @property + def config_file(self): + """ Full path to configuration file. """ + return "%s/%s" % (self.config_dir, self._config_file) - if kernelLabel is None: - log.error("unable to find default image, bailing") - w.pop() - return + @property + def device_map_file(self): + """ Full path to device.map file. """ + return "%s/%s" % (self.config_dir, self._device_map_file) + + @property + def grub_conf_device_line(self): + return "" + + def write_config_console(self, config): + """ Write console-related configuration. """ + if not self.console: + return + + if self.console.startswith("ttyS"): + unit = self.console[-1] + speed = "9600" + for opt in self.console_options.split(","): + if opt.isdigit: + speed = opt + break + + config.write("serial --unit=%s --speed=%s\n" % (unit, speed)) + config.write("terminal --timeout=%s serial console\n" % self.timeout) + + console_arg = "console=%s" % self.console + if self.console_options: + console_arg += ",%s" % self.console_options + self.kernel_args.append(console_arg) + + def write_config_password(self, config): + """ Write password-related configuration. """ + if self.password: + # FIXME: implement encryption + config.write("password --encrypted %s\n" % self.password) + + def write_config_header(self, config, install_root=""): + """Write global configuration information. """ + if self.grub_prefix: + have_boot = "do not " + else: + have_boot = "" + + s = """# grub.conf generated by anaconda\n" +# Note that you do not have to rerun grub after making changes to this file. +# NOTICE: You %(do)shave a /boot partition. This means that all kernel and +# initrd paths are relative to %(boot)s, eg. +# root %(grub_target)s +# kernel %(prefix)s/vmlinuz-version ro root=%(root_device)s +# initrd %(prefix)s/initrd-[generic-]version.img +""" % {"do": have_boot, "boot": self.stage2_device.format.mountpoint, + "root_device": self.stage2_device.path + "grub_target": self.grub_device_name(self.stage1_device), + "prefix": self.grub_prefix} + + config.write(s) + config.write("boot=%s\n" % self.stage1_device.path + config.write(self.grub_conf_device_line) + + # find the index of the default image + try: + default_index = self.images.index(self.default) + except ValueError: + e = "Failed to find default image (%s)" % self.default.label + raise BootLoaderError(e) + + config.write("default=%d\n" % default_index) + config.write("timeout=%d\n" % self.timeout) + + self.write_config_console(config) + + if not flags.serial: + splash = "splash.xpm.gz" + splash_path = os.path.normpath("%s%s/%s" % (install_root, + self.config_dir, + splash)) + if os.access(splash_path, os.R_OK): + grub_root_grub_name = self.grub_device_name(self.stage2_device) + f.write("splashimage=%s%s/%s\n" % (grub_root_grub_name, + self.grub_config_dir, + splash)) + f.write("hiddenmenu\n") + + self.write_config_password() + + def write_config_images(self, config): + """ Write image entries into configuration file. """ + for image in self.images: + if isinstance(image, LinuxBootLoaderImage): + kernel_args = ["ro", "root=%s" % image.device.fstabSpec] + kernel_args.extend(self.kernel_args) + stanza = ("title %(label)s (%(version)s)\n" + "\troot %(grub_root)s\n" + "\tkernel %(prefix)s%(kernel)s %(args)s\n" + "\tinitrd %(prefix)s%(initrd)s\n" + % {"label": image.label, "version": image.version, + "grub_root": self.grub_device_name(image.device), + "kernel": image.kernel, + "args": " ".join(kernel_args), + "prefix": self.grub_prefix}) + else: + stanza = ("title %(label)s\n" + "\trootnoverify %(grub_root)s\n" + "\tchainloader +1\n" + % {"label": image.label, + "grub_root": self.grub_device_name(image.device)}) + + config.write(stanza) + + def write_device_map(self, install_root=""): + """ Write out a device map containing all supported devices. """ + map_path = os.path.normpath(install_root + self.device_map_file) + if os.access(map_path, os.R_OK): + os.rename(map_path, map_path + ".rpmsave") + + dev_map = open(map_path, "w") + dev_map.write("# this device map was generated by anaconda\n") + for drive in self.drives: + dev_map.write("(hd%d) %s\n" % (self.grub_device_name(drive), + drive.path)) + dev_map.close() + + def write_config_post(self, install_root=""): + """ Perform additional configuration after writing config file(s). """ + super(GRUB, self).write_config_post(install_root=install_root) - plainLabelUsed = 0 - defkern = "kernel" - for (version, arch, nick) in \ - anaconda.backend.kernelVersionList(anaconda.rootPath): - if plainLabelUsed: - kernelList.append(("%s-%s" %(kernelLabel, nick), - "%s-%s" %(kernelLongLabel, nick), - version)) - else: - kernelList.append((kernelLabel, kernelLongLabel, version)) - if nick != "base": - defkern = "kernel-%s" %(nick,) - plainLabelUsed = 1 + # make symlink for menu.lst (grub's default config file name) + menu_lst = "%s%s/menu.lst" % (install_root, self.config_dir) + if os.access(menu_lst, os.R_OK): + try: + os.rename(menu_lst, menu_lst + '.rpmsave') + except OSError as e: + log.error("failed to back up %s: %s" % (menu_lst, e)) + try: + os.symlink(self._config_file, menu_lst) + except OSError as e: + log.error("failed to create grub menu.lst symlink: %s" % e) + + # make symlink to grub.conf in /etc since that's where configs belong + etc_grub = "%s/etc/%s" % (install_root, self._config_file) + if os.access(etc_grub, os.R_OK): + try: + os.rename(etc_grub, etc_grub + '.rpmsave') + except OSError as e: + log.error("failed to back up %s: %s" % (etc_grub, e)) + + try: + os.symlink("..%s" % self.config_file, etc_grub) + except OSError as e: + log.error("failed to create /etc/grub.conf symlink: %s" % e) + + def write_config(self, install_root=""): + """ Write bootloader configuration to disk. """ + # write device.map + self.write_device_map(install_root=install_root) + + # this writes the actual configuration file + super(GRUB, self).write_config(install_root=install_root) + + # + # installation + # + + def install(self, install_root=""): + rc = iutil.execWithRedirect("grub-install", ["--just-copy"], + stdout="/dev/tty5", stderr="/dev/tty5", + root=install_root) + if rc: + raise BootLoaderError("bootloader install failed") + + boot = self.stage2_device + targets = [] + if boot.type != "mdarray": + targets.append((self.stage1_device, self.stage2_device)) + else: + if [d for d in self.stage2_device.parents if d.type != "partition"]: + raise BootLoaderError("boot array member devices must be " + "partitions") + + # make sure we have stage1 and stage2 installed with redundancy + # so that boot can succeed even in the event of failure or removal + # of some of the disks containing the member partitions of the + # /boot array + for stage2dev in self.stage2_device.parents: + if self.stage1_device.isDisk: + # install to mbr + if self.stage2_device.dependsOn(self.stage1_device): + # if target disk contains any of /boot array's member + # partitions, set up stage1 on each member's disk + # and stage2 on each member partition + stage1dev = stage2dev.disk + else: + # if target disk does not contain any of /boot array's + # member partitions, install stage1 to the target disk + # and stage2 to each of the member partitions + stage1dev = self.stage1_device + else: + # target is /boot device and /boot is raid, so install + # grub to each of /boot member partitions + stage1dev = stage2dev + + targets.append((stage1dev, stage2dev)) + + for (stage1dev, stage2dev) in targets: + cmd = ("root %(stage2dev)s\n" + "install --stage2=%(config_dir)s/stage2" + " %(grub_config_dir)s/stage1 d %(stage1dev)" + " %(grub_config_dir)s/stage2 p" + " %(stage2dev)s%(grub_config_dir)s/%(config_basename)s\n" + % {"grub_config_dir": self.grub_config_dir, + "config_dir": self.config_dir, + "config_basename": self._config_file, + "stage1dev": self.grub_device(self.stage1_device), + "stage2dev": self.grub_device(self.stage2_device)}) + (pread, pwrite) = os.pipe() + os.write(pwrite, cmd) + os.close(pwrite) + args = ["--batch", "--no-floppy", + "--device-map=%s" % self.device_map_file] + rc = iutil.execWithRedirect("grub", args, + stdout="/dev/tty5", stderr="/dev/tty5", + stdin=pread, root=install_root) + os.close(pread) + if rc: + raise BootLoaderError("bootloader install failed") + + def update(self): + self.install(install_root=install_root) + + # + # miscellaneous + # + + @property + def has_windows(self): + return len(self.bootable_chain_devices) != 0 + +bootloader_classes[GRUB.name] = GRUB + + +class EFIGRUB(GRUB): + name = "GRUB (EFI)" + can_dual_boot = False + _config_dir = "efi/EFI/redhat" + + # bootloader target device types + target_device_types = ["partition", "mdarray"] + target_device_raid_levels = [mdraid.RAID1] + target_device_format_types = ["efi"] + target_device_mountpoints = ["/boot/efi"] + target_device_disklabel_types = ["gpt"] + target_device_min_size = 50 + target_device_max_size = 256 + + # boot device types + boot_device_format_types = [] + + def efibootmgr(self, *args, **kwargs): + if kwargs.pop("capture", False): + exec_func = iutil.execWithCapture + else: + exec_func = iutil.execWithRedirect + + return exec_func("efibootmgr", list(args), **kwargs) + + # + # configuration + # + + @property + def efi_product_path(self): + """ The EFI product path. + + eg: HD(1,800,64000,faacb4ef-e361-455e-bd97-ca33632550c3) + """ + path = "" + buf = self.efibootmgr("-v", stderr="/dev/tty5", capture=True) + matches = re.search(productName + r'\s+HD\(.+?\)', buf) + if matches: + path = re.sub(productName + r'\s+', + '', + buf[matches[0].start():matches[0].end()] + + return path + + @property + def grub_conf_device_line(self): + return "device %s %s\n" % (self.grub_device_name(self.stage2_device), + self.efi_product_path) + + # + # installation + # + + def remove_efi_boot_target(self, install_root=""): + buf = self.efibootmgr(capture=True) + for line in buf.splitlines(): + try: + (slot, product) = line.split(None, 1) + except ValueError: + continue + + if product == productName: + slot_id = slot[4:8] + if not slot_id.isdigit() + log.warning("failed to parse efi boot slot (%s)" % slot) + continue + + rc = self.efibootmgr("-b", slot_id, "-B", + root=install_root, + stdout="/dev/tty5", stderr="/dev/tty5") + if rc: + raise BootLoaderError("failed to remove old efi boot entry") + + def add_efi_boot_target(self, install_root=""): + boot_efi = self.storage.mountpoints["/boot/efi"] + if boot_efi.type == "partition": + boot_disk = boot_efi.disk + boot_part_num = boot_efi.partedPartition.number + elif boot_efi.type == "mdarray": + # FIXME: I'm just guessing here. This probably needs the full + # treatment, ie: multiple targets for each member. + boot_disk = boot_efi.parents[0].disk + boot_part_num = boot_efi.parents[0].partedPartition.number + + rc = self.efibootmgr("-c", "-w", "-L", productName, + "-d", boot_disk.path, "-p", boot_part_num, + "-l", "\\EFI\\redhat\\grub.efi", + root=install_root, + stdout="/dev/tty5", stderr="/dev/tty5") + if rc: + raise BootLoaderError("failed to set new efi boot target") + + def install(self, install_root=""): + self.remove_efi_boot_target(install_root=install_root) + self.add_efi_boot_target(install_root=install_root) + + def update(self, install_root=""): + self.install(install_root=install_root) + +bootloader_classes[EFIGRUB.name] = EFIGRUB + + +class Yaboot(BootLoader): + name = "Yaboot" + arches = ["ppc*"] + _config_file = "yaboot.conf" + prog = "ybin" + + # list of strings representing options for bootloader target device types + target_device_types = ["mbr", "partition", "mdarray"] + target_device_raid_levels = [mdraid.RAID1] + target_device_format_types = ["appleboot", "prepboot"] + + # list of strings representing options for boot device types + boot_device_types = ["partition", "mdarray"] + boot_device_raid_levels = [mdraid.RAID1] + boot_device_format_types = ["hfs", "hfs+"] + + packages = ["yaboot"] + + image_label_attr = "short_label" + + def __init__(self, storage): + BootLoader.__init__(self, storage) + + # + # configuration + # + + @property + def config_dir(self): + conf_dir = "/etc" + if self.stage2_device.format.mountpoint == "/boot": + conf_dir = "/boot/etc" + return conf_dir + + @property + def config_file(self): + return "%s/%s" % (self.config_dir, self._config_file) + + @property + def boot_prefix(self): + if self.stage2_device.format.mountpoint == "/boot": + return "" + else: + return "/boot" + + def write_config_password(self, config): + if self.password: + config.write("password=%s\n" % self.password) + config.write("restricted\n") + + def write_config_header(self, config): + if self.stage2_device.type = "mdarray": + boot_part_num = self.stage2_device.parents[0].partedPartition.number + else: + boot_part_num = self.stage2_device.partedPartition.number + + header = ("# yaboot.conf generated by anaconda\n\n" + "boot=%(stage1dev)s\n" + "init-message=\"Welcome to %(product)s!\\nHit for " + "boot options\"\n\n" + "partition=%(part_num)d\n" + "timeout=%(timeout)d\n" + "install=/usr/lib/yaboot/yaboot\n" + "delay=5\n" + "enablecdboot\n" + "enableofboot\n" + "enablenetboot\n" + % {"stage1dev": self.stage1_device.path, + "product": productName, "part_num": boot_part_num, + "timeout": self.timeout}) + config.write(header) + self.write_variant_header(config) + self.write_config_password(config) + config.write("\n") + + def write_config_variant_header(self, config): + config.write("nonvram\n") + config.write("mntpoint=/boot/yaboot\n") + config.write("usemount\n") + + def write_config_images(self, config): + for image in self.images: + if not isinstance(image, LinuxBootLoaderImage): + # mac os images are handled specially in the header on mac + continue + + args = self.kernel_args[:] + if image.initrd: + initrd_line = "\tinitrd=%s/%s\n" % (self.boot_prefix, + image.initrd) + else: + initrd_line = "" + + root_device_spec = self.storage.rootDevice.fstabSpec + if root_device_spec.startswith("/"): + root_line = "\troot=%s\n" % root_device_spec + else: + args.append("root=%s" % root_device_spec) + root_line = "" + + stanza = ("image=%(boot_prefix)s%(kernel)s\n" + "\tlabel=%(label)s\n" + "\tread-only\n" + "%(initrd_line)s" + "%(root_line)s" + "\tappend=\"%(args)s\"\n\n" + % {"kernel": image.kernel, "initrd_line": initrd_line, + "label": self.image_label(image), + "root_line": root_line, "args": " ".join(args), + "boot_prefix": self.boot_prefix}) + config.write(stanza) + + def write_config_post(self, install_root=""): + super(Yaboot, self).write_config_post(install_root=install_root) + + # make symlink in /etc to yaboot.conf if config is in /boot/etc + etc_yaboot_conf = install_root + "/etc/yaboot.conf" + if not os.access(etc_yaboot_conf, os.R_OK): + try: + os.symlink("../boot/etc/yaboot.conf", etc_yaboot_conf) + except OSError as e: + log.error("failed to create /etc/yaboot.conf symlink: %s" % e) + + def write_config(self, install_root=""): + if not os.path.isdir(install_root + self.config_dir): + os.mkdir(install_root + self.config_dir) + + # this writes the config + super(Yaboot, self).write_config(install_root=install_root) + + # + # installation + # + + def install(self, install_root=""): + args = ["-f", "-C", self.config_file] + rc = iutil.execWithRedirect(self.prog, args, + stdout="/dev/tty5", stderr="/dev/tty5", + root=install_root) + if rc: + raise BootLoaderError("bootloader installation failed") + + +bootloader_classes[Yaboot.name] = Yaboot + + +class IPSeriesYaboot(Yaboot): + # FIXME: is this just for pSeries? + name = "Yaboot (IPSeries)" + prog = "mkofboot" + + target_device_format_types = ["prepboot"] + target_device_min_size = 4 + target_device_max_size = 10 + + # + # configuration + # + + def write_config_variant_header(self, config): + config.write("nonvram\n") # only on pSeries? + config.write("fstype=raw\n") + +bootloader_classes[IPSeriesYaboot.name] = IPSeriesYaboot + + +class MacYaboot(Yaboot): + name = "Yaboot (Mac)" + prog = "mkofboot" + + can_dual_boot = True + target_device_format_types = ["appleboot"] + target_device_disklabel_types = ["mac"] + target_device_min_size = 800.00 / 1024.00 + target_device_max_size = 1 + + # + # configuration + # + + def write_config_variant_header(self, config): + try: + mac_os = [i for i in self.chain_images if i.label][0] + except IndexError: + pass + else: + config.write("macosx=%s\n" % mac_os.device.path) + + config.write("magicboot=/usr/lib/yaboot/ofboot\n") + + +bootloader_classes[MacYaboot.name] = MacYaboot + + +class ZIPL(BootLoader): + name = "ZIPL" + arches = ["s390*"] + config_file = "/etc/zipl.conf" + chandev_config_file = "/etc/chandev.conf" + + # list of strings representing options for bootloader target device types + target_device_types = ["disk", "partition"] + target_device_disklabel_types = ["msdos", "dasd"] + + # list of strings representing options for boot device types + boot_device_types = ["partition", "mdarray", "lvm"] + boot_device_raid_levels = [mdraid.RAID1] + + packages = ["s390utils"] # is this bootloader or platform? + + image_label_attr = "short_label" + + def __init__(self, storage, chandevs=None): + super(ZIPL, self).__init__(storage) + self.chandevs = chandevs + + # + # configuration + # + + @property + def boot_dir(self): + return "/boot" + + def write_config_images(self, config): + for image in self.images: + args = self.kernel_args[:] + args.insert(0, "root=%s/%s" % (self.boot_dir, image.kernel)) + stanza = ("[%(label)s]\n" + "\timage=%(boot_dir)/%(kernel)s\n" + "%(initrd_line)s" + "\tparameters=\"%(args)s\"\n" + % {"label": self.image_label(image), + "kernel": image.kernel, "initrd_line": initrd_line, + "args": " ".join(args), + "boot_dir": self.boot_dir}) + config.write(stanza) + + def write_config_header(self, config): + header = ("[defaultboot]\n" + "timeout=%{timeout}d\n" + "default=%{default}\n" + "target=/boot\n" + % {"timeout": self.timeout, + "default": self.image_label(self.default)}) + config.write(header) + + def write_chandev_config(self, install_root=""): + if self.chandevs: + chandev_path = install_root + self.chandev_config_file + if os.access(chandev_path, os.R_OK): + try: + os.rename(chandev_path, chandev_path + ".rpmsave") + except OSError as e: + log.warning("failed to back up /etc/chandev.conf: %s" % e) + + f = open(chandev_path, "w") + f.write("noauto\n") + for chandev in self.chandevs: + f.write("%s\n" % chandev) + f.close() + + try: + os.chmod(chandev_path, 0644) + except OSError as e: + log.error("failed to set mode of /etc/chandev.conf: %s" % e) + + def write_config(self, install_root=""): + self.write_chandev_config(install_root=install_root) + super(ZIPL, self).write_config(install_root=install_root) + + # + # installation + # + + def install(self, install_root=""): + buf = iutil.execWithCapture("zipl", [], + stderr="/dev/tty5", + root=install_root) + for line in buf.splitlines(): + if line.startswith("Preparing boot device: "): + # Output here may look like: + # Preparing boot device: dasdb (0200). + # Preparing boot device: dasdl. + # We want to extract the device name and pass that. + device = re.sub(".+?: ", "", line) + device = re.sub("(\s\(.+\))?\.$", "", device) + # XXX FIXME: why do we do this? booty called self.setDevice + # with the above device. for ks.cfg? is our + # already-set device wrong? + + +bootloader_classes[ZIPL.name] = ZIPL + + +class SILO(BootLoader): + name = "SILO" + arches = ["sparc*"] + config_file = "silo.conf" + message_file = "/etc/silo.message" + + # list of strings representing options for bootloader target device types + target_device_types = ["partition"] + target_device_disklabel_types = ["sun"] + + # list of strings representing options for boot device types + boot_device_types = ["partition"] + + packages = ["silo"] + + image_label_attr = "short_label" + + # + # configuration + # + + @property + def boot_prefix(self): + # FIXME: same as Yaboot + if self.stage2_device.format.mountpoint == "/boot": + return "" + else: + return "/boot" + + @property + def config_dir(self): + if self.stage2_device.format.mountpoint == "/boot": + return "/boot" + else: + return "/etc" + + @property + def config_file(self): + return "%s/%s" % (self.config_dir, self._config_file) + + def write_message_file(self, install_root=""): + message_file = os.path.normpath(install_root + self.message_file) + f = open(message_file, "w") + f.write("Welcome to %s!\nHit for boot options\n\n" % productName) + f.close() + os.chmod(message_file, 0600) + + def write_config_images(self, config): + # FIXME: same as Yaboot minus the macos bit + for image in self.images: + args = self.kernel_args[:] + if image.initrd: + initrd_line = "\tinitrd=%s/%s\n" % (self.boot_prefix, + image.initrd) + else: + initrd_line = "" + + root_device_spec = self.storage.rootDevice.fstabSpec + if root_device_spec.startswith("/"): + root_line = "\troot=%s\n" % root_device_spec + else: + args.append("root=%s" % root_device_spec) + root_line = "" + + stanza = ("image=%(boot_prefix)s%(kernel)s\n" + "\tlabel=%(label)s\n" + "\tread-only\n" + "%(initrd_line)s" + "%(root_line)s" + "\tappend=\"%(args)s\"\n\n" + % {"kernel": image.kernel, "initrd_line": initrd_line, + "label": self.image_label(image), + "root_line": root_line, "args": " ".join(args), + "boot_prefix": self.boot_prefix}) + config.write(stanza) + + def write_config_password(self, config): + # FIXME: same as Yaboot + if self.password: + config.write("password=%s\n" % self.password) + config.write("restricted\n") + + def write_config_header(self, config): + header = ("# silo.conf generated by anaconda\n\n" + "#boot=%(stage1dev)s\n" + "message=%(message)s\n" + "timeout=%(timeout)d\n" + "partition=%(boot_part_num)d\n" + "default=%(default)s\n" + % {"stage1dev": self.stage1_device.path, + "message": self.message_file, "timeout": self.timeout, + "boot_part_num": self.stage1_device.partedPartition.number, + "default": self.image_label(self.default)}) + config.write(header) + self.write_config_password(config) + + def write_config_post(self, install_root=""): + etc_silo = os.path.normpath(install_root + "/etc/" + self._config_file) + if not os.access(etc_silo, os.R_OK): + try: + os.symlink("../boot/%s" % self._config_file, etc_silo) + except OSError as e: + log.warning("failed to create /etc/silo.conf symlink: %s" % e) + + def write_config(self, install_root=""): + self.write_message_file(install_root=install_root) + super(SILO, self).write_config(install_root=install_root) + + # + # installation + # + + def install(self, install_root=""): + backup = "%s/backup.b" % self.config_dir + args = ["-f", "-C", self.config_file, "-S", backup] + variant = iutil.getSparcMachine() + if variant in ("sun4u", "sun4v"): + args.append("-u") + else: + args.append("-U") + + rc = iutil.execWithRedirect("silo", args, + stdout="/dev/tty5", stderr="/dev/tty5", + root=install_root) + + if rc: + raise BootLoaderError("bootloader install failed") + +bootloader_classes[SILO.name] = SILO + +""" anaconda-specific functions """ + +# this doesn't need to exist anymore, but the messageWindow probably needs to +# go somewhere +def bootloaderSetupChoices(anaconda): + if anaconda.dir == DISPATCH_BACK: + rc = anaconda.intf.messageWindow(_("Warning"), + _("Filesystems have already been activated. You " + "cannot go back past this point.\n\nWould you like to " + "continue with the installation?"), + type="custom", custom_icon=["error","error"], + custom_buttons=[_("_Exit installer"), _("_Continue")]) + + if rc == 0: + sys.exit(0) + return DISPATCH_FORWARD + +def writeSysconfigKernel(anaconda, default_kernel): f = open(anaconda.rootPath + "/etc/sysconfig/kernel", "w+") f.write("# UPDATEDEFAULT specifies if new-kernel-pkg should make\n" "# new kernels the default\n") # only update the default if we're setting the default to linux (#156678) - if (not defaultDev and not rootDev) or (defaultDev and rootDev.name == defaultDev.name): + if anaconda.bootloader.default.device == anaconda.storage.rootDevice: f.write("UPDATEDEFAULT=yes\n") else: - f.write("UPDATEDEFAULT=no\n") + f.write("UPDATEDEFAULT=no\n") f.write("\n") f.write("# DEFAULTKERNEL specifies the default kernel package type\n") - f.write("DEFAULTKERNEL=%s\n" %(defkern,)) + f.write("DEFAULTKERNEL=%s\n" % default_kernel) f.close() - dosync() - try: - rc = anaconda.bootloader.write(anaconda.rootPath, anaconda.bootloader, - kernelList, otherList, defaultDev) - w.pop() - if rc and anaconda.intf: - anaconda.intf.messageWindow(_("Warning"), - _("There was an error installing the bootloader. " - "The system may not be bootable.")) - except booty.BootyNoKernelWarning: - w.pop() +def writeBootloader(anaconda): + # FIXME: verify both stage1 and stage2 devices are set + stage1_device = anaconda.bootloader.stage1_device + stage2_device = anaconda.bootloader.stage2_device + + w = None + if anaconda.intf: + w = anaconda.intf.waitWindow(_("Bootloader"), + _("Installing bootloader.")) + + """ Current behavior is to use the first kernel as the default. + + When we get here, the bootloader will already have a default linux + image. We only have to add images for the non-default kernels and + adjust the default to reflect whatever the default variant is. """ + + # get a list of installed kernel packages + kernel_versions = anaconda.backend.kernelVersionList(anaconda.rootPath) + if not kernel_versions: + log.warning("no kernel was installed -- bootloader config unchanged") if anaconda.intf: anaconda.intf.messageWindow(_("Warning"), - _("No kernel packages were installed on the " - "system. Bootloader configuration " - "will not be changed.")) + _("No kernel packages were installed on the system " + "Bootloader configuration will not be changed.")) + return - dosync() + # The first one is the default kernel. Update the bootloader's default + # entry to reflect the details of the default kernel. + (version, arch, nick) = kernel_versions[0] + default_image = anaconda.bootloader.default + if not default_image: + log.error("unable to find default image, bailing") + if w: + w.pop() + return -def hasWindows(bl): - if not booty.doesDualBoot(): - return False + default_image.version = version + + # get the name of the default kernel package for use in + # /etc/sysconfig/kernel + default_kernel = "kernel" + if nick != "base": + default_kernel += "-%s" % nick + + # all the linux images' labels are based on the default image's + base_label = default_image.label + base_short = default_image.short_label + kernel_versions.pop(0) + + # now add images for each of the other kernels + for (version, arch, nick) in kernel_versions: + label = "%s-%s" % (base_label, nick) + short = "%s-%s" % (base_short, nick) + image = LinuxBootLoaderImage(device=anaconda.storage.rootDevice, + version=version, + label=label, short=short) + anaconda.bootloader.add_image(image) - foundWindows = False - # no point looking at the root device which won't be a dos type anyway - for (dev, type) in bl.images.availableBootDevices(bl.storage, addRoot=False): - if type in booty.dosFilesystems: - foundWindows = True - break + # write out /etc/sysconfig/kernel + writeSysconfigKernel(anaconda, default_kernel) + + try: + anaconda.bootloader.write(install_root=anaconda.rootPath) + except BootLoaderError as e: + if anaconda.intf: + anaconda.intf.messageWindow(_("Warning"), + _("There was an error installing the bootloader. " + "The system may not be bootable.")) + finally: + if w: + w.pop() - return foundWindows diff --git a/pyanaconda/booty/Makefile.am b/pyanaconda/booty/Makefile.am deleted file mode 100644 index 8d03ee0..0000000 --- a/pyanaconda/booty/Makefile.am +++ /dev/null @@ -1,24 +0,0 @@ -# booty/Makefile.am for anaconda -# -# Copyright (C) 2009 Red Hat, Inc. -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published -# by the Free Software Foundation; either version 2.1 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License -# along with this program. If not, see . -# -# Author: David Cantrell - -pkgpyexecdir = $(pyexecdir)/py$(PACKAGE_NAME) -bootydir = $(pkgpyexecdir)/booty -booty_PYTHON = *.py - -MAINTAINERCLEANFILES = Makefile.in diff --git a/pyanaconda/booty/__init__.py b/pyanaconda/booty/__init__.py deleted file mode 100644 index defff9a..0000000 --- a/pyanaconda/booty/__init__.py +++ /dev/null @@ -1,52 +0,0 @@ -# -# bootloader.py - generic boot loader handling backend for up2date and anaconda -# -# Jeremy Katz -# Adrian Likins -# Peter Jones -# -# Copyright 2001-2005 Red Hat, Inc. -# -# This software may be freely redistributed under the terms of the GNU -# library public license. -# -# You should have received a copy of the GNU Library Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -# -"""Module for manipulation and creation of boot loader configurations""" - -from pyanaconda import iutil -from bootloaderInfo import * -from pyanaconda.bootloader import * - -class BootyNoKernelWarning(Exception): - def __init__ (self, value=""): - self.value = value - - def __str__ (self): - return self.value - -# return instance of the appropriate bootloader for our arch -def getBootloader(anaconda): - """Get the bootloader info object for your architecture""" - if iutil.isX86(): - import x86 - return x86.x86BootloaderInfo(anaconda) - elif iutil.isIA64(): - import ia64 - return ia64.ia64BootloaderInfo(anaconda) - elif iutil.isS390(): - import s390 - return s390.s390BootloaderInfo(anaconda) - elif iutil.isAlpha(): - import alpha - return alpha.alphaBootloaderInfo(anaconda) - elif iutil.isPPC(): - import ppc - return ppc.ppcBootloaderInfo(anaconda) - elif iutil.isSparc(): - import sparc - return sparc.sparcBootloaderInfo(anaconda) - else: - return bootloaderInfo(anaconda) diff --git a/pyanaconda/booty/alpha.py b/pyanaconda/booty/alpha.py deleted file mode 100644 index c7bd6e9..0000000 --- a/pyanaconda/booty/alpha.py +++ /dev/null @@ -1,140 +0,0 @@ -import os -from pyanaconda import iutil - -from pyanaconda.booty import BootyNoKernelWarning -from bootloaderInfo import * -from util import getDiskPart - -class alphaBootloaderInfo(bootloaderInfo): - def writeAboot(self, instRoot, bl, kernelList, - chainList, defaultDev): - rootDevice = self.storage.rootDevice - try: - bootDevice = self.storage.mountpoints["/boot"] - except KeyError: - bootDevice = rootDevice - - bootnotroot = bootDevice != rootDevice - - confFile = instRoot + self.configfile - - # If /etc/aboot.conf already exists we rename it - # /etc/aboot.conf.rpmsave. - if os.path.isfile(confFile): - os.rename (confFile, confFile + ".rpmsave") - - # Then we create the necessary files. If the root device isn't - # the boot device, we create /boot/etc/ where the aboot.conf - # will live, and we create /etc/aboot.conf as a symlink to it. - if bootnotroot: - # Do we have /boot/etc ? If not, create one - if not os.path.isdir (instRoot + '/boot/etc'): - os.mkdir(instRoot + '/boot/etc', 0755) - - # We install the symlink (/etc/aboot.conf has already been - # renamed in necessary.) - os.symlink("../boot" + self.configfile, confFile) - - cfPath = instRoot + "/boot" + self.configfile - # Kernel path is set to / because a boot partition will - # be a root on its own. - kernelPath = '/' - # Otherwise, we just need to create /etc/aboot.conf. - else: - cfPath = confFile - kernelPath = self.kernelLocation - - # If we already have an aboot.conf, rename it - if os.access (cfPath, os.R_OK): - self.perms = os.stat(cfPath)[0] & 0777 - os.rename(cfPath, cfPath + '.rpmsave') - - # Now we're going to create and populate cfPath. - f = open (cfPath, 'w+') - f.write ("# aboot default configurations\n") - - if bootnotroot: - f.write ("# NOTICE: You have a /boot partition. This means that\n") - f.write ("# all kernel paths are relative to /boot/\n") - - # bpn is the boot partition number. - bpn = getDiskPart(bootDevice) - lines = 0 - - # We write entries line using the following format: - # root= [options] - # We get all the kernels we need to know about in kernelList. - - for (kernel, tag, version) in kernelList: - kernelTag = "-" + version - kernelFile = "%svmlinuz%s" %(kernelPath, kernelTag) - - f.write("%d:%d%s" %(lines, bpn, kernelFile)) - - # See if we can come up with an initrd argument that exists - initrd = self.makeInitrd(kernelTag, instRoot) - if initrd: - f.write(" initrd=%s%s" %(kernelPath, initrd)) - - realroot = rootDevice.fstabSpec - f.write(" root=%s" %(realroot,)) - - args = self.args.get() - if args: - f.write(" %s" %(args,)) - - f.write("\n") - lines = lines + 1 - - # We're done writing the file - f.close () - del f - - # Now we're ready to write the relevant boot information. wbd - # is the whole boot device, bdpn is the boot device partition - # number. - wbd = getDiskPart(bootDevice)[0] - bdpn = getDiskPart(bootDevice)[1] - - # Calling swriteboot. The first argument is the disk to write - # to and the second argument is a path to the bootstrap loader - # file. - args = [("/dev/%s" % wbd), "/boot/bootlx"] - rc = iutil.execWithRedirect ('/sbin/swriteboot', args, - root = instRoot, - stdout = "/dev/tty5", - stderr = "/dev/tty5") - if rc: - return rc - - # Calling abootconf to configure the installed aboot. The - # first argument is the disk to use, the second argument is - # the number of the partition on which aboot.conf resides. - # It's always the boot partition whether it's / or /boot (with - # the mount point being omitted.) - args = [("/dev/%s" % wbd), str (bdpn)] - rc = iutil.execWithRedirect ('/sbin/abootconf', args, - root = instRoot, - stdout = "/dev/tty5", - stderr = "/dev/tty5") - if rc: - return rc - - return 0 - - - def write(self, instRoot, bl, kernelList, chainList, defaultDev): - if len(kernelList) < 1: - raise BootyNoKernelWarning - - return self.writeAboot(instRoot, bl, kernelList, - chainList, defaultDev) - - def __init__(self, anaconda): - bootloaderInfo.__init__(self, anaconda) - self.useGrubVal = 0 - self._configdir = "/etc" - self._configname = "aboot.conf" - # self.kernelLocation is already set to what we need. - self.password = None - self.pure = None diff --git a/pyanaconda/booty/bootloaderInfo.py b/pyanaconda/booty/bootloaderInfo.py deleted file mode 100644 index c297557..0000000 --- a/pyanaconda/booty/bootloaderInfo.py +++ /dev/null @@ -1,714 +0,0 @@ -# -# bootloaderInfo.py - bootloader config object used in creation of new -# bootloader configs. Originally from anaconda -# -# Jeremy Katz -# Erik Troan -# Peter Jones -# -# Copyright 2005-2008 Red Hat, Inc. -# -# This software may be freely redistributed under the terms of the GNU -# library public license. -# -# You should have received a copy of the GNU Library Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -# - -import os, sys -import crypt -import random -import shutil -import string -import struct -from copy import copy - -import gettext -_ = lambda x: gettext.ldgettext("anaconda", x) -N_ = lambda x: x - -from lilo import LiloConfigFile - -from pyanaconda.flags import flags -from pyanaconda import iutil -from pyanaconda import isys -from pyanaconda.product import * - -import pyanaconda.booty -import checkbootloader -from util import getDiskPart - -if not iutil.isS390(): - import block - -dosFilesystems = ('FAT', 'fat16', 'fat32', 'ntfs', 'hpfs') - -def doesDualBoot(): - if iutil.isX86(): - return 1 - return 0 - -def checkForBootBlock(device): - fd = os.open(device, os.O_RDONLY) - buf = os.read(fd, 512) - os.close(fd) - if len(buf) >= 512 and \ - struct.unpack("H", buf[0x1fe: 0x200]) == (0xaa55,): - return True - return False - -# hack and a half -# there's no guarantee that data is written to the disk and grub -# reads both the filesystem and the disk. suck. -def syncDataToDisk(dev, mntpt, instRoot = "/"): - isys.sync() - isys.sync() - isys.sync() - - # and xfs is even more "special" (#117968) - if isys.readFSType(dev) == "xfs": - iutil.execWithRedirect("/usr/sbin/xfs_freeze", - ["-f", mntpt], - stdout = "/dev/tty5", - stderr = "/dev/tty5", - root = instRoot) - iutil.execWithRedirect("/usr/sbin/xfs_freeze", - ["-u", mntpt], - stdout = "/dev/tty5", - stderr = "/dev/tty5", - root = instRoot) - -def rootIsDevice(dev): - if dev.startswith("LABEL=") or dev.startswith("UUID="): - return False - return True - -class KernelArguments: - - def getDracutStorageArgs(self, devices): - args = [] - types = {} - for device in devices: - for d in self.anaconda.storage.devices: - if d is not device and not device.dependsOn(d): - continue - - s = d.dracutSetupString() - types[s.split("=")[0]] = True - if s not in args: - args.append(s) - - from pyanaconda import storage - if isinstance(d, storage.devices.NetworkStorageDevice): - s = self.anaconda.network.dracutSetupString(d) - if s not in args: - args.append(s) - - for i in [ [ "rd_LUKS_UUID", "rd_NO_LUKS" ], - [ "rd_LVM_LV", "rd_NO_LVM" ], - [ "rd_MD_UUID", "rd_NO_MD" ], - [ "rd_DM_UUID", "rd_NO_DM" ] ]: - if not types.has_key(i[0]): - args.append(i[1]) - - return args - - def get(self): - args = "" - bootArgs = [] - rootDev = self.anaconda.storage.rootDevice - neededDevs = [ rootDev ] + self.anaconda.storage.swaps - - if flags.cmdline.get("fips") == "1": - bootDev = self.anaconda.storage.mountpoints.get("/boot", rootDev) - bootArgs = [ "boot=%s" % bootDev.fstabSpec ] - if bootDev is not rootDev: - neededDevs = [ rootDev, bootDev ] - - if self.anaconda.storage.fsset.swapDevices: - neededDevs.append(self.anaconda.storage.fsset.swapDevices[0]) - - for s in bootArgs + \ - self.getDracutStorageArgs(neededDevs) + [ - self.anaconda.instLanguage.dracutSetupString(), - self.anaconda.keyboard.dracutSetupString(), - self.args, - self.appendArgs ]: - s = s.strip() - if not s: - continue - if args: - args += " " - args += s - - return args - - def set(self, args): - self.args = args - self.appendArgs = "" - - def getNoDracut(self): - args = self.args.strip() + " " + self.appendArgs.strip() - return args.strip() - - def chandevget(self): - return self.cargs - - def chandevset(self, args): - self.cargs = args - - def append(self, args): - # don't duplicate the addition of an argument (#128492) - if self.args.find(args) != -1: - return - if self.appendArgs.find(args) != -1: - return - - if self.appendArgs: - self.appendArgs += " " - - self.appendArgs += args - - def __init__(self, anaconda): - newArgs = [] - - self.anaconda = anaconda - - if iutil.isS390(): - self.cargs = [] - - # look for kernel arguments we know should be preserved and add them - ourargs = ["speakup_synth", "apic", "noapic", "apm", "ide", "noht", - "acpi", "video", "pci", "nodmraid", "nompath", "nomodeset", - "noiswmd", "fips"] - - if iutil.isS390(): - ourargs.append("cio_ignore") - - for arg in ourargs: - if not flags.cmdline.has_key(arg): - continue - - val = flags.cmdline.get(arg, "") - if val: - newArgs.append("%s=%s" % (arg, val)) - else: - newArgs.append(arg) - - self.args = " ".join(newArgs) - self.appendArgs = "" - - -class BootImages: - """A collection to keep track of boot images available on the system. - Examples would be: - ('linux', 'Red Hat Linux', 'ext2'), - ('Other', 'Other', 'fat32'), ... - """ - def __init__(self): - self.default = None - self.images = {} - - def getImages(self): - """returns dictionary of (label, longlabel, devtype) pairs - indexed by device""" - # return a copy so users can modify it w/o affecting us - return copy(self.images) - - def setDefault(self, default): - # default is a device - self.default = default - - def getDefault(self): - return self.default - - # Construct a dictionary mapping device names to (OS, product, type) - # tuples. - def setup(self, storage): - devices = {} - bootDevs = self.availableBootDevices(storage) - - for (dev, type) in bootDevs: - devices[dev.name] = 1 - - # These partitions have disappeared - for dev in self.images.keys(): - if not devices.has_key(dev): - del self.images[dev] - - # These have appeared - for (dev, type) in bootDevs: - if not self.images.has_key(dev.name): - if type in dosFilesystems and doesDualBoot(): - self.images[dev.name] = ("Other", "Other", type) - elif type in ("hfs", "hfs+") and iutil.getPPCMachine() == "PMac": - self.images[dev.name] = ("Other", "Other", type) - else: - self.images[dev.name] = (None, None, type) - - if not self.images.has_key(self.default): - self.default = storage.rootDevice.name - (label, longlabel, type) = self.images[self.default] - if not label: - self.images[self.default] = ("linux", productName, type) - - # Return a list of (storage.Device, string) tuples that are bootable - # devices. The string is the type of the device, which is just a string - # like "vfat" or "swap" or "lvm". - # Appends information about the root device when addRoot is true. - def availableBootDevices(self, storage, addRoot=True): - import parted - retval = [] - foundDos = False - foundAppleBootstrap = False - - for part in [p for p in storage.partitions if p.exists]: - # Skip extended, metadata, freespace, etc. - if part.partType not in (parted.PARTITION_NORMAL, parted.PARTITION_LOGICAL) or not part.format: - continue - - if part.format.type in dosFilesystems and \ - not foundDos and doesDualBoot() and \ - not part.getFlag(parted.PARTITION_DIAG): - try: - bootable = checkForBootBlock(part.path) - except (OSError, struct.error): - pass - else: - retval.append((part, part.format.type)) - foundDos = True - elif part.format.type == "appleboot" and \ - iutil.getPPCMachine() == "PMac" and part.bootable: - foundAppleBootstrap = True - elif part.format.type in ["hfs", "hfs+"] and foundAppleBootstrap: - # questionable for same reason as above, but on mac this time - retval.append((part, part.format.type)) - - if addRoot: - rootDevice = storage.rootDevice - if not rootDevice or not rootDevice.format: - raise ValueError, ("Trying to pick boot devices but do not have a " - "sane root partition. Aborting install.") - retval.append((rootDevice, rootDevice.format.type)) - - retval.sort() - return retval - -class bootloaderInfo(object): - def getConfigFileName(self): - if not self._configname: - raise NotImplementedError - return self._configname - configname = property(getConfigFileName, None, None, \ - "bootloader config file name") - - def getConfigFileDir(self): - if not self._configdir: - raise NotImplementedError - return self._configdir - configdir = property(getConfigFileDir, None, None, \ - "bootloader config file directory") - - def getConfigFilePath(self): - return "%s/%s" % (self.configdir, self.configname) - configfile = property(getConfigFilePath, None, None, \ - "full path and name of the real config file") - - def setUseGrub(self, val): - pass - - def useGrub(self): - return self.useGrubVal - - def setPassword(self, val, isCrypted = 1): - pass - - def getPassword(self): - pass - - def getDevice(self): - return self.device - - def setDevice(self, device): - self.device = device - - (dev, part) = getDiskPart( - self.storage.devicetree.getDeviceByName(device)) - if part is None: - self.defaultDevice = "mbr" - else: - self.defaultDevice = "partition" - - def makeInitrd(self, kernelTag, instRoot): - initrd = "initrd%s.img" % kernelTag - if os.access(instRoot + "/boot/" + initrd, os.R_OK): - return initrd - - initrd = "initramfs%s.img" % kernelTag - if os.access(instRoot + "/boot/" + initrd, os.R_OK): - return initrd - - return None - - def getBootloaderConfig(self, instRoot, bl, kernelList, - chainList, defaultDev): - images = bl.images.getImages() - - confFile = instRoot + self.configfile - - # on upgrade read in the lilo config file - lilo = LiloConfigFile () - self.perms = 0600 - if os.access (confFile, os.R_OK): - self.perms = os.stat(confFile)[0] & 0777 - lilo.read(confFile) - os.rename(confFile, confFile + ".rpmsave") - # if it's an absolute symlink, just get it out of our way - elif (os.path.islink(confFile) and os.readlink(confFile)[0] == '/'): - os.rename(confFile, confFile + ".rpmsave") - - # Remove any invalid entries that are in the file; we probably - # just removed those kernels. - for label in lilo.listImages(): - (fsType, sl, path, other) = lilo.getImage(label) - if fsType == "other": continue - - if not os.access(instRoot + sl.getPath(), os.R_OK): - lilo.delImage(label) - - lilo.addEntry("prompt", replace = 0) - lilo.addEntry("timeout", self.timeout or "20", replace = 0) - - rootDev = self.storage.rootDevice - - if rootDev.name == defaultDev.name: - lilo.addEntry("default", kernelList[0][0]) - else: - lilo.addEntry("default", chainList[0][0]) - - for (label, longlabel, version) in kernelList: - kernelTag = "-" + version - kernelFile = self.kernelLocation + "vmlinuz" + kernelTag - - try: - lilo.delImage(label) - except IndexError as msg: - pass - - sl = LiloConfigFile(imageType = "image", path = kernelFile) - - initrd = self.makeInitrd(kernelTag, instRoot) - - sl.addEntry("label", label) - if initrd: - sl.addEntry("initrd", "%s%s" %(self.kernelLocation, initrd)) - - sl.addEntry("read-only") - - append = "%s" %(self.args.get(),) - realroot = rootDev.fstabSpec - if rootIsDevice(realroot): - sl.addEntry("root", rootDev.path) - else: - if len(append) > 0: - append = "%s root=%s" %(append,realroot) - else: - append = "root=%s" %(realroot,) - - if len(append) > 0: - sl.addEntry('append', '"%s"' % (append,)) - - lilo.addImage (sl) - - for (label, longlabel, device) in chainList: - if ((not label) or (label == "")): - continue - try: - (fsType, sl, path, other) = lilo.getImage(label) - lilo.delImage(label) - except IndexError: - sl = LiloConfigFile(imageType = "other", - path = "/dev/%s" %(device)) - sl.addEntry("optional") - - sl.addEntry("label", label) - lilo.addImage (sl) - - # Sanity check #1. There could be aliases in sections which conflict - # with the new images we just created. If so, erase those aliases - imageNames = {} - for label in lilo.listImages(): - imageNames[label] = 1 - - for label in lilo.listImages(): - (fsType, sl, path, other) = lilo.getImage(label) - if sl.testEntry('alias'): - alias = sl.getEntry('alias') - if imageNames.has_key(alias): - sl.delEntry('alias') - imageNames[alias] = 1 - - # Sanity check #2. If single-key is turned on, go through all of - # the image names (including aliases) (we just built the list) and - # see if single-key will still work. - if lilo.testEntry('single-key'): - singleKeys = {} - turnOff = 0 - for label in imageNames.keys(): - l = label[0] - if singleKeys.has_key(l): - turnOff = 1 - singleKeys[l] = 1 - if turnOff: - lilo.delEntry('single-key') - - return lilo - - def write(self, instRoot, bl, kernelList, chainList, defaultDev): - rc = 0 - - if len(kernelList) >= 1: - config = self.getBootloaderConfig(instRoot, bl, - kernelList, chainList, - defaultDev) - rc = config.write(instRoot + self.configfile, perms = self.perms) - else: - raise pyanaconda.booty.BootyNoKernelWarning - - return rc - - def getArgList(self): - args = [] - - if self.defaultDevice is None: - args.append("--location=none") - return args - - args.append("--location=%s" % (self.defaultDevice,)) - args.append("--driveorder=%s" % (",".join(self.drivelist))) - - if self.args.getNoDracut(): - args.append("--append=\"%s\"" %(self.args.getNoDracut())) - - return args - - def writeKS(self, f): - f.write("bootloader") - for arg in self.getArgList(): - f.write(" " + arg) - f.write("\n") - - def updateDriveList(self, sortedList=[]): - # bootloader is unusual in that we only want to look at disks that - # have disklabels -- no partitioned md or unpartitioned disks - disks = self.storage.disks - partitioned = self.storage.partitioned - self._drivelist = [d.name for d in disks if d in partitioned] - self._drivelist.sort(self.storage.compareDisks) - - # If we're given a sort order, make sure the drives listed in it - # are put at the head of the drivelist in that order. All other - # drives follow behind in whatever order they're found. - if sortedList != []: - revSortedList = sortedList - revSortedList.reverse() - - for i in revSortedList: - try: - ele = self._drivelist.pop(self._drivelist.index(i)) - self._drivelist.insert(0, ele) - except LookupError: - pass - - def _getDriveList(self): - if self._drivelist is not None: - return self._drivelist - self.updateDriveList() - return self._drivelist - def _setDriveList(self, val): - self._drivelist = val - drivelist = property(_getDriveList, _setDriveList) - - def __init__(self, anaconda): - self.args = KernelArguments(anaconda) - self.images = BootImages() - self.device = None - self.defaultDevice = None # XXX hack, used by kickstart - self.useGrubVal = 0 # only used on x86 - self._configdir = None - self._configname = None - self.kernelLocation = "/boot/" - self.password = None - self.pure = None - self.above1024 = 0 - self.timeout = None - self.storage = anaconda.storage - self.serial = 0 - self.serialDevice = None - self.serialOptions = None - - # this has somewhat strange semantics. if 0, act like a normal - # "install" case. if 1, update lilo.conf (since grubby won't do that) - # and then run lilo or grub only. - # XXX THIS IS A HACK. implementation details are only there for x86 - self.doUpgradeOnly = 0 - self.kickstart = 0 - - self._drivelist = None - - if flags.serial != 0: - self.serial = 1 - self.timeout = 5 - - console = flags.cmdline.get("console", "") - if console: - # the options are everything after the comma - comma = console.find(",") - if comma != -1: - self.serialDevice = console[:comma] - self.serialOptions = console[comma + 1:] - else: - self.serialDevice = console - self.serialOptions = "" - else: - self.serialDevice = "ttyS0" - self.serialOptions = "" - - if self.serialOptions: - self.args.append("console=%s,%s" %(self.serialDevice, - self.serialOptions)) - else: - self.args.append("console=%s" % self.serialDevice) - - if flags.virtpconsole is not None: - if flags.virtpconsole.startswith("/dev/"): - con = flags.virtpconsole[5:] - else: - con = flags.virtpconsole - self.args.append("console=%s" %(con,)) - -class efiBootloaderInfo(bootloaderInfo): - def getBootloaderName(self): - return self._bootloader - bootloader = property(getBootloaderName, None, None, \ - "name of the bootloader to install") - - # XXX wouldn't it be nice to have a real interface to use efibootmgr from? - def removeOldEfiEntries(self, instRoot): - p = os.pipe() - rc = iutil.execWithRedirect('efibootmgr', [], - root = instRoot, stdout = p[1]) - os.close(p[1]) - if rc: - return rc - - c = os.read(p[0], 1) - buf = c - while (c): - c = os.read(p[0], 1) - buf = buf + c - os.close(p[0]) - lines = string.split(buf, '\n') - for line in lines: - fields = string.split(line) - if len(fields) < 2: - continue - if string.join(fields[1:], " ") == productName: - entry = fields[0][4:8] - rc = iutil.execWithRedirect('efibootmgr', - ["-b", entry, "-B"], - root = instRoot, - stdout="/dev/tty5", stderr="/dev/tty5") - if rc: - return rc - - return 0 - - def addNewEfiEntry(self, instRoot): - try: - bootdev = self.storage.mountpoints["/boot/efi"].name - except (KeyError, AttributeError): - bootdev = "sda1" - - link = "%s%s/%s" % (instRoot, "/etc/", self.configname) - if not os.access(link, os.R_OK): - os.symlink("../%s" % (self.configfile), link) - - ind = len(bootdev) - try: - while (bootdev[ind-1] in string.digits): - ind = ind - 1 - except IndexError: - ind = len(bootdev) - 1 - - bootdisk = bootdev[:ind] - bootpart = bootdev[ind:] - if (bootdisk.startswith('ida/') or bootdisk.startswith('cciss/') or - bootdisk.startswith('rd/') or bootdisk.startswith('sx8/')): - bootdisk = bootdisk[:-1] - - argv = [ "efibootmgr", "-c" , "-w", "-L", - productName, "-d", "/dev/%s" % bootdisk, - "-p", bootpart, "-l", "\\EFI\\redhat\\" + self.bootloader ] - rc = iutil.execWithRedirect(argv[0], argv[1:], root = instRoot, - stdout = "/dev/tty5", - stderr = "/dev/tty5") - return rc - - def getEfiProductPath(self, productName, force=False): - """ Return the full EFI path of the installed product. - eg. HD(4,2c8800,64000,902c1655-2677-4455-b2a5-29d0ce835610) - - pass force=True to skip the cache and rerun efibootmgr - """ - if not force and self._efiProductPath: - return self._efiProductPath - - argv = [ "efibootmgr", "-v" ] - buf = iutil.execWithCapture(argv[0], argv[1:], - stderr="/dev/tty5") - - efiProductPath = None - for line in buf.splitlines(): - line = line.strip() - if not line: - continue - if productName in line: - efiProductPath = line[line.rfind(productName)+len(productName):].strip() - break - - if efiProductPath: - # Grab just the drive path - import re - m = re.match("(.*?\(.*?\)).*", efiProductPath) - if m: - efiProductPath = m.group(1) - else: - efiProductPath = None - - self._efiProductPath = efiProductPath - return self._efiProductPath - - def installGrub(self, instRoot, bootDev, grubTarget, grubPath, cfPath): - if not iutil.isEfi(): - raise EnvironmentError - rc = self.removeOldEfiEntries(instRoot) - if rc: - return rc - return self.addNewEfiEntry(instRoot) - - def __init__(self, anaconda, initialize = True): - if initialize: - bootloaderInfo.__init__(self, anaconda) - else: - self.storage = anaconda.storage - - self._efiProductPath = None - - if iutil.isEfi(): - self._configdir = "/boot/efi/EFI/redhat" - self._configname = "grub.conf" - self._bootloader = "grub.efi" - self.useGrubVal = 1 - self.kernelLocation = "" diff --git a/pyanaconda/booty/checkbootloader.py b/pyanaconda/booty/checkbootloader.py deleted file mode 100644 index 3cf8dc3..0000000 --- a/pyanaconda/booty/checkbootloader.py +++ /dev/null @@ -1,176 +0,0 @@ -#!/usr/bin/python -# -# Check to see whether it looks like GRUB or LILO is the boot loader -# being used on the system. -# -# Jeremy Katz -# Peter Jones -# -# Copyright 2001,2005 Red Hat, Inc. -# -# This software may be freely redistributed under the terms of the GNU -# library public license. -# -# You should have received a copy of the GNU Library Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -import os -import string - -from util import getDiskPart -from pyanaconda import iutil - -grubConfigFile = "/etc/grub.conf" -liloConfigFile = "/etc/lilo.conf" -yabootConfigFile = "/etc/yaboot.conf" -siloConfigFile = "/etc/silo.conf" -ziplConfigFile = "/etc/zipl.conf" - -def getBootBlock(bootDev, instRoot, seekBlocks=0): - """Get the boot block from bootDev. Return a 512 byte string.""" - block = " " * 512 - if bootDev is None: - return block - - try: - fd = os.open("%s%s" % (instRoot, bootDev), os.O_RDONLY) - if seekBlocks > 0: - os.lseek(fd, seekBlocks * 512, 0) - block = os.read(fd, 512) - os.close(fd) - except: - pass - - return block - -# takes a line like #boot=/dev/hda and returns /dev/hda -# also handles cases like quoted versions and other nonsense -def getBootDevString(line): - dev = string.split(line, '=')[1] - dev = string.strip(dev) - dev = string.replace(dev, '"', '') - dev = string.replace(dev, "'", "") - return dev - -def getBootDevList(line): - devs = string.split(line, '=')[1] - rets = [] - for dev in devs: - dev = getBootDevString("=%s" % (dev,)) - rets.append(dev) - return string.join(rets) - -def getBootloaderTypeAndBoot(instRoot, storage): - haveGrubConf = 1 - haveLiloConf = 1 - haveYabootConf = 1 - haveSiloConf = 1 - haveZiplConf = 1 - - bootDev = None - - # make sure they have the config file, otherwise we definitely can't - # use that bootloader - if not os.access(instRoot + grubConfigFile, os.R_OK): - haveGrubConf = 0 - if not os.access(instRoot + liloConfigFile, os.R_OK): - haveLiloConf = 0 - if not os.access(instRoot + yabootConfigFile, os.R_OK): - haveYabootConf = 0 - if not os.access(instRoot + siloConfigFile, os.R_OK): - haveSiloConf = 0 - if not os.access(instRoot + ziplConfigFile, os.R_OK): - haveZiplConf = 0 - - if haveGrubConf: - bootDev = None - for (fn, stanza) in [ ("/etc/sysconfig/grub", "boot="), - (grubConfigFile, "#boot=") ]: - try: - f = open(instRoot + fn, "r") - except: - continue - - # the following bits of code are straight from checkbootloader.py - lines = f.readlines() - f.close() - for line in lines: - if line.startswith(stanza): - bootDev = getBootDevString(line) - break - if bootDev is not None: - break - - if iutil.isEfi(): - return ("GRUB", bootDev) - - if bootDev is not None: - block = getBootBlock(bootDev, instRoot) - # XXX I don't like this, but it's what the maintainer suggested :( - if string.find(block, "GRUB") >= 0: - return ("GRUB", bootDev) - - if haveLiloConf: - f = open(instRoot + liloConfigFile, "r") - lines = f.readlines() - for line in lines: - if line[0:5] == "boot=": - bootDev = getBootDevString(line) - break - - block = getBootBlock(bootDev, instRoot) - # this at least is well-defined - if block[6:10] == "LILO": - return ("LILO", bootDev) - - if haveYabootConf: - f = open(instRoot + yabootConfigFile, "r") - lines = f.readlines() - for line in lines: - if line[0:5] == "boot=": - bootDev = getBootDevList(line) - - if bootDev: - return ("YABOOT", bootDev) - - if haveSiloConf: - bootDev = None - # We've never done the /etc/sysconfig/silo thing, but maybe - # we should start... - for (fn, stanza) in [ ("/etc/sysconfig/silo", "boot="), - (grubConfigFile, "#boot=") ]: - try: - f = open(instRoot + fn, "r") - except: - continue - - lines = f.readlines() - f.close() - for line in lines: - if line.startswith(stanza): - bootDev = getBootDevString(line) - break - if bootDev is not None: - break - - if bootDev is not None: - # XXX SILO sucks just like grub. - dev = storage.devicetree.getDeviceByName(bootDev) - if getDiskPart(dev)[1] != 4: - block = getBootBlock(bootDev, instRoot, 1) - if block[24:28] == "SILO": - return ("SILO", bootDev) - - if haveZiplConf: - bootDev = None - f = open(instRoot + ziplConfigFile, "r") - lines = f.readlines() - for line in lines: - if line[0:7] == "target=": - bootDev = getBootDevList(line) - - if bootDev: - return ("ZIPL", bootDev) - - return (None, None) diff --git a/pyanaconda/booty/ia64.py b/pyanaconda/booty/ia64.py deleted file mode 100644 index 796ed5c..0000000 --- a/pyanaconda/booty/ia64.py +++ /dev/null @@ -1,38 +0,0 @@ -from pyanaconda.booty import BootyNoKernelWarning -from bootloaderInfo import * - -class ia64BootloaderInfo(efiBootloaderInfo): - def getBootloaderConfig(self, instRoot, bl, kernelList, - chainList, defaultDev): - config = bootloaderInfo.getBootloaderConfig(self, instRoot, - bl, kernelList, chainList, - defaultDev) - # altix boxes need relocatable (#120851) - config.addEntry("relocatable") - - return config - - def writeLilo(self, instRoot, bl, kernelList, - chainList, defaultDev): - config = self.getBootloaderConfig(instRoot, bl, - kernelList, chainList, defaultDev) - return config.write(instRoot + self.configfile, perms = 0755) - - def write(self, instRoot, bl, kernelList, chainList, defaultDev): - if len(kernelList) >= 1: - rc = self.writeLilo(instRoot, bl, kernelList, - chainList, defaultDev) - if rc: - return rc - else: - raise BootyNoKernelWarning - - rc = self.removeOldEfiEntries(instRoot) - if rc: - return rc - return self.addNewEfiEntry(instRoot) - - def __init__(self, anaconda): - efiBootloaderInfo.__init__(self, anaconda) - self._configname = "elilo.conf" - self._bootloader = "elilo.efi" diff --git a/pyanaconda/booty/lilo.py b/pyanaconda/booty/lilo.py deleted file mode 100644 index dc2328e..0000000 --- a/pyanaconda/booty/lilo.py +++ /dev/null @@ -1,308 +0,0 @@ -#!/usr/bin/python -# -# Module for manipulation of lilo.conf files. Original found -# in the anaconda installer -# Copyright (c) 1999-2001 Red Hat, Inc. Distributed under GPL. -# -# Author: Matt Wilson -# Eric Troan -# Adrian Likins -"""Module for manipulation of lilo.conf files.""" -import string -import os - -from UserDict import UserDict - - -class UserDictCase(UserDict): - """A dictionary with case insensitive keys""" - def __init__(self, data = {}): - UserDict.__init__(self) - # if we are passed a dictionary transfer it over... - for k in data.keys(): - kl = string.lower(k) - self.data[kl] = data[k] - # some methods used to make the class work as a dictionary - def __setitem__(self, key, value): - key = string.lower(key) - self.data[key] = value - def __getitem__(self, key): - key = string.lower(key) - if not self.data.has_key(key): - return None - return self.data[key] - get = __getitem__ - def __delitem__(self, key): - key = string.lower(key) - del self.data[key] - def has_key(self, key): - key = string.lower(key) - return self.data.has_key(key) - # return this data as a real hash - def get_hash(self): - return self.data - # return the data for marshalling - def __getstate__(self): - return self.data - # we need a setstate because of the __getstate__ presence screws up deepcopy - def __setstate__(self, state): - self.__init__(state) - # get a dictionary out of this instance ({}.update doesn't get instances) - def dict(self): - return self.data - -class LiloConfigFile: - """class representing a lilo.conf lilo configuration file. Used to - manipulate the file directly""" - - def __repr__ (self, tab = 0): - s = "" - for n in self.order: - if (tab): - s = s + '\t' - if n[0] == '#': - s = s + n[1:] - else: - s = s + n - if self.items[n]: - s = s + "=" + self.items[n] - s = s + '\n' - for count in range(len(self.diskRemaps)): - s = s + "disk = %s\n" % self.diskRemaps[count][1] - s = s + "\tbios = %s\n" % self.biosRemaps[count][1] - for cl in self.images: - s = s + "\n%s=%s\n" % (cl.imageType, cl.path) - s = s + cl.__repr__(1) - return s - - def addEntry(self, item, val = None, replace = 1): - if not self.items.has_key(item): - self.order.append(item) - elif not replace: - return - - if (val): - self.items[item] = str(val) - else: - self.items[item] = None - - def getEntry(self, item): - if self.items.has_key(item): - return self.items[item] - else: - return None - - def delEntry(self, item): - newOrder = [] - for i in self.order: - if item != i: newOrder.append(i) - self.order = newOrder - - del self.items[item] - - def listEntries(self): - foo = self.items - return foo - - def testEntry(self, item): - if self.items.has_key(item): - return 1 - else: - return 0 - - def getImage(self, label): - for config in self.images: - # sanity check - if label is None: - break - if config.getEntry('label'): - if string.lower(config.getEntry('label')) == string.lower(label): - return (config.imageType, config, config.path, config.other) - if config.getEntry('alias'): - if string.lower(config.getEntry('alias')) == string.lower(label): - return (config.imageType, config, config.path, config.other) - - - raise IndexError, "unknown image %s" % (label) - - def addImage (self, config,first=None): - # make sure the config has a valid label - config.getEntry('label') - if not config.path or not config.imageType: - raise ValueError, "subconfig missing path or image type" - - if first: - self.images = [config] + self.images - else: - self.images.append(config) - - def delImage (self, label): - for config in self.images: - # sanity check - if label is None: - break - if config.getEntry('label'): - if string.lower(config.getEntry('label')) == string.lower(label): - self.images.remove (config) - return - - raise IndexError, "unknown image %s" % (label,) - - def getDefault (self): - default = None - try: - default = self.getEntry("default") - except: - pass - - if not default: - default = self.listImages()[0] - - theDefault = self.getImage(default) - - return theDefault[1] - - def getDefaultLinux (self): - defaultIsOther = None - - # XXX ick... this code badly needs work =\ - theDefault = self.getDefault() - - if theDefault.other: - defaultIsOther = 1 - - # if the default is other, look for the first linux image - if theDefault.other: - for image_label in self.listImages(): - image = self.getImage(image_label)[1] - if not image.other: - theDefault = image - break - - # if we get here and are *still* an other, then we have no linux - # images. ick - if theDefault.other: - return None - else: - return theDefault - - def listImages (self): - l = [] - for config in self.images: - l.append(config.getEntry('label')) - return l - - def listAliases (self): - l = [] - for config in self.images: - if config.getEntry('alias'): - l.append(config.getEntry('alias')) - return l - - def getPath (self): - return self.path - - def write(self, file, perms = 0644): - f = open(file, "w") - f.write(self.__repr__()) - f.close() - os.chmod(file, perms) - - def read (self, file): - f = open(file, "r") - image = None - for l in f.readlines(): - l = l[:-1] - orig = l - while (l and (l[0] == ' ' or l[0] == '\t')): - l = l[1:] - if not l: - continue - if l[0] == '#' and not image: - self.order.append('#' + orig) - continue - fields = string.split(l, '=', 1) - if l[0] == '#' and image: - args = ('#' + l,) - elif (len(fields) == 2): - f0 = string.strip (fields [0]) - f1 = string.strip (fields [1]) - if (f0 != "append"): - # people are silly and put quotes brokenly in their - # lilo.conf but you have to use them for append. ARGH! - f1 = string.replace(f1, '"', '') - f1 = string.replace(f1, "'", "") - if (f0 == "image" or f0 == "other"): - if image: self.addImage(image) - image = LiloConfigFile(imageType = f0, - path = f1) - if (f0 == "other"): - image.other = 1 - args = None - else: - args = (f0, f1) - if (f0 == "disk"): - self.diskRemaps.append((f0,f1)) - args = None - if (f0 == "bios"): - self.biosRemaps.append((f0,f1)) - args = None - - else: - args = (string.strip (l),) - - if (args and image): - apply(image.addEntry, args) - elif args: - apply(self.addEntry, args) - - if image: self.addImage(image) - - f.close() - - def __init__(self, imageType = None, path = None): - self.imageType = imageType - self.path = path - self.order = [] - self.images = [] - self.other = None - self.items = UserDictCase() - self.biosRemaps = [] - self.diskRemaps = [] - self.unsupported = [] - - -if __name__ == "__main__": - import sys - #sys.path.append("") - config = LiloConfigFile () - config.read ('/etc/lilo.conf') - print config - print "image list", config.listImages() - config.delImage ('linux') - print '----------------------------------' - config = LiloConfigFile () - config.read ('/etc/lilo.conf') - print config - print '----------------------------------' - print '----------------------------------' - print "list images" - print config.listImages() - print config.getImage('linux') - print "----------------------------------" - print "addimage (testlinux)" - blip = """ -read-only -blippy-blob=sdfsdf -append=\"sdfasdfasdf\" -root=/dev/hda6 -""" - sl = LiloConfigFile(imageType = "image", path="/boot/somevmlinuz-2.4.0") - sl.addEntry("label", "newkernel") - sl.addEntry("initrd", "blipppy") - config.addImage(sl) - - print '-------------------------------------' - print "writing out /tmp/lilo.conf" - print config.write("/tmp/lilo.conf") - print config diff --git a/pyanaconda/booty/ppc.py b/pyanaconda/booty/ppc.py deleted file mode 100644 index c473156..0000000 --- a/pyanaconda/booty/ppc.py +++ /dev/null @@ -1,184 +0,0 @@ -import string -import os - -from pyanaconda.booty import BootyNoKernelWarning -from bootloaderInfo import * -from pyanaconda import iutil - -class ppcBootloaderInfo(bootloaderInfo): - def getBootDevs(self, bl): - import parted - - retval = [] - machine = iutil.getPPCMachine() - - if machine == 'pSeries': - for dev in self.storage.fsset.devices: - if dev.format.type == "prepboot": - retval.append(dev.path) - elif machine == 'PMac': - for dev in self.storage.fsset.devices: - if dev.format.type == "hfs" and dev.format.bootable: - retval.append(dev.path) - - if len(retval) == 0: - # Try to get a boot device; bplan OF understands ext3 - if machine == 'Pegasos' or machine == 'Efika': - try: - device = self.storage.mountpoints["/boot"] - except KeyError: - # Try / if we don't have this we're not going to work - device = self.storage.rootDevice - - retval.append(device.path) - else: - if bl.getDevice(): - d = bl.getDevice() - retval.append(self.storage.devicetree.getDeviceByName(d).path) - - return retval - - def writeYaboot(self, instRoot, bl, kernelList, - chainList, defaultDev): - - yabootTarget = string.join(self.getBootDevs(bl)) - - try: - bootDev = self.storage.mountpoints["/boot"] - - cf = "/boot/etc/yaboot.conf" - cfPath = "" - if not os.path.isdir(instRoot + "/boot/etc"): - os.mkdir(instRoot + "/boot/etc") - except KeyError: - bootDev = self.storage.rootDevice - - cfPath = "/boot" - cf = "/etc/yaboot.conf" - - if bootDev.type == "mdarray": - partNumber = bootDev.parents[0].partedPartition.number - else: - partNumber = bootDev.partedPartition.number - - f = open(instRoot + cf, "w+") - - f.write("# yaboot.conf generated by anaconda\n\n") - f.write("boot=%s\n" %(yabootTarget,)) - f.write("init-message=\"Welcome to %s!\\nHit for boot options\"\n\n" - % productName) - - f.write("partition=%s\n" % partNumber) - f.write("timeout=%s\n" % (self.timeout or 80)) - f.write("install=/usr/lib/yaboot/yaboot\n") - f.write("delay=5\n") - f.write("enablecdboot\n") - f.write("enableofboot\n") - f.write("enablenetboot\n") - - yabootProg = "/sbin/mkofboot" - if iutil.getPPCMachine() == "PMac": - # write out the first hfs/hfs+ partition as being macosx - for (label, longlabel, device) in chainList: - if ((not label) or (label == "")): - continue - f.write("macosx=/dev/%s\n" %(device,)) - break - - f.write("magicboot=/usr/lib/yaboot/ofboot\n") - - elif iutil.getPPCMachine() == "pSeries": - f.write("nonvram\n") - f.write("fstype=raw\n") - - else: # Default non-destructive case for anything else. - f.write("nonvram\n") - f.write("mntpoint=/boot/yaboot\n") - f.write("usemount\n") - if not os.access(instRoot + "/boot/yaboot", os.R_OK): - os.mkdir(instRoot + "/boot/yaboot") - yabootProg = "/sbin/ybin" - - if self.password: - f.write("password=%s\n" %(self.password,)) - f.write("restricted\n") - - f.write("\n") - - rootDev = self.storage.rootDevice - - for (label, longlabel, version) in kernelList: - kernelTag = "-" + version - kernelFile = "%s/vmlinuz%s" %(cfPath, kernelTag) - - f.write("image=%s\n" %(kernelFile,)) - f.write("\tlabel=%s\n" %(label,)) - f.write("\tread-only\n") - - initrd = self.makeInitrd(kernelTag, instRoot) - if initrd: - f.write("\tinitrd=%s/%s\n" %(cfPath, initrd)) - - append = "%s" %(self.args.get(),) - - realroot = rootDev.fstabSpec - if rootIsDevice(realroot): - f.write("\troot=%s\n" %(realroot,)) - else: - if len(append) > 0: - append = "%s root=%s" %(append,realroot) - else: - append = "root=%s" %(realroot,) - - if len(append) > 0: - f.write("\tappend=\"%s\"\n" %(append,)) - f.write("\n") - - f.close() - os.chmod(instRoot + cf, 0600) - - # FIXME: hack to make sure things are written to disk - from pyanaconda import isys - isys.sync() - isys.sync() - isys.sync() - - ybinargs = [ yabootProg, "-f", "-C", cf ] - - rc = iutil.execWithRedirect(ybinargs[0], - ybinargs[1:], - stdout = "/dev/tty5", - stderr = "/dev/tty5", - root = instRoot) - if rc: - return rc - - if (not os.access(instRoot + "/etc/yaboot.conf", os.R_OK) and - os.access(instRoot + "/boot/etc/yaboot.conf", os.R_OK)): - os.symlink("../boot/etc/yaboot.conf", - instRoot + "/etc/yaboot.conf") - - return 0 - - def setPassword(self, val, isCrypted = 1): - # yaboot just handles the password and doesn't care if its crypted - # or not - self.password = val - - def write(self, instRoot, bl, kernelList, chainList, defaultDev): - if len(kernelList) >= 1: - rc = self.writeYaboot(instRoot, bl, kernelList, - chainList, defaultDev) - if rc: - return rc - else: - raise BootyNoKernelWarning - - return 0 - - def __init__(self, anaconda): - bootloaderInfo.__init__(self, anaconda) - self.useYabootVal = 1 - self.kernelLocation = "/boot" - self._configdir = "/etc" - self._configname = "yaboot.conf" diff --git a/pyanaconda/booty/s390.py b/pyanaconda/booty/s390.py deleted file mode 100644 index 38f6a9e..0000000 --- a/pyanaconda/booty/s390.py +++ /dev/null @@ -1,186 +0,0 @@ -import os - -from bootloaderInfo import * -from pyanaconda import iutil - -class s390BootloaderInfo(bootloaderInfo): - def getBootloaderConfig(self, instRoot, bl, kernelList, - chainList, defaultDev): - # on upgrade read in the lilo config file - lilo = LiloConfigFile () - self.perms = 0600 - confFile = instRoot + self.configfile - - if os.access (confFile, os.R_OK): - self.perms = os.stat(confFile)[0] & 0777 - lilo.read(confFile) - os.rename(confFile, confFile + ".rpmsave") - - # Remove any invalid entries that are in the file; we probably - # just removed those kernels. - for label in lilo.listImages(): - (fsType, sl, path, other) = lilo.getImage(label) - if fsType == "other": continue - - if not os.access(instRoot + sl.getPath(), os.R_OK): - lilo.delImage(label) - - rootDev = self.storage.rootDevice - - if rootDev.name == defaultDev.name: - lilo.addEntry("default", kernelList[0][0]) - else: - lilo.addEntry("default", chainList[0][0]) - - for (label, longlabel, version) in kernelList: - kernelTag = "-" + version - kernelFile = self.kernelLocation + "vmlinuz" + kernelTag - - try: - lilo.delImage(label) - except IndexError as msg: - pass - - sl = LiloConfigFile(imageType = "image", path = kernelFile) - - initrd = self.makeInitrd(kernelTag, instRoot) - - sl.addEntry("label", label) - if initrd: - sl.addEntry("initrd", "%s%s" %(self.kernelLocation, initrd)) - - sl.addEntry("read-only") - sl.addEntry("root", rootDev.path) - sl.addEntry("ipldevice", rootDev.path[:-1]) - - if self.args.get(): - sl.addEntry('append', '"%s"' % self.args.get()) - - lilo.addImage (sl) - - for (label, longlabel, device) in chainList: - if ((not label) or (label == "")): - continue - try: - (fsType, sl, path, other) = lilo.getImage(label) - lilo.delImage(label) - except IndexError: - sl = LiloConfigFile(imageType = "other", - path = "/dev/%s" %(device)) - sl.addEntry("optional") - - sl.addEntry("label", label) - lilo.addImage (sl) - - # Sanity check #1. There could be aliases in sections which conflict - # with the new images we just created. If so, erase those aliases - imageNames = {} - for label in lilo.listImages(): - imageNames[label] = 1 - - for label in lilo.listImages(): - (fsType, sl, path, other) = lilo.getImage(label) - if sl.testEntry('alias'): - alias = sl.getEntry('alias') - if imageNames.has_key(alias): - sl.delEntry('alias') - imageNames[alias] = 1 - - # Sanity check #2. If single-key is turned on, go through all of - # the image names (including aliases) (we just built the list) and - # see if single-key will still work. - if lilo.testEntry('single-key'): - singleKeys = {} - turnOff = 0 - for label in imageNames.keys(): - l = label[0] - if singleKeys.has_key(l): - turnOff = 1 - singleKeys[l] = 1 - if turnOff: - lilo.delEntry('single-key') - - return lilo - - def writeChandevConf(self, bl, instroot): # S/390 only - cf = "/etc/chandev.conf" - self.perms = 0644 - if bl.args.chandevget(): - fd = os.open(instroot + "/etc/chandev.conf", - os.O_WRONLY | os.O_CREAT) - os.write(fd, "noauto\n") - for cdev in bl.args.chandevget(): - os.write(fd,'%s\n' % cdev) - os.close(fd) - return "" - - - def writeZipl(self, instRoot, bl, kernelList, chainList, - defaultDev, justConfigFile): - rootDev = self.storage.rootDevice - - cf = '/etc/zipl.conf' - self.perms = 0600 - if os.access (instRoot + cf, os.R_OK): - self.perms = os.stat(instRoot + cf)[0] & 0777 - os.rename(instRoot + cf, - instRoot + cf + '.rpmsave') - - f = open(instRoot + cf, "w+") - - f.write('[defaultboot]\n') - if self.timeout: - f.write('timeout=%d\n' % self.timeout) - f.write('default=' + kernelList[0][0] + '\n') - f.write('target=%s\n' % (self.kernelLocation)) - - cfPath = "/boot/" - for (label, longlabel, version) in kernelList: - kernelTag = "-" + version - kernelFile = "%svmlinuz%s" % (cfPath, kernelTag) - - initrd = self.makeInitrd(kernelTag, instRoot) - f.write('[%s]\n' % (label)) - f.write('\timage=%s\n' % (kernelFile)) - if initrd: - f.write('\tramdisk=%s%s\n' %(self.kernelLocation, initrd)) - - realroot = rootDev.fstabSpec - f.write('\tparameters="root=%s' %(realroot,)) - if bl.args.get(): - f.write(' %s' % (bl.args.get())) - f.write('"\n') - - f.close() - - if not justConfigFile: - rc = iutil.execWithCapture("zipl", [], root = instRoot, - stderr = "/dev/stderr") - for line in rc.splitlines(): - if line.startswith("Preparing boot device: "): - # Output here may look like: - # Preparing boot device: dasdb (0200). - # Preparing boot device: dasdl. - # We want to extract the device name and pass that. - - fields = line[23:].split() - self.setDevice(fields[0].replace('.', '')) - - return 0 - - def write(self, instRoot, bl, kernelList, chainList, - defaultDev): - rc = self.writeZipl(instRoot, bl, kernelList, - chainList, defaultDev, - not self.useZiplVal) - if rc: - return rc - - return self.writeChandevConf(bl, instRoot) - - def __init__(self, anaconda): - bootloaderInfo.__init__(self, anaconda) - self.useZiplVal = 1 # only used on s390 - self.kernelLocation = "/boot/" - self._configdir = "/etc" - self._configname = "zipl.conf" diff --git a/pyanaconda/booty/sparc.py b/pyanaconda/booty/sparc.py deleted file mode 100644 index aad3b00..0000000 --- a/pyanaconda/booty/sparc.py +++ /dev/null @@ -1,128 +0,0 @@ -import string -import os - -from pyanaconda.booty import BootyNoKernelWarning -from util import getDiskPart -from bootloaderInfo import * -from pyanaconda import iutil - -class sparcBootloaderInfo(bootloaderInfo): - def writeSilo(self, instRoot, bl, kernelList, - chainList, defaultDev): - - try: - bootDev = self.storage.mountpoints["/boot"] - - mf = '/silo.message' - cf = "/boot/silo.conf" - mfdir = '/boot' - cfPath = "" - if not os.path.isdir(instRoot + "/boot"): - os.mkdir(instRoot + "/boot") - except KeyError: - bootDev = self.storage.rootDevice - - cf = "/etc/silo.conf" - mfdir = '/etc' - cfPath = "/boot" - - f = open(instRoot + mfdir + mf, "w+") - f.write("Welcome to %s!\nHit for boot options\n\n" % productName) - f.close() - os.chmod(instRoot + mfdir + mf, 0600) - - f = open(instRoot + cf, "w+") - f.write("# silo.conf generated by anaconda\n\n") - - f.write("#boot=%s\n" % (bootDev.path,)) - f.write("message=%s\n" % (mf,)) - f.write("timeout=%s\n" % (self.timeout or 50)) - - (disk, partNum) = getDiskPart(bootDev) - f.write("partition=%s\n" % (partNum,)) - - if self.password: - f.write("password=%s\n" % (self.password,)) - f.write("restricted\n") - - f.write("default=%s\n" % (kernelList[0][0],)) - f.write("\n") - - rootDev = self.storage.rootDevice - - for (label, longlabel, version) in kernelList: - kernelTag = "-" + version - kernelFile = "%s/vmlinuz%s" % (cfPath, kernelTag) - - f.write("image=%s\n" % (kernelFile,)) - f.write("\tlabel=%s\n" % (label,)) - f.write("\tread-only\n") - - initrd = self.makeInitrd(kernelTag, instRoot) - if initrd: - f.write("\tinitrd=%s/%s\n" % (cfPath, initrd)) - - append = "%s" % (self.args.get(),) - - realroot = rootDev.fstabSpec - if rootIsDevice(realroot): - f.write("\troot=%s\n" % (realroot,)) - else: - if len(append) > 0: - append = "%s root=%s" % (append, realroot) - else: - append = "root=%s" % (realroot,) - - if len(append) > 0: - f.write("\tappend=\"%s\"\n" % (append,)) - f.write("\n") - - f.close() - os.chmod(instRoot + cf, 0600) - - # FIXME: hack to make sure things are written to disk - from pyanaconda import isys - isys.sync() - isys.sync() - isys.sync() - - backup = "%s/backup.b" % (cfPath,) - sbinargs = ["/sbin/silo", "-f", "-C", cf, "-S", backup] - if (iutil.getSparcMachine() == "sun4u" or iutil.getSparcMachine() == "sun4v"): - sbinargs += ["-u"] - else: - sbinargs += ["-U"] - - rc = iutil.execWithRedirect(sbinargs[0], - sbinargs[1:], - stdout = "/dev/tty5", - stderr = "/dev/tty5", - root = instRoot) - if rc: - return rc - - if (not os.access(instRoot + "/etc/silo.conf", os.R_OK) and - os.access(instRoot + "/boot/silo.conf", os.R_OK)): - os.symlink("../boot/silo.conf", - instRoot + "/etc/silo.conf") - - return 0 - - def setPassword(self, val, isCrypted = 1): - # silo just handles the password unencrypted - self.password = val - - def write(self, instRoot, bl, kernelList, chainList, - defaultDev): - if len(kernelList) >= 1: - return self.writeSilo(instRoot, bl, kernelList, chainList, - defaultDev) - else: - raise BootyNoKernelWarning - - def __init__(self, anaconda): - bootloaderInfo.__init__(self, anaconda) - self.useSiloVal = 1 - self.kernelLocation = "/boot" - self._configdir = "/etc" - self._configname = "silo.conf" diff --git a/pyanaconda/booty/util.py b/pyanaconda/booty/util.py deleted file mode 100644 index ab33ae1..0000000 --- a/pyanaconda/booty/util.py +++ /dev/null @@ -1,9 +0,0 @@ -def getDiskPart(dev): - if dev.type == "partition": - partNum = dev.partedPartition.number - disk = dev.disk - else: - partNum = None - disk = dev - - return (disk, partNum) diff --git a/pyanaconda/booty/x86.py b/pyanaconda/booty/x86.py deleted file mode 100644 index 003901a..0000000 --- a/pyanaconda/booty/x86.py +++ /dev/null @@ -1,562 +0,0 @@ -import os -import string - -from pyanaconda.booty import BootyNoKernelWarning -from util import getDiskPart -from bootloaderInfo import * -from pyanaconda.flags import flags -import checkbootloader -from pyanaconda import iutil - -import logging -log = logging.getLogger("anaconda") - -class x86BootloaderInfo(efiBootloaderInfo): - def setPassword(self, val, isCrypted = 1): - if not val: - self.password = val - self.pure = val - return - - if isCrypted and self.useGrubVal == 0: - self.pure = None - return - elif isCrypted: - self.password = val - self.pure = None - else: - salt = "$6$" - saltLen = 16 - - saltchars = string.letters + string.digits + './' - rnd = random.SystemRandom() - for i in range(saltLen): - salt += rnd.choice(saltchars) - - self.password = crypt.crypt(val, salt) - self.pure = val - - def getPassword (self): - return self.pure - - def setUseGrub(self, val): - self.useGrubVal = val - - def getPhysicalDevices(self, dev): - # This finds a list of devices on which the given device name resides. - # Accepted values for "device" are raid1 md devices (i.e. "md0"), - # physical disks ("hda"), and real partitions on physical disks - # ("hda1"). Anything else gets ignored. - if dev.type == "mdarray": - if dev.level != 1: - log.error("x86BootloaderInfo.getPhysicalDevices ignoring non " - "level 1 raid array %s" % dev.name) - return [] - devs = dev.parents - else: - devs = [ dev ] - - physicalDevices = [] - for dev in devs: - if dev in self.storage.disks or dev.type == "partition": - physicalDevices.append(dev) - else: - log.error("x86BootloaderInfo.getPhysicalDevices ignoring %s" % - dev.name) - - return physicalDevices - - def runGrubInstall(self, instRoot, bootDev, cmds, cfPath): - if cfPath == "/": - syncDataToDisk(bootDev, "/boot", instRoot) - else: - syncDataToDisk(bootDev, "/", instRoot) - - # copy the stage files over into /boot - rc = iutil.execWithRedirect("/sbin/grub-install", - ["--just-copy"], - stdout = "/dev/tty5", stderr = "/dev/tty5", - root = instRoot) - if rc: - return rc - - # really install the bootloader - for cmd in cmds: - p = os.pipe() - os.write(p[1], cmd + '\n') - os.close(p[1]) - - # FIXME: hack to try to make sure everything is written - # to the disk - if cfPath == "/": - syncDataToDisk(bootDev, "/boot", instRoot) - else: - syncDataToDisk(bootDev, "/", instRoot) - - rc = iutil.execWithRedirect('/sbin/grub' , - [ "--batch", "--no-floppy", - "--device-map=/boot/grub/device.map" ], - stdin = p[0], - stdout = "/dev/tty5", stderr = "/dev/tty5", - root = instRoot) - os.close(p[0]) - - if rc: - return rc - - def matchingBootTargets(self, stage1Devs, bootDevs): - matches = [] - for stage1Dev in stage1Devs: - for mdBootPart in bootDevs: - if getDiskPart(stage1Dev)[0] == getDiskPart(mdBootPart)[0]: - matches.append((stage1Dev, mdBootPart)) - return matches - - def addMemberMbrs(self, matches, bootDevs): - updatedMatches = list(matches) - bootDevsHavingStage1Dev = [match[1] for match in matches] - for mdBootPart in bootDevs: - if mdBootPart not in bootDevsHavingStage1Dev: - updatedMatches.append((getDiskPart(mdBootPart)[0], mdBootPart)) - return updatedMatches - - def installGrub(self, instRoot, bootDev, grubTarget, grubPath, cfPath): - if iutil.isEfi(): - return efiBootloaderInfo.installGrub(self, instRoot, bootDev, grubTarget, - grubPath, cfPath) - - args = "--stage2=/boot/grub/stage2 " - - stage1Devs = self.getPhysicalDevices( - self.storage.devicetree.getDeviceByName(grubTarget)) - bootDevs = self.getPhysicalDevices(bootDev) - - installs = [(None, - self.grubbyPartitionName(stage1Devs[0]), - self.grubbyPartitionName(bootDevs[0]))] - - if bootDev.type == "mdarray": - - matches = self.matchingBootTargets(stage1Devs, bootDevs) - - # If the stage1 target disk contains member of boot raid array (mbr - # case) or stage1 target partition is member of boot raid array - # (partition case) - if matches: - # 1) install stage1 on target disk/partiton - stage1Dev, mdMemberBootPart = matches[0] - installs = [(None, - self.grubbyPartitionName(stage1Dev), - self.grubbyPartitionName(mdMemberBootPart))] - firstMdMemberDiskGrubbyName = self.grubbyDiskName( - getDiskPart(mdMemberBootPart)[0]) - - # 2) and install stage1 on other members' disks/partitions too - # NOTES: - # - the goal is to be able to boot after a members' disk removal - # - so we have to use grub device names as if after removal - # (i.e. the same disk name (e.g. (hd0)) for both member disks) - # - if member partitions have different numbers only removal of - # specific one of members will work because stage2 containing - # reference to config file is shared and therefore can contain - # only one value - - # if target is mbr, we want to install also to mbr of other - # members, so extend the matching list - matches = self.addMemberMbrs(matches, bootDevs) - for stage1Target, mdMemberBootPart in matches[1:]: - # prepare special device mapping corresponding to member removal - mdMemberBootDisk = getDiskPart(mdMemberBootPart)[0] - # It can happen due to ks --driveorder option, but is it ok? - if not mdMemberBootDisk.name in self.drivelist: - continue - mdRaidDeviceRemap = (firstMdMemberDiskGrubbyName, - mdMemberBootDisk.name) - - stage1TargetGrubbyName = self.grubbyPartitionName(stage1Target) - rootPartGrubbyName = self.grubbyPartitionName(mdMemberBootPart) - - # now replace grub disk name part according to special device - # mapping - old = self.grubbyDiskName(mdMemberBootDisk).strip('() ') - new = firstMdMemberDiskGrubbyName.strip('() ') - rootPartGrubbyName = rootPartGrubbyName.replace(old, new) - stage1TargetGrubbyName = stage1TargetGrubbyName.replace(old, new) - - installs.append((mdRaidDeviceRemap, - stage1TargetGrubbyName, - rootPartGrubbyName)) - - # This is needed for case when /boot member partitions have - # different numbers. Shared stage2 can contain only one reference - # to grub.conf file, so let's ensure that it is reference to partition - # on disk which we will boot from - that is, install grub to - # this disk as last so that its reference is not overwritten. - installs.reverse() - - cmds = [] - for mdRaidDeviceRemap, stage1Target, rootPart in installs: - if mdRaidDeviceRemap: - cmd = "device (%s) /dev/%s\n" % tuple(mdRaidDeviceRemap) - else: - cmd = '' - cmd += "root %s\n" % (rootPart,) - cmd += "install %s%s/stage1 d %s %s/stage2 p %s%s/grub.conf" % \ - (args, grubPath, stage1Target, grubPath, rootPart, grubPath) - cmds.append(cmd) - return self.runGrubInstall(instRoot, bootDev.name, cmds, cfPath) - - def writeGrub(self, instRoot, bl, kernelList, chainList, - defaultDev, upgrade=False): - - rootDev = self.storage.rootDevice - grubTarget = bl.getDevice() - - try: - bootDev = self.storage.mountpoints["/boot"] - grubPath = "/grub" - cfPath = "/" - except KeyError: - bootDev = rootDev - grubPath = "/boot/grub" - cfPath = "/boot/" - - if not upgrade and not iutil.isEfi(): - self.writeGrubConf(instRoot, bootDev, rootDev, defaultDev, - kernelList, chainList, grubTarget, grubPath, - cfPath) - - # keep track of which devices are used for the device.map - usedDevs = set() - usedDevs.update(self.getPhysicalDevices( - self.storage.devicetree.getDeviceByName(grubTarget))) - usedDevs.update(self.getPhysicalDevices(bootDev)) - usedDevs.update([self.storage.devicetree.getDeviceByName(dev) for - (label, longlabel, dev) in chainList if longlabel]) - - if not upgrade: - self.writeDeviceMap(instRoot, usedDevs, upgrade) - self.writeSysconfig(instRoot, grubTarget, upgrade) - - ret = self.installGrub(instRoot, bootDev, grubTarget, grubPath, cfPath) - if iutil.isEfi(): - self.writeGrubConf(instRoot, bootDev, rootDev, defaultDev, - kernelList, chainList, grubTarget, grubPath, - cfPath) - return ret - - def writeGrubConf(self, instRoot, bootDev, rootDev, defaultDev, kernelList, - chainList, grubTarget, grubPath, cfPath): - - bootDevs = self.getPhysicalDevices(bootDev) - - # XXX old config file should be read here for upgrade - - cf = "%s%s" % (instRoot, self.configfile) - self.perms = 0600 - if os.access (cf, os.R_OK): - self.perms = os.stat(cf)[0] & 0777 - os.rename(cf, cf + '.rpmsave') - - f = open(cf, "w+") - - f.write("# grub.conf generated by anaconda\n") - f.write("#\n") - f.write("# Note that you do not have to rerun grub " - "after making changes to this file\n") - - if grubPath == "/grub": - f.write("# NOTICE: You have a /boot partition. This means " - "that\n") - f.write("# all kernel and initrd paths are relative " - "to /boot/, eg.\n") - else: - f.write("# NOTICE: You do not have a /boot partition. " - "This means that\n") - f.write("# all kernel and initrd paths are relative " - "to /, eg.\n") - - f.write('# root %s\n' % self.grubbyPartitionName(bootDevs[0])) - f.write("# kernel %svmlinuz-version ro root=%s\n" % (cfPath, rootDev.path)) - f.write("# initrd %sinitrd-[generic-]version.img\n" % (cfPath)) - f.write("#boot=/dev/%s\n" % (grubTarget)) - - if iutil.isEfi(): - from pyanaconda.product import productName - # Map the target device to the full EFI path - if self.getEfiProductPath(productName): - (n, pn) = getDiskPart(bootDevs[0]) - f.write("device (%s) %s\n" % (self.grubbyDiskName(n), self.getEfiProductPath(productName))) - - # get the default image to boot... we have to walk and find it - # since grub indexes by where it is in the config file - if defaultDev.name == rootDev.name: - default = 0 - else: - # if the default isn't linux, it's the first thing in the - # chain list - default = len(kernelList) - - - f.write('default=%s\n' % (default)) - - if self.serial == 1: - # Set the global timeout in serial case - f.write('timeout=%d\n' % (self.timeout or 5)) - # grub the 0-based number of the serial console device - unit = self.serialDevice[-1] - - # and we want to set the speed too - speedend = 0 - for char in self.serialOptions: - if char not in string.digits: - break - speedend = speedend + 1 - if speedend != 0: - speed = self.serialOptions[:speedend] - else: - # reasonable default - speed = "9600" - - f.write("serial --unit=%s --speed=%s\n" %(unit, speed)) - f.write("terminal --timeout=%s serial console\n" % (self.timeout or 5)) - else: - # Default to 0 timeout in the non-serial case - f.write('timeout=%d\n' % (self.timeout or 0)) - # we only want splashimage if they're not using a serial console - if os.access("%s/boot/grub/splash.xpm.gz" %(instRoot,), os.R_OK): - f.write('splashimage=%s%sgrub/splash.xpm.gz\n' - % (self.grubbyPartitionName(bootDevs[0]), cfPath)) - f.write("hiddenmenu\n") - - - if self.password: - f.write('password --encrypted %s\n' %(self.password)) - - for (label, longlabel, version) in kernelList: - kernelTag = "-" + version - kernelFile = "%svmlinuz%s" % (cfPath, kernelTag) - - initrd = self.makeInitrd(kernelTag, instRoot) - - f.write('title %s (%s)\n' % (longlabel, version)) - f.write('\troot %s\n' % self.grubbyPartitionName(bootDevs[0])) - - realroot = " root=%s" % rootDev.fstabSpec - - if version.endswith("xen0") or (version.endswith("xen") and not os.path.exists("/proc/xen")): - # hypervisor case - sermap = { "ttyS0": "com1", "ttyS1": "com2", - "ttyS2": "com3", "ttyS3": "com4" } - if self.serial and sermap.has_key(self.serialDevice) and \ - self.serialOptions: - hvs = "%s=%s" %(sermap[self.serialDevice], - self.serialOptions) - else: - hvs = "" - if version.endswith("xen0"): - hvFile = "%sxen.gz-%s %s" %(cfPath, - version.replace("xen0", ""), - hvs) - else: - hvFile = "%sxen.gz-%s %s" %(cfPath, - version.replace("xen", ""), - hvs) - f.write('\tkernel %s\n' %(hvFile,)) - f.write('\tmodule %s ro%s' %(kernelFile, realroot)) - if self.args.get(): - f.write(' %s' % self.args.get()) - f.write('\n') - - if initrd: - f.write('\tmodule %s%s\n' % (cfPath, initrd)) - else: # normal kernel - f.write('\tkernel %s ro%s' % (kernelFile, realroot)) - if self.args.get(): - f.write(' %s' % self.args.get()) - f.write('\n') - - if initrd: - f.write('\tinitrd %s%s\n' % (cfPath, initrd)) - - for (label, longlabel, device) in chainList: - if ((not longlabel) or (longlabel == "")): - continue - f.write('title %s\n' % (longlabel)) - f.write('\trootnoverify %s\n' % self.grubbyPartitionName( - self.storage.devicetree.getDeviceByName(device))) -# f.write('\tmakeactive\n') - f.write('\tchainloader +1') - f.write('\n') - - f.close() - - if not "/efi/" in cf: - os.chmod(cf, self.perms) - - try: - # make symlink for menu.lst (default config file name) - menulst = "%s%s/menu.lst" % (instRoot, self.configdir) - if os.access (menulst, os.R_OK): - os.rename(menulst, menulst + ".rpmsave") - os.symlink("./grub.conf", menulst) - except: - pass - - try: - # make symlink for /etc/grub.conf (config files belong in /etc) - etcgrub = "%s%s" % (instRoot, "/etc/grub.conf") - if os.access (etcgrub, os.R_OK): - os.rename(etcgrub, etcgrub + ".rpmsave") - os.symlink(".." + self.configfile, etcgrub) - except: - pass - - def writeDeviceMap(self, instRoot, usedDevs, upgrade=False): - - if os.access(instRoot + "/boot/grub/device.map", os.R_OK): - # For upgrade, we want also e.g. devs that has been added - # to file during install for chainloading. - if upgrade: - f = open(instRoot + "/boot/grub/device.map", "r") - for line in f: - if line.startswith('(hd'): - (grubdisk, dev) = line.split()[:2] - dev = dev[5:] - if dev in self.drivelist: - usedDevs.add( - self.storage.devicetree.getDeviceByName(dev)) - f.close() - os.rename(instRoot + "/boot/grub/device.map", - instRoot + "/boot/grub/device.map.rpmsave") - - f = open(instRoot + "/boot/grub/device.map", "w+") - f.write("# this device map was generated by anaconda\n") - usedDiskDevs = set() - for dev in usedDevs: - drive = getDiskPart(dev)[0] - usedDiskDevs.add(drive) - devs = list(usedDiskDevs) - devs.sort(key=lambda d: d.name) - for dev in devs: - f.write("(%s) %s\n" % (self.grubbyDiskName(dev), dev.path)) - f.close() - - def writeSysconfig(self, instRoot, grubTarget, upgrade): - sysconf = '/etc/sysconfig/grub' - if os.access (instRoot + sysconf, os.R_OK): - if upgrade: - return - self.perms = os.stat(instRoot + sysconf)[0] & 0777 - os.rename(instRoot + sysconf, - instRoot + sysconf + '.rpmsave') - # if it's an absolute symlink, just get it out of our way - elif (os.path.islink(instRoot + sysconf) and - os.readlink(instRoot + sysconf)[0] == '/'): - if upgrade: - return - os.rename(instRoot + sysconf, - instRoot + sysconf + '.rpmsave') - f = open(instRoot + sysconf, 'w+') - f.write("boot=/dev/%s\n" %(grubTarget,)) - f.write("forcelba=0\n") - f.close() - - def grubbyDiskName(self, dev): - return "hd%d" % self.drivelist.index(dev.name) - - def grubbyPartitionName(self, dev): - (disk, partNum) = getDiskPart(dev) - if partNum != None: - return "(%s,%d)" % (self.grubbyDiskName(disk), partNum - 1) - else: - return "(%s)" %(self.grubbyDiskName(disk)) - - def getBootloaderConfig(self, instRoot, bl, kernelList, - chainList, defaultDev): - config = bootloaderInfo.getBootloaderConfig(self, instRoot, - bl, kernelList, chainList, - defaultDev) - - liloTarget = bl.getDevice() - - config.addEntry("boot", '/dev/' + liloTarget, replace = 0) - config.addEntry("map", "/boot/map", replace = 0) - config.addEntry("install", "/boot/boot.b", replace = 0) - message = "/boot/message" - - if self.pure is not None and not self.useGrubVal: - config.addEntry("restricted", replace = 0) - config.addEntry("password", self.pure, replace = 0) - - if self.serial == 1: - # grab the 0-based number of the serial console device - unit = self.serialDevice[-1] - # FIXME: we should probably put some options, but lilo - # only supports up to 9600 baud so just use the defaults - # it's better than nothing :( - config.addEntry("serial=%s" %(unit,)) - else: - # message screws up serial console - if os.access(instRoot + message, os.R_OK): - config.addEntry("message", message, replace = 0) - - if not config.testEntry('lba32'): - if bl.above1024 and not iutil.isX86(bits=32): - config.addEntry("lba32", replace = 0) - - return config - - def write(self, instRoot, bl, kernelList, chainList, - defaultDev): - if self.timeout is None and chainList: - self.timeout = 5 - - # XXX HACK ALERT - see declaration above - if self.doUpgradeOnly: - if self.useGrubVal: - return self.writeGrub(instRoot, bl, kernelList, - chainList, defaultDev, - upgrade = True) - return 0 - - if len(kernelList) < 1: - raise BootyNoKernelWarning - - rc = self.writeGrub(instRoot, bl, kernelList, - chainList, defaultDev, - not self.useGrubVal) - if rc: - return rc - - # XXX move the lilo.conf out of the way if they're using GRUB - # so that /sbin/installkernel does a more correct thing - if self.useGrubVal and os.access(instRoot + '/etc/lilo.conf', os.R_OK): - os.rename(instRoot + "/etc/lilo.conf", - instRoot + "/etc/lilo.conf.anaconda") - - return 0 - - def getArgList(self): - args = bootloaderInfo.getArgList(self) - - if self.password: - args.append("--password=%s" %(self.password)) - - return args - - def __init__(self, anaconda): - bootloaderInfo.__init__(self, anaconda) - - # these have to be set /before/ efiBootloaderInfo.__init__(), or - # they'll be overwritten. - self._configdir = "/boot/grub" - self._configname = "grub.conf" - - efiBootloaderInfo.__init__(self, anaconda, initialize=False) - - # XXX use checkbootloader to determine what to default to - self.useGrubVal = 1 - self.kernelLocation = "/boot/" - self.password = None - self.pure = None diff --git a/pyanaconda/dispatch.py b/pyanaconda/dispatch.py index f111f84..c67e96a 100644 --- a/pyanaconda/dispatch.py +++ b/pyanaconda/dispatch.py @@ -92,7 +92,6 @@ installSteps = [ ("storagedone", storageComplete, ), ("enablefilesystems", turnOnFilesystems, ), ("upgbootloader", ), - ("bootloadersetup", bootloaderSetupChoices, ), ("bootloader", ), ("reposetup", doBackendSetup, ), ("tasksel", ), diff --git a/pyanaconda/iw/blpasswidget.py b/pyanaconda/iw/blpasswidget.py index 4b377b5..6655be3 100644 --- a/pyanaconda/iw/blpasswidget.py +++ b/pyanaconda/iw/blpasswidget.py @@ -31,12 +31,9 @@ class BootloaderPasswordWidget: self.parent = parent self.intf = anaconda.intf - if anaconda.bootloader.getPassword(): - usePass = 1 - self.password = anaconda.bootloader.getPassword() - else: - usePass = 0 - self.password = None + self.password = anaconda.platform.bootloader.password + if self.password: + usePass = True vbox = gtk.VBox(False, 6) @@ -44,12 +41,8 @@ class BootloaderPasswordWidget: self.usePassCb = gtk.CheckButton(_("_Use a boot loader password")) self.usePassCb.set_tooltip_text(_("A boot loader password prevents users from changing kernel options, increasing security.")) self.passButton = gtk.Button("No password") - if usePass: - self.usePassCb.set_active(True) - self.passButton.set_sensitive(True) - else: - self.usePassCb.set_active(False) - self.passButton.set_sensitive(False) + self.usePassCb.set_active(usePass) + self.passButton.set_sensitive(usePass) self.usePassCb.connect("toggled", self.passCallback) self.passButton.connect("clicked", self.passButtonCallback) self.setPassLabel() @@ -130,7 +123,7 @@ class BootloaderPasswordWidget: dialog.show_all() - while 1: + while True: rc = dialog.run() if rc in [gtk.RESPONSE_CANCEL, gtk.RESPONSE_DELETE_EVENT]: rc = False diff --git a/pyanaconda/iw/bootloader_main_gui.py b/pyanaconda/iw/bootloader_main_gui.py index 3512026..738e24f 100644 --- a/pyanaconda/iw/bootloader_main_gui.py +++ b/pyanaconda/iw/bootloader_main_gui.py @@ -47,24 +47,19 @@ class MainBootloaderWindow(InstallWindow): def getNext(self): # go ahead and set the device even if we already knew it # since that won't change anything - self.bl.setDevice(self.bldev) - - self.bl.drivelist = self.driveorder + self.bl._target_device = self.bldev + self.bl.drive_order = self.driveorder if not self.grubCB.get_active(): # if we're not installing a boot loader, don't show the second # screen and don't worry about other options self.dispatch.skipStep("instbootloader", skip = 1) - - # kind of a hack... - self.bl.defaultDevice = None return else: self.dispatch.skipStep("instbootloader", skip = 0) - self.bl.setUseGrub(1) # set the password - self.bl.setPassword(self.blpass.getPassword(), isCrypted = 0) + self.bl.password = self.blpass.getPassword() # set the bootloader images based on what's in our list self.oslist.setBootloaderImages() @@ -88,7 +83,7 @@ class MainBootloaderWindow(InstallWindow): first = combo.get_model()[iter][1] desc = choices["mbr"][1] - dxml.get_widget("mbrRadio").set_label("%s - /dev/%s" %(_(desc), first)) + dxml.get_widget("mbrRadio").set_label("%s - %s" %(_(desc), first.path)) dxml.get_widget("mbrRadio").set_data("bootDevice", first) def __genStore(combo, disks, active): @@ -114,13 +109,13 @@ class MainBootloaderWindow(InstallWindow): dialog.set_transient_for(self.parent) dialog.show() - choices = anaconda.platform.bootloaderChoices(self.bl) + choices = anaconda.platform.bootloaderChoices for t in ("mbr", "boot"): if not choices.has_key(t): continue (device, desc) = choices[t] w = dxml.get_widget("%sRadio" %(t,)) - w.set_label("%s - /dev/%s" %(_(desc), device)) + w.set_label("%s - %s" %(_(desc), device.path)) w.show() if self.bldev == device: w.set_active(True) @@ -128,6 +123,7 @@ class MainBootloaderWindow(InstallWindow): w.set_active(False) w.set_data("bootDevice", device) + bl_disks = anaconda.platform.bootloader.drives for i in range(1, 5): if len(self.driveorder) < i: break @@ -135,15 +131,12 @@ class MainBootloaderWindow(InstallWindow): lbl = dxml.get_widget("bd%dLabel" %(i,)) combo.show() lbl.show() - partitioned = anaconda.storage.partitioned - disks = anaconda.storage.disks - bl_disks = [d for d in disks if d in partitioned] m = __genStore(combo, bl_disks, self.driveorder[i - 1]) dxml.get_widget("bd1Combo").connect("changed", __driveChange, dxml, choices) __driveChange(dxml.get_widget("bd1Combo"), dxml, choices) - while 1: + while True: rc = dialog.run() if rc in [gtk.RESPONSE_CANCEL, gtk.RESPONSE_DELETE_EVENT]: break @@ -174,55 +167,28 @@ class MainBootloaderWindow(InstallWindow): if d not in neworder: neworder.append(d) self.driveorder = neworder - break dialog.destroy() - self.grubCB.set_label(_("_Install boot loader on /dev/%s.") % - (self.bldev,)) + self.grubCB.set_label(_("_Install boot loader on %s.") % + (self.bldev.path,)) return rc - def _setBLCBText(self): - self.grubCB.set_label(_("_Install boot loader on /dev/%s.") % - (self.bldev,)) - - def getScreen(self, anaconda): self.dispatch = anaconda.dispatch self.bl = anaconda.bootloader self.intf = anaconda.intf - - self.driveorder = self.bl.drivelist - if len(self.driveorder) == 0: - partitioned = anaconda.storage.partitioned - disks = anaconda.storage.disks - self.driveorder = [d.name for d in disks if d in partitioned] - - if self.bl.getPassword(): - self.usePass = 1 - self.password = self.bl.getPassword() - else: - self.usePass = 0 - self.password = None + self.driveorder = [d.name for d in self.bl.drives] thebox = gtk.VBox (False, 12) thebox.set_border_width(18) # make sure we get a valid device to say we're installing to - if self.bl.getDevice() is not None: - self.bldev = self.bl.getDevice() - else: - # we don't know what it is yet... if mbr is possible, we want - # it, else we want the boot dev - choices = anaconda.platform.bootloaderChoices(self.bl) - if choices.has_key('mbr'): - self.bldev = choices['mbr'][0] - else: - self.bldev = choices['boot'][0] + self.bldev = self.bl.stage1_device hb = gtk.HBox(False, 12) - self.grubCB = gtk.CheckButton(_("_Install boot loader on /dev/%s.") % - (self.bldev,)) + self.grubCB = gtk.CheckButton(_("_Install boot loader on %s.") % + (self.bldev.path,)) self.grubCB.set_active(not self.dispatch.stepInSkipList("instbootloader")) self.grubCB.connect("toggled", self.bootloaderChanged) hb.pack_start(self.grubCB, False) diff --git a/pyanaconda/iw/osbootwidget.py b/pyanaconda/iw/osbootwidget.py index 9fc3dbd..4cca438 100644 --- a/pyanaconda/iw/osbootwidget.py +++ b/pyanaconda/iw/osbootwidget.py @@ -27,6 +27,7 @@ from pyanaconda import gui import datacombo from pyanaconda.constants import * from pyanaconda.storage.devices import devicePathToName +import copy import gettext _ = lambda x: gettext.ldgettext("anaconda", x) @@ -34,15 +35,12 @@ _ = lambda x: gettext.ldgettext("anaconda", x) class OSBootWidget: """Widget to display OSes to boot and allow adding new ones.""" - def __init__(self, anaconda, parent, blname = None): - self.bl = anaconda.bootloader + def __init__(self, anaconda, parent): + self.bl = anaconda.platform.bootloader self.storage = anaconda.storage self.parent = parent self.intf = anaconda.intf - if blname is not None: - self.blname = blname - else: - self.blname = "GRUB" + self.blname = self.bl.name self.setIllegalChars() @@ -59,9 +57,8 @@ class OSBootWidget: sw.set_size_request(300, 100) box.pack_start(sw, True) - self.osStore = gtk.ListStore(gobject.TYPE_BOOLEAN, gobject.TYPE_STRING, - gobject.TYPE_STRING, gobject.TYPE_BOOLEAN) + gobject.TYPE_STRING, gobject.TYPE_PYOBJECT) self.osTreeView = gtk.TreeView(self.osStore) theColumns = [ _("Default"), _("Label"), _("Device") ] @@ -86,8 +83,11 @@ class OSBootWidget: sw.add(self.osTreeView) self.osTreeView.connect('row-activated', self.osTreeActivateCb) - self.imagelist = self.bl.images.getImages() - self.defaultDev = self.bl.images.getDefault() + self.images = {} + for image in self.bl.images: + self.images[image.device.name] = copy.copy(image) + + self.defaultDev = self.bl.default.device self.fillOSList() buttonbar = gtk.VButtonBox() @@ -126,7 +126,7 @@ class OSBootWidget: self.fillOSList() # adds/edits a new "other" os to the boot loader config - def editOther(self, oldDevice, oldLabel, isDefault, isRoot = 0): + def editOther(self, image): dialog = gtk.Dialog(_("Image"), self.parent) dialog.add_button('gtk-cancel', gtk.RESPONSE_CANCEL) dialog.add_button('gtk-ok', 1) @@ -152,7 +152,7 @@ class OSBootWidget: label = gui.MnemonicLabel(_("_Device")) table.attach(label, 0, 1, 2, 3, gtk.FILL, 0, 10) - if not isRoot: + if not image.device == self.storage.rootDevice: parts = [] for part in self.storage.partitions: @@ -167,8 +167,8 @@ class OSBootWidget: defindex = 0 i = 0 for part in parts: - deviceCombo.append(part.path, part.name) - if oldDevice and oldDevice == part.name: + deviceCombo.append(part.path, part) + if image.device and image.device == part: defindex = i i = i + 1 @@ -178,22 +178,17 @@ class OSBootWidget: table.attach(deviceCombo, 1, 2, 2, 3, gtk.FILL, 0, 10) label.set_mnemonic_widget(deviceCombo) else: - table.attach(gtk.Label(oldDevice), 1, 2, 2, 3, gtk.FILL, 0, 10) + table.attach(gtk.Label(image.device.name), 1, 2, 2, 3, gtk.FILL, 0, 10) default = gtk.CheckButton(_("Default Boot _Target")) table.attach(default, 0, 2, 3, 4, gtk.FILL, 0, 10) - if isDefault != 0: - default.set_active(True) - - if self.numentries == 1 and oldDevice != None: + default.set_active(image.device == self.defaultDev) + if len(self.images.keys()) == 1 and image.device: default.set_sensitive(False) - else: - default.set_sensitive(True) - dialog.vbox.pack_start(table) dialog.show_all() - while 1: + while True: rc = dialog.run() # cancel @@ -202,10 +197,10 @@ class OSBootWidget: label = labelEntry.get_text() - if not isRoot: + if not image.device == self.storage.rootDevice: dev = deviceCombo.get_active_value() else: - dev = oldDevice + dev = image.device if not dev: self.intf.messageWindow(_("Error"), @@ -234,17 +229,15 @@ class OSBootWidget: # verify that the label hasn't been used foundBad = 0 - for key in self.imagelist.keys(): - if dev == key: + for key in self.images.keys(): + if dev.name == key: continue - if self.blname == "GRUB": - thisLabel = self.imagelist[key][1] - else: - thisLabel = self.imagelist[key][0] + + thisLabel = self.bl.image_label(self.images[key]) # if the label is the same as it used to be, they must # have changed the device which is fine - if thisLabel == oldLabel: + if thisLabel == image.label: continue if thisLabel == label: @@ -258,32 +251,23 @@ class OSBootWidget: continue # they could be duplicating a device, which we don't handle - if dev in self.imagelist.keys() and (not oldDevice or - dev != oldDevice): + if dev.name in self.images.keys() and (not image.device or + dev != image.device): self.intf.messageWindow(_("Duplicate Device"), _("This device is already being " "used for another boot entry."), type="warning") continue - # if we're editing a previous, get what the old info was for - # labels. otherwise, make it something safe for grub and the - # device name for lilo for lack of any better ideas - if oldDevice: - (oldshort, oldlong, oldisroot) = self.imagelist[oldDevice] - else: - (oldshort, oldlong, oldisroot) = (dev, label, None) - # if we're editing and the device has changed, delete the old - if oldDevice and dev != oldDevice: - del self.imagelist[oldDevice] + if image.device and dev != image.device: + del self.images[image.device.name] + image.device = dev + + image.label = label # go ahead and add it - if self.blname == "GRUB": - self.imagelist[dev] = (oldshort, label, isRoot) - else: - self.imagelist[dev] = (label, oldlong, isRoot) - + self.images[dev.name] = image if default.get_active(): self.defaultDev = dev @@ -299,27 +283,22 @@ class OSBootWidget: if not iter: return None - dev = devicePathToName(model.get_value(iter, 2)) - label = model.get_value(iter, 1) - isRoot = model.get_value(iter, 3) - isDefault = model.get_value(iter, 0) - return (dev, label, isDefault, isRoot) - + return model.get_value(iter, 3) def addEntry(self, widget, *args): - self.editOther(None, None, 0) + image = bootloader.BootLoaderImage(device=None, label=None) + self.editOther(image) def deleteEntry(self, widget, *args): rc = self.getSelected() if not rc: return - (dev, label, isDefault, isRoot) = rc - if not isRoot: - del self.imagelist[dev] - if isDefault: - keys = self.imagelist.keys() - keys.sort() - self.defaultDev = keys[0] + if image.device != self.storage.rootDevice: + del self.images[image.device.name] + if image.device == self.defaultDev: + devs = [i.device for i in self.images] + devs.sort(key=lambda d: d.name) + self.defaultDev = devs[0] self.fillOSList() else: @@ -334,68 +313,45 @@ class OSBootWidget: rc = self.getSelected() if not rc: return - (dev, label, isDefault, isRoot) = rc - self.editOther(dev, label, isDefault, isRoot) + self.editOther(rc) # the default os was changed in the treeview def toggledDefault(self, data, row): iter = self.osStore.get_iter((int(row),)) - dev = self.osStore.get_value(iter, 2) - self.defaultDev = devicePathToName(dev) + self.defaultDev = self.osStore.get_value(iter, 3) self.fillOSList() # fill in the os list tree view def fillOSList(self): self.osStore.clear() - keys = self.imagelist.keys() - keys.sort() - - for dev in keys: - (label, longlabel, fstype) = self.imagelist[dev] - device = self.storage.devicetree.getDeviceByName(dev) - if self.blname == "GRUB": - theLabel = longlabel - else: - theLabel = label + devs = sorted(self.images.keys()) + for dev in devs: + image = self.images[dev] # if the label is empty, remove from the image list and don't # worry about it - if not theLabel: - del self.imagelist[dev] + if not image.label: + del self.images[dev] continue - isRoot = 0 - rootDev = self.storage.rootDevice - if rootDev and rootDev.name == dev: - isRoot = 1 - - devPath = getattr(device, "path", "/dev/%s" % dev) iter = self.osStore.append() - self.osStore.set_value(iter, 1, theLabel) - self.osStore.set_value(iter, 2, devPath) - self.osStore.set_value(iter, 3, isRoot) - if self.defaultDev == dev: - self.osStore.set_value(iter, 0, True) - else: - self.osStore.set_value(iter, 0, False) - - self.numentries = len(keys) + self.osStore.set_value(iter, 0, self.defaultDev == image.device) + self.osStore.set_value(iter, 1, self.bl.image_label(image)) + self.osStore.set_value(iter, 2, dev) + self.osStore.set_value(iter, 3, image) def osTreeActivateCb(self, view, path, col): self.editEntry(view) - - + def getWidget(self): return self.widget - # FIXME: I really shouldn't have such intimate knowledge of - # the bootloader object def setBootloaderImages(self): - "Apply the changes from our list into the self.bl object" - # make a copy of our image list to shove into the bl struct - self.bl.images.images = {} - for key in self.imagelist.keys(): - self.bl.images.images[key] = self.imagelist[key] - self.bl.images.setDefault(self.defaultDev) + """Apply the changes from our list into the self.bl object.""" + self.bl.clear_images() + for image in self.images.values(): + self.bl.add_image(image) + if image.device == self.defaultDev: + self.bl.default = image diff --git a/pyanaconda/iw/upgrade_bootloader_gui.py b/pyanaconda/iw/upgrade_bootloader_gui.py index afbe62b..60ecad2 100644 --- a/pyanaconda/iw/upgrade_bootloader_gui.py +++ b/pyanaconda/iw/upgrade_bootloader_gui.py @@ -23,7 +23,6 @@ from iw_gui import * import gtk -from pyanaconda.booty import checkbootloader from pyanaconda.storage.devices import devicePathToName from pyanaconda.constants import * @@ -41,96 +40,37 @@ class UpgradeBootloaderWindow (InstallWindow): def getNext(self): if self.nobl_radio.get_active(): - self.dispatch.skipStep("bootloadersetup", skip = 1) self.dispatch.skipStep("bootloader", skip = 1) self.dispatch.skipStep("instbootloader", skip = 1) elif self.newbl_radio.get_active(): - self.dispatch.skipStep("bootloadersetup", skip = 0) self.dispatch.skipStep("bootloader", skip = 0) self.dispatch.skipStep("instbootloader", skip = 0) self.bl.doUpgradeOnly = 0 else: - self.dispatch.skipStep("bootloadersetup", skip = 0) self.dispatch.skipStep("bootloader", skip = 1) self.dispatch.skipStep("instbootloader", skip = 0) self.bl.doUpgradeOnly = 1 - if self.type == "GRUB": - self.bl.useGrubVal = 1 - else: - self.bl.useGrubVal = 0 - self.bl.setDevice(devicePathToName(self.bootDev)) - - def _newToLibata(self, rootPath): - # NOTE: any changes here need to be done in upgrade_bootloader_text too - try: - f = open("/proc/modules", "r") - buf = f.read() - if buf.find("libata") == -1: - return False - except: - log.debug("error reading /proc/modules") - pass - - try: - f = open(rootPath + "/etc/modprobe.conf") - except: - log.debug("error reading /etc/modprobe.conf") - return False - - modlines = f.readlines() - f.close() - - try: - f = open("/tmp/scsidisks") - except: - log.debug("error reading /tmp/scsidisks") - return False - mods = [] - for l in f.readlines(): - (disk, mod) = l.split() - if mod.strip() not in mods: - mods.append(mod.strip()) - f.close() - - for l in modlines: - stripped = l.strip() - - if stripped == "" or stripped[0] == "#": - continue - - if stripped.find("scsi_hostadapter") != -1: - mod = stripped.split()[-1] - if mod in mods: - mods.remove(mod) - - if len(mods) > 0: - return True - return False + self.bl.stage1_device = self.bootDev def getScreen(self, anaconda): self.dispatch = anaconda.dispatch - self.bl = anaconda.bootloader + self.bl = anaconda.platform.bootloader - newToLibata = self._newToLibata(anaconda.rootPath) - - (self.type, self.bootDev) = \ - checkbootloader.getBootloaderTypeAndBoot(anaconda.rootPath, storage=anaconda.storage) + # TODO: implement bootloader detection + self.type = None + self.bootDev = None self.update_radio = gtk.RadioButton(None, _("_Update boot loader configuration")) updatestr = _("This will update your current boot loader.") - if newToLibata or (self.type is None or self.bootDev is None): - if newToLibata: - current = _("Due to system changes, your boot loader " - "configuration can not be automatically updated.") - else: - current = _("The installer is unable to detect the boot loader " - "currently in use on your system.") + if self.type is None or self.bootDev is None: + current = _("The installer is unable to detect the boot loader " + "currently in use on your system.") self.update_label = gtk.Label("%s" % (updatestr,)) self.update_radio.set_sensitive(False) self.update_label.set_sensitive(False) - update = 0 + update = False else: current = _("The installer has detected the %(type)s boot loader " "currently installed on %(bootDev)s.") \ @@ -138,7 +78,7 @@ class UpgradeBootloaderWindow (InstallWindow): self.update_label = gtk.Label("%s %s" % (updatestr, _("This is the recommended option."))) self.update_radio.set_active(False) - update = 1 + update = True self.newbl_radio = gtk.RadioButton(self.update_radio, _("_Create new boot loader " @@ -165,10 +105,8 @@ class UpgradeBootloaderWindow (InstallWindow): str = _("What would you like to do?") # if they have one, the default is to update, otherwise the # default is to not touch anything - if update == 1: + if update: default = self.update_radio - elif newToLibata: - default = self.newbl_radio else: default = self.nobl_radio diff --git a/pyanaconda/kickstart.py b/pyanaconda/kickstart.py index 636d0fe..87f20fe 100644 --- a/pyanaconda/kickstart.py +++ b/pyanaconda/kickstart.py @@ -246,8 +246,8 @@ class Bootloader(commands.bootloader.F15_Bootloader): raise KickstartValueError, formatErrorMsg(self.lineno, msg="Selected upgrade mode for bootloader but not doing an upgrade") if self.upgrade: - self.anaconda.bootloader.kickstart = 1 - self.anaconda.bootloader.doUpgradeOnly = 1 + # TODO: new dispatcher step or flag in BootLoader? + raise NotImplementedError("bootloader update not implemented yet") if location is None: self.anaconda.ksdata.permanentSkipSteps.extend(["bootloadersetup", "instbootloader"]) @@ -255,45 +255,29 @@ class Bootloader(commands.bootloader.F15_Bootloader): self.anaconda.ksdata.showSteps.append("bootloader") if self.appendLine: - self.anaconda.bootloader.args.append(self.appendLine) + self.anaconda.bootloader.kernel_args.append(self.appendLine) if self.password: - self.anaconda.bootloader.setPassword(self.password, isCrypted = self.isCrypted) + # TODO: implement password encryption + self.anaconda.bootloader.password = self.password if location != None: - self.anaconda.bootloader.defaultDevice = location - else: - self.anaconda.bootloader.defaultDevice = -1 + self.anaconda.bootloader.default = location if self.timeout: self.anaconda.bootloader.timeout = self.timeout - # add unpartitioned devices that will get partitioned into - # bootloader.drivelist - disks = self.anaconda.storage.disks - partitioned = self.anaconda.storage.partitioned - for disk in [d for d in disks if not d.partitioned]: - if shouldClear(disk, self.anaconda.storage.config.clearPartType, - self.anaconda.storage.config.clearPartDisks): - # add newly partitioned disks to the drivelist - self.anaconda.bootloader.drivelist.append(disk.name) - elif disk.name in self.anaconda.bootloader.drivelist: - # remove unpartitioned disks from the drivelist - self.anaconda.bootloader.drivelist.remove(disk.name) - self.anaconda.bootloader.drivelist.sort( - cmp=self.anaconda.storage.compareDisks) + # TODO: add sanity checking for things like choosing unpartitioned disks as + # bootloader target devices # Throw out drives specified that don't exist. - if self.driveorder and len(self.driveorder) > 0: - new = [] - for drive in self.driveorder: - if drive in self.anaconda.bootloader.drivelist: - new.append(drive) - else: - log.warning("requested drive %s in boot drive order " - "doesn't exist" %(drive,)) + disk_names = [d.name for d in self.anaconda.storage.disks] + for drive in self.driveorder[:]: + if drive not in disk_names: + log.warning("requested drive %s in boot drive order doesn't exist" % drive) + self.driveorder.remove(drive) - self.anaconda.bootloader.updateDriveList(new) + self.anaconda.bootloader.drive_order = self.driveorder self.anaconda.ksdata.permanentSkipSteps.extend(["upgbootloader", "bootloader"]) diff --git a/pyanaconda/platform.py b/pyanaconda/platform.py index 3942887..4d6a85e 100644 --- a/pyanaconda/platform.py +++ b/pyanaconda/platform.py @@ -34,15 +34,9 @@ class Platform(object): during installation. The intent is to eventually encapsulate all the architecture quirks in one place to avoid lots of platform checks throughout anaconda.""" - _bootFSTypes = ["ext3"] - _diskLabelType = "msdos" - _isEfi = iutil.isEfi() _minimumSector = 0 _packages = [] - _supportsLvmBoot = False - _supportsMdRaidBoot = False - _minBootPartSize = 50 - _maxBootPartSize = 0 + _bootloaderClass = BootLoader def __init__(self, anaconda): """Creates a new Platform object. This is basically an abstract class. @@ -50,34 +44,40 @@ class Platform(object): returned by getPlatform below. Not all subclasses need to provide all the methods in this class.""" self.anaconda = anaconda + self.bootloader = self._bootloaderClass(storage=anaconda.storage) - def _mntDict(self): - """Return a dictionary mapping mount points to devices.""" - ret = {} - for device in [d for d in self.anaconda.storage.devices if d.format.mountable]: - ret[device.format.mountpoint] = device - - return ret - + @property def bootDevice(self): - """Return the device where /boot is mounted.""" - if self.__class__ is Platform: - raise NotImplementedError("bootDevice not implemented for this platform") + """The device that includes the /boot filesystem.""" + return self.bootloader.stage2_device - mntDict = self._mntDict() - return mntDict.get("/boot", mntDict.get("/")) + @property + def bootloaderDevice(self): + """The device the bootloader will be installed into.""" + return self.bootloader.stage1_device + + @property + def bootFSTypes(self): + """A list of all valid filesystem types for the boot partition.""" + return self.bootloader.linux_boot_device_format_types @property def defaultBootFSType(self): - """Return the default filesystem type for the boot partition.""" - return self._bootFSTypes[0] + """The default filesystem type for the boot partition.""" + return self.bootFSTypes[0] @property - def bootFSTypes(self): - """Return a list of all valid filesystem types for the boot partition.""" - return self._bootFSTypes + def diskLabelTypes(self): + """A list of valid disklabel types for this architecture.""" + return self.bootloader.target_device_disklabel_types - def bootloaderChoices(self, bl): + @property + def defaultDiskLabelType(self): + """The default disklabel type for this architecture.""" + return self.diskLabelTypes[0] + + @property + def bootloaderChoices(self): """Return the default list of places to install the bootloader. This is returned as a dictionary of locations to (device, identifier) tuples. If there is no boot device, an empty dictionary is @@ -85,21 +85,41 @@ class Platform(object): if self.__class__ is Platform: raise NotImplementedError("bootloaderChoices not implemented for this platform") - bootDev = self.bootDevice() + bootDev = self.bootDevice ret = {} if not bootDev: return ret + # XXX reconsider how/if this is used if bootDev.type == "mdarray": ret["boot"] = (bootDev.name, N_("RAID Device")) - ret["mbr"] = (bl.drivelist[0], N_("Master Boot Record (MBR)")) + ret["mbr"] = (self.bootloader.drives[0].name, + N_("Master Boot Record (MBR)")) else: ret["boot"] = (bootDev.name, N_("First sector of boot partition")) - ret["mbr"] = (bl.drivelist[0], N_("Master Boot Record (MBR)")) + ret["mbr"] = (self.bootloaderDevice.name, + N_("Master Boot Record (MBR)")) return ret + def checkDiskLabel(self, req): + """Check the disk containing req for the correct disklabel type. + + Return a list of error strings if incorrect disklabels are found.""" + errors = [] + for partition in self.storage.partitions: + if req != partition and not req.dependsOn(partition): + continue + + labelType = partition.disk.format.labelType + labelTypes = self.bootloader.target_device_diskabel_types + if labelType not in labelTypes: + errors.append(_("%s must have a %s disk label.") + % (partition.disk.name, + " or ".join([t.upper for t in labelTypes]))) + return errors + def checkBootRequest(self, req): """Perform an architecture-specific check on the boot device. Not all platforms may need to do any checks. Returns a list of errors if @@ -109,47 +129,52 @@ class Platform(object): if not req: return [_("You have not created a bootable partition.")] - # most arches can't have boot on RAID - if req.type == "mdarray": - if not self.supportsMdRaidBoot: - errors.append(_("Bootable partitions cannot be on a RAID device.")) - elif req.type == "mdarray" and req.level != 1: - errors.append(_("Bootable partitions can only be on RAID1 devices.")) - else: - for p in req.parents: - if p.type != "partition": - errors.append(_("Bootable RAID1 set members must be partitions.")) - break - - # most arches can't have boot on a logical volume - if req.type == "lvmlv" and not self.supportsLvmBoot: - errors.append(_("Bootable partitions cannot be on a logical volume.")) + # TODO: reimplement BootLoader._device_is_bootable(req, linux=True) + # such that it returns a list of error strings instead of + # True/False + + if req.type not in self.bootloader.boot_device_types: + errors.append(_("The /boot filesystem cannot be on devices of " + "type %s") % req.type) + elif req.type == "mdarray": + raid_levels = self.bootloader.boot_device_raid_levels + if req.level not in raid_levels: + levels = ",".join(["RAID%d" % l for l in raid_levels]) + errors.append(_("RAID sets containing the /boot filesystem " + "must have one of the following raid levels: " + "%s.") % levels) + + for p in req.parents: + if p.type != "partition": + errors.append(_("RAID sets containing the /boot " + "filesystem may only have partitions " + "as member devices.")) + break # Make sure /boot is on a supported FS type. This prevents crazy # things like boot on vfat. if not req.format.bootable or \ - (getattr(req.format, "mountpoint", None) == "/boot" and - req.format.type not in self.bootFSTypes): - errors.append(_("Bootable partitions cannot be on an %s filesystem.") % req.format.type) + req.format.type not in self.bootFSTypes: + errors.append(_("The /boot filesystem cannot be of type %s.") % req.format.type) if req.type == "luks/dm-crypt": # Handle encrypted boot on a partition. - errors.append(_("Bootable partitions cannot be on an encrypted block device")) + errors.append(_("The /boot filesystem cannot be on an encrypted block device")) else: # Handle encrypted boot on more complicated devices. for dev in filter(lambda d: d.type == "luks/dm-crypt", self.anaconda.storage.devices): if req in self.anaconda.storage.deviceDeps(dev): - errors.append(_("Bootable partitions cannot be on an encrypted block device")) + errors.append(_("The /boot filesystem cannot be on an encrypted block device")) + errors.extend(self.checkDiskLabel(req) return errors - def diskLabelType(self, deviceType): - """Return the disk label type as a string.""" - return self._diskLabelType + def checkBootLoaderRequest(self, req): + """ Perform architecture-specific checks on the bootloader device. - @property - def isEfi(self): - return self._isEfi + Returns a list of error strings. + """ + return self.checkDiskLabel(req) @property def minimumSector(self, disk): @@ -158,7 +183,7 @@ class Platform(object): @property def packages (self): - return self._packages + return self._packages + self.bootloader.packages def setDefaultPartitioning(self): """Return the default platform-specific partitioning information.""" @@ -166,59 +191,36 @@ class Platform(object): return [PartSpec(mountpoint="/boot", fstype=self.defaultBootFSType, size=500, weight=self.weight(mountpoint="/boot"))] - @property - def supportsLvmBoot(self): - """Does the platform support /boot on LVM logical volume?""" - return self._supportsLvmBoot - - @property - def supportsMdRaidBoot(self): - """Does the platform support /boot on MD RAID?""" - return self._supportsMdRaidBoot - - @property - def minBootPartSize(self): - return self._minBootPartSize - - @property - def maxBootPartSize(self): - return self._maxBootPartSize - - def validBootPartSize(self, size): - """ Is the given size (in MB) acceptable for a boot device? """ + def validBootLoaderPartSize(self, size): + """ Is the given size (in MB) acceptable for a bootloader device? """ if not isinstance(size, int) and not isinstance(size, float): return False - return ((not self.minBootPartSize or size >= self.minBootPartSize) + return ((self.bootloader.target_device_min_size is None or + size >= self.bootloader.target_device_min_size) and - (not self.maxBootPartSize or size <= self.maxBootPartSize)) + (self.bootloader.target_device_max_size is None or + size <= self.bootloader.target_device_max_size)) def weight(self, fstype=None, mountpoint=None): """ Given an fstype (as a string) or a mountpoint, return an integer for the base sorting weight. This is used to modify the sort algorithm for partition requests, mainly to make sure bootable partitions and /boot are placed where they need to be.""" - return 0 - -class EFI(Platform): - _bootFSTypes = ["ext4", "ext3", "ext2"] - _diskLabelType = "gpt" - _minBootPartSize = 50 - _maxBootPartSize = 256 - - def bootDevice(self): - bootDev = None + if fstype in self.bootFSTypes and mountpoint == "/boot": + return 2000 + else: + return 0 - for part in self.anaconda.storage.partitions: - if part.format.type == "efi" and self.validBootPartSize(part.size): - bootDev = part - # if we're only picking one, it might as well be the first - break +class X86(Platform): + _bootloaderClass = GRUB - return bootDev +class EFI(Platform): + _bootloaderClass = EFIGRUB - def bootloaderChoices(self, bl): - bootDev = self.bootDevice() + @property + def bootloaderChoices(self): + bootDev = self.bootloaderDevice ret = {} if not bootDev: @@ -227,40 +229,20 @@ class EFI(Platform): ret["boot"] = (bootDev.name, N_("EFI System Partition")) return ret - def checkBootRequest(self, req): - """ Perform architecture-specific checks on the boot device. + def checkBootLoaderRequest(self, req): + """ Perform architecture-specific checks on the bootloader device. Returns a list of error strings. - - NOTE: X86 does not have a separate checkBootRequest method, - so this one must work for x86 as well as EFI. """ if not req: return [_("You have not created a /boot/efi partition.")] - errors = Platform.checkBootRequest(self, req) - - if req.format.mountpoint == "/boot/efi": - if req.format.type != "efi": - errors.append(_("/boot/efi is not EFI.")) + errors = [] - # Don't try to check the disklabel on lv's etc, using lv for /boot - # is already checked in the generic Platform.checkBootRequest() - partitions = [] - if req.type == "partition": - partitions = [ req ] - elif req.type == "mdarray": - partitions = filter(lambda d: d.type == "partition", req.parents) - - # Check that we've got a correct disk label. - for p in partitions: - partedDisk = p.disk.format.partedDisk - labelType = self.diskLabelType(partedDisk.device.type) - # Allow using gpt with x86, but not msdos with EFI - if partedDisk.type != labelType and partedDisk.type != "gpt": - errors.append(_("%s must have a %s disk label.") - % (p.disk.name, labelType.upper())) + if req.format.type != "efi": + errors.append(_("/boot/efi is not EFI.")) + errors.extend(Platform.checkBootLoaderRequest(self)) return errors def setDefaultPartitioning(self): @@ -271,31 +253,26 @@ class EFI(Platform): return ret def weight(self, fstype=None, mountpoint=None): - if fstype and fstype == "efi" or mountpoint and mountpoint == "/boot/efi": + score = Platform.weight(self, fstype=fstype, mountpoint=mountpoint) + if score: + return score + elif fstype and fstype == "efi" or mountpoint and mountpoint == "/boot/efi": return 5000 - elif mountpoint and mountpoint == "/boot": - return 2000 else: return 0 class Alpha(Platform): - _diskLabelType = "bsd" + _bootloaderClass = "aboot" - def checkBootRequest(self, req): - errors = Platform.checkBootRequest(self, req) + def checkBootLoaderRequest(self, req): + errors = Platform.checkBootLoaderRequest(self, req) if not req or req.type != "partition" or not req.disk: return errors - disk = req.disk.format.partedDisk - - # Check that we're a BSD disk label - if not disk.type == self._diskLabelType: - errors.append(_("%s must have a bsd disk label.") % req.disk.name) - # The first free space should start at the beginning of the drive and # span for a megabyte or more. - free = disk.getFirstPartition() + free = disk.format.firstPartition while free: if free.type & parted.PARTITION_FREESPACE: break @@ -307,74 +284,46 @@ class Alpha(Platform): return errors -class IA64(EFI): - _packages = ["elilo"] - - def __init__(self, anaconda): - EFI.__init__(self, anaconda) - class PPC(Platform): - _bootFSTypes = ["ext4", "ext3", "ext2"] - _packages = ["yaboot"] + _bootloaderClass = Yaboot + _packages = [] _ppcMachine = iutil.getPPCMachine() - _supportsMdRaidBoot = True @property def ppcMachine(self): return self._ppcMachine class IPSeriesPPC(PPC): - _minBootPartSize = 4 - _maxBootPartSize = 10 - - def bootDevice(self): - bootDev = None - - # We want the first PReP partition. - for device in self.anaconda.storage.partitions: - if device.format.type == "prepboot": - bootDev = device - break - - return bootDev + _bootloaderClass = IPSeriesYaboot - def bootloaderChoices(self, bl): + @property + def bootloaderChoices(self): ret = {} - bootDev = self.bootDevice() + bootDev = self.bootDevice if not bootDev: return ret if bootDev.type == "mdarray": ret["boot"] = (bootDev.name, N_("RAID Device")) - ret["mbr"] = (bl.drivelist[0], N_("Master Boot Record (MBR)")) + ret["mbr"] = (self.bootloader.drives[0].name, + N_("Master Boot Record (MBR)")) else: - ret["boot"] = (bootDev.name, N_("PPC PReP Boot")) + ret["boot"] = (self.bootloaderDevice.name, N_("PPC PReP Boot")) return ret - def checkBootRequest(self, req): - errors = PPC.checkBootRequest(self, req) + def checkBootLoaderRequest(self, req): + errors = PPC.checkBootLoaderRequest(self, req) bootPart = getattr(req, "partedPartition", None) if not bootPart: return errors - # All of the above just checks the PPC PReP boot partitions. We still - # need to make sure that whatever /boot is on also meets these criteria. - if req == self.bootDevice(): - # However, this check only applies to prepboot. - if bootPart.geometry.end * bootPart.geometry.device.sectorSize / (1024.0 * 1024) > 4096: - errors.append(_("The boot partition must be within the first 4MB of the disk.")) + if bootPart.geometry.end * bootPart.geometry.device.sectorSize / (1024.0 * 1024) > 10: + errors.append(_("The boot partition must be within the first 10MB of the disk.")) - try: - req = self.anaconda.storage.mountpoints["/boot"] - except KeyError: - req = self.anaconda.storage.rootDevice - - return errors + self.checkBootRequest(req) - else: - return errors + return errors def setDefaultPartitioning(self): from storage.partspec import PartSpec @@ -384,70 +333,44 @@ class IPSeriesPPC(PPC): return ret def weight(self, fstype=None, mountpoint=None): - if fstype and fstype == "prepboot": + score = Platform.weight(self, fstype=fstype, mountpoint=mountpoint) + if score: + return score + elif fstype and fstype == "prepboot": return 5000 - elif mountpoint and mountpoint == "/boot": - return 2000 else: return 0 class NewWorldPPC(PPC): - _diskLabelType = "mac" - _minBootPartSize = (800.00 / 1024.00) - _maxBootPartSize = 1 - - def bootDevice(self): - bootDev = None + _bootloaderClass = MacYaboot - for part in self.anaconda.storage.partitions: - if part.format.type == "appleboot" and self.validBootPartSize(part.size): - bootDev = part - # if we're only picking one, it might as well be the first - break - - return bootDev - - def bootloaderChoices(self, bl): + @property + def bootloaderChoices(self): ret = {} - bootDev = self.bootDevice() + bootDev = self.bootDevice if not bootDev: return ret if bootDev.type == "mdarray": ret["boot"] = (bootDev.name, N_("RAID Device")) - ret["mbr"] = (bl.drivelist[0], N_("Master Boot Record (MBR)")) + ret["mbr"] = (self.bootloader.drives[0].name, + N_("Master Boot Record (MBR)")) else: - ret["boot"] = (bootDev.name, N_("Apple Bootstrap")) - for (n, device) in enumerate(self.anaconda.storage.partitions): - if device.format.type == "appleboot" and device.path != bootDev.path: - ret["boot%d" % n] = (device.path, N_("Apple Bootstrap")) + ret["boot"] = (self.bootloaderDevice.name, N_("Apple Bootstrap")) + for (n, device) in enumerate(self.bootloader.target_devices): + if device != self.bootloaderDevice: + ret["boot%d" % n] = (device.name, N_("Apple Bootstrap")) return ret - def checkBootRequest(self, req): - errors = PPC.checkBootRequest(self, req) + def checkBootLoaderRequest(self, req): + errors = PPC.checkBootLoaderRequest(self, req) if not req or req.type != "partition" or not req.disk: return errors - disk = req.disk.format.partedDisk - - # Check that we're a Mac disk label - if not disk.type == self._diskLabelType: - errors.append(_("%s must have a mac disk label.") % req.disk.name) - - # All of the above just checks the appleboot partitions. We still - # need to make sure that whatever /boot is on also meets these criteria. - if req == self.bootDevice(): - try: - req = self.anaconda.storage.mountpoints["/boot"] - except KeyError: - req = self.anaconda.storage.rootDevice - - return errors + self.checkBootRequest(req) - else: - return errors + return errors def setDefaultPartitioning(self): from storage.partspec import PartSpec @@ -457,34 +380,25 @@ class NewWorldPPC(PPC): return ret def weight(self, fstype=None, mountpoint=None): - if fstype and fstype == "appleboot": + score = Platform.weight(self, fstype=fstype, mountpoint=mountpoint) + if score: + return score + elif fstype and fstype == "appleboot": return 5000 - elif mountpoint and mountpoint == "/boot": - return 2000 else: return 0 class PS3(PPC): - _diskLabelType = "msdos" - def __init__(self, anaconda): PPC.__init__(self, anaconda) class S390(Platform): - _bootFSTypes = ["ext4", "ext3", "ext2"] + _bootloaderClass = ZIPL _packages = ["s390utils"] - _supportsLvmBoot = True def __init__(self, anaconda): Platform.__init__(self, anaconda) - def diskLabelType(self, deviceType): - """Return the disk label type as a string.""" - if deviceType == parted.DEVICE_DASD: - return "dasd" - else: - return Platform.diskLabelType(self, deviceType) - def setDefaultPartitioning(self): """Return the default platform-specific partitioning information.""" from storage.partspec import PartSpec @@ -492,15 +406,8 @@ class S390(Platform): weight=self.weight(mountpoint="/boot"), asVol=True, singlePV=True)] - def weight(self, fstype=None, mountpoint=None): - if mountpoint and mountpoint == "/boot": - return 5000 - else: - return 0 - class Sparc(Platform): - _diskLabelType = "sun" - _packages = ["silo"] + _bootloaderClass = SILO @property def minimumSector(self, disk): @@ -509,72 +416,12 @@ class Sparc(Platform): start /= long(1024 / disk.device.sectorSize) return start+1 -class X86(EFI): - _bootFSTypes = ["ext4", "ext3", "ext2"] - _packages = ["grub"] - _supportsMdRaidBoot = True - - def __init__(self, anaconda): - EFI.__init__(self, anaconda) - - if self.isEfi: - self._diskLabelType = "gpt" - else: - self._diskLabelType = "msdos" - - def bootDevice(self): - if self.isEfi: - return EFI.bootDevice(self) - else: - return Platform.bootDevice(self) - - def bootloaderChoices(self, bl): - if self.isEfi: - return EFI.bootloaderChoices(self, bl) - - bootDev = self.bootDevice() - ret = {} - - if not bootDev: - return {} - - if bootDev.type == "mdarray": - ret["boot"] = (bootDev.name, N_("RAID Device")) - ret["mbr"] = (bl.drivelist[0], N_("Master Boot Record (MBR)")) - else: - ret["boot"] = (bootDev.name, N_("First sector of boot partition")) - ret["mbr"] = (bl.drivelist[0], N_("Master Boot Record (MBR)")) - - return ret - - @property - def maxBootPartSize(self): - if self.isEfi: - return EFI._maxBootPartSize - else: - return Platform._maxBootPartSize - - @property - def minBootPartSize(self): - if self.isEfi: - return EFI._minBootPartSize - else: - return Platform._minBootPartSize - - def setDefaultPartitioning(self): - if self.isEfi: - return EFI.setDefaultPartitioning(self) - else: - return Platform.setDefaultPartitioning(self) - def getPlatform(anaconda): """Check the architecture of the system and return an instance of a Platform subclass to match. If the architecture could not be determined, raise an exception.""" if iutil.isAlpha(): return Alpha(anaconda) - elif iutil.isIA64(): - return IA64(anaconda) elif iutil.isPPC(): ppcMachine = iutil.getPPCMachine() @@ -590,6 +437,8 @@ def getPlatform(anaconda): return S390(anaconda) elif iutil.isSparc(): return Sparc(anaconda) + elif iutil.isEfi(): + return EFI(anaconda) elif iutil.isX86(): return X86(anaconda) else: diff --git a/pyanaconda/storage/__init__.py b/pyanaconda/storage/__init__.py index a0cbdfa..f063c0f 100644 --- a/pyanaconda/storage/__init__.py +++ b/pyanaconda/storage/__init__.py @@ -375,7 +375,7 @@ class Storage(object): # now set the boot partition's flag try: - boot = self.platform.bootDevice() + boot = self.platform.bootDevice if boot.type == "mdarray": bootDevs = boot.parents else: @@ -1055,7 +1055,7 @@ class Storage(object): root = self.fsset.rootDevice swaps = self.fsset.swapDevices try: - boot = self.platform.bootDevice() + boot = self.platform.bootDevice except (DeviceError, AttributeError): # AttributeError means we have no anaconda or platform. it's ok. boot = None diff --git a/pyanaconda/storage/partitioning.py b/pyanaconda/storage/partitioning.py index e8f3921..d09cfb6 100644 --- a/pyanaconda/storage/partitioning.py +++ b/pyanaconda/storage/partitioning.py @@ -849,13 +849,11 @@ def doPartitioning(storage, bootloader=None): # start over with flexible-size requests part.req_size = part.req_base_size - # FIXME: isn't there a better place for this to happen? try: - bootDev = storage.platform.bootDevice() + bootDev = storage.platform.bootDevice except DeviceError: - bootDev = None - - if bootDev: + pass + else: bootDev.req_bootable = True removeNewPartitions(disks, partitions)