summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJoel Andres Granados <jgranado@redhat.com>2008-08-06 16:06:30 +0200
committerJoel Andres Granados <jgranado@redhat.com>2008-08-07 15:20:47 +0200
commit7e334e5e4626d8696e8127e494c9eee2fcbb8474 (patch)
treeabf15a2c6edce89bfd0552307d91a5520363413e
parent7f2d0899817cd7894b44ab014d1a0744691ac7d0 (diff)
downloadfirstaidkit-7e334e5e4626d8696e8127e494c9eee2fcbb8474.tar.gz
firstaidkit-7e334e5e4626d8696e8127e494c9eee2fcbb8474.tar.xz
firstaidkit-7e334e5e4626d8696e8127e494c9eee2fcbb8474.zip
Initial commit for the Grub plugin.
Nothing was used from the original grub plugin. For now they will both coexists in the code.
-rw-r--r--firstaidkit.spec17
-rw-r--r--plugins/grub/__init__.py20
-rw-r--r--plugins/grub/grub.py261
-rw-r--r--plugins/grub/grubUtils.py406
-rw-r--r--plugins/grub/grubconf.py17
-rw-r--r--plugins/grub/minihal.py74
-rw-r--r--pyfirstaidkit/utils/__init__.py11
7 files changed, 801 insertions, 5 deletions
diff --git a/firstaidkit.spec b/firstaidkit.spec
index a840e58..0848f43 100644
--- a/firstaidkit.spec
+++ b/firstaidkit.spec
@@ -91,7 +91,11 @@ configuration file xorg.conf.
Group: Applications/System
Summary: FirstAidKit plugin to diagnose or repair the GRUB instalation
Requires: %{name} = %{version}-%{release}
-Requires: anaconda, booty
+#Requires: anaconda, booty
+
+Requires: dbus-python
+Requires: grub
+Requires: pyparted
%description plugin-grub
This FirstAidKit plugin automates the recovery from the GRUB bootloader problems.
@@ -172,8 +176,10 @@ desktop-file-install --vendor="fedora" --dir=${RPM_BUILD_ROOT}%{_datadir}/applic
%{__cp} -f plugins/rpm/*.py $RPM_BUILD_ROOT%{_libdir}/firstaidkit/plugins/rpm/
%{__install} -d $RPM_BUILD_ROOT%{_libdir}/firstaidkit/plugins/rpm_lowlevel
%{__cp} -f plugins/rpm_lowlevel/*.py $RPM_BUILD_ROOT%{_libdir}/firstaidkit/plugins/rpm_lowlevel/
-%{__cp} -f plugins/grub.py $RPM_BUILD_ROOT%{_libdir}/firstaidkit/plugins/
-%{__install} -p etc/firstaidkit/firstaidkit-plugin-grub $RPM_BUILD_ROOT%{_sysconfdir}/firstaidkit
+#%{__cp} -f plugins/grub.py $RPM_BUILD_ROOT%{_libdir}/firstaidkit/plugins/
+#%{__install} -p etc/firstaidkit/firstaidkit-plugin-grub $RPM_BUILD_ROOT%{_sysconfdir}/firstaidkit
+%{__install} -d $RPM_BUILD_ROOT%{_libdir}/firstaidkit/plugins/grub
+%{__cp} -f plugins/grub/*.py $RPM_BUILD_ROOT%{_libdir}/firstaidkit/plugins/grub
%{__cp} -f plugins/mdadm_conf.py $RPM_BUILD_ROOT%{_libdir}/firstaidkit/plugins/
@@ -217,8 +223,9 @@ desktop-file-install --vendor="fedora" --dir=${RPM_BUILD_ROOT}%{_datadir}/applic
%{_libdir}/firstaidkit/plugins/rpm/*
%files plugin-grub
-%{_libdir}/firstaidkit/plugins/grub.py*
-%{_sysconfdir}/firstaidkit/firstaidkit-plugin-grub
+#%{_libdir}/firstaidkit/plugins/grub.py*
+#%{_sysconfdir}/firstaidkit/firstaidkit-plugin-grub
+%{_libdir}/firstaidkit/plugins/grub/*
%files plugin-mdadm-conf
%{_libdir}/firstaidkit/plugins/mdadm_conf.py*
diff --git a/plugins/grub/__init__.py b/plugins/grub/__init__.py
new file mode 100644
index 0000000..dfeb749
--- /dev/null
+++ b/plugins/grub/__init__.py
@@ -0,0 +1,20 @@
+# First Aid Kit - diagnostic and repair tool for Linux
+# Copyright (C) 2008 Joel Andres Granados <jgranado@redhat.com>
+#
+# 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, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+import grub
+def get_plugin():
+ return grub.Grub
diff --git a/plugins/grub/grub.py b/plugins/grub/grub.py
new file mode 100644
index 0000000..8056218
--- /dev/null
+++ b/plugins/grub/grub.py
@@ -0,0 +1,261 @@
+# First Aid Kit - diagnostic and repair tool for Linux
+# Copyright (C) 2008 Joel Andres Granados <jgranado@redhat.com>
+#
+# 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, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+from pyfirstaidkit.plugins import Plugin,Flow
+from pyfirstaidkit.returns import *
+from pyfirstaidkit.utils import *
+from pyfirstaidkit.reporting import PLUGIN
+from pyfirstaidkit.issue import SimpleIssue
+from pyfirstaidkit import Config
+from pyfirstaidkit.errors import *
+
+from grubUtils import Dname
+import grubUtils
+
+import os
+
+class Grub(Plugin):
+ """Plugin to detect and fix grub failure."""
+ flows = Flow.init(Plugin)
+ name = "Grub"
+ version = "0.0.1"
+ author = "Joel Andres Granados"
+
+ @classmethod
+ def getDeps(cls):
+ return set(["root", "experimental", "filesystem"])
+
+ def __init__(self, *args, **kwargs):
+ Plugin.__init__(self, *args, **kwargs)
+
+ # The key is a dev name, the value is a list of partitions in drive.
+ self.devices = {}
+
+ # The partitions where the grub directory and all the necessary files
+ # found.
+ self.grub_dir_parts = []
+
+ # The partitions where the grub binary is found in the first 446 bytes.
+ self.grub_bin_parts = []
+
+ # The devs where the grub binary is found in the first 446 bytes.
+ self.grub_bin_devs = []
+
+ # Initialize our list of related issues.
+ self.issue_grub_dir = SimpleIssue(self.name, "Missing grub dir or " \
+ "dir files.")
+ self.issue_grub_image = SimpleIssue(self.name, "Bad grub stage1 image.")
+
+ # Initialize the backup space.
+ self.backupSpace = self._backups.getBackup(str(self))
+
+ def prepare(self):
+ self._reporting.info("Initializing the search for all the grub " \
+ "related elements.", origin = self)
+
+ try:
+ # We end up with a structure where the keys are the drive names.
+ self.devices = grubUtils.get_all_devs()
+
+ self._reporting.info("Searching for grub related files in the " \
+ "system storage devices.", origin = self)
+ # We must search in all the possible partitions for the grub files:
+ # stage1, stage1.5, stage2.... When we find these directories we
+ # will be able to install grub from there. And the vmliuz (kernel
+ # image), initrd.img, the grub.conf is probably in these places as
+ # well.
+ for (dev, parts) in self.devices.iteritems():
+ for part in parts:
+ try:
+ if grubUtils.grub_dir_in_partition(part):
+ self._reporting.info("Found grub dir in %s " \
+ "partition." % part.path(), origin = self)
+ self.grub_dir_parts.append(part)
+ except Exception, e:
+ # If something happened while checking the partition
+ # it will not be taken into account.
+ self._reporting.error("There was an error while " \
+ "searching partition %s. Error: %s." % \
+ (part.path(), e) ,origin = self)
+
+ self._reporting.info("Searching for the grub stage1 image.", \
+ origin = self)
+ # We must search in all the possible partitions and devices for the
+ # grub binary.
+ for (dev, parts) in self.devices.iteritems():
+
+ # We look for grub in the device
+ try:
+ if grubUtils.grub_bin_in_dev(Dname(dev)):
+ self._reporting.info("Found grub stage1 image in %s " \
+ "device" % Dname.asPath(dev), origin = self)
+ self.grub_bin_devs.append(grubUtils.Dname(dev))
+
+ except Exception, e:
+ self._reporting.error("There was an error searching for " \
+ "the grub images in device %s. Error %s." % \
+ (Dname.asPath(dev), e), origin = self)
+
+ # No we look for the grub in the partitions.
+ for part in parts:
+ try:
+ if grubUtils.grub_bin_in_part(Dname(part)):
+ self._reporting.info("Found grub stage1 image " \
+ "in %s partition" % Dname.asPath(dev), \
+ origin = self)
+ self.grub_bin_parts.append(Dname(part))
+
+ except Exception, e:
+ self._reporting.error("There was an error searching " \
+ "for grub images in partition %s. Error %s." % \
+ (Dname.asPath(part), e), origin = self)
+
+ self._result = ReturnSuccess
+ except Exception, e:
+ self._reporting.error("An error has ocurred while searching for " \
+ "grubs elements. Error: %s" % e, origin = self)
+ self._result = ReturnFailure
+
+ def diagnose(self):
+ # FIXME ATM we will not take care of the cases that are missing some
+ # stuff from the grub directory, like images and conffile.
+ #
+ # The diagnose is tricky because we have no way of knowing (yet) if
+ # the images found in the devices actually map correctly to the stuff
+ # that we found on the directories. This means that we will allways
+ # reinstall grub in the mbr (the changes will be revertable so its
+ # not that bad) unless we don't find any directory. If no dir is
+ # found, then we fail (fail in this case means postponing the decision
+ # until the fix step to actually ReturnFailure)
+ self._reporting.info("Diagnosing the current state of grub.",
+ origin = self)
+ if len(self.grub_dir_parts) < 0:
+ self._reporting.error("No grub directories where found.",
+ origin = self)
+
+ self.issue_grub_dir.set(checked = True, happened = True,
+ reporting = self._reporting, origin = self)
+ self.issue_grub_image.set(checked = False,
+ reporting = self._reporting, origin = self)
+
+ self._result = ReturnFailure
+ return
+ self.issue_grub_dir.set(checked = True, happened = False,
+ reporting = self._reporting, origin = self)
+
+ # At this point we know that we are going to reinstall the grub. We
+ # inform the user about the state of the stage1 in the partitions but
+ # we do nothing else with this information.
+ if len(self.grub_bin_parts) == 0:
+ self._reporting.info("No valid grub image was found in any " \
+ "partition in the system.", origin = self)
+ if len(self.grub_bin_devs) == 0:
+ self._reporting.info("No valid grub image was found in any " \
+ "device in the system.", origin = self)
+
+ # Only if there is no recognizable image do we consider it to be
+ # an issue.
+ if len(self.grub_bin_parts) == 0 and len(self.grub_bin_devs) == 0:
+ self.issue_grub_image.set(checked = True, happened = True,
+ reporting = self._reporting, origin = self)
+ else:
+ self.issue_grub_image.set(checked = True, happened = False,
+ reporting = self._reporting, origin = self)
+
+ self._result = ReturnFailure
+
+
+ def backup(self):
+ # The grub process is related to the 446 firsta bytes of the partitions
+ # of the device. This means we must backup all the beginings fo the
+ # partitions and devices that we will possibly modify.
+
+ # Since we are going to install the stage1 grub image in all the
+ # devices, we will backup all of them.
+ self._reporting.info("Going to backup all the first 446 bytes of " \
+ "all storage devices in the system.", origin = self)
+ for (device, partitions) in self.devices.iteritems():
+ fd = os.open(Dname.asPath(device), os.O_RDONLY)
+ first446btemp = os.read(fd, 446)
+ os.close(fd)
+ self.backupSpace.backupValue(first446btemp, Dname.asName(device))
+ self._result = ReturnSuccess
+
+
+ def fix(self):
+ # We ar to fail if there is no dirs.
+ if len(self.grub_dir_parts) == 0:
+ self._reporting.error("No grub directories where found... exiting.",
+ origin = self)
+ self.issue_grub_image.set(fixed = False, \
+ reporting = self._reporting, origin = self)
+ self.issue_grub_image.set(fixed = False, \
+ reporting = self._reporting, origin = self)
+
+ self._result = ReturnFailure
+ return
+
+ # Choose and prepare one root in the system.
+ # FIXME: extend the root concept so it can handle varous roots in a
+ # system. For now we just select the first and hope it has
+ # everything.
+
+ # Install the grub in all devs pointing at the special root part.
+ for (drive, partitions) in self.devices.iteritems():
+ self._reporting.info("Trying to install grub on drive %s, " \
+ "pointing to grub directory in %s."%(Dname.asName(drive), \
+ self.grub_dir_parts[0].path()), origin = self)
+ try:
+ grubUtils.install_grub(self.grub_dir_parts[0], Dname(drive))
+ except Exception, e:
+ self._reporting.error("Grub installation on drive %s, " \
+ "pointing to grub directory in %s has failed." % \
+ (Dname.asName(drive), self.grub_dir_parts[0].path()), \
+ origin = self)
+ # If one installation fails its safer to just restore
+ # everything
+ self._result = ReturnFailure
+ return
+
+ self._reporting.info("Grub has successfully installed in all the " \
+ "devices.", origin = self)
+ self._result = ReturnSuccess
+
+ def restore(self):
+ self._reporting.info("Starting the restore process....", level=PLUGIN, \
+ origin = self)
+ for (device, partitions) in self.devices.iteritems():
+ self._reporting.info("Restoring data from device %s" % \
+ Dname.asPath(device), origin = self)
+ try:
+ # Get the value from the backup space
+ first446btemp = self.backupSpace.restoreValue(
+ Dname.asName(device))
+
+ # Copy the value over the existing stuff.
+ fd = os.open(Dname.asPath(device), os.O_WRONLY)
+ os.write(fd, first446btemp)
+ os.close(fd)
+ except Exception, e:
+ self._reporting.info("An error has occurred while trying to " \
+ "recover %s device." % Dname.asName(device), \
+ origin = self)
+ continue
+ self._result = ReturnSuccess
+
+ def clean(self):
+ self._result = ReturnSuccess
diff --git a/plugins/grub/grubUtils.py b/plugins/grub/grubUtils.py
new file mode 100644
index 0000000..6a5578f
--- /dev/null
+++ b/plugins/grub/grubUtils.py
@@ -0,0 +1,406 @@
+# First Aid Kit - diagnostic and repair tool for Linux
+# Copyright (C) 2008 Joel Andres Granados <jgranado@redhat.com>
+#
+# 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, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+import pyfirstaidkit.utils as utils
+
+import os.path
+import re
+import subprocess
+import tempfile
+
+import minihal
+import parted
+
+# List of known or expected values for the system.
+#
+# Where the grub dir shoulc be with respect to the partition root.
+locations = ["/boot/grub", "/grub"]
+
+# The files that are expected to make grub work. nfiles -> needed files.
+nfiles = ["stage1", "stage2"]
+
+# Expected grub configuration file name.
+conffile = "grub.conf"
+
+# Expected mounts file
+mounts = "/proc/mounts"
+
+# Disks starting with these strings will be ignored when looking for system
+# storage devices.
+ignore_devs = ["sr"]
+
+def get_all_devs():
+ """Get all the storage devices that exists currently on the system.
+
+ We only want the device name and the partitions for each device.
+ We don't want the parted structures.
+ Return - dictionary with device name and all device partitions.
+ """
+
+ # Must use an inner function as the test does not consider the device
+ # number. Olny device type.
+ def is_dev_in_ignored(dev):
+ for ignored in ignore_devs:
+ if dev["device"].replace("/dev/","").startswith(ignored):
+ return True
+ return False
+
+ retval = {}
+ for device in minihal.get_devices_by_type("storage"):
+ if device is None:
+ continue
+
+ elif is_dev_in_ignored(device):
+ continue
+
+ elif "storage.removable.media_available" in device.keys() and \
+ device["storage.removable.media_available"] == False:
+ # We ignore stuff that has no media inserted.
+ continue
+
+ else:
+ # parted will provide us with all the partitions.
+ partitions = []
+ parteddev = parted.PedDevice.get(device["device"])
+ disk = parted.PedDisk.new(parteddev)
+ part = disk.next_partition()
+ while part:
+ if part.num > 0:
+ partitions.append(
+ Dname("%s%s"%(device["device"],part.num)))
+ part = disk.next_partition(part)
+ # The key will be the device name and it will contain a list of
+ # parts.
+ retval[Dname.asName(device["device"])] = partitions
+
+ return retval
+
+def grub_dir_in_partition(part):
+ """Search for the grub directory and all needed files in the partition
+
+ It will search for the known locations and necessary files for in the
+ specified partition.
+ Return - list containing partitions with grub.
+ """
+ def do_unmount():
+ part_unmount(part)
+ if os.path.isdir(mountpoint):
+ os.rmdir(mountpoint)
+
+
+ # We search to see if the partition is mounted. If its not we must
+ # mount it in a temporary place to unmount it before we leave this
+ # function.
+ unmount=False
+ mountpoint = is_part_mounted(part)
+ if len(mountpoint) == 0:
+ # This means that its not mounted. And we must unmount it at the
+ # end.
+ unmount=True
+
+ # Select a safe temporary directory where to mount it.
+ mountpoint = tempfile.mkdtemp(prefix=part.name())
+
+ # If the mount fails it will raise an excpetion. We must catch the
+ # exception when this function is called. Same goes for part_unmount.
+ try:
+ part_mount(part, mountpoint)
+ except:
+ # The partition was not mounted erase the directory if empty.
+ # leave if the directoy is not empty
+ os.rmdir(mountpoint)
+ return False
+
+ # Search for the grub directorie in the mounted partition.
+ grubdir=""
+ for dir in locations:
+ if os.path.isdir(utils.join(mountpoint, dir)):
+ grubdir=utils.join(mountpoint, dir)
+ # We don't care if there is another directory in the same partition
+ # It is very unlikely and not an intelligent thing to do anyway.
+ break
+
+ # At this point if we didn't find any of the locations, then grub is not
+ # in this partition.
+ if len(grubdir) == 0:
+ if unmount:
+ do_unmount()
+ return False
+
+ # Now we have to search for the files in the grub directory. The list in
+ # nfiles is the needed files. So if one of the files is not found we
+ # consider that there is not enough context to fix the issue in this part.
+ # FIXME add some code that can replace the files that are missing.
+ foundfiles = 0
+ for file in nfiles:
+ if os.path.isfile(utils.join(grubdir, file)):
+ foundfiles = foundfiles + 1
+
+ # If we don't have all the files we will not even consider this partition.
+ if len(nfiles) > foundfiles:
+ if unmount:
+ do_unmount()
+ return False
+
+ # Search for the grub config file.
+ if not os.path.isfile(utils.join(grubdir, conffile)):
+ if unmount:
+ do_unmount
+ return False
+
+ # FIXME need to implement the kernel and initrd image searching code.
+ # for now we trust that the images are actually there.
+
+ if unmount:
+ do_unmount()
+
+ return True
+
+def is_part_mounted(part):
+ """Search /proc/mounts for the presence of the partition.
+
+ It searches for the "/dev/something" device.
+ If its not mounted it returns an empty mountpoint (not mounted).
+ If its mounted it returns the mount point.
+ """
+ for line in file(mounts).readlines():
+ if re.search(part.path(), line) != None:
+ # The mountpoint is in the second possition.
+ return line.split(" ")[1]
+
+ return ""
+
+def part_mount(part, mountPoint, opts=None):
+ """Mount the partition at mountpoint"""
+ # Create the call
+ call = ["mount"]
+ if opts:
+ call.append(opts)
+ call.extend([part.path(), mountPoint])
+
+ # Call mount
+ proc = subprocess.Popen(call, stdout=subprocess.PIPE, \
+ stderr=subprocess.PIPE)
+ (out, err) = proc.communicate()
+ retcode = proc.wait()
+ if retcode != 0 or len(err) > 0:
+ # The mount failed
+ raise Exception("%s" % (part.path(), err))
+ else:
+ # This probably means that the mount succeded.
+ return True
+
+def part_unmount(part, opts=None):
+ """Unmount the partition that is mounted at mountPoint
+
+ part - It can actually be the part path or the mountpoint
+ """
+
+ # If its not a dev path its a mountpoint.
+ if part.__class__.__name__ == "Dname":
+ umountarg = part.path()
+ else:
+ umountarg = part
+
+ # Create the call
+ call = ["umount"]
+ if opts:
+ call.append(opts)
+ call.append(umountarg)
+
+ # Call umount
+ proc = subprocess.Popen(call, stdout=subprocess.PIPE, \
+ stderr=subprocess.PIPE)
+ (out, err) = proc.communicate()
+ retcode = proc.wait()
+ if retcode != 0 or len(err) > 0:
+ raise Exception("There was an error unmounting partition %s. " \
+ "Error: %s." % (part.path(), err))
+ else:
+ return True
+
+# The Strings contained in the grub stage one:
+stage1strings = ["GRUB", "Geom", "Hard", "Disk", "Read", "Error"]
+
+def grub_bin_in_dev(dev):
+ """Will look in the first 446 bytes of the device for traces of grub.
+
+ Will look for the strings that come with the grub stage1 image. The
+ strings are: "GRUB", "Geom", "Hard", "Disk", "Read" and "Error". These
+ strings must be compared considering the letter case.
+ dev - Dname object representing the storage device.
+ """
+ if (os.path.exists(dev.path())):
+
+ # Read the first 446 bytes of the dev.
+ fd = os.open(dev.path(), os.O_RDONLY)
+ first446b = os.read(fd, 446)
+ os.close(fd)
+
+ # Search for all the strings
+ foundstrings = 0
+ for string in stage1strings:
+ if re.search(string, first446b) != None:
+ foundstrings = foundstrings + 1
+
+ # Only if all the strings are present we give the goahead.
+ if foundstrings == len(stage1strings):
+ return True
+
+ return False
+
+
+def grub_bin_in_part(part):
+ """Will look in the first 446 bytes of the partition for traces of grub.
+
+ Same conditions apply as in grub_bin_in_dev.
+ """
+ return grub_bin_in_dev(part)
+
+# Input string for the grub batch mode.
+# FIXME: Do we need lba, stage2 specification, prefix?
+batch_grub_install = """
+root (%s)
+setup (%s)
+quit
+"""
+def install_grub(root, setup):
+ """Install stage1 grub image in the specified place.
+
+ root - the root where the dir is. This can be a divice or partition.
+ It must be a Dname
+ setup - the dev where to install image. This can be device or partition.
+ It must be a Dname
+
+ return - whatever the grub console puts on stdout.
+ """
+
+ # Construct the root string.
+ grubroot = root.grubName()
+ grubsetup = setup.grubName()
+
+ # Run the command that installs the grub.
+ # FIXME: We are not taking into account the device map.
+ command = ["grub", "--batch"]
+ proc = subprocess.Popen(command, stdout = subprocess.PIPE,
+ stdin = subprocess.PIPE, stderr = subprocess.PIPE)
+ (out, err) = proc.communicate(batch_grub_install%(grubroot, grubsetup))
+
+ m = re.search("Error.*\\n", "%s%s"%(out,err))
+ if m != None:
+ # raise an exception when grub shell returned an error.
+ raise Exception("There was an error while installing grub. Error %s " \
+ % m.group(0))
+
+ return out
+
+# I really don't like the fact that you can have a variable that represents
+# a device or partition and not know, until runtime, with total certainty,
+# if its "/dev/something" or just "something".
+
+# The constant to transform a device leter to a grub hd number. ciconst
+# (char int constant)
+ciconst = ord('a')
+class Dname:
+ """Class to represent device names.
+
+ It will only represent device and partitiosn.
+ """
+ # FIXME: extend this class to raid.
+ def __init__(self, name):
+ if name.__class__.__name__ == "Dname":
+ self.dname = name.dname
+ elif name.startswith("/dev/"):
+ self.dname = name[5:]
+ else:
+ self.dname = name
+
+ @classmethod
+ def asPath(self, dev):
+ """return the device in the "/dev/somthing" form."""
+ if dev.__class__.__name__ == "Dname":
+ return dev.path()
+ else:
+ temp = Dname(dev)
+ return temp.path()
+
+ @classmethod
+ def asName(self, dev):
+ """return the device in the "somthing" form"""
+ if dev.__class__.__name__ == "Dname":
+ return dev.name()
+ else:
+ temp = Dname(dev)
+ return temp.name()
+
+ @classmethod
+ def asGrubName(self, dev, parenthesis = False):
+ """return something that grub understands."""
+ if dev.__class__.__name__ == "Dname":
+ return dev.grubName(parenthesis)
+ else:
+ temp = Dname(dev)
+ return temp.grubName(parenthesis)
+
+ def path(self):
+ return utils.join("/dev/", self.dname)
+
+ def name(self):
+ return self.dname
+
+ def grubName(self, parenthesis = False):
+ """Change the kernel device name to something that grub understands
+
+ It returns a string of the form hd[devicd],[partition]
+ """
+
+ # First we search for the number that ends the device string.
+ m = re.search("[0-9]+$", self.dname)
+ if m == None:
+ partnum = None
+ devnum = ord(self.dname[len(self.dname)-1]) - ciconst
+ else:
+ # The grub partition number scheme is a little different. Its safe
+ # to assume that its one less than the usual scheme.
+ partnum = int(m.group(0))
+ temp = self.dname.strip(str(partnum))
+
+ # Follow grub scheme
+ partnum = partnum - 1
+
+ # We now get the letter that is before the number
+ devnum = ord(temp[len(temp)-1]) - ciconst
+
+ # Must check to see if the values are in range.
+ if (partnum != None and partnum < 0) or (devnum < 0):
+ raise Exception("The conversion from kernel device scheme to " \
+ "grub scheme failed.")
+
+ # Decide weather to return with or withoug parenthesis.
+ if parenthesis:
+ openpar = "("
+ closepar = ")"
+ else:
+ openpar = ""
+ closepar = ""
+
+ # Create the gurb device string.
+ if partnum == None:
+ return "%shd%s%s"%(openpar, devnum, closepar)
+ else:
+ return "%shd%s,%s%s"%(openpar, devnum, partnum, closepar)
+
diff --git a/plugins/grub/grubconf.py b/plugins/grub/grubconf.py
new file mode 100644
index 0000000..13808c0
--- /dev/null
+++ b/plugins/grub/grubconf.py
@@ -0,0 +1,17 @@
+# First Aid Kit - diagnostic and repair tool for Linux
+# Copyright (C) 2008 Joel Andres Granados <jgranado@redhat.com>
+#
+# 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, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
diff --git a/plugins/grub/minihal.py b/plugins/grub/minihal.py
new file mode 100644
index 0000000..2b5504e
--- /dev/null
+++ b/plugins/grub/minihal.py
@@ -0,0 +1,74 @@
+# minihal.py: Simple wrapper around HAL
+#
+# Copyright (C) 2008 Red Hat, Inc. All rights reserved.
+#
+# 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 <http://www.gnu.org/licenses/>.
+#
+# Author(s): Bill Nottingham <notting@redhat.com>
+# This file is stolen from anaconda code :). In rescue mode there will be
+# no replication of code as this file will not go into the rescue mode image.
+#
+
+"""Simple wrapper around HAL"""
+
+import dbus
+
+def get_device(udi):
+ """Retrieve all properties of a particular device (by UDI)"""
+ try:
+ bus = dbus.SystemBus()
+ haldev = dbus.Interface(bus.get_object("org.freedesktop.Hal", udi), \
+ "org.freedesktop.Hal.Device")
+ props = haldev.GetAllProperties()
+ except dbus.exceptions.DBusException:
+ return None
+
+ if props.has_key('block.device'):
+ props['device'] = props['block.device'].encode("utf-8")
+ elif props.has_key('linux.device_file'):
+ props['device'] = props['linux.device_file'].encode("utf-8")
+ elif props.has_key('net.interface'):
+ props['device'] = props['net.interface'].encode("utf-8")
+ else:
+ props['device'] = None
+
+ props['description'] = ''
+ if props.has_key('info.product'):
+ if props.has_key('info.vendor'):
+ props['description'] = '%s %s' % \
+ (props['info.vendor'],props['info.product'])
+ else:
+ props['description'] = props['info.product']
+ else:
+ props['description'] = props['info.udi']
+ if props.has_key('net.originating_device'):
+ pdev = get_device(props['net.originating_device'])
+ props['description'] = pdev['description']
+
+ return props
+
+def get_devices_by_type(type):
+ """Retrieve all devices of a particular type"""
+ ret = []
+ try:
+ bus = dbus.SystemBus()
+ hal = dbus.Interface(bus.get_object("org.freedesktop.Hal", \
+ "/org/freedesktop/Hal/Manager"),"org.freedesktop.Hal.Manager")
+ except:
+ return ret
+ for udi in hal.FindDeviceByCapability(type):
+ dev = get_device(udi)
+ if dev:
+ ret.append(dev)
+ return ret
diff --git a/pyfirstaidkit/utils/__init__.py b/pyfirstaidkit/utils/__init__.py
index 94e9279..28eb37e 100644
--- a/pyfirstaidkit/utils/__init__.py
+++ b/pyfirstaidkit/utils/__init__.py
@@ -16,6 +16,7 @@
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
import os
+import os.path
import sys
import subprocess
from backup import *
@@ -44,4 +45,14 @@ Returns the subprocess.Popen object"""
stderr = subprocess.PIPE)
+def join(path1, path2):
+ """Avoids the os.path.join behavioir.
+ if a full path is given to os.path.join it ignores the prefious
+ arguments. The needed behavior is to join two paths with only
+ one separator.
+ path1 - being the begining of the path name and
+ path2 - being the end.
+ """
+ # We strip the paths first and then join them.
+ return os.path.join(os.sep, path1.strip(os.sep), path2.strip(os.sep))