232 lines
5.9 KiB
Bash
Executable File
232 lines
5.9 KiB
Bash
Executable File
#!/bin/bash
|
|
# vim: softtabstop=2 shiftwidth=2 expandtab
|
|
|
|
usage() {
|
|
cat <<-EOF
|
|
USAGE: $0 [OPTIONS] <distro> <pool> [testdir]
|
|
|
|
Install a distribution into the given ZFS pool. If testdir is
|
|
provided and ZFSBootMenu images are built during installation,
|
|
the images will be copied to testdir afterwards.
|
|
|
|
OPTIONS
|
|
-h
|
|
Display this message and exit
|
|
|
|
-c <cachedir>
|
|
If possible, use the given installation cache directory
|
|
(This can also be set as CACHEDIR in the environment)
|
|
|
|
-e <keyfile>
|
|
If the pool is encrypted, use the given keyfile to unlock it
|
|
(This can also be set as ENCRYPT_KEYFILE in the environment)
|
|
EOF
|
|
}
|
|
|
|
cleanup() {
|
|
if [ -n "${CHROOT_MNT}" ]; then
|
|
echo "Cleaning up chroot mount '${CHROOT_MNT}'"
|
|
mountpoint -q "${CHROOT_MNT}" && umount -R "${CHROOT_MNT}"
|
|
[ -d "${CHROOT_MNT}" ] && rmdir "${CHROOT_MNT}"
|
|
unset CHROOT_MNT
|
|
fi
|
|
|
|
if [ -n "${ZBM_POOL}" ]; then
|
|
echo "Exporting pool '${ZBM_POOL}'"
|
|
zpool export "${ZBM_POOL}"
|
|
unset ZBM_POOL
|
|
fi
|
|
|
|
exit
|
|
}
|
|
|
|
error() {
|
|
echo "ERROR: $*" >&2
|
|
}
|
|
|
|
while getopts "hc:e:" opt; do
|
|
case "${opt}" in
|
|
c)
|
|
CACHEDIR="${OPTARG}"
|
|
;;
|
|
e)
|
|
ENCRYPT_KEYFILE="${OPTARG}"
|
|
;;
|
|
h)
|
|
usage
|
|
exit 0
|
|
;;
|
|
*)
|
|
usage
|
|
exit 1
|
|
;;
|
|
esac
|
|
done
|
|
|
|
shift "$((OPTIND - 1))"
|
|
|
|
DISTRO="${1?ERROR: a distribution name is required}"
|
|
zpool_name="${2?ERROR: a pool name is required}"
|
|
|
|
TESTDIR="${3}"
|
|
|
|
INSTALL_SCRIPT="./helpers/install-${DISTRO}.sh"
|
|
if [ ! -x "${INSTALL_SCRIPT}" ]; then
|
|
error "install script '${INSTALL_SCRIPT}' missing or not executable"
|
|
exit 1
|
|
fi
|
|
|
|
CHROOT_SCRIPT="./helpers/chroot-${DISTRO}.sh"
|
|
if [ ! -x "${CHROOT_SCRIPT}" ]; then
|
|
error "chroot script '${CHROOT_SCRIPT}' missing or not executable"
|
|
exit 1
|
|
fi
|
|
|
|
export ZBM_POOL=""
|
|
|
|
CHROOT_MNT="$( mktemp -d )" || exit 1
|
|
export CHROOT_MNT
|
|
|
|
# Perform all necessary cleanup for this script
|
|
trap cleanup EXIT INT TERM
|
|
|
|
# Import the pool at the temporary chroot
|
|
if ! zpool import -o cachefile=none -R "${CHROOT_MNT}" "${zpool_name}"; then
|
|
error "unable to import ZFS pool ${zpool_name}"
|
|
exit 1
|
|
else
|
|
export ZBM_POOL="${zpool_name}"
|
|
fi
|
|
|
|
# The distribution must not exist at this point
|
|
ZBM_ROOT="${ZBM_POOL}/ROOT/${DISTRO}"
|
|
if zfs list -o name -H "${ZBM_ROOT}" >/dev/null 2>&1; then
|
|
error "ZFS filesystem ${ZBM_ROOT} already exists"
|
|
exit 1
|
|
fi
|
|
|
|
# Unlock the pool, if required
|
|
case "$( zfs get -H -o value encryptionroot "${ZBM_POOL}" 2>/dev/null )" in
|
|
"-"|"")
|
|
;;
|
|
*)
|
|
if [ -r "${ENCRYPT_KEYFILE}" ]; then
|
|
zfs load-key -L "file://${ENCRYPT_KEYFILE}" "${ZBM_POOL}"
|
|
else
|
|
zfs load-key -L prompt "${ZBM_POOL}"
|
|
export ENCRYPT_KEYFILE=""
|
|
fi
|
|
esac
|
|
|
|
# Prepare the empty boot environment
|
|
zfs create -o mountpoint=/ -o canmount=noauto "${ZBM_ROOT}"
|
|
zfs snapshot -r "${ZBM_ROOT}@barebe"
|
|
|
|
zfs set org.zfsbootmenu:commandline="rw loglevel=4 console=tty1 console=ttyS0" "${ZBM_ROOT}"
|
|
|
|
zpool set bootfs="${ZBM_ROOT}" "${ZBM_POOL}"
|
|
|
|
if ! zfs mount "${ZBM_ROOT}"; then
|
|
error "unable to mount ${ZBM_ROOT}"
|
|
exit 1
|
|
fi
|
|
|
|
if [ -r "${ENCRYPT_KEYFILE}" ]; then
|
|
# Make sure the ZFS key exists in the BE
|
|
mkdir -p "${CHROOT_MNT}/etc/zfs"
|
|
cp "${ENCRYPT_KEYFILE}" "${CHROOT_MNT}/etc/zfs/"
|
|
|
|
# Set a ZBM key source if one is not already provided
|
|
if [ "$( zfs get -o value -H org.zfsbootmenu:keysource "${ZBM_POOL}" )" = "-" ]; then
|
|
zfs set "org.zfsbootmenu:keysource=${ZBM_ROOT}" "${ZBM_POOL}"
|
|
fi
|
|
fi
|
|
|
|
# Bind-mount any cache directory in the target
|
|
CACHEDIR="$( realpath "${CACHEDIR:-./cache}" )"
|
|
if [ -d "${CACHEDIR}" ]; then
|
|
HOSTCACHE="${CHROOT_MNT}/hostcache"
|
|
mkdir -p "${CACHEDIR}/${DISTRO}" "${HOSTCACHE}"
|
|
|
|
if mount -B "${CACHEDIR}/${DISTRO}" "${HOSTCACHE}"; then
|
|
mount --make-slave "${HOSTCACHE}"
|
|
export CACHEDIR
|
|
else
|
|
echo "WARNING: failed to bind-mount cache directory; ignoring"
|
|
unset CACHEDIR
|
|
fi
|
|
else
|
|
unset CACHEDIR
|
|
fi
|
|
|
|
# Run the initial install
|
|
if ! "${INSTALL_SCRIPT}"; then
|
|
echo "ERROR: install script '${INSTALL_SCRIPT}' failed"
|
|
exit 1
|
|
fi
|
|
|
|
zfs snapshot -r "${ZBM_ROOT}@pre-chroot"
|
|
|
|
# Make sure the chroot script exists
|
|
mkdir -p "${CHROOT_MNT}/root"
|
|
cp "${CHROOT_SCRIPT}" "${CHROOT_MNT}/root/"
|
|
|
|
# Make sure special filesystems are mounted
|
|
mkdir -p "${CHROOT_MNT}"/{proc,sys,dev/pts}
|
|
mount -t proc proc "${CHROOT_MNT}/proc"
|
|
mount -t sysfs sys "${CHROOT_MNT}/sys"
|
|
mount -B /dev "${CHROOT_MNT}/dev" && mount --make-slave "${CHROOT_MNT}/dev"
|
|
mount -t devpts pts "${CHROOT_MNT}/dev/pts"
|
|
|
|
# Make sure the zpool information is cached
|
|
mkdir -p "${CHROOT_MNT}/etc/zfs"
|
|
zpool set cachefile="${CHROOT_MNT}/etc/zfs/zpool.cache" "${ZBM_POOL}"
|
|
|
|
# Set hostname for environment
|
|
echo "${DISTRO}" > "${CHROOT_MNT}/etc/hostname"
|
|
|
|
# Pre-populate SSH keys, if available
|
|
if [ -d "./keys/etc/ssh" ]; then
|
|
cp -R "./keys/etc/ssh" "${CHROOT_MNT}/etc/"
|
|
fi
|
|
|
|
# Pre-populate authorized keys, if available
|
|
if [ -r "./keys/authorized_keys" ]; then
|
|
mkdir -p "${CHROOT_MNT}/root/.ssh"
|
|
chmod 700 "${CHROOT_MNT}/root/.ssh"
|
|
cp "./keys/authorized_keys" "${CHROOT_MNT}/root/.ssh/"
|
|
fi
|
|
|
|
# Launch the chroot script
|
|
if ! chroot "${CHROOT_MNT}" "/root/${CHROOT_SCRIPT##*/}"; then
|
|
echo "ERROR: chroot script '${CHROOT_SCRIPT}' failed"
|
|
exit 1
|
|
fi
|
|
|
|
# Pre-populate the test environment with ZBM from the testbed
|
|
if [ -d "${CHROOT_MNT}/zfsbootmenu" ]; then
|
|
if chroot "${CHROOT_MNT}" /usr/bin/generate-zbm --prefix vmlinuz; then
|
|
for f in vmlinuz-bootmenu initramfs-bootmenu.img vmlinuz.EFI; do
|
|
file="${CHROOT_MNT}/zfsbootmenu/build/${f}"
|
|
[ -f "${file}" ] || continue
|
|
|
|
if [ -d "${TESTDIR}" ]; then
|
|
cp "${file}" "${TESTDIR}/${f}.${DISTRO}"
|
|
chmod 644 "${TESTDIR}/${f}.${DISTRO}"
|
|
|
|
if [ ! -e "${TESTDIR}/${f}" ] || [ -L "${TESTDIR}/${f}" ]; then
|
|
ln -Tsf "${f}.${DISTRO}" "${TESTDIR}/${f}"
|
|
fi
|
|
fi
|
|
done
|
|
fi
|
|
fi
|
|
|
|
zfs snapshot -r "${ZBM_ROOT}@full-setup"
|
|
|
|
touch "${CHROOT_MNT}/root/IN_THE_MATRIX"
|
|
zfs snapshot -r "${ZBM_ROOT}@minor-changes"
|
|
|
|
rm "${CHROOT_MNT}/root/IN_THE_MATRIX"
|
|
rm "${CHROOT_MNT}/root/${CHROOT_SCRIPT##*/}"
|