310 lines
8.6 KiB
Bash
Executable File
310 lines
8.6 KiB
Bash
Executable File
#!/bin/bash -e
|
|
|
|
# deb-ostree-builder - Build bootable Debian OSTree commits
|
|
#
|
|
# Copyright (C) 2017 Dan Nicholson <nicholson@endlessm.com>
|
|
#
|
|
# This program is free software; you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation; either version 2 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License along
|
|
# with this program; if not, write to the Free Software Foundation, Inc.,
|
|
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
|
|
PROG=$(readlink -f "$0")
|
|
PROGDIR=$(dirname "$PROG")
|
|
|
|
# Defaults
|
|
ARCH=$(dpkg --print-architecture)
|
|
BUILDDIR=
|
|
GPG_SIGN=()
|
|
GPG_HOMEDIR=
|
|
|
|
usage() {
|
|
cat <<EOF
|
|
Usage: $0 [OPTION...] CONFIG SUITE REPO
|
|
|
|
-a, --arch build architecture
|
|
-d, --dir build directory
|
|
--gpg-sign GPG key ID to use for signing
|
|
--gpg-homedir GPG homedir to find keys
|
|
-h, --help show this message and exit
|
|
|
|
deb-ostree-builder constructs a Debian OS for use as a bootable
|
|
OSTree. It uses debootstrap to construct the OS, adjusts it to be
|
|
compatible with OSTree, and then commits it to a repository.
|
|
|
|
CONFIG is a multistrap configuration file. SUITE is the distribution
|
|
codename. This is used for the OSTree ref and must match the
|
|
multistrap configuration. REPO is the path the the OSTree repository
|
|
where the commit will be made.
|
|
EOF
|
|
}
|
|
|
|
ARGS=$(getopt -n "$0" \
|
|
-o a:d:h \
|
|
-l arch:,dir:,gpg-sign:,gpg-homedir:,help \
|
|
-- "$@")
|
|
eval set -- "$ARGS"
|
|
|
|
while true; do
|
|
case "$1" in
|
|
-a|--arch)
|
|
ARCH=$2
|
|
shift 2
|
|
;;
|
|
-d|--dir)
|
|
BUILDDIR=$2
|
|
shift 2
|
|
;;
|
|
--gpg-sign)
|
|
GPG_SIGN+=($2)
|
|
shift 2
|
|
;;
|
|
--gpg-homedir)
|
|
GPG_HOMEDIR=$2
|
|
shift 2
|
|
;;
|
|
-h|--help)
|
|
usage
|
|
exit 0
|
|
;;
|
|
--)
|
|
shift
|
|
break
|
|
;;
|
|
esac
|
|
done
|
|
|
|
if [ $# -lt 3 ]; then
|
|
echo "Must specify CONFIG, SUITE and REPO" >&2
|
|
exit 1
|
|
fi
|
|
|
|
CONFIG=$1
|
|
SUITE=$2
|
|
REPO=$3
|
|
|
|
# Mount cleanup handler
|
|
DEVICES_MOUNTED=false
|
|
cleanup_mounts()
|
|
{
|
|
if $DEVICES_MOUNTED; then
|
|
echo "Unmounting filesystems in $BUILDDIR"
|
|
for dir in dev/pts dev sys proc; do
|
|
umount "$BUILDDIR/$dir"
|
|
done
|
|
DEVICES_MOUNTED=false
|
|
fi
|
|
}
|
|
|
|
# Exit handler
|
|
TMP_BUILDDIR=
|
|
cleanup()
|
|
{
|
|
cleanup_mounts || true
|
|
if [ -n "$TMP_BUILDDIR" ]; then
|
|
rm -rf "$TMP_BUILDDIR"
|
|
fi
|
|
}
|
|
trap cleanup EXIT
|
|
|
|
if [ -n "$BUILDDIR" ]; then
|
|
# Create specified build directory
|
|
echo "Creating $BUILDDIR"
|
|
mkdir -p "$BUILDDIR"
|
|
else
|
|
# Create a temporary build directory in /var/tmp since it could be
|
|
# fairly large
|
|
TMP_BUILDDIR=$(mktemp -d -p /var/tmp deb-ostree-builder-XXXXXXXX)
|
|
BUILDDIR=$TMP_BUILDDIR
|
|
echo "Using temporary directory $BUILDDIR for build"
|
|
fi
|
|
|
|
# Ensure that dracut makes generic initramfs instead of looking just
|
|
# at the host configuration. This is also in the dracut-config-generic
|
|
# package, but that only gets installed after dracut makes the first
|
|
# initramfs.
|
|
echo "Configuring dracut for generic initramfs"
|
|
mkdir -p "$BUILDDIR"/etc/dracut.conf.d
|
|
cat > "$BUILDDIR"/etc/dracut.conf.d/90-deb-ostree.conf <<EOF
|
|
# Don't make host-specific initramfs
|
|
hostonly=no
|
|
EOF
|
|
|
|
# Define a temporary policy-rc.d that ensures that no daemons are
|
|
# launched from the installation.
|
|
mkdir -p "$BUILDDIR"/usr/sbin
|
|
cat > "$BUILDDIR"/usr/sbin/policy-rc.d <<EOF
|
|
#!/bin/sh
|
|
exit 101
|
|
EOF
|
|
chmod +x "$BUILDDIR"/usr/sbin/policy-rc.d
|
|
|
|
# Mount common kernel filesystems. dracut expects /dev to be mounted.
|
|
echo "Mounting filesystems in $BUILDDIR"
|
|
DEVICES_MOUNTED=true
|
|
for dir in proc sys dev dev/pts; do
|
|
mkdir -p "$BUILDDIR/$dir"
|
|
mount --bind "/$dir" "$BUILDDIR/$dir"
|
|
done
|
|
|
|
# Build with multistrap
|
|
echo "Building system with multistrap in $BUILDDIR"
|
|
multistrap -f "$CONFIG" -d "$BUILDDIR"
|
|
|
|
# All done with filesystems
|
|
cleanup_mounts
|
|
|
|
# Remove temporary policy-rc.d
|
|
rm -f "$BUILDDIR"/usr/sbin/policy-rc.d
|
|
|
|
# Cleanup cruft
|
|
echo "Preparing system for OSTree"
|
|
rm -rf \
|
|
"$BUILDDIR"/boot/*.bak \
|
|
"$BUILDDIR"/etc/apt/sources.list~ \
|
|
"$BUILDDIR"/etc/apt/trusted.gpg~ \
|
|
"$BUILDDIR"/etc/{passwd,group,shadow,gshadow}- \
|
|
"$BUILDDIR"/var/cache/debconf/*-old \
|
|
"$BUILDDIR"/var/lib/dpkg/*-old \
|
|
"$BUILDDIR"/boot/{initrd.img,vmlinuz} \
|
|
"$BUILDDIR"/{initrd.img,vmlinuz}{,.old}
|
|
|
|
# Remove dbus machine ID cache (makes each system unique)
|
|
rm -f "$BUILDDIR"/var/lib/dbus/machine-id "$BUILDDIR"/etc/machine-id
|
|
|
|
# Remove resolv.conf copied from the host by debootstrap. The settings
|
|
# are only valid on the target host and will be populated at runtime.
|
|
rm -f "$BUILDDIR"/etc/resolv.conf
|
|
|
|
# Remove temporary files
|
|
rm -rf "$BUILDDIR"/var/cache/man/*
|
|
rm -rf "$BUILDDIR"/tmp "$BUILDDIR"/var/tmp
|
|
mkdir -p "$BUILDDIR"/tmp "$BUILDDIR"/var/tmp
|
|
chmod 1777 "$BUILDDIR"/tmp "$BUILDDIR"/var/tmp
|
|
|
|
# OSTree uses a single checksum of the combined kernel and initramfs
|
|
# to manage boot. Determine the checksum and rename the files the way
|
|
# OSTree expects.
|
|
echo "Renaming kernel and initramfs per OSTree requirements"
|
|
pushd "$BUILDDIR"/boot >/dev/null
|
|
|
|
vmlinuz_match=(vmlinuz*)
|
|
vmlinuz_file=${vmlinuz_match[0]}
|
|
initrd_match=(initrd.img* initramfs*)
|
|
initrd_file=${initrd_match[0]}
|
|
|
|
csum=$(cat ${vmlinuz_file} ${initrd_file} | \
|
|
sha256sum --binary | \
|
|
awk '{print $1}')
|
|
echo "OSTree boot checksum: ${csum}"
|
|
|
|
mv ${vmlinuz_file} ${vmlinuz_file}-${csum}
|
|
mv ${initrd_file} ${initrd_file/initrd.img/initramfs}-${csum}
|
|
|
|
popd >/dev/null
|
|
|
|
# OSTree only commits files or symlinks
|
|
rm -rf "$BUILDDIR"/dev
|
|
mkdir -p "$BUILDDIR"/dev
|
|
|
|
# Fixup home directory base paths for OSTree
|
|
sed -i -e 's|DHOME=/home|DHOME=/sysroot/home|g' \
|
|
"${BUILDDIR}"/etc/adduser.conf
|
|
sed -i -e 's|# HOME=/home|HOME=/sysroot/home|g' \
|
|
"${BUILDDIR}"/etc/default/useradd
|
|
|
|
# Move /etc to /usr/etc.
|
|
#
|
|
# FIXME: Need to handle passwd and group to be updatable. This can be
|
|
# done with libnss-altfiles, though that has other drawbacks.
|
|
if [ -d "${BUILDDIR}"/usr/etc ]; then
|
|
echo "ERROR: Non-empty /usr/etc found!" >&2
|
|
ls -lR "${BUILDDIR}"/usr/etc
|
|
exit 1
|
|
fi
|
|
mv "${BUILDDIR}"/etc "${BUILDDIR}"/usr
|
|
|
|
# Move dpkg database to /usr so it's accessible after the OS /var is
|
|
# mounted, but make a symlink so it works without modifications to dpkg
|
|
# or apt
|
|
mkdir -p "${BUILDDIR}"/usr/share/dpkg
|
|
if [ -e "${BUILDDIR}"/usr/share/dpkg/database ]; then
|
|
echo "ERROR: /usr/share/dpkg/database already exists!" >&2
|
|
ls -lR "${BUILDDIR}"/usr/share/dpkg/database >&2
|
|
exit 1
|
|
fi
|
|
mv "${BUILDDIR}"/var/lib/dpkg "${BUILDDIR}"/usr/share/dpkg/database
|
|
ln -sr "${BUILDDIR}"/usr/share/dpkg/database \
|
|
"${BUILDDIR}"/var/lib/dpkg
|
|
|
|
# tmpfiles.d setup to make the ostree root compatible with persistent
|
|
# directories in the sysroot.
|
|
cat > "${BUILDDIR}"/usr/lib/tmpfiles.d/ostree.conf <<EOF
|
|
d /sysroot/home 0755 root root -
|
|
d /sysroot/root 0700 root root -
|
|
d /var/opt 0755 root root -
|
|
d /var/local 0755 root root -
|
|
d /run/media 0755 root root -
|
|
L /var/lib/dpkg - - - - ../../usr/share/dpkg/database
|
|
EOF
|
|
|
|
# Create symlinks in the ostree for persistent directories.
|
|
mkdir -p "${BUILDDIR}"/sysroot
|
|
rm -rf "${BUILDDIR}"/{home,root,media,opt} "${BUILDDIR}"/usr/local
|
|
ln -s /sysroot/ostree "${BUILDDIR}"/ostree
|
|
ln -s /sysroot/home "${BUILDDIR}"/home
|
|
ln -s /sysroot/root "${BUILDDIR}"/root
|
|
ln -s /var/opt "${BUILDDIR}"/opt
|
|
ln -s /var/local "${BUILDDIR}"/usr/local
|
|
ln -s /run/media "${BUILDDIR}"/media
|
|
|
|
# Now ready to commit. Make the repo if necessary. An archive-z2 repo
|
|
# is used since the intention is to use this repo to serve updates
|
|
# from.
|
|
mkdir -p "$REPO"
|
|
if [ ! -f "$REPO"/config ]; then
|
|
echo "Initialiazing OSTree repo $REPO"
|
|
ostree --repo="$REPO" init --mode=archive-z2
|
|
fi
|
|
|
|
# Make the commit. The ostree ref is flatpak style.
|
|
branch="os/debian/$ARCH/$SUITE"
|
|
COMMIT_OPTS=(
|
|
--repo="$REPO"
|
|
--branch="$branch"
|
|
--subject="Build $SUITE $ARCH $(date --iso-8601=seconds)"
|
|
--skip-if-unchanged
|
|
--table-output
|
|
)
|
|
for id in ${GPG_SIGN[@]}; do
|
|
COMMIT_OPTS+=(--gpg-sign="$id")
|
|
done
|
|
if [ -n "$GPG_HOMEDIR" ]; then
|
|
COMMIT_OPTS+=(--gpg-homedir="$GPG_HOMEDIR")
|
|
fi
|
|
echo "Committing $BUILDDIR to $REPO branch $branch"
|
|
ostree commit "${COMMIT_OPTS[@]}" "$BUILDDIR"
|
|
|
|
# Update the repo summary
|
|
SUMMARY_OPTS=(
|
|
--repo="$REPO"
|
|
--update
|
|
)
|
|
for id in ${GPG_SIGN[@]}; do
|
|
SUMMARY_OPTS+=(--gpg-sign="$id")
|
|
done
|
|
if [ -n "$GPG_HOMEDIR" ]; then
|
|
SUMMARY_OPTS+=(--gpg-homedir="$GPG_HOMEDIR")
|
|
fi
|
|
echo "Updating $REPO summary file"
|
|
ostree summary "${SUMMARY_OPTS[@]}"
|