Upd2. Change layout.
Table of content:
A. Notes about grub-install.
B. Notes about update-grub.
C. Other notes.
D. Multiboot schemes.
E. Scripts and config examples.
A. grub-install {{{
I'll refer to following variable names from grub-install:
- $bootdir (/boot by default) is specified by --boot-directory option and
used only for determining $grubdir.
- $grubdir is $bootdir/grub.
- $grubd_drive is drive (in grub notation), where $grubdir resides, and
$grub_partition is corresponding partition (in grub notation).
- $install_device is system device (e.g. /dev/sda), where grub will be
installed.
- $install_drive is drive (in grub notation) of $install_device.
and assume following layout in the examples below:
# sfdisk -l /dev/sda
..
Device Boot Start End #cyls #blocks Id System
..
/dev/sda2 3648+ 3713- 66- 524288 83 Linux
..
# grep /proc/mounts -esda2
/dev/sda2 /boot ext2 rw,relatime 0 0
# pvs
PV VG Fmt Attr PSize PFree
/dev/sda3 shilvana_sys lvm2 a- 60.00g 6.00g
..
# grep /proc/mounts -esys-root
/dev/mapper/shilvana_sys-root / ext4 rw,nodev,relatime,barrier=1,journal_checksum,data=ordered 0 0
Then grub-install will do:
- create (or recreate, if requested) device map file.
- copy *.mod, *.img and other image files into $grubdir.
- create environment file ($grubdir/grubenv).
- determine modules needed to access $grubdir:
- disk_module,
- fs_module,
- partmap_module,
- abstraction_module (e.g. lvm), if any.
- determine prefix - an absolute filename (including drive in grub
notation) to $grubdir. It is used by insmod command for finding modules
to load.
If $grubdir has no abstraction (e.g. lvm) and {{{
- if $grubdir resides on $install_drive ($grub_drive equals to
$install_drive), grub-install sets prefix to $grub_drive 's
partition (without drive speicifcation) followed by relative (to
$grub_drive 's fs root) path to $grubdir. In other words,
grub-install assumes, that partition number (but not bios drive
number!) is reliable enough, and because $grubdir is on the same
drive grub will boot from, there is no need to specify drive itself.
# grub-install --boot-directory /boot/test_install /dev/sda
Stopping before actual write
Grub dir and device: '/boot/test_install/grub' - '/dev/sda2'
Modules to access grub device (disk, fs, partmap, abstraction): ' biosdisk ext2 part_msdos '
Grub drive, partition and uuid (for cross-disk install): '(hd0)' - 'msdos2' - ''
Install device and drive: '/dev/sda' - '(hd0)'
Prefix drive: '(,msdos2)'
Want to execute next (no actual run):
/usr/bin/grub-mkimage -d /usr/lib/grub/i386-pc -O i386-pc --output=/boot/test_install/grub/core.img --prefix=(,msdos2)/test_install/grub biosdisk ext2 part_msdos
/usr/sbin/grub-setup --directory=/boot/test_install/grub --device-map=/boot/test_install/grub/device.map /dev/sda
Installation finished. No error reported.
- if drives are not equal (cross disk install - $grubdir is not on
$install_drive), grub-install embeds config, which finds $grubdir by
uuid and then sets prefix. Embedded config is in $grubdir/load.cfg
file. In other words, grub-install does not trust to bios drive
numbers and uses uuids to search. Note, also, that, when installing
to MBR gap, install device should contain valid mbr and partition
table, and partition should have Id set (e.g. to 83). Otherwise,
such
/usr/sbin/grub-setup: error: unable to identify a filesystem in hd1; safety check can't be performed.
or such
/usr/sbin/grub-setup: error: embedding is not possible, but this is required for cross-disk install.
errors are possible. Here is correct cross-disk install log:
# grub-install --boot-directory /boot/test_install /dev/sdb
Grub cross-disk install
Stopping before actual write
Grub dir and device: '/boot/test_install/grub' - '/dev/sda2'
Modules to access grub device (disk, fs, partmap, abstraction): ' biosdisk ext2 part_msdos search_fs_uuid'
Grub drive, partition and uuid (for cross-disk install): '(hd0)' - 'msdos2' - '4d2154ef-97ef-4513-94bf-2d6580056abf'
Install device and drive: '/dev/sdb' - '(hd1)'
Prefix drive: ''
Want to execute next (no actual run):
/usr/bin/grub-mkimage -c /boot/test_install/grub/load.cfg -d /usr/lib/grub/i386-pc -O i386-pc --output=/boot/test_install/grub/core.img --prefix=/test_install/grub biosdisk ext2 part_msdos search_fs_uuid
/usr/sbin/grub-setup --directory=/boot/test_install/grub --device-map=/boot/test_install/grub/device.map /dev/sdb
Installation finished. No error reported.
# cat /boot/test_install/grub/load.cfg
search.fs_uuid 4d2154ef-97ef-4513-94bf-2d6580056abf root
set prefix=($root)/test_install/grub
}}}
If $grubdir has abstraction (e.g. lvm), {{{
grub-install in either of the above cases (single-disk or cross-disk
install) just sets prefix to the appropriate $grub_drive. In other
words, grub-install entrusts to the abstraction layer to uniquely
identify correct drive. For single-disk case:
# grub-install --boot-directory /root/grub/test_install /dev/sda
Stopping before actual write
Grub dir and device: '/root/grub/test_install/grub' - '/dev/mapper/shilvana_sys-root'
Modules to access grub device (disk, fs, partmap, abstraction): ' biosdisk ext2 part_msdos lvm '
Grub drive, partition and uuid (for cross-disk install): '' - '' - ''
Install device and drive: '/dev/sda' - ''
Prefix drive: '(shilvana_sys-root)'
Want to execute next (no actual run):
/usr/bin/grub-mkimage -d /usr/lib/grub/i386-pc -O i386-pc --output=/root/grub/test_install/grub/core.img --prefix=(shilvana_sys-root)/root/grub/test_install/grub biosdisk ext2 part_msdos lvm
/usr/sbin/grub-setup --directory=/root/grub/test_install/grub --device-map=/root/grub/test_install/grub/device.map /dev/sda
Installation finished. No error reported.
and for cross-disk case
# grub-install --boot-directory /root/grub/test_install /dev/sdb
Stopping before actual write
Grub dir and device: '/root/grub/test_install/grub' - '/dev/mapper/shilvana_sys-root'
Modules to access grub device (disk, fs, partmap, abstraction): ' biosdisk ext2 part_msdos lvm '
Grub drive, partition and uuid (for cross-disk install): '' - '' - ''
Install device and drive: '/dev/sdb' - ''
Prefix drive: '(shilvana_sys-root)'
Want to execute next (no actual run):
/usr/bin/grub-mkimage -d /usr/lib/grub/i386-pc -O i386-pc --output=/root/grub/test_install/grub/core.img --prefix=(shilvana_sys-root)/root/grub/test_install/grub biosdisk ext2 part_msdos lvm
/usr/sbin/grub-setup --directory=/root/grub/test_install/grub --device-map=/root/grub/test_install/grub/device.map /dev/sdb
Installation finished. No error reported.
}}}
- but grub-install does NOT generate any grub.cfg.
Here are some notes about grub's $root and $prefix variables (available in
grub shell during boot):
- $root contain only drive name (in grub notation).
- $prefix is an absolute filename of $grubdir (drive name followed by path
relative to drive's fs root).
- $grubdir (referred by $prefix) contains all data required by core.img to
work properly and its location is hardcoded (?) (during grub-mkimage?)
into the core.img.
- Grub modules loaded from $prefix folder (i.e. $grubdir), not from $root.
So, you can reset $root to something else and insmod will still works.
Hence, when you're in grub rescue mode (grub can't find $grubdir and
load normal module), you should first fix '$prefix' value, then you
should be able to load normal module (and should load it). And then run
normal command, and then (may be) set '$root' to something more
convenient.
- grub-install sets up only core.img functionality. It does not care about
configuration (grub.cfg) in any way - configuration is grub-mkconfig
duty. So, grub-install does all you need to get normal grub environment
(grub normal shell, not rescue shell) at the boot (in other words, to
load normal module and run normal command), but it knows nothing about
local OSes and how to boot them.
}}}
B. update-grub {{{
I'll use following terms:
- grubdir is directory, where all grub modules and other stuff have
installed by grub-install. It is usually /boot/grub, but may be set to
'DIR/grub' using '--boot-directory DIR' option of grub-install.
update-grub just calls grub-mkconfig:
# cat $(which update-grub)
#!/bin/sh
set -e
exec grub-mkconfig -o /boot/grub/grub.cfg "$@"
grub-mkconfig will do
- Check device map file (device.map) and create, if it does not exist.
- Execute all scripts (with 'x' bit set) from /etc/grub.d. These scripts
may access some other files from grubdir. E.g. /etc/grub.d/00_header
need video.lst, when generating load_video() function (if neither of
video backends have been specified in GRUB_VIDEO_BACKEND from
/etc/default/grub). Also 00_header tries to open localedir, and may try
to open serial.mod, and may be more.. who knows?
- Overwrite grub.cfg with new one.
Here is inotify log of update-grub run:
# cp -a /mnt/boot/grub/* -t /boot/grub/
# inotifywait -mr -e open /boot/grub/ 2>&1 | tee grubdir_inotify.log
# uniq update-grub_inotify.log
Setting up watches. Beware: since -r was given, this may take a while!
Watches established.
/boot/grub/ OPEN device.map
/boot/grub/ OPEN grub.cfg.new
/boot/grub/ OPEN video.lst
/boot/grub/ OPEN device.map
/boot/grub/ OPEN,ISDIR
/boot/grub/ OPEN device.map
/boot/grub/ OPEN grub.cfg.new
With different configuration (/etc/default/grub) and different scripts in
/etc/grub.d, files required for correct update-grub (grub-mkconfig) run may
differ. In other words, i can safely assume only, that "entire grubdir is
required for update-grub run". This is important point, when designing proper
multiboot config.
}}}
C. Other notes {{{
Other notes about grub2.
Terminal output. {{{
Only two terminal outputs are suitable for doing something in grub's cmd:
console and vga_text. gfxterm even on low resolutions is very slow. Try
grub> set pager=1
grub> cat /some_not_too_little_file
and hold down the key (to scroll down by one line). And it most likely will
beep soon. As i understand, this means, that it can't refresh screen as fast
as input received (input buffer overflow?). So, to display boot menu gfxterm
may be suitable, but for working in grub's cmd - absolutely not.
List vbe modes
grub> vbeinfo
test them
grub> vbetest
switch to gfxterm
grub> insmod vbe
grub> insmod gfxterm
grub> set gfxmode='640x480x32' # vbe to which switch to
grub> terminal_output gfxterm
switch to other terminal output (e.g. console)
grub> terminal_output console
}}}
When installing grub to MBR gap: {{{
- boot.img is first sector (mbr sector itself).
- diskboot.img is 2nd scetor (offset 0x200 bytes).
- core.img contains diskboot.img as its first sector (so core.img starts
from offset 0x200 as well) and continues up to.. well, further in the
free space before first partition.
}}}
Some variables from default scripts in /etc/grub.d {{{
- 'prev_saved_entry' environment variable (in grub's config part generated
by 00_header script) is set by grub-reboot shell script. It is used to
save previous 'saved_entry' value before overwriting it with new one.
This is needed, because grub-reboot sets new 'saved_entry' for one
(next) boot only, and grub.cfg should restore previous one after
(during) next boot.
- When OS root device is inside lvm, grub-mkconfig will not use UUIDs for
'root=' kernel parameter (this is desired behavior, just confirm it).
Code, which checks this (as well as GRUB_DISABLE_LINUX_UUID variable
from /etc/default/grub), is in 10_linux script.
}}}
}}}
D. Multiboot schemes {{{
I'll use following terms:
- grubdir is directory, where all grub modules and other stuff have
installed by grub-install. It is usually /boot/grub, but may be set to
'DIR/grub' using '--boot-directory DIR' option of grub-install.
Here suggested two schemes for booting several Linux systems with grub2. They
are designed to satisfy following requirments:
- update-grub should work in all Linux systems and does not break
anything.
- No incorrect and useless menuentries, which often generated by
/etc/grub.d/30_os-prober script.
- New linux system should not ruin boot, if update-grub will accidently
run on it with default config.
- OS kernel and initrd should be stored on corresponding OS root
partition. This is OS-dependent data and i don't want to store it in
shared location (like shared boot partition). Also, if OS will be moved
to some other computer, kernel and initrd will still be there.
Generally, there is two approaches to this problem:
- One main config (grub.cfg), which have created and updated by hand (or
some other method, but not by update-grub), and many OS-specific
configs, which have generated and updated by update-grub from
corresponding OS. Main config should find and load OS-specific ones.
- One merged config. Merge should occur during generation or update by
update-grub from any OS.
Because each OS may run update-grub, second approach requires each OS to have
specific merge script in the /etc/grub.d, and if it is not there (for newly
installed OS), update-grub will overwrite grub.cfg making all other systems
unbootable. Hence, i'm not considering second approach further, and choosing
first one.
Main grub config should be stored on the shared boot partition, but
OS-specific ones may be stored on either shared boot partititon as well or on
OS root partition. I can't definitely say, that OS-specific grub config
belongs to OS or to grub. On the one hand, OS-specific config is generated
using OS-specific scripts from /etc/grub.d, and, hence, belongs to OS. But, on
the other hand, grub-mkconfig and scripts from /etc/grub.d may read files from
grubdir and make some choices depending on their content (and they actually
will), hence, OS-specific config depends on particular grub installation and
belongs to grub. In other words, OS-specific grub.cfg depends on both OS
configuration and bootloader features available.
So, i implement two layouts for both points of view on OS-specific grub
config.
1. OS-specific grub config stored on the OS root partition. {{{
Requirments:
- grubdir should be accessible under /boot/grub, because grub-mkconfig
hardcodes this path. (grub-mkconfig and some scripts from /etc/grub.d
may need access to grubdir to check some files)
- OS-specific grub.cfg should be stored in /boot/grub on OS root partition
(not the same, as grubdir's partition). Note, that according to the
definition at the beginning /boot/grub on the OS root is _not_ grubdir,
because grub-install does not install anything into it. In other words,
it's just a folder, and may be changed to any other.
- main grub.cfg should be stored in grubdir on shared boot partition.
Mounts (order is significant!):
/mnt/boot - shared boot partition.
/mnt/os_boot_grub - bind of /boot/grub from OS root partition
(where to store OS-specific grub.cfg).
/boot/grub - bind of grubdir (/mnt/boot/grub). I.e. this is
grubdir, where update-grub expects to see it.
Notes:
- I can't use symlink to grubdir: /boot/grub -> /mnt/boot/grub, because in
this case /boot/grub directory on the OS root, where i want to store
OS-specific grub config, will not exist at all.
- Using symlinks, like /boot/grub/smth -> /mnt/boot/grub/smth, to every
file (and folder) in /mnt/boot/grub is not a good idea, because, when
content of grubdir have changed, i need to update such symlinks in every
OS's /boot/grub (or similar folder). So, first of all i need to find all
OSes root devices and corresponding folders on them. This is not
reliable, because i may either miss some OSes or different folders
(instead of /boot/grub) in some OSes.
- I bind only grubdir (.../grub directory on the shared boot partition),
but not the whole shared boot partition, because i need to store kernel
image and initrd, which usually placed in /boot, on the OS root
partition. Hence, /boot should still be on the OS root partition.
There is one thing left: update-grub tells grub-mkconfig to write result to
/boot/grub/grub.cfg, but since /boot/grub is bind of grubdir, this is not
appropriate place for OS-specific grub.cfg (it overwrites main grub.cfg, if
would be written there). OS-specific grub.cfg should be written to
/mnt/os_boot_grub/grub.cfg, but this requires change in update-grub script. I
need to divert update-grub to other file, and put changed version in its
place.
Disadvantages of this implementation:
- dpkg-divert (distibution-specific feature).
- Because OS-specific grub.cfg depends on bootloader features configured
(*.mods available, etc), placing it on the OS root partition may be not
appropriate: if you move OS to another computer, which most likely will
have bootloader (suppose still grub2) configured (by grub-install)
differently, old grub.cfg may not work.
Here is complete walkthrough {{{
1. Divert update-grub.
# dpkg-divert --add --rename --divert /usr/sbin/update-grub.distrib /usr/sbin/update-grub
Adding 'local diversion of /usr/sbin/update-grub to /usr/sbin/update-grub.distrib'
# ls -l /usr/sbin/update-grub*
lrwxrwxrwx 1 root root 11 Jun 30 21:39 /usr/sbin/update-grub2 -> update-grub
-rwxr-xr-x 1 root root 64 Jun 15 13:13 /usr/sbin/update-grub.distrib
# dpkg-divert --list | grep grub
local diversion of /usr/sbin/update-grub to /usr/sbin/update-grub.distrib
2. Create new update-grub, which writes result to /mnt/os_boot_grub/grub.cfg .
# cat /usr/sbin/update-grub
#!/bin/sh
set -e
exec grub-mkconfig -o /mnt/os_boot_grub/grub.cfg "$@"
3. Mount all properly.
# grep /etc/fstab -eboot
LABEL=common_boot /mnt/boot ext2 defaults 0 2
/boot/grub /mnt/os_boot_grub none bind 0 0
/mnt/boot/grub /boot/grub none bind 0 0
4. Install grub, and tell grub-install where grubdir should be
# grub-install --boot-directory /mnt/boot/ /dev/sda
Installation finished. No error reported.
5. Generate (or update) OS-specific grub config.
# update-grub
Generating grub.cfg ...
Found background image: /usr/share/images/desktop-base/desktop-grub.png
Found linux image: /boot/vmlinuz-2.6.32-5-686-bigmem
Found initrd image: /boot/initrd.img-2.6.32-5-686-bigmem
Found Windows Vista (loader) on /dev/sda1
Found Debian GNU/Linux (wheezy/sid) on /dev/mapper/shilvana_sys-testing_root
done
And then you can find OS-specific grub.cfg
# ls -l /mnt/os_boot_grub/
total 4
-r--r--r-- 1 root root 3512 Jul 5 15:58 grub.cfg
and can ensure, that it is on the correct device (OS root partition)
# mountpoint -d /mnt/os_boot_grub/
254:0
# mountpoint -d /
254:0
or, if you unmount binds,
# umount /boot/grub /mnt/os_boot_grub
# ls -l /boot/
total 6900
-rw-r----- 1 root root 111601 Jul 3 15:19 config-2.6.32-5-686-bigmem
drwxr-x--- 2 root root 4096 Jul 5 16:04 grub
-rw-r--r-- 1 root root 3234993 Jul 5 16:04 initrd.img-2.6.32-5-686-bigmem
-rw-r----- 1 root root 1326610 Jul 3 15:19 System.map-2.6.32-5-686-bigmem
-rw-r----- 1 root root 2367872 Jul 3 15:19 vmlinuz-2.6.32-5-686-bigmem
# ls -l /boot/grub/
total 4
-r--r--r-- 1 root root 3512 Jul 5 16:04 grub.cfg
6. Copy main grub.cfg to grubdir (/mnt/boot/grub). It should find OS root
devices (using 'search --label' or 'search --fs-uuid') and load
corresponding grub.cfg-s from them (using configfile).
}}}
}}}
2. OS-specific grub config stored in subfolder on the shared boot partition. {{{
Requirments:
- grubdir should be accessible under /boot/grub, because grub-mkconfig
hardcodes this path. (grub-mkconfig and some scripts from /etc/grub.d
may need access to grubdir to check some files)
- OS-specific grub.cfg should be stored in subfolder on shared boot
partition (grubdir's partition).
- main grub.cfg should be stored in grubdir on shared boot partition.
Structure of shared boot partition (mounted on /mnt/boot):
/mnt/boot/common/grub - grubdir .
/mnt/boot/OS_name/grub - location (folder) of OS-sepcific grub.cfg.
Mounts:
/mnt/boot - shared boot partition.
/boot/grub - bind of OS-specific grub.cfg location (folder)
(/mnt/boot/OS_name/grub).
And to make grubdir content accessible under /boot/grub (where update-grub
expects to see it), i should hardlink all files, _except_ grub.cfg, from
common/grub into all OS-specific locations 'OS_name/grub' on shared boot
partition.
Disadvantages of this implementation:
- If you mess up with hardlinks, you can easily overwrite other OSes
grub.cfg-s or main grub.cfg and end up with unbootable system. So, it's
better to use script for updating hardlinks.
- Because OS-specific grub.cfg is on the shared boot partition, if you
move OS to another computer, you will not have any grub.cfg for this OS
(even not working now, but which can give you some hints how to boot
OS).
Here is complete walkthrough: {{{
1. Create grubdir and OS-specific grub.cfg locations on the shared boot
partition
# mkdir -p common stable_linux/grub testing_linux/grub
2. Install grub, and tell grub-install where grubdir should be
# grub-install --boot-directory /mnt/boot/common /dev/sda
Installation finished. No error reported.
3. Hardlink all files, except grub.cfg, from grubdir (common/grub) to
OS-specific locations:
# shopt -s extglob
# cp -al common/grub/!(*.cfg) -t stable_linux/grub/
and check, that all is right (i have several grub.cfg-s in grubdir, so i
exclude them all during hardlinking):
# find common/grub/ -links 1
common/grub/grub.cfg
common/grub/grub_load_from_folder.cfg
common/grub/grub2.cfg
Alternatively, you can use script for hardlinking (this is recommended).
Note, that grub-install recreates (removes and creates again) files in
grubdir, hence, all hardlinks must be recreated after each grub-install run.
4. Mount OS-specific grub.cfg location under /boot/grub (fstab):
LABEL=common_boot /mnt/boot ext2 defaults 0 2
/mnt/boot/stable_linux/grub /boot/grub none bind 0 0
5. Generate (or update) OS-specific grub config.
# update-grub
Generating grub.cfg ...
Found background image: /usr/share/images/desktop-base/desktop-grub.png
Found linux image: /boot/vmlinuz-2.6.32-5-686-bigmem
Found initrd image: /boot/initrd.img-2.6.32-5-686-bigmem
Found Windows Vista (loader) on /dev/sda1
Found Debian GNU/Linux (wheezy/sid) on /dev/mapper/shilvana_sys-testing_root
done
and check, that all is right
# ls -l stable_linux/grub/grub.cfg
-r--r--r-- 1 root root 3525 Jul 16 18:42 stable_linux/grub/grub.cfg
6. Copy main grub.cfg to grubdir (/mnt/boot/common/grub). It should find
OS-specific grub.cfg-s and load them (using configfile).
}}}
}}}
}}}
E. Examples. {{{
Here are some (working) examples.
E1. Example of main grub.cfg {{{
E1A. Functions {{{
function reset_os_args {
# NOTE: This function must be called at the end of menuentry. Otherwise,
# further selected menuentries (if any) may have mixed args (some from
# previously selected menuentries).
set OS_name='Undefined'
# NOTE: Empty label matches with device without label, but until you use
# search_os_root() this is not the problem, because it will not call
# search with empty label. What about empty uuid? It matches with fs not
# supporting uuids?
set OS_root_uuid='' # required
set OS_root_label='' # required
set OS_hints=''
set OS_boot_config='/boot/grub/grub.cfg'
set OS_chainloader='+0'
set OS_root_not_found='0'
set OS_boot_not_found='0'
}
function search_os_root {
echo "Searching for '$OS_name' root device.."
set OS_root_not_found='0'
if [ -n "$OS_root_uuid" ]; then
if search --no-floppy --set=root --fs-uuid "$OS_root_uuid" $OS_hints; then
return 0
fi
echo "UUID '$OS_root_uuid' not found. Press Enter."
elif [ -n "$OS_root_label" ]; then
if search --no-floppy --set=root --label "$OS_root_label" $OS_hints; then
return 0
fi
echo "Label '$OS_root_label' not found. Press Enter."
else
echo "No label or uuid specified. Press Enter."
fi
read
set OS_root_not_found='1'
return 1
}
function load_os_boot_config {
echo "Loading '$OS_name' boot config.."
set OS_boot_not_found='0'
if [ -n "$OS_boot_config" ]; then
if [ -f "$OS_boot_config" ]; then
save_main_default
configfile "$OS_boot_config"
return 0
fi
echo "Boot config ('$OS_boot_config') not found. Press Enter."
else
echo "Boot config not specified, use default (/boot/grub/grub.cfg)."
if [ -f "/boot/grub/grub.cfg" ]; then
save_main_default
configfile "/boot/grub/grub.cfg"
return 0
fi
echo "Default boot config (/boot/grub/grub.cfg) not found. Press Enter."
fi
read
set OS_boot_not_found='1'
return 1
}
function load_os {
# Search for OS root, then load os boot config.
if search_os_root; then
if load_os_boot_config; then
set root="$old_root" # I'm here, if i exit from nested grub.cfg.
set main_saved_entry="$old_main_saved_entry"
# Saved (in grubenv) 'main_saved_entry' value is still not reset,
# but it should be overwritten, when other menuentry will be
# chosen. Also loading of other config does not affect 'default'
# value in this config, and it is still '$old_main_saved_entry'.
return 0
fi
set root="$old_root"
fi
return 1
}
function chainload_os {
# Search for OS root, then chainload OS.
if search_os_root; then
echo "Chain-loading to '${OS_name}'.."
chainloader "$OS_chainloader"
save_main_default
boot # I shouldn't exit the function, if all is ok.
set root="$old_root"
set main_saved_entry="$old_main_saved_entry"
fi
return 1
}
function try_label_then_uuid {
# Execute command (passed as first positional parameter) first with label
# set (and uuid empty) and, if it failes, with uuid set (and label empty).
# Actaully, now retry will happen only, if root device search failed.
# Arguments:
# 1 - command to execute.
set t="$OS_root_uuid"
set OS_root_uuid=''
"$1"
set ret="$?"
set OS_root_uuid="$t"
if [ "$OS_root_not_found" = '1' ]; then
# Retry only, if root have not found. Otherwise - success or missed
# boot config - do nothing.
set t="$OS_root_label"
set OS_root_label=''
echo "Loading '$OS_name' by label failed, try by uuid.."
"$1"
set ret="$?"
set OS_root_label="$t"
fi
return "$ret"
}
function try_uuid_then_label {
# Arguments:
# 1 - command to execute.
set t="$OS_root_label"
set OS_root_label=''
"$1"
set ret="$?"
set OS_root_label="$t"
if [ "$OS_root_not_found" = '1' ]; then
set t="$OS_root_uuid"
set OS_root_uuid=''
echo "Loading '$OS_name' by uuid failed, try by label.."
"$1"
set ret="$?"
set OS_root_uuid="$t"
fi
return "$ret"
}
function save_main_default {
set main_saved_entry="$chosen"
save_env main_saved_entry
# In case loaded configfile also saves default, this (now chosen)
# menuentry will be included in saved value (will look like 'this>that'),
# but that config has no menuentry 'this', it has only 'that', and, hence,
# saved menuentry path will be incorrect. So, i need to make it think,
# that 'that' menuentry is topmost. Note, that if i set 'chosen' to empty
# value, grub will _not_ believe, that 'that' menuentry is topmost, hence
# resulted menuentry path will look like '>that' and still won't work.
unset chosen
}
# Set variables to defaults.
reset_os_args
}}}
E1B. Menuentries for storing OS-specific grub config on the OS root {{{
# FIXME: Theme.
# FIXME: Timeout.
# FIXME: In the both boot schemes i store linux kernel and initrd on the OS
# root partition (in /boot folder), hence, /boot will be always on the same
# device as /, but _not_ on the same as /boot/grub (in the both boot schemes
# shared boot partition (or some folder on it) mounted here). grub-mkconfig
# probes device under /boot and sets GRUB_DEVICE_BOOT and
# GRUB_DEVICE_BOOT_UUID variables, which have exported to all /etc/grub.d
# scripts. I'm not sure whether these variables should contain device, which
# holds kernel image and initrd, or device, which holds grubdir (/boot/grub).
# For the last case GRUB_DEVICE_BOOT will be set incorrectly. I can't confirm
# neither any of the intended usages of these variables nor any side-effects
# of possible incorrect value. Though, the way 10_linux script uses
# GRUB_DEVICE_BOOT variable _may_ mean, that it (variable) should hold kernel
# image's and initrd's device. So, this is just a reminder of possible failure
# ;)
insmod lvm
insmod part_msdos
insmod ext2
if [ -s $prefix/grubenv ]; then
load_env
fi
# Comment line below, if you don't want to set as default (for main grub
# config) last used entry.
set default="$main_saved_entry"
set old_main_saved_entry="$main_saved_entry"
set old_root="$root"
# NOTE: It's advised to explicitly set all os arguments to prevent some of
# them left unchanged from previous menuentries. Also, it is required to call
# reset_os_args at the end of each menuentry (though, code does not check
# this).
menuentry "Stable Linux" {
set OS_name='Stable Linux'
set OS_root_uuid='eda990a3-434f-4646-a578-2f7743b7f269'
set OS_root_label='stable_root'
set OS_hints='--hint (shilvana_sys-stable_root)'
set OS_boot_config='/boot/grub/grub.cfg'
try_label_then_uuid 'load_os'
echo "Ret = $?"
read
reset_os_args
}
menuentry "Testing Linux" {
set OS_name='Testing Linux'
set OS_root_uuid='d80f0166-c472-11e1-b606-00215c4d0de1'
set OS_root_label='testing_root'
set OS_hints='--hint (shilvana_sys-testing_root)'
set OS_boot_config='/boot/grub/grub.cfg'
try_label_then_uuid 'load_os'
echo "Ret = $?"
read
reset_os_args
}
menuentry "Windows" {
insmod ntfs
set OS_name='Windows'
set OS_root_uuid='C0FEC280FEC26DEA'
set OS_root_label='wvista'
set OS_hints='--hint (hd0,msdos1)'
set OS_chainloader='+1'
try_label_then_uuid 'chainload_os'
echo "Ret = $?"
read
reset_os_args
}
menuentry "Test fallback label to uuid" {
set OS_name='Test fallback'
set OS_root_uuid='d80f0166-c472-11e1-b606-00215c4d0de1'
set OS_root_label='test_fallback'
set OS_hints=''
set OS_boot_config=''
try_label_then_uuid 'load_os'
echo "Ret = $?"
read
reset_os_args
}
menuentry "Test fallback uuid to label" {
set OS_name='Test fallback'
set OS_root_uuid='d80f0166-c472-11e1-b606-01215c4d0ce1'
set OS_root_label='testing_root'
set OS_hints=''
set OS_boot_config='/boot/grub/grub.cfg'
try_uuid_then_label 'load_os'
echo "Ret = $?"
read
reset_os_args
}
menuentry "Test fallback both empty" {
set OS_name='Test fallback'
set OS_root_uuid=''
set OS_root_label=''
set OS_hints=''
set OS_boot_config='/boot/grub/grub.cfg'
try_uuid_then_label 'load_os'
echo "Ret = $?"
read
reset_os_args
}
}}}
E1C. Menuentries for storing OS-specific grub config in subfolder on the shared boot {{{
# FIXME: Theme.
# FIXME: Timeout.
# FIXME: In the both boot schemes i store linux kernel and initrd on the OS
# root partition (in /boot folder), hence, /boot will be always on the same
# device as /, but _not_ on the same as /boot/grub (in the both boot schemes
# shared boot partition (or some folder on it) mounted here). grub-mkconfig
# probes device under /boot and sets GRUB_DEVICE_BOOT and
# GRUB_DEVICE_BOOT_UUID variables, which have exported to all /etc/grub.d
# scripts. I'm not sure whether these variables should contain device, which
# holds kernel image and initrd, or device, which holds grubdir (/boot/grub).
# For the last case GRUB_DEVICE_BOOT will be set incorrectly. I can't confirm
# neither any of the intended usages of these variables nor any side-effects
# of possible incorrect value. Though, the way 10_linux script uses
# GRUB_DEVICE_BOOT variable _may_ mean, that it (variable) should hold kernel
# image's and initrd's device. So, this is just a reminder of possible failure
# ;)
insmod lvm
insmod part_msdos
insmod ext2
if [ -s $prefix/grubenv ]; then
load_env
fi
# Comment line below, if you don't want to set as default (for main grub
# config) last used entry.
set default="$main_saved_entry"
set old_main_saved_entry="$main_saved_entry"
set old_root="$root"
# NOTE: It's advised to explicitly set all os arguments to prevent some of
# them left unchanged from previous menuentries. Also, it is required to call
# reset_os_args at the end of each menuentry (though, code does not check
# this).
menuentry "Stable Linux" {
set OS_name='Stable Linux'
set OS_root_uuid=''
set OS_root_label=''
set OS_hints=''
set OS_boot_config='/stable_linux/grub/grub.cfg'
load_os_boot_config
echo "Ret = $?"
read
reset_os_args
}
menuentry "Testing Linux" {
set OS_name='Testing Linux'
set OS_root_uuid=''
set OS_root_label=''
set OS_hints=''
set OS_boot_config='/testing_linux/grub/grub.cfg'
load_os_boot_config
echo "Ret = $?"
read
reset_os_args
}
menuentry "Windows" {
insmod ntfs
set OS_name='Windows'
set OS_root_uuid='C0FEC280FEC26DEA'
set OS_root_label='wvista'
set OS_hints='--hint (hd0,msdos1)'
set OS_chainloader='+1'
try_label_then_uuid 'chainload_os'
echo "Ret = $?"
read
reset_os_args
}
menuentry "Test fallback label to uuid" {
set OS_name='Test fallback'
set OS_root_uuid='d80f0166-c472-11e1-b606-00215c4d0de1'
set OS_root_label='test_fallback'
set OS_hints=''
set OS_boot_config=''
try_label_then_uuid 'load_os'
echo "Ret = $?"
read
reset_os_args
}
menuentry "Test fallback uuid to label" {
set OS_name='Test fallback'
set OS_root_uuid='d80f0166-c472-11e1-b606-01215c4d0ce1'
set OS_root_label='testing_root'
set OS_hints=''
set OS_boot_config='/boot/grub/grub.cfg'
try_uuid_then_label 'load_os'
echo "Ret = $?"
read
reset_os_args
}
menuentry "Test fallback both empty" {
set OS_name='Test fallback'
set OS_root_uuid=''
set OS_root_label=''
set OS_hints=''
set OS_boot_config='/boot/grub/grub.cfg'
try_uuid_then_label 'load_os'
echo "Ret = $?"
read
reset_os_args
}
}}}
In order to make working main grub.cfg you should concatenate functions with
corresponding (to chosen boot scheme) menuentries flavour. You may use such
script to do this (adjust pathes)
#!/bin/sh
set -ef
project_path='/home/sgf/Documents/boot_usb'
src_path='src/main_grub_config'
bin_path='bin'
cd "$project_path"
cat "./$src_path/grub_main_lib.cfg" \
"./$src_path/grub_main.cfg" \
>| "./$bin_path/grub_main.cfg"
}}}
E2. Example of script for installing grub hardlinks {{{
#!/bin/sh
set -euf
OIFS="$IFS"
newline='
'
bkp_dirs=''
repl=''
# Adjust these pathes, if necessary.
boot_path='/mnt/boot' # Where shared boot partition mounted.
grubdir_relpath='common/grub' # Relative (to $boot_path) path to grubdir.
# find's regexp (posix-basic) for matching grub config files. These files will
# not be hardlinked from grubdir.
os_config_pattern='grub*.cfg'
grubdir="$boot_path/$grubdir_relpath"
os_config_dirs="$(find -P "$boot_path" -path "$grubdir" -prune \
-o -path '*/grub' -print)"
echo "Update hardlinks in OS boot directories."
echo "Shared boot partition mounted on '$boot_path'."
echo "Main grub directory (full path) is '$grubdir'."
echo "Found OS boot directories are:"
echo "$os_config_dirs"
read -p 'Proceed? (y/any key): ' repl
if [ "$repl" != 'y' ]; then
echo "..exit"
exit 0
fi
IFS="$newline"
for os_config_dir in $os_config_dirs; do
echo "Updating '$os_config_dir'.."
echo "Moving old files out of the way.."
bkp_dir="$(mktemp -d "$os_config_dir".XXXX)"
# I'll use `cp && rm` for moving files, because i want to check every file
# for match with "$os_config_pattern", and, hence, if current file is in
# subdir under '$os_config_dir', i should recreate this subdir(s) under
# backup location before moving file, but mv hasn't option '--parents'.
# For determining correctly subdir(s), which should be recreated by
# '--parents', `cp` must run in the source top dir.
cd "$os_config_dir"
find -P . -mindepth 1 \
-not -type d -not -iname "$os_config_pattern" \
-exec sh -euf -c "
cp -Pl --parents -t \"$bkp_dir\" \"\$@\" \
&& rm -f \"\$@\"" \
sh {} \+
echo "..done"
echo "Hardlinking.."
# I'm in grubdir now.
cd "$grubdir"
find -P . -mindepth 1 \
-not -type d -and -not -iname "$os_config_pattern" \
-exec cp -Pl --parents -t "$os_config_dir" {} \+
echo "..done"
bkp_dirs="${bkp_dirs}${bkp_dir}${newline}"
done
IFS="$OIFS"
cat <<EOF
grub hardlinks have been updated. If you want to restore previous
os_config_dir use
cp -RTl --remove-destination backup_dir os_config_dir
for restoring (without '--remove-destination' you'll overwrite grubdir files
and will need to reinstall grub (by grub-install) after that) and then
find backup_dir -depth -links '+1' -delete
for deleting backup. Otherwise, you may safely remove backup directories.
Here they are:
EOF
echo "$bkp_dirs"
}}}
}}}

Комментариев нет:
Отправить комментарий