245 lines
10 KiB
Bash
245 lines
10 KiB
Bash
|
#!/bin/sh
|
||
|
#-
|
||
|
# Copyright (c) 2017 Google
|
||
|
# All rights reserved.
|
||
|
#
|
||
|
# Redistribution and use in source and binary forms, with or without
|
||
|
# modification, are permitted provided that the following conditions
|
||
|
# are met:
|
||
|
# 1. Redistributions of source code must retain the above copyright
|
||
|
# notice, this list of conditions and the following disclaimer.
|
||
|
# 2. Redistributions in binary form must reproduce the above copyright
|
||
|
# notice, this list of conditions and the following disclaimer in the
|
||
|
# documentation and/or other materials provided with the distribution.
|
||
|
#
|
||
|
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||
|
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||
|
# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||
|
# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||
|
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||
|
# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||
|
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||
|
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||
|
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||
|
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||
|
#-
|
||
|
|
||
|
readonly PROGNAME=$(basename "$0")
|
||
|
readonly ARCH=$(uname -m)
|
||
|
readonly REQTOOLS="xbps-install xbps-reconfigure tar xz"
|
||
|
|
||
|
# This source pulls in all the functions from lib.sh. This set of
|
||
|
# functions makes it much easier to work with chroots and abstracts
|
||
|
# away all the problems with running binaries with QEMU.
|
||
|
# shellcheck source=./lib.sh
|
||
|
. ./lib.sh
|
||
|
|
||
|
# Die is a function provided in lib.sh which handles the cleanup of
|
||
|
# the mounts and removal of temporary directories if the running
|
||
|
# program exists unexpectedly.
|
||
|
trap 'die "Interrupted! exiting..."' INT TERM HUP
|
||
|
|
||
|
# Even though we only support really one target for most of these
|
||
|
# architectures this lets us refer to these quickly and easily by
|
||
|
# XBPS_ARCH. This makes it a lot more obvious what is happening later
|
||
|
# in the script, and it makes it easier to consume the contents of
|
||
|
# these down the road in later scripts.
|
||
|
usage() {
|
||
|
cat <<-EOH
|
||
|
Usage: $PROGNAME [options] <platform> <rootfs-tarball>
|
||
|
|
||
|
Generates a platform-specific ROOTFS tarball from a generic Peppermintos ROOTFS
|
||
|
generated by mkrootfs.sh.
|
||
|
|
||
|
Supported platforms: i686, x86_64, GCP,
|
||
|
rpi-armv6l, rpi-armv7l, rpi-aarch64,
|
||
|
pinebookpro, pinephone, rock64, rockpro64
|
||
|
|
||
|
OPTIONS
|
||
|
-b <system-pkg> Set an alternative base-system package (default: base-system)
|
||
|
-c <cachedir> Set the XBPS cache directory (default: ./xbps-cachedir-<arch>)
|
||
|
-C <file> Full path to the XBPS configuration file
|
||
|
-k <cmd> Call '<cmd> <ROOTFSPATH>' after building the ROOTFS
|
||
|
-n Do not compress the image, instead print out the ROOTFS directory
|
||
|
-o <file> Filename to write the PLATFORMFS archive to (default: automatic)
|
||
|
-p "<pkg> ..." Additional packages to install into the ROOTFS
|
||
|
-r <repo> Use this XBPS repository. May be specified multiple times
|
||
|
-x <num> Number of threads to use for image compression (default: dynamic)
|
||
|
-h Show this help and exit
|
||
|
-V Show version and exit
|
||
|
EOH
|
||
|
}
|
||
|
|
||
|
# ########################################
|
||
|
# SCRIPT EXECUTION STARTS HERE
|
||
|
# ########################################
|
||
|
|
||
|
BASEPKG=base-system
|
||
|
COMPRESSION="y"
|
||
|
|
||
|
while getopts "b:p:k:c:C:r:x:o:nhV" opt; do
|
||
|
case $opt in
|
||
|
b) BASEPKG="$OPTARG" ;;
|
||
|
p) EXTRA_PKGS="$OPTARG" ;;
|
||
|
k) POST_CMD="$OPTARG" ;;
|
||
|
c) XBPS_CACHEDIR="--cachedir=$OPTARG" ;;
|
||
|
C) XBPS_CONFFILE="-C $OPTARG" ;;
|
||
|
r) XBPS_REPOSITORY="--repository=$OPTARG $XBPS_REPOSITORY" ;;
|
||
|
x) COMPRESSOR_THREADS="$OPTARG" ;;
|
||
|
o) FILENAME="$OPTARG" ;;
|
||
|
n) COMPRESSION="n" ;;
|
||
|
V) version; exit 0;;
|
||
|
h) usage; exit 0 ;;
|
||
|
*) usage >&2; exit 1 ;;
|
||
|
esac
|
||
|
done
|
||
|
shift $((OPTIND - 1))
|
||
|
PLATFORM="$1"
|
||
|
BASE_TARBALL="$2"
|
||
|
|
||
|
if [ -z "$PLATFORM" ] || [ -z "$BASE_TARBALL" ]; then
|
||
|
usage >&2
|
||
|
exit 1
|
||
|
fi
|
||
|
|
||
|
# This is an aweful hack since the script isn't using privesc
|
||
|
# mechanisms selectively. This is a TODO item.
|
||
|
if [ "$(id -u)" -ne 0 ]; then
|
||
|
die "need root perms to continue, exiting."
|
||
|
fi
|
||
|
|
||
|
# Before going any further, check that the tools that are needed are
|
||
|
# present. If we delayed this we could check for the QEMU binary, but
|
||
|
# its a reasonable tradeoff to just bail out now.
|
||
|
check_tools
|
||
|
|
||
|
# Most platforms have a base system package that includes specific
|
||
|
# packages for bringing up the hardware. In the case of the cloud
|
||
|
# platforms the base package includes the components needed to inject
|
||
|
# SSH keys and user accounts. The base platform packages are always
|
||
|
# noarch though, so we strip off the -musl extention if it was
|
||
|
# provided.
|
||
|
case "$PLATFORM" in
|
||
|
rpi*) PKGS="$BASEPKG rpi-base" ;;
|
||
|
i686*) PKGS="$BASEPKG" ;;
|
||
|
x86_64*) PKGS="$BASEPKG" ;;
|
||
|
GCP*) PKGS="$BASEPKG ${PLATFORM%-*}-base" ;;
|
||
|
pinebookpro*) PKGS="$BASEPKG ${PLATFORM%-*}-base" ;;
|
||
|
pinephone*) PKGS="$BASEPKG ${PLATFORM%-*}-base" ;;
|
||
|
rock64*) PKGS="$BASEPKG ${PLATFORM%-*}-base" ;;
|
||
|
rockpro64*) PKGS="$BASEPKG ${PLATFORM%-*}-base" ;;
|
||
|
*) die "$PROGNAME: invalid platform!";;
|
||
|
esac
|
||
|
|
||
|
# Derive the target architecture using the static map
|
||
|
set_target_arch_from_platform
|
||
|
|
||
|
# And likewise set the cache
|
||
|
set_cachedir
|
||
|
|
||
|
# Append any additional packages if they were requested
|
||
|
if [ -n "$EXTRA_PKGS" ] ; then
|
||
|
PKGS="$PKGS $EXTRA_PKGS"
|
||
|
fi
|
||
|
|
||
|
# We need to operate on a tempdir, if this fails to create, it is
|
||
|
# absolutely crucial to bail out so that we don't hose the system that
|
||
|
# is running the script.
|
||
|
ROOTFS=$(mktemp -d) || die "failed to create tempdir, exiting..."
|
||
|
|
||
|
# Now that we have a directory for the ROOTFS, we can expand the
|
||
|
# existing base filesystem into the directory
|
||
|
if [ ! -e "$BASE_TARBALL" ]; then
|
||
|
die "no valid base tarball given, exiting."
|
||
|
fi
|
||
|
|
||
|
info_msg "Expanding base tarball $BASE_TARBALL into $ROOTFS for $PLATFORM build."
|
||
|
tar xf "$BASE_TARBALL" --xattrs --xattrs-include='*' -C "$ROOTFS"
|
||
|
|
||
|
# This will install, but not configure, the packages specified by
|
||
|
# $PKGS. After this step we will do an xbps-reconfigure -f $PKGS
|
||
|
# under the correct architecture to ensure the system is setup
|
||
|
# correctly.
|
||
|
run_cmd_target "xbps-install -SU $XBPS_CONFFILE $XBPS_CACHEDIR $XBPS_REPOSITORY -r $ROOTFS -y $PKGS"
|
||
|
|
||
|
# Now that the packages are installed, we need to chroot in and
|
||
|
# reconfigure. This needs to be done as the right architecture.
|
||
|
# Since this is the only thing we're doing in the chroot, we clean up
|
||
|
# right after.
|
||
|
run_cmd_chroot "$ROOTFS" "xbps-reconfigure -a"
|
||
|
|
||
|
# Before final cleanup the ROOTFS needs to be checked to make sure it
|
||
|
# contains an initrd and if its a platform with arch 'arm*' it needs
|
||
|
# to also have a uInitrd. For this to work the system needs to have
|
||
|
# the uboot-mkimage package installed. Base system packages that do
|
||
|
# not provide this must provide the uInitrd pre-prepared if they are
|
||
|
# arm based. x86 images will have this built using native dracut
|
||
|
# using post unpacking steps for platforms that consume the x86
|
||
|
# tarballs. This check is very specific and ensures that applicable
|
||
|
# tooling is present before proceeding.
|
||
|
if [ ! -f "$ROOTFS/boot/uInitrd" ] ||
|
||
|
[ ! -f "$ROOTFS/boot/initrd" ] &&
|
||
|
[ -z "${XBPS_TARGET_ARCH##*arm*}" ] &&
|
||
|
[ -x "$ROOTFS/usr/bin/dracut" ] &&
|
||
|
[ -x "$ROOTFS/usr/bin/mkimage" ]; then
|
||
|
|
||
|
# Dracut needs to know the kernel version that will be using this
|
||
|
# initrd so that it can install the kernel drivers in it. Normally
|
||
|
# this check is quite complex, but since this is a clean rootfs and we
|
||
|
# just installed exactly one kernel, this check can get by with a
|
||
|
# really niave command to figure out the kernel version
|
||
|
KERNELVERSION=$(ls "$ROOTFS/usr/lib/modules/")
|
||
|
|
||
|
# Some platforms also have special arguments that need to be set
|
||
|
# for dracut. This allows us to kludge around issues that may
|
||
|
# exist on certain specific platforms we build for.
|
||
|
set_dracut_args_from_platform
|
||
|
|
||
|
# Now that things are setup, we can call dracut and build the initrd.
|
||
|
# This will pretty much step through the normal process to build
|
||
|
# initrd with the exception that the autoinstaller and netmenu are
|
||
|
# force added since no module depends on them.
|
||
|
info_msg "Building initrd for kernel version $KERNELVERSION"
|
||
|
run_cmd_chroot "$ROOTFS" "env -i /usr/bin/dracut $dracut_args /boot/initrd $KERNELVERSION"
|
||
|
[ $? -ne 0 ] && die "Failed to generate the initramfs"
|
||
|
|
||
|
run_cmd_chroot "$ROOTFS" "env -i /usr/bin/mkimage -A arm -O linux -T ramdisk -C gzip -a 0 -e 0 -n 'Peppermintos' -d /boot/initrd /boot/uInitrd"
|
||
|
fi
|
||
|
|
||
|
cleanup_chroot
|
||
|
|
||
|
# The cache isn't that useful since by the time the ROOTFS will be
|
||
|
# used it is likely to be out of date. Rather than shipping it around
|
||
|
# only for it to be out of date, we remove it now.
|
||
|
rm -rf "$ROOTFS/var/cache/*" 2>/dev/null
|
||
|
|
||
|
# Now we can run the POST_CMD script. This user-supplied script gets the
|
||
|
# $ROOTFS as a parameter.
|
||
|
if [ -n "$POST_CMD" ]; then
|
||
|
info_msg "Running user supplied command: $POST_CMD"
|
||
|
run_cmd $POST_CMD $ROOTFS
|
||
|
fi
|
||
|
|
||
|
|
||
|
# Compress the tarball or just print out the path?
|
||
|
if [ "$COMPRESSION" = "y" ]; then
|
||
|
# Finally we can compress the tarball, the name will include the
|
||
|
# platform and the date on which the tarball was built.
|
||
|
tarball=${FILENAME:-pep-${PLATFORM}-PLATFORMFS-$(date -u '+%Y%m%d').tar.xz}
|
||
|
run_cmd "tar cp --posix --xattrs --xattrs-include='*' -C $ROOTFS . | xz -T${COMPRESSOR_THREADS:-0} -9 > $tarball "
|
||
|
[ $? -ne 0 ] && die "Failed to compress tarball"
|
||
|
|
||
|
# Now that we have the tarball we don't need the rootfs anymore, so we
|
||
|
# can get rid of it.
|
||
|
rm -rf "$ROOTFS"
|
||
|
|
||
|
# Last thing to do before closing out is to let the user know that
|
||
|
# this succeeded. This also ensures that there's something visible
|
||
|
# that the user can look for at the end of the script, which can make
|
||
|
# it easier to see what's going on if something above failed.
|
||
|
info_msg "Successfully created $tarball ($PLATFORM)"
|
||
|
else
|
||
|
# User requested just printing out the path to the rootfs, here it comes.
|
||
|
info_msg "Successfully created rootfs under $ROOTFS"
|
||
|
fi
|