#!/usr/bin/python
#
# Check to see whether it looks like GRUB or LILO is the boot loader
# being used on the system.
#
# Jeremy Katz <[email protected]>
# Peter Jones <[email protected]>
#
# Copyright 2001,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
import string

grubConfigFile = "/boot/grub/grub.conf"
liloConfigFile = "/etc/lilo.conf"
yabootConfigFile = "/etc/yaboot.conf"
siloConfigFile = "/etc/silo.conf"


# XXX: this is cut and pasted directly from booty/bootloaderInfo.py
# should eventually just go from there
def getDiskPart(dev):
   """Return (disk, partition number) tuple for dev"""
   cut = len(dev)
   if (dev[:3] == "rd/" or dev[:4] == "ida/" or
       dev[:6] == "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)


def getRaidDisks(raidDevice, raidLevel=None, stripPart=1):
   rc = []
   if raidLevel is not None:
       try:
           raidLevel = "raid%d" % (int(raidLevel),)
       except ValueError:
           pass

   try:
       f = open("/proc/mdstat", "r")
       lines = f.readlines()
       f.close()
   except:
       return rc

   for line in lines:
       fields = string.split(line, ' ')
       if fields[0] == raidDevice:
           if raidLevel is not None and fields[3] != raidLevel:
               continue
           for field in fields[4:]:
               if string.find(field, "[") == -1:
                   continue
               dev = string.split(field, '[')[0]
               if len(dev) == 0:
                   continue
               if stripPart:
                   disk = getDiskPart(dev)[0]
                   rc.append(disk)
               else:
                   rc.append(dev)

   return rc


def getBootBlock(bootDev, instRoot, seekBlocks=0):
   """Get the boot block from bootDev.  Return a 512 byte string."""
   block = " " * 512
   if bootDev is None:
       return block

   # get the devices in the raid device
   if bootDev[5:7] == "md":
       bootDevs = getRaidDisks(bootDev[5:])
       bootDevs.sort()
   else:
       bootDevs = [ bootDev[5:] ]

   # FIXME: this is kind of a hack
   # look at all of the devs in the raid device until we can read the
   # boot block for one of them.  should do this better at some point
   # by looking at all of the drives properly
   for dev in bootDevs:
       try:
           fd = os.open("%s/dev/%s" % (instRoot, dev), os.O_RDONLY)
           if seekBlocks > 0:
               os.lseek(fd, seekBlocks * 512, 0)
           block = os.read(fd, 512)
           os.close(fd)
           return block
       except:
           pass
   return block

# takes a line like #boot=/dev/hda and returns /dev/hda
# also handles cases like quoted versions and other nonsense
def getBootDevString(line):
   dev = string.split(line, '=')[1]
   dev = string.strip(dev)
   dev = string.replace(dev, '"', '')
   dev = string.replace(dev, "'", "")
   return dev

def getBootloaderTypeAndBoot(instRoot = "/"):
   haveGrubConf = 1
   haveLiloConf = 1
   haveYabootConf = 1
   haveSiloConf = 1

   bootDev = None

   # make sure they have the config file, otherwise we definitely can't
   # use that bootloader
   if not os.access(instRoot + grubConfigFile, os.R_OK):
       haveGrubConf = 0
   if not os.access(instRoot + liloConfigFile, os.R_OK):
       haveLiloConf = 0
   if not os.access(instRoot + yabootConfigFile, os.R_OK):
       haveYabootConf = 0
   if not os.access(instRoot + siloConfigFile, os.R_OK):
       haveSiloConf = 0

   if haveGrubConf:
       bootDev = None
       for (fn, stanza) in [ ("/etc/sysconfig/grub", "boot="),
                             (grubConfigFile, "#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):
                   bootDev = getBootDevString(line)
                   break
           if bootDev is not None:
               break

       if bootDev is not None:
           block = getBootBlock(bootDev, instRoot)
           # XXX I don't like this, but it's what the maintainer suggested :(
           if string.find(block, "GRUB") >= 0:
               return ("GRUB", bootDev)

   if haveLiloConf:
       f = open(instRoot + liloConfigFile, "r")
       lines = f.readlines()
       for line in lines:
           if line[0:5] == "boot=":
               bootDev = getBootDevString(line)
               break

       block = getBootBlock(bootDev, instRoot)
       # this at least is well-defined
       if block[6:10] == "LILO":
           return ("LILO", bootDev)

   if haveYabootConf:
       f = open(instRoot + yabootConfigFile, "r")
       lines = f.readlines()
       for line in lines:
           if line[0:5] == "boot=":
               bootDev = getBootDevString(line)

       if bootDev:
               return ("YABOOT", bootDev)

   if haveSiloConf:
       bootDev = None
       # We've never done the /etc/sysconfig/silo thing, but maybe
       # we should start...
       for (fn, stanza) in [ ("/etc/sysconfig/silo", "boot="),
                             (grubConfigFile, "#boot=") ]:
           try:
               f = open(instRoot + fn, "r")
           except:
               continue

           lines = f.readlines()
           f.close()
           for line in lines:
               if line.startswith(stanza):
                   bootDev = getBootDevString(line)
                   break
           if bootDev is not None:
               break

       if bootDev is not None:
           # XXX SILO sucks just like grub.
           if getDiskPart(bootDev)[1] != 3:
               block = getBootBlock(bootDev, instRoot, 1)
               if block[24:28] == "SILO":
                   return ("SILO", bootDev)

   return (None, None)

def whichBootLoader(instRoot = "/"):
   ret = getBootloaderTypeAndBoot(instRoot)
   if not ret:
       return None
   else:
       return ret[0]

if __name__ == "__main__":
   bootloader = whichBootLoader()
   if bootloader:
       print "Found %s." % (bootloader)
   else:
       print "Unable to determine boot loader."