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" }}} }}}
Комментариев нет:
Отправить комментарий