#
# bootloaderInfo.py - bootloader config object used in creation of new
#                     bootloader configs.  Originally from anaconda
#
# Jeremy Katz <[email protected]>
# Erik Troan <[email protected]>
# Peter Jones <[email protected]>
#
# Copyright 2005 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, sys
import crypt
import random
import butil
import string

from lilo import LiloConfigFile
from rhpl.log import log
from rhpl.translate import _, N_
import rhpl.executil

import booty
import checkbootloader

dosFilesystems = ('FAT', 'fat16', 'fat32', 'ntfs', 'hpfs')

if os.path.exists('edd'):
   sys.path.append('edd/')
else:
   sys.path.append('/usr/lib/bootloader')

if butil.getArch() == "i386":
   import edd

def doesDualBoot():
   if butil.getArch() == "i386" or butil.getArch() == "x86_64":
       return 1
   return 0

# hack and a half
# there's no guarantee that data is written to the disk and grub
# reads both the filesystem and the disk.  suck.
def syncDataToDisk(dev, mntpt, instRoot = "/"):
   import isys, fsset
   isys.sync()
   isys.sync()
   isys.sync()

   # and xfs is even more "special" (#117968)
   if fsset.isValidXFS(dev):
       rhpl.executil.execWithRedirect( "/usr/sbin/xfs_freeze",
                                       ["/usr/sbin/xfs_freeze", "-f", mntpt],
                                       stdout = "/dev/tty5",
                                       stderr = "/dev/tty5",
                                       root = instRoot)
       rhpl.executil.execWithRedirect( "/usr/sbin/xfs_freeze",
                                       ["/usr/sbin/xfs_freeze", "-u", mntpt],
                                       stdout = "/dev/tty5",
                                       stderr = "/dev/tty5",
                                       root = instRoot)

class BootyNoKernelWarning:
   def __init__ (self, value=""):
       self.value = value

   def __str__ (self):
       return self.value

class KernelArguments:

   def get(self):
       return self.args

   def set(self, args):
       self.args = args

   def chandevget(self):
       return self.cargs

   def chandevset(self, args):
       self.cargs = args

   def append(self, args):
       if self.args:
           # don't duplicate the addition of an argument (#128492)
           if self.args.find(args) != -1:
               return
           self.args = self.args + " "
       self.args = self.args + "%s" % (args,)


   def __init__(self):
       str = ""

       if butil.getArch() == "s390":
           self.cargs = []
           f = open("/tmp/install.cfg")
           lines = f.readlines()
           for line in lines:
               try:
                   (vname,vparm) = line.split('=', 1)
                   vname = vname.strip()
                   vparm = vparm.replace('"','')
                   vparm = vparm.strip()
                   if vname == "DASD":
                       str = str + "dasd=" + vparm
                   if vname == "CHANDEV":
                       self.cargs.append(vparm)
                   if vname == "QETHPARM":
                       self.cargs.append(vparm)
               except Exception, e:
                   pass

       # look for kernel arguments we know should be preserved and add them
       ourargs = ["speakup_synth=", "apic", "noapic", "apm=", "ide=nodma", "noht", "acpi=", "video="]
       f = open("/proc/cmdline")
       cmdline = f.read()[:-1]
       f.close()
       cmdlineargs = cmdline.split(" ")
       for arg in cmdlineargs:
           for check in ourargs:
               if arg.startswith(check):
                   if str: str = str + " "
                   str = str + arg

       self.args = str

class BootImages:
   # returns dictionary of (label, longlabel, devtype) pairs indexed by device
   def getImages(self):
       # return a copy so users can modify it w/o affecting us

       dict = {}
       for key in self.images.keys():
           dict[key] = self.images[key]

       return dict

   def setImageLabel(self, dev, label, setLong = 0):
       if setLong:
           self.images[dev] = (self.images[dev][0], label,
                               self.images[dev][2])
       else:
           self.images[dev] = (label, self.images[dev][1],
                               self.images[dev][2])


   # default is a device
   def setDefault(self, default):
       self.default = default

   def getDefault(self):
       return self.default

   # XXX this has internal anaconda-ish knowledge.  ick
   def setup(self, diskSet, fsset):
       devices = {}
       devs = self.availableBootDevices(diskSet, fsset)
       for (dev, type) in devs:
           devices[dev] = 1

       # These partitions have disappeared
       for dev in self.images.keys():
           if not devices.has_key(dev): del self.images[dev]

       # These have appeared
       for (dev, type) in devs:
           if not self.images.has_key(dev):
               if type in dosFilesystems and doesDualBoot():
                   self.images[dev] = ("Other", "Other", type)
               elif type in ("hfs", "hfs+") and butil.getPPCMachine() == "PMac":
                   self.images[dev] = ("Other", "Other", type)
               else:
                   self.images[dev] = (None, None, type)


       if not self.images.has_key(self.default):
           entry = fsset.getEntryByMountPoint('/')
           self.default = entry.device.getDevice()
           (label, longlabel, type) = self.images[self.default]
           if not label:
               self.images[self.default] = ("linux",
                                            butil.getProductName(), type)

   # XXX more internal anaconda knowledge
   def availableBootDevices(self, diskSet, fsset):
       devs = []
       foundDos = 0
       for (dev, type) in diskSet.partitionTypes():
           if type in dosFilesystems and not foundDos and doesDualBoot():
               import isys
               import partedUtils

               isys.makeDevInode(dev, '/tmp/' + dev)
               part = partedUtils.get_partition_by_name(diskSet.disks, dev)
               if part.native_type not in partedUtils.dosPartitionTypes:
                   continue

               try:
                   bootable = isys.checkBoot('/tmp/' + dev)
                   devs.append((dev, type))
                   foundDos = 1
               except Exception, e:
                   log("exception checking %s: %s" %(dev, e))
                   pass
           elif ((type == 'ntfs' or type =='hpfs') and not foundDos
                 and doesDualBoot()):
               devs.append((dev, type))
               # maybe questionable, but the first ntfs or fat is likely to
               # be the correct one to boot with XP using ntfs
               foundDos = 1
           elif type in ('hfs', 'hfs+') and butil.getPPCMachine() == "PMac":
               import isys
               import partedUtils

               part = partedUtils.get_partition_by_name(diskSet.disks, dev)
               if partedUtils.get_flags(part) != "boot":
                   devs.append((dev, type))

       slash = fsset.getEntryByMountPoint('/')
       if not slash or not slash.device or not slash.fsystem:
           raise ValueError, ("Trying to pick boot devices but do not have a "
                              "sane root partition.  Aborting install.")
       devs.append((slash.device.getDevice(), slash.fsystem.getName()))

       devs.sort()

       return devs



   def __init__(self):
       self.default = None
       self.images = {}


class bootloaderInfo:
   def setUseGrub(self, val):
       pass

   def useGrub(self):
       return self.useGrubVal

   def setForceLBA(self, val):
       pass

   def setPassword(self, val, isCrypted = 1):
       pass

   def getPassword(self):
       pass

   def getDevice(self):
       return self.device

   def setDevice(self, device):
       self.device = device

       (dev, part) = getDiskPart(device)
       if part is None:
           self.defaultDevice = "mbr"
       else:
           self.defaultDevice = "partition"

   # XXX need to abstract out the requirement for a fsset to be able
   # to get it "on the fly" on a running system as well as being able
   # to get it how we do now from anaconda.  probably by having the
   # first thing in the chain create a "fsset" object that has the
   # dictionary of mounted filesystems since that's what we care about
   def getBootloaderConfig(self, instRoot, fsset, bl, langs, kernelList,
                           chainList, defaultDev):
       images = bl.images.getImages()

       # on upgrade read in the lilo config file
       lilo = LiloConfigFile ()
       self.perms = 0600
       if os.access (instRoot + self.configfile, os.R_OK):
           self.perms = os.stat(instRoot + self.configfile)[0] & 0777
           lilo.read (instRoot + self.configfile)
           os.rename(instRoot + self.configfile,
                     instRoot + self.configfile + '.rpmsave')
       # if it's an absolute symlink, just get it out of our way
       elif (os.path.islink(instRoot + self.configfile) and
             os.readlink(instRoot + self.configfile)[0] == '/'):
           os.rename(instRoot + self.configfile,
                     instRoot + self.configfile + '.rpmsave')

       # Remove any invalid entries that are in the file; we probably
       # just removed those kernels.
       for label in lilo.listImages():
           (fsType, sl, path, other) = lilo.getImage(label)
           if fsType == "other": continue

           if not os.access(instRoot + sl.getPath(), os.R_OK):
               lilo.delImage(label)

       lilo.addEntry("prompt", replace = 0)
       lilo.addEntry("timeout", "20", replace = 0)

       rootDev = fsset.getEntryByMountPoint("/").device.getDevice()
       if not rootDev:
           raise RuntimeError, "Installing lilo, but there is no root device"

       if rootDev == defaultDev:
           lilo.addEntry("default", kernelList[0][0])
       else:
           lilo.addEntry("default", chainList[0][0])

       for (label, longlabel, version) in kernelList:
           kernelTag = "-" + version
           kernelFile = self.kernelLocation + "vmlinuz" + kernelTag

           try:
               lilo.delImage(label)
           except IndexError, msg:
               pass

           sl = LiloConfigFile(imageType = "image", path = kernelFile)

           initrd = booty.makeInitrd (kernelTag, instRoot)

           sl.addEntry("label", label)
           if os.access (instRoot + initrd, os.R_OK):
               sl.addEntry("initrd", "%sinitrd%s.img" %(self.kernelLocation,
                                                        kernelTag))

           sl.addEntry("read-only")

           append = "%s" %(self.args.get(),)
           realroot = getRootDevName(initrd, fsset, rootDev, instRoot)
           if not realroot.startswith("LABEL="):
               sl.addEntry("root", '/dev/' + rootDev)
           else:
               if len(append) > 0:
                   append = "%s root=%s" %(append,realroot)
               else:
                   append = "root=%s" %(realroot,)

           if len(append) > 0:
               sl.addEntry('append', '"%s"' % (append,))

           lilo.addImage (sl)

       for (label, longlabel, device) in chainList:
           if ((not label) or (label == "")):
               continue
           try:
               (fsType, sl, path, other) = lilo.getImage(label)
               lilo.delImage(label)
           except IndexError:
               sl = LiloConfigFile(imageType = "other",
                                   path = "/dev/%s" %(device))
               sl.addEntry("optional")

           sl.addEntry("label", label)
           lilo.addImage (sl)

       # Sanity check #1. There could be aliases in sections which conflict
       # with the new images we just created. If so, erase those aliases
       imageNames = {}
       for label in lilo.listImages():
           imageNames[label] = 1

       for label in lilo.listImages():
           (fsType, sl, path, other) = lilo.getImage(label)
           if sl.testEntry('alias'):
               alias = sl.getEntry('alias')
               if imageNames.has_key(alias):
                   sl.delEntry('alias')
               imageNames[alias] = 1

       # Sanity check #2. If single-key is turned on, go through all of
       # the image names (including aliases) (we just built the list) and
       # see if single-key will still work.
       if lilo.testEntry('single-key'):
           singleKeys = {}
           turnOff = 0
           for label in imageNames.keys():
               l = label[0]
               if singleKeys.has_key(l):
                   turnOff = 1
               singleKeys[l] = 1
           if turnOff:
               lilo.delEntry('single-key')

       return lilo

   def write(self, instRoot, fsset, bl, langs, kernelList, chainList,
                 defaultDev, justConfig, intf = None):
       if len(kernelList) >= 1:
           config = self.getBootloaderConfig(instRoot, fsset, bl, langs,
                                             kernelList, chainList,
                                             defaultDev)
           config.write(instRoot + self.configfile, perms = self.perms)
       else:
           self.noKernelsWarn(intf)

       return ""

   # XXX in the future we also should do some validation on the config
   # file that's already there
   # XXX concept of the intf isn't very well defined outside of anaconda...
   # probably should just pass back up an error
   def noKernelsWarn(self, intf):
       raise BootyNoKernelWarning

   def getArgList(self):
       args = []

       if self.defaultDevice is None:
           args.append("--location=none")
           return args

       args.append("--location=%s" % (self.defaultDevice,))
       args.append("--driveorder=%s" % (",".join(self.drivelist)))

       if self.args.get():
           args.append("--append=\"%s\"" %(self.args.get()))

       return args

   def writeKS(self, f):
       f.write("bootloader")
       for arg in self.getArgList():
           f.write(" " + arg)
       f.write("\n")

   def createDriveList(self):
       # create a drive list that we can use for drive mappings
       # XXX has anaconda internals knowledge
       import isys
       import flags
       drives = isys.hardDriveDict().keys()
       drives.sort (isys.compareDrives)

       # now filter out all of the removable media unless expert mode
       rc = []
       for drive in drives:
           if not isys.driveIsRemovable(drive) or flags.flags.expert:
               rc.append(drive)
       return rc

   def __init__(self):
       self.args = KernelArguments()
       self.images = BootImages()
       self.device = None
       self.defaultDevice = None  # XXX hack, used by kickstart
       self.useGrubVal = 0      # only used on x86
       self.configfile = None
       self.kernelLocation = "/boot/"
       self.forceLBA32 = 0
       self.password = None
       self.pure = None
       self.above1024 = 0

       # this has somewhat strange semantics.  if 0, act like a normal
       # "install" case.  if 1, update lilo.conf (since grubby won't do that)
       # and then run lilo or grub only.
       # XXX THIS IS A HACK.  implementation details are only there for x86
       self.doUpgradeOnly = 0
       self.kickstart = 0

       self.drivelist = self.createDriveList()

       from flags import flags
       if flags.serial != 0:
           # now look at /proc/cmdline to pull any serial console
           # args
           f = open("/proc/cmdline", "r")
           cmdline = f.read()[:-1]
           f.close()

           options = ""
           device = None
           cmdlineargs = cmdline.split(" ")
           for arg in cmdlineargs:
               # found a console argument
               if arg.startswith("console="):
                   (foo, console) = arg.split("=")
                   # the options are everything after the comma
                   comma = console.find(",")
                   if comma != -1:
                       options = console[comma:]
                       device = console[:comma]
                   else:
                       options = ""
                       device = console

           if device is None:
               self.serialDevice = "ttyS0"
               self.serialOptions = ""
           else:
               self.serialDevice = device
               # don't keep the comma in the options
               self.serialOptions = options[1:]

           self.args.append("console=%s%s" %(self.serialDevice, options))

           self.serial = 1
       else:
           self.serial = 0
           self.serialDevice = None
           self.serialOptions = None

       if flags.virtpconsole is not None:
           if flags.virtpconsole.startswith("/dev/"):
               con = flags.virtpconsole[5:]
           else:
               con = flags.virtpconsole
           self.args.append("console=%s" %(con,))


class ia64BootloaderInfo(bootloaderInfo):
   # XXX wouldn't it be nice to have a real interface to use efibootmgr from?
   def removeOldEfiEntries(self, instRoot):
       p = os.pipe()
       rhpl.executil.execWithRedirect('/usr/sbin/efibootmgr', ["efibootmgr"],
                              root = instRoot, stdout = p[1])
       os.close(p[1])

       c = os.read(p[0], 1)
       buf = c
       while (c):
           c = os.read(p[0], 1)
           buf = buf + c
       os.close(p[0])
       lines = string.split(buf, '\n')
       for line in lines:
           fields = string.split(line)
           if len(fields) < 2:
               continue
           if string.join(fields[1:], " ") == butil.getProductName():
               entry = fields[0][4:8]
               rhpl.executil.execWithRedirect('/usr/sbin/efibootmgr',
                                      ["efibootmgr", "-b", entry, "-B"],
                                      root = instRoot,
                                      stdout="/dev/tty5", stderr="/dev/tty5")

   def getBootloaderConfig(self, instRoot, fsset, bl, langs, kernelList,
                           chainList, defaultDev):
       config = bootloaderInfo.getBootloaderConfig(self, instRoot, fsset,
                                                   bl, langs,
                                                   kernelList, chainList,
                                                   defaultDev)
       # altix boxes need relocatable (#120851)
       config.addEntry("relocatable")

       return config

   def writeLilo(self, instRoot, fsset, bl, langs, kernelList,
                 chainList, defaultDev, justConfig):
       config = self.getBootloaderConfig(instRoot, fsset, bl, langs,
                                         kernelList, chainList, defaultDev)
       config.write(instRoot + self.configfile, perms = self.perms)

       return ""

   def write(self, instRoot, fsset, bl, langs, kernelList, chainList,
                 defaultDev, justConfig, intf):
       if len(kernelList) >= 1:
           str = self.writeLilo(instRoot, fsset, bl, langs, kernelList,
                                chainList, defaultDev, justConfig)
       else:
           self.noKernelsWarn(intf)

       bootdev = fsset.getEntryByMountPoint("/boot/efi").device.getDevice()
       if not bootdev:
           bootdev = fsset.getEntryByDeviceName("sda1").device.getDevice()

       if not os.access(instRoot + "/etc/elilo.conf", os.R_OK):
           os.symlink("../" + self.configfile, instRoot + "/etc/elilo.conf")

       ind = len(bootdev)
       try:
           while (bootdev[ind-1] in string.digits):
               ind = ind - 1
       except IndexError:
           ind = len(bootdev) - 1

       bootdisk = bootdev[:ind]
       bootpart = bootdev[ind:]
       if (bootdisk.startswith('ida/') or bootdisk.startswith('cciss/') or
           bootdisk.startswith('rd/') or bootdisk.startswith('sx8/')):
           bootdisk = bootdisk[:-1]

       self.removeOldEfiEntries(instRoot)

       argv = [ "/usr/sbin/efibootmgr", "-c" , "-w", "-L",
                butil.getProductName(), "-d", "/dev/%s" % bootdisk,
                "-p", bootpart, "-l", "EFI\\redhat\\elilo.efi" ]
       rhpl.executil.execWithRedirect(argv[0], argv, root = instRoot,
                              stdout = "/dev/tty5",
                              stderr = "/dev/tty5")


   def __init__(self):
       bootloaderInfo.__init__(self)
       self.useGrubVal = 1
       self.kernelLocation = ""
       self.configfile = "/boot/efi/EFI/redhat/elilo.conf"


class x86BootloaderInfo(bootloaderInfo):
   def setPassword(self, val, isCrypted = 1):
       if not val:
           self.password = val
           self.pure = val
           return

       if isCrypted and self.useGrubVal == 0:
           log("requested crypted password with lilo; ignoring")
           self.pure = None
           return
       elif isCrypted:
           self.password = val
           self.pure = None
       else:
           salt = "$1$"
           saltLen = 8

           for i in range(saltLen):
               salt = salt + random.choice (string.letters +
                                            string.digits + './')

           self.password = crypt.crypt (val, salt)
           self.pure = val

   def getPassword (self):
       return self.pure

   def setForceLBA(self, val):
       self.forceLBA32 = val

   def setUseGrub(self, val):
       self.useGrubVal = val

   def getPhysicalDevices(self, device):
       # This finds a list of devices on which the given device name resides.
       # Accepted values for "device" are raid1 md devices (i.e. "md0"),
       # physical disks ("hda"), and real partitions on physical disks
       # ("hda1").  Volume groups/logical volumes are not accepted.
       #
       # XXX this has internal anaconda-ish knowledge.  ick.
       import isys
       import lvm

       if string.split(device, '/', 1)[0] in map (lambda vg: vg[0],
                                                  lvm.vglist()):
           return []

       if device.startswith('md'):
           bootable = 0
           parts = checkbootloader.getRaidDisks(device, 1, stripPart=0)
           parts.sort()
           return parts

       return [device]

   def writeGrub(self, instRoot, fsset, bl, langs, kernelList, chainList,
                 defaultDev, justConfigFile):
       if len(kernelList) < 1:
           return ""

       images = bl.images.getImages()
       rootDev = fsset.getEntryByMountPoint("/").device.getDevice()

       if not os.path.isdir(instRoot + '/boot/grub/'):
           os.mkdir(instRoot + '/boot/grub', 0755)

       # XXX old config file should be read here for upgrade

       cf = '/boot/grub/grub.conf'
       self.perms = 0600
       if os.access (instRoot + cf, os.R_OK):
           self.perms = os.stat(instRoot + cf)[0] & 0777
           os.rename(instRoot + cf,
                     instRoot + cf + '.rpmsave')

       grubTarget = bl.getDevice()
       target = "mbr"
       if grubTarget[-1].isdigit() and not grubTarget.startswith('md'):
           target = "partition"

       f = open(instRoot + cf, "w+")

       f.write("# grub.conf generated by anaconda\n")
       f.write("#\n")
       f.write("# Note that you do not have to rerun grub "
               "after making changes to this file\n")

       bootDev = fsset.getEntryByMountPoint("/boot")
       grubPath = "/grub"
       cfPath = "/"
       if not bootDev:
           bootDev = fsset.getEntryByMountPoint("/")
           grubPath = "/boot/grub"
           cfPath = "/boot/"
           f.write("# NOTICE:  You do not have a /boot partition.  "
                   "This means that\n")
           f.write("#          all kernel and initrd paths are relative "
                   "to /, eg.\n")
       else:
           f.write("# NOTICE:  You have a /boot partition.  This means "
                   "that\n")
           f.write("#          all kernel and initrd paths are relative "
                   "to /boot/, eg.\n")

       bootDevs = self.getPhysicalDevices(bootDev.device.getDevice())
       bootDev = bootDev.device.getDevice()

       f.write('#          root %s\n' % self.grubbyPartitionName(bootDevs[0]))
       f.write("#          kernel %svmlinuz-version ro "
               "root=/dev/%s\n" % (cfPath, rootDev))
       f.write("#          initrd %sinitrd-version.img\n" % (cfPath))
       f.write("#boot=/dev/%s\n" % (grubTarget))

       # get the default image to boot... we have to walk and find it
       # since grub indexes by where it is in the config file
       if defaultDev == rootDev:
           default = 0
       else:
           # if the default isn't linux, it's the first thing in the
           # chain list
           default = len(kernelList)

       # keep track of which devices are used for the device.map
       usedDevs = {}

       f.write('default=%s\n' % (default))
       if len(chainList) > 0:
           timeout = 5
       else:
           timeout = 5
       f.write('timeout=%d\n' %(timeout,))

       if self.serial == 1:
           # grub the 0-based number of the serial console device
           unit = self.serialDevice[-1]

           # and we want to set the speed too
           speedend = 0
           for char in self.serialOptions:
               if char not in string.digits:
                   break
               speedend = speedend + 1
           if speedend != 0:
               speed = self.serialOptions[:speedend]
           else:
               # reasonable default
               speed = "9600"

           f.write("serial --unit=%s --speed=%s\n" %(unit, speed))
           f.write("terminal --timeout=5 serial console\n")
       else:
           # we only want splashimage if they're not using a serial console
           if os.access("%s/boot/grub/splash.xpm.gz" %(instRoot,), os.R_OK):
               f.write('splashimage=%s%sgrub/splash.xpm.gz\n'
                       % (self.grubbyPartitionName(bootDevs[0]), cfPath))
               f.write("hiddenmenu\n")

       for dev in self.getPhysicalDevices(grubTarget):
           usedDevs[dev] = 1

       if self.password:
           f.write('password --md5 %s\n' %(self.password))

       for (label, longlabel, version) in kernelList:
           kernelTag = "-" + version
           kernelFile = "%svmlinuz%s" % (cfPath, kernelTag)

           initrd = booty.makeInitrd (kernelTag, instRoot)

           f.write('title %s (%s)\n' % (longlabel, version))
           f.write('\troot %s\n' % self.grubbyPartitionName(bootDevs[0]))

           realroot = getRootDevName(initrd, fsset, rootDev, instRoot)
           realroot = " root=%s" %(realroot,)

           f.write('\tkernel %s ro%s' % (kernelFile, realroot))
           if self.args.get():
               f.write(' %s' % self.args.get())
           f.write('\n')

           if os.access (instRoot + initrd, os.R_OK):
               f.write('\tinitrd %sinitrd%s.img\n' % (cfPath, kernelTag))

       for (label, longlabel, device) in chainList:
           if ((not longlabel) or (longlabel == "")):
               continue
           f.write('title %s\n' % (longlabel))
           f.write('\trootnoverify %s\n' % self.grubbyPartitionName(device))
#            f.write('\tmakeactive\n')
           f.write('\tchainloader +1')
           f.write('\n')
           usedDevs[device] = 1

       f.close()
       os.chmod(instRoot + "/boot/grub/grub.conf", self.perms)

       try:
           # make symlink for menu.lst (default config file name)
           if os.access (instRoot + "/boot/grub/menu.lst", os.R_OK):
               os.rename(instRoot + "/boot/grub/menu.lst",
                         instRoot + "/boot/grub/menu.lst.rpmsave")
           os.symlink("./grub.conf", instRoot + "/boot/grub/menu.lst")
       except:
           pass

       try:
           # make symlink for /etc/grub.conf (config files belong in /etc)
           if os.access (instRoot + "/etc/grub.conf", os.R_OK):
               os.rename(instRoot + "/etc/grub.conf",
                         instRoot + "/etc/grub.conf.rpmsave")
           os.symlink("../boot/grub/grub.conf", instRoot + "/etc/grub.conf")
       except:
           pass

       for dev in self.getPhysicalDevices(rootDev):
           usedDevs[dev] = 1

       if not os.access(instRoot + "/boot/grub/device.map", os.R_OK):
           f = open(instRoot + "/boot/grub/device.map", "w+")
           f.write("# this device map was generated by anaconda\n")
           f.write("(fd0)     /dev/fd0\n")
           devs = usedDevs.keys()
           usedDevs = {}
           for dev in devs:
               drive = getDiskPart(dev)[0]
               if usedDevs.has_key(drive):
                   continue
               usedDevs[drive] = 1
           devs = usedDevs.keys()
           devs.sort()
           for drive in devs:
               # XXX hack city.  If they're not the sort of thing that'll
               # be in the device map, they shouldn't still be in the list.
               if not drive.startswith('md'):
                   f.write("(%s)     /dev/%s\n" % (self.grubbyDiskName(drive),
                                               drive))
           f.close()

       if self.forceLBA32:
           forcelba = "--force-lba "
       else:
           forcelba = ""

       sysconf = '/etc/sysconfig/grub'
       if os.access (instRoot + sysconf, os.R_OK):
           self.perms = os.stat(instRoot + sysconf)[0] & 0777
           os.rename(instRoot + sysconf,
                     instRoot + sysconf + '.rpmsave')
       # if it's an absolute symlink, just get it out of our way
       elif (os.path.islink(instRoot + sysconf) and
             os.readlink(instRoot + sysconf)[0] == '/'):
           os.rename(instRoot + sysconf,
                     instRoot + sysconf + '.rpmsave')
       f = open(instRoot + sysconf, 'w+')
       f.write("boot=/dev/%s\n" %(grubTarget,))
       # XXX forcelba never gets read back...
       if self.forceLBA32:
           f.write("forcelba=1\n")
       else:
           f.write("forcelba=0\n")
       f.close()

       cmds = []
       for bootDev in bootDevs:
           gtPart = self.getMatchingPart(bootDev, grubTarget)
           gtDisk = self.grubbyPartitionName(getDiskPart(gtPart)[0])
           bPart = self.grubbyPartitionName(bootDev)
           cmd = "root %s" % (bPart,)
           cmds.append(cmd)

           stage1Target = gtDisk
           if target == "partition":
               stage1Target = self.grubbyPartitionName(gtPart)

           cmd = "install %s%s/stage1 d %s %s/stage2 p %s%s/grub.conf" % \
               (forcelba, grubPath, stage1Target, grubPath, bPart, grubPath)
           cmds.append(cmd)

       if not justConfigFile:
           log("GRUB commands:")
           for cmd in cmds:
               log("\t%s\n", cmd)

           # copy the stage files over into /boot
           rhpl.executil.execWithRedirect( "/sbin/grub-install",
                                   ["/sbin/grub-install", "--just-copy"],
                                   stdout = "/dev/tty5", stderr = "/dev/tty5",
                                   root = instRoot)



           # FIXME: hack to try to make sure everything is written to the disk
           if cfPath == "/":
               syncDataToDisk(bootDev, "/boot", instRoot)
           else:
               syncDataToDisk(bootDev, "/", instRoot)

           # really install the bootloader
           p = os.pipe()
           for cmd in cmds:
               os.write(p[1], cmd + '\n')
           os.close(p[1])
           rhpl.executil.execWithRedirect('/sbin/grub' ,
                                   [ "grub",  "--batch", "--no-floppy",
                                     "--device-map=/boot/grub/device.map" ],
                                   stdin = p[0],
                                   stdout = "/dev/tty5", stderr = "/dev/tty5",
                                   root = instRoot)
           os.close(p[0])

       return ""

   def getMatchingPart(self, bootDev, target):
       bootName, bootPartNum = getDiskPart(bootDev)
       devices = self.getPhysicalDevices(target)
       for device in devices:
           name, partNum = getDiskPart(device)
           if name == bootName:
               return device
       return devices[0]

   def grubbyDiskName(self, name):
       return "hd%d" % self.drivelist.index(name)

   def grubbyPartitionName(self, dev):
       (name, partNum) = getDiskPart(dev)
       if partNum != None:
           return "(%s,%d)" % (self.grubbyDiskName(name), partNum)
       else:
           return "(%s)" %(self.grubbyDiskName(name))


   def getBootloaderConfig(self, instRoot, fsset, bl, langs, kernelList,
                           chainList, defaultDev):
       config = bootloaderInfo.getBootloaderConfig(self, instRoot, fsset,
                                                   bl, langs,
                                                   kernelList, chainList,
                                                   defaultDev)

       liloTarget = bl.getDevice()

       config.addEntry("boot", '/dev/' + liloTarget, replace = 0)
       config.addEntry("map", "/boot/map", replace = 0)
       config.addEntry("install", "/boot/boot.b", replace = 0)
       message = "/boot/message"

       if self.pure is not None and not self.useGrubVal:
           config.addEntry("restricted", replace = 0)
           config.addEntry("password", self.pure, replace = 0)


       import language
       for lang in language.expandLangs(langs.getDefault()):
           fn = "/boot/message." + lang
           if os.access(instRoot + fn, os.R_OK):
               message = fn
               break

       if self.serial == 1:
          # grab the 0-based number of the serial console device
           unit = self.serialDevice[-1]
           # FIXME: we should probably put some options, but lilo
           # only supports up to 9600 baud so just use the defaults
           # it's better than nothing :(
           config.addEntry("serial=%s" %(unit,))
       else:
           # message screws up serial console
           if os.access(instRoot + message, os.R_OK):
               config.addEntry("message", message, replace = 0)

       if not config.testEntry('lba32'):
           if self.forceLBA32 or (bl.above1024 and
                                  butil.getArch() != "x86_64" and
                                  edd.detect()):
               config.addEntry("lba32", replace = 0)

       return config

   # this is a hackish function that depends on the way anaconda writes
   # out the grub.conf with a #boot= comment
   # XXX this falls into the category of self.doUpgradeOnly
   def upgradeGrub(self, instRoot, fsset, bl, langs, kernelList, chainList,
                   defaultDev, justConfigFile):
       if justConfigFile:
           return ""

       theDev = None
       for (fn, stanza) in [ ("/etc/sysconfig/grub", "boot="),
                             ("/boot/grub/grub.conf", "#boot=") ]:
           try:
               f = open(instRoot + fn, "r")
           except:
               continue

           # the following bits of code are straight from checkbootloader.py
           lines = f.readlines()
           f.close()
           for line in lines:
               if line.startswith(stanza):
                   theDev = checkbootloader.getBootDevString(line)
                   break
           if theDev is not None:
               break

       if theDev is None:
           # we could find the dev before, but can't now...  cry about it
           return ""

       # migrate info to /etc/sysconfig/grub
       sysconf = '/etc/sysconfig/grub'
       if not os.access(instRoot + sysconf, os.R_OK):
           f = open(instRoot + sysconf, "w+")
           f.write("boot=%s\n" %(theDev,))
           # XXX forcelba never gets read back at all...
           if self.forceLBA32:
               f.write("forcelba=1\n")
           else:
               f.write("forcelba=0\n")
           f.close()

       # more suckage.  grub-install can't work without a valid /etc/mtab
       # so we have to do shenanigans to get updated grub installed...
       # steal some more code above
       bootDev = fsset.getEntryByMountPoint("/boot")
       grubPath = "/grub"
       cfPath = "/"
       if not bootDev:
           bootDev = fsset.getEntryByMountPoint("/")
           grubPath = "/boot/grub"
           cfPath = "/boot/"

       masterBootDev = bootDev.device.getDevice(asBoot = 0)
       if masterBootDev[0:2] == 'md':
           rootDevs = checkbootloader.getRaidDisks(masterBootDev, raidLevel=1,
                           stripPart = 0)
       else:
           rootDevs = [masterBootDev]

       if theDev[5:7] == 'md':
           stage1Devs = checkbootloader.getRaidDisks(theDev[5:], raidLevel=1)
       else:
           stage1Devs = [theDev[5:]]

       for stage1Dev in stage1Devs:
           # cross fingers; if we can't find a root device on the same
           # hardware as this boot device, we just blindly hope the first
           # thing in the list works.

           grubbyStage1Dev = self.grubbyPartitionName(stage1Dev)

           grubbyRootPart = self.grubbyPartitionName(rootDevs[0])

           for rootDev in rootDevs:
               testGrubbyRootDev = getDiskPart(rootDev)[0]
               testGrubbyRootDev = self.grubbyPartitionName(testGrubbyRootDev)

               if grubbyStage1Dev == testGrubbyRootDev:
                   grubbyRootPart = self.grubbyPartitionName(rootDev)
                   break

           cmd = "root %s\ninstall %s/stage1 d %s %s/stage2 p %s%s/grub.conf" \
               % (grubbyRootPart, grubPath, grubbyStage1Dev,
                  grubPath, grubbyRootPart, grubPath)

           if not justConfigFile:
               log("GRUB command %s", cmd)

               # copy the stage files over into /boot
               rhpl.executil.execWithRedirect( "/sbin/grub-install",
                                   ["/sbin/grub-install", "--just-copy"],
                                   stdout = "/dev/tty5", stderr = "/dev/tty5",
                                   root = instRoot)

               # get the stage files synced to disk
               import isys
               isys.sync()
               isys.sync()
               isys.sync()

               # really install the bootloader
               p = os.pipe()
               os.write(p[1], cmd + '\n')
               os.close(p[1])
               rhpl.executil.execWithRedirect('/sbin/grub' ,
                                  [ "grub",  "--batch", "--no-floppy",
                                    "--device-map=/boot/grub/device.map" ],
                                  stdin = p[0],
                                  stdout = "/dev/tty5", stderr = "/dev/tty5",
                                  root = instRoot)
               os.close(p[0])

       return ""


   def write(self, instRoot, fsset, bl, langs, kernelList, chainList,
                 defaultDev, justConfig, intf):
       # XXX HACK ALERT - see declaration above
       if self.doUpgradeOnly:
           if self.useGrubVal:
               self.upgradeGrub(instRoot, fsset, bl, langs, kernelList,
                                chainList, defaultDev, justConfig)

           # and the hacks continue.  with 2.6, ide-scsi isn't used
           # anymore (#116622)
           import isys
           cdrw = isys.ideCdRwList()
           torem = []
           for device in cdrw:
               torem.append("%s=ide-scsi" %(device,))

           for fn in ("/etc/lilo.conf", "/boot/grub/grub.conf",
                      "/etc/lilo.conf.anaconda"):
               if not os.path.exists(instRoot + fn):
                   continue
               f = open(instRoot + fn, "r")
               buf = f.read()
               f.close()
               for dev in torem:
                   buf = buf.replace(dev, "")
               f = open(instRoot + fn, "w")
               f.write(buf)
               f.close()
           return
       if len(kernelList) < 1:
           self.noKernelsWarn(intf)

       str = self.writeGrub(instRoot, fsset, bl, langs, kernelList,
                            chainList, defaultDev,
                            justConfig | (not self.useGrubVal))
       # XXX move the lilo.conf out of the way if they're using GRUB
       # so that /sbin/installkernel does a more correct thing
       if self.useGrubVal and os.access(instRoot + '/etc/lilo.conf', os.R_OK):
           os.rename(instRoot + "/etc/lilo.conf",
                     instRoot + "/etc/lilo.conf.anaconda")



   def getArgList(self):
       args = bootloaderInfo.getArgList(self)

       if self.forceLBA32:
           args.append("--lba32")
       if self.password:
           args.append("--md5pass=%s" %(self.password))


       # XXX add location of bootloader here too

       return args

   def __init__(self):
       bootloaderInfo.__init__(self)
       # XXX use checkbootloader to determine what to default to
       self.useGrubVal = 1
       self.kernelLocation = "/boot/"
       self.configfile = "/etc/lilo.conf"
       self.password = None
       self.pure = None

class s390BootloaderInfo(bootloaderInfo):
   def getBootloaderConfig(self, instRoot, fsset, bl, langs, kernelList,
                           chainList, defaultDev):
       images = bl.images.getImages()

       # on upgrade read in the lilo config file
       lilo = LiloConfigFile ()
       self.perms = 0600
       if os.access (instRoot + self.configfile, os.R_OK):
           self.perms = os.stat(instRoot + self.configfile)[0] & 0777
           lilo.read (instRoot + self.configfile)
           os.rename(instRoot + self.configfile,
                     instRoot + self.configfile + '.rpmsave')

       # Remove any invalid entries that are in the file; we probably
       # just removed those kernels.
       for label in lilo.listImages():
           (fsType, sl, path, other) = lilo.getImage(label)
           if fsType == "other": continue

           if not os.access(instRoot + sl.getPath(), os.R_OK):
               lilo.delImage(label)

       rootDev = fsset.getEntryByMountPoint("/").device.getDevice()
       if not rootDev:
           raise RuntimeError, "Installing zipl, but there is no root device"

       if rootDev == defaultDev:
           lilo.addEntry("default", kernelList[0][0])
       else:
           lilo.addEntry("default", chainList[0][0])

       for (label, longlabel, version) in kernelList:
           kernelTag = "-" + version
           kernelFile = self.kernelLocation + "vmlinuz" + kernelTag

           try:
               lilo.delImage(label)
           except IndexError, msg:
               pass

           sl = LiloConfigFile(imageType = "image", path = kernelFile)

           initrd = booty.makeInitrd (kernelTag, instRoot)

           sl.addEntry("label", label)
           if os.access (instRoot + initrd, os.R_OK):
               sl.addEntry("initrd",
                           "%sinitrd%s.img" %(self.kernelLocation, kernelTag))

           sl.addEntry("read-only")
           sl.addEntry("root", '/dev/' + rootDev)
           sl.addEntry("ipldevice", '/dev/' + rootDev[:-1])

           if self.args.get():
               sl.addEntry('append', '"%s"' % self.args.get())

           lilo.addImage (sl)

       for (label, longlabel, device) in chainList:
           if ((not label) or (label == "")):
               continue
           try:
               (fsType, sl, path, other) = lilo.getImage(label)
               lilo.delImage(label)
           except IndexError:
               sl = LiloConfigFile(imageType = "other",
                                   path = "/dev/%s" %(device))
               sl.addEntry("optional")

           sl.addEntry("label", label)
           lilo.addImage (sl)

       # Sanity check #1. There could be aliases in sections which conflict
       # with the new images we just created. If so, erase those aliases
       imageNames = {}
       for label in lilo.listImages():
           imageNames[label] = 1

       for label in lilo.listImages():
           (fsType, sl, path, other) = lilo.getImage(label)
           if sl.testEntry('alias'):
               alias = sl.getEntry('alias')
               if imageNames.has_key(alias):
                   sl.delEntry('alias')
               imageNames[alias] = 1

       # Sanity check #2. If single-key is turned on, go through all of
       # the image names (including aliases) (we just built the list) and
       # see if single-key will still work.
       if lilo.testEntry('single-key'):
           singleKeys = {}
           turnOff = 0
           for label in imageNames.keys():
               l = label[0]
               if singleKeys.has_key(l):
                   turnOff = 1
               singleKeys[l] = 1
           if turnOff:
               lilo.delEntry('single-key')

       return lilo

   def writeChandevConf(self, bl, instroot):   # S/390 only
       cf = "/etc/chandev.conf"
       self.perms = 0644
       if bl.args.chandevget():
           fd = os.open(instroot + "/etc/chandev.conf",
                        os.O_WRONLY | os.O_CREAT)
           os.write(fd, "noauto\n")
           for cdev in bl.args.chandevget():
               os.write(fd,'%s\n' % cdev)
           os.close(fd)
       return ""


   def writeZipl(self, instRoot, fsset, bl, langs, kernelList, chainList,
                 defaultDev, justConfigFile):
       images = bl.images.getImages()
       rootDev = fsset.getEntryByMountPoint("/").device.getDevice()

       cf = '/etc/zipl.conf'
       self.perms = 0600
       if os.access (instRoot + cf, os.R_OK):
           self.perms = os.stat(instRoot + cf)[0] & 0777
           os.rename(instRoot + cf,
                     instRoot + cf + '.rpmsave')

       f = open(instRoot + cf, "w+")

       f.write('[defaultboot]\n')
       f.write('default=' + kernelList[0][0] + '\n')
       f.write('target=%s\n' % (self.kernelLocation))

       cfPath = "/boot/"
       for (label, longlabel, version) in kernelList:
           kernelTag = "-" + version
           kernelFile = "%svmlinuz%s" % (cfPath, kernelTag)

           initrd = booty.makeInitrd (kernelTag, instRoot)
           f.write('[%s]\n' % (label))
           f.write('\timage=%s\n' % (kernelFile))
           if os.access (instRoot + initrd, os.R_OK):
               f.write('\tramdisk=%sinitrd%s.img\n' %(self.kernelLocation,
                                                    kernelTag))
           realroot = getRootDevName(initrd, fsset, rootDev, instRoot)
           f.write('\tparameters="root=%s' %(realroot,))
           if bl.args.get():
               f.write(' %s' % (bl.args.get()))
           f.write('"\n')

       f.close()

       if not justConfigFile:
           argv = [ "/sbin/zipl" ]
           rhpl.executil.execWithRedirect(argv[0], argv, root = instRoot,
                                  stdout = "/dev/stdout",
                                  stderr = "/dev/stderr")

       return ""

   def write(self, instRoot, fsset, bl, langs, kernelList, chainList,
                 defaultDev, justConfig, intf):
       str = self.writeZipl(instRoot, fsset, bl, langs, kernelList,
                            chainList, defaultDev,
                            justConfig | (not self.useZiplVal))
       str = self.writeChandevConf(bl, instRoot)

   def __init__(self):
       bootloaderInfo.__init__(self)
       self.useZiplVal = 1      # only used on s390
       self.kernelLocation = "/boot/"
       self.configfile = "/etc/zipl.conf"


class alphaBootloaderInfo(bootloaderInfo):
   def wholeDevice (self, path):
       (device, foo) = getDiskPart(path)
       return device

   def partitionNum (self, path):
       # getDiskPart returns part numbers 0-based; we need it one based
       # *sigh*
       (foo, partitionNumber) = getDiskPart(path)
       return partitionNumber + 1

   # See if we would have to use MILO. MILO isn't supported by Red Hat.
   def useMilo (self):
       try:
           f = open ('/proc/cpuinfo', 'ro')
       except:
           return
       lines = f.readlines ()
       f.close()
       serial = ""
       for line in lines:
           if line.find("system serial number") != -1:
               serial = string.strip (string.split (line, ':')[1])
               break

       if serial and len (serial) >= 4 and serial.startswith("MILO"):
           return 1
       else:
           return 0

   def writeAboot(self, instRoot, fsset, bl, langs, kernelList,
                  chainList, defaultDev, justConfig):
       # Get bootDevice and rootDevice
       rootDevice = fsset.getEntryByMountPoint("/").device.getDevice()
       if fsset.getEntryByMountPoint("/boot"):
           bootDevice = fsset.getEntryByMountPoint("/boot").device.getDevice()
       else:
           bootDevice = rootDevice
       bootnotroot = bootDevice != rootDevice

       # If /etc/aboot.conf already exists we rename it
       # /etc/aboot.conf.rpmsave.
       if os.path.isfile(instRoot + self.configfile):
           os.rename (instRoot + self.configfile,
                      instRoot + self.configfile + ".rpmsave")

       # Then we create the necessary files. If the root device isn't
       # the boot device, we create /boot/etc/ where the aboot.conf
       # will live, and we create /etc/aboot.conf as a symlink to it.
       if bootnotroot:
           # Do we have /boot/etc ? If not, create one
           if not os.path.isdir (instRoot + '/boot/etc'):
               os.mkdir(instRoot + '/boot/etc', 0755)

           # We install the symlink (/etc/aboot.conf has already been
           # renamed in necessary.)
           os.symlink("../boot" + self.configfile, instRoot + self.configfile)

           cfPath = instRoot + "/boot" + self.configfile
           # Kernel path is set to / because a boot partition will
           # be a root on its own.
           kernelPath = '/'
       # Otherwise, we just need to create /etc/aboot.conf.
       else:
           cfPath = instRoot + self.configfile
           kernelPath = self.kernelLocation

       # If we already have an aboot.conf, rename it
       if os.access (cfPath, os.R_OK):
           self.perms = os.stat(cfPath)[0] & 0777
           os.rename(cfPath, cfPath + '.rpmsave')

       # Now we're going to create and populate cfPath.
       f = open (cfPath, 'w+')
       f.write ("# aboot default configurations\n")

       if bootnotroot:
           f.write ("# NOTICE: You have a /boot partition. This means that\n")
           f.write ("#         all kernel paths are relative to /boot/\n")

       # bpn is the boot partition number.
       bpn = self.partitionNum(bootDevice)
       lines = 0

       # We write entries line using the following format:
       # <line><bpn><kernel-name> root=<rootdev> [options]
       # We get all the kernels we need to know about in kernelList.

       for (kernel, tag, kernelTag) in kernelList:
           kernelFile = "%svmlinuz-%s" %(kernelPath, kernelTag)

           f.write("%d:%d%s" %(lines, bpn, kernelFile))

           # See if we can come up with an initrd argument that exists
           initrd = booty.makeInitrd (kernelTag, instRoot)
           if os.access(instRoot + initrd, os.R_OK):
               f.write(" initrd=%sinitrd-%s.img" %(kernelPath, kernelTag))

           realroot = getRootDevName(initrd, fsset, rootDevice, instRoot)
           f.write(" root=%s" %(realroot,))

           args = self.args.get()
           if args:
               f.write(" %s" %(args,))

           f.write("\n")
           lines = lines + 1

       # We're done writing the file
       f.close ()
       del f

       if not justConfig:
           # Now we're ready to write the relevant boot information. wbd
           # is the whole boot device, bdpn is the boot device partition
           # number.
           wbd = self.wholeDevice (bootDevice)
           bdpn = self.partitionNum (bootDevice)

           # Calling swriteboot. The first argument is the disk to write
           # to and the second argument is a path to the bootstrap loader
           # file.
           args = ("swriteboot", ("/dev/%s" % wbd), "/boot/bootlx")
           log("swriteboot command: %s" %(args,))
           rhpl.executil.execWithRedirect ('/sbin/swriteboot', args,
                                   root = instRoot,
                                   stdout = "/dev/tty5",
                                   stderr = "/dev/tty5")

           # Calling abootconf to configure the installed aboot. The
           # first argument is the disk to use, the second argument is
           # the number of the partition on which aboot.conf resides.
           # It's always the boot partition whether it's / or /boot (with
           # the mount point being omitted.)
           args = ("abootconf", ("/dev/%s" % wbd), str (bdpn))
           log("abootconf command: %s" %(args,))
           rhpl.executil.execWithRedirect ('/sbin/abootconf', args,
                                   root = instRoot,
                                   stdout = "/dev/tty5",
                                   stderr = "/dev/tty5")


   def write(self, instRoot, fsset, bl, langs, kernelList, chainList,
                 defaultDev, justConfig, intf):
       if len(kernelList) < 1:
           self.noKernelsWarn(intf)

       if self.useMilo():
           intf.messageWindow(_("MILO Not Supported"),
                              "This system requires the support of MILO to " +
                              "boot linux (MILO is not included in this " +
                              "distribution.) The installation of the " +
                              "bootloader can't be completed on this system.")
       # elif not justConfig FIXME.
       else:
           self.writeAboot(instRoot, fsset, bl, langs, kernelList,
                           chainList, defaultDev, justConfig)

   def __init__(self):
       bootloaderInfo.__init__(self)
       self.useGrubVal = 0
       self.configfile = "/etc/aboot.conf"
       # self.kernelLocation is already set to what we need.
       self.password = None
       self.pure = None


class ppcBootloaderInfo(bootloaderInfo):
   def writeYaboot(self, instRoot, fsset, bl, langs, kernelList,
                 chainList, defaultDev, justConfigFile):

       from flags import flags

       yabootTarget = '/dev/%s' %(bl.getDevice())

       bootDev = fsset.getEntryByMountPoint("/boot")
       if bootDev:
           cf = "/boot/etc/yaboot.conf"
           cfPath = ""
           if not os.path.isdir(instRoot + "/boot/etc"):
               os.mkdir(instRoot + "/boot/etc")
       else:
           bootDev = fsset.getEntryByMountPoint("/")
           cfPath = "/boot"
           cf = "/etc/yaboot.conf"
       bootDev = bootDev.device.getDevice(asBoot = 1)

       f = open(instRoot + cf, "w+")

       f.write("# yaboot.conf generated by anaconda\n\n")

       f.write("boot=%s\n" %(yabootTarget,))
       f.write("init-message=Welcome to %s\\!\nHit <TAB> for boot options\n\n"
               %(butil.getProductName(),))

       (name, partNum) = getDiskPart(bootDev)
       partno = partNum + 1 # 1 based
       f.write("partition=%s\n" %(partno,))

       f.write("timeout=80\n")
       f.write("install=/usr/lib/yaboot/yaboot\n")
       f.write("delay=5\n")
       f.write("enablecdboot\n")
       f.write("enableofboot\n")
       f.write("enablenetboot\n")

       if butil.getPPCMachine() == "PMac":
           # write out the first hfs/hfs+ partition as being macosx
           for (label, longlabel, device) in chainList:
               if ((not label) or (label == "")):
                   continue
               f.write("macosx=/dev/%s\n" %(device,))
               break

           f.write("magicboot=/usr/lib/yaboot/ofboot\n")

       if butil.getPPCMachine() == "pSeries":
           f.write("nonvram\n")

       if self.password:
           f.write("password=%s\n" %(self.password,))
           f.write("restricted\n")

       f.write("\n")

       rootDev = fsset.getEntryByMountPoint("/").device.getDevice()

       for (label, longlabel, version) in kernelList:
           kernelTag = "-" + version
           kernelFile = "%s/vmlinuz%s" %(cfPath, kernelTag)

           f.write("image=%s\n" %(kernelFile,))
           f.write("\tlabel=%s\n" %(label,))
           f.write("\tread-only\n")

           initrd = booty.makeInitrd(kernelTag, instRoot)
           if os.access(instRoot + initrd, os.R_OK):
               f.write("\tinitrd=%s/initrd%s.img\n" %(cfPath,kernelTag))

           append = "%s" %(self.args.get(),)

           realroot = getRootDevName(initrd, fsset, rootDev, instRoot)
           if not realroot.startswith("LABEL="):
               f.write("\troot=%s\n" %(realroot,))
           else:
               if len(append) > 0:
                   append = "%s root=%s" %(append,realroot)
               else:
                   append = "root=%s" %(realroot,)

           if len(append) > 0:
               f.write("\tappend=\"%s\"\n" %(append,))
           f.write("\n")

       f.close()
       os.chmod(instRoot + cf, 0600)

       # FIXME: hack to make sure things are written to disk
       import isys
       isys.sync()
       isys.sync()
       isys.sync()

       ybinargs = [ "/sbin/mkofboot", "-f", "-C", cf ]
       if butil.getPPCMachine() == "pSeries":
           ybinargs.extend(["--filesystem", "raw"])


       log("running: %s" %(ybinargs,))
       if not flags.test:
           rhpl.executil.execWithRedirect(ybinargs[0],
                                          ybinargs,
                                          stdout = "/dev/tty5",
                                          stderr = "/dev/tty5",
                                          root = instRoot)

       if (not os.access(instRoot + "/etc/yaboot.conf", os.R_OK) and
           os.access(instRoot + "/boot/etc/yaboot.conf", os.R_OK)):
           os.symlink("../boot/etc/yaboot.conf",
                      instRoot + "/etc/yaboot.conf")

       return ""

   def setPassword(self, val, isCrypted = 1):
       # yaboot just handles the password and doesn't care if its crypted
       # or not
       self.password = val

   def write(self, instRoot, fsset, bl, langs, kernelList, chainList,
                 defaultDev, justConfig, intf):
       if len(kernelList) >= 1:
           str = self.writeYaboot(instRoot, fsset, bl, langs, kernelList,
                                chainList, defaultDev, justConfig)
       else:
           self.noKernelsWarn(intf)

   def __init__(self):
       bootloaderInfo.__init__(self)
       self.useYabootVal = 1
       self.kernelLocation = "/boot"
       self.configfile = "/etc/yaboot.conf"


class iseriesBootloaderInfo(bootloaderInfo):
   def ddFile(self, inf, of, bs = 4096):
       src = os.open(inf, os.O_RDONLY)
       dest = os.open(of, os.O_WRONLY | os.O_CREAT)
       size = 0

       buf = os.read(src, bs)
       while len(buf) > 0:
           size = size + len(buf)
           os.write(dest, buf)
           buf = os.read(src, bs)

       os.close(src)
       os.close(dest)

       return size

   def write(self, instRoot, fsset, bl, langs, kernelList, chainList,
             defaultDev, justConfig, intf):
       if len(kernelList) < 1:
           self.noKernelsWarn(intf)
           return
       if len(kernelList) > 1:
           # FIXME: how can this happen?
           log("more than one kernel on iSeries.  bailing and just using "
               "the first")

       # iseries is Weird (tm) -- here's the basic theory
       # a) have /boot/vmlinitrd-$(version)
       # b) copy default kernel to PReP partition
       # c) dd default kernel to /proc/iSeries/mf/C/vmlinux
       # d) set cmdline in /boot/cmdline-$(version)
       # e) copy cmdline to /proc/iSeries/mf/C/cmdline
       # f) set default side to 'C' i /proc/iSeries/mf/side
       # g) put default kernel and cmdline on side B too (#91038)

       rootDevice = fsset.getEntryByMountPoint("/").device.getDevice()

       # write our command line files
       for (kernel, tag, kernelTag) in kernelList:
           cmdFile = "%scmdline-%s" %(self.kernelLocation, kernelTag)
           initrd = "%sinitrd-%s.img" %(self.kernelLocation, kernelTag)
           realroot = getRootDevName(initrd, fsset, rootDevice, instRoot)
           f = open(instRoot + cmdFile, "w")
           f.write("ro root=%s" %(realroot,))
           if bl.args.get():
               f.write(" %s" %(bl.args.get(),))
           f.write("\n")
           f.close()
           os.chmod(instRoot + cmdFile, 0644)

       kernel, tag, kernelTag = kernelList[0]
       kernelFile = "%svmlinitrd-%s" %(self.kernelLocation, kernelTag)

       # write the kernel to the PReP partition since that's what
       # OS/400 will load as NWSSTG
       bootDev = bl.getDevice()
       if bootDev:
           log("Writing kernel %s to PReP partition %s" %(kernelFile,
                                                          bootDev))
           try:
               self.ddFile(instRoot + kernelFile, "%s/dev/%s" %(instRoot,
                                                                bootDev))
           except Exception, e:
               # FIXME: should this be more fatal
               log("Failed to write kernel: %s" %(e,))
       else:
           log("No PReP boot partition, not writing kernel for NWSSTG")


       # now, it's a lot faster to boot if we don't make people go back
       # into OS/400, so set up side C (used by default for NWSSTG) with
       # our current bits
       for side in ("C", "B"):
           log("Writing kernel and cmdline to side %s" %(side,))
           wrotekernel = 0
           try:
               self.ddFile(instRoot + kernelFile,
                           "%s/proc/iSeries/mf/%s/vmlinux" %(instRoot, side))
               wrotekernel = 1
           except Exception, e:
               # FIXME: should this be more fatal?
               log("Failed to write kernel to side %s: %s" %(side, e))

           if wrotekernel == 1:
               try:
                   # blank it.  ugh.
                   f = open("%s/proc/iSeries/mf/%s/cmdline" %(instRoot, side),
                            "w+")
                   f.write(" " * 255)
                   f.close()

                   self.ddFile("%s/%scmdline-%s" %(instRoot,
                                                   self.kernelLocation,
                                                   kernelTag),
                               "%s/proc/iSeries/mf/%s/cmdline" %(instRoot,
                                                                 side))
               except Exception, e:
                   log("Failed to write kernel command line to side %s: %s"
                       %(side, e))

       log("Setting default side to C")
       f = open(instRoot + "/proc/iSeries/mf/side", "w")
       f.write("C")
       f.close()

   def __init__(self):
       bootloaderInfo.__init__(self)
       self.kernelLocation = "/boot/"

class sparcBootloaderInfo(bootloaderInfo):
   def writeSilo(self, instRoot, fsset, bl, langs, kernelList,
               chainList, defaultDev, justConfigFile):

       from flags import flags

       bootDev = fsset.getEntryByMountPoint("/boot")
       if bootDev:
           cf = "/boot/silo.conf"
           mf = "/boot/silo.message"
           cfPath = ""
           if not os.path.isdir(instRoot + "/boot"):
               os.mkdir(instRoot + "/boot")
       else:
           bootDev = fsset.getEntryByMountPoint("/")
           cf = "/etc/silo.conf"
           mf = "/etc/silo.message"
           cfPath = "/boot"
       bootDev = bootDev.device.getDevice(asBoot = 1)

       f = open(instRoot + mf, "w+")
       f.write("Welcome to %s!\nHit <TAB> for boot options\n\n" % \
           (butil.getProductName(),))
       f.close()
       os.chmod(instRoot + mf, 0600)

       f = open(instRoot + cf, "w+")
       f.write("# silo.conf generated by anaconda\n\n")

       f.write("#boot=%s\n" % (bootDev,))
       f.write("message=%s\n" % (mf,))
       f.write("timeout=50\n")

       (name, partNum) = getDiskPart(bootDev)
       partno = partNum + 1
       f.write("partition=%s\n" % (partno,))

       if self.password:
           f.write("password=%s\n" % (self.password,))
           f.write("restricted\n")

       f.write("default=%s\n" % (kernelList[0][0],))
       f.write("\n")

       rootDev = fsset.getEntryByMountPoint("/").device.getDevice()

       for (label, longlabel, version) in kernelList:
           kernelTag = "-" + version
           kernelFile = "%s/vmlinuz%s" % (cfPath, kernelTag)

           f.write("image=%s\n" % (kernelFile,))
           f.write("\tlabel=%s\n" % (label,))
           f.write("\tread-only\n")

           initrd = booty.makeInitrd(kernelTag, instRoot)
           if os.access(instRoot + initrd, os.R_OK):
               f.write("\tinitrd=%s/initrd%s.img\n" % (cfPath, kernelTag))

           append = "%s" % (self.args.get(),)

           realroot = getRootDevName(initrd, fsset, rootDev, instRoot)
           if not realroot.startswith("LABEL="):
               f.write("\troot=%s\n" % (realroot,))
           else:
               if len(append) > 0:
                   append = "%s root=%s" % (append, realroot)
               else:
                   append = "root=%s" % (realroot,)

           if len(append) > 0:
               f.write("\tappend=\"%s\"\n" % (append,))
           f.write("\n")

       f.close()
       os.chmod(instRoot + cf, 0600)

       # FIXME: hack to make sure things are written to disk
       import isys
       isys.sync()
       isys.sync()
       isys.sync()

       backup = "%s/backup.b" % (cfPath,)
       sbinargs = ["/sbin/silo", "-f", "-C", cf, "-S", backup]
       if butil.getSparcMachine() == "sun4u":
           sbinargs += ["-u"]
       else:
           sbinargs += ["-U"]

       log("running: %s" % (sbinargs,))
       if not flags.test:
           rhpl.executil.execWithRedirect(sbinargs[0],
                                           sbinargs,
                                           stdout = "/dev/tty5",
                                           stderr = "/dev/tty5",
                                           root = instRoot)

       if (not os.access(instRoot + "/etc/silo.conf", os.R_OK) and
           os.access(instRoot + "/boot/etc/silo.conf", os.R_OK)):
           os.symlink("../boot/etc/silo.conf",
                      instRoot + "/etc/silo.conf")

       return ""

   def setPassword(self, val, isCrypted = 1):
       # silo just handles the password unencrypted
       self.password = val

   def write(self, instRoot, fsset, bl, langs, kernelList, chainList,
                 defaultDev, justConfig, intf):
       if len(kernelList) >= 1:
           self.writeSilo(instRoot, fsset, bl, langs, kernelList, chainList,
                       defaultDev, justConfig)
       else:
           self.noKernelsWarn(intf)

   def __init__(self):
       bootloaderInfo.__init__(self)
       self.useSiloVal = 1
       self.kernelLocation = "/boot"
       self.configfile = "/etc/silo.conf"

###############
# end of boot loader objects... these are just some utility functions used

# return (disk, partition number) eg ('hda', 1)
def getDiskPart(dev):
   cut = len(dev)
   if (dev.startswith('rd/') or dev.startswith('ida/') or
       dev.startswith('cciss/')):
       if dev[-2] == 'p':
           cut = -1
       elif dev[-3] == 'p':
           cut = -2
   else:
       if dev[-2] in string.digits:
           cut = -2
       elif dev[-1] in string.digits:
           cut = -1

   name = dev[:cut]

   # hack off the trailing 'p' from /dev/cciss/*, for example
   if name[-1] == 'p':
       for letter in name:
           if letter not in string.letters and letter != "/":
               name = name[:-1]
               break

   if cut < 0:
       partNum = int(dev[cut:]) - 1
   else:
       partNum = None

   return (name, partNum)

# hackery to determine if we should do root=LABEL=/ or whatnot
# as usual, knows too much about anaconda
def getRootDevName(initrd, fsset, rootDev, instRoot):
   if not os.access(instRoot + initrd, os.R_OK):
       return "/dev/%s" % (rootDev,)

   try:
       rootEntry = fsset.getEntryByMountPoint("/")
       if rootEntry.getLabel() is not None:
           return "LABEL=%s" %(rootEntry.getLabel(),)
       return "/dev/%s" %(rootDev,)
   except:
       return "/dev/%s" %(rootDev,)