#!/bin/bash # # setup-buildchroot - set up a Debian build chroot # # For details, see usage() and Documentation/building-xfstests.md set -e -u SCRIPTNAME="$(basename "$0")" SCRIPTDIR="$(readlink -f "$(dirname "$0")")" INTERACTIVE=true DEBIAN_RELEASE= DEBIAN_RELEASE_DEFAULT=bookworm DEBIAN_PORTS_RELEASE_DEFAULT=unstable DEBIAN_KEYRING= DEBIAN_KEYRING_DEFAULT=/usr/share/keyrings/debian-archive-keyring.gpg DEBIAN_PORTS_KEYRING_DEFAULT=/usr/share/keyrings/debian-ports-archive-keyring.gpg DEBIAN_ARCH= case $(uname -m) in aarch64) DEBIAN_ARCH_DEFAULT=arm64 ;; i386|i686) DEBIAN_ARCH_DEFAULT=i386 ;; x86_64) DEBIAN_ARCH_DEFAULT=amd64 ;; *) DEBIAN_ARCH_DEFAULT=$(uname -m) ;; esac DEBIAN_PORTS_ARCH_DEFAULT=x32 DEBIAN_MIRROR= DEBIAN_MIRROR_DEFAULT=http://ftp.debian.org/debian DEBIAN_PORTS_MIRROR_DEFAULT=http://ftp.ports.debian.org/debian-ports CHROOT_DIR= CHROOT_NAME= CHROOT_USER= DO_OVERLAY= DO_OVERLAY_DEFAULT=no QEMU= BINFMT_MISC_MNT=/proc/sys/fs/binfmt_misc SCHROOT_CONFFILE=/etc/schroot/schroot.conf SCHROOT_CHROOT_D=/etc/schroot/chroot.d SCHROOT_FSTAB=xfstests-bld/fstab SCHROOT_FSTAB_FILE=/etc/schroot/$SCHROOT_FSTAB PROXY= select_packages() { # Additional packages to install in the build chroot BUILD_DEPENDENCIES=( autoconf autoconf2.64 automake autopoint bison build-essential ca-certificates debootstrap e2fslibs-dev ed fakechroot flex gettext git libblkid-dev libdbus-1-3 libgdbm-dev libicu-dev libkeyutils-dev libsqlite3-dev libssl-dev libsystemd-dev libtool-bin liburcu-dev lsb-release meson pkg-config python3-setuptools qemu-utils rsync symlinks uuid-dev zlib1g-dev ) case "$DEBIAN_RELEASE" in bullseye) BUILD_PACKAGES+=(golang-1.15-go) ;; buster) BUILD_PACKAGES+=(golang-1.11-go) ;; stretch) BUILD_PACKAGES+=(golang-1.8-go) ;; esac } die() { local msg echo 1>&2 "ERROR: $1" shift for msg; do echo 1>&2 " $msg" done exit 1 } log() { local msg echo "[INFO] $1" shift for msg; do echo " $msg" done } run_cmd() { log "Running command: $*" "$@" } usage() { cat </dev/null; then die "debootstrap is not installed!" \ "On Debian-based systems, run: 'sudo apt-get install debootstrap'" fi if ! type -P schroot >/dev/null; then die "schroot is not installed!" \ "On Debian-based systems, run: 'sudo apt-get install schroot'" fi if [ ! -f "$SCHROOT_CONFFILE" ]; then die "$SCHROOT_CONFFILE does not exist!" fi if [ "$(id -u)" != 0 ]; then die "this script must be run as root!" fi } prompt_for_param() { local prompt="$1" local param_name="$2" local default_value="$3" if [ -z "${!param_name}" ] && $INTERACTIVE; then echo -n "Enter $prompt (${default_value:-none}): " read -r "$param_name" fi if [ -z "${!param_name}" ]; then declare -g "$param_name=$default_value" fi } select_debian_release() { prompt_for_param "Debian release" DEBIAN_RELEASE "$DEBIAN_RELEASE_DEFAULT" } select_debian_arch() { while true; do prompt_for_param "Debian architecture" \ DEBIAN_ARCH "$DEBIAN_ARCH_DEFAULT" local suggestion= case "$DEBIAN_ARCH" in x86|i686) suggestion=i386 ;; x86_64|x86-64|x64) suggestion=amd64 ;; arm|arm32|aarch32) suggestion=armhf ;; aarch64) suggestion=arm64 ;; esac if [ -z "$suggestion" ]; then break fi local msg="$DEBIAN_ARCH is not a valid Debian architecture name; did you mean $suggestion?" if ! $INTERACTIVE; then die "$msg" fi echo "$msg" DEBIAN_ARCH= done } select_debian_mirror() { prompt_for_param "Debian mirror" DEBIAN_MIRROR "$DEBIAN_MIRROR_DEFAULT" } select_debian_keyring() { prompt_for_param "Debian keyring" DEBIAN_KEYRING "$DEBIAN_KEYRING_DEFAULT" } add_fstab_bind() { if ! grep "^$SCRIPTDIR\s" $SCHROOT_FSTAB_FILE &> /dev/null; then echo "Adding $SCRIPTDIR bind mount to $SCHROOT_FSTAB_FILE" echo "$SCRIPTDIR $SCRIPTDIR none rw,bind 0 0" >> $SCHROOT_FSTAB_FILE return 0 fi return 1 } select_chroot_dir() { prompt_for_param "chroot directory" \ CHROOT_DIR "/chroots/${DEBIAN_RELEASE}-${DEBIAN_ARCH}" if [ -e "$CHROOT_DIR" ]; then add_fstab_bind && exit 0 die "$CHROOT_DIR already exists and $SCHROOT_FSTAB_FILE up-to-date!" fi } select_chroot_name() { prompt_for_param "chroot name" CHROOT_NAME "$(basename "$CHROOT_DIR")" prompt_for_param "yes to use overlay file system" DO_OVERLAY "$DO_OVERLAY_DEFAULT" } select_chroot_user() { prompt_for_param "chroot user" CHROOT_USER "${SUDO_USER:-}" } select_proxy() { prompt_for_param "proxy" PROXY "${PROXY}" } parse_options() { local longopts="help" local options longopts+=",release:" longopts+=",arch:" longopts+=",mirror:" longopts+=",keyring:" longopts+=",chroot-dir:" longopts+=",chroot-name:" longopts+=",chroot-user:" longopts+=",proxy:" longopts+=",use-overlay" longopts+=",no-overlay" longopts+=",noninteractive" longopts+=",ports" if ! options=$(getopt -o "" -l "$longopts" -- "$@"); then usage 1>&2 exit 2 fi eval set -- "$options" while (( $# >= 0 )); do case "$1" in --help) usage exit 0 ;; --release) DEBIAN_RELEASE="$2" shift ;; --arch) DEBIAN_ARCH="$2" shift ;; --mirror) DEBIAN_MIRROR="$2" shift ;; --keyring) DEBIAN_KEYRING="$2" shift ;; --chroot-dir) CHROOT_DIR="$2" shift ;; --chroot-name) CHROOT_NAME="$2" shift ;; --chroot-user) CHROOT_USER="$2" shift ;; --proxy) PROXY="$2" shift ;; --use-overlay) DO_OVERLAY=yes ;; --no-overlay) DO_OVERLAY=no ;; --ports) DEBIAN_ARCH_DEFAULT="$DEBIAN_PORTS_ARCH_DEFAULT" DEBIAN_KEYRING_DEFAULT="$DEBIAN_PORTS_KEYRING_DEFAULT" DEBIAN_MIRROR_DEFAULT="$DEBIAN_PORTS_MIRROR_DEFAULT" DEBIAN_RELEASE_DEFAULT="$DEBIAN_PORTS_RELEASE_DEFAULT" ;; --noninteractive) INTERACTIVE=false ;; --) shift break ;; *) echo 1>&2 "Invalid option: \"$1\"" usage 1>&2 exit 2 ;; esac shift done } is_native_chroot() { case "$(uname -m)" in x86_64) [[ $DEBIAN_ARCH = amd64 || $DEBIAN_ARCH = i386 || $DEBIAN_ARCH = x32 ]] ;; aarch64) [[ $DEBIAN_ARCH = arm64 || $DEBIAN_ARCH = armhf ]] ;; arm) [[ $DEBIAN_ARCH = armhf ]] ;; ppc64le) [[ $DEBIAN_ARCH = ppc64el ]] ;; *) [[ $DEBIAN_ARCH = "$(uname -m)" ]] ;; esac } # Validate that binfmt_misc support for the requested architecture is enabled, # then set $QEMU to the path to the QEMU binary that should be used. validate_binfmt_misc() { local qemu_arch binfmt binfmt_file case "$DEBIAN_ARCH" in armhf) qemu_arch=arm ;; arm64) qemu_arch=aarch64 ;; ppc64el) qemu_arch=ppc64le ;; *) qemu_arch="$DEBIAN_ARCH" ;; esac if [ ! -d "$BINFMT_MISC_MNT" ]; then die "$BINFMT_MISC_MNT doesn't exist. To set up a chroot for a" \ "foreign architecture, you must enable CONFIG_BINFMT_MISC in your kernel." fi if ! mountpoint "$BINFMT_MISC_MNT" &>/dev/null; then die "binfmt_misc is not mounted on $BINFMT_MISC_MNT!" \ "If your init system isn't mounting binfmt_misc automatically" \ "(systemd should mount it by default), you can add to your fstab:" \ "" \ " binfmt_misc $BINFMT_MISC_MNT binfmt_misc defaults 0 0" fi if [ "$(<"$BINFMT_MISC_MNT/status")" = "disabled" ]; then die "binfmt_misc is currently disabled! To enable it, run:" " sudo sh -c 'echo 1 > $BINFMT_MISC_MNT/status'" fi binfmt="qemu-$qemu_arch" binfmt_file="$BINFMT_MISC_MNT/$binfmt" if [ ! -e "$binfmt_file" ]; then die "No binfmt_misc handler for $binfmt is installed!" \ "Make sure you've installed both the binfmt-support and qemu-user-static" \ "packages, e.g. on Debian-based systems:" \ "" \ " sudo apt-get install binfmt-support qemu-user-static" \ "" \ "on some systems you must also explicitly enable and start the" \ "binfmt-support service:" \ "" \ " sudo systemctl enable --now binfmt-support.service" fi QEMU=$(awk '/^interpreter/{print $2}' "$binfmt_file") log "Detected foreign chroot, using user-mode emulation with $QEMU" } # Get the schroot configuration from either schroot.conf or entries in # the chroot.d directory get_schroot_config() { cat $SCHROOT_CONFFILE if test -d $SCHROOT_CHROOT_D then (cd $SCHROOT_CHROOT_D ; find . -maxdepth 1 -type f | grep -E '^./[a-ZA-Z0-9_-][a-ZA-Z0-9_.-]*$' | xargs cat) fi } # Extract the schroot.conf entry, if any, for $CHROOT_NAME extract_schroot_entry() { get_schroot_config | awk ' { if ($0 ~ /^[[:space:]]*\[.*\][[:space:]]*$/) { sub(/\][[:space:]]*$/, "") sub(/^[[:space:]]*\[/, "") section=$0 } else if (section == "'"$CHROOT_NAME"'" && $0 !~ /^[[:space:]]*(#.*)?$/) { print } } ' } generate_schroot_entry() { # See `man schroot.conf` # # Note: 'setup.nssdatabases=' prevents NSS databases, such as passwd # entries, from being copied into the chroot. They aren't needed anyway, # and there could be a large number of entries on the host system which # break things and/or make building things much slower. cat < Wanted:" generate_schroot_entry echo echo "--> Found:" extract_schroot_entry echo die "Please either delete the existing entry first, or fix it manually." } # Add an entry to schroot.conf for the chroot. add_schroot_conf_entry() { local f check_for_conflicting_schroot_entry if [ -n "$(extract_schroot_entry)" ]; then log "$CHROOT_NAME entry already exists in $SCHROOT_CONFFILE" return fi if test -d $SCHROOT_CHROOT_D then f="$SCHROOT_CHROOT_D/$CHROOT_NAME" else f="$SCHROOT_CONFFILE" fi log "Adding new entry to $f:" { echo echo "# entry added by $SCRIPTNAME" echo "[$CHROOT_NAME]" generate_schroot_entry } | tee -a "$f" echo } # Create the schroot fstab file needed by the chroot. The fstab will contain # the usual entries to mount sysfs, procfs, etc., as well as an entry that # bind-mounts the xfstests-bld directory into the chroot. # # Note that all chroots created by this script will share the same fstab. create_schroot_fstab() { if [ -e "$SCHROOT_FSTAB_FILE" ]; then log "$SCHROOT_FSTAB_FILE was already created" add_fstab_bind || true return fi mkdir -p "$(dirname "$SCHROOT_FSTAB_FILE")" cat >"$SCHROOT_FSTAB_FILE" <"$CHROOT_DIR/etc/apt/sources.list" <> "$CHROOT_DIR/etc/passwd" getent group "$(id -g "$CHROOT_USER")" >> "$CHROOT_DIR/etc/group" run_in_chroot "adduser $CHROOT_USER sudo" fi } setup_mtab() { log "Linking /etc/mtab to ../proc/self/mounts" schroot -c "$CHROOT_NAME" -u root -- ln -sf ../proc/self/mounts /etc/mtab } install_build_dependencies() { local chroot log "Installing build dependencies: ${BUILD_DEPENDENCIES[*]}" if [ "${DO_OVERLAY}" = "no" ]; then chroot="${CHROOT_NAME}" else chroot="source:${CHROOT_NAME}" fi schroot -c "$chroot" -u root -- <