Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add mkinitcpio tools for zfs #16882

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 61 additions & 0 deletions contrib/mkinitcpio/parse-cmdline
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
#!/usr/bin/awk -f
# input variable:
# command: "mode" or "pool" or "dataset" (default to "mode" when non is given)
#
# mode checks the boot mode, returns "dataset", "import", "import_all" or "none"
# dataset: use a particular dataset
# import: import a specific pool, use bootfs to determine the dataset
# import_all: import all pools, use the first one with a bootfs property
# none: not using zfs for root
# pool checks the root pool, returns the pool name (used with mode "dataset" and "import")
# dataset checks the root dataset, returns the dataset name (used with mode "dataset")
{
split($0, args, " ")

for (i in args) {
if (match(args[i], /^root=/)) {
root = substr(args[i], 6)
}
}

if (root == "zfs") {
# import all pools
if (command == "pool" || command == "dataset") {
print "using auto detection for pool and dataset" > "/dev/stderr"
exit 1
} else {
printf "import_all"
}
} else if (match(root, /^zfs:[^\/]+$/)) {
# import a specific pool
if (command == "dataset") {
print "using auto detection for dataset" > "/dev/stderr"
exit 1
} else if (command == "pool") {
printf "%s", substr(root, 5)
} else {
printf "import"
}
} else if (match(root, /^zfs:[^\/]+(\/[^\/]+)+$/)) {
# use a particular dataset
dSet = substr(root, 5)
paths = split(dSet, p, "/")
pool = p[1]
if (command == "pool") {
printf "%s", pool
} else if (command == "dataset") {
printf "%s", dSet
} else {
printf "dataset"
}
} else {
if (command == "pool" || command == "dataset") {
print "not using zfs for root" > "/dev/stderr"
exit 1
} else {
printf "none"
}
}

exit 0
}
72 changes: 72 additions & 0 deletions contrib/mkinitcpio/sd-zfs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
#!/usr/bin/bash

true || source /usr/lib/initcpio/functions
true || source /usr/lib/initcpio/install/systemd

build() {
map add_module \
zfs \
spl

map add_file \
/usr/lib/udev/rules.d/60-zvol.rules \
/usr/lib/udev/rules.d/69-vdev.rules \
/usr/lib/udev/rules.d/90-zfs.rules

map add_binary \
mount.zfs \
zfs \
zpool

add_systemd_unit "systemd-udev-settle.service"

{
copied_files=(
/etc/hostid
)

for f in "${copied_files[@]}"; do
add_file "$f"
done

# Include hostid when it's not written to file
if [[ ! -f /etc/hostid ]]; then
AA="$(hostid | cut -b 1,2)"
BB="$(hostid | cut -b 3,4)"
CC="$(hostid | cut -b 5,6)"
DD="$(hostid | cut -b 7,8)"
printf "\x${DD}\x${CC}\x${BB}\x${AA}" > "${BUILDROOT}/etc/hostid"
fi
}

add_binary /usr/lib/systemd/system-generators/systemd-debug-generator

# generator and its dependencies
add_binary /usr/lib/zfs/initcpio/zfs-root-generator /usr/lib/systemd/system-generators/
add_binary /usr/lib/zfs/initcpio/zfs-set-env /usr/bin/
add_binary /usr/lib/zfs/initcpio/parse-cmdline /usr/bin/

add_binary /usr/lib/initcpio/busybox /usr/bin/
for applet in $(/usr/lib/initcpio/busybox --list); do
add_symlink "/usr/bin/$applet" busybox
done
}

help() {
cat << EOF
This hook adds ZFS support for systemd-based initrd.
It has a hard dependency on the systemd hook. Since it's implemented
using shell scripts, it still requires busybox and will copy it to
the initrd if you don't have the base hook.

To use this hook, simply add it to your HOOKS array in mkinitcpio.conf.
You'll also need to change the kernel command line. The supported cmdline
formats are:
1. root=zfs, which imports all pools in initrd, searches for the
first pool with the bootfs property set, and then mounts bootfs as root.
2. root=zfs:poolname, which imports only the specified pool and then mounts
the pool's bootfs as root.
3. root=zfs:poolname/dataset, which imports only the specified pool and then
mounts the specified dataset as root.
EOF
}
125 changes: 125 additions & 0 deletions contrib/mkinitcpio/zfs-root-generator
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
#!/usr/bin/sh
# shellcheck shell=ash
# cmdline format:
#
# use a particular dataset:
# root=zfs:pool/dataset
# use the bootfs property of a pool:
# root=zfs:pool
# import all pools and use the first one with a bootfs property:
# root=zfs
set -e

# usage: klog "message" [level]
klog() {
if [ -n "$DEBUG_CMDLINE" ]; then
return
fi
echo "<${2:-6}>zfs-root-generator: $1" | tee /dev/kmsg
}

write_sysroot_override_unit() {
klog "writing sysroot.mount override"
mkdir -p "$GENERATOR_DIR"/sysroot.mount.d
{
echo "[Unit]"
echo "After=zfs-import.target"
echo
echo "[Mount]"
echo "PassEnvironment=ROOT_DATASET"
echo "Type=zfs"
echo 'What=${ROOT_DATASET}'
echo 'Options=zfsutil'
} > "$GENERATOR_DIR"/sysroot.mount.d/zfs-override.conf
}

write_set_env_unit() {
klog "generating zfs-set-env.service"
{
echo "[Unit]"
echo "Description=Set systemd environment variables for ZFS sysroot.mount unit"
echo "DefaultDependencies=no"
echo "Before=sysroot.mount"
echo
echo "[Service]"
echo "Type=oneshot"
echo "RemainAfterExit=yes"
echo "ExecStart=/usr/bin/zfs-set-env"
} > "$GENERATOR_DIR"/zfs-set-env.service
}

enable_unit() {
local unit="$1"
klog "enabling $unit"
mkdir -p "$GENERATOR_DIR"/initrd-root-device.target.wants/
ln -fs "$GENERATOR_DIR"/"$unit" "$GENERATOR_DIR"/initrd-root-device.target.wants/
}

GENERATOR_DIR="$1"
[ -n "$GENERATOR_DIR" ] || {
klog "impossible: no generator directory specified" 2
exit 1
}
CMDLINE=${DEBUG_CMDLINE:-/proc/cmdline}

mode=$(parse-cmdline "$CMDLINE")
klog "mode: $mode"

case $mode in
import_all)
write_sysroot_override_unit

write_set_env_unit
enable_unit zfs-set-env.service

klog "generating zfs-import-all.service"
{
echo "[Unit]"
echo "Description=Import all ZFS pools"
echo "DefaultDependencies=no"
echo "Wants=systemd-udev-settle.service"
echo "After=systemd-udev-settle.service cryptsetup.target"
echo "Before=sysroot.mount zfs-set-env.service"
echo
echo "[Service]"
echo "Type=oneshot"
echo "RemainAfterExit=yes"
echo "ExecStart=/usr/bin/zpool import -o cachefile=none -aN"
} > "$GENERATOR_DIR"/zfs-import-all.service
enable_unit zfs-import-all.service
;;
import|dataset)
write_sysroot_override_unit

write_set_env_unit
enable_unit zfs-set-env.service

pool=$(parse-cmdline -v command=pool "$CMDLINE")
klog "generating zfs-import-root-pool.service for pool $pool"
{
echo "[Unit]"
echo "Description=Import ZFS pool $pool for rootfs"
echo "DefaultDependencies=no"
echo "Requires=systemd-udev-settle.service"
echo "After=systemd-udev-settle.service cryptsetup.target"
echo "Before=sysroot.mount zfs-set-env.service"
echo
echo "[Service]"
echo "Type=oneshot"
echo "RemainAfterExit=yes"
echo "ExecStart=/usr/bin/zpool import -o cachefile=none -N \"$pool\""
} > "$GENERATOR_DIR"/zfs-import-root-pool.service
enable_unit zfs-import-root-pool.service
;;
none)
klog "not using zfs for root: stopping"
exit 0
;;
*)
klog "impossible: unknown mode $mode" 2
exit 1
;;
esac

klog "finished generating"
exit 0
43 changes: 43 additions & 0 deletions contrib/mkinitcpio/zfs-set-env
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#!/usr/bin/sh
# shellcheck shell=ash
set -e

CMDLINE=${DEBUG_CMDLINE:-/proc/cmdline}

# usage: log "message" [level]
log() {
echo "<${2:-6}>$1"
}

mode=$(parse-cmdline "$CMDLINE")
log "mode: $mode"

case $mode in
import_all)
if dSet=$(zpool list -Ho bootfs | grep -m1 -vFx -); then
:
else
log "no pool with bootfs property found" 2
exit 1
fi
;;
import)
pool=$(parse-cmdline -v command=pool "$CMDLINE")
if dSet=$(zpool list -Ho bootfs "$pool" | grep -m1 -vFx -); then
:
else
log "no bootfs property found for pool $pool" 2
exit 1
fi
;;
dataset)
dSet=$(parse-cmdline -v command=dataset "$CMDLINE")
;;
none)
log "no zfs root specified" 2
exit 1
;;
esac

log "setting ROOT_DATASET to $dSet"
exec systemctl set-environment ROOT_DATASET="$dSet"
Loading