#!/bin/sh # # Insert a list of installed kernels in a grub menu.lst file # Copyright 2001 Wichert Akkerman # # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # # Contributors: # Jason Thomas # David B.Harris # Marc Haber # Crispin Flowerday # Kris Van Hees # Abort on errors set -e abort() { message=$@ echo >&2 echo -e "$message" >&2 echo >&2 exit 1 } find_grub_dir () { echo -n "Searching for GRUB installation directory ... " >&2 for d in $grub_dirs ; do if [ -d "$d" ] ; then grub_dir="$d" break fi done if [ -z "$grub_dir" ] ; then abort "No GRUB directory found.\n To create a template run 'mkdir /boot/grub' first.\n To install grub, install it manually or try the 'grub-install' command.\n ### Warning, grub-install is used to change your MBR. ###" else echo "found: $grub_dir ." >&2 fi echo $grub_dir } find_device () { mount_point=$1 # Autodetect current root device device= if [ -f /etc/fstab ] ; then script=' /^#/ { next } $2 == "'"$mount_point"'" { print $1 ; exit } ' device=`awk "$script" /etc/fstab` fi if [ -n "$device" ] ; then case "$device" in LABEL=* | UUID=*) device=`readlink -f "$(findfs $device)"` ;; *) device=`readlink -f "$device"` ;; esac fi echo $device } find_root_device () { device=$(find_device "/") if [ -z "$device" ]; then echo "$PROG: Cannot determine root device. Assuming /dev/hda1" >&2 echo "This error is probably caused by an invalid /etc/fstab" >&2 device=/dev/hda1 fi echo $device } # Usage: convert_raid1 os_device # Checks if os_device is a software raid1. # If so, converts to first physical device in array. convert_raid1 () { case $1 in /dev/md[0-9]) : ;; # Continue *) return 1 ;; esac [ -x /sbin/mdadm ] || return 1 # Check that the raid device is raid1 raidlevel=$(mdadm -D -b $1 | grep "^ARRAY" | \ sed "s/^.*level=//" | cut -d" " -f1) [ "$raidlevel" = "raid1" ] || return 1 # Take only the first device that makes up the raid raiddev=$(mdadm -D -b $1 | grep "^ devices=" | \ sed "s/^ devices=//" | cut -d"," -f1) [ -n "$raiddev" ] || return 1 echo $raiddev return 0 } # Usage: convert os_device # Convert an OS device to the corresponding GRUB drive. # This part is OS-specific. convert () { # First, check if the device file exists. if test -e "$1"; then : else echo "$1: Not found or not a block device." 1>&2 exit 1 fi host_os=`uname -s | tr '[A-Z]' '[a-z]'` # Break the device name into the disk part and the partition part. case "$host_os" in linux) tmp_disk=`echo "$1" | sed -e 's%\([sh]d[a-z]\)[0-9]*$%\1%' \ -e 's%\(fd[0-9]*\)$%\1%' \ -e 's%/part[0-9]*$%/disc%' \ -e 's%\(c[0-7]d[0-9]*\).*$%\1%'` tmp_part=`echo "$1" | sed -e 's%.*/[sh]d[a-z]\([0-9]*\)$%\1%' \ -e 's%.*/fd[0-9]*$%%' \ -e 's%.*/floppy/[0-9]*$%%' \ -e 's%.*/\(disc\|part\([0-9]*\)\)$%\2%' \ -e 's%.*c[0-7]d[0-9]*p*%%'` ;; gnu) tmp_disk=`echo "$1" | sed 's%\([sh]d[0-9]*\).*%\1%'` tmp_part=`echo "$1" | sed "s%$tmp_disk%%"` ;; freebsd|*/kfreebsd) tmp_disk=`echo "$1" | sed 's%r\{0,1\}\([saw]d[0-9]*\).*$%r\1%' \ | sed 's%r\{0,1\}\(da[0-9]*\).*$%r\1%'` tmp_part=`echo "$1" \ | sed "s%.*/r\{0,1\}[saw]d[0-9]\(s[0-9]*[a-h]\)%\1%" \ | sed "s%.*/r\{0,1\}da[0-9]\(s[0-9]*[a-h]\)%\1%"` ;; netbsd|*/knetbsd) tmp_disk=`echo "$1" | sed 's%r\{0,1\}\([sw]d[0-9]*\).*$%r\1d%' \ | sed 's%r\{0,1\}\(fd[0-9]*\).*$%r\1a%'` tmp_part=`echo "$1" \ | sed "s%.*/r\{0,1\}[sw]d[0-9]\([abe-p]\)%\1%"` ;; *) echo "update-grub does not support your OS yet." 1>&2 exit 1 ;; esac # Get the drive name. tmp_drive=`grep -v '^#' $device_map | grep "$tmp_disk *$" \ | sed 's%.*\(([hf]d[0-9][a-g0-9,]*)\).*%\1%'` # If not found, print an error message and exit. if test "x$tmp_drive" = x; then echo "$1 does not have any corresponding BIOS drive." 1>&2 exit 1 fi if test "x$tmp_part" != x; then # If a partition is specified, we need to translate it into the # GRUB's syntax. case "$host_os" in linux) echo "$tmp_drive" | sed "s%)$%,`expr $tmp_part - 1`)%" ;; gnu) if echo $tmp_part | grep "^s" >/dev/null; then tmp_pc_slice=`echo $tmp_part \ | sed "s%s\([0-9]*\)[a-g]*$%\1%"` tmp_drive=`echo "$tmp_drive" \ | sed "s%)%,\`expr "$tmp_pc_slice" - 1\`)%"` fi if echo $tmp_part | grep "[a-g]$" >/dev/null; then tmp_bsd_partition=`echo "$tmp_part" \ | sed "s%[^a-g]*\([a-g]\)$%\1%"` tmp_drive=`echo "$tmp_drive" \ | sed "s%)%,$tmp_bsd_partition)%"` fi echo "$tmp_drive" ;; freebsd|*/kfreebsd) if echo $tmp_part | grep "^s" >/dev/null; then tmp_pc_slice=`echo $tmp_part \ | sed "s%s\([0-9]*\)[a-h]*$%\1%"` tmp_drive=`echo "$tmp_drive" \ | sed "s%)%,\`expr "$tmp_pc_slice" - 1\`)%"` fi if echo $tmp_part | grep "[a-h]$" >/dev/null; then tmp_bsd_partition=`echo "$tmp_part" \ | sed "s%s\{0,1\}[0-9]*\([a-h]\)$%\1%"` tmp_drive=`echo "$tmp_drive" \ | sed "s%)%,$tmp_bsd_partition)%"` fi echo "$tmp_drive" ;; netbsd|*/knetbsd) if echo $tmp_part | grep "^[abe-p]$" >/dev/null; then tmp_bsd_partition=`echo "$tmp_part" \ | sed "s%\([a-p]\)$%\1%"` tmp_drive=`echo "$tmp_drive" \ | sed "s%)%,$tmp_bsd_partition)%"` fi echo "$tmp_drive" ;; esac else # If no partition is specified, just print the drive name. echo "$tmp_drive" fi } # Usage: convert_default os_device # Convert an OS device to the corresponding GRUB drive. # Calls OS-specific convert, and returns a default of # (hd0,0) if anything goes wrong convert_default () { # Check if device is software raid1 array if tmp_dev=$(convert_raid1 $1 2>/dev/null) ; then : # Use device returned by convert_raid1 else tmp_dev=$1 fi if tmp=$(convert $tmp_dev 2>/dev/null) ; then echo $tmp else echo "(hd0,0)" fi } ## Configuration Options # directory's to look for the grub installation and the menu file grub_dirs="/boot/grub /boot/boot/grub" # The grub installation directory grub_dir=$(find_grub_dir) # Full path to the menu.lst menu_file=$grub_dir/menu.lst # the device for the / filesystem root_device=$(find_root_device) # the device for the /boot filesystem boot_device=$(find_device "/boot") # Full path to the device.map device_map=$grub_dir/device.map # Default kernel options, overidden by the kopt statement in the menufile. kopt="root=$root_device ro" # Drive(in GRUB terms) where the kernel is located. Overridden by the # kopt statement in menufile. # if we don't have a device.map then we can't use the convert function. if test -f "$device_map" ; then if test -z "$boot_device" ; then grub_root_device=$(convert_default "$root_device") else grub_root_device=$(convert_default "$boot_device") fi else grub_root_device="(hd0,0)" fi # should grub create the alternative boot options in the menu alternative="true" # should grub lock the alternative boot options in the menu lockalternative="false" # options to use with the alternative boot options altoptions="(recovery mode) single" # controls howmany kernels are listed in the menu.lst, # this does not include the alternative kernels howmany="all" # should grub create a memtest86 entry memtest86="true" # stores the command line arguments command_line_arguments=$1 # Default options to use in a new menu.lst . This will only be used if menu.lst # doesn't already exist. Only edit the lines between the two "EOF"s. The others are # part of the script. newtemplate=$(tempfile) cat > "$newtemplate" <B # This compares version numbers of the form # 2.4.14-random > 2.4.14-ac10 > 2.4.14 > 2.4.14-pre2 > # 2.4.14-pre1 > 2.4.13-ac99 CompareVersions() { echo "Comparing [$1] and [$2]" >/dev/stderr # First split the version number and remove any '.' 's or dashes v1=$(echo $1 | sed -e 's![-\.]\+! !g' -e 's![\.\-]!!g') v2=$(echo $2 | sed -e 's![-\.]\+! !g' -e 's![\.\-]!!g') # we weight different kernel suffixes here # ac = 50 # pre = -50 # rc = -40 # test = -60 # others are given 99 v1=$(echo $v1 | sed -e 's! k7! 786 !g' -e 's! ac! 50 !g' -e 's! rc! -40 !g' -e 's! pre! -50 !g' -e 's! test! -60 !g' -e 's![^-0-9 ]\+!99!g') v2=$(echo $v2 | sed -e 's! k7! 786 !g' -e 's! ac! 50 !g' -e 's! rc! -40 !g' -e 's! pre! -50 !g' -e 's! test! -60 !g' -e 's![^-0-9 ]\+!99!g') result=0; v1finished=0; v2finished=0; while [ $result -eq 0 ] && [ $v1finished -eq 0 ] && [ $v2finished -eq 0 ]; do if [ "$v1" = "" ]; then v1comp=0; v1finished=1 else set -- $v1; v1comp=$1; shift; v1=$* fi if [ "$v2" = "" ]; then v2comp=0; v2finished=1 else set -- $v2; v2comp=$1; shift; v2=$* fi if [ $v1comp -gt $v2comp ]; then result=1 elif [ $v1comp -lt $v2comp ]; then result=-1 fi done # finally return the result echo $result } # looks in the directory specified for an initrd image with the version specified FindInitrdName() { # strip trailing slashes directory=$(echo $1 | sed -e 's#/*$##') version=$2 # initrd # initrd.img # initrd-lvm # .*.gz initrdName="" names="initrd initrd.img initrd-lvm" compressed="gz" for n in $names ; do # make sure we haven't already found it if [ -z "$initrdName" ] ; then if [ -f "$directory/$n$version" ] ; then initrdName="$n$version" break else for c in $compressed ; do if [ -f "$directory/$n$version.$c" ] ; then initrdName="$n$version.$c" break fi done fi else break fi done # return the result echo $initrdName } get_kernel_opt() { kernel_version=$1 version=$(echo $kernel_version | sed 's/^[^0-9]*//') version=$(echo $version | sed 's/[-\+\.]/_/g') if [ -n "$version" ] ; then while [ -n "$version" ] ; do currentOpt="$(eval "echo \${kopt_$version}")" if [ -n "$currentOpt" ] ; then break fi version=$(echo $version | sed 's/_\?[^_]*$//') done fi if [ -z "$currentOpt" ] ; then currentOpt=$kopt fi echo $currentOpt } write_kernel_entry() { kernel_version=$1 recovery_desc=$2 lock_alternative=$3 grub_root_device=$4 kernel=$5 kernel_options=$6 recovery_suffix=$7 initrd=$8 savedefault=$9 echo "title Debian GNU/Linux, kernel $kernel_version $recovery_desc" >> $buffer # lock the alternative options if test x"$lock_alternative" = x"true" ; then echo "lock" >> $buffer fi echo "root $grub_root_device" >> $buffer echo "kernel $kernel $kernel_options $recovery_suffix" >> $buffer if [ -n "$initrd" ]; then echo "initrd $initrd" >> $buffer fi if test x"$savedefault" = x"true" ; then echo "savedefault" >> $buffer fi echo "boot" >> $buffer echo >> $buffer } echo -n "Testing for an existing GRUB menu.list file... " # Test if our menu file exists if [ -f "$menu_file" ] ; then menu="$menu_file" rm -f $newtemplate unset newtemplate echo "found: $menu_file ." cp -f "$menu_file" "$menu_file~" else # if not ask user if they want us to create one menu="$menu_file" echo echo echo -n "Could not find $menu_file file. " if [ "-y" = "$command_line_arguments" ] ; then echo echo "Generating $menu_file" answer=y else echo -n "Would you like $menu_file generated for you? " echo -n "(y/N) " read answer fi case "$answer" in y* | Y*) cat "$newtemplate" > $menu_file rm -f $newtemplate unset newtemplate ;; *) abort "Not creating $menu_file as you wish" ;; esac fi # Extract the kernel options to use kopt=$(GetMenuOpt "kopt" "$kopt") # Extract options for specific kernels eval $(ExtractMenuOpts "\(kopt_[a-zA-Z0-9_]\+\)") CustomKopts=$(GetMenuOpts "\(kopt_[a-zA-Z0-9_]\+\)") # Extract the grub root grub_root_device=$(GetMenuOpt "groot" "$grub_root_device") # Extract the old recovery value alternative=$(GetMenuOpt "recovery" "$alternative") # Extract the alternative value alternative=$(GetMenuOpt "alternative" "$alternative") # Extract the lockalternative value lockalternative=$(GetMenuOpt "lockalternative" "$lockalternative") # Extract the howmany value howmany=$(GetMenuOpt "howmany" "$howmany") # Extract the memtest86 value memtest86=$(GetMenuOpt "memtest86" "$memtest86") # Generate the menu options we want to insert buffer=$(tempfile) echo $start >> $buffer echo "## lines between the AUTOMAGIC KERNELS LIST markers will be modified" >> $buffer echo "## by the debian update-grub script except for the default options below" >> $buffer echo >> $buffer echo "## DO NOT UNCOMMENT THEM, Just edit them to your needs" >> $buffer echo >> $buffer echo "## ## Start Default Options ##" >> $buffer echo "## default kernel options" >> $buffer echo "## default kernel options for automagic boot options" >> $buffer echo "## If you want special options for specifiv kernels use kopt_x_y_z" >> $buffer echo "## where x.y.z is kernel version. Minor versions can be omitted." >> $buffer echo "## e.g. kopt=root=/dev/hda1 ro" >> $buffer echo "# kopt=$kopt" >> $buffer if [ -n "$CustomKopts" ] ; then echo "$CustomKopts" >> $buffer fi echo >> $buffer echo "## default grub root device" >> $buffer echo "## e.g. groot=(hd0,0)" >> $buffer echo "# groot=$grub_root_device" >> $buffer echo >> $buffer echo "## should update-grub create alternative automagic boot options" >> $buffer echo "## e.g. alternative=true" >> $buffer echo "## alternative=false" >> $buffer echo "# alternative=$alternative" >> $buffer echo >> $buffer echo "## should update-grub lock alternative automagic boot options" >> $buffer echo "## e.g. lockalternative=true" >> $buffer echo "## lockalternative=false" >> $buffer echo "# lockalternative=$lockalternative" >> $buffer echo >> $buffer echo "## altoption boot targets option" >> $buffer echo "## multiple altoptions lines are allowed" >> $buffer echo "## e.g. altoptions=(extra menu suffix) extra boot options" >> $buffer echo "## altoptions=(recovery mode) single" >> $buffer if ! grep -q "^# altoptions" $menu ; then echo "# altoptions=$altoptions" >> $buffer else grep "^# altoptions" $menu >> $buffer fi echo >> $buffer echo "## controls how many kernels should be put into the menu.lst" >> $buffer echo "## only counts the first occurence of a kernel, not the" >> $buffer echo "## alternative kernel options" >> $buffer echo "## e.g. howmany=all" >> $buffer echo "## howmany=7" >> $buffer echo "# howmany=$howmany" >> $buffer echo >> $buffer echo "## should update-grub create memtest86 boot option" >> $buffer echo "## e.g. memtest86=true" >> $buffer echo "## memtest86=false" >> $buffer echo "# memtest86=$memtest86" >> $buffer echo >> $buffer echo "## ## End Default Options ##" >> $buffer echo >> $buffer echo -n "Searching for splash image... " if [ -f /boot/grub/splash.xpm.gz ]; then echo "found: /boot/grub/splash.xpm.gz ." echo "splashimage=${grub_root_device}${grub_dir}/splash.xpm.gz" >> $buffer echo >> $buffer else echo "none found, skipping..." fi sortedKernels="" for kern in $(/usr/bin/find /boot -name vmlinuz-\* | grep -v dpkg-*) ; do # found a kernel newerKernels="" v1=$(echo $kern | sed -e 's!^/\([^/]*/\)*!!') for i in $sortedKernels ; do v2=$(echo $i | sed -e 's!^/\([^/]*/\)*!!') res=$(CompareVersions "$v1" "$v2") if [ "$kern" != "" ] && [ "$res" -gt 0 ] ; then newerKernels="$newerKernels $kern $i" kern="" else newerKernels="$newerKernels $i" fi done if [ "$kern" != "" ] ; then newerKernels="$newerKernels $kern" fi sortedKernels="$newerKernels" done if test -f "/boot/vmlinuz.old" ; then sortedKernels="/boot/vmlinuz.old $sortedKernels" fi if test -f "/boot/vmlinuz" ; then sortedKernels="/boot/vmlinuz $sortedKernels" fi # figure out where grub looks for the kernels at boot time kernel_dir=/boot if [ -n "$boot_device" ] ; then kernel_dir= fi ## heres where we start writing out the kernel entries counter=0 for kern in $sortedKernels ; do counter=$(($counter + 1)) if test ! x"$howmany" = x"all" ; then if [ $counter -gt $howmany ] ; then break fi fi kernelDir=$(dirname $kern | sed -e 's|/boot/|/|') kernelName=$(basename $kern) kernelVersion=$(echo $kernelName | sed -e 's/vmlinuz//') initrdName=$(FindInitrdName "/boot$kernelDir" "$kernelVersion") initrd="" kernel=$kernel_dir$kernelDir/$kernelName if [ -n "$initrdName" ] ; then initrd=$kernel_dir$kernelDir/$initrdName fi echo "Found kernel: $kernel" if [ "$kernelName" = "vmlinuz" ]; then if [ -L "/boot$kernelDir/$kernelName" ]; then kernelVersion=`readlink -f "/boot$kernelDir/$kernelName"` kernelVersion=$(echo $kernelVersion | sed -e 's/.*vmlinuz-//') kernelVersion="$kernelVersion Default" else kernelVersion="Default" fi fi if [ "$kernelName" = "vmlinuz.old" ]; then if [ -L "/boot$kernelDir/$kernelName" ]; then kernelVersion=`readlink -f "/boot$kernelDir/$kernelName"` kernelVersion=$(echo $kernelVersion | sed -e 's/.*vmlinuz-//') kernelVersion="$kernelVersion Previous" else kernelVersion="Previous" fi fi kernelVersion=$(echo $kernelVersion | sed -e 's/^-//') currentOpt=$(get_kernel_opt $kernelVersion) write_kernel_entry "$kernelVersion" "" "" "$grub_root_device" "$kernel" \ "$currentOpt" "" "$initrd" "true" # insert the alternative boot options if test ! x"$alternative" = x"false" ; then # for each altoptions line do this stuff sed -ne 's/# altoptions=\(.*\)/\1/p' $buffer | while read line; do descr=$(echo $line | sed -ne 's/\(([^)]*)\)[[:space:]]\(.*\)/\1/p') suffix=$(echo $line | sed -ne 's/\(([^)]*)\)[[:space:]]\(.*\)/\2/p') write_kernel_entry "$kernelVersion" "$descr" "$lockalternative" \ "$grub_root_device" "$kernel" "$currentOpt" "$suffix" "$initrd" \ "true" done fi done memtest86names="memtest86 memtest86+" if test ! x"$memtest86" = x"false" ; then for name in $memtest86names ; do if test -f "/boot/$name.bin" ; then kernelVersion="$name" kernel="$kernel_dir/$name.bin" currentOpt= initrd= echo "Found kernel: $kernel" write_kernel_entry "$kernelVersion" "" "" "$grub_root_device" \ "$kernel" "$currentOpt" "" "$initrd" "false" fi done fi echo $end >> $buffer echo -n "Updating $menu ... " # Insert the new options into the menu if ! grep -q "^$start" $menu ; then cat $buffer >> $menu rm -f $buffer else umask 077 sed -e "/^$start/,/^$end/{ /^$start/r $buffer d } " $menu > $menu.new cat $menu.new > $menu rm -f $buffer $menu.new fi echo "done" echo