# # iutil.py - generic install utility functions # # Erik Troan # # Copyright 1999-2003 Red Hat, Inc. # # This software may be freely redistributed under the terms of the GNU # library public license. # # You should have received a copy of the GNU Library Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # import os, isys, string, stat import signal import os.path from errno import * import rhpl import warnings import subprocess from flags import flags from constants import * from rhpl.translate import _ import re import logging log = logging.getLogger("anaconda") def execWithRedirect(command, argv, stdin = 0, stdout = 1, stderr = 2, searchPath = 0, root = '/'): def chroot (): os.chroot(root) if not searchPath and not os.access (command, os.X_OK): raise RuntimeError, command + " can not be run" if type(stdin) == type("string"): if os.access(stdin, os.R_OK): stdin = open(stdin) else: stdin = 0 if type(stdout) == type("string"): stdout = open(stdout, "w") if type(stderr) == type("string"): stderr = open(stderr, "w") try: proc = subprocess.Popen([command] + argv, stdin=stdin, stdout=stdout, stderr=stderr, preexec_fn=chroot, cwd=root) ret = proc.wait() except OSError, (errno, msg): raise RuntimeError, "Error running " + command + ": " + msg return ret def execWithCapture(command, argv, stdin = 0, stderr = 2, root='/'): def chroot(): os.chroot(root) if type(stdin) == type("string"): if os.access(stdin, os.R_OK): stdin = open(stdin) else: stdin = 0 if type(stderr) == type("string"): stderr = open(stderr, "w") try: pipe = subprocess.Popen([command] + argv, stdin=stdin, stdout=subprocess.PIPE, stderr=stderr, preexec_fn=chroot, cwd=root) except OSError, (errno, msg): raise RuntimeError, "Error running " + command + ": " + msg rc = pipe.stdout.read() pipe.wait() return rc def execConsole(): try: proc = subprocess.Popen(["/bin/sh"]) proc.wait() except OSError, (errno, msg): raise RuntimeError, "Error running /bin/sh: " + msg # return size of directory (and subdirs) in kilobytes def getDirSize(dir): def getSubdirSize(dir): # returns size in bytes mydev = os.lstat(dir)[stat.ST_DEV] dsize = 0 for f in os.listdir(dir): curpath = '%s/%s' % (dir, f) sinfo = os.lstat(curpath) if stat.S_ISDIR(sinfo[stat.ST_MODE]): if mydev == sinfo[stat.ST_DEV]: dsize += getSubdirSize(curpath) elif stat.S_ISREG(sinfo[stat.ST_MODE]): dsize += sinfo[stat.ST_SIZE] else: pass return dsize return getSubdirSize(dir)/1024 # this is in kilobytes - returns amount of RAM not used by /tmp def memAvailable(): tram = memInstalled() ramused = getDirSize("/tmp") if os.path.isdir("/tmp/ramfs"): ramused += getDirSize("/tmp/ramfs") return tram - ramused # this is in kilobytes def memInstalled(): f = open("/proc/meminfo", "r") lines = f.readlines() f.close() for l in lines: if l.startswith("MemTotal:"): fields = string.split(l) mem = fields[1] break return int(mem) # try to keep 2.4 kernel swapper happy! def swapSuggestion(quiet=0): mem = memInstalled()/1024 mem = ((mem/16)+1)*16 if not quiet: log.info("Detected %sM of memory", mem) if mem < 128: minswap = 96 maxswap = 192 else: if mem > 2000: minswap = 1000 maxswap = 2000 + mem else: minswap = mem maxswap = 2*mem if not quiet: log.info("Swap attempt of %sM to %sM", minswap, maxswap) return (minswap, maxswap) # this is a mkdir that won't fail if a directory already exists and will # happily make all of the directories leading up to it. def mkdirChain(dir): try: os.makedirs(dir, 0755) except OSError, (errno, msg): try: if errno == EEXIST and stat.S_ISDIR(os.stat(dir).st_mode): return except: pass log.error("could not create directory %s: %s" % (dir, msg)) def swapAmount(): f = open("/proc/meminfo", "r") lines = f.readlines() f.close() for l in lines: if l.startswith("SwapTotal:"): fields = string.split(l) return int(fields[1]) return 0 def copyDeviceNode(src, dest): """Copies the device node at src to dest by looking at the type of device, major, and minor of src and doing a new mknod at dest""" filestat = os.lstat(src) mode = filestat[stat.ST_MODE] if stat.S_ISBLK(mode): type = stat.S_IFBLK elif stat.S_ISCHR(mode): type = stat.S_IFCHR else: # XXX should we just fallback to copying normally? raise RuntimeError, "Tried to copy %s which isn't a device node" % (src,) os.mknod(dest, mode | type, filestat.st_rdev) # make the device-mapper control node def makeDMNode(root="/"): major = minor = None for (fn, devname, val) in ( ("/proc/devices", "misc", "major"), ("/proc/misc", "device-mapper", "minor") ): f = open(fn) lines = f.readlines() f.close() for line in lines: try: (num, dev) = line.strip().split(" ") except: continue if dev == devname: s = "%s = int(num)" %(val,) exec s break # print "major is %s, minor is %s" %(major, minor) if major is None or minor is None: return mkdirChain(root + "/dev/mapper") try: os.mknod(root + "/dev/mapper/control", stat.S_IFCHR | 0600, os.makedev(major, minor)) except: pass # make some miscellaneous character device nodes def makeCharDeviceNodes(): for dev in ["input/event0", "input/event1", "input/event2", "input/event3"]: isys.makeDevInode(dev, "/dev/%s" % (dev,)) # make the device nodes for all of the drives on the system def makeDriveDeviceNodes(): hardDrives = isys.hardDriveDict() for drive in hardDrives.keys(): if drive.startswith("mapper"): continue isys.makeDevInode(drive, "/dev/%s" % (drive,)) if drive.startswith("hd"): num = 32 elif drive.startswith("dasd"): num = 4 else: num = 15 if (drive.startswith("cciss") or drive.startswith("ida") or drive.startswith("rd") or drive.startswith("sx8")): sep = "p" else: sep = "" for i in range(1, num): dev = "%s%s%d" % (drive, sep, i) isys.makeDevInode(dev, "/dev/%s" % (dev,)) cdroms = isys.cdromList() for drive in cdroms: isys.makeDevInode(drive, "/dev/%s" % (drive,)) tapeDrives = isys.tapeDriveList() for drive in tapeDrives: # make all tape device variants (stX,stXl,stXm,stXa,nstX,nstXl,nstXm,nstXa) for prefix in ("", "n"): for postfix in ("", "l", "m", "a"): device = "%s%s%s" % (prefix, drive, postfix) isys.makeDevInode(device, "/dev/%s" % (device,)) for mdMinor in range(0, 32): md = "md%d" %(mdMinor,) isys.makeDevInode(md, "/dev/%s" %(md,)) # make the node for the device mapper makeDMNode() # this is disgusting and I feel very dirty def hasiSeriesNativeStorage(): if rhpl.getArch() != "ppc": return f = open("/proc/modules", "r") lines = f.readlines() f.close() for line in lines: if line.startswith("ibmsis"): return 1 if line.startswith("ipr"): return 1 return 0 # return the ppc machine variety type def getPPCMachine(): if rhpl.getArch() != "ppc": return 0 machine = rhpl.getPPCMachine() if machine is None: log.warning("Unable to find PowerPC machine type") elif machine == 0: log.warning("Unknown PowerPC machine type: %s" %(machine,)) return machine # return the pmac machine id def getPPCMacID(): machine = None if rhpl.getArch() != "ppc": return 0 if getPPCMachine() != "PMac": return 0 f = open('/proc/cpuinfo', 'r') lines = f.readlines() f.close() for line in lines: if line.find('machine') != -1: machine = line.split(':')[1] machine = machine.strip() return machine log.warning("No Power Mac machine id") return 0 # return the pmac generation def getPPCMacGen(): # XXX: should NuBus be here? pmacGen = ['OldWorld', 'NewWorld', 'NuBus'] if rhpl.getArch() != "ppc": return 0 if getPPCMachine() != "PMac": return 0 f = open('/proc/cpuinfo', 'r') lines = f.readlines() f.close() gen = None for line in lines: if line.find('pmac-generation') != -1: gen = line.split(':')[1] break if gen is None: log.warning("Unable to find pmac-generation") for type in pmacGen: if gen.find(type) != -1: return type log.warning("Unknown Power Mac generation: %s" %(gen,)) return 0 # return if pmac machine is it an iBook/PowerBook def getPPCMacBook(): if rhpl.getArch() != "ppc": return 0 if getPPCMachine() != "PMac": return 0 f = open('/proc/cpuinfo', 'r') lines = f.readlines() f.close() for line in lines: if not string.find(string.lower(line), 'book') == -1: return 1 return 0 cell = None def isCell(): global cell if cell is not None: return cell cell = False if rhpl.getArch() != "ppc": return cell f = open('/proc/cpuinfo', 'r') lines = f.readlines() f.close() for line in lines: if not string.find(line, 'Cell') == -1: cell = True return cell mactel = None # return True if this is one of the Intel-based Apple Macs def isMactel(): global mactel if mactel is not None: return mactel if rhpl.getArch() not in ("x86_64", "i386"): mactel = False elif not os.path.exists("/usr/sbin/dmidecode"): mactel = False else: buf = execWithCapture("/usr/sbin/dmidecode", ["dmidecode", "-s", "system-manufacturer"]) if buf.lower().find("apple") != -1: mactel = True else: mactel = False return mactel efi = None def isEfi(): global efi if efi is not None: return efi if not os.path.exists("/sys/firmware/efi"): efi = False else: efi = True return efi def cpuFeatureFlags(): """Convenience function to get CPU feature flags from /proc/cpuinfo.""" if rhpl.getArch() not in ("i386", "x86_64"): return False f = open("/proc/cpuinfo", "r") lines = f.readlines() f.close() for line in lines: if not line.startswith("flags"): continue # get the actual flags flags = line[:-1].split(":", 1)[1] # and split them flst = flags.split(" ") return flst return [] def valid_dm_name(name): """ Tests whether name is a good name for a dm device Sometimes the user could call the device 'devp3' and then how is bootloaderInfo.getDiskPart() to tell it's not a partition? """ if re.search("p\d{1,2}$", name): return False return True def writeRpmPlatform(root="/"): import rhpl.arch if flags.test: return if os.access("%s/etc/rpm/platform" %(root,), os.R_OK): return if not os.access("%s/etc/rpm" %(root,), os.X_OK): os.mkdir("%s/etc/rpm" %(root,)) myarch = rhpl.arch.canonArch # now allow an override with rpmarch=i586 on the command line (#101971) if flags.targetarch != None: myarch = flags.targetarch # now make the current install believe it, too rhpl.arch.canonArch = myarch f = open("%s/etc/rpm/platform" %(root,), 'w+') f.write("%s-redhat-linux\n" %(myarch,)) f.close() # FIXME: writing /etc/rpm/macros feels wrong somehow # temporary workaround for #92285 if os.access("%s/etc/rpm/macros" %(root,), os.R_OK): return if not (myarch.startswith("ppc64") or myarch in ("s390x", "sparc64", "x86_64", "ia64")): return f = open("%s/etc/rpm/macros" %(root,), 'w+') f.write("%_transaction_color 3\n") f.close() ## Check to see if we are in a xen environment. # def inXen(): if os.path.exists("/proc/xen/capabilities"): return True return False ## Check to see if we are in a vmware environment. # def inVmware(): lspci = ["lspci", "-vvv"] # only the very verbose show the VMware stuff :) try: proc = subprocess.Popen(lspci, stdout = subprocess.PIPE) (out, err) = proc.communicate() except: return False if "VMware" in out: return True return False def getScsiDeviceByWwpnLunid(wwpn, lunid): found = 0 for tgt in os.listdir('/sys/class/fc_transport'): if tgt[:6] != "target": continue dir= '/sys/class/fc_transport/%s' % tgt f = open('%s/port_name' % dir, "r") tgt_wwpn = f.readline() tgt_wwpn = tgt_wwpn.rstrip("\n") if tgt_wwpn.startswith("0x"): tgt_wwpn = tgt_wwpn[2:] tgt_wwpn = tgt_wwpn.upper() f.close() ## check the first match only if tgt_wwpn == wwpn: found = 1 break if found == 0: return "" scsi_hctl="%s:%s" % (tgt[6:], lunid) found = 0 for tgt in os.listdir('/sys/block'): devf = '/sys/block/%s/device' % tgt if not os.path.exists(devf): continue devf = os.readlink(devf) devf = os.path.basename(devf) if devf == scsi_hctl: found = 1 break if found == 0: tgt = "" return tgt def writeReiplMethod(reipl_path, reipl_type): filename = "%s/reipl_type" % (reipl_path,) try: f = open(filename, "w") except Exception, e: message = "Error: On open, cannot set reIPL method to %s (%s: %s)" % (reipl_type,filename,e,) log.warning(message) raise Exception (message) try: f.write(reipl_type) f.flush() except Exception, e: message = "Error: On write, cannot set reIPL method to %s (%s: %s)" % (reipl_type,filename,e,) log.warning(message) raise Exception (message) try: f.close() except Exception, e: message = "Error: On close, cannot set reIPL method to %s (%s: %s)" % (reipl_type,filename,e,) log.warning(message) raise Exception (message) def reIPLonCCW(iplsubdev, reipl_path): device = "" try: iplbits = re.split ('([0-9]+)', iplsubdev) if len (iplbits) != 3: message = "Error: %s splits into %s but not like we expect" % (iplsubdev,iplbits,) log.warning(message) raise Exception (message) device = os.readlink("/sys/block/" + iplbits[0] + "/device").split('/')[-1] writeReiplMethod(reipl_path, 'ccw') try: f = open("%s/ccw/device" % (reipl_path,), "w") f.write(device) f.close() except Exception, e: message = "Error: Could not set %s as reIPL device (%s)" % (device,e,) log.warning(message) raise Exception (message) try: f = open("%s/ccw/loadparm" % (reipl_path,), "w") f.write("\n") f.close() except Exception, e: message = "Error: Could not reset loadparm (%s)" % (e,) log.warning(message) raise Exception (message) try: f = open("%s/ccw/parm" % (reipl_path,), "w") f.write("\n") f.close() except Exception, e: message = "Warning: Could not reset parm (%s)" % (e,) log.warning(message) # do NOT raise an exception since this might not exist or not be writable except Exception, e: try: message = e.args[0] except: message = e.__str__ () return (message, (_("After shutdown, please perform a manual IPL from DASD device %s to continue " "installation") % (device,))) return None def reIPLonFCP(iplsubdev, reipl_path): fcpvalue = { "device": "", "wwpn": "", "lun": "" } try: iplbits = re.split ('([0-9]+)', iplsubdev) if len (iplbits) != 3: message = "Error: %s splits into %s but not like we expect" % (iplsubdev,iplbits,) log.warning(message) raise Exception (message) syspath = "/sys/block/" + iplbits[0] + "/device" fcpprops = [ ("hba_id", "device"), ("wwpn", "wwpn"), ("fcp_lun", "lun") ] # Read in values to change. # This way, if we can't set FCP mode, we can tell the user what to manually reboot to. for (syspath_property, reipl_property) in fcpprops: try: f = open(syspath + "/" + syspath_property, "r") value = f.read().strip() fcpvalue[reipl_property] = value f.close() except Exception, e: message = "Error: reading FCP property %s for reIPL (%s)" % (syspath_property,e,) log.warning(message) raise Exception (message) writeReiplMethod(reipl_path, 'fcp') # Write out necessary parameters. for (syspath_property, reipl_property) in fcpprops: try: f = open("%s/fcp/%s" % (reipl_path, reipl_property,), "w") f.write(fcpvalue[reipl_property]) f.close() except Exception, e: message = "Error: writing FCP property %s for reIPL (%s)" % (reipl_property,e,) log.warning(message) raise Exception (message) defaultprops = [ ("bootprog", "0"), ("br_lba", "0") ] # Write out default parameters. for (reipl_property, default_value) in defaultprops: try: f = open("%s/fcp/%s" % (reipl_path, reipl_property,), "w") f.write (default_value) f.close() except Exception, e: message = "Error: writing default FCP property %s for reIPL (%s)" % (reipl_property,e,) log.warning(message) raise Exception (message) except Exception, e: try: message = e.args[0] except: message = e.__str__ () return (message, (_("After shutdown, please perform a manual IPL from FCP %(device)s with WWPN %(wwpn)s " "and LUN %(lun)s to continue installation") % (fcpvalue))) return None def reIPL(anaconda, loader_pid): instruction = _("After shutdown, please perform a manual IPL from the device " "now containing /boot to continue installation") reipl_path = "/sys/firmware/reipl" iplfs = anaconda.id.fsset.getEntryByMountPoint("/boot") if iplfs is None: iplfs = anaconda.id.fsset.getEntryByMountPoint("/") if iplfs is None: message = _("Could not get information for mount point /boot or /") log.warning(message) return (message, instruction) try: ipldev = iplfs.device.device except: ipldev = None if ipldev is None: message = _("Error determining mount point type") log.warning(message) return (message, instruction) message = (_("The mount point /boot or / is on a disk that we are not familiar with"), instruction) if ipldev.startswith("dasd"): message = reIPLonCCW(ipldev, reipl_path) elif ipldev.startswith("sd"): message = reIPLonFCP(ipldev, reipl_path) # the final return is either None if reipl configuration worked (=> reboot), # or a two-item list with errorMessage and rebootInstr (=> shutdown) return message