# # 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 . # # Author(s): Mike Fulbright # Jeremy Katz # 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, updpct=0.05, pulse=False): return ProgressWindow(self.screen, title, text, total, updpct, pulse) 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.id.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, instClass): 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.id.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.isKickstart and anaconda.id.ksdata.scripts: from kickstart import runPostScripts runPostScripts(anaconda) else: runShell() sys.exit(0) screen = SnackScreen() anaconda.intf = RescueInterface(screen) if anaconda.isKickstart: if anaconda.id.ksdata.rescue and anaconda.id.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.isKickstart: root = disks[0] else: height = min (len (disks), 12) if height == 12: scroll = 1 else: scroll = 0 devList = [] for (device, relstr, arch) 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.isKickstart: 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.isKickstart: 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 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.id.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,)) fd = open("%s/.autorelabel" % anaconda.rootPath, "w+") fd.close() # 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.isKickstart: 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 to get a shell. The system will reboot " "automatically when you exit from the shell.") % (anaconda.rootPath,), [_("OK")] ) else: if anaconda.isKickstart and \ anaconda.id.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.id.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.isKickstart: from kickstart import runPostScripts runPostScripts(anaconda) # start shell if reboot wasn't requested if not anaconda.isKickstart or \ not anaconda.id.ksdata.reboot.action in [KS_REBOOT, KS_SHUTDOWN]: runShell(screen, msgStr) sys.exit(0)