diff --git a/greenboot.spec b/greenboot.spec index 8a1dab72..c71b91a5 100644 --- a/greenboot.spec +++ b/greenboot.spec @@ -154,6 +154,8 @@ install -DpZm 0644 etc/greenboot/greenboot.conf %{buildroot}%{_sysconfdir}/%{nam %{_prefix}/lib/bootupd/grub2-static/configs.d/*.cfg %{_unitdir}/greenboot-status.service %{_libexecdir}/%{name}/greenboot-grub2-set-counter +%{_libexecdir}/%{name}/greenboot-grub2-set-success +%{_libexecdir}/%{name}/greenboot-boot-remount %{_unitdir}/greenboot-grub2-set-success.service %{_unitdir}/greenboot-grub2-set-counter.service %{_libexecdir}/%{name}/greenboot-rpm-ostree-grub2-check-fallback diff --git a/usr/lib/systemd/system/greenboot-grub2-set-counter.service b/usr/lib/systemd/system/greenboot-grub2-set-counter.service index 72837dc9..a8827daa 100644 --- a/usr/lib/systemd/system/greenboot-grub2-set-counter.service +++ b/usr/lib/systemd/system/greenboot-grub2-set-counter.service @@ -18,6 +18,7 @@ RequiresMountsFor=/boot Type=oneshot RemainAfterExit=yes ExecStart=/usr/libexec/greenboot/greenboot-grub2-set-counter +PrivateMounts=yes [Install] RequiredBy=ostree-finalize-staged.service diff --git a/usr/lib/systemd/system/greenboot-grub2-set-success.service b/usr/lib/systemd/system/greenboot-grub2-set-success.service index f5ef55a1..db10061d 100644 --- a/usr/lib/systemd/system/greenboot-grub2-set-success.service +++ b/usr/lib/systemd/system/greenboot-grub2-set-success.service @@ -15,8 +15,8 @@ After=boot-complete.target [Service] Type=oneshot RemainAfterExit=yes -ExecStart=/usr/bin/grub2-editenv - set boot_success=1 -ExecStart=/usr/bin/grub2-editenv - unset boot_counter +ExecStart=/usr/libexec/greenboot/greenboot-grub2-set-success +PrivateMounts=yes [Install] WantedBy=multi-user.target diff --git a/usr/lib/systemd/system/greenboot-rpm-ostree-grub2-check-fallback.service b/usr/lib/systemd/system/greenboot-rpm-ostree-grub2-check-fallback.service index 30312d36..795eb748 100644 --- a/usr/lib/systemd/system/greenboot-rpm-ostree-grub2-check-fallback.service +++ b/usr/lib/systemd/system/greenboot-rpm-ostree-grub2-check-fallback.service @@ -16,6 +16,7 @@ Before=greenboot-grub2-set-success.service Type=oneshot RemainAfterExit=yes ExecStart=/usr/libexec/greenboot/greenboot-rpm-ostree-grub2-check-fallback +PrivateMounts=yes [Install] RequiredBy=greenboot-healthcheck.service diff --git a/usr/libexec/greenboot/greenboot-boot-remount b/usr/libexec/greenboot/greenboot-boot-remount new file mode 100644 index 00000000..1c88d49a --- /dev/null +++ b/usr/libexec/greenboot/greenboot-boot-remount @@ -0,0 +1,23 @@ +#!/bin/bash +set -eo pipefail + +# Boolean variable to track if /boot was initially mounted as read-only +# Ensure compatibility with rpm-ostree where /boot is rw but in bootc /boot is ro +boot_was_ro=false + +# Remount /boot as read-only if it was mounted as read-only ealier +function remount_boot_ro { + if $boot_was_ro; then + mount -o remount,ro /boot || exit 13 + fi + return +} + +# Remount /boot as read-write if it was mounted as read-only +function remount_boot_rw { + if grep -q " /boot .* ro," /proc/mounts; then + mount -o remount,rw /boot || exit 13 + boot_was_ro=true + fi + return +} diff --git a/usr/libexec/greenboot/greenboot-grub2-set-counter b/usr/libexec/greenboot/greenboot-grub2-set-counter index 1547a806..f9a1ea7b 100755 --- a/usr/libexec/greenboot/greenboot-grub2-set-counter +++ b/usr/libexec/greenboot/greenboot-grub2-set-counter @@ -1,6 +1,8 @@ #!/bin/bash set -eo pipefail +source /usr/libexec/greenboot/greenboot-boot-remount + GREENBOOT_CONFIGURATION_FILE=/etc/greenboot/greenboot.conf if test -f "$GREENBOOT_CONFIGURATION_FILE"; then # shellcheck source=etc/greenboot/greenboot.conf @@ -14,7 +16,23 @@ elif [ -n "$GREENBOOT_MAX_BOOT_ATTEMPTS" ]; then else max_boot_attempts=3 # default to 3 attempts fi -grub2-editenv - set boot_counter="$max_boot_attempts" -grub2-editenv - set boot_success=0 + + +remount_boot_rw + +if ! /usr/bin/grub2-editenv - set boot_counter="$max_boot_attempts"; then + # If the first command fails, remount /boot as read-only and exit with failure + remount_boot_ro + exit 1 +fi + +if ! /usr/bin/grub2-editenv /boot/grub2/grubenv set boot_success=0; then + # If the first command fails, remount /boot as read-only and exit with failure + remount_boot_ro + exit 1 +fi + +# Revert /boot as read-only +remount_boot_ro echo "<3>GRUB2 environment variables have been set for system upgrade. Max boot attempts is $max_boot_attempts" diff --git a/usr/libexec/greenboot/greenboot-grub2-set-success b/usr/libexec/greenboot/greenboot-grub2-set-success new file mode 100644 index 00000000..b8999115 --- /dev/null +++ b/usr/libexec/greenboot/greenboot-grub2-set-success @@ -0,0 +1,25 @@ +#!/bin/bash + +set -eo pipefail + +source /usr/libexec/greenboot/greenboot-boot-remount +remount_boot_rw + +# Run the grub2-editenv commands +if ! /usr/bin/grub2-editenv /boot/grub2/grubenv set boot_success=1; then + # If the first command fails, remount /boot as read-only and exit with failure + remount_boot_ro + exit 1 +fi + +if ! /usr/bin/grub2-editenv /boot/grubenv unset boot_counter; then + # If the second command fails, remount /boot as read-only and exit with failure + remount_boot_ro + exit 1 +fi + +# Remount /boot as read-only if it was mounted as read-write +remount_boot_ro + +# If everything succeeded, exit with success +exit 0 diff --git a/usr/libexec/greenboot/greenboot-rpm-ostree-grub2-check-fallback b/usr/libexec/greenboot/greenboot-rpm-ostree-grub2-check-fallback index bbe25f66..d15b98ea 100755 --- a/usr/libexec/greenboot/greenboot-rpm-ostree-grub2-check-fallback +++ b/usr/libexec/greenboot/greenboot-rpm-ostree-grub2-check-fallback @@ -1,16 +1,47 @@ #!/bin/bash set -euo pipefail +source /usr/libexec/greenboot/greenboot-boot-remount + +function attempt_rollback { + # Check if the bootc command is available + if command -v bootc &> /dev/null; then + status_type=$(bootc status --booted --json 2>/dev/null | jq -r .status.type 2>/dev/null) + if [ "$status_type" == "bootcHost" ]; then + bootc rollback + echo "<3>FALLBACK BOOT DETECTED! Default bootc deployment has been rolled back." + fi + return + fi + # Check if its ostree based os + if [ -f /run/ostree-booted ]; then + rpm-ostree rollback + echo "<3>FALLBACK BOOT DETECTED! Default rpm-ostree deployment has been rolled back." + return + fi + echo "<3>Rollback is only supported in ostree or bootc based os." + return +} + # Determine if the current boot is a fallback boot # If booted into fallback deployment, clean up bootloader entries (rollback) if grub2-editenv list | grep -q "^boot_counter=-1$"; then # Logs from previous boot may be unavailable on systems without internal RTC; defaulting to empty string prev_logs="$(journalctl -u greenboot-healthcheck.service -p 2 -b -1 -o cat)" || true - rpm-ostree rollback - echo "<3>FALLBACK BOOT DETECTED! Default rpm-ostree deployment has been rolled back." + attempt_rollback if [ -n "$prev_logs" ]; then echo "<3>Health check logs from previous boot:" echo "<3>$prev_logs" fi - grub2-editenv - unset boot_counter + +remount_boot_rw + + if ! /usr/bin/grub2-editenv - unset boot_counter; then + # If the above command fails, remount /boot as read-only and exit with failure + remount_boot_ro + exit 1 + fi fi + +# Remount /boot as read-only if it was mounted as read-write +remount_boot_ro