From e22490465c43b9b223a6c5ca945d3a7d271c5717 Mon Sep 17 00:00:00 2001 From: Joel Andres Granados Date: Tue, 15 Jul 2008 21:53:31 +0200 Subject: Continue with the name standarization and simplification. - The "_p" is not needed. Avoiding the suffix makes the names simpler. - When the plugin needs to be separated into varios subplugins, use the _ to signify that a subplugin is related to a father plugin. Like rpm_lowlevel, this say that lowlevel is part of the rpm plugin. - When naming a plugin keep the name SHORT and simple. - Be sure to name all the plugin packages as "plugin-name" --- plugins/grub.py | 192 ++++++++++ plugins/mdadm_conf.py | 192 ++++++++++ plugins/p_discovery.py | 110 ------ plugins/p_grub.py | 192 ---------- plugins/p_mdadm_config.py | 192 ---------- plugins/p_passwd.py | 72 ---- plugins/p_rpm/__init__.py | 68 ---- plugins/p_rpm/issue_packages.py | 67 ---- plugins/p_rpm_lowlevel/__init__.py | 71 ---- plugins/p_rpm_lowlevel/issue_locks.py | 73 ---- plugins/p_rpm_lowlevel/issue_packages.py | 81 ----- plugins/p_undelparts/Makefile | 38 -- plugins/p_undelparts/__init__.py | 20 -- plugins/p_undelparts/_undelpart.c | 578 ------------------------------ plugins/p_undelparts/undeletePartition.py | 171 --------- plugins/p_xserver.py | 208 ----------- plugins/passwd.py | 72 ++++ plugins/rpm/__init__.py | 68 ++++ plugins/rpm/issue_packages.py | 67 ++++ plugins/rpm_lowlevel/__init__.py | 71 ++++ plugins/rpm_lowlevel/issue_locks.py | 73 ++++ plugins/rpm_lowlevel/issue_packages.py | 81 +++++ plugins/sysinfo.py | 110 ++++++ plugins/undelparts/Makefile | 38 ++ plugins/undelparts/__init__.py | 20 ++ plugins/undelparts/_undelpart.c | 578 ++++++++++++++++++++++++++++++ plugins/undelparts/undeletePartition.py | 171 +++++++++ plugins/xserver.py | 208 +++++++++++ 28 files changed, 1941 insertions(+), 1941 deletions(-) create mode 100644 plugins/grub.py create mode 100644 plugins/mdadm_conf.py delete mode 100644 plugins/p_discovery.py delete mode 100644 plugins/p_grub.py delete mode 100644 plugins/p_mdadm_config.py delete mode 100644 plugins/p_passwd.py delete mode 100644 plugins/p_rpm/__init__.py delete mode 100644 plugins/p_rpm/issue_packages.py delete mode 100644 plugins/p_rpm_lowlevel/__init__.py delete mode 100644 plugins/p_rpm_lowlevel/issue_locks.py delete mode 100644 plugins/p_rpm_lowlevel/issue_packages.py delete mode 100644 plugins/p_undelparts/Makefile delete mode 100644 plugins/p_undelparts/__init__.py delete mode 100644 plugins/p_undelparts/_undelpart.c delete mode 100644 plugins/p_undelparts/undeletePartition.py delete mode 100644 plugins/p_xserver.py create mode 100644 plugins/passwd.py create mode 100644 plugins/rpm/__init__.py create mode 100644 plugins/rpm/issue_packages.py create mode 100644 plugins/rpm_lowlevel/__init__.py create mode 100644 plugins/rpm_lowlevel/issue_locks.py create mode 100644 plugins/rpm_lowlevel/issue_packages.py create mode 100644 plugins/sysinfo.py create mode 100644 plugins/undelparts/Makefile create mode 100644 plugins/undelparts/__init__.py create mode 100644 plugins/undelparts/_undelpart.c create mode 100644 plugins/undelparts/undeletePartition.py create mode 100644 plugins/xserver.py (limited to 'plugins') diff --git a/plugins/grub.py b/plugins/grub.py new file mode 100644 index 0000000..146ee6f --- /dev/null +++ b/plugins/grub.py @@ -0,0 +1,192 @@ +# First Aid Kit - diagnostic and repair tool for Linux +# Copyright (C) 2007 Martin Sivak +# +# 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.reporting import PLUGIN +from pyfirstaidkit.returns import * +from pyfirstaidkit.configuration import Config,getConfigBits +from pyfirstaidkit.utils import spawnvch +from pyfirstaidkit.issue import SimpleIssue +import os.path +import difflib +import re +import copy + +cfgBits = getConfigBits("firstaidkit-plugin-grub") +import sys +sys.path.append(cfgBits.anaconda.path) +sys.path.append(cfgBits.booty.path) + +class Sample1Plugin(Plugin): + """This plugin checks the GRUB bootloader.""" + name = "GRUB plugin" + version = "0.0.1" + author = "Martin Sivak" + + flows = Flow.init(Plugin) + flows["install"] = Flow({ + Plugin.initial: {Return: "prepare"}, + "prepare" : {ReturnSuccess: "diagnose", ReturnFailure: "clean", None: "clean"}, + "diagnose" : {ReturnSuccess: "clean", ReturnFailure: "backup", None: "clean"}, + "backup" : {ReturnSuccess: "install", ReturnFailure: "clean", None: "clean"}, + "restore" : {ReturnSuccess: "clean", ReturnFailure: "clean", None: "clean"}, + "install" : {ReturnSuccess: "clean", ReturnFailure: "restore", None: "restore"}, + "clean" : {ReturnSuccess: Plugin.final, ReturnFailure: Plugin.final, None: "clean"} + }, description="Install the bootloader into the partition specified in parameters") + + @classmethod + def getDeps(cls): + return set(["experimental", "root", "filesystem", "arch-x86"]) + + def __init__(self, *args, **kwargs): + Plugin.__init__(self, *args, **kwargs) + self._partitions = set() #partitions in the system + self._drives = [] #drives in the system + self._bootable = [] #partitions with boot flag + self._linux = [] #Linux type partitions (0x83 Linux) + self._grub_dir = set() #directories with stage1, menu.lst and other needed files + self._grub = set() #devices with possible grub instalation + self._grub_map = {} #mapping from linux device names to grub device names + self._grub_mask = re.compile("""\(hd[0-9]*,[0-9]*\)""") + + self._issue_boot_flag = SimpleIssue("Bootable partition", "No bootable partition found in partition table and GRUB is not MBR installed") + self._issue_grub_dir = SimpleIssue("GRUB directory", "No GRUB directory was not found") + self._issue_grub_installed = SimpleIssue("GRUB installed", "No GRUB was found in bootable partitions or MBR") + + def prepare(self): + from bootloaderInfo import x86BootloaderInfo + self._bootloaderInfo = x86BootloaderInfo() + self._result=ReturnSuccess + + self._issue_boot_flag.set(reporting = self._reporting, origin = self, level = PLUGIN) + self._issue_grub_dir.set(reporting = self._reporting, origin = self, level = PLUGIN) + self._issue_grub_installed.set(reporting = self._reporting, origin = self, level = PLUGIN) + + def backup(self): + self._result=ReturnSuccess + + def restore(self): + self._result=ReturnSuccess + + def diagnose(self): + #Find partitions + self._reporting.debug(origin = self, level = PLUGIN, message = "Reading partition list") + self._partitions = set(map(lambda l: l.split()[3], file("/proc/partitions").readlines()[2:])) + + #and select only partitions with minor number 0 -> drives + self._drives = map(lambda l: l.split(), file("/proc/partitions").readlines()[2:]) + self._drives = filter(lambda l: l[1]=="0", self._drives) + self._drives = map(lambda l: l[3], self._drives) + + #get boot flags + self._reporting.debug(origin = self, level = PLUGIN, message = "Locating bootable partitions") + for d in self._drives: + fdisk = spawnvch(executable = "/sbin/fdisk", args = ["fdisk", "-l", "/dev/%s" % (d,)], chroot = Config.system.root) + ret = fdisk.wait() + if ret==0: + for l in fdisk.stdout.readlines(): + if l.startswith("/dev/%s" % (d,)): + data = l.split() + if data[1]=="*": #boot flag + self._reporting.info(origin = self, level = PLUGIN, message = "Bootable partition found: %s" % (data[0][5:],)) + self._bootable.append(data[0][5:]) #strip the "/dev/" beginning + if data[6]=="Linux": + self._linux.append(data[0][5:]) + self._reporting.debug(origin = self, level = PLUGIN, message = "Linux partition found: %s" % (data[0][5:],)) + else: + if data[5]=="Linux": + self._linux.append(data[0][5:]) + self._reporting.debug(origin = self, level = PLUGIN, message = "Linux partition found: %s" % (data[0][5:],)) + + if len(self._bootable) == 0: + self._issue_boot_flag.set(checked = True, happened = True, reporting = self._reporting, origin = self, level = PLUGIN) + + + #Find grub directories + self._reporting.debug(origin = self, level = PLUGIN, message = "Locating the grub directories") + grub = spawnvch(executable = "/sbin/grub", args = ["grub", "--batch"], chroot = Config.system.root) + (stdout, stderr) = grub.communicate("find /boot/grub/menu.lst\nfind /grub/menu.lst\n") + for l in stdout.split("\n"): + if self._grub_mask.search(l): + self._reporting.info(origin = self, level = PLUGIN, message = "Grub directory found at %s" % (l.strip(),)) + self._grub_dir.add(l.strip()) + + if len(self._grub_dir) == 0: + self._issue_grub_dir.set(checked = True, happened = True, reporting = self._reporting, origin = self, level = PLUGIN) + + #TODO Mount the required partitions from self._grub_dir and read the important files from there + for gdrive in self._grub_dir: + drive, partition = gdrive[3:-1].split(",") + devicename = "%s%d" % (self._bootloaderInfo.drivelist[drive], int(partition)+1) + #XXX here, check the mount + + #Read the device map + self._reporting.debug(origin = self, level = PLUGIN, message = "Reading device map") + try: + for l in file(os.path.join(Config.system.root, "/boot/grub/device.map"), "r").readlines(): + if l.startswith("#"): + continue + (v,k) = l.split() + self._grub_map[k] = v + except OSError, e: + self._reporting.warning(origin = self, level = PLUGIN, message = "Device map file not found: %s" % (str(e),)) + pass #the file is not there, bad but not a reason to crash + + #Find out where is the grub installed + try: + stage1mask = file(os.path.join(Config.system.root, "/boot/grub/stage1"), "r").read(512) + bootsectors = {} + for p in self._partitions: + self._reporting.debug(origin = self, level = PLUGIN, message = "Reading boot sector from %s" % (p,)) + bootsector = file(os.path.join("/dev", p), "r").read(512) + bootsectors[bootsector] = self._bootloaderInfo.grubbyPartitionName(p) + + for k in difflib.get_close_matches(stage1mask, bootsectors.keys()): + self._reporting.info(origin = self, level = PLUGIN, message = "Installed Grub probably found at %s" % (bootsectors[k],)) + self._grub.add(bootsectors[k]) + except OSError, e: + self._reporting.warning(origin = self, level = PLUGIN, message = "Stage1 image not found: %s" % (str(e),)) + pass #the file is not there, too bad but not a reason to crash the diagnose + + #if there is the grub configuration dir and the grub appears installed into MBR or bootable partition, then we are probably OK + if len(self._grub_dir)>0 and len(self._grub)>0 and len(set(self._grub).intersection(set(self._bootable+self._drives)))>0: + self._result=ReturnSuccess + self._issue_grub_installed.set(checked = True, happened = False, reporting = self._reporting, origin = self, level = PLUGIN) + self._issue_boot_flag.set(checked = True, happened = False, reporting = self._reporting, origin = self, level = PLUGIN) #if we can find bootable, than it didn't happen + self._dependencies.provide("boot-grub") + else: + self._issue_grub_installed.set(checked = True, happened = True, reporting = self._reporting, origin = self, level = PLUGIN) + self._result=ReturnFailure + + def fix(self): + self._result=ReturnFailure + self._issue_boot_flag.set(fixed = False, reporting = self._reporting, origin = self, level = PLUGIN) + self._issue_grub_dir.set(fixed = False, reporting = self._reporting, origin = self, level = PLUGIN) + self._issue_grub_installed.set(fixed = False, reporting = self._reporting, origin = self, level = PLUGIN) + + def install(self): + #install the grub to Config.operation.params partition + self._reporting.info(origin = self, level = PLUGIN, message = "Installing grub into %s (%s)" % (Config.operation.params, self._bootloaderInfo.grubbyPartitionName(Config.operation.params))) + grub = spawnvch(executable = "/sbin/grub", args = ["grub", "--batch"], chroot = Config.system.root) + (stdout, stderr) = grub.communicate("root %s\nsetup %s\n" % (self._grub_dir[0], self._bootloaderInfo.grubbyPartitionName(Config.operation.params))) + self._result=ReturnFailure + + def clean(self): + self._result=ReturnSuccess + +def get_plugin(): + return Sample1Plugin diff --git a/plugins/mdadm_conf.py b/plugins/mdadm_conf.py new file mode 100644 index 0000000..55bb6be --- /dev/null +++ b/plugins/mdadm_conf.py @@ -0,0 +1,192 @@ +# First Aid Kit - diagnostic and repair tool for Linux +# Copyright (C) 2008 Joel Andres Granados +# +# 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 * + +import os.path + +class MdadmConfig(Plugin): + """ Addresses the validity and presence of /etc/mdadm.conf """ + flows = Flow.init(Plugin) + name = "mdadm configuration" + 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) + self.currentFileDict = {} #what we read from /etc/mdadm.conf + self.scannedFileDict = {} #what `mdadm --misc --detail --scan` + self.scannedFile = None # what `mdadm --misc --detail --scan` returns + self.configFile = os.path.join(Config.system.root,"/etc/mdadm.conf") + self.backupSpace = self._backups.getBackup(str(self)) + self._issue = SimpleIssue(self.name, "mdadm.con misconfigured") + + def prepare(self): + # We read the configuration file if it exists + if os.path.exists(self.configFile): + self._reporting.info("Gathering information from %s."% + self.configFile, level = PLUGIN, origin = self) + fd = open(self.configFile, "r") + for line in fd.readlines(): + splitline = line.strip("\n").split(" ") + if "ARRAY" in splitline: + self.currentFileDict[splitline[1]] = splitline + fd.close() + + else: + self._reporting.info("File %s was not found."% + self.configFile, level = PLUGIN, origin = self) + + # We execute the mdadm command + self._reporting.info("Scanning for software raid with mdadm.", + level = PLUGIN, origin = self) + mdadmargs = ["--misc", "--detail", "--scan"] + proc = spawnvch(executable = "mdadm", args = mdadmargs, + chroot = Config.system.root) + (out, err) = proc.communicate() + + if err == '': + # means that the command ran with no errors. + for line in out.__str__().split("\n"): + splitline = line.strip("\n").split(" ") + if "ARRAY" in splitline: + self.scannedFileDict[splitline[1]] = splitline + self.scannedFile = out + else: + # This should make the flow go to clean. If there is an error we + # should not trust what mdadm tells us. + self._reporting.info("The mdadm command had the following " \ + "error:%s. The plugin will silently exit."%err, + level = PLUGIN, origin = self) + self._result = None + return + self._result = ReturnSuccess + self._issue.set(reporting = self._reporting, level = PLUGIN, + origin = self) + + def diagnose(self): + # If nothing was returned by the mdadm command. we dont have software + # raid. + if len(self.scannedFileDict) == 0: + self._reporting.info("There was no sofware raid found by the " \ + "mdadm command.... Nothing to do.", level = PLUGIN, + origin = self) + self._result = ReturnSuccess + self._issue.set(checked=True, happened=False, + reporting=self._reporting, level=PLUGIN, origin=self) + return + + # If nothing is detected the result is successfull. + self._result = ReturnSuccess + + # If there is one difference between the configs, regarding the + # ARRAYS. We replace the config file. Lets check for missing arrays + # in the curren config file. + for key, value in self.scannedFileDict.iteritems(): + if not self.currentFileDict.has_key(key): + self._reporting.info("Found that the current mdadm.conf is " \ + "missing: %s."%value, level = PLUGIN, origin = self) + self._result = ReturnFailure + + # Lets check for additional ARRAYS that should not be there. + for key, value in self.currentFileDict.iteritems(): + if not self.scannedFileDict.has_key(key): + self._reporting.info("The followint entry: %s, is in the " \ + "config file but was not detected by mdadm."%value, + level = PLUGIN, origin = self) + self._result = ReturnFailure + + if self._result == ReturnSuccess: + self._reporting.info("There was no problem found with the " \ + "current mdadm.conf file.", level = PLUGIN, origin = self) + + self._issue.set(checked = True, + happened = (self._result == ReturnFailure), + reporting = self._reporting, level = PLUGIN, origin = self) + + def backup(self): + if os.path.isfile(self.configFile): + self._reporting.info("Making a backup of %s."% + self.configFile, level = PLUGIN, origin = self) + self.backupSpace.backupPath(self.configFile) + + else: + self._reporting.info("It appears that the file %s does not "\ + "exist. No backup attempt will be made."%self.configFile, + level = PLUGIN, origin = self) + + self._result = ReturnSuccess + + def fix(self): + try: + self._reporting.info("Going to write configuration to %s."% + self.configFile, level = PLUGIN, origin = self) + fd = open(self.configFile, "w") + fd.write("# File created by Firstaidkit.\n") + fd.write("# DEVICE partitions\n") + fd.write("# MAILADDR root\n") + fd.write(self.scannedFile) + fd.close() + self._reporting.info("Configuration file writen.", level = PLUGIN, + origin = self) + + # The original mdadm.conf will be restore to + # mdadm.conf.firstaidkit, just in case. + self._reporting.info("Will put the old mdadm.conf in %s."% + os.path.join(Config.system.root, + self.configFile+".firstaidkit"), level=PLUGIN, + origin=self) + self.backupSpace.restoreName(self.configFile, + path = self.configFile+".firstaidkit") + self._result = ReturnSuccess + + except IOError: + fd.close() + self._reporting.info("Error occurred while writing %s."% + self.configFile, level = PLUGIN, origin = self) + self._result = ReturnFailure + self._issue.set(fixed = (self._result == ReturnSuccess), + reporting = self._reporting, level = PLUGIN, origin = self) + + def restore(self): + if not self.backupSpace.exists(self.configFile): + # This is the case where there is no config file. + self._reporting.info("The backedup file was not present. " \ + "Assuming that %s was ont present to begin with."% + self.configFile, level = PLUGIN, original = self) + else: + self._reporting.info("Restoring original file.", level = PLUGIN , + origin = self) + self.backupSpace.restoreName(self.configFile) + self._result = ReturnSuccess + + def clean(self): + self._result = ReturnSuccess + +def get_plugin(): + return MdadmConfig + diff --git a/plugins/p_discovery.py b/plugins/p_discovery.py deleted file mode 100644 index 72bb733..0000000 --- a/plugins/p_discovery.py +++ /dev/null @@ -1,110 +0,0 @@ -# First Aid Kit - diagnostic and repair tool for Linux -# Copyright (C) 2007 Martin Sivak -# -# 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.reporting import PLUGIN -from pyfirstaidkit.returns import * -from pyfirstaidkit.issue import SimpleIssue -from pyfirstaidkit.configuration import Config -from pyfirstaidkit.utils import spawnvch - -import os - -class Sample1Plugin(Plugin): - """Discover information about the system""" - name = "Discovery" - description = "Discover properties of the system" - version = "0.0.1" - author = "Martin Sivak" - - flows = Flow.init(Plugin) - flows["fix"] = flows["diagnose"] - - def __init__(self, *args, **kwargs): - Plugin.__init__(self, *args, **kwargs) - self._issue = SimpleIssue(self.name, "Discovering system properties failed") - - def prepare(self): - self._issue.set(reporting = self._reporting, origin = self, level = PLUGIN) - self._result=ReturnSuccess - - def diagnose(self): - #architecture and cpus - (unamestdout, unamestderr) = spawnvch(executable = "/bin/uname", args = ["uname", "-a"], chroot = Config.system.root).communicate("") - self._info.uname = unamestdout.split("\n")[0] - - #memory - (freestdout, freestderr) = spawnvch(executable = "/usr/bin/free", args = ["free"], chroot = Config.system.root).communicate("") - freedata = freestdout.split("\n") - self._info.memory = freedata[1].split()[1] - self._info.swap = freedata[3].split()[1] - - #pci - pcilist = [] - (lspcistdout, lspcistderr) = spawnvch(executable = "/sbin/lspci", args = ["lspci"], chroot = Config.system.root).communicate("") - for l in lspcistdout.split("\n"): - try: - (id, name) = l.split(" ", 1) - setattr(self._info, "_".join(["pci", id]), name) - pcilist.append(id) - except: - pass - self._info.pci = " ".join(pcilist) - - #usb - if os.path.exists(os.path.join(Config.system.root, "/proc/bus/usb")): - self._info.usb = "True" - else: - self._info.usb = "False" - - #scsi - if os.path.exists(os.path.join(Config.system.root, "/proc/scsi/device_info")): - self._info.scsi = "True" - else: - self._info.scsi = "False" - - #ide - if os.path.exists(os.path.join(Config.system.root, "/proc/ide")): - self._info.ide = "True" - else: - self._info.ide = "False" - - #partitions - partitionlist = [] - for l in open("/proc/partitions").readlines()[2:]: - try: - (major, minor, blocks, name) = l.split() - if name.startswith("ram"): - continue - setattr(self._info, "_".join(["partition", name]), blocks) - partitionlist.append(name) - except: - continue - self._info.partition = " ".join(partitionlist) - - #net - - - self._dependencies.provide("discovery") - self._issue.set(checked = True, happened = False, reporting = self._reporting, origin = self, level = PLUGIN) - self._result=ReturnSuccess - - def clean(self): - self._result=ReturnSuccess - -def get_plugin(): - return Sample1Plugin diff --git a/plugins/p_grub.py b/plugins/p_grub.py deleted file mode 100644 index 146ee6f..0000000 --- a/plugins/p_grub.py +++ /dev/null @@ -1,192 +0,0 @@ -# First Aid Kit - diagnostic and repair tool for Linux -# Copyright (C) 2007 Martin Sivak -# -# 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.reporting import PLUGIN -from pyfirstaidkit.returns import * -from pyfirstaidkit.configuration import Config,getConfigBits -from pyfirstaidkit.utils import spawnvch -from pyfirstaidkit.issue import SimpleIssue -import os.path -import difflib -import re -import copy - -cfgBits = getConfigBits("firstaidkit-plugin-grub") -import sys -sys.path.append(cfgBits.anaconda.path) -sys.path.append(cfgBits.booty.path) - -class Sample1Plugin(Plugin): - """This plugin checks the GRUB bootloader.""" - name = "GRUB plugin" - version = "0.0.1" - author = "Martin Sivak" - - flows = Flow.init(Plugin) - flows["install"] = Flow({ - Plugin.initial: {Return: "prepare"}, - "prepare" : {ReturnSuccess: "diagnose", ReturnFailure: "clean", None: "clean"}, - "diagnose" : {ReturnSuccess: "clean", ReturnFailure: "backup", None: "clean"}, - "backup" : {ReturnSuccess: "install", ReturnFailure: "clean", None: "clean"}, - "restore" : {ReturnSuccess: "clean", ReturnFailure: "clean", None: "clean"}, - "install" : {ReturnSuccess: "clean", ReturnFailure: "restore", None: "restore"}, - "clean" : {ReturnSuccess: Plugin.final, ReturnFailure: Plugin.final, None: "clean"} - }, description="Install the bootloader into the partition specified in parameters") - - @classmethod - def getDeps(cls): - return set(["experimental", "root", "filesystem", "arch-x86"]) - - def __init__(self, *args, **kwargs): - Plugin.__init__(self, *args, **kwargs) - self._partitions = set() #partitions in the system - self._drives = [] #drives in the system - self._bootable = [] #partitions with boot flag - self._linux = [] #Linux type partitions (0x83 Linux) - self._grub_dir = set() #directories with stage1, menu.lst and other needed files - self._grub = set() #devices with possible grub instalation - self._grub_map = {} #mapping from linux device names to grub device names - self._grub_mask = re.compile("""\(hd[0-9]*,[0-9]*\)""") - - self._issue_boot_flag = SimpleIssue("Bootable partition", "No bootable partition found in partition table and GRUB is not MBR installed") - self._issue_grub_dir = SimpleIssue("GRUB directory", "No GRUB directory was not found") - self._issue_grub_installed = SimpleIssue("GRUB installed", "No GRUB was found in bootable partitions or MBR") - - def prepare(self): - from bootloaderInfo import x86BootloaderInfo - self._bootloaderInfo = x86BootloaderInfo() - self._result=ReturnSuccess - - self._issue_boot_flag.set(reporting = self._reporting, origin = self, level = PLUGIN) - self._issue_grub_dir.set(reporting = self._reporting, origin = self, level = PLUGIN) - self._issue_grub_installed.set(reporting = self._reporting, origin = self, level = PLUGIN) - - def backup(self): - self._result=ReturnSuccess - - def restore(self): - self._result=ReturnSuccess - - def diagnose(self): - #Find partitions - self._reporting.debug(origin = self, level = PLUGIN, message = "Reading partition list") - self._partitions = set(map(lambda l: l.split()[3], file("/proc/partitions").readlines()[2:])) - - #and select only partitions with minor number 0 -> drives - self._drives = map(lambda l: l.split(), file("/proc/partitions").readlines()[2:]) - self._drives = filter(lambda l: l[1]=="0", self._drives) - self._drives = map(lambda l: l[3], self._drives) - - #get boot flags - self._reporting.debug(origin = self, level = PLUGIN, message = "Locating bootable partitions") - for d in self._drives: - fdisk = spawnvch(executable = "/sbin/fdisk", args = ["fdisk", "-l", "/dev/%s" % (d,)], chroot = Config.system.root) - ret = fdisk.wait() - if ret==0: - for l in fdisk.stdout.readlines(): - if l.startswith("/dev/%s" % (d,)): - data = l.split() - if data[1]=="*": #boot flag - self._reporting.info(origin = self, level = PLUGIN, message = "Bootable partition found: %s" % (data[0][5:],)) - self._bootable.append(data[0][5:]) #strip the "/dev/" beginning - if data[6]=="Linux": - self._linux.append(data[0][5:]) - self._reporting.debug(origin = self, level = PLUGIN, message = "Linux partition found: %s" % (data[0][5:],)) - else: - if data[5]=="Linux": - self._linux.append(data[0][5:]) - self._reporting.debug(origin = self, level = PLUGIN, message = "Linux partition found: %s" % (data[0][5:],)) - - if len(self._bootable) == 0: - self._issue_boot_flag.set(checked = True, happened = True, reporting = self._reporting, origin = self, level = PLUGIN) - - - #Find grub directories - self._reporting.debug(origin = self, level = PLUGIN, message = "Locating the grub directories") - grub = spawnvch(executable = "/sbin/grub", args = ["grub", "--batch"], chroot = Config.system.root) - (stdout, stderr) = grub.communicate("find /boot/grub/menu.lst\nfind /grub/menu.lst\n") - for l in stdout.split("\n"): - if self._grub_mask.search(l): - self._reporting.info(origin = self, level = PLUGIN, message = "Grub directory found at %s" % (l.strip(),)) - self._grub_dir.add(l.strip()) - - if len(self._grub_dir) == 0: - self._issue_grub_dir.set(checked = True, happened = True, reporting = self._reporting, origin = self, level = PLUGIN) - - #TODO Mount the required partitions from self._grub_dir and read the important files from there - for gdrive in self._grub_dir: - drive, partition = gdrive[3:-1].split(",") - devicename = "%s%d" % (self._bootloaderInfo.drivelist[drive], int(partition)+1) - #XXX here, check the mount - - #Read the device map - self._reporting.debug(origin = self, level = PLUGIN, message = "Reading device map") - try: - for l in file(os.path.join(Config.system.root, "/boot/grub/device.map"), "r").readlines(): - if l.startswith("#"): - continue - (v,k) = l.split() - self._grub_map[k] = v - except OSError, e: - self._reporting.warning(origin = self, level = PLUGIN, message = "Device map file not found: %s" % (str(e),)) - pass #the file is not there, bad but not a reason to crash - - #Find out where is the grub installed - try: - stage1mask = file(os.path.join(Config.system.root, "/boot/grub/stage1"), "r").read(512) - bootsectors = {} - for p in self._partitions: - self._reporting.debug(origin = self, level = PLUGIN, message = "Reading boot sector from %s" % (p,)) - bootsector = file(os.path.join("/dev", p), "r").read(512) - bootsectors[bootsector] = self._bootloaderInfo.grubbyPartitionName(p) - - for k in difflib.get_close_matches(stage1mask, bootsectors.keys()): - self._reporting.info(origin = self, level = PLUGIN, message = "Installed Grub probably found at %s" % (bootsectors[k],)) - self._grub.add(bootsectors[k]) - except OSError, e: - self._reporting.warning(origin = self, level = PLUGIN, message = "Stage1 image not found: %s" % (str(e),)) - pass #the file is not there, too bad but not a reason to crash the diagnose - - #if there is the grub configuration dir and the grub appears installed into MBR or bootable partition, then we are probably OK - if len(self._grub_dir)>0 and len(self._grub)>0 and len(set(self._grub).intersection(set(self._bootable+self._drives)))>0: - self._result=ReturnSuccess - self._issue_grub_installed.set(checked = True, happened = False, reporting = self._reporting, origin = self, level = PLUGIN) - self._issue_boot_flag.set(checked = True, happened = False, reporting = self._reporting, origin = self, level = PLUGIN) #if we can find bootable, than it didn't happen - self._dependencies.provide("boot-grub") - else: - self._issue_grub_installed.set(checked = True, happened = True, reporting = self._reporting, origin = self, level = PLUGIN) - self._result=ReturnFailure - - def fix(self): - self._result=ReturnFailure - self._issue_boot_flag.set(fixed = False, reporting = self._reporting, origin = self, level = PLUGIN) - self._issue_grub_dir.set(fixed = False, reporting = self._reporting, origin = self, level = PLUGIN) - self._issue_grub_installed.set(fixed = False, reporting = self._reporting, origin = self, level = PLUGIN) - - def install(self): - #install the grub to Config.operation.params partition - self._reporting.info(origin = self, level = PLUGIN, message = "Installing grub into %s (%s)" % (Config.operation.params, self._bootloaderInfo.grubbyPartitionName(Config.operation.params))) - grub = spawnvch(executable = "/sbin/grub", args = ["grub", "--batch"], chroot = Config.system.root) - (stdout, stderr) = grub.communicate("root %s\nsetup %s\n" % (self._grub_dir[0], self._bootloaderInfo.grubbyPartitionName(Config.operation.params))) - self._result=ReturnFailure - - def clean(self): - self._result=ReturnSuccess - -def get_plugin(): - return Sample1Plugin diff --git a/plugins/p_mdadm_config.py b/plugins/p_mdadm_config.py deleted file mode 100644 index 55bb6be..0000000 --- a/plugins/p_mdadm_config.py +++ /dev/null @@ -1,192 +0,0 @@ -# First Aid Kit - diagnostic and repair tool for Linux -# Copyright (C) 2008 Joel Andres Granados -# -# 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 * - -import os.path - -class MdadmConfig(Plugin): - """ Addresses the validity and presence of /etc/mdadm.conf """ - flows = Flow.init(Plugin) - name = "mdadm configuration" - 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) - self.currentFileDict = {} #what we read from /etc/mdadm.conf - self.scannedFileDict = {} #what `mdadm --misc --detail --scan` - self.scannedFile = None # what `mdadm --misc --detail --scan` returns - self.configFile = os.path.join(Config.system.root,"/etc/mdadm.conf") - self.backupSpace = self._backups.getBackup(str(self)) - self._issue = SimpleIssue(self.name, "mdadm.con misconfigured") - - def prepare(self): - # We read the configuration file if it exists - if os.path.exists(self.configFile): - self._reporting.info("Gathering information from %s."% - self.configFile, level = PLUGIN, origin = self) - fd = open(self.configFile, "r") - for line in fd.readlines(): - splitline = line.strip("\n").split(" ") - if "ARRAY" in splitline: - self.currentFileDict[splitline[1]] = splitline - fd.close() - - else: - self._reporting.info("File %s was not found."% - self.configFile, level = PLUGIN, origin = self) - - # We execute the mdadm command - self._reporting.info("Scanning for software raid with mdadm.", - level = PLUGIN, origin = self) - mdadmargs = ["--misc", "--detail", "--scan"] - proc = spawnvch(executable = "mdadm", args = mdadmargs, - chroot = Config.system.root) - (out, err) = proc.communicate() - - if err == '': - # means that the command ran with no errors. - for line in out.__str__().split("\n"): - splitline = line.strip("\n").split(" ") - if "ARRAY" in splitline: - self.scannedFileDict[splitline[1]] = splitline - self.scannedFile = out - else: - # This should make the flow go to clean. If there is an error we - # should not trust what mdadm tells us. - self._reporting.info("The mdadm command had the following " \ - "error:%s. The plugin will silently exit."%err, - level = PLUGIN, origin = self) - self._result = None - return - self._result = ReturnSuccess - self._issue.set(reporting = self._reporting, level = PLUGIN, - origin = self) - - def diagnose(self): - # If nothing was returned by the mdadm command. we dont have software - # raid. - if len(self.scannedFileDict) == 0: - self._reporting.info("There was no sofware raid found by the " \ - "mdadm command.... Nothing to do.", level = PLUGIN, - origin = self) - self._result = ReturnSuccess - self._issue.set(checked=True, happened=False, - reporting=self._reporting, level=PLUGIN, origin=self) - return - - # If nothing is detected the result is successfull. - self._result = ReturnSuccess - - # If there is one difference between the configs, regarding the - # ARRAYS. We replace the config file. Lets check for missing arrays - # in the curren config file. - for key, value in self.scannedFileDict.iteritems(): - if not self.currentFileDict.has_key(key): - self._reporting.info("Found that the current mdadm.conf is " \ - "missing: %s."%value, level = PLUGIN, origin = self) - self._result = ReturnFailure - - # Lets check for additional ARRAYS that should not be there. - for key, value in self.currentFileDict.iteritems(): - if not self.scannedFileDict.has_key(key): - self._reporting.info("The followint entry: %s, is in the " \ - "config file but was not detected by mdadm."%value, - level = PLUGIN, origin = self) - self._result = ReturnFailure - - if self._result == ReturnSuccess: - self._reporting.info("There was no problem found with the " \ - "current mdadm.conf file.", level = PLUGIN, origin = self) - - self._issue.set(checked = True, - happened = (self._result == ReturnFailure), - reporting = self._reporting, level = PLUGIN, origin = self) - - def backup(self): - if os.path.isfile(self.configFile): - self._reporting.info("Making a backup of %s."% - self.configFile, level = PLUGIN, origin = self) - self.backupSpace.backupPath(self.configFile) - - else: - self._reporting.info("It appears that the file %s does not "\ - "exist. No backup attempt will be made."%self.configFile, - level = PLUGIN, origin = self) - - self._result = ReturnSuccess - - def fix(self): - try: - self._reporting.info("Going to write configuration to %s."% - self.configFile, level = PLUGIN, origin = self) - fd = open(self.configFile, "w") - fd.write("# File created by Firstaidkit.\n") - fd.write("# DEVICE partitions\n") - fd.write("# MAILADDR root\n") - fd.write(self.scannedFile) - fd.close() - self._reporting.info("Configuration file writen.", level = PLUGIN, - origin = self) - - # The original mdadm.conf will be restore to - # mdadm.conf.firstaidkit, just in case. - self._reporting.info("Will put the old mdadm.conf in %s."% - os.path.join(Config.system.root, - self.configFile+".firstaidkit"), level=PLUGIN, - origin=self) - self.backupSpace.restoreName(self.configFile, - path = self.configFile+".firstaidkit") - self._result = ReturnSuccess - - except IOError: - fd.close() - self._reporting.info("Error occurred while writing %s."% - self.configFile, level = PLUGIN, origin = self) - self._result = ReturnFailure - self._issue.set(fixed = (self._result == ReturnSuccess), - reporting = self._reporting, level = PLUGIN, origin = self) - - def restore(self): - if not self.backupSpace.exists(self.configFile): - # This is the case where there is no config file. - self._reporting.info("The backedup file was not present. " \ - "Assuming that %s was ont present to begin with."% - self.configFile, level = PLUGIN, original = self) - else: - self._reporting.info("Restoring original file.", level = PLUGIN , - origin = self) - self.backupSpace.restoreName(self.configFile) - self._result = ReturnSuccess - - def clean(self): - self._result = ReturnSuccess - -def get_plugin(): - return MdadmConfig - diff --git a/plugins/p_passwd.py b/plugins/p_passwd.py deleted file mode 100644 index 508ec0b..0000000 --- a/plugins/p_passwd.py +++ /dev/null @@ -1,72 +0,0 @@ -# First Aid Kit - diagnostic and repair tool for Linux -# Copyright (C) 2007 Martin Sivak -# -# 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 import Config -from random import Random - -rng = Random() - -class PasswdPlugin(Plugin): - """This plugin provides operations for convenient manipulation with the - password system.""" - # - # Additional flow defprepareion. - # - # flows = Flow.init(Plugin) # we do not need the default fix and diagnose - # flows - flows = {} - flows["resetRoot"] = Flow({ - Plugin.initial: {Return: "resetRoot"}, - "resetRoot" : {ReturnSuccess: Plugin.final} - }, description="Reset root password to random value so " \ - "the user can login and change it") - - name = "Password plugin" - version = "0.0.1" - author = "Martin Sivak" - - @classmethod - def getDeps(cls): - return set(["root", "experimental", "filesystem"]) - - def __init__(self, *args, **kwargs): - Plugin.__init__(self, *args, **kwargs) - - def resetRoot(self): - charlist = "abcdefghijklmnopqrstuvwxyz" \ - "1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ.," - passlen = 10 - newpasswd = [] - while len(newpasswd) -# -# 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 IssuesPlugin,Flow -from pyfirstaidkit.returns import * -from pyfirstaidkit.utils import * -from pyfirstaidkit.reporting import PLUGIN,TASK -from pyfirstaidkit import Config -import rpm - -from issue_packages import RequiredPackages - -class RPMPlugin(IssuesPlugin): - """This plugin provides checks for RPM database.""" - # - # Additional flow defprepareion. - # - flows = Flow.init(IssuesPlugin) - - name = "RPM plugin" - version = "0.0.1" - author = "Martin Sivak" - - issue_tests = [RequiredPackages] - set_flags = ["rpm_consistent"] - - @classmethod - def getDeps(cls): - return set(["root", "experimental", "filesystem", "rpm_lowlevel"]) - - def __init__(self, *args, **kwargs): - IssuesPlugin.__init__(self, *args, **kwargs) - self.rpm = None - - def prepare(self): - self._reporting.info(self.name+" in Prepare task", origin = self, level = PLUGIN) - self.rpm = rpm.ts(Config.system.root) - IssuesPlugin.prepare(self) - - def backup(self): - self._reporting.info(self.name+" in backup task", origin = self, level = PLUGIN) - self._result=ReturnSuccess - - def restore(self): - self._reporting.info(self.name+" in Restore task", origin = self, level = PLUGIN) - self._result=ReturnSuccess - - def clean(self): - self._reporting.info(self.name+" in Clean task", origin = self, level = PLUGIN) - del self.rpm - self._result=ReturnSuccess - -def get_plugin(): - return RPMPlugin diff --git a/plugins/p_rpm/issue_packages.py b/plugins/p_rpm/issue_packages.py deleted file mode 100644 index 11ed622..0000000 --- a/plugins/p_rpm/issue_packages.py +++ /dev/null @@ -1,67 +0,0 @@ -# File name: issue_filesystem.py -# Date: 2008/03/14 -# Author: Martin Sivak -# -# Copyright (C) Red Hat 2008 -# -# 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 -# in a file called COPYING along with this program; if not, write to -# the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA -# 02139, USA. - -from pyfirstaidkit.issue import Issue -from pyfirstaidkit.reporting import TASK - -class RequiredPackages(Issue): - name = "Required Packages" - description = "There are some very important packages missing. It is likely your instalation could be damaged." - - packages_list = ["filesystem", "initscripts", "glibc", "kernel", "bash", "module-init-tools"] - - def detect(self): - result = Issue.detect(self) - if result is not None: - return result - - architectures = {} - - for p in self.packages_list: - architectures[p] = set() - mi=self._plugin.rpm.dbMatch("name", p) - for hdr in mi: - self._plugin._reporting.debug(level = TASK, origin = self, message = "Found package %s with architecture %s" % (p, hdr["arch"])) - architectures[p].add(hdr["arch"]) - - #is there a common architecture for all the packages? - all = reduce(lambda acc,x: acc.union(x), architectures.values(), set()) - common = reduce(lambda acc,x: acc.intersection(x), architectures.values(), all) - self._plugin._reporting.debug(level = TASK, origin = self, message = "Common architecture for all packages is %s" % ("+".join(common),)) - - if len(common)==0: - self._happened = True - else: - self._happened = False - - self._checked = True - return True - - def fix(self): - result = Issue.fix(self) - if result is not None: - return result - - yum = spawnvch(executable = "/usr/bin/yum", args = ["yum", "install"] + packages_list, chroot = Config.system.root).communicate("y\ny\n") - if yum.returncode==0: - self._fixed = True - - return True diff --git a/plugins/p_rpm_lowlevel/__init__.py b/plugins/p_rpm_lowlevel/__init__.py deleted file mode 100644 index b4daef9..0000000 --- a/plugins/p_rpm_lowlevel/__init__.py +++ /dev/null @@ -1,71 +0,0 @@ -# First Aid Kit - diagnostic and repair tool for Linux -# Copyright (C) 2007 Martin Sivak -# -# 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 IssuesPlugin,Flow -from pyfirstaidkit.returns import * -from pyfirstaidkit.utils import * -from pyfirstaidkit.reporting import PLUGIN,TASK -from pyfirstaidkit import Config - -from issue_packages import Packages -from issue_locks import Locks - -import os.path - -class RPMLowlevelPlugin(IssuesPlugin): - """This plugin provides lowlevel checks for RPM database.""" - # - # Additional flow defprepareion. - # - flows = Flow.init(IssuesPlugin) - - name = "RPM lowlevel structure plugin" - version = "0.0.1" - author = "Martin Sivak" - - issue_tests = [Packages, Locks] - set_flags = ["rpm_lowlevel"] - - @classmethod - def getDeps(cls): - return set(["root", "experimental", "filesystem"]) - - def __init__(self, *args, **kwargs): - IssuesPlugin.__init__(self, *args, **kwargs) - self.rpm = None - - def prepare(self): - self.backup = self._backups.getBackup(self.__class__.__name__+" -- "+self.name) - IssuesPlugin.prepare(self) - - def backup(self): - IssuesPlugin.backup(self) - self.backup.backupPath(path = os.path.join(Config.system.root,"/var/lib/rpm"), name="rpm") - self._result=ReturnSuccess - - def restore(self): - self.backup.restorePath(path = os.path.join(Config.system.root,"/var/lib/rpm"), name="rpm") - IssuesPlugin.restore(self) - self._result=ReturnSuccess - - def clean(self): - self._backups.closeBackup(self.backup._id) - IssuesPlugin.clean(self) - self._result=ReturnSuccess - -def get_plugin(): - return RPMLowlevelPlugin diff --git a/plugins/p_rpm_lowlevel/issue_locks.py b/plugins/p_rpm_lowlevel/issue_locks.py deleted file mode 100644 index d64c3f7..0000000 --- a/plugins/p_rpm_lowlevel/issue_locks.py +++ /dev/null @@ -1,73 +0,0 @@ -# File name: issue_filesystem.py -# Date: 2008/03/14 -# Author: Martin Sivak -# -# Copyright (C) Red Hat 2008 -# -# 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 -# in a file called COPYING along with this program; if not, write to -# the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA -# 02139, USA. - -from pyfirstaidkit.issue import Issue -from pyfirstaidkit.reporting import TASK -from pyfirstaidkit.utils import spawnvch -from pyfirstaidkit.configuration import Config -import os.path -import re - -class Locks(Issue): - name = "Staled RPM locks" - description = "The database is still locked, but it shouldn't be. Probably a result of some unexpectedly aborted rpm operation." - - def __init__(self, *args, **kwargs): - Issue.__init__(self, *args, **kwargs) - - def detect(self): - result = Issue.detect(self) - if result is not None: - return result - - path = os.path.join(Config.system.root,"/var/lib/rpm/") - self.locks = [] - - def walkfunc(arg, dirname, fnames): - for f in fnames: - if f[:4]=="__db" and os.path.isfile(os.path.join(dirname, f)): - arg.append(os.path.join(dirname, f)) - - os.path.walk(path, walkfunc, self.locks) - - if len(self.locks)==0: - self._happened = False - else: - self._happened = True - - self._checked = True - return True - - def fix(self): - result = Issue.fix(self) - if result is not None: - return result - - for f in self.locks: - os.unlink(f) - - self._fixed = True - return True - - def reset(self): - Issue.reset(self) - self.locks = [] - diff --git a/plugins/p_rpm_lowlevel/issue_packages.py b/plugins/p_rpm_lowlevel/issue_packages.py deleted file mode 100644 index 34bac91..0000000 --- a/plugins/p_rpm_lowlevel/issue_packages.py +++ /dev/null @@ -1,81 +0,0 @@ -# File name: issue_filesystem.py -# Date: 2008/03/14 -# Author: Martin Sivak -# -# Copyright (C) Red Hat 2008 -# -# 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 -# in a file called COPYING along with this program; if not, write to -# the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA -# 02139, USA. - -from pyfirstaidkit.issue import Issue -from pyfirstaidkit.reporting import TASK -from pyfirstaidkit.utils import spawnvch -from pyfirstaidkit.configuration import Config -import os, os.path - - -class Packages(Issue): - name = "Required Packages database" - description = "The file containing the rpm database of packages is missing or corrupted" - - def detect(self): - result = Issue.detect(self) - if result is not None: - return result - - dbname = os.path.join(Config.system.root, "/var/lib/rpm/Packages") - self._happened = False - - if not os.path.isfile(os.path.realpath(dbname)): - self._db_missing = True - self._happened = True - self._checked = True - return True - - #verify the Package database - rpm_verify = spawnvch(executable = "/usr/lib/rpm/rpmdb_verify", args = ["/usr/lib/rpm/rpmdb_verify", dbname], chroot = Config.system.root) - err = rpm_verify.wait() - if err!=0: - return False - - if len(rpm_verify.stdout.read())>0: - self._happened = True - if len(rpm_verify.stderr.read())>0: - self._happened = True - - self._checked = True - return True - - def fix(self): - result = Issue.fix(self) - if result is not None: - return result - - dbname = os.path.join(Config.system.root,"/var/lib/rpm/Packages") - - if not self._db_missing: - #dump&load the database - os.rename(dbname, dbname+".orig") - err = spawnvch(executable = "/bin/sh", args = ["sh", "-c", "/usr/lib/rpm/rpmdb_dump /var/lib/rpm/Packages.orig | /usr/lib/rpm/rpmdb_load /var/lib/rpm/Packages"], chroot = Config.system.root).wait() - if rpm.returncode!=0: - os.rename(dbname+".orig", dbname) - return False - - #rebuild the indexes - rpm = spawnvch(executable = "/bin/rpm", args = ["rpm", "--rebuilddb"], chroot = Config.system.root).wait() - if rpm.returncode==0: - self._fixed = True - return True - diff --git a/plugins/p_undelparts/Makefile b/plugins/p_undelparts/Makefile deleted file mode 100644 index a9ef6cc..0000000 --- a/plugins/p_undelparts/Makefile +++ /dev/null @@ -1,38 +0,0 @@ -# Makefile -# -# First Aid Kit - diagnostic and repair tool for Linux -# Copyright (C) 2008 Joel Andres Granados -# -# 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. -# -# - -PYVER := $(shell python -c 'import sys; print sys.version[0:3]') -PYTHON = python$(PYVER) -PYTHONINCLUDE = /usr/include/$(PYTHON) - - -CFLAGS = -I$(PYTHONINCLUDE) - -PYMODULE = _undelpart.so - -#Build with libparted (parted-1.8.3 and higher have a pkg-config file) -LDFLAGS = $(shell pkg-config --libs libparted) - -build: - $(CC) $(CFLAGS) -shared -fPIC -o _undelpart.so _undelpart.c $(LDFLAGS) - -clean: - rm -f _undelpart.so *.pyc diff --git a/plugins/p_undelparts/__init__.py b/plugins/p_undelparts/__init__.py deleted file mode 100644 index b3402ea..0000000 --- a/plugins/p_undelparts/__init__.py +++ /dev/null @@ -1,20 +0,0 @@ -# First Aid Kit - diagnostic and repair tool for Linux -# Copyright (C) 2008 Joel Andres Granados -# -# 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 undeletePartition -def get_plugin(): - return undeletePartition.UndeletePartition diff --git a/plugins/p_undelparts/_undelpart.c b/plugins/p_undelparts/_undelpart.c deleted file mode 100644 index aab4f07..0000000 --- a/plugins/p_undelparts/_undelpart.c +++ /dev/null @@ -1,578 +0,0 @@ -/* -* First Aid Kit - diagnostic and repair tool for Linux -* Copyright (C) 2008 Joel Andres Granados -* -* 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. -* -*/ - - -#include -#include -#include -#include "parted/parted.h" - -/* - * Simple representation of the partition. - */ -typedef struct{ - int partnum; - PedSector partstart; - PedSector partend; -} partElem; - - -/* - * Helper function - */ - -/* - * Returns a disk with the path of the device. - */ -static PedDisk * -_getDiskFromPath(char * path){ - PedDevice * dev; - PedDisk * disk; - /* Try to create the device with the path */ - dev = ped_device_get(path); - if(dev == NULL) - return NULL; - - /* Read the partition table off of the device */ - disk = ped_disk_new(dev); - if(disk == NULL) - return NULL; - - return disk; -} - -/* - * Return the partition type. - */ -static PedPartitionType -_disk_get_part_type_for_sector (PedDisk* disk, PedSector sector) -{ - PedPartition* extended; - - extended = ped_disk_extended_partition (disk); - if (!extended - || !ped_geometry_test_sector_inside (&extended->geom, sector)) - return 0; - - return PED_PARTITION_LOGICAL; -} - -/* - * Create a python list from one partElement struct - */ -static PyObject * -_getPPartList( int partnum, int partstart, int partend ){ - PyObject * num, * start, * end; - PyObject * list; - /* - * Create a new temporary list. and add all the related values - */ - num = PyString_FromFormat("%d",partnum); - start = PyLong_FromLong(partstart); - end = PyLong_FromLong(partend); - list = PyList_New(3); - if(num == NULL || start == NULL || end == NULL || - list == NULL || - PyList_SetItem(list, 0, num) == -1 || - PyList_SetItem(list, 1, start) == -1 || - PyList_SetItem(list, 2, end) == -1){ - goto handle_error; - } - return list; - - handle_error: - - Py_XDECREF(num); - Py_XDECREF(start); - Py_XDECREF(end); - Py_XDECREF(list); - return NULL; -} - -/* - * Create a array of partElem with the python list - */ -static partElem -_getCPartList( PyObject * list ){ - partElem _list = {0}; - - // check that its a list. - if(!PyList_Check(list)){ - PyErr_SetString(PyExc_StandardError, - "Error malformed argument, list does not contian lisit."); - return _list; - } - - // check that it has three elements. - if(PyList_Size(list) < 3){ - PyErr_SetString(PyExc_StandardError, - "Error Malformed argument, contained list is to small."); - return _list; - } - - // Populate the _partList array. - _list.partnum = PyLong_AsLong( PyNumber_Long(PyList_GetItem(list, 0)) ); - _list.partstart = PyLong_AsLong( PyList_GetItem(list, 1) ); - _list.partend = PyLong_AsLong( PyList_GetItem(list, 2) ); - if( PyErr_Occurred()) - _list.partnum = '\0'; - return _list; -} - -static int -MEGABYTE_SECTORS (PedDevice* dev) -{ - return PED_MEGABYTE_SIZE / dev->sector_size; -} - -/* - * Tries to recover the partition part in the disk disk. Reutrns null if it - * was not possible, or the partition if it was. - */ -static PedPartition * -add_partition(PedDisk * disk, partElem part){ - - //PedDisk * clone; - PedSector s; - PedGeometry * probed; - PedGeometry sect_geom; - PedGeometry entire_dev; - PedPartition * parttemp = NULL; - PedConstraint disk_constraint, * part_constraint; - PedPartitionType part_type; - PedFileSystemType * fs_type; - - /* Initialize the entire_dev geom for the constraint calculation */ - ped_geometry_init(&entire_dev, disk->dev, 0, disk->dev->length); - part_type = _disk_get_part_type_for_sector (disk, (part.partstart + part.partend) / 2); - - - /* The end is a temporary hack until we get the method of search done */ - for (s = part.partstart; s < part.partstart+(part.partend-part.partstart)/10; s++) { - - /* Get a part from the specific s sector with the device constraint */ - ped_geometry_init (§_geom, disk->dev, s, 1); - ped_constraint_init (&disk_constraint, ped_alignment_any, ped_alignment_any, - §_geom, &entire_dev, 1, disk->dev->length); - - parttemp = ped_partition_new (disk, part_type, NULL, s, part.partend); - if(!parttemp){ - ped_disk_remove_partition(disk, parttemp); - ped_constraint_done(&disk_constraint); - parttemp = NULL; - continue; - } - - - /* add the partition to the disk */ - ped_exception_fetch_all(); //dont show errors - if(!ped_disk_add_partition(disk, parttemp, &disk_constraint)){ - ped_disk_remove_partition(disk, parttemp); - ped_constraint_done(&disk_constraint); - parttemp = NULL; - continue; - } - ped_exception_leave_all();// show errors. - - /* try to detect filesystem in the partition region */ - fs_type = ped_file_system_probe(&parttemp->geom); - if(!fs_type){ - ped_disk_remove_partition(disk, parttemp); - ped_constraint_done(&disk_constraint); - parttemp = NULL; - continue; - } - - /* try to find the exact region the filesystem ocupies */ - probed = ped_file_system_probe_specific(fs_type, &parttemp->geom); - if(!probed){ - ped_disk_remove_partition(disk, parttemp); - ped_constraint_done(&disk_constraint); - ped_geometry_destroy(probed); - parttemp = NULL; - continue; - } - - /* see if probed is inside the partition region */ - if(!ped_geometry_test_inside(&parttemp->geom, probed)) { - ped_disk_remove_partition(disk, parttemp); - ped_constraint_done(&disk_constraint); - ped_geometry_destroy(probed); - parttemp = NULL; - continue; - } - - /* create a constraint for the probed region */ - part_constraint = ped_constraint_exact (probed); - - /* set the region for the partition */ - if (!ped_disk_set_partition_geom (parttemp->disk, parttemp, part_constraint, - probed->start, probed->end)) { - ped_disk_remove_partition(disk, parttemp); - ped_constraint_done(part_constraint); - ped_constraint_done(&disk_constraint); - ped_geometry_destroy(probed); - parttemp = NULL; - continue; - } - ped_partition_set_system(parttemp, fs_type); - ped_disk_commit(disk); - ped_disk_commit_to_dev(disk); - ped_disk_commit_to_os(disk); - break; - } - return parttemp; -} - -/* Pythong facing functions. - * - * Returns a dictionary of the form { DISK : [None, None, None, None] ...} - */ -static PyObject * -undelpart_getDiskList(PyObject * self, PyObject * args){ - - PedDevice * dev; - - PyObject * dict; - PyObject * list; - PyObject * diskName; - - int i; - - dict = PyDict_New(); - if(dict == NULL){ - PyErr_SetString(PyExc_StandardError, "Error creating a new dictionary."); - goto handle_error; - } - - /* Search for the disks on the system */ - ped_device_probe_all(); - - for(dev=ped_device_get_next(dev); dev ; dev=ped_device_get_next(dev)){ - /* - * Build the list for this particular disk and fill it with Python - * None Objects. - */ - list = PyList_New(4); - if(list == NULL){ - PyErr_SetString(PyExc_StandardError, "Error creating a new list."); - goto handle_error; - } - for(i=0 ; i < 4 ; i++){ //We set all items to None. - if(PyList_SetItem(list, i, Py_None) == -1){ - PyErr_SetString(PyExc_StandardError, - "Error setting up the None valued list."); - goto handle_error; - } - } - - /* - * Take the name out of the PedDevice structure and place it as a - * dictionary key. Use the device path. - */ - diskName = Py_BuildValue("s", dev->path); - if(diskName == NULL){ - PyErr_SetString(PyExc_StandardError, - "Error creating key for dictionary."); - goto handle_error; - } - - if(PyDict_SetItem(dict, diskName, list) == -1){ - PyErr_SetString(PyExc_StandardError, - "Error while creating the dictionary entry"); - goto handle_error; - } - } - - /* If the dictionary's length is 0, something is wrong. */ - if(PyDict_Size(dict) == 0){ - PyErr_SetString(PyExc_StandardError, - "libparted was unable to get a disk list. Are you root?"); - goto handle_error; - } - - ped_device_free_all(); - return dict; - - handle_error: - assert(PyErr_Occurred()); - - Py_XDECREF(diskName); - Py_XDECREF(list); - Py_XDECREF(dict); - ped_device_free_all(); - - return NULL; -} - -/* - * Returns a list of partitions that are present in the disk but not in its - * partition table. If the disk does not exist it errors. If the disk - * has no rescueable partitions it returns a void list. It is a list of - * possible partitions, it will NOT check for rescuability. - */ -static PyObject * -undelpart_getRescuable(PyObject * self, PyObject * args){ - - PedDisk * disk; - PedPartition * part; - - PyObject * tempList; - PyObject * partitions; - - char * path; - - if(!PyArg_ParseTuple(args, "s", &path)){ - PyErr_SetString(PyExc_TypeError, "Argument is not a String"); - goto handle_error; - } - - /* Build the empty list*/ - partitions = PyList_New(0); - if(partitions == NULL){ - PyErr_SetString(PyExc_StandardError, "Error creating a new list."); - goto handle_error_destroy_disk; - } - - /* create the disk */ - disk = _getDiskFromPath(path); - if(disk == NULL){ - PyErr_SetString(PyExc_StandardError, "Error reading disk information."); - goto handle_error_destroy_disk; - } - - /* - * We start looking for the partitions. The partitions will be detected if - * the numpart is less than 1. The basic idea is to traverse all the partitions - * and look for holes in between. - */ - for(part = ped_disk_next_partition(disk, NULL); part ; - part = ped_disk_next_partition(disk, part) ){ - // All partitions with partnum less than 1 is a possibility. - if(part->num < 1 && part->geom.start < part->geom.end){ - /* create the python object */ - tempList = _getPPartList(part->num, part->geom.start, part->geom.end); - /* Append the list to the return value */ - if(tempList == NULL || PyList_Append(partitions, tempList) == -1){ - PyErr_SetString(PyExc_StandardError, - "Error creating the partition information."); - goto handle_error_destroy_disk; - } - } - } - ped_disk_destroy(disk); - ped_device_free_all(); - - return partitions; - - handle_error_destroy_disk: - ped_disk_destroy(disk); - - handle_error: - assert(PyErr_Occurred()); - - Py_XDECREF(partitions); - Py_XDECREF(tempList); - - ped_device_free_all(); - return NULL; -} - -/* - * Returns a list of valid partitions at time of scan. - */ -static PyObject * -undelpart_getPartitionList(PyObject * self, PyObject * args){ - - PedDisk * disk; - PedDevice * dev; - PedPartition * part; //libparted object - - PyObject * partList; //python list of partitions - PyObject * tempList; //python temporary object to hold the temprorary list. - - char * path; - - if(!PyArg_ParseTuple(args, "s", &path)){ - PyErr_SetString(PyExc_TypeError, "Argument is not a String"); - goto handle_error; - } - - /* create the disk an dev */ - disk = _getDiskFromPath(path); - if(disk == NULL){ - PyErr_SetString(PyExc_StandardError, "Error reading disk information."); - goto handle_error; - } - dev = disk->dev; - - /* Create the python list that we are to fill */ - partList = PyList_New(0); - if(partList == NULL){ - PyErr_SetString(PyExc_StandardError, "Error creating a new list."); - goto handle_error; - } - - /* Get all the active partitions from disk */ - for(part = ped_disk_next_partition(disk, NULL) ; - part ; part = ped_disk_next_partition(disk, part)){ - if(part->num < 0) - continue; - - tempList = _getPPartList(part->num, - part->geom.start, - part->geom.end); - /* Append the list to the return value */ - if(tempList == NULL || PyList_Append(partList, tempList) == -1){ - PyErr_SetString(PyExc_StandardError, - "Error appending the partition to the list."); - goto handle_error; - } - } - ped_device_free_all(); - - return partList; - - handle_error: - assert(PyErr_Occurred()); - - Py_XDECREF(partList); - Py_XDECREF(tempList); - - ped_device_free_all(); - - return NULL; -} - -/* - * On a specific disk try to rescue a list of partitions. Return the list of partitions - * that was recovered. The partitions should be in the [[partNum, start, end]...] - * format. - */ -static PyObject * -undelpart_rescue(PyObject * self, PyObject * args){ - - PedDisk * disk; - PedPartition * part; - - PyObject * partList; - PyObject * rescuedParts; - PyObject * tempList; - - partElem * _partList = NULL; - char * path; - int partListSize = 0; - int i; - - /* Check the arguments */ - if(!PyArg_ParseTuple(args, "sO", &path, &partList)){ - PyErr_SetString(PyExc_TypeError, "Arguments are not valid (String, List)"); - goto handle_error; - } - if(! PyList_Check(partList)){ - PyErr_SetString(PyExc_TypeError, - "The object that was passed is not a list."); - goto handle_error; - } - - /* Build the empty list, this is the return value. */ - rescuedParts = PyList_New(0); - if(rescuedParts == NULL){ - PyErr_SetString(PyExc_StandardError, "Error creating a new list."); - goto handle_error; - } - - /* Put the values of the list into a array of partElem */ - partListSize = PyList_Size(partList); - _partList = malloc(sizeof(partElem)*partListSize+1); - if(!_partList){ - PyErr_SetString(PyExc_StandardError, "Error allocating memory."); - goto handle_error; - } - for(i=0; i < partListSize ; i++){ - _partList[i] = _getCPartList(PyList_GetItem(partList, i)); - if( PyErr_Occurred() || _partList[i].partnum == '\0'){ - goto handle_error; - } - } - _partList[partListSize].partnum = '\0'; - - /* create the disk an dev */ - disk = _getDiskFromPath(path); - if(disk == NULL){ - PyErr_SetString(PyExc_StandardError, "Error reading disk information."); - goto handle_error; - } - - /* Try to add each partition. */ - for(i=0 ; _partList[i].partnum ; i++){ - part = add_partition(disk, _partList[i]); - if(!part){ - // could not rescue this partition. sorry - continue; - }else{ - tempList = _getPPartList(part->num, part->geom.start, part->geom.end); - /* Append the list to the return value */ - if(tempList == NULL || PyList_Append(rescuedParts, tempList) == -1){ - PyErr_SetString(PyExc_StandardError, - "Error creating the partition information."); - goto handle_error; - } - } - } - ped_disk_destroy(disk); - free(_partList); - ped_device_free_all(); - - return rescuedParts; - - handle_error: - assert(PyErr_Occurred()); - - free(_partList); - ped_device_free_all(); - - return NULL; -} - -static struct PyMethodDef undelpart_methods [] = { - { "getDiskList", - (PyCFunction)undelpart_getDiskList, - METH_VARARGS, "Generaly returns the system disk list. Receives nothing." }, - { "getRescuable", - (PyCFunction)undelpart_getRescuable, - METH_VARARGS, "Get a list of partitions from a specific disk that might " - "be rescuable. It returns the partitions that are not in the partition " - "table but where present after a disk scan. It expects the disk name."}, - { "getPartitionList", - (PyCFunction)undelpart_getPartitionList, - METH_VARARGS, "Get the partition list off of a certain disk. This is intended " - "to be used as a backup. It returns the number of the partition, start " - "sector and the end sector."}, - {"rescue", - (PyCFunction)undelpart_rescue, - METH_VARARGS, "Try to put the list of rescuable partitions into the partition " - "table. If the partitions are already there, nothing will be done. A list " - "of rescued partitions is returned. This does NOT delete any partitions."} -}; - -void init_undelpart(void){ - (void) Py_InitModule("_undelpart", undelpart_methods); -} diff --git a/plugins/p_undelparts/undeletePartition.py b/plugins/p_undelparts/undeletePartition.py deleted file mode 100644 index 47566d2..0000000 --- a/plugins/p_undelparts/undeletePartition.py +++ /dev/null @@ -1,171 +0,0 @@ -# First Aid Kit - diagnostic and repair tool for Linux -# Copyright (C) 2008 Joel Andres Granados -# -# 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.reporting import PLUGIN -import signal -import _undelpart - -class UndeletePartition(Plugin): - """Plugin to detect and correct deleted partitions from system disks. - - Uses parted libriary to search for partitions that are not included in - the partition table of a disk. If it is possible, this plugin will put - the partition back into the parition table so it is visible to the - system again. - """ - - flows = Flow.init(Plugin) - # We have not restore in the noBackup flow because we have no information to restore with. - flows["noBackup"] = Flow({ - Plugin.initial: {Return: "prepare"}, - "prepare" : {ReturnSuccess: "diagnose"}, - "diagnose" : {ReturnSuccess: "clean", ReturnFailure: "fix"}, - "fix" : {ReturnSuccess: "clean", ReturnFailure: "clean"}, - "clean" : {ReturnSuccess: Plugin.final} - }, description="This flow skips the backup test. Use with care.") - - name = "Undelete Partitions" - version = "0.1.0" - author = "Joel Andres Granados" - def __init__(self, *args, **kwargs): - Plugin.__init__(self, *args, **kwargs) - - # Dictionary that will hold the partitions that are not included in the - # partition table of a certain disk and can be recovered. It will also - # house the initial partition table and the partition table that is a - # result of running the fix. The structure is: - # slef.disks={diskname: [ [recoverables], initialPT, finalPT ], .... } - self.disks = {} - - def prepare(self): - # For now there is no real action in the prepare task. - self._result=ReturnSuccess - - # - # The diagnose will not be a real diagnose but more of an informative task. - # It will report all the possible paritions that could house a rescuable - # partition. - # - def diagnose(self): - self._reporting.info("Beginning Diagnose...", origin = self, level = PLUGIN) - self.disks = _undelpart.getDiskList() - self._reporting.info("Disks present in the system %s"%self.disks.keys(), - origin = self, level = PLUGIN) - # When we find a rescuable partition we change this to true. - rescuablePresent = False - for disk, elements in self.disks.iteritems(): - self.disks[disk] = [ _undelpart.getRescuable(disk), _undelpart.getPartitionList(disk), [] ] - if len(self.disks[disk][0]) > 0: - self._reporting.info("Possible partitions to recover in disk %s: %s"%(disk, self.disks[disk][0]), - origin = self, level = PLUGIN) - rescuablePresent = True - if not rescuablePresent: - self._result = ReturnSuccess - self._reporting.info("Did not find any partitions that need rescueing.", - origin = self, level = PLUGIN) - else: - self._result = ReturnFailure - - def backup(self): - self._reporting.info("Backing up partition table." , origin = self, level = PLUGIN) - # We actually already have the backup of the partition table in the self.disks dict. - # Lets check anyway. - backupSane = True - for disk, members in self.disks.iteritems(): - if members[1] == None or len(members[1]) <= 0: - # We don't really have the partition table backup. - self._reporting.info("Couldn't backup the partition table for %s."%disk, - origin = self, level = PLUGIN) - self._reporting.info("To force the recovery of this disk without the backup " \ - "please run the flow named noBackup from this plugin.", - origin = self, level = PLUGIN) - backupSane = False - self._result = ReturnFailure - - if backupSane: - self._result = ReturnSuccess - - # - # Every partition that we suspect is rescuable, (given that it has a partition table from - # wich we can recover if we mess up) we try to rescue. This will take a long time. - # - def fix(self): - self._reporting.info("Lets see if I can fix this... Starting fix task.", - origin = self, level = PLUGIN) - self._reporting.info("Might want to go and get a cup of coffee," - "this could take a looooooong time...", origin = self, level = PLUGIN) - self._result = ReturnSuccess - rescued = [] - try: - for disk, members in self.disks.iteritems(): - if len(members[0]) > 0:#there are partitions to rescue :) - self._reporting.info("Trying to rescue %s from disk %s"%(members[0], disk), - origin = self, level = PLUGIN) - rescued = _undelpart.rescue(disk,members[0]) - self._reporting.info("Partitions rescued: %s"%rescued, - origin = self, level = PLUGIN) - elif len(members[0]) == 0: - self._reporting.info("Nothing to rescue on disk %s."%disk, - origin = self, level = PLUGIN) - else: - self_result = ReturnFailure - break - except KeyboardInterrupt, e: - self._reporting.error("Received a user interruption... Moving to Restore task.", - origin = self, level = PLUGIN, action = None) - # The user might want to keep on pushing ctrl-c, lets lock the SIGINT signal. - signal.signal(signal.SIGINT, keyboaordInterruptHandler) - self._reporting.info("Please wait until the original partition table is recovered.", - origin = self, level = PLUGIN) - self._result = ReturnFailure - - # - # We are not really erasing anything, so recovering is kinda out of the point. That said - # anything can happen with partitioning. :) Lets get the current partitionList and try - # to add all the partitions that are not in the current part list but are in the backedup - # one. - # - def restore(self): - self._reporting.info("Starting Restoring task." , origin = self, level = PLUGIN) - tempPartList = [] - backupPartList = [] - for disk, members in self.disk.iteritems(): - tempPartList = _undelpart.getPartitionList(disk) - backupPartList = members[1] - for part in backupPartList: - if part not in tempPartList:# we need to restore - self._reporting.info("Trying to restore partition %s on disk %s"%(part, disk), - origin = self, level = PLUGIN) - restore = _undelpart.rescue(disk, [part]) - if len(restore) > 0: - self._reporting.info("Restored partition %s on disk %s"%(part, disk), - origin = self, level = PLUGIN) - else: - self._reporting.error("Could not restore partititon %s on disk %s"%(part, disk), - origin = self, level = PLUGIN, action = None) - # Return the signal to its previous state. - signal.signal(signal.SIGINT, signal.SIG_DFL) - self._result = ReturnSuccess - - def clean(self): - self._reporting.info("Cleanning...",origin = self, level = PLUGIN) - self._result = ReturnSuccess - -def keyboardInterruptHandler(signum, frame): - pass diff --git a/plugins/p_xserver.py b/plugins/p_xserver.py deleted file mode 100644 index 71c7d2c..0000000 --- a/plugins/p_xserver.py +++ /dev/null @@ -1,208 +0,0 @@ -# First Aid Kit - diagnostic and repair tool for Linux -# Copyright (C) 2008 Joel Andres Granados -# -# 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 * - -import rhpxl.xserver -import rhpl.keyboard -import tempfile -import subprocess -import time -import signal -import os -import os.path -import shutil - -class Xserver(Plugin): - """ Plugin to detect an rescue faulty xserver configurations. """ - flows = Flow.init(Plugin) - flows["force"] = Flow({ - Plugin.initial: {Return: "prepare"}, - "prepare" : {ReturnSuccess: "diagnose2"}, - "diagnose2" : {ReturnSuccess: "clean", ReturnFailure: "backup"}, - "backup" : {ReturnSuccess: "fix", ReturnFailure: "clean"}, - "restore" : {ReturnSuccess: "clean", ReturnFailure: "clean"}, - "fix" : {ReturnSuccess: "clean", ReturnFailure: "restore"}, - "clean" : {ReturnSuccess: Plugin.final} - }, description="This flow skips the search for the xserver lock file") - name = "X server" - 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) - # Arbitrary test display - self.display = ":10" - self.confPath = "/etc/X11/xorg.conf" - self.backupSpace = self._backups.getBackup(str(self)) - self._issue = SimpleIssue(self.name, "X server didn't start") - - def prepare(self): - # Nothing to prepare really. - self._result = ReturnSuccess - self._issue.set(reporting = self._reporting, level = PLUGIN, - origin = self) - - def diagnose(self): - # Lets see if there is a server active. - if os.path.exists("/tmp/.X0-lock"): - self._reporting.info("An X server is already running.", - level = PLUGIN, origin = self) - self._reporting.info("You can run the \"force\" flow to " - "avoud this check. In some cases it works.", - level = PLUGIN, origin = self) - self._result = ReturnSuccess - - elif self.serverStart(): - self._reporting.info("Everything seems ok with the X server.", - level = PLUGIN, origin = self) - self._result = ReturnSuccess - - elif not os.path.exists(self.confPath): - # If the configuration is not there dont even bother to try - #fixing it. This will go through the proces of trying to fix - #it. at least we told the user. - self._reporting.info("The error is in the xservers autodetection " - "mechanism, this does not have an automated solution yet.", - level = PLUGIN, origin = self) - self._result = ReturnFailure - - else: - self._reporting.info("X server is missconfigured.", level = PLUGIN, - origin = self) - self._result = ReturnFailure - self._issue.set(checked = True, - happened = (self._result == ReturnFailure), - reporting = self._reporting, level = PLUGIN, origin = self) - - def diagnose2(self): - """Just a diagnose without the lock check""" - if self.serverStart(): - self._reporting.info("Everything seems ok with the X server.", - level = PLUGIN, origin = self) - self._result = ReturnSuccess - - elif not os.path.exists(self.confPath): - # If the configuration is not there dont even bother to try fixing it. - # This will go through the proces of trying to fix it. at least we - #told the user. - self._reporting.info("The error is in the xservers autodetection " - "mechanism, this does not have an automated solution yet.", - level = PLUGIN, origin = self) - self._result = ReturnFailure - - else: - self._reporting.info("X server is missconfigured.", level = PLUGIN, - origin = self) - self._result = ReturnFailure - self._issue.set(checked = True, - happened = (self._result == ReturnFailure), - reporting = self._reporting, level = PLUGIN, origin = self) - - - def backup(self): - if os.path.isfile(self.confPath): - self.backupSpace.backupPath(self.confPath) - else: - self._reporting.info("%s does not exist." % self.confPath, - level = PLUGIN, origin = self) - self._result = ReturnSuccess - - def fix(self): - self._reporting.info("Starting the fix task.", level = PLUGIN, - origin = self) - # With the current xorg server the only thing that we need to do is to - # erase the conf file. - if os.path.exists(self.confPath): - os.remove(self.confPath) - - self._reporting.info("Testing modified environment", level = PLUGIN, - origin = self) - if self.serverStart(): - self._reporting.info("X server started successfully with no " - "config file.", level = PLUGIN, origin = self) - self._reporting.info("If you must have a config file, create " - "one with system-config-display and place it at " - "/etc/X11/xorg.conf", level = PLUGIN, origin = self ) - self._result = ReturnSuccess - - # Lets give the user his previous file. - if self.backupSpace.exists(path=self.confPath): - self.backupSpace.restoreName(self.confPath, - "%s-FAKbackup"%self.confPath) - - else: - self._reporting.info("X server is does not autodetect the users " - "environment.", level = PLUGIN, origin = self) - self._result = ReturnFailure - - self._issue.set(fixed = (self._result == ReturnSuccess), - reporting = self._reporting, level = PLUGIN, origin = self) - - def restore(self): - if not self.backupSpace.exists(path=self.confPath): - # This is the case where there is no config file. - self._reporting.info("The backedup file was not present. Assuming " - "that xorg did not have a config file to begin with.", - level = PLUGIN, origin = self) - else: - self._reporting.info("Restoring original file.", level = PLUGIN , - origin = self) - self.backupSpace.restoreName(self.confPath) - - self._result = ReturnSuccess - - def clean(self): - self._result = ReturnSuccess - - - def serverStart(self): - self._reporting.info("Trying to start X server", level = PLUGIN, - origin = self) - xorgargs = [self.display] - try: - proc = spawnvch(executable = "/usr/bin/Xorg", args = xorgargs, - chroot = Config.system.root) - self._reporting.info("Waiting for the X server to start...", - level = PLUGIN, origin = self) - time.sleep(5) - if proc.poll() is not None: - # process has terminated, failed. - raise OSError - except: - self._reporting.info("The X server has failed to start", - level = PLUGIN, origin = self) - return False - self._reporting.info("The X server has started successfully", - level = PLUGIN, origin = self) - os.kill(proc.pid, signal.SIGINT) - return True - - -def get_plugin(): - return Xserver - diff --git a/plugins/passwd.py b/plugins/passwd.py new file mode 100644 index 0000000..508ec0b --- /dev/null +++ b/plugins/passwd.py @@ -0,0 +1,72 @@ +# First Aid Kit - diagnostic and repair tool for Linux +# Copyright (C) 2007 Martin Sivak +# +# 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 import Config +from random import Random + +rng = Random() + +class PasswdPlugin(Plugin): + """This plugin provides operations for convenient manipulation with the + password system.""" + # + # Additional flow defprepareion. + # + # flows = Flow.init(Plugin) # we do not need the default fix and diagnose + # flows + flows = {} + flows["resetRoot"] = Flow({ + Plugin.initial: {Return: "resetRoot"}, + "resetRoot" : {ReturnSuccess: Plugin.final} + }, description="Reset root password to random value so " \ + "the user can login and change it") + + name = "Password plugin" + version = "0.0.1" + author = "Martin Sivak" + + @classmethod + def getDeps(cls): + return set(["root", "experimental", "filesystem"]) + + def __init__(self, *args, **kwargs): + Plugin.__init__(self, *args, **kwargs) + + def resetRoot(self): + charlist = "abcdefghijklmnopqrstuvwxyz" \ + "1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ.," + passlen = 10 + newpasswd = [] + while len(newpasswd) +# +# 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 IssuesPlugin,Flow +from pyfirstaidkit.returns import * +from pyfirstaidkit.utils import * +from pyfirstaidkit.reporting import PLUGIN,TASK +from pyfirstaidkit import Config +import rpm + +from issue_packages import RequiredPackages + +class RPMPlugin(IssuesPlugin): + """This plugin provides checks for RPM database.""" + # + # Additional flow defprepareion. + # + flows = Flow.init(IssuesPlugin) + + name = "RPM plugin" + version = "0.0.1" + author = "Martin Sivak" + + issue_tests = [RequiredPackages] + set_flags = ["rpm_consistent"] + + @classmethod + def getDeps(cls): + return set(["root", "experimental", "filesystem", "rpm_lowlevel"]) + + def __init__(self, *args, **kwargs): + IssuesPlugin.__init__(self, *args, **kwargs) + self.rpm = None + + def prepare(self): + self._reporting.info(self.name+" in Prepare task", origin = self, level = PLUGIN) + self.rpm = rpm.ts(Config.system.root) + IssuesPlugin.prepare(self) + + def backup(self): + self._reporting.info(self.name+" in backup task", origin = self, level = PLUGIN) + self._result=ReturnSuccess + + def restore(self): + self._reporting.info(self.name+" in Restore task", origin = self, level = PLUGIN) + self._result=ReturnSuccess + + def clean(self): + self._reporting.info(self.name+" in Clean task", origin = self, level = PLUGIN) + del self.rpm + self._result=ReturnSuccess + +def get_plugin(): + return RPMPlugin diff --git a/plugins/rpm/issue_packages.py b/plugins/rpm/issue_packages.py new file mode 100644 index 0000000..11ed622 --- /dev/null +++ b/plugins/rpm/issue_packages.py @@ -0,0 +1,67 @@ +# File name: issue_filesystem.py +# Date: 2008/03/14 +# Author: Martin Sivak +# +# Copyright (C) Red Hat 2008 +# +# 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 +# in a file called COPYING along with this program; if not, write to +# the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA +# 02139, USA. + +from pyfirstaidkit.issue import Issue +from pyfirstaidkit.reporting import TASK + +class RequiredPackages(Issue): + name = "Required Packages" + description = "There are some very important packages missing. It is likely your instalation could be damaged." + + packages_list = ["filesystem", "initscripts", "glibc", "kernel", "bash", "module-init-tools"] + + def detect(self): + result = Issue.detect(self) + if result is not None: + return result + + architectures = {} + + for p in self.packages_list: + architectures[p] = set() + mi=self._plugin.rpm.dbMatch("name", p) + for hdr in mi: + self._plugin._reporting.debug(level = TASK, origin = self, message = "Found package %s with architecture %s" % (p, hdr["arch"])) + architectures[p].add(hdr["arch"]) + + #is there a common architecture for all the packages? + all = reduce(lambda acc,x: acc.union(x), architectures.values(), set()) + common = reduce(lambda acc,x: acc.intersection(x), architectures.values(), all) + self._plugin._reporting.debug(level = TASK, origin = self, message = "Common architecture for all packages is %s" % ("+".join(common),)) + + if len(common)==0: + self._happened = True + else: + self._happened = False + + self._checked = True + return True + + def fix(self): + result = Issue.fix(self) + if result is not None: + return result + + yum = spawnvch(executable = "/usr/bin/yum", args = ["yum", "install"] + packages_list, chroot = Config.system.root).communicate("y\ny\n") + if yum.returncode==0: + self._fixed = True + + return True diff --git a/plugins/rpm_lowlevel/__init__.py b/plugins/rpm_lowlevel/__init__.py new file mode 100644 index 0000000..b4daef9 --- /dev/null +++ b/plugins/rpm_lowlevel/__init__.py @@ -0,0 +1,71 @@ +# First Aid Kit - diagnostic and repair tool for Linux +# Copyright (C) 2007 Martin Sivak +# +# 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 IssuesPlugin,Flow +from pyfirstaidkit.returns import * +from pyfirstaidkit.utils import * +from pyfirstaidkit.reporting import PLUGIN,TASK +from pyfirstaidkit import Config + +from issue_packages import Packages +from issue_locks import Locks + +import os.path + +class RPMLowlevelPlugin(IssuesPlugin): + """This plugin provides lowlevel checks for RPM database.""" + # + # Additional flow defprepareion. + # + flows = Flow.init(IssuesPlugin) + + name = "RPM lowlevel structure plugin" + version = "0.0.1" + author = "Martin Sivak" + + issue_tests = [Packages, Locks] + set_flags = ["rpm_lowlevel"] + + @classmethod + def getDeps(cls): + return set(["root", "experimental", "filesystem"]) + + def __init__(self, *args, **kwargs): + IssuesPlugin.__init__(self, *args, **kwargs) + self.rpm = None + + def prepare(self): + self.backup = self._backups.getBackup(self.__class__.__name__+" -- "+self.name) + IssuesPlugin.prepare(self) + + def backup(self): + IssuesPlugin.backup(self) + self.backup.backupPath(path = os.path.join(Config.system.root,"/var/lib/rpm"), name="rpm") + self._result=ReturnSuccess + + def restore(self): + self.backup.restorePath(path = os.path.join(Config.system.root,"/var/lib/rpm"), name="rpm") + IssuesPlugin.restore(self) + self._result=ReturnSuccess + + def clean(self): + self._backups.closeBackup(self.backup._id) + IssuesPlugin.clean(self) + self._result=ReturnSuccess + +def get_plugin(): + return RPMLowlevelPlugin diff --git a/plugins/rpm_lowlevel/issue_locks.py b/plugins/rpm_lowlevel/issue_locks.py new file mode 100644 index 0000000..d64c3f7 --- /dev/null +++ b/plugins/rpm_lowlevel/issue_locks.py @@ -0,0 +1,73 @@ +# File name: issue_filesystem.py +# Date: 2008/03/14 +# Author: Martin Sivak +# +# Copyright (C) Red Hat 2008 +# +# 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 +# in a file called COPYING along with this program; if not, write to +# the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA +# 02139, USA. + +from pyfirstaidkit.issue import Issue +from pyfirstaidkit.reporting import TASK +from pyfirstaidkit.utils import spawnvch +from pyfirstaidkit.configuration import Config +import os.path +import re + +class Locks(Issue): + name = "Staled RPM locks" + description = "The database is still locked, but it shouldn't be. Probably a result of some unexpectedly aborted rpm operation." + + def __init__(self, *args, **kwargs): + Issue.__init__(self, *args, **kwargs) + + def detect(self): + result = Issue.detect(self) + if result is not None: + return result + + path = os.path.join(Config.system.root,"/var/lib/rpm/") + self.locks = [] + + def walkfunc(arg, dirname, fnames): + for f in fnames: + if f[:4]=="__db" and os.path.isfile(os.path.join(dirname, f)): + arg.append(os.path.join(dirname, f)) + + os.path.walk(path, walkfunc, self.locks) + + if len(self.locks)==0: + self._happened = False + else: + self._happened = True + + self._checked = True + return True + + def fix(self): + result = Issue.fix(self) + if result is not None: + return result + + for f in self.locks: + os.unlink(f) + + self._fixed = True + return True + + def reset(self): + Issue.reset(self) + self.locks = [] + diff --git a/plugins/rpm_lowlevel/issue_packages.py b/plugins/rpm_lowlevel/issue_packages.py new file mode 100644 index 0000000..34bac91 --- /dev/null +++ b/plugins/rpm_lowlevel/issue_packages.py @@ -0,0 +1,81 @@ +# File name: issue_filesystem.py +# Date: 2008/03/14 +# Author: Martin Sivak +# +# Copyright (C) Red Hat 2008 +# +# 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 +# in a file called COPYING along with this program; if not, write to +# the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA +# 02139, USA. + +from pyfirstaidkit.issue import Issue +from pyfirstaidkit.reporting import TASK +from pyfirstaidkit.utils import spawnvch +from pyfirstaidkit.configuration import Config +import os, os.path + + +class Packages(Issue): + name = "Required Packages database" + description = "The file containing the rpm database of packages is missing or corrupted" + + def detect(self): + result = Issue.detect(self) + if result is not None: + return result + + dbname = os.path.join(Config.system.root, "/var/lib/rpm/Packages") + self._happened = False + + if not os.path.isfile(os.path.realpath(dbname)): + self._db_missing = True + self._happened = True + self._checked = True + return True + + #verify the Package database + rpm_verify = spawnvch(executable = "/usr/lib/rpm/rpmdb_verify", args = ["/usr/lib/rpm/rpmdb_verify", dbname], chroot = Config.system.root) + err = rpm_verify.wait() + if err!=0: + return False + + if len(rpm_verify.stdout.read())>0: + self._happened = True + if len(rpm_verify.stderr.read())>0: + self._happened = True + + self._checked = True + return True + + def fix(self): + result = Issue.fix(self) + if result is not None: + return result + + dbname = os.path.join(Config.system.root,"/var/lib/rpm/Packages") + + if not self._db_missing: + #dump&load the database + os.rename(dbname, dbname+".orig") + err = spawnvch(executable = "/bin/sh", args = ["sh", "-c", "/usr/lib/rpm/rpmdb_dump /var/lib/rpm/Packages.orig | /usr/lib/rpm/rpmdb_load /var/lib/rpm/Packages"], chroot = Config.system.root).wait() + if rpm.returncode!=0: + os.rename(dbname+".orig", dbname) + return False + + #rebuild the indexes + rpm = spawnvch(executable = "/bin/rpm", args = ["rpm", "--rebuilddb"], chroot = Config.system.root).wait() + if rpm.returncode==0: + self._fixed = True + return True + diff --git a/plugins/sysinfo.py b/plugins/sysinfo.py new file mode 100644 index 0000000..72bb733 --- /dev/null +++ b/plugins/sysinfo.py @@ -0,0 +1,110 @@ +# First Aid Kit - diagnostic and repair tool for Linux +# Copyright (C) 2007 Martin Sivak +# +# 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.reporting import PLUGIN +from pyfirstaidkit.returns import * +from pyfirstaidkit.issue import SimpleIssue +from pyfirstaidkit.configuration import Config +from pyfirstaidkit.utils import spawnvch + +import os + +class Sample1Plugin(Plugin): + """Discover information about the system""" + name = "Discovery" + description = "Discover properties of the system" + version = "0.0.1" + author = "Martin Sivak" + + flows = Flow.init(Plugin) + flows["fix"] = flows["diagnose"] + + def __init__(self, *args, **kwargs): + Plugin.__init__(self, *args, **kwargs) + self._issue = SimpleIssue(self.name, "Discovering system properties failed") + + def prepare(self): + self._issue.set(reporting = self._reporting, origin = self, level = PLUGIN) + self._result=ReturnSuccess + + def diagnose(self): + #architecture and cpus + (unamestdout, unamestderr) = spawnvch(executable = "/bin/uname", args = ["uname", "-a"], chroot = Config.system.root).communicate("") + self._info.uname = unamestdout.split("\n")[0] + + #memory + (freestdout, freestderr) = spawnvch(executable = "/usr/bin/free", args = ["free"], chroot = Config.system.root).communicate("") + freedata = freestdout.split("\n") + self._info.memory = freedata[1].split()[1] + self._info.swap = freedata[3].split()[1] + + #pci + pcilist = [] + (lspcistdout, lspcistderr) = spawnvch(executable = "/sbin/lspci", args = ["lspci"], chroot = Config.system.root).communicate("") + for l in lspcistdout.split("\n"): + try: + (id, name) = l.split(" ", 1) + setattr(self._info, "_".join(["pci", id]), name) + pcilist.append(id) + except: + pass + self._info.pci = " ".join(pcilist) + + #usb + if os.path.exists(os.path.join(Config.system.root, "/proc/bus/usb")): + self._info.usb = "True" + else: + self._info.usb = "False" + + #scsi + if os.path.exists(os.path.join(Config.system.root, "/proc/scsi/device_info")): + self._info.scsi = "True" + else: + self._info.scsi = "False" + + #ide + if os.path.exists(os.path.join(Config.system.root, "/proc/ide")): + self._info.ide = "True" + else: + self._info.ide = "False" + + #partitions + partitionlist = [] + for l in open("/proc/partitions").readlines()[2:]: + try: + (major, minor, blocks, name) = l.split() + if name.startswith("ram"): + continue + setattr(self._info, "_".join(["partition", name]), blocks) + partitionlist.append(name) + except: + continue + self._info.partition = " ".join(partitionlist) + + #net + + + self._dependencies.provide("discovery") + self._issue.set(checked = True, happened = False, reporting = self._reporting, origin = self, level = PLUGIN) + self._result=ReturnSuccess + + def clean(self): + self._result=ReturnSuccess + +def get_plugin(): + return Sample1Plugin diff --git a/plugins/undelparts/Makefile b/plugins/undelparts/Makefile new file mode 100644 index 0000000..a9ef6cc --- /dev/null +++ b/plugins/undelparts/Makefile @@ -0,0 +1,38 @@ +# Makefile +# +# First Aid Kit - diagnostic and repair tool for Linux +# Copyright (C) 2008 Joel Andres Granados +# +# 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. +# +# + +PYVER := $(shell python -c 'import sys; print sys.version[0:3]') +PYTHON = python$(PYVER) +PYTHONINCLUDE = /usr/include/$(PYTHON) + + +CFLAGS = -I$(PYTHONINCLUDE) + +PYMODULE = _undelpart.so + +#Build with libparted (parted-1.8.3 and higher have a pkg-config file) +LDFLAGS = $(shell pkg-config --libs libparted) + +build: + $(CC) $(CFLAGS) -shared -fPIC -o _undelpart.so _undelpart.c $(LDFLAGS) + +clean: + rm -f _undelpart.so *.pyc diff --git a/plugins/undelparts/__init__.py b/plugins/undelparts/__init__.py new file mode 100644 index 0000000..b3402ea --- /dev/null +++ b/plugins/undelparts/__init__.py @@ -0,0 +1,20 @@ +# First Aid Kit - diagnostic and repair tool for Linux +# Copyright (C) 2008 Joel Andres Granados +# +# 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 undeletePartition +def get_plugin(): + return undeletePartition.UndeletePartition diff --git a/plugins/undelparts/_undelpart.c b/plugins/undelparts/_undelpart.c new file mode 100644 index 0000000..aab4f07 --- /dev/null +++ b/plugins/undelparts/_undelpart.c @@ -0,0 +1,578 @@ +/* +* First Aid Kit - diagnostic and repair tool for Linux +* Copyright (C) 2008 Joel Andres Granados +* +* 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. +* +*/ + + +#include +#include +#include +#include "parted/parted.h" + +/* + * Simple representation of the partition. + */ +typedef struct{ + int partnum; + PedSector partstart; + PedSector partend; +} partElem; + + +/* + * Helper function + */ + +/* + * Returns a disk with the path of the device. + */ +static PedDisk * +_getDiskFromPath(char * path){ + PedDevice * dev; + PedDisk * disk; + /* Try to create the device with the path */ + dev = ped_device_get(path); + if(dev == NULL) + return NULL; + + /* Read the partition table off of the device */ + disk = ped_disk_new(dev); + if(disk == NULL) + return NULL; + + return disk; +} + +/* + * Return the partition type. + */ +static PedPartitionType +_disk_get_part_type_for_sector (PedDisk* disk, PedSector sector) +{ + PedPartition* extended; + + extended = ped_disk_extended_partition (disk); + if (!extended + || !ped_geometry_test_sector_inside (&extended->geom, sector)) + return 0; + + return PED_PARTITION_LOGICAL; +} + +/* + * Create a python list from one partElement struct + */ +static PyObject * +_getPPartList( int partnum, int partstart, int partend ){ + PyObject * num, * start, * end; + PyObject * list; + /* + * Create a new temporary list. and add all the related values + */ + num = PyString_FromFormat("%d",partnum); + start = PyLong_FromLong(partstart); + end = PyLong_FromLong(partend); + list = PyList_New(3); + if(num == NULL || start == NULL || end == NULL || + list == NULL || + PyList_SetItem(list, 0, num) == -1 || + PyList_SetItem(list, 1, start) == -1 || + PyList_SetItem(list, 2, end) == -1){ + goto handle_error; + } + return list; + + handle_error: + + Py_XDECREF(num); + Py_XDECREF(start); + Py_XDECREF(end); + Py_XDECREF(list); + return NULL; +} + +/* + * Create a array of partElem with the python list + */ +static partElem +_getCPartList( PyObject * list ){ + partElem _list = {0}; + + // check that its a list. + if(!PyList_Check(list)){ + PyErr_SetString(PyExc_StandardError, + "Error malformed argument, list does not contian lisit."); + return _list; + } + + // check that it has three elements. + if(PyList_Size(list) < 3){ + PyErr_SetString(PyExc_StandardError, + "Error Malformed argument, contained list is to small."); + return _list; + } + + // Populate the _partList array. + _list.partnum = PyLong_AsLong( PyNumber_Long(PyList_GetItem(list, 0)) ); + _list.partstart = PyLong_AsLong( PyList_GetItem(list, 1) ); + _list.partend = PyLong_AsLong( PyList_GetItem(list, 2) ); + if( PyErr_Occurred()) + _list.partnum = '\0'; + return _list; +} + +static int +MEGABYTE_SECTORS (PedDevice* dev) +{ + return PED_MEGABYTE_SIZE / dev->sector_size; +} + +/* + * Tries to recover the partition part in the disk disk. Reutrns null if it + * was not possible, or the partition if it was. + */ +static PedPartition * +add_partition(PedDisk * disk, partElem part){ + + //PedDisk * clone; + PedSector s; + PedGeometry * probed; + PedGeometry sect_geom; + PedGeometry entire_dev; + PedPartition * parttemp = NULL; + PedConstraint disk_constraint, * part_constraint; + PedPartitionType part_type; + PedFileSystemType * fs_type; + + /* Initialize the entire_dev geom for the constraint calculation */ + ped_geometry_init(&entire_dev, disk->dev, 0, disk->dev->length); + part_type = _disk_get_part_type_for_sector (disk, (part.partstart + part.partend) / 2); + + + /* The end is a temporary hack until we get the method of search done */ + for (s = part.partstart; s < part.partstart+(part.partend-part.partstart)/10; s++) { + + /* Get a part from the specific s sector with the device constraint */ + ped_geometry_init (§_geom, disk->dev, s, 1); + ped_constraint_init (&disk_constraint, ped_alignment_any, ped_alignment_any, + §_geom, &entire_dev, 1, disk->dev->length); + + parttemp = ped_partition_new (disk, part_type, NULL, s, part.partend); + if(!parttemp){ + ped_disk_remove_partition(disk, parttemp); + ped_constraint_done(&disk_constraint); + parttemp = NULL; + continue; + } + + + /* add the partition to the disk */ + ped_exception_fetch_all(); //dont show errors + if(!ped_disk_add_partition(disk, parttemp, &disk_constraint)){ + ped_disk_remove_partition(disk, parttemp); + ped_constraint_done(&disk_constraint); + parttemp = NULL; + continue; + } + ped_exception_leave_all();// show errors. + + /* try to detect filesystem in the partition region */ + fs_type = ped_file_system_probe(&parttemp->geom); + if(!fs_type){ + ped_disk_remove_partition(disk, parttemp); + ped_constraint_done(&disk_constraint); + parttemp = NULL; + continue; + } + + /* try to find the exact region the filesystem ocupies */ + probed = ped_file_system_probe_specific(fs_type, &parttemp->geom); + if(!probed){ + ped_disk_remove_partition(disk, parttemp); + ped_constraint_done(&disk_constraint); + ped_geometry_destroy(probed); + parttemp = NULL; + continue; + } + + /* see if probed is inside the partition region */ + if(!ped_geometry_test_inside(&parttemp->geom, probed)) { + ped_disk_remove_partition(disk, parttemp); + ped_constraint_done(&disk_constraint); + ped_geometry_destroy(probed); + parttemp = NULL; + continue; + } + + /* create a constraint for the probed region */ + part_constraint = ped_constraint_exact (probed); + + /* set the region for the partition */ + if (!ped_disk_set_partition_geom (parttemp->disk, parttemp, part_constraint, + probed->start, probed->end)) { + ped_disk_remove_partition(disk, parttemp); + ped_constraint_done(part_constraint); + ped_constraint_done(&disk_constraint); + ped_geometry_destroy(probed); + parttemp = NULL; + continue; + } + ped_partition_set_system(parttemp, fs_type); + ped_disk_commit(disk); + ped_disk_commit_to_dev(disk); + ped_disk_commit_to_os(disk); + break; + } + return parttemp; +} + +/* Pythong facing functions. + * + * Returns a dictionary of the form { DISK : [None, None, None, None] ...} + */ +static PyObject * +undelpart_getDiskList(PyObject * self, PyObject * args){ + + PedDevice * dev; + + PyObject * dict; + PyObject * list; + PyObject * diskName; + + int i; + + dict = PyDict_New(); + if(dict == NULL){ + PyErr_SetString(PyExc_StandardError, "Error creating a new dictionary."); + goto handle_error; + } + + /* Search for the disks on the system */ + ped_device_probe_all(); + + for(dev=ped_device_get_next(dev); dev ; dev=ped_device_get_next(dev)){ + /* + * Build the list for this particular disk and fill it with Python + * None Objects. + */ + list = PyList_New(4); + if(list == NULL){ + PyErr_SetString(PyExc_StandardError, "Error creating a new list."); + goto handle_error; + } + for(i=0 ; i < 4 ; i++){ //We set all items to None. + if(PyList_SetItem(list, i, Py_None) == -1){ + PyErr_SetString(PyExc_StandardError, + "Error setting up the None valued list."); + goto handle_error; + } + } + + /* + * Take the name out of the PedDevice structure and place it as a + * dictionary key. Use the device path. + */ + diskName = Py_BuildValue("s", dev->path); + if(diskName == NULL){ + PyErr_SetString(PyExc_StandardError, + "Error creating key for dictionary."); + goto handle_error; + } + + if(PyDict_SetItem(dict, diskName, list) == -1){ + PyErr_SetString(PyExc_StandardError, + "Error while creating the dictionary entry"); + goto handle_error; + } + } + + /* If the dictionary's length is 0, something is wrong. */ + if(PyDict_Size(dict) == 0){ + PyErr_SetString(PyExc_StandardError, + "libparted was unable to get a disk list. Are you root?"); + goto handle_error; + } + + ped_device_free_all(); + return dict; + + handle_error: + assert(PyErr_Occurred()); + + Py_XDECREF(diskName); + Py_XDECREF(list); + Py_XDECREF(dict); + ped_device_free_all(); + + return NULL; +} + +/* + * Returns a list of partitions that are present in the disk but not in its + * partition table. If the disk does not exist it errors. If the disk + * has no rescueable partitions it returns a void list. It is a list of + * possible partitions, it will NOT check for rescuability. + */ +static PyObject * +undelpart_getRescuable(PyObject * self, PyObject * args){ + + PedDisk * disk; + PedPartition * part; + + PyObject * tempList; + PyObject * partitions; + + char * path; + + if(!PyArg_ParseTuple(args, "s", &path)){ + PyErr_SetString(PyExc_TypeError, "Argument is not a String"); + goto handle_error; + } + + /* Build the empty list*/ + partitions = PyList_New(0); + if(partitions == NULL){ + PyErr_SetString(PyExc_StandardError, "Error creating a new list."); + goto handle_error_destroy_disk; + } + + /* create the disk */ + disk = _getDiskFromPath(path); + if(disk == NULL){ + PyErr_SetString(PyExc_StandardError, "Error reading disk information."); + goto handle_error_destroy_disk; + } + + /* + * We start looking for the partitions. The partitions will be detected if + * the numpart is less than 1. The basic idea is to traverse all the partitions + * and look for holes in between. + */ + for(part = ped_disk_next_partition(disk, NULL); part ; + part = ped_disk_next_partition(disk, part) ){ + // All partitions with partnum less than 1 is a possibility. + if(part->num < 1 && part->geom.start < part->geom.end){ + /* create the python object */ + tempList = _getPPartList(part->num, part->geom.start, part->geom.end); + /* Append the list to the return value */ + if(tempList == NULL || PyList_Append(partitions, tempList) == -1){ + PyErr_SetString(PyExc_StandardError, + "Error creating the partition information."); + goto handle_error_destroy_disk; + } + } + } + ped_disk_destroy(disk); + ped_device_free_all(); + + return partitions; + + handle_error_destroy_disk: + ped_disk_destroy(disk); + + handle_error: + assert(PyErr_Occurred()); + + Py_XDECREF(partitions); + Py_XDECREF(tempList); + + ped_device_free_all(); + return NULL; +} + +/* + * Returns a list of valid partitions at time of scan. + */ +static PyObject * +undelpart_getPartitionList(PyObject * self, PyObject * args){ + + PedDisk * disk; + PedDevice * dev; + PedPartition * part; //libparted object + + PyObject * partList; //python list of partitions + PyObject * tempList; //python temporary object to hold the temprorary list. + + char * path; + + if(!PyArg_ParseTuple(args, "s", &path)){ + PyErr_SetString(PyExc_TypeError, "Argument is not a String"); + goto handle_error; + } + + /* create the disk an dev */ + disk = _getDiskFromPath(path); + if(disk == NULL){ + PyErr_SetString(PyExc_StandardError, "Error reading disk information."); + goto handle_error; + } + dev = disk->dev; + + /* Create the python list that we are to fill */ + partList = PyList_New(0); + if(partList == NULL){ + PyErr_SetString(PyExc_StandardError, "Error creating a new list."); + goto handle_error; + } + + /* Get all the active partitions from disk */ + for(part = ped_disk_next_partition(disk, NULL) ; + part ; part = ped_disk_next_partition(disk, part)){ + if(part->num < 0) + continue; + + tempList = _getPPartList(part->num, + part->geom.start, + part->geom.end); + /* Append the list to the return value */ + if(tempList == NULL || PyList_Append(partList, tempList) == -1){ + PyErr_SetString(PyExc_StandardError, + "Error appending the partition to the list."); + goto handle_error; + } + } + ped_device_free_all(); + + return partList; + + handle_error: + assert(PyErr_Occurred()); + + Py_XDECREF(partList); + Py_XDECREF(tempList); + + ped_device_free_all(); + + return NULL; +} + +/* + * On a specific disk try to rescue a list of partitions. Return the list of partitions + * that was recovered. The partitions should be in the [[partNum, start, end]...] + * format. + */ +static PyObject * +undelpart_rescue(PyObject * self, PyObject * args){ + + PedDisk * disk; + PedPartition * part; + + PyObject * partList; + PyObject * rescuedParts; + PyObject * tempList; + + partElem * _partList = NULL; + char * path; + int partListSize = 0; + int i; + + /* Check the arguments */ + if(!PyArg_ParseTuple(args, "sO", &path, &partList)){ + PyErr_SetString(PyExc_TypeError, "Arguments are not valid (String, List)"); + goto handle_error; + } + if(! PyList_Check(partList)){ + PyErr_SetString(PyExc_TypeError, + "The object that was passed is not a list."); + goto handle_error; + } + + /* Build the empty list, this is the return value. */ + rescuedParts = PyList_New(0); + if(rescuedParts == NULL){ + PyErr_SetString(PyExc_StandardError, "Error creating a new list."); + goto handle_error; + } + + /* Put the values of the list into a array of partElem */ + partListSize = PyList_Size(partList); + _partList = malloc(sizeof(partElem)*partListSize+1); + if(!_partList){ + PyErr_SetString(PyExc_StandardError, "Error allocating memory."); + goto handle_error; + } + for(i=0; i < partListSize ; i++){ + _partList[i] = _getCPartList(PyList_GetItem(partList, i)); + if( PyErr_Occurred() || _partList[i].partnum == '\0'){ + goto handle_error; + } + } + _partList[partListSize].partnum = '\0'; + + /* create the disk an dev */ + disk = _getDiskFromPath(path); + if(disk == NULL){ + PyErr_SetString(PyExc_StandardError, "Error reading disk information."); + goto handle_error; + } + + /* Try to add each partition. */ + for(i=0 ; _partList[i].partnum ; i++){ + part = add_partition(disk, _partList[i]); + if(!part){ + // could not rescue this partition. sorry + continue; + }else{ + tempList = _getPPartList(part->num, part->geom.start, part->geom.end); + /* Append the list to the return value */ + if(tempList == NULL || PyList_Append(rescuedParts, tempList) == -1){ + PyErr_SetString(PyExc_StandardError, + "Error creating the partition information."); + goto handle_error; + } + } + } + ped_disk_destroy(disk); + free(_partList); + ped_device_free_all(); + + return rescuedParts; + + handle_error: + assert(PyErr_Occurred()); + + free(_partList); + ped_device_free_all(); + + return NULL; +} + +static struct PyMethodDef undelpart_methods [] = { + { "getDiskList", + (PyCFunction)undelpart_getDiskList, + METH_VARARGS, "Generaly returns the system disk list. Receives nothing." }, + { "getRescuable", + (PyCFunction)undelpart_getRescuable, + METH_VARARGS, "Get a list of partitions from a specific disk that might " + "be rescuable. It returns the partitions that are not in the partition " + "table but where present after a disk scan. It expects the disk name."}, + { "getPartitionList", + (PyCFunction)undelpart_getPartitionList, + METH_VARARGS, "Get the partition list off of a certain disk. This is intended " + "to be used as a backup. It returns the number of the partition, start " + "sector and the end sector."}, + {"rescue", + (PyCFunction)undelpart_rescue, + METH_VARARGS, "Try to put the list of rescuable partitions into the partition " + "table. If the partitions are already there, nothing will be done. A list " + "of rescued partitions is returned. This does NOT delete any partitions."} +}; + +void init_undelpart(void){ + (void) Py_InitModule("_undelpart", undelpart_methods); +} diff --git a/plugins/undelparts/undeletePartition.py b/plugins/undelparts/undeletePartition.py new file mode 100644 index 0000000..47566d2 --- /dev/null +++ b/plugins/undelparts/undeletePartition.py @@ -0,0 +1,171 @@ +# First Aid Kit - diagnostic and repair tool for Linux +# Copyright (C) 2008 Joel Andres Granados +# +# 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.reporting import PLUGIN +import signal +import _undelpart + +class UndeletePartition(Plugin): + """Plugin to detect and correct deleted partitions from system disks. + + Uses parted libriary to search for partitions that are not included in + the partition table of a disk. If it is possible, this plugin will put + the partition back into the parition table so it is visible to the + system again. + """ + + flows = Flow.init(Plugin) + # We have not restore in the noBackup flow because we have no information to restore with. + flows["noBackup"] = Flow({ + Plugin.initial: {Return: "prepare"}, + "prepare" : {ReturnSuccess: "diagnose"}, + "diagnose" : {ReturnSuccess: "clean", ReturnFailure: "fix"}, + "fix" : {ReturnSuccess: "clean", ReturnFailure: "clean"}, + "clean" : {ReturnSuccess: Plugin.final} + }, description="This flow skips the backup test. Use with care.") + + name = "Undelete Partitions" + version = "0.1.0" + author = "Joel Andres Granados" + def __init__(self, *args, **kwargs): + Plugin.__init__(self, *args, **kwargs) + + # Dictionary that will hold the partitions that are not included in the + # partition table of a certain disk and can be recovered. It will also + # house the initial partition table and the partition table that is a + # result of running the fix. The structure is: + # slef.disks={diskname: [ [recoverables], initialPT, finalPT ], .... } + self.disks = {} + + def prepare(self): + # For now there is no real action in the prepare task. + self._result=ReturnSuccess + + # + # The diagnose will not be a real diagnose but more of an informative task. + # It will report all the possible paritions that could house a rescuable + # partition. + # + def diagnose(self): + self._reporting.info("Beginning Diagnose...", origin = self, level = PLUGIN) + self.disks = _undelpart.getDiskList() + self._reporting.info("Disks present in the system %s"%self.disks.keys(), + origin = self, level = PLUGIN) + # When we find a rescuable partition we change this to true. + rescuablePresent = False + for disk, elements in self.disks.iteritems(): + self.disks[disk] = [ _undelpart.getRescuable(disk), _undelpart.getPartitionList(disk), [] ] + if len(self.disks[disk][0]) > 0: + self._reporting.info("Possible partitions to recover in disk %s: %s"%(disk, self.disks[disk][0]), + origin = self, level = PLUGIN) + rescuablePresent = True + if not rescuablePresent: + self._result = ReturnSuccess + self._reporting.info("Did not find any partitions that need rescueing.", + origin = self, level = PLUGIN) + else: + self._result = ReturnFailure + + def backup(self): + self._reporting.info("Backing up partition table." , origin = self, level = PLUGIN) + # We actually already have the backup of the partition table in the self.disks dict. + # Lets check anyway. + backupSane = True + for disk, members in self.disks.iteritems(): + if members[1] == None or len(members[1]) <= 0: + # We don't really have the partition table backup. + self._reporting.info("Couldn't backup the partition table for %s."%disk, + origin = self, level = PLUGIN) + self._reporting.info("To force the recovery of this disk without the backup " \ + "please run the flow named noBackup from this plugin.", + origin = self, level = PLUGIN) + backupSane = False + self._result = ReturnFailure + + if backupSane: + self._result = ReturnSuccess + + # + # Every partition that we suspect is rescuable, (given that it has a partition table from + # wich we can recover if we mess up) we try to rescue. This will take a long time. + # + def fix(self): + self._reporting.info("Lets see if I can fix this... Starting fix task.", + origin = self, level = PLUGIN) + self._reporting.info("Might want to go and get a cup of coffee," + "this could take a looooooong time...", origin = self, level = PLUGIN) + self._result = ReturnSuccess + rescued = [] + try: + for disk, members in self.disks.iteritems(): + if len(members[0]) > 0:#there are partitions to rescue :) + self._reporting.info("Trying to rescue %s from disk %s"%(members[0], disk), + origin = self, level = PLUGIN) + rescued = _undelpart.rescue(disk,members[0]) + self._reporting.info("Partitions rescued: %s"%rescued, + origin = self, level = PLUGIN) + elif len(members[0]) == 0: + self._reporting.info("Nothing to rescue on disk %s."%disk, + origin = self, level = PLUGIN) + else: + self_result = ReturnFailure + break + except KeyboardInterrupt, e: + self._reporting.error("Received a user interruption... Moving to Restore task.", + origin = self, level = PLUGIN, action = None) + # The user might want to keep on pushing ctrl-c, lets lock the SIGINT signal. + signal.signal(signal.SIGINT, keyboaordInterruptHandler) + self._reporting.info("Please wait until the original partition table is recovered.", + origin = self, level = PLUGIN) + self._result = ReturnFailure + + # + # We are not really erasing anything, so recovering is kinda out of the point. That said + # anything can happen with partitioning. :) Lets get the current partitionList and try + # to add all the partitions that are not in the current part list but are in the backedup + # one. + # + def restore(self): + self._reporting.info("Starting Restoring task." , origin = self, level = PLUGIN) + tempPartList = [] + backupPartList = [] + for disk, members in self.disk.iteritems(): + tempPartList = _undelpart.getPartitionList(disk) + backupPartList = members[1] + for part in backupPartList: + if part not in tempPartList:# we need to restore + self._reporting.info("Trying to restore partition %s on disk %s"%(part, disk), + origin = self, level = PLUGIN) + restore = _undelpart.rescue(disk, [part]) + if len(restore) > 0: + self._reporting.info("Restored partition %s on disk %s"%(part, disk), + origin = self, level = PLUGIN) + else: + self._reporting.error("Could not restore partititon %s on disk %s"%(part, disk), + origin = self, level = PLUGIN, action = None) + # Return the signal to its previous state. + signal.signal(signal.SIGINT, signal.SIG_DFL) + self._result = ReturnSuccess + + def clean(self): + self._reporting.info("Cleanning...",origin = self, level = PLUGIN) + self._result = ReturnSuccess + +def keyboardInterruptHandler(signum, frame): + pass diff --git a/plugins/xserver.py b/plugins/xserver.py new file mode 100644 index 0000000..71c7d2c --- /dev/null +++ b/plugins/xserver.py @@ -0,0 +1,208 @@ +# First Aid Kit - diagnostic and repair tool for Linux +# Copyright (C) 2008 Joel Andres Granados +# +# 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 * + +import rhpxl.xserver +import rhpl.keyboard +import tempfile +import subprocess +import time +import signal +import os +import os.path +import shutil + +class Xserver(Plugin): + """ Plugin to detect an rescue faulty xserver configurations. """ + flows = Flow.init(Plugin) + flows["force"] = Flow({ + Plugin.initial: {Return: "prepare"}, + "prepare" : {ReturnSuccess: "diagnose2"}, + "diagnose2" : {ReturnSuccess: "clean", ReturnFailure: "backup"}, + "backup" : {ReturnSuccess: "fix", ReturnFailure: "clean"}, + "restore" : {ReturnSuccess: "clean", ReturnFailure: "clean"}, + "fix" : {ReturnSuccess: "clean", ReturnFailure: "restore"}, + "clean" : {ReturnSuccess: Plugin.final} + }, description="This flow skips the search for the xserver lock file") + name = "X server" + 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) + # Arbitrary test display + self.display = ":10" + self.confPath = "/etc/X11/xorg.conf" + self.backupSpace = self._backups.getBackup(str(self)) + self._issue = SimpleIssue(self.name, "X server didn't start") + + def prepare(self): + # Nothing to prepare really. + self._result = ReturnSuccess + self._issue.set(reporting = self._reporting, level = PLUGIN, + origin = self) + + def diagnose(self): + # Lets see if there is a server active. + if os.path.exists("/tmp/.X0-lock"): + self._reporting.info("An X server is already running.", + level = PLUGIN, origin = self) + self._reporting.info("You can run the \"force\" flow to " + "avoud this check. In some cases it works.", + level = PLUGIN, origin = self) + self._result = ReturnSuccess + + elif self.serverStart(): + self._reporting.info("Everything seems ok with the X server.", + level = PLUGIN, origin = self) + self._result = ReturnSuccess + + elif not os.path.exists(self.confPath): + # If the configuration is not there dont even bother to try + #fixing it. This will go through the proces of trying to fix + #it. at least we told the user. + self._reporting.info("The error is in the xservers autodetection " + "mechanism, this does not have an automated solution yet.", + level = PLUGIN, origin = self) + self._result = ReturnFailure + + else: + self._reporting.info("X server is missconfigured.", level = PLUGIN, + origin = self) + self._result = ReturnFailure + self._issue.set(checked = True, + happened = (self._result == ReturnFailure), + reporting = self._reporting, level = PLUGIN, origin = self) + + def diagnose2(self): + """Just a diagnose without the lock check""" + if self.serverStart(): + self._reporting.info("Everything seems ok with the X server.", + level = PLUGIN, origin = self) + self._result = ReturnSuccess + + elif not os.path.exists(self.confPath): + # If the configuration is not there dont even bother to try fixing it. + # This will go through the proces of trying to fix it. at least we + #told the user. + self._reporting.info("The error is in the xservers autodetection " + "mechanism, this does not have an automated solution yet.", + level = PLUGIN, origin = self) + self._result = ReturnFailure + + else: + self._reporting.info("X server is missconfigured.", level = PLUGIN, + origin = self) + self._result = ReturnFailure + self._issue.set(checked = True, + happened = (self._result == ReturnFailure), + reporting = self._reporting, level = PLUGIN, origin = self) + + + def backup(self): + if os.path.isfile(self.confPath): + self.backupSpace.backupPath(self.confPath) + else: + self._reporting.info("%s does not exist." % self.confPath, + level = PLUGIN, origin = self) + self._result = ReturnSuccess + + def fix(self): + self._reporting.info("Starting the fix task.", level = PLUGIN, + origin = self) + # With the current xorg server the only thing that we need to do is to + # erase the conf file. + if os.path.exists(self.confPath): + os.remove(self.confPath) + + self._reporting.info("Testing modified environment", level = PLUGIN, + origin = self) + if self.serverStart(): + self._reporting.info("X server started successfully with no " + "config file.", level = PLUGIN, origin = self) + self._reporting.info("If you must have a config file, create " + "one with system-config-display and place it at " + "/etc/X11/xorg.conf", level = PLUGIN, origin = self ) + self._result = ReturnSuccess + + # Lets give the user his previous file. + if self.backupSpace.exists(path=self.confPath): + self.backupSpace.restoreName(self.confPath, + "%s-FAKbackup"%self.confPath) + + else: + self._reporting.info("X server is does not autodetect the users " + "environment.", level = PLUGIN, origin = self) + self._result = ReturnFailure + + self._issue.set(fixed = (self._result == ReturnSuccess), + reporting = self._reporting, level = PLUGIN, origin = self) + + def restore(self): + if not self.backupSpace.exists(path=self.confPath): + # This is the case where there is no config file. + self._reporting.info("The backedup file was not present. Assuming " + "that xorg did not have a config file to begin with.", + level = PLUGIN, origin = self) + else: + self._reporting.info("Restoring original file.", level = PLUGIN , + origin = self) + self.backupSpace.restoreName(self.confPath) + + self._result = ReturnSuccess + + def clean(self): + self._result = ReturnSuccess + + + def serverStart(self): + self._reporting.info("Trying to start X server", level = PLUGIN, + origin = self) + xorgargs = [self.display] + try: + proc = spawnvch(executable = "/usr/bin/Xorg", args = xorgargs, + chroot = Config.system.root) + self._reporting.info("Waiting for the X server to start...", + level = PLUGIN, origin = self) + time.sleep(5) + if proc.poll() is not None: + # process has terminated, failed. + raise OSError + except: + self._reporting.info("The X server has failed to start", + level = PLUGIN, origin = self) + return False + self._reporting.info("The X server has started successfully", + level = PLUGIN, origin = self) + os.kill(proc.pid, signal.SIGINT) + return True + + +def get_plugin(): + return Xserver + -- cgit