Saturday, June 5, 2010

Ubuntu 10.04 & Windows Dual-Boot: Customize GRUB2 Boot Menu

I had a dual-boot machine with Windows XP and Ubuntu 10.04 (Lucid Lynx) on it.  I wanted to clean up and customize the GRUB menu that came up whenever I booted the system.  There were several parts to this.

I started with The Grub 2 Guide.  The Guide's point 6, "Adding Entries to Grub 2," said that files named 10_linux and 30_os-prober would search for installed Linux kernels and other operating systems.  I typed this:

cd /etc/grub.d
and, sure enough, I had files called 10_linux, 20_memtest86+, 30_os-prober, and 40_custom, along with 00_header and 05_debian_theme files.  There seemed to be useful ways to change several of these items, so I went down the list in numerical order, starting with 10_linux.  I typed "sudo gedit /etc/grub.d/10_linux."  The thing to do here was to stop GRUB from listing Recovery Mode options in the startup menu.  I did a Ctrl-F to see if 10_linux contained this line:


It didn't appear to have that, so I added it near the start of the file, right before the first "if" statement.  That was all for 10_linux, so I saved and closed it.  Now, how about controlling the menu so that it wouldn't list older kernels?  At this point, I thought about going with their "Building a Totally Customized Menu" option.  But the Grub 2 Guide said that a fully customized menu would not be updated with the addition of any new kernels.  The reason seemed to be that I would make 10_linux non-executable, so it would no longer go sniffing around to see what's new.   I didn't want to have to mess with updating the list manually every time a new kernel came along.  For guidance, I looked to the Grub 2 Title Tweaks Thread.  That, and the Grub 2 Guide, led to the following approach:
uname -r
sudo update-grub
The "uname" command told me what kernel I was now using.  At present, that was 2.6.32-22-generic-pae.  The "sudo update-grub" step told me what else was being listed on the Grub menu.  There was the option of seeking out other kernels in Synaptic, but running ubuntu-tweak (available via Synaptic, if you have the right repository) gave me an easier way of removing older kernels.  In Ubuntu Tweak, I went to Applications > Package Cleaner > Unlock > Clean Kernels.  This showed all installed kernels other than the one currently in use.  They said it was a good idea to keep one previously working kernel, so I skipped the first one on the list and checked the others.  Then I clicked Cleanup.  Ubuntu Tweak did its thing, and then I closed it.  It turned out there was another way to do this, that didn't require the Ubuntu Tweak step:  just tell 10_linux how many kernels to display.  This method required me to type "sudo gedit /etc/grub.d/10_linux" and then search for the place that had these two lines:
list=`echo $list | tr ' ' '\n' | grep -vx $linux | tr '\n' ' '`
and change it by inserting another list line between those two:
list=`echo $list | tr ' ' '\n' | grep -vx $linux | tr '\n' ' '`
list=`version_find_latest $list`
I found that right at the end of the 10_linux file.  So I made that change and then saved and closed 10_linux.  Next, I didn't want memtest+ to appear in the GRUB menu list, so I typed this:
sudo chmod -x /etc/grub.d/20_memtest86+
I looked for a quick way to confirm whether a file was executable, but there didn't appear to be an option for that in chmod, and the first several webpages I tried in response to a Google search didn't tell me.  Moving on, GRUB on my laptop had gotten confused, and it now pointed to two different Vista installations, only one of which was working.  The Grub 2 Title Tweaks Thread said I could hide it, but it seemed like I should also be able to remove it.  It seemed like a bad idea to have it hanging around.  Here's how those lines looked in GRUB:
Windows Vista (loader) (on /dev/sda1)
Windows Vista (loader) (on /dev/sda2)
The first one was not working.  If I hit it, I got "Disk error.  Press any key to restart."  I actually had to power down the laptop to get past that.  I didn't yet have a very well configured Ubuntu setup on the laptop, so I rebooted with a GParted CD (an Ubuntu Live CD would have worked too).  In GParted, I looked at sda1 and, well, no wonder it was showing up.  I had a Windows XP installation on a hidden partition in there!  I'd had some troubles installing Vista on the laptop, and apparently I had decided to keep the WinXP installation just in case.  So, OK, that was interesting.  I could have tried to rejigger the setup so that I'd have a triple boot system, but I didn't plan to be using XP very often on the laptop, and I could use GParted within Ubuntu (once I finished tweaking the laptop) to hide and unhide as needed.  For now, what I needed to do, in the GRUB menu, was to change the entry so that it would report the situation more informatively.  I typed this:
sudo cat /boot/grub/grub.cfg | grep "menuentry" | cut -d '"' -f 2
sudo gedit /etc/grub.d/30_os-prober
The first line gave me the current list of GRUB menu entries from grub.cfg (in case I hadn't already written down the names I wanted to change).  Precisely what I wanted to change was the first of the two "Windows Vista (loader)" entries.  So:  around the middle of 30_os-prober, I found this:
for OS in ${OSPROBED} ; do
  DEVICE="`echo ${OS} | cut -d ':' -f 1`"
  LONGNAME="`echo ${OS} | cut -d ':' -f 2 | tr '^' ' '`"
  LABEL="`echo ${OS} | cut -d ':' -f 3 | tr '^' ' '`"
  BOOT="`echo ${OS} | cut -d ':' -f 4`"
  if [ -z "${LONGNAME}" ] ; then
Following their advice, I could have changed those last three lines to five lines that read as follows:
  if [ "${LONGNAME}" = "Windows Vista (loader)" ] ; then
    LONGNAME="Windows XP (hidden)"
  elif [ -z "${LONGNAME}" ] ; then
In this case, unfortunately, both of those items were named "Windows Vista (loader)," so I figured they would both change to "Windows XP (hidden)."  What I needed had to be more specific, so instead I left their last three lines unchanged and added this after them:
  if [ "$LONGNAME" = "Windows Vista (loader)" ] && [ "${DEVICE}" = "/dev/sda1" ] ; then
    LONGNAME="Windows XP (hidden)"
And that worked.  The one other thing I wanted to change was to get GRUB to remember which operating system choice I had used last time, and default to that one again this time unless I selected something else.  In previous Ubuntu installations, the steps I had taken to do that had been to type "sudo gedit /etc/default/grub"; change one line to say GRUB_DEFAULT=saved instead of GRUB_DEFAULT=0; save and close that file; and then type "sudo update-grub."  But now I saw this in the Grub 2 Guide:  "The default OS will not be set merely by an interactive selection of an OS from the menu."  So that was apparently a change from Grub 1.5, or whatever I had been using previously.  So now, one option was to set up a custom menu, except that it wouldn't be updated for the latest kernels.  But then, confusingly, the Grub 2 Basics seemed to say that there was a simple solution after all.  I typed "sudo gedit /etc/default/grub" and made sure it said that "GRUB_DEFAULT=saved" and I also added a line right after that (since there wasn't already a line for this):


So I saved that, typed "sudo update-grub," and watched as it generated its list.  It looked like it was only going to show one Linux image, but I reserved judgment and rebooted.  Sure enough (speaking, here, of the desktop computer on which I had been making most of these changes), GRUB now showed only two items:  the latest Ubuntu kernel and Windows XP.  I chose Windows, the second of the two items.  I hit Enter and immediately hit F8, in the theory that Safe Mode would load faster than Normal Mode.  I guess it did, but it still wasn't in a hell of a hurry.  I clicked "Turn off computer" > Restart and watched to see if GRUB would remember that I had just booted Windows, not Ubuntu.  It did.  Excellent.  That seemed to be working.  I chose Ubuntu instead and rebooted again.  This time it went into Ubuntu.  Likewise, on the laptop, after some tinkering, sudo update-grub eventually produced the desired list, and it remembered the last boot too.