Compare commits

...

37 Commits
v1.0 ... main

Author SHA1 Message Date
Adrian Wannenmacher c666eb8e3f
update readme 2022-10-01 22:15:59 +02:00
Adrian Wannenmacher 4d4c2c7920
simplify program 2022-10-01 22:08:39 +02:00
Adrian Wannenmacher 38745b9d44
check depenencies only if enabled 2021-10-17 15:47:35 +02:00
Adrian Wannenmacher e4a9af9cd0
fix bug in enabling system 2021-10-17 15:42:00 +02:00
Adrian Wannenmacher bb931ff68c
fix pacman hook error 2021-10-17 15:34:27 +02:00
Adrian Wannenmacher 6924fd8cee
add a new feautre to make pacman installation possible 2021-10-17 15:19:54 +02:00
Adrian Wannenmacher 9514f84340
prepare for pacman package 2021-10-17 15:01:26 +02:00
Adrian Wannenmacher 107721ef8f
Allow separate kernel cmd for current system
This commit adds the requirement for a new kernel command line file at
`/etc/snap-pac-uki/kernel-cmd-current`. When generating current ukis, this file
will be used for the non-fallback variant instead of the generic `kernel-cmd`.

The reason for this change is hibernating. When saving the system state to RAM,
users might not want snapshot varints to resume from there. Previosly, this
would not have been a possible configuration.
2021-10-17 02:17:46 +02:00
Adrian Wannenmacher 944e65bdae
fix: 🐛 make secureboot detection work 2021-06-19 16:24:45 +02:00
Adrian Wannenmacher 5f890bb2a4
feat: add microcode if available 2021-06-19 15:48:39 +02:00
Adrian Wannenmacher 16c3dc6b66
feat: make efibootmgr and sbsign optional dependencies 2021-06-19 15:17:59 +02:00
Adrian Wannenmacher c4dc5fdc0a
fix: 🐛 stop removing uki uefi boot entries if deactivated 2021-06-18 21:17:02 +02:00
Adrian Wannenmacher 8a27b4e196
feat: add an option to stop creating efi boot entries 2021-06-18 20:02:35 +02:00
Adrian Wannenmacher 782be942eb
feat: stop deleting or overwriting config file 2021-06-18 20:02:03 +02:00
Adrian Wannenmacher 53cc21a6f1 Merge branch 'main' of ssh://git.tfld.dev:2200/tfld/snap-pac-uki 2021-06-16 18:46:15 +02:00
Adrian Wannenmacher 033771209c
Update to new snap-pac version 2021-06-16 18:45:40 +02:00
Adrian Wannenmacher 538af9f721
docs: 📝 mention martix room in README 2021-02-13 19:53:18 +01:00
Adrian Wannenmacher e437d37b24
chore: 📄 Add LICENSE 2021-02-13 19:18:43 +01:00
Adrian Wannenmacher 69df4744c7
Update readme 2021-02-13 19:10:36 +01:00
Adrian Wannenmacher 38d3035920
feat: improve install script and add unistall script 2021-02-13 19:09:29 +01:00
Adrian Wannenmacher 52c785e844
fix: 💬 give current uki creation hook a unique description 2021-02-12 18:20:46 +01:00
Adrian Wannenmacher 2c65c9b8dc
feat: read config from a file inside /etc 2021-02-12 18:18:54 +01:00
Adrian Wannenmacher 6b1db28923
feat: implement uki signing 2021-02-12 17:45:15 +01:00
Adrian Wannenmacher 473c2e9a61
feat: add cleaning facilities 2021-02-12 02:05:34 +01:00
Adrian Wannenmacher 7282ad8025
feat: add pacman hooks and update install script 2021-02-12 01:34:13 +01:00
Adrian Wannenmacher 1f33307608
finish rewrite 2021-02-12 01:18:56 +01:00
Adrian Wannenmacher 4e7e835bd9
refactor: ♻️ start another rewrite with much smaller scope 2021-02-12 00:41:24 +01:00
Adrian Wannenmacher 333b8d06fe
fix: 🐛 create boot entry after copying uki 2021-02-11 08:02:38 +01:00
Adrian Wannenmacher 988331e6e3
fix: 🐛 make efibootmgr silent 2021-02-11 07:58:15 +01:00
Adrian Wannenmacher 5885881075
feat: add pacman hooks and install script 2021-02-11 07:56:53 +01:00
Adrian Wannenmacher 16ec7f0955
refactor: ♻️ change folder structure 2021-02-11 07:38:48 +01:00
Adrian Wannenmacher 5383069d66
fix: 🐛 fix some bugs in the previous commit 2021-02-10 22:42:05 +01:00
Adrian Wannenmacher 53279cb983
feat: tell the efi about snapshot ukis 2021-02-10 22:40:10 +01:00
Adrian Wannenmacher 0bbca8b8ff
fix: 🐛 fix copying 2021-02-10 22:02:46 +01:00
Adrian Wannenmacher 3c018e29ed
fix: 🐛 create ukis with appropriate configuration 2021-02-10 21:57:18 +01:00
Adrian Wannenmacher 79a7067f58
fix: 🐛 remove missed output 2021-02-10 21:42:21 +01:00
Adrian Wannenmacher 7cba17357b
refactor: ♻️ start rewrite
BREAKING CHANGE: everything
2021-02-10 21:32:05 +01:00
13 changed files with 394 additions and 81 deletions

21
LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2021 Adrian Wannenmacher
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

113
README.md
View File

@ -1,9 +1,108 @@
# snap-pac-uki
Pacman hooks creating unified kernel images for snapshots created by snap-pac.
## Limits
- only works on the "root" snap-pac config
- puts all UKIs into `/boot/EFI/Linux/`
- kernel parameters are extracted from a file called `/boot/loader/entries/00-arch.conf`
- expects that the default subvolume is mounted via kernel command line
- expects that the default subvolume is called `@`
Need help? Talk to me at [#snap-pac-uki:tfld.dev][matrix-room].
## What is this?
This is a collection of scripts, pacman hooks and and systemd units for creating
and managing unified kernel images (UKIs) based on snapper. It originally
expected to be executed as a pacman hook after `snap-pac`, but this is no longer
required (though the name remains).
## Installation
### Prerequisites
While not depending on it directly, this tool expects that you already set up
snapper. It expects, that each snapshot contains its kernel
(`/boot/vmlinuz-linux`) and initramfs (`/boot/intitramfs-linux.img`), as well as
a kernel command line file (`/etc/snap-pac-uki/kernel-cmd`).
For generating the current and fallback UKIs, the kernel and initramfs files of
the currently running system are used. For a kernel cmd, it uses
`/etc/snap-pac-uki/kernel-cmd-current` and `/etc/snap-pac-uki/kernel-cmd-fallback`.
If you want to boot a UKI directly, use the `efibootmgr` command to create an
entry.
```bash
# template
efibootmgr -c \
-d <EFI DEVICE> \
-p <EFI PARTITION NUMBER> \
-l <EFI PATH> \
-L <LABEL>
# example
efibootmgr -c \
-d /dev/nvme0n1 \
-p 1 \
-l "\EFI\Linux\arch-linux.efi" \
-L "Arch Linux"
# example for fallback
efibootmgr -c \
-d /dev/nvme0n1 \
-p 1 \
-l "\EFI\Linux\arch-linux-fallback.efi" \
-L "Arch Linux (Fallback)"
```
### Using the install script
1. You can install the dependencies of this program with the following command:
```bash
pacman -S fish ripgrep sbsigntools sed
```
2. Download the latest release from [here][releases] and unpack it.
3. Then run the install script (you need to be in the same directory):
```bash
./install.fish
```
## Usage
### Creating snapshot UKIs
This creates a new UKI for all snapshots where none is found:
```bash
snap-pac-uki snapshots
```
### Create current UKIs
This creates a new UKI for the current system (including a fallback variant):
```bash
snap-pac-uki current
```
### Clean up UKIs
This removes all UKIs without corresponding snapshot (solely based on number):
```bash
snap-pac-uki clean
```
### Enable periodic cleanups using systemd timer
```
systemctl enable --now snap-pac-uki-cleanup.timer
```
## Uninstall
Simply run the following command in this directory:
```bash
./uninstall.fish
```
[matrix-room]: https://matrix.to/#/#snap-pac-uki:tfld.dev
[releases]: https://git.tfld.dev/tfld/snap-pac-uki/releases

View File

@ -1,4 +1,23 @@
#!/bin/fish
cp ./hooks/* /usr/share/libalpm/hooks/
cp ./scripts/* /usr/share/libalpm/scripts/
cp ./src/snap-pac-uki.fish /usr/local/bin/snap-pac-uki
echo "installed script."
if not test -d "/etc/snap-pac-uki"
mkdir -p "/etc/snap-pac-uki"
echo "created config dir."
end
if not test -f "/etc/snap-pac-uki/config.fish"
cp ./src/config.fish /etc/snap-pac-uki/config.fish
echo "installed config script."
else
cp ./src/config.fish /etc/snap-pac-uki/config-new.fish
echo "found existing config script -> installed as -new file."
end
cp ./pacman/* /usr/share/libalpm/hooks/
echo "installed pacman hooks."
cp ./systemd/* /etc/systemd/system/
echo "installed systemd unit files"

View File

@ -8,5 +8,5 @@ Target = *
[Action]
Description = Performing pre UKI creation...
When = PreTransaction
Exec = /usr/share/libalpm/scripts/snap-pac-uki_create.fish
Exec = /usr/local/bin/snap-pac-uki snapshots
AbortOnFail

View File

@ -8,4 +8,4 @@ Target = *
[Action]
Description = Performing post UKI creation...
When = PostTransaction
Exec = /usr/share/libalpm/scripts/snap-pac-uki_create.fish
Exec = /usr/local/bin/snap-pac-uki snapshots

View File

@ -6,6 +6,6 @@ Type = Package
Target = *
[Action]
Description = Performing UKI cleanup...
Description = Performing current UKI creation...
When = PostTransaction
Exec = /usr/share/libalpm/scripts/snap-pac-uki_cleanup.fish
Exec = /usr/local/bin/snap-pac-uki current

View File

@ -1,17 +0,0 @@
#!/bin/fish
# get snapshots
set -l config "root"
set -l snapshot_ids (snapper -c $config --jsonout ls | jq ".$config | .[] | .number")
# get current ukis
set -l ukis (ls /boot/EFI/Linux/ | grep '^snap-pac-uki-[1-9][0-9]*\.efi$' | grep -o "[1-9][0-9]*")
for x in $ukis
if not contains $x $snapshot_ids
set -l path "/boot/EFI/Linux/snap-pac-uki-$x.efi"
rm $path
echo "==> removed UKI: $path"
end
end

View File

@ -1,46 +0,0 @@
#!/bin/fish
# get id and type of last snapshot
set -l config "root"
set -l snapshot (snapper -c $config --jsonout ls | jq -c ".$config | last")
set -l id (echo $snapshot | jq ".number")
if not contains (echo $snapshot | jq -r ".type") "pre" "post"
echo "==> last snapshot type is neither 'pre' nor 'post'; no UKI creation"
exit
end
# check if UKI already exists
set -l ukis (ls /boot/EFI/Linux/ | grep '^snap-pac-uki-[1-9][0-9]*\.efi$' | grep -o "[1-9][0-9]*")
if contains $id $ukis
echo "==> UKI already exists; no UKI creation"
exit
end
# prepare uki creation
set -l buildpath "/tmp/snap-pac-uki/$id"
mkdir -p $buildpath
rm -rf "$buildpath/*"
cd $buildpath
sed "s/BUILD_ID=.*/BUILD_ID=\"Snapshot $id\"/" </usr/lib/os-release >./os-release
grep options /boot/loader/entries/00-arch.conf | \
string sub -s 9 | \
sed ':a;N;$!ba;s/\n/ /g' | \
sed "s/subvol=@/subvol=@snapshots\\/$id\\/snapshot/" >./kernel-parameters
# create uki
set -l ukipath "/boot/EFI/Linux/snap-pac-uki-$id.efi"
objcopy \
--add-section .osrel="./os-release" --change-section-vma .osrel=0x20000 \
--add-section .cmdline="./kernel-parameters" --change-section-vma .cmdline=0x30000 \
--add-section .splash="/usr/share/systemd/bootctl/splash-arch.bmp" --change-section-vma .splash=0x40000 \
--add-section .linux="/boot/vmlinuz-linux" --change-section-vma .linux=0x2000000 \
--add-section .initrd="/boot/initramfs-linux.img" --change-section-vma .initrd=0x3000000 \
"/usr/lib/systemd/boot/efi/linuxx64.efi.stub" $ukipath
echo "==> created UKI: $ukipath"

41
src/config.fish Executable file
View File

@ -0,0 +1,41 @@
function config -d "sets some configuration variables"
# If this is not set to "yes", snap-pac-uki will not do anything when
# executed.
#
# This is to make sure that this program can be installed with pacman. If
# this wasn't done, pacman would install this program with an default
# config, which most likely wouldn't work correctly. Therefore this needs
# to be set to "yes" by the user, once they've configured snap-pac-uki.
set -g CFG_ENABLED "no"
# A directory where ukis can be built.
#
# It is recommended to keep this within /tmp, as the generated data will not
# be needed later.
# It is recommended to not set this to a path with data inside. This program
# will sometimes remove all content of specific subfolders.
set -g CFG_BUILD_DIR "/tmp/snap-pac-uki"
# The subvolume to use on the kernel command line when generating current
# images.
set -g CFG_DEFAULT_SUBVOL "@"
# The linux fs path containing all snapshot data
set -g CFG_SNAPSHOT_PATH "/.snapshots"
# The name of the subvolume containing all snapshots
set -g CFG_SNAPSHOT_SUBVOL "@snapshots"
# The directory to put uki files in
set -g CFG_UKI_DIR "/efi/EFI/Linux"
# Whether generated uki files should be signed
set -g CFG_SECUREBOOT "yes"
# The path to the .key file with which to sign uki files
set -g CFG_SECUREBOOT_KEY "/etc/secureboot/keys/db/db.key"
# The path to the .crt file with which to sign uki files
set -g CFG_SECUREBOOT_CERT "/etc/secureboot/keys/db/db.crt"
end

176
src/snap-pac-uki.fish Executable file
View File

@ -0,0 +1,176 @@
#!/bin/fish
function error -d "prints an error and exits" -a error
echo $error 1>&2
exit 1
end
function check_deps -d "checks if all dependencies are available"
set -l dependencies \
"rg" \
"sed" \
for dep in $dependencies
which $dep >/dev/null 2>/dev/null
if test $status != "0"
error "missing depenency: $dep"
end
end
which "sbsign" >/dev/null 2>/dev/null
if test $status != "0" -a $CFG_SECUREBOOT = "yes"
set -g CFG_SECUREBOOT "no"
echo "CFG_SECUREBOOT overwriten to \"no\": 'sbsign' not available"
end
end
function find_ukiless_snapshots -d "find"
set -ge TASK_UKI
for id in (ls $CFG_SNAPSHOT_PATH)
if test $id = "0"
continue
end
if test -f "$CFG_UKI_DIR/arch-linux-$id.efi"
continue
end
set -ga TASK_UKI $id
end
end
function create_uki -d "creates a new uki" -a variant id
switch $variant
case "rolling"
set bd "$CFG_BUILD_DIR/current"
set current "-current"
case "fallback"
set bd "$CFG_BUILD_DIR/current"
set fallback "-fallback"
case "snapshot"
set bd "$CFG_BUILD_DIR/$id"
set snid "-$id"
set prefix "$CFG_SNAPSHOT_PATH/$id/snapshot"
case "*"
error "unknown uki variant: $variant"
end
# create initrd
set initrd "$bd/initramfs-linux$fallback.img"
cat "$prefix/boot/initramfs-linux$fallback.img" >> $initrd
# create uki
objcopy \
--add-section .osrel="$bd/os-release$fallback" --change-section-vma .osrel=0x20000 \
--add-section .cmdline="$bd/kernel-cmd$current$fallback" --change-section-vma .cmdline=0x30000 \
--add-section .splash="$prefix/usr/share/systemd/bootctl/splash-arch.bmp" --change-section-vma .splash=0x40000 \
--add-section .linux="$prefix/boot/vmlinuz-linux" --change-section-vma .linux=0x2000000 \
--add-section .initrd="$initrd" --change-section-vma .initrd=0x3000000 \
"$prefix/usr/lib/systemd/boot/efi/linuxx64.efi.stub" "$bd/arch-linux$fallback$snid.efi.unsigned"
# sign
if test "$CFG_SECUREBOOT" = "yes"
sbsign --key $CFG_SECUREBOOT_KEY \
--cert $CFG_SECUREBOOT_CERT \
--output "$bd/arch-linux$fallback$snid.efi" \
"$bd/arch-linux$fallback$snid.efi.unsigned"
else # otherwise move unsigned efi to location
mv "$bd/arch-linux$fallback$snid.efi.unsigned" "$bd/arch-linux$fallback$snid.efi"
end
end
function create_snapshot_uki -d "creates an uki for a snapshot" -a id
# create build directory
set bd "$CFG_BUILD_DIR/$id"
if test -f $bd
error "build dir for snapshot $id is a file"
else if test -d $bd
rm -rf "$bd/*"
else
mkdir -p $bd
end
# prepare files
sed "s/BUILD_ID=.*/BUILD_ID=\"snapshot $id\"/;s/PRETTY_NAME=\"\(.*\)\"/PRETTY_NAME=\"\1 (Snapshot $id)\"/" <"$CFG_SNAPSHOT_PATH/$id/snapshot/usr/lib/os-release" >"$bd/os-release"
sed "s/SNAPSHOT/$CFG_SNAPSHOT_SUBVOL\/$id\/snapshot/" <"$CFG_SNAPSHOT_PATH/$id/snapshot/etc/snap-pac-uki/kernel-cmd" >"$bd/kernel-cmd"
# create uki
create_uki snapshot $id
end
function create_current_uki -d "creates an uki for the current system"
# create build directory
set bd "$CFG_BUILD_DIR/current"
if test -f $bd
error "build dir for snapshot $id is a file"
else if test -d $bd
rm -rf "$bd/*"
else
mkdir -p $bd
end
# create default
cp "/usr/lib/os-release" "$bd/os-release"
sed "s/SNAPSHOT/$CFG_DEFAULT_SUBVOL/" <"/etc/snap-pac-uki/kernel-cmd-current" >"$bd/kernel-cmd-current"
create_uki rolling
# create fallbac
sed "s/BUILD_ID=.*/BUILD_ID=fallback/;s/PRETTY_NAME=\"\(.*\)\"/PRETTY_NAME=\"\1 (Fallback)\"/" <"/usr/lib/os-release" >"$bd/os-release-fallback"
sed "s/SNAPSHOT/$CFG_DEFAULT_SUBVOL/" <"/etc/snap-pac-uki/kernel-cmd-fallback" >"$bd/kernel-cmd-fallback"
create_uki fallback
end
function main
# prepare for execution
source "/etc/snap-pac-uki/config.fish"
config
if test "$CFG_ENABLED" != "yes"
echo "==> snap-pac-uki not enabled in config file" 1>&2
exit 0
end
check_deps
switch $argv[1]
case "snapshots"
find_ukiless_snapshots
for id in $TASK_UKI
echo "==> found snapshot without uki: $id"
create_snapshot_uki $id
echo " -> created uki for snapshot $id"
cp "$CFG_BUILD_DIR/$id/arch-linux-$id.efi" "$CFG_UKI_DIR"
echo " -> copied uki for snapshot $id into uki directory"
end
case "current"
echo "==> creating current ukis"
create_current_uki
echo " -> created ukis"
cp "$CFG_BUILD_DIR/current/arch-linux.efi" "$CFG_UKI_DIR"
cp "$CFG_BUILD_DIR/current/arch-linux-fallback.efi" "$CFG_UKI_DIR"
echo " -> copied ukis into uki directory"
case "clean"
echo "==> cleaning ukis"
set ids (ls $CFG_SNAPSHOT_PATH)
for uki in (ls "$CFG_UKI_DIR" | rg "[0-9]" | sed "s/arch-linux-\([0-9]*\).efi/\1/")
if not contains "$uki" $ids
echo " -> cleaning uki and boot entry for removed snapshot $uki"
rm "$CFG_UKI_DIR/arch-linux-$uki.efi"
end
end
case "*"
error "unknown command"
end
end
main $argv

View File

@ -0,0 +1,6 @@
[Unit]
Description=cleaning up ukis
[Service]
Type=oneshot
ExecStart=/usr/local/bin/snap-pac-uki clean

View File

@ -0,0 +1,9 @@
[Unit]
Description="timer for triggering uki cleanup
[Timer]
OnBootSec=15min
OnUnitActiveSec=1d
[Install]
WantedBy=timers.target

View File

@ -1,9 +1,14 @@
#!/bin/fish
for x in (ls ./hooks)
rm "/usr/share/libalpm/hooks/$x"
end
rm /usr/local/bin/snap-pac-uki
echo "removed script."
for x in (ls ./scripts)
rm "/usr/share/libalpm/scripts/$x"
for name in (ls ./pacman)
rm /usr/share/libalpm/hooks/$name
end
echo "removed pacman hooks."
for name in (ls ./systemd)
rm /etc/systemd/system/$name
end
echo "removed systemd unit files"