#!/bin/bash

#: Title       : findgrub
#: Date Created: Wed Sep 29 20:02:40 CDT 2010
#: Last Edit   : Mon Oct 11 00:45:27 PDT 2010
#: Author      : please_try_again & edited by j McDaniel
#: Version     : 2.2b
#: Description : Locates the Grub Boot Loader & Windows BootLoader, Creates Grub Menu.Lst Entries
#: Options     : -h | --help, -w | -- writemenu, -k | --kernel OR -d | --debug  

# Created for the openSUSE forums September 30th in the year 2010 with loving care and presented to all of our great openSUSE USERS!

#
# Copy and paste this text into a text file and save it in your home area, bin folder (~/bin) as the file findgrub
# Once the file is saved, you must make the file executable using the terminal command: chmod +u ~/bin/findgrub
#

# current version
version="2.2b"




# default mountpoint
mnt=/mnt

#
# Check to see if we are root
#

if [[ $UID -ne 0 ]]; then
  id -Gn | grep -q wheel || echo "Root User Permissions are required, Please Enter the ..."
  echo
  sudo $0 $1
  exit 0
fi

echo
echo "Find Grub Version $version - Written for openSUSE Forums"
echo

function help {
    cat << EOFHELP

$0 - description and help
options:
-h --help      : show this help 
-k --kernel    : look for linux kernels 
-w --writemenu : add boot entries to /boot/grub/men.lst 
-d --debug     : print Win bootsectors to standard output

EOFHELP
exit
}

function Linux2LegacyGrub {
    eval `echo ${1:2} | sed 's|\(.\)|maj=\1;min=|;s|$|;|'`
	dev=${1:0:3}
    let min--
	# parse /boot/grub/device.map if present
	if [ -f /boot/grub/device.map ] ; then
		# check if device.map uses disk/by-id syntax (as in openSUSE)
		if [ "x$(grep 'disk' /boot/grub/device.map)" != "x" ] ; then
			eval `eval $(awk '/\(hd/ { printf "echo -n \"%s \" ; readlink %s ;", $1, $2 }' /boot/grub/device.map) | sed 's|/\.\.|/dev|;s|\.\.||;s|/dev/||;s|(||;s|)||' | awk '{ printf "%s=%s;", $2, $1 }'`
		# assume device.map uses device name syntax (as in Fedora, Mandriva, Arch Linux)
		else
			eval `sed -n '/[sh]d/s|/dev/||p' /boot/grub/device.map | tr -d "()" | awk '{ printf "%s=%s;", $2, $1 }'`
		fi
		echo "(${!dev},$min)"
	# no device.map found 
	else
    	echo "(hd$(($(printf "%d\n" \'$maj) - 97)),$min)" 
	fi
}

function chainloadEntry { 

if  [ "${WRITEMENU}" ] ; then
    printf " - append boot entry to chainload Windows bootmanager on %s to /boot/grub/menu.lst\n" $1
    cp /boot/grub/menu.lst{,.findgrub}
    cat << EOFWriteGrubEntry | sed '/^  *$/d' >> /boot/grub/menu.lst 

###Don't change this comment - YaST2 identifier: Original name: WindowsBootLoader###
title Windows on $1
    rootnoverify $(Linux2LegacyGrub ${1#/dev/*})
    $makeactive
    chainloader +1

EOFWriteGrubEntry
else
    printf "\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\nYou can add the following entry to /boot/grub/menu.lst :\n"
    cat << EOFDisplayGrubEntry | sed '/^  *$/d'

###Don't change this comment - YaST2 identifier: Original name: WindowsBootLoader###
title Windows on $1
    rootnoverify $(Linux2LegacyGrub ${1#/dev/*})
    $makeactive
    chainloader +1

EOFDisplayGrubEntry
    printf "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
fi
}

function printbs {
printf "\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n%s Bootsector\n---------------------------------------------------------------------------\n " $1     
dd if=$1 bs=512 count=1 2>/dev/null | tr -cd "[:alnum:] [:space:]"
printf " \n----------------------------------------------------------------------------\n"
dd if=/dev/sda1 bs=512 count=1 2>/dev/null | od -x 
printf " \n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"     
}

function printptbl {
printf "\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n%s Partition table\n---------------------------------------------------------------\n" $1     
dd if=$1 bs=1 skip=446 count=64 2>/dev/null | od -x | head -4
printf " \n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"     
}

function searchkernel {
	pt=$1
	unset kernels ismounted
	mount | grep -q "$pt " && ismounted=1
	eval `blkid $pt | sed 's|.*: ||;s| |;|g'`

	if [ "$ismounted" ] ; then
		mp=`mount | grep "$pt " | awk '{ print $3 }'`
		if [ "$mp" == "/boot" -o "$mp" == "/" ] ; then
			kernels=`find /boot -type f -name "vmlinuz*"`
 		fi
	else
		mount -t $TYPE $pt $mnt
	
		if [ -d /mnt/boot ] ; then
			kernels=`find $mnt/boot -type f -name "vmlinuz*"`
		else
			kernels=`find $mnt -maxdepth 1 -type f -name "vmlinuz*"`
		fi
		umount $mnt 2> /dev/null
	fi
	
	if [ "$kernels" ] ; then
		printf "\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
		for kern in $kernels ; do
			printf " --> kernel %-51s found in %s\n" ${kern##*/} $pt
		done
		printf "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
	fi	
}

function findbootmgr {
    case $3 in
        6|b|c|e) fs=vfat ;;
        7) fs=ntfs;;
    esac

    mp=`mount | grep $1 | awk '{ print $3 }'`

    if [ "x$mp" == "x" ] ; then
        mp=$mnt
        mount | grep -q "$mp " && mp=/findgrub
        [ -d $mp ] || mkdir $mp
        mount -t $fs $1 $mp
        UNMOUNT=1
    else
        unset UNMOUNT
    fi

    case $2 in
        bootmgr)
            find $mp -maxdepth 1 -iname "bootmgr" | grep -q -i "bootmgr" && {
            printf " --> Windows7/Vista Loader found in %s\n" $pt
            chainloadEntry $pt
        }
    ;;
        ntldr)
            find $mp -maxdepth 1 -iname "ntldr" | grep -q  -i "ntldr" && {
            printf " --> Windows NT/2K/XP Loader found in %s\n" $pt
            chainloadEntry $pt
        }
    ;;
    esac


    if [ "$UNMOUNT" ] ; then
        umount $mp
    fi
    [ -d /findgrub ] && rmdir /findgrub
}


# some partition types with color

x6=FAT16                ; c6='33;1m'
x16="Hidden FAT16"      ; c16='33;1m'
xb=FAT32                ; cb='32;1m'
xc=FAT32                ; cc='32;1m'
xe=FAT32                ; ce='32;1m'
x1b="Hidden FAT32"      ; c1b='32;1m'
x1c="Hidden FAT32"      ; c1c='32;1m'
x1e="Hidden FAT32"      ; c1e='32;1m'
x5=Extended             ; c5='32;1m'
xf=Extended             ; cf='32;1m'
x7=NTFS                 ; c7='34;1m'
x17="Hidden NTFS"       ; c17='31m'
x27="Win Recovery"      ; c27='31m'
x82=swap                ; c82='33m'
x83=LINUX               ; c83='36;1m'
x8e="LINUX LVM"         ; c8e='36;1m'
xee=GPT                 ; cee='31m'
xef=EFI                 ; cef='31m'
xaf=HFS                 ; caf='35m'
xa5=FreeBSD             ; ca5='35m'
xa6=OpenBSD             ; ca6='35m'
xa7="Next Step"         ; ca7='35m'
xa8=Darwin              ; ca8='35m'
xa9=NetBSD              ; ca9='35m'
x39=Plan9               ; c39='36m'
x4d=QNX4                ; c4d='36m'
x4e=QNX4                ; c4e='36m'
x4f=QNX4                ; c4f='36m'
x63="GNU HURD"          ; c63='36m'
x81=MINIX               ; c81='36m'
xbe="Solaris boot"      ; cbe='36m'
xbf="Solaris"           ; cbf='36m'
xeb="BeOS"              ; ceb='36m'
xfb="VMware VMFS"       ; cfb='36m'
xfc="VMware VMKCORE"    ; cfc='36m'
xfd="LINUX RAID AUTO"   ; cfd='36m'

# Main - BEGIN --------------------------

devs=(`fdisk -l 2>/dev/null | sed -n 's|^Disk \(/dev/[hs]d[a-z]\):.*|\1|p'`)

case "$1" in
    -w|--writemenu) WRITEMENU=1 ; shift ;;
    -k|--kernel) SEARCHKERNEL=1 ; shift ;;
    -d|--debug) 
        DEBUG=1
        shift 
        case "$1" in
            -n|--nocolor) COLOR=0 ;;
        esac    
    ;;
    -h|--help) help ;;
esac


# look for GRUB signature in MBR, Linux(0x83) and Extended(0x05, 0x0f) partitions 
# skip EFI (0xee), swap (0x82) and BSD (0xa5, 0xa6, 0xa9) partitions
# !!! deliberately skip Windows Recovery (0x27) partition

# look for strings 'BOOTMGR' or 'NTLDR' in all other partitions bootsector and
# if found mount the partition and search for files bootmgr or ntldr   

# in debug mode, output bootsector of NTFS and FAT  partitons in ASCII and HEX
# output partition table


# get all partitions ids
eval `fdisk -l 2>/dev/null | awk '/^\/dev/ { if ($2 == "*") ID = $6 ; else ID = $5 ; print $1"="ID";" }' | sed s'|/dev/||'`


for dev in ${devs[*]} ; do
    if [ -b $dev ] ; then
        printf "\n - reading MBR on disk %-27s ..." "$dev"
        dd if=$dev bs=512 count=1 2> /dev/null | grep -q GRUB && {
printf " --> Grub found in MBR" 
[ "$dev" == "/dev/sda" ] && makeactive=makeactive
}
        for pt in `fdisk -l $dev 2>/dev/null | awk '/^\/dev/ { print $1 }'` ; do
            PT=$(basename $pt)    fs=${!PT}
            f=x${fs} ; FS=${!f} 
            if [ "$DEBUG" ] ; then
                case $fs in
                    6|b|c|e|7) printbs $pt ;;
                esac
            else
                case $fs in
                    ee|af|a5|a6|a9|82|27) 
                    printf "\n - skiping partition   %-11s %-15s" "$pt" "($FS)"
                    ;;
                    5|f)
                    printf "\n - reading bootsector  %-11s %-15s ..."  "$pt" "($FS)"
                    dd if=$pt bs=512 count=1 2> /dev/null | grep -q GRUB && printf " --> Grub found in %s" $pt
                    ;;
                    83)
                    printf "\n - reading bootsector  %-11s %-15s ..."  "$pt" "($FS)"
                    dd if=$pt bs=512 count=1 2> /dev/null | grep -q GRUB && printf " --> Grub found in %s" $pt
					[ "${SEARCHKERNEL}" ] && searchkernel $pt
                    ;;
                    *)
                    printf "\n - searching partition %-11s %-15s ..." "$pt" "($FS)"
                    dd if=$pt bs=512 count=1 2> /dev/null | grep -q NTLDR && findbootmgr $pt ntldr $fs
                    dd if=$pt bs=512 count=1 2> /dev/null | grep -q BOOTMGR && findbootmgr $pt bootmgr $fs
                    ;;
                esac
            fi    
        done
        [ "$DEBUG" ] && printptbl $dev
    fi
done

printf "\n"

[ -d /findgrub ] && rmdir /findgrub

# Main - END ---------------------------

exit 0
# End Of ScripT
