Advanced OS Customizations & Bootloaders

Ultimate Guide: Dynamic GRUB2 Menus – Conditional Booting & OS Detection for Advanced Users

Google AdSense Native Placement - Horizontal Top-Post banner

Introduction: Beyond the Static Boot Menu

For advanced Linux users, system administrators, and multi-boot enthusiasts, the default GRUB2 boot menu, while functional, often falls short in terms of flexibility and automation. Imagine a bootloader that intelligently detects operating systems, conditionally boots into specific environments based on system state, or even offers a recovery option only when a particular flag is set. This isn’t just a fantasy; it’s entirely achievable with GRUB2’s powerful scripting capabilities.

This ultimate guide delves deep into the art of dynamic GRUB2 menus, empowering you to move beyond static entries. We’ll explore conditional logic, environment variables, OS detection techniques, and practical scripting examples to create a GRUB2 menu that adapts to your system’s needs, enhancing your multi-boot experience, server management, and disaster recovery strategies.

GRUB2 Fundamentals: A Quick Recap

The Core Files: grub.cfg and 40_custom

At the heart of GRUB2’s configuration lies /boot/grub/grub.cfg. This file is the primary configuration, but it’s generated automatically by update-grub (or grub-mkconfig). Manual edits to grub.cfg are almost always overwritten. Instead, we use a modular approach.

The recommended way to add custom entries and scripts is by creating executable files in /etc/grub.d/. The most common file for custom entries is /etc/grub.d/40_custom. Files in this directory are sourced by update-grub in numerical and then alphabetical order, and their output forms part of the final grub.cfg. By default, 40_custom contains a placeholder:

#!/bin/sh
exec tail -n +3 $0
# This file provides an easy way to add custom menu entries. Simply type the
# menu entries you want to add after this comment. Be careful not to change
# the 'exec tail' line above.

All your dynamic scripting and custom menu entries will reside here, or in new files you create (e.g., 41_mydynamicboot).

Environment Variables and Defaults

GRUB2 relies on variables defined in /etc/default/grub for global settings. Key variables include:

  • GRUB_DEFAULT: Specifies the default boot entry. Can be an integer (0 for first entry), a quoted menu entry title, or saved to boot the last chosen entry.
  • GRUB_TIMEOUT: Sets the delay in seconds before the default entry is booted.
  • GRUB_TIMEOUT_STYLE: Can be hidden, menu, or countdown.

For dynamic menus, we’ll often manipulate GRUB_DEFAULT and leverage GRUB2’s internal environment variables.

Unleashing GRUB2 Scripting Power

GRUB2’s shell-like environment allows for powerful scripting directly within its configuration. This is where the magic of dynamic menus truly begins.

Conditional Logic: if/else/elif/fi

The fundamental building block for dynamic behavior is conditional execution. GRUB2 supports familiar if/else/elif/fi constructs:

if [ condition ]; then
    # commands if condition is true
elif [ another_condition ]; then
    # commands if another_condition is true
else
    # commands if no condition is true
fi

This syntax is crucial for making decisions about which menu entries to show or which OS to boot.

Testing Conditions: The ‘test’ Command

The test command (or [ ] shorthand) is used within if statements to evaluate conditions. Common tests include:

  • test -f /path/to/file: Checks if a file exists.
  • test -d /path/to/directory: Checks if a directory exists.
  • test x = y: Compares strings (x and y). Using x prefix handles empty strings safely.
  • test x != y: Checks for string inequality.

Example: Check if a recovery flag file exists:

if test -f /recovery_mode_flag; then
    echo "Recovery mode enabled!"
fi

Variables and Persistence: set & save_env

GRUB2 allows you to set and manage variables:

  • set VARNAME="value": Defines a variable for the current GRUB session.
  • save_env VARNAME: Persistently saves the current value of VARNAME to /boot/grub/grubenv.
  • load_env: Loads all persistently saved variables from grubenv into the current session.

To use persistent variables effectively, you typically enable GRUB_DEFAULT=saved in /etc/default/grub and then use save_env in your custom entries to remember the last booted OS.

Advanced OS Detection Techniques

True dynamic menus require the ability to detect the presence and location of operating systems or specific boot environments.

Identifying Partitions: The ‘search’ Command

The search command is indispensable for locating partitions without relying on fixed GRUB device names (like (hd0,msdos1)), which can change. It can search by UUID, filesystem label, or even by file presence.

  • search --set=root --fs-uuid UUID_STRING: Sets root to the partition with the given UUID.
  • search --set=root --label LABEL_STRING: Sets root to the partition with the given label.
  • search --set=root --file /path/to/file --hint hd0,msdos1: Finds a partition containing a specific file. The hint improves search speed.

Example: Finding a Linux partition by UUID:

search --set=root --fs-uuid a1b2c3d4-e5f6-7890-1234-567890abcdef
set prefix=(${root})/boot/grub

Probing for OS Presence

Once you’ve identified a potential root partition, you can use test -f to check for OS-specific files.

  • For Linux: Check for /vmlinuz, /boot/vmlinuz, /etc/fstab.
  • For Windows: Check for /EFI/Microsoft/Boot/bootmgfw.efi (UEFI) or /bootmgr (BIOS).

Combined example: Detect a specific Linux installation:

# Attempt to find the partition by UUID
search --no-floppy --set=linux_root --fs-uuid a1b2c3d4-e5f6-7890-1234-567890abcdef

if [ "${linux_root}" != "" ]; then
    # Check if a Linux kernel exists on that partition
    if test -f (${linux_root})/boot/vmlinuz; then
        echo "Linux found on ${linux_root}!"
        # You can now define a menuentry using this root
    else
        echo "Linux partition found, but no kernel."
    fi
else
    echo "Specific Linux partition not found."
fi

Practical Dynamic Menu Scenarios & Implementation

Let’s put these concepts into practice with real-world scenarios. All these examples will be placed into /etc/grub.d/40_custom (or a similar custom file).

Scenario 1: Default to Last Booted OS

This is a common desire. GRUB2 supports it natively using saved environment variables.

First, edit /etc/default/grub and change:

GRUB_DEFAULT=0

to

GRUB_DEFAULT=saved
GRUB_SAVEDEFAULT=true

Then, in your 40_custom or any menu entry in other /etc/grub.d/ files, ensure the --id option is used, and save_env boot_once is called. This example defines a custom menu entry for an imaginary OS and ensures GRUB remembers if it was selected:

menuentry 'My Custom OS (Persistent)' --id='my_custom_os' {
    set root=(hd0,msdos2)
    linux /boot/vmlinuz-custom root=/dev/sda2 quiet splash
    initrd /boot/initrd.img-custom
    save_env boot_once
}

After booting this entry, grubenv will record boot_once=my_custom_os, making it the default until another entry is selected.

Scenario 2: Conditional Boot into a Recovery Environment

Imagine a system that normally boots into a production OS, but if a specific flag file exists (e.g., /force_recovery on your main partition), it should offer a prominent recovery option or even boot directly into it.

menuentry 'Production System' --id='production_os' {
    search --set=root --label MyProdOSLabel
    linux /boot/vmlinuz root=LABEL=MyProdOSLabel ro quiet splash
    initrd /boot/initrd.img
}

# --- Conditional Recovery Mode ---

# Search for the main production OS partition to check for the flag file
search --no-floppy --set=prod_root --label MyProdOSLabel

if [ "${prod_root}" != "" ]; then
    if test -f (${prod_root})/force_recovery; then
        menuentry '!!! EMERGENCY RECOVERY MODE !!!' --id='recovery_mode' {
            # Assuming recovery kernel/initrd are on the same partition or a dedicated one
            set root=${prod_root}
            linux /boot/vmlinuz-recovery root=LABEL=MyProdOSLabel ro recovery_mode
            initrd /boot/initrd.img-recovery
        }
        # Optionally, make recovery the default if the flag is present
        set default="recovery_mode"
        set timeout="10"
    fi
fi

This script first defines the regular

Android Mobile Specs & Compare Directory

Are you researching mobile hardware properties, processor SoCs, GPU chipsets, or RAM configurations? Access our complete specs catalog to compare up to 5 devices side-by-side!

Compare Devices Specs →
Google AdSense Inline Placement - Content Footer banner