Compare commits

...

17 Commits

Author SHA1 Message Date
Antynea
8142691be1 Adds missing newline
* Adds missing newline:
  "\n" was missing.
2021-09-24 15:54:04 +02:00
Antynea
bab78d4ed8 Write-protect user data
* Write-protect user data
  * The script does not need write access to the data.
  Adds "ro" flags to provide protection against accidental writing.
2021-09-24 12:24:15 +02:00
Antynea
544d2e84ac Adds security if a false positive is detected
* Adds security if a false positive is detected:
  * if the command "grep -qs "$gbgmp" /proc/mounts;" returns a false positive.
  "rm -d" will return an error if the directory is not empty.
2021-09-24 12:12:43 +02:00
Antynea
b346727219 Deletes unused code
* Deletes unused code:
  * Limit generation of menuentries if exceeds 250 has never been used.
  Uses "GRUB_BTRFS_LIMIT" (default 50) instead
2021-09-23 15:28:28 +02:00
Antynea
f1ca0db36d Update header installation section
* Update header installation section:
  * Remove instructions
  (Refet to the project website, installation section)
2021-09-23 14:54:59 +02:00
Antynea
c4d0df3a97 Rework the creation of the "grub-btrfs.cfg" file.
* Rework the creation of the "grub-btrfs.cfg" file:
  * Before, the "grub-btrfs.cfg" file was deleted and then the new configuration was written.
  No backup was made.
  * Now, the new configuration will be written in a temporary file "grub-btrfs.new" and then analysed with the "grub_script_check" command before being finally written in the "grub-btrfs.cfg" file.
  If an error is detected, the "grub-btrfs.new" file will coexist with the old "grub-btrfs.cfg" file, if it exists.
2021-09-23 14:49:56 +02:00
Antynea
c1cadccd1f Adds trap command on EXIT signal
* Adds trap command on EXIT signal:
  * Now umount command will launch on EXIT signal. 
  * Adds a function called by the trap on EXIT signal to unmount and delete the temporary folder. 
  (That should be the end of multiple tmp.xxxxxxxxxx in /tmp)
    - If the command fails, retry every 2 seconds. After 10 attempts, it will stop and display a warning.
    - If the command is successful, "Succes" will be displayed.
  * Adds "grub-btrfs" as a prefix to the temporarie mount folder.
  (before = tmp.xxxxxxxxx , now = grub-btrfs.xxxxxxxxxx)
2021-09-23 11:49:21 +02:00
Antynea
8cc214fd0e Corrects printf format string
* Corrects printf format string:
  * printf interprets escape sequences and format specifiers in the format string. If variables are included, any escape sequences or format specifiers in the data will be interpreted too, when you most likely wanted to treat it as data.
2021-09-22 19:08:55 +02:00
Antynea
81bde02b03 Update "Installation section"
* Update "Installation section":
  * Adds required dependencies
  * Indicates that the command "update-grub", is an alias to "grub-mkconfig" on Debian-like distributions.
2021-09-22 17:07:44 +02:00
Antynea
14bf041ba6 Deactivate the script as soon as possible
* With these changes, the script will be disabled as soon as possible, if :
  * "GRUB_BTRFS_DISABLE" If this variable is set to "true"
  * "btrfs-progs isn't installed" This package is required to retrieve information from the btrfs filesystem.
  * "grub-mkconfig_lib couldn't be found" This library is required because the script depends on it.
  * "Root filesystem isn't btrfs" grub-btrfs currently checks only the btrfs snapshots present on the root partition.
2021-09-22 15:49:02 +02:00
Antynea
863107588c Remove redundant check
Running a command and then checking its exit status $? against 0 is redundant.
Instead of just checking the exit code of a command, it checks the exit code of a command that checks the exit code of a command.
2021-09-22 15:40:05 +02:00
Antynea
2851ecd72b Removes double negation in file test operators
* Removes double negation in file test operators:
  * Replaces the "! -z" operator with "-n".
2021-09-22 15:31:03 +02:00
Antynea
679d000446 Add possibility to boot without init (#164)
* Add possibility to boot without init(rd,ramfs):
  - For a snapshot to be valid, it must contain a boot folder and a kernel from the official list (or have been added to the custom kernel list)
  - if a snapshot doesn't contain an init(rd,ramfs), it will be detected as valid.
  - Suppress script stop when an init is not found
  - If init isn't found, add the letter "x" to the init list. (hoping this doesn't break the support for custom init names)
  - The microcode support is still present, despite the absence of init(rd,ramfs), is it really relevant ?
2021-09-17 15:39:23 +02:00
Antynea
981777d745 Add filter snapper's snapshot (#158)
- Add filter to ignore snapper's snapshot "type or description" during run "grub-mkconfig"

- Two new variables available in the config file:
# Ignore specific type of snapper's snapshot during run "grub-mkconfig".
# Type = single, pre or post.
# Default: ("")
GRUB_BTRFS_IGNORE_SNAPPER_TYPE=("")

# Ignore specific description of snapper's snapshot during run "grub-mkconfig".
# Default: ("")
GRUB_BTRFS_IGNORE_SNAPPER_DESCRIPTION=("")
2021-07-14 12:48:20 +02:00
Björn Daase
3433754c69 Makefile: Make sure make is invoked with appropriate permissions (#146) 2021-03-01 13:35:44 +01:00
Antynea
db753a9ac8 41_snapshots: update header
* 41_snapshots: update header
  * Update "Automatically update Grub" section.
  * Update "Purpose" section.
  * Update "What this script does" section
2021-02-21 15:37:43 +01:00
Antynea
83bf1da01e Improves systemd.path unit (#139)
* grub-btrfs.path : improves unit

#### Remember to modify your configuration accordingly.  
Allows the unit to start and stop automatically when the mount point is detected.  
If the mount point is already mounted when the unit is activated `systemctl enable grub-btrfs.path`,  
it will be necessary to start the unit for monitoring to begin `systemctl start grub-btrfs.path`.

- `DefaultDependencies=no`
Prevents systemd from automatically generating `Wants= or Requires= or After=`.
- `Requires=\x2esnapshots.mount`
Ensures unit is started if mount point exists.
- `After=\x2esnapshots.mount`
Unit start after the mounting point exists.
- `BindsTo=\x2esnapshots.mount`
If the mount point is removed, the unit will stop.
- `PathModified=\x2esnapshots.mount`
The monitored folder containing the snapshots.
- `WantedBy=\x2esnapshots.mount`
If the mount point exists, unit start automatically. (only if unit is activated before the mount point is mounted)

* Readme: Update "Automatically update grub" section

Update section following the recent change in the `grub-btrfs.path` unit.  
Add information to be able to update the `grub-btrfs` menu every time the system is restart/shutdown.
2021-02-21 05:26:39 +01:00
5 changed files with 228 additions and 124 deletions

View File

@@ -1,38 +1,33 @@
#! /usr/bin/env bash
#
#
# Written by: Antynea
# BTC donation address: 1Lbvz244WA8xbpHek9W2Y12cakM6rDe5Rt
# Github: https://github.com/Antynea/grub-btrfs
#
# Purpose:
# Improves Grub by adding "btrfs snapshots" to the Grub menu.
# You can start your system on a "snapshot" from the Grub menu.
# Supports manual snapshots, snapper ...
# Warning : booting on read-only snapshots can be tricky
# You can boot your system on a "snapshot" from the Grub menu.
# Supports manual snapshots, snapper, timeshift ...
# Warning : booting on read-only snapshots can be tricky.
# (Read about it, https://github.com/Antynea/grub-btrfs#warning-booting-on-read-only-snapshots-can-be-tricky)
#
# What this script does:
# - Automatically List snapshots existing on root partition (btrfs).
# - Automatically Detect if "/boot" is in separate partition.
# - Automatically Detect kernel, initramfs and intel microcode in "/boot" directory on snapshots.
# - Automatically Create corresponding "menuentry" in grub.cfg
# - Automatically Detect kernel, initramfs and intel/amd microcode in "/boot" directory on snapshots.
# - Automatically Create corresponding "menuentry" in grub.cfg.
# - Automatically detect snapper and use snapper's snapshot description if available.
# - Automatically generate grub.cfg if you use the provided systemd service.
#
# Installation:
# - Run `make install` or look into Makefile for instructions on where to put each file.
# - Refer to https://github.com/Antynea/grub-btrfs#installation-
#
# Customization:
# Refer to config for the list of available options and their default values.
# Place your configurations to /etc/default/grub-btrfs/config.
# You have the possibility to modify many parameters in /etc/default/grub-btrfs/config.
#
# Automatically update Grub
# If you would like Grub to automatically update when a snapshots is made or deleted:
# - Use systemctl start/enable grub-btrfs.path
# - grub-btrfs.path automatically (re)generates grub.cfg when a modification appears in /.snapshots folder (by default).
# - If your snapshots aren't mounted in /.snapshots, you must modify the watch folder using systemctl edit grub-btrfs.path
# - See https://github.com/Antynea/grub-btrfs#automatically-update-grub
# If you would like grub-btrfs menu to automatically update when a snapshot is created or deleted:
# - Refer to https://github.com/Antynea/grub-btrfs#automatically-update-grub.
#
# Special thanks for assistance and contributions:
# - My friends
@@ -41,25 +36,34 @@
set -e
prefix="/usr"
exec_prefix="/usr"
datarootdir="/usr/share"
sysconfdir="/etc"
grub_btrfs_config="${sysconfdir}/default/grub-btrfs/config"
[[ -f "$grub_btrfs_config" ]] && . "$grub_btrfs_config"
. "$datarootdir/grub/grub-mkconfig_lib"
. "${sysconfdir}/default/grub"
[[ -f "${sysconfdir}/default/grub" ]] && . "${sysconfdir}/default/grub"
## Exit the script, if:
disable_script() {
# Disable Grub-btrfs is set to true (default=false)
[[ "${GRUB_BTRFS_DISABLE:-"false"}" == "true" ]] && return 1
# btrfs-progs isn't installed
if ! type btrfs >/dev/null 2>&1; then return 1; fi
# grub-mkconfig_lib couldn't be found
[[ -f "$datarootdir/grub/grub-mkconfig_lib" ]] && . "$datarootdir/grub/grub-mkconfig_lib" || return 1
# Root filesystem isn't btrfs
root_fs=$(${grub_probe} --target="fs" / 2>/dev/null)
[[ "$root_fs" != "btrfs" ]] && return 1
return 0
}
disable_script
### Variables in /etc/default/grub-btrfs/config
## Disable Grub-btrfs (default=active)
[[ "${GRUB_BTRFS_DISABLE:-"false"}" == "true" ]] && exit 0
## Submenu name
distro=$(awk -F "=" '/^NAME=/ {gsub(/"/, "", $2); print $2}' /etc/os-release)
submenuname=${GRUB_BTRFS_SUBMENUNAME:-"${distro:-Linux} snapshots"}
## Prefix entry
prefixentry=${GRUB_BTRFS_PREFIXENTRY:-"Snapshot:"}
## Limit snapshots to show in the Grub menu
## Limit snapshots to show in the Grub menu (default=50)
limit_snap_show="${GRUB_BTRFS_LIMIT:-50}"
## How to sort snapshots list
btrfssubvolsort=(--sort="${GRUB_BTRFS_SUBVOLUME_SORT:-"-rootid"}")
@@ -76,31 +80,24 @@ case "${GRUB_BTRFS_DISABLE_PROTECTION_SUBMENU:-"false"}" in
*) unrestricted_access_submenu=""
esac
# Authorized users (--users foo,bar)
if [ ! -z "${GRUB_BTRFS_PROTECTION_AUTHORIZED_USERS}" ] ; then
if [ -n "${GRUB_BTRFS_PROTECTION_AUTHORIZED_USERS}" ] ; then
protection_authorized_users="--users ${GRUB_BTRFS_PROTECTION_AUTHORIZED_USERS} "
fi
### variables script
## Probe info "Boot partition"
# Boot device
boot_device=$(${grub_probe} --target=device ${boot_directory})
# hints string
boot_hs=$(${grub_probe} --device ${boot_device} --target="hints_string" 2>/dev/null)
# UUID of the boot partition
boot_uuid=$(${grub_probe} --device ${boot_device} --target="fs_uuid" 2>/dev/null)
# Type filesystem of boot partition
boot_fs=$(${grub_probe} --device ${boot_device} --target="fs" 2>/dev/null)
## Probe info "Root partition"
# Type filesystem of root partition
root_fs=$(${grub_probe} --target="fs" / 2>/dev/null)
# Root device
root_device=$(${grub_probe} --target=device /)
# UUID of the root partition
root_uuid=$(${grub_probe} --device ${root_device} --target="fs_uuid" 2>/dev/null)
## Probe informations of Root and Boot devices
# Probe info "Root partition"
root_device=$(${grub_probe} --target=device /) # Root device
root_uuid=$(${grub_probe} --device ${root_device} --target="fs_uuid" 2>/dev/null) # UUID of the root device
# Probe info "Boot partition"
boot_device=$(${grub_probe} --target=device ${boot_directory}) # Boot device
boot_uuid=$(${grub_probe} --device ${boot_device} --target="fs_uuid" 2>/dev/null) # UUID of the boot device
boot_hs=$(${grub_probe} --device ${boot_device} --target="hints_string" 2>/dev/null) # hints string
boot_fs=$(${grub_probe} --device ${boot_device} --target="fs" 2>/dev/null) # Type filesystem of boot device
## Parameters passed to the kernel
kernel_parameters="$GRUB_CMDLINE_LINUX $GRUB_CMDLINE_LINUX_DEFAULT"
## Mount point location
gbgmp=$(mktemp -d)
gbgmp=$(mktemp -dt grub-btrfs.XXXXXXXXXX)
## Class for theme
CLASS="--class snapshots --class gnu-linux --class gnu --class os"
## save IFS
@@ -122,32 +119,54 @@ detect_rootflags()
| sed -E 's/^.*[[:space:]]([[:graph:]]+)$/\1/;s/,?subvol(id)?=[^,$]+//g;s/^,//')
rootflags="rootflags=${fstabflags:+$fstabflags,}${GRUB_BTRFS_ROOTFLAGS:+$GRUB_BTRFS_ROOTFLAGS,}"
}
## Path to grub-script-check
grub_script_check="${bindir}/grub-script-check"
### Error Handling
## Error Handling
print_error()
{
local arg="$@"
local nothing_to_do="If you think an error has occurred , please file a bug report at \" https://github.com/Antynea/grub-btrfs \"\nNothing to do. Abort.\n"
printf "${arg}\n${nothing_to_do}" >&2 ;
local err_msg="$*"
local bug_report="If you think an error has occurred , please file a bug report at \" https://github.com/Antynea/grub-btrfs \""
printf "%s\n" "${err_msg}" "${bug_report}" >&2 ;
exit 0
}
test_btrfs()
unmount_gbgmp()
{
[[ "$root_fs" != "btrfs" ]] && print_error "Root partition isn't a btrfs filesystem.\nThis script only supports snapshots of the btrfs filesystem."
set +e
type btrfs >/dev/null 2>&1
if [[ $? -ne 0 ]]; then
print_error "Unable to retrieve info from btrfs filesystem, make sure you have btrfs-progs on your system."
if [[ -d "$gbgmp" ]]; then
local wait=true
local wait_max=0
printf "Unmount %s .." "$gbgmp" >&2;
while $wait; do
if grep -qs "$gbgmp" /proc/mounts; then
wait_max=$((1+$wait_max))
if umount "$gbgmp" >/dev/null 2>&1; then
wait=false # umount successful
printf " Success\n" >&2;
elif [[ $wait_max = 10 ]]; then
printf "\nWarning: Unable to unmount %s in %s\n" "$root_device" "$gbgmp" >&2;
break;
else
printf "." >&2 ; # output to show that the script is alive
sleep 2 # wait 2 seconds before retry
fi
else
wait=false # not mounted
printf " Success\n" >&2;
fi
done
if [[ "$wait" != true ]]; then
if ! rm -d "$gbgmp" >/dev/null 2>&1; then
printf "Unable to delete %s: Device or ressource is busy\n" "$gbgmp" >&2;
fi
fi
set -e
fi
}
### Script
## Create entry
entry()
{
echo "$@" >> "$grub_directory/grub-btrfs.cfg"
echo "$@" >> "$grub_directory/grub-btrfs.new"
}
## menu entries
@@ -160,24 +179,24 @@ make_menu_entries()
[[ ! -f "${boot_dir}"/"${k}" ]] && continue;
kversion=${k#*"-"}
for i in "${name_initramfs[@]}"; do
prefix_i=${i%%"-"*}
suffix_i=${i#*"-"}
alt_suffix_i=${i##*"-"}
if [ "${kversion}" = "${suffix_i}" ]; then i="${i}";
elif [ "${kversion}.img" = "${suffix_i}" ]; then i="${i}";
elif [ "${kversion}-fallback.img" = "${suffix_i}" ]; then i="${i}";
elif [ "${kversion}.gz" = "${suffix_i}" ]; then i="${i}";
else continue ;
fi
for u in "${name_microcode[@]}"; do
if [[ "${name_microcode}" != "x" ]] ; then
if [[ "${name_initramfs}" != "x" ]] ; then
prefix_i=${i%%"-"*}
suffix_i=${i#*"-"}
alt_suffix_i=${i##*"-"}
if [ "${kversion}" = "${suffix_i}" ]; then i="${i}";
elif [ "${kversion}.img" = "${suffix_i}" ]; then i="${i}";
elif [ "${kversion}-fallback.img" = "${suffix_i}" ]; then i="${i}";
elif [ "${kversion}.gz" = "${suffix_i}" ]; then i="${i}";
fi
for u in "${name_microcode[@]}"; do
if [[ "${name_microcode}" != "x" ]] ; then
entry "
menuentry '"${k}" & "${i}" & "${u}"' ${CLASS} "\$menuentry_id_option" 'gnulinux-snapshots-$boot_uuid' {"
else
else
entry "
menuentry '"${k}" & "${i}"' ${CLASS} "\$menuentry_id_option" 'gnulinux-snapshots-$boot_uuid' {"
fi
entry "\
fi
entry "\
if [ x\$feature_all_video_module = xy ]; then
insmod all_video
fi
@@ -191,18 +210,50 @@ make_menu_entries()
echo 'Loading Snapshot: "${snap_date_time}" "${snap_dir_name}"'
echo 'Loading Kernel: "${k}" ...'
linux \"${boot_dir_root_grub}/"${k}"\" root="${LINUX_ROOT_DEVICE}" ${kernel_parameters} ${rootflags}subvol=\""${snap_dir_name}"\""
if [[ "${name_microcode}" != "x" ]] ; then
entry "\
if [[ "${name_microcode}" != "x" ]] ; then
entry "\
echo 'Loading Microcode & Initramfs: "${u}" "${i}" ...'
initrd \"${boot_dir_root_grub}/"${u}"\" \"${boot_dir_root_grub}/"${i}"\""
else
entry "\
else
entry "\
echo 'Loading Initramfs: "${i}" ...'
initrd \"${boot_dir_root_grub}/"${i}"\""
fi
entry " }"
count_warning_menuentries=$((1+$count_warning_menuentries))
done
fi
entry " }"
count_warning_menuentries=$((1+$count_warning_menuentries))
done
else
for u in "${name_microcode[@]}"; do
if [[ "${name_microcode}" != "x" ]] ; then
entry "
menuentry '"${k}" & "${u}"' ${CLASS} "\$menuentry_id_option" 'gnulinux-snapshots-$boot_uuid' {"
else
entry "
menuentry '"${k}"' ${CLASS} "\$menuentry_id_option" 'gnulinux-snapshots-$boot_uuid' {"
fi
entry "\
if [ x\$feature_all_video_module = xy ]; then
insmod all_video
fi
set gfxpayload=keep
insmod ${boot_fs}
if [ x\$feature_platform_search_hint = xy ]; then
search --no-floppy --fs-uuid --set=root ${boot_hs} ${boot_uuid}
else
search --no-floppy --fs-uuid --set=root ${boot_uuid}
fi
echo 'Loading Snapshot: "${snap_date_time}" "${snap_dir_name}"'
echo 'Loading Kernel: "${k}" ...'
linux \"${boot_dir_root_grub}/"${k}"\" root="${LINUX_ROOT_DEVICE}" ${kernel_parameters} ${rootflags}subvol=\""${snap_dir_name}"\""
if [[ "${name_microcode}" != "x" ]] ; then
entry "\
echo 'Loading Microcode: "${u}" ...'
initrd \"${boot_dir_root_grub}/"${u}"\""
fi
entry " }"
count_warning_menuentries=$((1+$count_warning_menuentries))
done
fi
done
done
entry "}"
@@ -220,17 +271,16 @@ trim() {
snapshot_list()
{
# Query info from snapper if it is installed
type snapper >/dev/null 2>&1
if [ $? -eq 0 ]; then
if type snapper >/dev/null 2>&1; then
if [ -s "/etc/snapper/configs/$snapper_config" ]; then
printf "Info: snapper detected, using config '$snapper_config'\n" >&2
printf "Info: snapper detected, using config: %s\n" "$snapper_config" >&2
local snapper_ids=($(snapper --no-dbus -t 0 -c "$snapper_config" list --disable-used-space | tail -n +3 | cut -d'|' -f 1))
local snapper_types=($(snapper --no-dbus -t 0 -c "$snapper_config" list --disable-used-space | tail -n +3 | cut -d'|' -f 2))
IFS=$'\n'
local snapper_descriptions=($(snapper --no-dbus -t 0 -c "$snapper_config" list --disable-used-space | tail -n +3 | rev | cut -d'|' -f 2 | rev))
else
printf "Warning: snapper detected but config '$snapper_config' does not exist\n" >&2
printf "Warning: snapper detected but config: %s does not exist\n" "$snapper_config" >&2
fi
fi
@@ -250,12 +300,12 @@ snapshot_list()
[[ ${snap_path_name%%"/"*} == "<FS_TREE>" ]] && snap_path_name=${snap_path_name#*"/"}
# ignore specific path during run "grub-mkconfig"
if [ ! -z "${GRUB_BTRFS_IGNORE_SPECIFIC_PATH}" ] ; then
if [ -n "${GRUB_BTRFS_IGNORE_SPECIFIC_PATH}" ] ; then
for isp in ${GRUB_BTRFS_IGNORE_SPECIFIC_PATH[@]} ; do
[[ "${snap_path_name}" == "${isp}" ]] && continue 2;
done
fi
if [ ! -z "${GRUB_BTRFS_IGNORE_PREFIX_PATH}" ] ; then
if [ -n "${GRUB_BTRFS_IGNORE_PREFIX_PATH}" ] ; then
for isp in ${GRUB_BTRFS_IGNORE_PREFIX_PATH[@]} ; do
[[ "${snap_path_name}" == "${isp}"/* ]] && continue 2;
done
@@ -300,6 +350,18 @@ snapshot_list()
if [[ "$snapper_id" == "$id" ]]; then
local snapper_type=$(trim "${snapper_types[$j]}")
local snapper_description=$(trim "${snapper_descriptions[$j]}")
# ignore snapper_type or snapper_description during run "grub-mkconfig"
if [ -n "${GRUB_BTRFS_IGNORE_SNAPPER_TYPE}" ] ; then
for ist in ${GRUB_BTRFS_IGNORE_SNAPPER_TYPE[@]} ; do
[[ "${snapper_type}" == "${ist}" ]] && continue 3;
done
fi
if [ -n "${GRUB_BTRFS_IGNORE_SNAPPER_DESCRIPTION}" ] ; then
for isd in ${GRUB_BTRFS_IGNORE_SNAPPER_DESCRIPTION[@]} ; do
[[ "${snapper_description}" == "${isd}" ]] && continue 3;
done
fi
printf -v entry "%-${max_entry_length}s | %-${max_type_length}s | %s" "$entry" "$snapper_type" "$snapper_description"
break
fi
@@ -323,7 +385,7 @@ detect_kernel()
done
# Custom name kernel in GRUB_BTRFS_NKERNEL
if [ ! -z "${GRUB_BTRFS_NKERNEL}" ] ; then
if [ -n "${GRUB_BTRFS_NKERNEL}" ] ; then
for ckernel in "${boot_dir}/${GRUB_BTRFS_NKERNEL[@]}" ; do
[[ ! -f "${ckernel}" ]] && continue;
list_kernel+=("$ckernel")
@@ -346,12 +408,13 @@ detect_initramfs()
done
# Custom name initramfs in GRUB_BTRFS_NINIT
if [ ! -z "${GRUB_BTRFS_NINIT}" ] ; then
if [ -n "${GRUB_BTRFS_NINIT}" ] ; then
for cinitramfs in "${boot_dir}/${GRUB_BTRFS_NINIT[@]}" ; do
[[ ! -f "${cinitramfs}" ]] && continue;
list_initramfs+=("$cinitramfs")
done
fi
if [ -z "${list_initramfs}" ]; then list_initramfs=(x); fi
}
## Detect microcode in "/boot"
@@ -371,7 +434,7 @@ detect_microcode()
done
# Custom name microcode in GRUB_BTRFS_CUSTOM_MICROCODE
if [ ! -z "${GRUB_BTRFS_CUSTOM_MICROCODE}" ] ; then
if [ -n "${GRUB_BTRFS_CUSTOM_MICROCODE}" ] ; then
for cucode in "${boot_dir}/${GRUB_BTRFS_CUSTOM_MICROCODE[@]}" ; do
[[ ! -f "${cucode}" ]] && continue
list_ucode+=("$cucode")
@@ -428,7 +491,6 @@ boot_bounded()
detect_rootflags
# Initramfs (Original + custom initramfs)
detect_initramfs
if [ -z "${list_initramfs}" ]; then continue; fi
name_initramfs=("${list_initramfs[@]##*"/"}")
# microcode (auto-detect + custom microcode)
detect_microcode
@@ -448,11 +510,10 @@ boot_bounded()
### Limit snapshots found during run "grub-mkconfig"
count_limit_snap=$((1+$count_limit_snap))
[[ $count_limit_snap -ge $limit_snap_show ]] && break;
# Limit generation of menuentries if exceeds 250
# [[ $count_warning_menuentries -ge 250 ]] && break;
done
IFS=$oldIFS
}
boot_separate()
{
boot_dir="${boot_directory}"
@@ -465,7 +526,6 @@ boot_separate()
name_kernel=("${list_kernel[@]##*"/"}")
# Initramfs (Original + custom initramfs)
detect_initramfs
if [ -z "${list_initramfs}" ]; then print_error "Initramfs not found."; fi
name_initramfs=("${list_initramfs[@]##*"/"}")
# microcode (auto-detect + custom microcode)
detect_microcode
@@ -496,22 +556,17 @@ boot_separate()
# Limit snapshots found during run "grub-mkconfig"
count_limit_snap=$((1+$count_limit_snap))
[[ $count_limit_snap -ge $limit_snap_show ]] && break;
# Limit generation of menuentries if exceeds 250
# [[ $count_warning_menuentries -ge 250 ]] && break;
done
IFS=$oldIFS
}
### Start
printf "Detecting snapshots ...\n" >&2 ;
# Only support btrfs snapshots
test_btrfs
# Delete existing config
#rm -f --preserve-root "$grub_directory/grub-btrfs.cfg"
> "$grub_directory/grub-btrfs.cfg"
rm -f "$grub_directory/grub-btrfs.new"
> "$grub_directory/grub-btrfs.new"
# Create mount point then mounting
[[ ! -d $gbgmp ]] && mkdir -p $gbgmp
mount -o subvolid=5 /dev/disk/by-uuid/$root_uuid $gbgmp/
[[ ! -d $gbgmp ]] && mkdir -p "$gbgmp"
mount -o ro,subvolid=5 /dev/disk/by-uuid/"$root_uuid" "$gbgmp/"
trap "unmount_gbgmp" EXIT # unmounting mount point on EXIT signal
# Count menuentries
count_warning_menuentries=0
# Count snapshots
@@ -531,23 +586,25 @@ else
boot_bounded
fi
fi
# unmounting mount point
umount $gbgmp
# Show warn, menuentries exceeds 250 entries
[[ $count_warning_menuentries -ge 250 ]] && printf "Generated ${count_warning_menuentries} total GRUB entries. You might experience issues loading snapshots menu in GRUB.\n" >&2 ;
# printf "menuentries = $count_warning_menuentries \n" >&2 ;
[[ $count_warning_menuentries -ge 250 ]] && printf "Generated %s total GRUB entries. You might experience issues loading snapshots menu in GRUB.\n" "${count_warning_menuentries}" >&2 ;
# Show total found snapshots
if [[ "${GRUB_BTRFS_SHOW_TOTAL_SNAPSHOTS_FOUND:-"true"}" = "true" && ! -z "${count_limit_snap}" && "${count_limit_snap}" != "0" ]]; then
printf "Found ${count_limit_snap} snapshot(s)\n" >&2 ;
if [[ "${GRUB_BTRFS_SHOW_TOTAL_SNAPSHOTS_FOUND:-"true"}" = "true" && -n "${count_limit_snap}" && "${count_limit_snap}" != "0" ]]; then
printf "Found %s snapshot(s)\n" "${count_limit_snap}" >&2 ;
fi
# if no snapshot found, exit
if [[ "${count_limit_snap}" = "0" || -z "${count_limit_snap}" ]]; then
print_error "No snapshots found."
fi
# Make a submenu in GRUB (grub.cfg)
cat << EOF
# Make a submenu in GRUB (grub.cfg) and move "grub-btrfs.new" to "grub-btrfs.cfg"
if ${grub_script_check} "$grub_directory/grub-btrfs.new"; then
cat "$grub_directory/grub-btrfs.new" > "$grub_directory/grub-btrfs.cfg"
rm -f "$grub_directory/grub-btrfs.new"
cat << EOF
submenu '${submenuname}' ${protection_authorized_users}${unrestricted_access_submenu}{
configfile "\${prefix}/grub-btrfs.cfg"
}
EOF
### End
else
print_error "Syntax errors are detected in generated grub-btrfs.cfg file."
fi

View File

@@ -9,6 +9,10 @@ LIB_DIR = $(DESTDIR)$(PREFIX)/lib
.PHONY: install uninstall help
install:
@if test "$(shell id -u)" != 0; then \
echo "You are not root, run this target as root please."; \
exit 1; \
fi
@install -Dm755 -t "$(DESTDIR)/etc/grub.d/" 41_snapshots-btrfs
@install -Dm644 -t "$(DESTDIR)/etc/default/grub-btrfs/" config
@install -Dm644 -t "$(LIB_DIR)/systemd/system/" grub-btrfs.service
@@ -23,6 +27,10 @@ install:
@install -Dm644 "initramfs/readme.md" "$(SHARE_DIR)/doc/$(PKGNAME)/initramfs-overlayfs.md"
uninstall:
@if test "$(shell id -u)" != 0; then \
echo "You are not root, run this target as root please."; \
exit 1; \
fi
@grub_dirname="$$(grep -oP '^[[:space:]]*GRUB_BTRFS_GRUB_DIRNAME=\K.*' "$(DESTDIR)/etc/default/grub-btrfs/config" | sed "s|\s*#.*||;s|(\s*\(.\+\)\s*)|\1|;s|['\"]||g")"; \
rm -f "$${grub_dirname:-/boot/grub}/grub-btrfs.cfg"
@rm -f "$(DESTDIR)/etc/default/grub-btrfs/config"

View File

@@ -6,10 +6,10 @@
This is a version 4.xx of grub-btrfs
##### BTC donation address: `1Lbvz244WA8xbpHek9W2Y12cakM6rDe5Rt`
##
### Description
### Description :
Improves Grub by adding "btrfs snapshots" to the Grub menu.
You can start your system on a "snapshot" from the Grub menu.
You can boot your system on a "snapshot" from the Grub menu.
Supports manual snapshots, snapper, timeshift ...
##### Warning: booting on read-only snapshots can be tricky
@@ -33,7 +33,7 @@ Refer to the [documentation](https://github.com/Antynea/grub-btrfs/blob/master/i
##
### Installation :
#### Arch Linux
The package is available in the community repository [grub-btrfs](https://archlinux.org/packages/community/any/grub-btrfs/)
```
pacman -S grub-btrfs
```
@@ -41,34 +41,60 @@ pacman -S grub-btrfs
#### Manual
* Run `make install` or look into Makefile for instructions on where to put each file.
* Dependencies:
* [btrfs-progs](https://archlinux.org/packages/core/x86_64/btrfs-progs/)
* [grub](https://archlinux.org/packages/core/x86_64/grub/)
NOTE: Generate your Grub menu after installation for the changes to take effect.
On Arch Linux use `grub-mkconfig -o /boot/grub/grub.cfg`.
On Arch Linux use `grub-mkconfig -o /boot/grub/grub.cfg`.
On Debian-like distribution `update-grub` is an alias to `grub-mkconfig ...`
##
### Customization:
### Customization :
You have the possibility to modify many parameters in `/etc/default/grub-btrfs/config`.
See [config file](https://github.com/Antynea/grub-btrfs/blob/master/config) for more information.
##
### Automatically update grub
If you would like Grub to automatically update when a snapshot is made or deleted:
* Use `systemctl start/enable grub-btrfs.path`.
* `grub-btrfs.path` automatically (re)generates `grub.cfg` when a modification appears in `/.snapshots` folder (by default).
* If your snapshots aren't mounted in `/.snapshots`, you must modify the watch folder using `systemctl edit grub-btrfs.path`.
* For example: Timeshift mount its snapshots in `/run/timeshift/backup/timeshift-btrfs/snapshots` folder.
1- If you would like grub-btrfs menu to automatically update when a snapshot is created or deleted:
* Use `systemctl enable grub-btrfs.path`.
* `grub-btrfs.path` automatically (re)generates `grub-btrfs.cfg` when a modification appears in `/.snapshots` mount point (by default).
* If the `/.snapshots` mount point is already mounted, then use `systemctl start grub-btrfs.path` to start monitoring.
Otherwise, the unit will automatically start monitoring when the mount point will be available.
* If your snapshots location aren't mounted in `/.snapshots`, you must modify `grub-btrfs.path` unit using
`systemctl edit --full grub-btrfs.path` and run `systemctl reenable grub-btrfs.path` for changes take effect.
To find out the name of the `.mount` unit
use `systemctl list-units -t mount`.
* For example: Timeshift mounts its snapshot folder in `/run/timeshift/backup/timeshift-btrfs/snapshots`.
Use `systemctl edit grub-btrfs.path`.
Then wrote:
Use `systemctl edit --full grub-btrfs.path`.
Then replace the whole block by:
```
[Unit]
Description=Monitors for new snapshots
DefaultDependencies=no
Requires=run-timeshift-backup.mount
After=run-timeshift-backup.mount
BindsTo=run-timeshift-backup.mount
[Path]
PathModified=/run/timeshift/backup/timeshift-btrfs/snapshots
[Install]
WantedBy=run-timeshift-backup.mount
```
and finally save.
Then save and finally run `systemctl reenable grub-btrfs.path` for changes take effect.
Optional:
If the `/run/timeshift/backup/timeshift-btrfs/snapshots` mount point is already mounted,
then use `systemctl start grub-btrfs.path` to start monitoring.
Otherwise, the unit will automatically start monitoring when the mount point will be available.
* You can view your change to `systemctl cat grub-btrfs.path`.
* To revert change use `systemctl revert grub-btrfs.path`.
##### Warning:
2- If you would like grub-btrfs menu to automatically update on system restart/shutdown:
[Look at this comment](https://github.com/Antynea/grub-btrfs/issues/138#issuecomment-766918328)
Currently not implemented
##### Warning :
by default, `grub-mkconfig` command is used.
Might be `grub2-mkconfig` on some systems (Fedora ...).
Edit `GRUB_BTRFS_MKCONFIG` variable in `/etc/default/grub-btrfs/config` file to reflect this.

9
config
View File

@@ -74,6 +74,15 @@ GRUB_BTRFS_IGNORE_SPECIFIC_PATH=("@")
# Default: ("var/lib/docker" "@var/lib/docker" "@/var/lib/docker")
GRUB_BTRFS_IGNORE_PREFIX_PATH=("var/lib/docker" "@var/lib/docker" "@/var/lib/docker")
# Ignore specific type of snapper's snapshot during run "grub-mkconfig".
# Type = single, pre, post.
# Default: ("")
GRUB_BTRFS_IGNORE_SNAPPER_TYPE=("")
# Ignore specific description of snapper's snapshot during run "grub-mkconfig".
# Default: ("")
GRUB_BTRFS_IGNORE_SNAPPER_DESCRIPTION=("")
# By default "grub-btrfs" automatically detects your boot partition,
# either located at the system root or on a separate partition,
# but cannot detect if it is in a subvolume.

View File

@@ -1,8 +1,12 @@
[Unit]
Description=Monitors for new snapshots
DefaultDependencies=no
Requires=\x2esnapshots.mount
After=\x2esnapshots.mount
BindsTo=\x2esnapshots.mount
[Path]
PathModified=/.snapshots
[Install]
WantedBy=multi-user.target
WantedBy=\x2esnapshots.mount