# # image.py: Support methods for CD/DVD and ISO image installations. # # Copyright (C) 2007 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 . # import isys, iutil import os, os.path, stat, string, sys, sets from constants import * import gettext _ = lambda x: gettext.ldgettext("anaconda", x) import logging log = logging.getLogger("anaconda") _arch = iutil.getArch() def findIsoImages(path, messageWindow): flush = os.stat(path) files = os.listdir(path) arch = _arch discImages = {} for file in files: what = path + '/' + file if not isys.isIsoImage(what): continue try: isys.losetup("/dev/loop2", what, readOnly = 1) except SystemError: continue try: isys.mount("/dev/loop2", "/mnt/cdimage", fstype = "iso9660", readOnly = 1) for num in range(1, 10): if os.access("/mnt/cdimage/.discinfo", os.R_OK): f = open("/mnt/cdimage/.discinfo") try: f.readline() # skip timestamp f.readline() # skip release description discArch = string.strip(f.readline()) # read architecture discNum = getDiscNums(f.readline().strip()) except: discArch = None discNum = [ 0 ] f.close() if num not in discNum or discArch != arch: continue # if it's disc1, it needs to have images/install.img if (num == 1 and not os.access("/mnt/cdimage/images/install.img", os.R_OK)): log.warning("%s doesn't have a install.img, skipping" %(what,)) continue # we only install binary packages, so let's look for a # product/ dir and hope that this avoids getting # discs from the src.rpm set if not os.path.isdir("/mnt/cdimage/%s" %(productPath,)): log.warning("%s doesn't have binary RPMS, skipping" %(what,)) continue # warn user if images appears to be wrong size if os.stat(what)[stat.ST_SIZE] % 2048: rc = messageWindow(_("Warning"), _("The ISO image %s has a size which is not " "a multiple of 2048 bytes. This may mean " "it was corrupted on transfer to this computer." "\n\n" "It is recommended that you exit and abort your " "installation, but you can choose to continue if " "you think this is in error.") % (file,), type="custom", custom_icon="warning", custom_buttons= [_("_Exit installer"), _("_Continue")]) if rc == 0: sys.exit(0) discImages[num] = file isys.umount("/mnt/cdimage", removeDir=0) except SystemError: pass isys.unlosetup("/dev/loop2") return discImages def getDiscNums(line): # get the disc numbers for this disc nums = line.split(",") if nums == ['ALL']: # Treat "ALL" DVD as disc 1 return [1] discNums = [] for num in nums: discNums.append(int(num)) return discNums def getMediaId(path): if os.access("%s/.discinfo" % path, os.R_OK): f = open("%s/.discinfo" % path) newStamp = f.readline().strip() f.close() return newStamp else: return None # This mounts the directory containing the iso images, and places the # mount point in /mnt/isodir. def mountDirectory(methodstr, messageWindow): if methodstr.startswith("hd:"): method = methodstr[3:] if method.count(":") == 1: (device, path) = method.split(":") fstype = "auto" else: (device, fstype, path) = method.split(":") if not device.startswith("/dev/") and not device.startswith("UUID=") \ and not device.startswith("LABEL="): device = "/dev/%s" % device elif methodstr.startswith("nfsiso:"): device = methodstr[7:] fstype = "nfs" else: return # No need to mount it again. if os.path.ismount("/mnt/isodir"): return while True: try: isys.mount(device, "/mnt/isodir", fstype = fstype) break except SystemError, msg: log.error("couldn't mount ISO source directory: %s" % msg) ans = messageWindow(_("Couldn't Mount ISO Source"), _("An error occurred mounting the source " "device %s. This may happen if your ISO " "images are located on an advanced storage " "device like LVM or RAID, or if there was a " "problem mounting a partition. Click exit " "to abort the installation.") % (device,), type="custom", custom_icon="error", custom_buttons=[_("_Exit"), _("_Retry")]) if ans == 0: sys.exit(0) else: continue def mountImage(isodir, tree, discnum, messageWindow, discImages={}): if os.path.ismount(tree): raise SystemError, "trying to mount already-mounted iso image!" if discImages == {}: discImages = findIsoImages(isodir, messageWindow) while True: try: isoImage = "%s/%s" % (isodir, discImages[discnum]) isys.losetup("/dev/loop1", isoImage, readOnly = 1) isys.mount("/dev/loop1", tree, fstype = 'iso9660', readOnly = 1); break except: ans = messageWindow(_("Missing ISO 9660 Image"), _("The installer has tried to mount " "image #%s, but cannot find it on " "the hard drive.\n\n" "Please copy this image to the " "drive and click Retry. Click Exit " "to abort the installation.") % (discnum,), type="custom", custom_icon="warning", custom_buttons=[_("_Exit"), _("_Retry")]) if ans == 0: sys.exit(0) elif ans == 1: discImages = findIsoImages(isodir, messageWindow) return discImages # given groupset containing information about selected packages, use # the disc number info in the headers to come up with message describing # the required CDs # # dialog returns a value of 0 if user selected to abort install def presentRequiredMediaMessage(anaconda): reqcds = anaconda.backend.getRequiredMedia() # if only one CD required no need to pop up a message if len(reqcds) < 2: return # check what discs our currently mounted one provides if os.access("%s/.discinfo" % anaconda.backend.ayum.tree, os.R_OK): discNums = [] try: f = open("%s/.discinfo" % anaconda.backend.ayum.tree) stamp = f.readline().strip() descr = f.readline().strip() arch = f.readline().strip() discNums = getDiscNums(f.readline().strip()) f.close() except Exception, e: log.critical("Exception reading discinfo: %s" %(e,)) log.info("discNums is %s" %(discNums,)) haveall = 0 s = sets.Set(reqcds) t = sets.Set(discNums) if s.issubset(t): haveall = 1 if haveall == 1: return reqcds.sort() reqcdstr = "" for cdnum in reqcds: if cdnum == -99: # non-CD bits continue reqcdstr += "\t\t%s %s disc #%d\n" % (product.productName, product.productVersion, cdnum,) return anaconda.intf.messageWindow( _("Required Install Media"), _("The software you have selected to " "install will require the following discs:\n\n" "%s\nPlease " "have these ready before proceeding with " "the installation. If you need to abort " "the installation and exit please " "select \"Reboot\".") % (reqcdstr,), type="custom", custom_icon="warning", custom_buttons=[_("_Reboot"), _("_Back"), _("_Continue")]) # Find an attached CD/DVD drive with media in it that contains packages, # and return that device name. def scanForMedia(tree): drive = None for cdr in map(lambda d: "/dev/%s" % d, isys.cdromList()): try: if isys.mount(cdr, tree, fstype="iso9660", readOnly=1): continue except: continue if not verifyMedia(tree, 1): isys.umount(tree) continue drive = cdr break return drive def umountImage(tree, currentMedia): if currentMedia is not None: isys.umount(tree, removeDir=0) isys.unlosetup("/dev/loop1") def unmountCD(path, messageWindow): if not path: return while True: try: isys.umount(path, removeDir=0) break except Exception, e: log.error("exception in _unmountCD: %s" %(e,)) messageWindow(_("Error"), _("An error occurred unmounting the disc. " "Please make sure you're not accessing " "%s from the shell on tty2 " "and then click OK to retry.") % (path,)) def verifyMedia(tree, discnum, timestamp=None): if os.access("%s/.discinfo" % tree, os.R_OK): f = open("%s/.discinfo" % tree) newStamp = f.readline().strip() try: descr = f.readline().strip() except: descr = None try: arch = f.readline().strip() except: arch = None try: discs = getDiscNums(f.readline().strip()) except: discs = [ 0 ] f.close() if timestamp is not None: if newStamp == timestamp and arch == _arch and discnum in discs: return True else: if arch == _arch and discnum in discs: return True return False