diff options
author | Martin Sivak <msivak@redhat.com> | 2010-05-11 17:31:06 +0200 |
---|---|---|
committer | Martin Sivak <msivak@redhat.com> | 2010-05-31 14:21:49 +0200 |
commit | 78421d4ed23ad58b021c4d9d1bae690b99c167fe (patch) | |
tree | 909c1b650afe9afea03ecf1a03b2d39d39cf72b7 /pyanaconda/rescue.py | |
parent | 40b979f28cfd73078d5dd58b8f7e97e76198a222 (diff) | |
download | anaconda-78421d4ed23ad58b021c4d9d1bae690b99c167fe.tar.gz anaconda-78421d4ed23ad58b021c4d9d1bae690b99c167fe.tar.xz anaconda-78421d4ed23ad58b021c4d9d1bae690b99c167fe.zip |
Structure the repo layout so it matches final structure better and make isys a real Python package.
Also updates the build and autotools stuff to work with the new structure
Diffstat (limited to 'pyanaconda/rescue.py')
-rw-r--r-- | pyanaconda/rescue.py | 517 |
1 files changed, 517 insertions, 0 deletions
diff --git a/pyanaconda/rescue.py b/pyanaconda/rescue.py new file mode 100644 index 000000000..e8e28764f --- /dev/null +++ b/pyanaconda/rescue.py @@ -0,0 +1,517 @@ +# +# rescue.py - anaconda rescue mode setup +# +# Copyright (C) 2001, 2002, 2003, 2004 Red Hat, Inc. All rights reserved. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +# Author(s): Mike Fulbright <msf@redhat.com> +# Jeremy Katz <katzj@redhat.com> +# + +import upgrade +from snack import * +from constants import * +from constants_text import * +from text import WaitWindow, OkCancelWindow, ProgressWindow, PassphraseEntryWindow, stepToClasses +from flags import flags +import sys +import os +import isys +from storage import mountExistingSystem +from installinterfacebase import InstallInterfaceBase +from add_drive_text import addDriveDialog +import iutil +import shutil +import time +import network +import subprocess +from pykickstart.constants import * + +import gettext +_ = lambda x: gettext.ldgettext("anaconda", x) + +import logging +log = logging.getLogger("anaconda") + +class RescueInterface(InstallInterfaceBase): + def waitWindow(self, title, text): + return WaitWindow(self.screen, title, text) + + def progressWindow(self, title, text, total): + return ProgressWindow(self.screen, title, text, total) + + def detailedMessageWindow(self, title, text, longText=None, type="ok", + default=None, custom_icon=None, + custom_buttons=[], expanded=False): + return self.messageWindow(title, text, type, default, custom_icon, + custom_buttons) + + def messageWindow(self, title, text, type = "ok", default = None, + custom_icon=None, custom_buttons=[]): + if type == "ok": + ButtonChoiceWindow(self.screen, title, text, + buttons=[TEXT_OK_BUTTON]) + elif type == "yesno": + if default and default == "no": + btnlist = [TEXT_NO_BUTTON, TEXT_YES_BUTTON] + else: + btnlist = [TEXT_YES_BUTTON, TEXT_NO_BUTTON] + rc = ButtonChoiceWindow(self.screen, title, text, + buttons=btnlist) + if rc == "yes": + return 1 + else: + return 0 + elif type == "custom": + tmpbut = [] + for but in custom_buttons: + tmpbut.append(string.replace(but,"_","")) + + rc = ButtonChoiceWindow(self.screen, title, text, width=60, + buttons=tmpbut) + + idx = 0 + for b in tmpbut: + if string.lower(b) == rc: + return idx + idx = idx + 1 + return 0 + else: + return OkCancelWindow(self.screen, title, text) + + def enableNetwork(self, anaconda): + if len(anaconda.network.netdevices) == 0: + return False + from netconfig_text import NetworkConfiguratorText + w = NetworkConfiguratorText(self.screen, anaconda) + ret = w.run() + return ret != INSTALL_BACK + + def passphraseEntryWindow(self, device): + w = PassphraseEntryWindow(self.screen, device) + (passphrase, isglobal) = w.run() + w.pop() + return (passphrase, isglobal) + + def resetInitializeDiskQuestion(self): + self._initLabelAnswers = {} + + def resetReinitInconsistentLVMQuestion(self): + self._inconsistentLVMAnswers = {} + + def questionInitializeDisk(self, path, description, size, details=""): + # Never initialize disks in rescue mode! + return False + + def questionReinitInconsistentLVM(self, pv_names=None, lv_name=None, vg_name=None): + # Never reinit VG's in rescue mode! + return False + + def questionInitializeDASD(self, c, devs): + # Special return value to let dasd.py know we're rescue mode + return 1 + + def shutdown (self): + pass + + def suspend(self): + pass + + def resume(self): + pass + + def __init__(self, screen): + InstallInterfaceBase.__init__(self) + self.screen = screen + +# XXX grub-install is stupid and uses df output to figure out +# things when installing grub. make /etc/mtab be at least +# moderately useful. +def makeMtab(instPath, storage): + try: + f = open(instPath + "/etc/mtab", "w+") + except IOError, e: + log.info("failed to open /etc/mtab for write: %s" % e) + return + + try: + f.write(storage.mtab) + finally: + f.close() + +def makeFStab(instPath = ""): + if os.access("/proc/mounts", os.R_OK): + f = open("/proc/mounts", "r") + buf = f.read() + f.close() + else: + buf = "" + + try: + f = open(instPath + "/etc/fstab", "a") + if buf: + f.write(buf) + f.close() + except IOError, e: + log.info("failed to write /etc/fstab: %s" % e) + +# make sure they have a resolv.conf in the chroot +def makeResolvConf(instPath): + if not os.access("/etc/resolv.conf", os.R_OK): + return + + if os.access("%s/etc/resolv.conf" %(instPath,), os.R_OK): + f = open("%s/etc/resolv.conf" %(instPath,), "r") + buf = f.read() + f.close() + else: + buf = "" + + # already have a nameserver line, don't worry about it + if buf.find("nameserver") != -1: + return + + f = open("/etc/resolv.conf", "r") + buf = f.read() + f.close() + + # no nameserver, we can't do much about it + if buf.find("nameserver") == -1: + return + + shutil.copyfile("%s/etc/resolv.conf" %(instPath,), + "%s/etc/resolv.conf.bak" %(instPath,)) + f = open("%s/etc/resolv.conf" %(instPath,), "w+") + f.write(buf) + f.close() + +# +# Write out something useful for networking and start interfaces +# +def startNetworking(network, intf): + # do lo first + try: + os.system("/usr/sbin/ifconfig lo 127.0.0.1") + except: + log.error("Error trying to start lo in rescue.py::startNetworking()") + + # start up dhcp interfaces first + if not network.bringUp(): + log.error("Error bringing up network interfaces") + +def runShell(screen = None, msg=""): + if screen: + screen.suspend() + + print + if msg: + print (msg) + print(_("When finished please exit from the shell and your " + "system will reboot.")) + print + + proc = None + + if os.path.exists("/usr/bin/firstaidkit-qs"): + proc = subprocess.Popen(["/usr/bin/firstaidkit-qs"]) + proc.wait() + + if proc is None or proc.returncode!=0: + if os.path.exists("/bin/bash"): + iutil.execConsole() + else: + print(_("Unable to find /bin/sh to execute! Not starting shell")) + time.sleep(5) + + if screen: + screen.finish() + +def runRescue(anaconda): + for file in [ "services", "protocols", "group", "joe", "man.config", + "nsswitch.conf", "selinux", "mke2fs.conf" ]: + try: + os.symlink('/mnt/runtime/etc/' + file, '/etc/' + file) + except: + pass + + # see if they would like networking enabled + if not network.hasActiveNetDev(): + screen = SnackScreen() + + while True: + rc = ButtonChoiceWindow(screen, _("Setup Networking"), + _("Do you want to start the network interfaces on " + "this system?"), [_("Yes"), _("No")]) + + if rc != string.lower(_("No")): + anaconda.intf = RescueInterface(screen) + + if not anaconda.intf.enableNetwork(anaconda): + anaconda.intf.messageWindow(_("No Network Available"), + _("Unable to activate a networking device. Networking " + "will not be available in rescue mode.")) + break + + startNetworking(anaconda.network, anaconda.intf) + break + else: + break + + anaconda.intf = None + screen.finish() + + # Early shell access with no disk access attempts + if not anaconda.rescue_mount: + # the %post should be responsible for mounting all needed file systems + # NOTE: 1st script must be bash or simple python as nothing else might be available in the rescue image + if anaconda.ksdata: + from kickstart import runPostScripts + runPostScripts(anaconda) + else: + runShell() + + sys.exit(0) + + screen = SnackScreen() + anaconda.intf = RescueInterface(screen) + + if anaconda.ksdata: + if anaconda.ksdata.rescue and anaconda.ksdata.rescue.romount: + readOnly = 1 + else: + readOnly = 0 + else: + # prompt to see if we should try and find root filesystem and mount + # everything in /etc/fstab on that root + while True: + rc = ButtonChoiceWindow(screen, _("Rescue"), + _("The rescue environment will now attempt to find your " + "Linux installation and mount it under the directory " + "%s. You can then make any changes required to your " + "system. If you want to proceed with this step choose " + "'Continue'. You can also choose to mount your file systems " + "read-only instead of read-write by choosing 'Read-Only'. " + "If you need to activate SAN devices choose 'Advanced'." + "\n\n" + "If for some reason this process fails you can choose 'Skip' " + "and this step will be skipped and you will go directly to a " + "command shell.\n\n") % (anaconda.rootPath,), + [_("Continue"), _("Read-Only"), _("Skip"), _("Advanced")] ) + + if rc == string.lower(_("Skip")): + runShell(screen) + sys.exit(0) + elif rc == string.lower(_("Advanced")): + addDialog = addDriveDialog(anaconda) + addDialog.addDriveDialog(screen) + continue + elif rc == string.lower(_("Read-Only")): + readOnly = 1 + else: + readOnly = 0 + break + + import storage + storage.storageInitialize(anaconda) + + disks = upgrade.findExistingRoots(anaconda, upgradeany=True) + + if not disks: + root = None + elif (len(disks) == 1) or anaconda.ksdata: + root = disks[0] + else: + height = min (len (disks), 12) + if height == 12: + scroll = 1 + else: + scroll = 0 + + devList = [] + for (device, relstr) in disks: + if getattr(device.format, "label", None): + devList.append("%s (%s) - %s" % (device.name, device.format.label, relstr)) + else: + devList.append("%s - %s" % (device.name, relstr)) + + (button, choice) = \ + ListboxChoiceWindow(screen, _("System to Rescue"), + _("Which device holds the root partition " + "of your installation?"), devList, + [ _("OK"), _("Exit") ], width = 30, + scroll = scroll, height = height, + help = "multipleroot") + + if button == string.lower (_("Exit")): + root = None + else: + root = disks[choice] + + rootmounted = 0 + + if root: + try: + rc = mountExistingSystem(anaconda, root, + allowDirty = 1, warnDirty = 1, + readOnly = readOnly) + + if rc == -1: + if anaconda.ksdata: + log.error("System had dirty file systems which you chose not to mount") + else: + ButtonChoiceWindow(screen, _("Rescue"), + _("Your system had dirty file systems which you chose not " + "to mount. Press return to get a shell from which " + "you can fsck and mount your partitions. The system " + "will reboot automatically when you exit from the " + "shell."), [_("OK")], width = 50) + rootmounted = 0 + else: + if anaconda.ksdata: + log.info("System has been mounted under: %s" % anaconda.rootPath) + else: + ButtonChoiceWindow(screen, _("Rescue"), + _("Your system has been mounted under %(rootPath)s.\n\n" + "Press <return> to get a shell. If you would like to " + "make your system the root environment, run the command:\n\n" + "\tchroot %(rootPath)s\n\nThe system will reboot " + "automatically when you exit from the shell.") % + {'rootPath': anaconda.rootPath}, + [_("OK")] ) + rootmounted = 1 + + # now turn on swap + if not readOnly: + try: + anaconda.storage.turnOnSwap() + except: + log.error("Error enabling swap") + + # and /selinux too + if flags.selinux and os.path.isdir("%s/selinux" %(anaconda.rootPath,)): + try: + isys.mount("/selinux", "%s/selinux" %(anaconda.rootPath,), + "selinuxfs") + except Exception, e: + log.error("error mounting selinuxfs: %s" %(e,)) + + # we have to catch the possible exception + # because we support read-only mounting + try: + fd = open("%s/.autorelabel" % anaconda.rootPath, "w+") + fd.close() + except Exception, e: + log.warning("cannot touch /.autorelabel") + + # set a library path to use mounted fs + libdirs = os.environ["LD_LIBRARY_PATH"].split(":") + mounted = map(lambda dir: "/mnt/sysimage%s" % dir, libdirs) + os.environ["LD_LIBRARY_PATH"] = ":".join(libdirs + mounted) + + # find groff data dir + try: + glst = os.listdir("/mnt/sysimage/usr/share/groff") + + # find a directory which is a numeral, its where + # data files are + gversion = None + for gdir in glst: + try: + isone = 1 + for idx in range(0, len(gdir)): + if string.find(string.digits + '.', gdir[idx]) == -1: + isone = 0 + break + if isone: + gversion = gdir + break + except: + gversion = None + continue + except: + gversion = None + + if gversion is not None: + gpath = "/mnt/sysimage/usr/share/groff/"+gversion + os.environ["GROFF_FONT_PATH"] = gpath + '/font' + os.environ["GROFF_TMAC_PATH"] = "%s:/mnt/sysimage/usr/share/groff/site-tmac" % (gpath + '/tmac',) + + # do we have bash? + try: + if os.access("/usr/bin/bash", os.R_OK): + os.symlink ("/usr/bin/bash", "/bin/bash") + except: + pass + except: + # This looks horrible, but all it does is catch every exception, + # and reraise those in the tuple check. This lets programming + # errors raise exceptions, while any runtime error will + # still result in a shell + (exc, val) = sys.exc_info()[0:2] + log.error(str(exc)+": "+str(val)) + if exc in (IndexError, ValueError, SyntaxError): + raise exc, val, sys.exc_info()[2] + + if anaconda.ksdata: + log.error("An error occurred trying to mount some or all of your system") + else: + ButtonChoiceWindow(screen, _("Rescue"), + _("An error occurred trying to mount some or all of your " + "system. Some of it may be mounted under %s.\n\n" + "Press <return> to get a shell. The system will reboot " + "automatically when you exit from the shell.") % (anaconda.rootPath,), + [_("OK")] ) + else: + if anaconda.ksdata and \ + anaconda.ksdata.reboot.action in [KS_REBOOT, KS_SHUTDOWN]: + log.info("No Linux partitions found") + screen.finish() + print(_("You don't have any Linux partitions. Rebooting.\n")) + sys.exit(0) + else: + ButtonChoiceWindow(screen, _("Rescue Mode"), + _("You don't have any Linux partitions. Press " + "return to get a shell. The system will reboot " + "automatically when you exit from the shell."), + [ _("OK") ], width = 50) + + msgStr = "" + + if rootmounted and not readOnly: + makeMtab(anaconda.rootPath, anaconda.storage) + try: + makeResolvConf(anaconda.rootPath) + except Exception, e: + log.error("error making a resolv.conf: %s" %(e,)) + msgStr = _("Your system is mounted under the %s directory.") % (anaconda.rootPath,) + ButtonChoiceWindow(screen, _("Rescue"), msgStr, [_("OK")] ) + + # we do not need ncurses anymore, shut them down + screen.finish() + + #create /etc/fstab in ramdisk, so it is easier to work with RO mounted filesystems + makeFStab() + + # run %post if we've mounted everything + if rootmounted and not readOnly and anaconda.ksdata: + from kickstart import runPostScripts + runPostScripts(anaconda) + + # start shell if reboot wasn't requested + if not anaconda.ksdata or \ + not anaconda.ksdata.reboot.action in [KS_REBOOT, KS_SHUTDOWN]: + runShell(screen, msgStr) + + sys.exit(0) |