From Mageia wiki
Jump to: navigation, search

Script to create a boot CD, DVD or USB stick for your system

No matter what systems are installed on your machine, they all depend on the master boot loader performing correctly.
If for example, a new install destroys your bootloader, then it's really handy to have an alternative way to boot any of your installations.
This will produce just that, a custom boot CD, DVD or USB stick to boot your systems.
It uses grub2 to create a boot iso and then offers to write it to the required media.
Various options are available during the creation, including erasure of CD/DVD-RW media, making it very easy to use.
The script installs any extra packages that it requires, and also offers to uninstall them on completion. If you do not remove them a list of the packages is created in a text file.
Please contact me if you find a bug.
Follow the instructions in the "Use:" section in the script below.

#!/bin/bash
# mk-grub2-boot
###########################################################
# Author - Barry Jackson (barjac)
###########################################################
# NOTE: !!!!!! NOT FOR UEFI SYSTEMS !!!!!
# Description:
# This script creates a Mageia GRUB2 boot CD/DVD or USB stick.
# An option to erase RW media is available before burning.
# All systems on the machine should be bootable and will be
# included in the menu.
# It does not matter whether grub, lilo, grub2 or whatever is
# the current bootloader.
# Grub2 and other packages are temporarily installed to create 
# the files, but this makes no permanent changes to your current 
# bootloader. The script offers to remove all extra packages it
# installs and restore any changed theme. (It may leave one orphan)
# os-prober is used to probe for all systems, however if you are 
# using grub2 and have it disabled in /etc/default/grub then it 
# will not be used.
# A graphical theme may be used for the boot menu.
# Note that a theme may cause the menu to be too slow on old
# hardware, so in this case you can opt to not use one.
###########################################################
# Use:
# Save this script as mk-grub2-boot
# Make it executable: chmod +x mk-grub2-boot
# Run it as root from the same directory.
# su
# ./mk-grub2-boot
###########################################################
ver=2.8
# Changelog:
# 2.8 17/12/2014 Don't try to remove orphan lib(64)lvm2app2.2
# 2.7 12/09/2013 Minor update to CD/DVD-RW media detection
# 2.6 10/09/2013 Don't use auto-orphans
# 2.5 28/07/2013 Add ESC to abort usb detection loop and fix
#                stray characters on ESC in CD and usb detection
# 2.4 24/07/2013 Really fix USB detection ;) Fix typo
#                Added DVD-RW erase option.
# 2.3 21/07/2013 Fix USB detection bug.
# 2.2 18/07/2013 Really fix cleanup messages, thanks Bogdan ;)
# 2.1 17/07/2013 Added option to erase a CD-RW before burn
# 2.0 16/07/2013 Fix CD Write problem caused by earlier code changes
#                Fix removal of grub2 when not prev installed
#                Fix incorrect cleanup messages in txt files
# 1.9 15/07/2013 Fix minor code duplication
# 1.8 14/07/2013 Fixed uninitialised var
#                Validate answer to theme to be used
#                Fix typos in created txt file
#                Don't mention current theme if there isn't one
# 1.7 12/07/2013 Add date to txt filename and fix bad entries
# 1.6 12/07/2013 Add * to indicate current theme in list.
#                Corrected some imperfect bash syntax.
# 1.5 11/07/2013 Added option to use any available theme from
#                currently enabled repos
#                Added mk-grub2-boot.txt in current dir to
#                record packages not removed or restored.
# 1.4 11/07/2013 Code clean up - removed redundant vars
# 1.3 10/07/2013 Corrected some typos, fixed usb detection issue
# 1.2 04/07/2013 Clarify opening question and only ask if iso exists
# 1.1 14/06/2013 Catch user error plugging usb drive
# 1.0 13/06/2013 Faster transactioned deps install/uninstall
# 0.9 13/06/2013 Better script structure, better dd safety,
#                ESC to abort CD detect, don't needlessly
#                install theme
# 0.8 13/06/2013 Fix udisks uninstalled too early
# 0.7 10/06/2013 Force umount of CDROM before write
# 0.6 10/06/2013 Option to use theme or not
# 0.5 10/06/2013 Use array in devlst() not tmp file
# 0.4 09/06/2013 Fix bug in udiskinfo(), improved listusbdrives()
# 0.3 09/06/2013 Don't add theme. Better usb detection
# 0.2 07/06/2013 Add options to burn CD or write USB.
# 0.1 06/06/2013 First release
###########################################################
# Configuration:
# None required :)
###########################################################
# License:
# Totally free with no warranty whatsoever.
###########################################################
# Thanks go to:
# Bogdan Gruescu for much testing and many bug reports :) 
###########################################################

# Functions------------------------------------------------

# Talk to user
confirm() {
params=( "$@" )
rval=3
while [ $rval -gt 2 ];do
    echo -ne ${params[0]}" "${params[1]}" "
    ans=
    rval=
    read ans
    if [[ -z "$ans" ]] || (( ${#ans} > 1 )); then
        rval=3
    else
        case "$ans" in
            [${params[2]}]*)
                rval=0
                ;;
            [${params[3]}]*)
                rval=1
                ;;
	    [${params[4]}]*)
                rval=2
                ;;
            *)
                rval=3
                ;;
        esac
    fi
done
    return $rval
}

# Install requires
getreqs() {
[[ -e /boot/grub2/grub.cfg ]] || { urpmi grub2 --no-suggests; grub2=1; }
instlist=()
rpm -q os-prober > /dev/null || instlist+=("os-prober")
rpm -q memtest86+ > /dev/null || instlist+=("memtest86+")
rpm -q udisks > /dev/null || instlist+=("udisks")
rpm -q xorriso > /dev/null || instlist+=("xorriso")
if confirm "\nDo you wish to use a theme for the boot menu?" "[Y/N]" "Yy" "Nn"; then
gettheme
rpm -q $theme2use > /dev/null || instlist+=($theme2use)
usetheme=1
else
usetheme=0
fi
if (( ${#instlist[@]} > 0 )); then
urpmi --auto ${instlist[@]} || { echo "Install of package failed"; exit 0; }
fi
[[ $grub2 = 1 ]] && instlist+=("grub2")
}

# Clean up
cleanup() {
now=$(dte)
if [[ $grub2 != 1 ]] && (( ${#theme2use} > 0 )); then
  instlist=($(echo ${instlist[@]} | sed "s/$theme2use//"))
fi
if (( ${#instlist[@]} > 0 )); then
  if confirm "\nDo you wish to remove all packages that were not originally installed on your machine?" "[Y/N]" "Yy" "Nn"; then
    urpme ${instlist[@]}
  else
    echo "The following packages were installed by mk-grub2-boot and you opted not to remove them: ${instlist[@]}" > mk-grub2-boot-$now.txt
  fi
fi

if [[ $grub2 != 1 ]] && [[ $burnwrite_only != 1 ]]; then
  if [[ ${#theme2use} > 0 ]] && [[ ${#themeinstalled} = 0 ]]; then
    if rpm -q $theme2use > /dev/null && confirm "\nA boot menu theme ($theme2use) was installed, do you wish to remove it?" "[Y/N]" "Yy" "Nn"; then
      urpme $theme2use
    else
      echo "A boot menu theme ($theme2use) was installed and you opted not to remove it." >> mk-grub2-boot-$now.txt
    fi
  else
    if [[ ${#theme2use} > 0 ]] && [[ ${#themeinstalled} > 0 ]] && [[ $themeinstalled != $theme2use ]]; then
      if confirm "\nYour boot menu theme was changed, do you wish to restore the original ($themeinstalled)?" "[Y/N]" "Yy" "Nn"; then
	urpmi $themeinstalled
      else
	echo "Your original theme ($themeinstalled) was changed (to $theme2use) and you opted not to restore it." >> mk-grub2-boot-$now.txt
      fi
    fi
  fi
fi
rm -rf grub2-boot
}

isnumeric(){ 
echo "$@" | grep -q -v "[^0-9]"
}

# Get theme
gettheme() {
themes=($(urpmq grub2 -y | grep theme))
i=0
echo
while true; do
mark=
if (( ${#themes[$i]} > 0 )); then
rpm -q --qf %{NAME} ${themes[$i]} > /dev/null 2>&1 && { themeinstalled=${themes[$i]}; mark="*"; }
echo "#$i	${themes[$i]}	$mark"
i=$((i+1))
else
(( ${#themeinstalled} > 0 )) && echo -e "\nYour current theme is marked with *"
echo -e "\nEnter the # of the theme you wish to use"
read ans
isnumeric $ans && (( $ans >= 0 )) && (( $ans <= (( ${#themes[@]}-1 )) )) || \
{ echo -e "\nInvalid selection!\n"; i=0; continue; }
break
fi
done
theme2use=${themes[$ans]}
}

# Create iso
createiso() {
getreqs

# Start with clean iso dir
  [[ -d grub2-boot ]] && rm -rf grub2-boot

# Create iso tree
  mkdir -p grub2-boot/boot/grub

# Add memtest to tree
  cp /boot/memtest.bin grub2-boot/boot/

# Copy grub2 files to tree
  cp -r /boot/grub2/* grub2-boot/boot/grub/
  configedit

# Create iso
  grub2-mkrescue -o grub2-boot.iso grub2-boot/ && \
  echo -e "\ngrub2-boot.iso was written sucessfully :)\n"
  burnwrite
}

# Function to output numeric date string
dte()
{
echo -n $(date +%Y%m%d%H%M)
}

# Get udisks info
udiskinfo() {
if [[ $2 = "media" ]];then
output=$(udisks --show-info $1|grep $2:|tail -1|cut -d: -f2)
else
output=$(udisks --show-info $1|grep -m1 $2:|cut -d: -f2)
fi
echo $output
}

# List all usb drives
listusbdrives () {
drives=()
for dev in $(udisks --enumerate |rev|cut -d/ -f1|rev|tr -d [:digit:]|sort -u ); do
  int=$(udiskinfo /dev/$dev "interface")
  [[ $int = "usb" ]] && drives+=($dev)
done
}

# Detect USB
detectusb() {
escape=$'\x1b'
while true; do
unset d
if confirm "\nMake sure the USB stick is UNPLUGGED now. Ready?" "[Y/N]" "Yy" "Nn";then
listusbdrives
a=("${drives[@]}")
  echo -n -e "\nPLUG IN the usb stick now. (ESC to abort)\nDetecting."
  while true; do
    listusbdrives
    b=("${drives[@]}")
    read -s -n1 -t1 key
      if [[ $key = $escape ]]; then
	esc=1
	break
      else 
	key=""
      fi
    (( ${#a[@]} != ${#b[@]} )) && break
    echo -n "."
  done
  ( (( ${#a[@]} > ${#b[@]} )) || [[ esc = 1 ]] ) && { echo; continue; }
  echo
  echo
fi
  for item1 in "${b[@]}"; do
    for item2 in "${a[@]}"; do
    found=0
        if [[ $item1 = $item2 ]]; then
	  found=1; break
        fi
    done
    [[ $found = 1 ]] && continue
    d=$item1; break
  done
  break
done
}

# Blank CD
rw=
blankcd() {
if confirm "\nWould you like to erase a CD/DVD-RW first?" "[Y/N]" "Yy" "Nn"; then
escape=$'\x1b'
echo -n -e "\nInsert a disk to erase now. (ESC to abort)\nLooking for RW media ..."
cdlst=($(udisks --enumerate | grep "devices/sr"|rev|cut -d/ -f1|rev))
[[ "${#cdlst}" -eq 0 ]] && { echo "No CD/DVD ROM found"; cdr=''; return 0; }
found=
while (( ${#found} == 0 )); do
  read -s -n1 -t1 key
  if [[ $key = $escape ]]; then
      esc=1
      break
  else 
      key=""
  fi
echo -n "."
  for cdr in ${cdlst[@]}; do
    found=$(udiskinfo /dev/$cdr "media")
    ( [[ $found = optical_cd_rw ]] || \
    [[ $found = optical_dvd_rw ]] || \
    [[ $found = optical_dvd_plus_rw ]] ) && \
    { rw=1; echo; break; }
  done
done
fi
if [[ $rw = 1 ]] && confirm "\nErase disk in /dev/$cdr now?" "[Y/N]" "Yy" "Nn"; then
  wodim speed=16 -eject dev=/dev/$cdr -blank fast && echo -e "\nDisk erased successfully"
fi
}

# Detect CDROM
detectcd() {
escape=$'\x1b'
echo -n -e "\nInsert a blank disk now. (ESC to abort)\nDetecting..."
cdlst=($(udisks --enumerate | grep "devices/sr"|rev|cut -d/ -f1|rev))
[[ ${#cdlst} -eq 0 ]] && { echo "No media found"; cdr=''; return 0; }
while [[ $blank != 1 ]]; do
  read -s -n1 -t1 key
  if [[ $key = $escape ]]; then
      esc=1
      break
  else 
      key=""
  fi
echo -n "."
for cdr in ${cdlst[@]}; do
blank=$(udiskinfo /dev/$cdr "blank")
[[ $blank = 1 ]] && { echo; break; }
done
done
}

# Write USB
writeusb() {
detectusb
if (( ${#d} != 0 )); then
  if confirm "Found /dev/$d ( $(udiskinfo /dev/$d "model") ) All data on /dev/$d will be lost - OK?" "[Y/N]" "Yy" "Nn"; then
    echo -e "\nPlease wait - writing to USB..."
    dd if=grub2-boot.iso of=/dev/$d bs=1M && \
    echo -e "\nUSB write complete - wait for USB light to stop flashing before removing device."
  fi
fi
}

# Burn CD
burncd() {
detectcd
if (( ${#cdr} > 0 )) && [[ $esc != 1 ]]; then
# Unmount CDROM in case a disk was swapped
umount $(df | grep /dev/$cdr | cut -d% -f2) > /dev/null 2>&1
  if confirm "\nBurn iso to /dev/$cdr - OK?" "[Y/N]" "Yy" "Nn"; then
    wodim speed=16 -eject dev=/dev/$cdr grub2-boot.iso && echo "Disk write complete"
  fi
fi
}

# Offer to burn CD or Write USB
burnwrite() {
rpm -q udisks > /dev/null || { instlist+=("udisks"); urpmi --auto udisks; }
while true; do
  if confirm "\nWould you like to (B)urn a CD/DVD, (W)rite a USB stick or (Q)uit?" "[B/W/Q]" "Bb" "Ww" "Qq"; then
    blankcd
    burncd
  elif (( $? == 1 )); then
    writeusb
  else
    break
  fi
done
}

# Get numeric position of letter in alphabet (a=0, b=1)
alphapos() {
y=0
for x in {a..z}; do
  [[ $1 = $x ]] && { echo $y; break; }
  y=$((y+1))
done
}

# Get array of root devices for all systems
devlst() {
devs=($(df -h /boot |(read; awk '{print $1;exit}'))" "$(os-prober|cut -d: -f1))
}

# Create 'hdx,msdosy' from /dev/sdxy
mkhdpart() {
hdalpha=$(echo $1 |tr -d [:digit:] | cut -dd -f3)
hdn=$(alphapos $hdalpha)
part=$(echo $1 | grep -P -o "[0-9]+")
hdstr="'"hd$hdn",msdos"$part"'"
}

# Get uuid from /dev/sdxy
getuuid() {
uuidstr=$(udiskinfo $1 "uuid")
}

# Modify grub.cfg and theme for boot iso
configedit() {
if (( $usetheme == 1 )); then
  sed -i 's/"Mageia Grub 2 Menu"/"Mageia Grub 2 Rescue"/' $(find . -name theme.txt)
else
  rm grub2-boot/boot/grub/themes -rf
  sed -i "s/^set theme=.*//" grub2-boot/boot/grub/grub.cfg
  sed -i "s/^export theme//" grub2-boot/boot/grub/grub.cfg
fi
# Add colours to text menu
cat>>grub2-boot/boot/grub/grub.cfg<<EOF
set menu_color_normal=cyan/blue
set menu_color_highlight=yellow/blue
EOF
# Remove root assignment as disk id will change if using usb
sed -i "s/^set root=.*//" grub2-boot/boot/grub/grub.cfg
# Change refs to 'grub2' as upstream uses 'grub'
sed -i 's/grub2/grub/g' grub2-boot/boot/grub/grub.cfg
# Remove more indirect references to original root
sed -i '/x$feature_platform_search_hint/,+4d' grub2-boot/boot/grub/grub.cfg
# Remove and replace memtest entry to remove root and add better title
sed -i '/93_memtest/,+8d' grub2-boot/boot/grub/grub.cfg
cat >> grub2-boot/boot/grub/grub.cfg <<EOF
menuentry 'Memtest' {
	insmod part_msdos
	insmod ext2
	echo	'Loading Memtest ...Loading memtest.bin  ...'
	linux16	/boot/memtest.bin
}
EOF
# Change all device references to cater for usb and replace with uuid search instead.
devlst
for dv in ${devs[@]}; do
  getuuid $dv
  mkhdpart $dv
  sed -i "s/set root=$hdstr/search --no-floppy --fs-uuid --set=root $uuidstr/g" grub2-boot/boot/grub/grub.cfg
done
}

# Main script----------------------------------------------

# Check we are root
(((UID))) && { echo "Must be root to run $0"; exit 0; }

# Say hello
clear
echo -e "Welcome to mk-grub2-boot - version $ver\n"

# Check for existing iso
if [[ -e grub2-boot.iso ]] && \
  confirm "You already have a previously generated iso.\n(U)se it or (C)reate a new one?" "[U/C]" "Uu" "Cc"; then
  burnwrite_only=1
  burnwrite
else
  createiso
fi
cleanup
echo -e "\nBye :)"
# End of mk-grub2-boot