summaryrefslogtreecommitdiff
path: root/src/chroot-tools
diff options
context:
space:
mode:
Diffstat (limited to 'src/chroot-tools')
-rw-r--r--src/chroot-tools/.gitignore7
-rw-r--r--src/chroot-tools/HACKING.md3
-rw-r--r--src/chroot-tools/Makefile27
-rw-r--r--src/chroot-tools/arch-nspawn.patch71
-rw-r--r--src/chroot-tools/chcleanup.in102
-rw-r--r--src/chroot-tools/chroot.conf13
-rwxr-xr-xsrc/chroot-tools/distcc-tool247
-rw-r--r--src/chroot-tools/hooks-chcleanup.sh32
-rw-r--r--src/chroot-tools/hooks-check.sh37
-rw-r--r--src/chroot-tools/hooks-distcc.sh106
-rwxr-xr-xsrc/chroot-tools/indent57
-rwxr-xr-xsrc/chroot-tools/librechroot509
-rwxr-xr-xsrc/chroot-tools/libremakepkg289
-rw-r--r--src/chroot-tools/makechrootpkg.sh.patch342
-rw-r--r--src/chroot-tools/mkarchroot.patch67
15 files changed, 1909 insertions, 0 deletions
diff --git a/src/chroot-tools/.gitignore b/src/chroot-tools/.gitignore
new file mode 100644
index 0000000..f0969c7
--- /dev/null
+++ b/src/chroot-tools/.gitignore
@@ -0,0 +1,7 @@
+makechrootpkg.sh*
+arch-nspawn*
+mkarchroot*
+!*.patch
+
+chcleanup
+chcleanup.lib
diff --git a/src/chroot-tools/HACKING.md b/src/chroot-tools/HACKING.md
new file mode 100644
index 0000000..e50aa11
--- /dev/null
+++ b/src/chroot-tools/HACKING.md
@@ -0,0 +1,3 @@
+Unfortunately, `makechrootpkg.sh` is GPLv2 ONLY. This means that
+everything that uses it must be held to GPLv2+ instead of GPLv3+. I'm
+calling this anything that gets loaded into the same process as it.
diff --git a/src/chroot-tools/Makefile b/src/chroot-tools/Makefile
new file mode 100644
index 0000000..48a1631
--- /dev/null
+++ b/src/chroot-tools/Makefile
@@ -0,0 +1,27 @@
+include $(dir $(lastword $(MAKEFILE_LIST)))/../../config.mk
+include $(topsrcdir)/automake.head.mk
+pkglibexecdir = $(libexecdir)/libretools/chroot
+
+libretools-bins = chcleanup librechroot libremakepkg
+libretools-libexecs += arch-nspawn chcleanup distcc-tool indent mkarchroot
+libretools-libs += makechrootpkg.sh
+devtools-files = makechrootpkg.sh.in mkarchroot.in arch-nspawn.in
+am_clean_files += chcleanup.lib
+
+$(srcdir)/makechrootpkg.sh.in: $(srcdir)/%.sh.in: $(devtoolsdir)/%.in
+ cp $< $@
+
+$(outdir)/chcleanup: $(srcdir)/chcleanup.in $(outdir)/chcleanup.lib
+ m4 -I$(@D) -P $< | $(edit) | install -m755 /dev/stdin $@
+$(outdir)/chcleanup.lib: $(call _am_path,$(topoutdir)/src/lib/common.sh) $(outdir)/Makefile
+ bash -c '. $<; declare -f _l plain msg msg2 error' > $@
+
+$(outdir)/distcc-tool.pot: LIBREXGETTEXT_FLAGS+=--simple=errusage
+
+$(DESTDIR)$(bindir)/chcleanup: $(var)bindir $(var)libexecdir
+ mkdir -p $(@D)
+ ln -srfT $(DESTDIR)$(libexecdir)/libretools/chroot/chcleanup $@
+
+am_depdirs = ../lib
+
+include $(topsrcdir)/automake.tail.mk
diff --git a/src/chroot-tools/arch-nspawn.patch b/src/chroot-tools/arch-nspawn.patch
new file mode 100644
index 0000000..dc4cb25
--- /dev/null
+++ b/src/chroot-tools/arch-nspawn.patch
@@ -0,0 +1,71 @@
+--- arch-nspawn.in 2016-05-10 13:48:14.303797115 -0400
++++ arch-nspawn.ugly 2016-05-10 13:48:23.296957958 -0400
+@@ -1,4 +1,6 @@
+ #!/bin/bash
++# License: GNU GPLv2
++#
+ # 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; version 2 of the License.
+@@ -14,6 +16,8 @@
+
+ working_dir=''
+
++files=()
++
+ usage() {
+ echo "Usage: ${0##*/} [options] working-dir [systemd-nspawn arguments]"
+ echo "A wrapper around systemd-nspawn. Provides support for pacman."
+@@ -22,17 +26,21 @@
+ echo ' -C <file> Location of a pacman config file'
+ echo ' -M <file> Location of a makepkg config file'
+ echo ' -c <dir> Set pacman cache'
++ echo ' -f <file> Copy file from the host to the chroot'
++ echo ' -s Do not run setarch'
+ echo ' -h This message'
+ exit 1
+ }
+
+ orig_argv=("$@")
+
+-while getopts 'hC:M:c:' arg; do
++while getopts 'hC:M:c:f:s' arg; do
+ case "$arg" in
+ C) pac_conf="$OPTARG" ;;
+ M) makepkg_conf="$OPTARG" ;;
+ c) cache_dir="$OPTARG" ;;
++ f) files+=("$OPTARG") ;;
++ s) nosetarch=1 ;;
+ h|?) usage ;;
+ *) error "invalid argument '%s'" "$arg"; usage ;;
+ esac
+@@ -78,6 +86,12 @@
+ [[ -n $pac_conf ]] && cp $pac_conf "$working_dir/etc/pacman.conf"
+ [[ -n $makepkg_conf ]] && cp $makepkg_conf "$working_dir/etc/makepkg.conf"
+
++ local file
++ for file in "${files[@]}"; do
++ mkdir -p "$(dirname "$working_dir$file")"
++ cp -T "$file" "$working_dir$file"
++ done
++
+ sed -r "s|^#?\\s*CacheDir.+|CacheDir = $(echo -n ${cache_dirs[@]})|g" -i "$working_dir/etc/pacman.conf"
+ }
+ # }}}
+@@ -92,6 +106,7 @@
+ fi
+
+ build_mount_args
++cache_dirs+=('/repo/')
+ copy_hostconf
+
+ eval $(grep '^CARCH=' "$working_dir/etc/makepkg.conf")
+@@ -99,6 +114,8 @@
+ armv7h) CARCH=armv7l;;
+ esac
+
++[[ -z $nosetarch ]] || unset CARCH
++
+ exec ${CARCH:+setarch "$CARCH"} systemd-nspawn -q \
+ -D "$working_dir" \
+ --register=no \
diff --git a/src/chroot-tools/chcleanup.in b/src/chroot-tools/chcleanup.in
new file mode 100644
index 0000000..04cf8ad
--- /dev/null
+++ b/src/chroot-tools/chcleanup.in
@@ -0,0 +1,102 @@
+#!/usr/bin/env bash
+# Copyright (C) 2011-2012 Nicolás Reynolds <fauno@parabola.nu>
+# Copyright (C) 2012-2013, 2015 Luke Shumaker <lukeshu@sbcglobal.net>
+#
+# If you don't see m4_include(...) below, but see function definitions
+# for msg() et al., then this is a generated file, and contains some
+# code from librelib. The the source distribution for full copyright
+# information.
+#
+# License: GNU GPLv3+
+#
+# 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 3 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 Affero General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+# Performs chroot cleanup smartly, it only removes the unneeded packages or
+# leaves you with a cleansystem
+#
+# See: HOOKPREBUILD
+
+set -eE
+
+DRYRUN=${DRYRUN:-false}
+
+# Statically include various library routines to avoid having
+# dependencies on outside files.
+[[ -n ${TEXTDOMAIN:-} ]] || export TEXTDOMAIN='libretools'
+[[ -n ${TEXTDOMAINDIR:-} ]] || export TEXTDOMAINDIR='/usr/share/locale'
+
+if type gettext &>/dev/null; then
+ _() { gettext "$@"; }
+else
+ _() { echo "$@"; }
+fi
+# Begin chcleanup.lib ##########################################################
+m4_include(chcleanup.lib)
+# End chcleanup.lib ############################################################
+
+if [[ ! -f /.arch-chroot ]] && ! ${DRYRUN}; then
+ error "(chcleanup): Must be run inside of a chroot"
+ exit 1
+fi
+
+# Note: the in-chroot pkgconfdir is non-configurable, this is
+# intentionally hard-coded.
+source /etc/libretools.d/chroot.conf
+# If we're running makepkg
+if [[ -f PKGBUILD ]]; then
+ export CARCH="$(. /etc/makepkg.conf; printf '%s' "$CARCH")"
+ source ./PKGBUILD
+ CHROOTEXTRAPKG+=("${depends[@]}"
+ "${makedepends[@]}"
+ "${checkdepends[@]}")
+fi
+
+msg "Cleaning chroot..."
+
+# Sync the local repo with pacman
+cp /repo/repo.db /var/lib/pacman/sync/repo.db
+
+# Setup the temporary directory
+TEMPDIR="$(mktemp --tmpdir -d ${0##*/}.XXXXXXXXXX)"
+trap "rm -rf -- $(printf '%q' "$TEMPDIR")" EXIT
+
+cp -a /var/lib/pacman/sync "${TEMPDIR}/"
+pkglist="${TEMPDIR}"/pkglist.txt
+
+# Get the full list of packages needed by dependencies, including the base system
+msg2 "Creating a full list of packages..."
+pacman -b "${TEMPDIR}" \
+ -Sp --print-format "%n" base-devel "${CHROOTEXTRAPKG[@]}" >"$pkglist" || {
+ ret=$?
+ error "Could not create a full list of packages, exiting."
+ plain "This is likely caused by a dependency that could not be found."
+ exit $ret
+}
+
+# Diff installed packages against a clean chroot then remove leftovers
+packages=($(comm -23 <(pacman -Qq | sort -u) \
+ <(sort -u "${pkglist}")))
+
+if [[ ${#packages[@]} = 0 ]]; then
+ msg2 "No packages to remove"
+else
+ msg2 "Removing %d packages" ${#packages[@]}
+
+ if ${DRYRUN}; then
+ echo "${packages[*]}"
+ else
+ # Only remove leftovers, -Rcs removes too much
+ pacman --noconfirm -Rn "${packages[@]}"
+ fi
+fi
diff --git a/src/chroot-tools/chroot.conf b/src/chroot-tools/chroot.conf
new file mode 100644
index 0000000..3b3c445
--- /dev/null
+++ b/src/chroot-tools/chroot.conf
@@ -0,0 +1,13 @@
+# The full path to the chroot is
+# $CHROOTDIR/$CHROOT/$COPY
+# where $COPY is set at runtime.
+# See `librechroot help` for details.
+CHROOTDIR=/var/lib/archbuild
+CHROOT=default
+
+# Extra packages to have installed on the chroot.
+# This is in addition to CHROOTPKG=(base-devel)
+CHROOTEXTRAPKG=()
+#CHROOTEXTRAPKG+=(distcc-nozeroconf socat) # for BUILDENV+=(distcc)
+#CHROOTEXTRAPKG+=(ccache) # for BUILDENV+=(ccache)
+#CHROOTEXTRAPKG+=(libretools) # for running libremakepkg inside the chroot
diff --git a/src/chroot-tools/distcc-tool b/src/chroot-tools/distcc-tool
new file mode 100755
index 0000000..d181109
--- /dev/null
+++ b/src/chroot-tools/distcc-tool
@@ -0,0 +1,247 @@
+#!/usr/bin/env bash
+# -*- tab-width: 4; sh-basic-offset: 4 -*-
+# distcc-tool
+
+# Copyright (C) 2013-2014 Luke Shumaker <lukeshu@sbcglobal.net>
+#
+# License: GNU GPLv3+
+#
+# This file is part of Parabola.
+#
+# Parabola 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 3 of the License, or
+# (at your option) any later version.
+#
+# Parabola 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 Parabola. If not, see <http://www.gnu.org/licenses/>.
+
+# This program has very few dependencies:
+# - bash: I don't know what version, I use fairly modern features
+# - socat
+# - cat: any version
+# - rm: any version
+# - sed: any version
+# On Parabola, this means the packages:
+# bash, coreutils, sed, socat
+
+if ! type gettext &>/dev/null; then
+ gettext() { echo "$@"; }
+fi
+
+q0="$(printf '%q' "$0")" # quoted $0
+
+panic() {
+ echo "$(gettext 'panic: malformed call to internal function')" >&2
+ exit 1
+}
+
+error() {
+ mesg="$(gettext "$1")"; shift
+ printf "$(gettext 'ERROR:') $mesg\n" "$@" >&2
+ exit 1
+}
+
+print() {
+ local mesg=$1
+ shift
+ printf -- "$(gettext "$mesg")\n" "$@"
+}
+
+usage() {
+ print "Usage: %s COMMAND [COMMAND-ARGS]" "$q0"
+ print "Tool for using distcc within a networkless chroot"
+ echo
+ print "Commands:"
+ print ' help print this message'
+ print ' odaemon CHROOTPATH daemon to run outside of the chroot'
+ print ' idaemon DISTCC_HOSTS daemon to run inside of the chroot'
+ print ' rewrite DISTCC_HOSTS prints a rewritten version of DISTCC_HOSTS'
+ print ' client HOST PORT connects stdio to TCP:$HOST:$PORT'
+ print 'Commands: for internal use'
+ print ' server counterpart to client; spawned by odaemon'
+}
+
+errusage() {
+ if [[ $# -gt 0 ]]; then
+ fmt="$(gettext "$1")"; shift
+ printf "$(gettext 'ERROR:') $fmt\n" "$@" >&2
+ fi
+ usage >&2
+ exit 1
+}
+
+main() {
+ local cmd=$1
+ shift
+ case "$cmd" in
+ help)
+ [[ $# -eq 0 ]] || errusage '%s: invalid number of arguments' "$cmd"
+ usage;;
+ odaemon|idaemon|rewrite)
+ [[ $# -eq 1 ]] || errusage '%s: invalid number of arguments' "$cmd"
+ $cmd "$@";;
+ client)
+ [[ $# -eq 2 ]] || errusage '%s: invalid number of arguments' "$cmd"
+ $cmd "$@";;
+ server)
+ [[ $# -eq 0 ]] || errusage '%s: invalid number of arguments' "$cmd"
+ $cmd "$@";;
+ *) errusage 'unknown subcommand: %s' "$cmd";;
+ esac
+}
+
+################################################################################
+# DISTCC_HOSTS parser #
+################################################################################
+
+# usage: parse_DISTCC_HOSTS true|false DISTCC_HOSTS
+# parses DISTCC_HOSTS and:
+# $1==true : It sets up port forwarding for inside the choot, sleep forever
+# $1==false: Prints a modified version of DISTCC_HOSTS that uses the forwarded
+# ports that were set up when $1==true.
+parse_DISTCC_HOSTS() {
+ { [[ $# -eq 2 ]] && { [[ $1 == true ]] || [[ $1 == false ]]; }; } || panic
+ local forward_ports=$1
+ local DISTCC_HOSTS=$2
+
+ local pids=() # child pids
+ local newhosts=()
+ local newport=8000 # next port to be used for port forwarding
+
+ # This is based on the grammar specified in distcc(1)
+ local HOSTSPEC
+ for HOSTSPEC in $(sed 's/#.*//' <<<"$DISTCC_HOSTS"); do
+ case "$HOSTSPEC" in
+ # LOCAL_HOST
+ localhost|localhost/*|--localslots=*|--localslots_cpp=*)
+ # "localhost" runs commands directly, not talking to distccd at
+ # localhost, use an IP or real hostname for that.
+ # So, just pass these through.
+ newhosts+=("$HOSTSPEC")
+ ;;
+ # SSH_HOST
+ *@*)
+ # SSH_HOST doesn't allow custom port numbers, and even if it
+ # did, ssh would complain about MITM. Instead, we'll count on
+ # ssh ProxyCommand being configured to use `client`.
+ newhosts+=("$HOSTSPEC")
+ ;;
+ # GLOBAL_OPTION
+ --*)
+ # pass these through
+ newhosts+=("$HOSTSPEC")
+ ;;
+ # ZEROCONF
+ +zeroconf)
+ error "%s does not support the +zeroconf option" "$q0"
+ exit 1
+ ;;
+ # TCP_HOST or OLDSTYLE_TCP_HOST
+ *)
+ declare HOSTID= PORT= LIMIT= OPTIONS=
+ if [[ $HOSTSPEC =~ ^([^:/]+)(:([0-9]+))?(/([0-9]+))?(,.*)?$ ]]; then
+ # TCP_HOST
+ HOSTID=${BASH_REMATCH[1]}
+ PORT=${BASH_REMATCH[3]}
+ LIMIT=${BASH_REMATCH[5]}
+ OPTIONS=${BASH_REMATCH[6]}
+ elif [[ $HOSTSPEC =~ ^([^:/]+)(/([0-9]+))?(:([0-9]+))?(,.*)?$ ]]; then
+ # OLDSTYLE_TCP_HOST
+ HOSTID=${BASH_REMATCH[1]}
+ LIMIT=${BASH_REMATCH[3]}
+ PORT=${BASH_REMATCH[5]}
+ OPTIONS=${BASH_REMATCH[6]}
+ else
+ error "Could not parse HOSTSPEC: %s" "$HOSTSPEC"
+ fi
+
+ # set up port forwaring
+ if $forward_ports; then
+ socat TCP-LISTEN:${newport},fork SYSTEM:"$q0 client $HOSTID ${PORT:-3632}" &
+ pids+=($!)
+ fi
+
+ # add the forwarded port
+ local newhost="127.0.0.1:$newport"
+ [[ -z $LIMIT ]] || newhost+="/$LIMIT"
+ [[ -z $OPTIONS ]] || newhost+="$OPTIONS"
+ newhosts+=("$newhost")
+ : $((newport++))
+ ;;
+ esac
+ done
+ if $forward_ports; then
+ if [[ -z "${pids[*]}" ]]; then
+ # listen on port 8000, but immediatly close, just so that we are
+ # listening on something
+ socat TCP-LISTEN:${newport},fork SYSTEM:true &
+ pids+=($!)
+ fi
+ trap "kill -- ${pids[*]}" EXIT
+ wait "${pids[@]}"
+ else
+ printf '%s\n' "${newhosts[*]}"
+ fi
+}
+
+################################################################################
+# Port forwarding primitives #
+################################################################################
+
+# Usage: server
+# Reads "host port" from the first line of stdin, then connects stdio to the
+# specified TCP socket.
+server() {
+ [[ $# -eq 0 ]] || panic
+ local host port
+ read -r host port
+ socat STDIO TCP:"$host:$port"
+}
+
+# Usage: client HOST PORT
+# For usage inside of a chroot. It talks through the UNIX-domain socket to an
+# instance of `server` outside, in order to connect stdio to the specified TCP
+# socket.
+client() {
+ [[ $# -eq 2 ]] || panic
+ local file=/socket
+ { printf '%s\n' "$*"; cat; } | socat UNIX-CONNECT:"$file" STDIO
+}
+
+################################################################################
+# High-level routines #
+################################################################################
+
+# Usage: odaemon CHROOTPATH
+# Listens on "$CHROOTPATH/socket" and spawns a `server` for each new connection.
+odaemon() {
+ [[ $# -eq 1 ]] || panic
+ local chrootpath=$1
+
+ umask 111
+ socat UNIX-LISTEN:"$chrootpath/socket",fork SYSTEM:"$q0 server" &
+ trap "kill -- $!; rm -f -- $(printf '%q' "$chrootpath/socket")" EXIT
+ wait
+}
+
+# Usage: idaemon DISTCC_HOSTS
+# Sets things up inside of the chroot to forward distcc hosts out.
+idaemon() {
+ [[ $# -eq 1 ]] || panic
+ parse_DISTCC_HOSTS true "$1"
+}
+
+# Usage: rewrite DISTCC_HOSTS
+# Prints a modified version of DISTCC_HOSTS for inside the chroot.
+rewrite() {
+ [[ $# -eq 1 ]] || panic
+ parse_DISTCC_HOSTS false "$1"
+}
+
+main "$@"
diff --git a/src/chroot-tools/hooks-chcleanup.sh b/src/chroot-tools/hooks-chcleanup.sh
new file mode 100644
index 0000000..7afb13d
--- /dev/null
+++ b/src/chroot-tools/hooks-chcleanup.sh
@@ -0,0 +1,32 @@
+#!/usr/bin/env bash
+# Copyright (C) 2013 Luke Shumaker <lukeshu@sbcglobal.net>
+#
+# License: GNU GPLv2+
+#
+# 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 Affero General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+set -euE
+
+hook_pre_build+=("clean_chroot")
+
+clean_chroot() (
+ set +x
+ local copydir=$1
+ if $INCHROOT; then
+ cd /startdir
+ "$(librelib chroot/chcleanup)"
+ else
+ librechroot "${librechroot_flags[@]}" clean-pkgs
+ fi
+)
diff --git a/src/chroot-tools/hooks-check.sh b/src/chroot-tools/hooks-check.sh
new file mode 100644
index 0000000..850516b
--- /dev/null
+++ b/src/chroot-tools/hooks-check.sh
@@ -0,0 +1,37 @@
+#!/usr/bin/env bash
+# Copyright (C) 2013 Luke Shumaker <lukeshu@sbcglobal.net>
+#
+# License: GNU GPLv2+
+#
+# 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 Affero General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+set -euE
+
+hook_check_pkgbuild+=("check_pkgbuild_nonfree")
+check_pkgbuild_nonfree() {
+ local s=0
+ sudo -EH -u "$LIBREUSER" pkgbuild-check-nonfree -f || s=$?
+ pkgbuild-summarize-nonfree $s
+}
+
+#hook_check_pkgbuild+=("check_pkgbuild_namcap")
+check_pkgbuild_namcap() {
+ sudo -EH -u "$LIBREUSER" namcap PKGBUILD
+}
+
+#hook_check_pkg+=("check_pkg")
+check_pkg() {
+ # TODO
+ :
+}
diff --git a/src/chroot-tools/hooks-distcc.sh b/src/chroot-tools/hooks-distcc.sh
new file mode 100644
index 0000000..bb234b8
--- /dev/null
+++ b/src/chroot-tools/hooks-distcc.sh
@@ -0,0 +1,106 @@
+#!/usr/bin/env bash
+# Copyright (C) 2013 Luke Shumaker <lukeshu@sbcglobal.net>
+#
+# License: GNU GPLv2+
+#
+# This file is part of Parabola.
+#
+# Parabola 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.
+#
+# Parabola 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 Parabola. If not, see <http://www.gnu.org/licenses/>.
+
+set -euE
+
+hook_pre_build+=("distcc_start")
+hook_post_build+=("distcc_stop")
+
+_distcc_check() {
+ local copydir=$1
+ local home=$2
+
+ local files=(
+ "$copydir/bin/distcc-tool"
+ "$copydir/run/distcc-tool.pid"
+ "$home/.makepkg.conf"
+ "$home/.ssh/config"
+ )
+
+ local file_err=false
+ for files in "${files[@]}"; do
+ if [[ -f $file ]]; then
+ file_err=true
+ error "Auto-generated file already exists, remove it: %s" "$file"
+ fi
+ done
+ if $file_err; then
+ exit 1
+ fi
+}
+
+distcc_start() {
+ local copydir=$1
+
+ # Because /{,usr/}{,s}bin are all symlinked together for
+ # fileystem 2013.05-2 and up, I can take shortcuts when checking for
+ # existance of programs.
+ if $NONET && [[ -f "$copydir/bin/socat" && -f "$copydir/bin/distcc" ]]; then
+ local home
+ if $INCHROOT; then
+ home=$LIBREHOME
+ else
+ home="$copydir/build"
+ fi
+
+ _distcc_check
+
+ local _distcc_tool="$(librelib chroot/distcc-tool)"
+ install -m755 "$_distcc_tool" "$copydir/bin/distcc-tool"
+
+ mkdir -p "$home/.ssh"
+
+ printf '%s\n' \
+ '/bin/distcc-tool idaemon "$DISTCC_HOSTS" &' \
+ 'DISTCC_HOSTS="$(/bin/distcc-tool rewrite "$DISTCC_HOSTS")"' \
+ > "$home/.makepkg.conf"
+
+ printf '%s\n' \
+ 'Host *' \
+ ' ProxyCommand /bin/distcc-tool client %h %p' \
+ > "$home/.ssh/config"
+
+ "$_distcc_tool" odaemon "$copydir" &
+ echo $! > "$copydir/run/distcc-tool.pid"
+ fi
+}
+
+distcc_stop() {
+ local copydir=$1
+
+ local home
+ if $INCHROOT; then
+ home=$LIBREHOME
+ else
+ home="$copydir/build"
+ fi
+
+ if [[ -f "$copydir/run/distcc-tool.pid" ]]; then
+
+ odaemon=$(< "$copydir/distcc-tool.pid")
+ kill -- "$odaemon"
+
+ rm -f -- \
+ "$home/.makepkg.conf" \
+ "$home/.ssh/config" \
+ "$copydir/bin/distcc-tool" \
+ "$copydir/run/distcc-tool.pid"
+ fi
+}
diff --git a/src/chroot-tools/indent b/src/chroot-tools/indent
new file mode 100755
index 0000000..0f047c6
--- /dev/null
+++ b/src/chroot-tools/indent
@@ -0,0 +1,57 @@
+#!/usr/bin/env perl
+# Copyright (C) 2013 Luke Shumaker <lukeshu@sbcglobal.net>
+#
+# License: GNU GPLv3+
+#
+# This file is part of Parabola.
+#
+# Parabola 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 3 of the License, or
+# (at your option) any later version.
+#
+# Parabola 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 Parabola. If not, see <http://www.gnu.org/licenses/>.
+
+use warnings;
+use strict;
+
+use constant BUFFER_SIZE => 40;
+binmode(STDIN);
+binmode(STDOUT);
+
+exit(1) if ($#ARGV != 0);
+my $indent = $ARGV[0];
+
+# 0: no indent has been printed for this line, an indent WILL need to be printed
+# 1: an indent needs to be printed for this line IFF there is any more output on it
+# 2: no indent (currently) needs to be printed for this line
+my $print_indent = 0;
+
+my $buffer;
+my $size;
+my $c;
+while (1) {
+ $size = sysread(STDIN, $buffer, BUFFER_SIZE);
+ last if ($size < 1);
+ for (0..$size-1) {
+ $c = substr($buffer, $_, 1);
+ if ($c eq "\n") {
+ syswrite(STDOUT, $indent) if ($print_indent == 0);
+ syswrite(STDOUT, $c, 1);
+ $print_indent = 0;
+ } elsif ($c eq "\r") {
+ syswrite(STDOUT, $c, 1);
+ $print_indent = 1 if ($print_indent == 2);
+ } else {
+ syswrite(STDOUT, $indent) if ($print_indent < 2);
+ syswrite(STDOUT, $c, 1);
+ $print_indent = 2;
+ }
+ }
+}
diff --git a/src/chroot-tools/librechroot b/src/chroot-tools/librechroot
new file mode 100755
index 0000000..cf564ed
--- /dev/null
+++ b/src/chroot-tools/librechroot
@@ -0,0 +1,509 @@
+#!/usr/bin/env bash
+set -euE
+# librechroot
+
+# Copyright (C) 2010-2012 Nicolás Reynolds <fauno@parabola.nu>
+# Copyright (C) 2011-2012 Joshua Ismael Haase Hernández (xihh) <hahj87@gmail.com>
+# Copyright (C) 2012 Michał Masłowski <mtjm@mtjm.eu>
+# Copyright (C) 2012-2016 Luke Shumaker <lukeshu@sbcglobal.net>
+#
+# License: GNU GPLv2+
+#
+# This file is part of Parabola.
+#
+# Parabola 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.
+#
+# Parabola 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 Parabola. If not, see <http://www.gnu.org/licenses/>.
+
+# HACKING: if a command is added or removed, it must be changed in 4 places:
+# - the usage() text
+# - the commands=() array
+# - the case statement in main() that checks the number of arguments
+# - the case statement in main() that runs them
+
+. "$(librelib conf)"
+load_files chroot
+
+. libremessages
+
+shopt -s nullglob
+umask 0022
+
+################################################################################
+# Wrappers for files in ${pkglibexecdir}/chroot/ #
+################################################################################
+
+readonly _arch_nspawn="$(librelib chroot/arch-nspawn)"
+readonly _mkarchroot="$(librelib chroot/mkarchroot)"
+readonly _makechrootpkg="$(librelib chroot/makechrootpkg.sh)"
+
+arch_nspawn_flags=()
+sysd_nspawn_flags=()
+
+hack_arch_nspawn_flags() {
+ local copydir="$1"
+
+ local makepkg_conf="$copydir/etc/makepkg.conf"
+
+ OPTIND=1
+ set -- ${arch_nspawn_flags+"${arch_nspawn_flags[@]}"}
+ while getopts 'hC:M:c:f:s' arg; do
+ case "$arg" in
+ M) makepkg_conf="$OPTARG" ;;
+ *) :;;
+ esac
+ done
+
+ # Detect the architecture of the chroot
+ local CARCH
+ if [[ -f "$makepkg_conf" ]]; then
+ eval $(grep '^CARCH=' "$makepkg_conf")
+ else
+ CARCH="$(uname -m)"
+ fi
+
+ if [[ "$CARCH" == armv7h ]] && ! setarch armv7l 2>/dev/null; then
+ # We're running an ARM chroot on a non-ARM processor
+
+ # Make sure that qemu-static is set up with binfmt_misc
+ if [[ $(grep -xF \
+ -e 'enabled'\
+ -e 'interpreter /usr/bin/qemu-arm-static' \
+ /proc/sys/fs/binfmt_misc/arm 2>/dev/null |wc -l) -lt 2 ]]; then
+ error 'Cannot cross-compile for ARM on x86'
+ plain 'This requires a binfmt_misc entry for qemu-arm-static,'
+ plain 'which is provided by the %s package.' binfmt-qemu-static
+ plain 'If you have this, you may need to restart %s.' systemd-binfmt.service
+ return 1
+ fi
+
+ # Let qemu/binfmt_misc do its thing
+ arch_nspawn_flags+=(-f /usr/bin/qemu-arm-static -s)
+
+ # The -any packages are built separately for ARM from
+ # x86, so if we use the same CacheDir as the x86 host,
+ # then there will be PGP errors.
+ mkdir -p /var/cache/pacman/pkg-arm
+ arch_nspawn_flags+=(-c /var/cache/pacman/pkg-arm)
+ fi
+}
+
+# Usage: arch-nspawn $copydir $cmd...
+arch-nspawn() {
+ local copydir=$1; shift
+ local cmd=("$@")
+
+ local arch_nspawn_flags=(${arch_nspawn_flags+"${arch_nspawn_flags[@]}"})
+ hack_arch_nspawn_flags "$copydir"
+
+ "$_arch_nspawn" \
+ ${arch_nspawn_flags+"${arch_nspawn_flags[@]}"} \
+ "$copydir" \
+ ${sysd_nspawn_flags+"${sysd_nspawn_flags[@]}"} \
+ -- \
+ "${cmd[@]}"
+}
+
+# Usage: mkarchroot $copydir $pkgs...
+mkarchroot() {
+ local copydir=$1; shift
+ local pkgs=("$@")
+
+ local arch_nspawn_flags=(${arch_nspawn_flags+"${arch_nspawn_flags[@]}"})
+ hack_arch_nspawn_flags "$copydir"
+
+ unshare -m "$_mkarchroot" \
+ ${arch_nspawn_flags+"${arch_nspawn_flags[@]}"} \
+ "$copydir" \
+ "${pkgs[@]}"
+}
+
+# Usage: _makechrootpkg $function $arguments...
+# Don't load $_makechrootpkg directly because it doesn't work with -euE
+_makechrootpkg() (
+ set +euE
+ . "$_makechrootpkg"
+ "$@"
+)
+
+################################################################################
+# Utility functions #
+################################################################################
+
+# Usage: make_empty_repo $copydir
+make_empty_repo() {
+ local copydir=$1
+ mkdir -p "${copydir}/repo"
+ bsdtar -czf "${copydir}/repo/repo.db.tar.gz" -T /dev/null
+ ln -s "repo.db.tar.gz" "${copydir}/repo/repo.db"
+}
+
+# Usage: chroot_add_to_local_repo $copydir $pkgfiles...
+chroot_add_to_local_repo() {
+ local copydir=$1; shift
+ mkdir -p "$copydir/repo"
+ local pkgfile
+ for pkgfile in "$@"; do
+ cp "$pkgfile" "$copydir/repo"
+ pushd "$copydir/repo" >/dev/null
+ repo-add repo.db.tar.gz "${pkgfile##*/}"
+ popd >/dev/null
+ done
+}
+
+# Print code to set $rootdir and $copydir; blank them on error
+calculate_directories() {
+ # Don't assume that CHROOTDIR or CHROOT are set,
+ # but assume that COPY is set.
+ local rootdir copydir
+
+ if [[ -n ${CHROOTDIR:-} ]] && [[ -n ${CHROOT:-} ]]; then
+ rootdir="${CHROOTDIR}/${CHROOT}/root"
+ else
+ rootdir=''
+ fi
+
+ if [[ ${COPY:0:1} = / ]]; then
+ copydir=$COPY
+ elif [[ -n ${CHROOTDIR:-} ]] && [[ -n ${CHROOT:-} ]]; then
+ copydir="${CHROOTDIR}/${CHROOT}/${COPY}"
+ else
+ copydir=''
+ fi
+
+ declare -p rootdir
+ declare -p copydir
+}
+
+check_mountpoint() {
+ local file=$1
+ local mountpoint="$(df -P "$file"|sed '1d;s/.*\s//')"
+ local mountopts=($(LC_ALL=C mount|awk "{ if (\$3==\"$mountpoint\") { gsub(/[(,)]/, \" \", \$6); print \$6 } }"))
+ ! in_array nosuid "${mountopts[@]}" && ! in_array noexec "${mountopts[@]}"
+}
+
+################################################################################
+# Main program #
+################################################################################
+
+usage() {
+ eval "$(calculate_directories)"
+ print "Usage: %s [OPTIONS] COMMAND [ARGS...]" "${0##*/}"
+ print 'Interacts with an archroot (arch chroot).'
+ echo
+ prose 'This is configured with `chroot.conf`, either in
+ `/etc/libretools.d/`, or `$XDG_CONFIG_HOME/libretools/`.
+ The variables you may set are $CHROOTDIR, $CHROOT, and
+ $CHROOTEXTRAPKG.'
+ echo
+ prose 'There may be multiple chroots; they are stored in $CHROOTDIR.'
+ echo
+ prose 'Each chroot is named; the default is configured with $CHROOT.'
+ echo
+ prose 'Each named chroot has a master clean copy (named `root`), and any
+ number of other named copies; the copy used by default is the
+ current username (or $SUDO_USER, or `copy` if root).'
+ echo
+ prose 'The full path to the chroot copy is "$CHROOTDIR/$CHROOT/$COPY",
+ unless the copy name is manually specified as an absolute path,
+ in which case, that path is used.'
+ echo
+ prose 'The current settings for the above variables are:'
+ printf ' CHROOTDIR : %s\n' "${CHROOTDIR:-$(_ 'ERROR: NO SETTING')}"
+ printf ' CHROOT : %s\n' "${CHROOT:-$(_ 'ERROR: NO SETTING')}"
+ printf ' COPY : %s\n' "$COPY"
+ printf ' rootdir : %s\n' "${rootdir:-$(_ 'ERROR')}"
+ printf ' copydir : %s\n' "${copydir:-$(_ 'ERROR')}"
+ echo
+ prose 'If the chroot or copy does not exist, it will be created
+ automatically. A chroot by default contains the packages in the
+ group "base-devel" and any packages named in $CHROOTEXTRAPKG.
+ Unless the `-C` or `-M` flags are used, the configuration files
+ that this program installs are the stock versions supplied in the
+ packages, not the versions from your host system. Other tools
+ (such as libremakepkg) may alter the configuration.'
+ echo
+ prose 'This command will make the following configuration changes in the
+ chroot:'
+ bullet 'overwrite `/etc/libretools.d/chroot.conf`'
+ bullet 'overwrite `/etc/pacman.d/mirrorlist`'
+ bullet 'set `CacheDir` in `/etc/pacman.conf`'
+ prose 'If a new `pacman.conf` is inserted with the `-C` flag, the change
+ is made after the file is copied in; the `-C` flag doesn'"'"'t
+ stop the change from being effective.'
+ echo
+ prose 'The processor architecture of the chroot is determined
+ by the by `CARCH` variable in the `/etc/makepkg.conf`
+ file inside of the chroot.'
+ echo
+ prose 'The `-A CARCH` flag is *almost* simply an alias for'
+ printf ' %s\n' \
+ '-C "/usr/share/pacman/defaults/pacman.conf.$CARCH" \' \
+ '-M "/usr/share/pacman/defaults/makepkg.conf.$CARCH"'
+ prose 'However, before doing that, it actually makes a temporary copy of
+ `pacman.conf`, and sets the `Architecture` line to match the
+ `CARCH` line in `makepkg.conf`.'
+ echo
+ prose 'Creating a copy, deleting a copy, or syncing a copy can be fairly
+ slow; but are very fast if $CHROOTDIR is on a btrfs partition.'
+ echo
+ print 'Options:'
+ flag "-n <$(_ CHROOT)>" 'Name of the chroot to use'
+ flag "-l <$(_ COPY)>" 'Name of, or absolute path to, the copy to use'
+ flag '-N' 'Disable networking in the chroot'
+ flag "-C <$(_ FILE)>" 'Copy this file to `$copydir/etc/pacman.conf`'
+ flag "-M <$(_ FILE)>" 'Copy this file to `$copydir/etc/makepkg.conf`'
+ flag "-A <$(_ CARCH)>" 'Set the architecture of the copy; simply an
+ alias for the `-C` and `-M` flags, see above.'
+ flag "-w <$(_ 'PATH[:PATH]')>" 'Bind mount a file or directory, read/write'
+ flag "-r <$(_ 'PATH[:PATH]')>" 'Bind mount a file or directory, read-only'
+ echo
+ print 'Commands:'
+ print ' Create/copy/delete:'
+ flag 'noop|make' 'Do not do anything, but still creates the chroot
+ copy if it does not exist'
+ flag 'sync' 'Sync the copy with the clean (`root`) copy'
+ flag 'delete' 'Delete the chroot copy'
+ print ' Dealing with packages:'
+ flag "install-file $(_ FILES...)" 'Like `pacman -U FILES...`'
+ flag "install-name $(_ NAMES...)" 'Like `pacman -S NAMES...`'
+ flag 'update' 'Like `pacman -Syu`'
+ flag 'clean-pkgs' 'Remove all packages from the chroot copy that
+ are not in base-devel, $CHROOTEXTRAPKG, or named
+ as a dependency in the file `/startdir/PKGBUILD`
+ in the chroot copy'
+ print ' Other:'
+ flag "run $(_ CMD...)" 'Run CMD in the chroot copy'
+ flag 'enter' 'Enter an interactive shell in the chroot copy'
+ flag 'clean-repo' 'Clean /repo in the chroot copy'
+ flag 'help' 'Show this message'
+}
+readonly commands=(
+ noop make sync delete
+ install-file install-name update clean-pkgs
+ run enter clean-repo help
+)
+
+# Globals: $CHROOTDIR, $CHROOT, $COPY, $rootdir and $copydir
+main() {
+ COPY=$LIBREUSER
+ [[ $COPY != root ]] || COPY=copy
+
+ local mode=enter
+ while getopts 'n:l:NC:M:A:w:r:' opt; do
+ case $opt in
+ n) CHROOT=$OPTARG;;
+ l) COPY=$OPTARG;;
+ N) sysd_nspawn_flags+=(--private-network);;
+ C|M) arch_nspawn_flags+=(-$opt "$OPTARG");;
+ A)
+ if ! [[ -f "/usr/share/pacman/defaults/pacman.conf.$OPTARG" && -f "/usr/share/pacman/defaults/makepkg.conf.$OPTARG" ]]; then
+ error 'Unsupported architecture: %s' "$OPTARG"
+ plain 'See the files in %q for valid architectures.' /usr/share/pacman/defaults/
+ return 1;
+ fi
+ trap 'rm -f -- "$tmppacmanconf"' EXIT
+ tmppacmanconf="$(mktemp --tmpdir librechroot-pacman.conf.XXXXXXXXXX)"
+ < "/usr/share/pacman/defaults/pacman.conf.$OPTARG" sed -r "s|^#?\\s*Architecture.+|Architecture = ${OPTARG}|g" > "$tmppacmanconf"
+ arch_nspawn_flags+=(
+ -C "$tmppacmanconf"
+ -M "/usr/share/pacman/defaults/makepkg.conf.$OPTARG"
+ );;
+ w) sysd_nspawn_flags+=("--bind=$OPTARG");;
+ r) sysd_nspawn_flags+=("--bind-ro=$OPTARG");;
+ *) usage >&2; return 1;;
+ esac
+ done
+ shift $(($OPTIND - 1))
+ if [[ $# -lt 1 ]]; then
+ error "Must specify a command"
+ usage >&2
+ return 1
+ fi
+ mode=$1
+ if ! in_array "$mode" "${commands[@]}"; then
+ error "Unrecognized command: %s" "$mode"
+ usage >&2
+ return 1
+ fi
+ shift
+ case "$mode" in
+ noop|make|sync|delete|update|enter|clean-pkgs|clean-repo)
+ if [[ $# -gt 0 ]]; then
+ error 'Command `%s` does not take any arguments: %s' "$mode" "$*"
+ usage >&2
+ return 1
+ fi
+ :;;
+ install-file)
+ if [[ $# -lt 1 ]]; then
+ error 'Command `%s` requires at least one file' "$mode"
+ usage >&2
+ return 1
+ else
+ local missing=()
+ local file
+ for file in "$@"; do
+ if ! [[ -f $file ]]; then
+ missing+=("$file")
+ fi
+ done
+ if [[ ${#missing[@]} -gt 0 ]]; then
+ error "%s: file(s) not found: %s" "$mode" "${missing[*]}"
+ return 1
+ fi
+ fi
+ :;;
+ install-name)
+ if [[ $# -lt 1 ]]; then
+ error 'Command `%s` requires at least one package name' "$mode"
+ usage >&2
+ return 1
+ fi
+ :;;
+ run)
+ if [[ $# -lt 1 ]]; then
+ error 'Command `%s` requires at least one argument' "$mode"
+ usage >&2
+ return 1
+ fi
+ :;;
+ esac
+
+
+ if [[ $mode == help ]]; then
+ usage
+ return 0
+ fi
+
+ check_vars chroot CHROOTDIR CHROOT
+ eval "$(calculate_directories)"
+
+ readonly LIBREUSER LIBREHOME
+ readonly CHROOTDIR CHROOT COPY
+ readonly rootdir copydir
+ readonly mode
+
+ ########################################################################
+
+ if (( EUID )); then
+ error "This program must be run as root."
+ return 1
+ fi
+
+ umask 0022
+
+ # XXX: SYSTEMD-STDIN HACK
+ if ! [[ -t 0 ]]; then
+ error "Input is not a TTY"
+ plain "https://labs.parabola.nu/issues/431"
+ plain "https://bugs.freedesktop.org/show_bug.cgi?id=70290"
+ prose "Due to a bug in systemd-nspawn, redirecting stdin is not
+ supported." >&2
+ return 1
+ fi
+
+ # Keep this lock for as long as we are running
+ # Note that '9' is the same FD number as in mkarchroot et al.
+ lock 9 "$copydir.lock" \
+ "Waiting for existing lock on chroot copy to be released: [%s]" "$COPY"
+
+ if [[ $mode != delete ]]; then
+ if ! check_mountpoint "$copydir.lock"; then
+ error "Chroot copy is mounted with nosuid or noexec options: [%s]" "$COPY"
+ return 1
+ fi
+
+ if [[ ! -d $rootdir ]]; then
+ msg "Creating 'root' copy for chroot [%s]" "$CHROOT"
+ mkarchroot "$rootdir" base-devel
+ make_empty_repo "$rootdir"
+ fi
+
+ if [[ ! -d $copydir ]] || [[ $mode == sync ]]; then
+ msg "Syncing copy [%s] with root copy" "$COPY"
+ _makechrootpkg sync_chroot "$CHROOTDIR/$CHROOT" "$COPY"
+ fi
+
+ # Note: the in-chroot pkgconfdir is non-configurable, this is
+ # intentionally hard-coded.
+ mkdir -p "$copydir/etc/libretools.d"
+ {
+ if [[ ${#CHROOTEXTRAPKG[*]} -eq 0 ]]; then
+ echo 'CHROOTEXTRAPKG=()'
+ else
+ printf 'CHROOTEXTRAPKG=('
+ printf '%q ' "${CHROOTEXTRAPKG[@]}"
+ printf ')\n'
+ fi
+ } > "$copydir"/etc/libretools.d/chroot.conf
+
+ # "touch" the chroot first
+ # this will
+ # - overwrite '/etc/pacman.d/mirrorlist'"
+ # - set 'CacheDir' in \`/etc/pacman.conf'"
+ # - apply -C or -M flags
+ arch-nspawn "$copydir" true
+ trap EXIT # clear the trap to remove the tmp pacman.conf from -A
+ arch_nspawn_flags=() # XXX dirty hack, don't apply -C or -M again
+ fi
+
+ ########################################################################
+
+ case "$mode" in
+ # Creat/copy/delete
+ noop|make|sync) :;;
+ delete)
+ if [[ -d $copydir ]]; then
+ _makechrootpkg delete_chroot "$copydir"
+ fi
+ ;;
+
+ # Dealing with packages
+ install-file)
+ _makechrootpkg install_packages "$copydir" "$@"
+ chroot_add_to_local_repo "$copydir" "$@"
+ ;;
+ install-name)
+ arch-nspawn "$copydir" pacman -Sy -- "$@"
+ ;;
+ update)
+ arch-nspawn "$copydir" pacman -Syu --noconfirm
+ ;;
+ clean-pkgs)
+ trap "rm -f -- $(printf '%q ' "$copydir"/{bin/chcleanup,chrootexec})" EXIT
+ install -m755 "$(librelib chroot/chcleanup)" "$copydir/bin/chcleanup"
+ printf '%s\n' \
+ '#!/bin/bash' \
+ 'mkdir -p /startdir' \
+ 'cd /startdir' \
+ '/bin/chcleanup' \
+ > "$copydir/chrootexec"
+ chmod 755 "$copydir/chrootexec"
+ arch-nspawn "$copydir" /chrootexec
+ ;;
+
+ # Other
+ run)
+ arch-nspawn "$copydir" "$@"
+ ;;
+ enter)
+ arch-nspawn "$copydir" bash
+ ;;
+ clean-repo)
+ rm -rf "${copydir}"/repo/*
+ make_empty_repo "$copydir"
+ ;;
+ esac
+}
+
+main "$@"
diff --git a/src/chroot-tools/libremakepkg b/src/chroot-tools/libremakepkg
new file mode 100755
index 0000000..a226e38
--- /dev/null
+++ b/src/chroot-tools/libremakepkg
@@ -0,0 +1,289 @@
+#!/usr/bin/env bash
+set -euE
+# libremakepkg
+
+# Copyright (C) 2010-2012 Nicolás Reynolds <fauno@parabola.nu>
+# Copyright (C) 2010-2012 Joshua Ismael Haase Hernández (xihh) <hahj87@gmail.com>
+# Copyright (C) 2012 Michał Masłowski <mtjm@mtjm.eu>
+# Copyright (C) 2012-2015 Luke Shumaker <lukeshu@sbcglobal.net>
+#
+# License: GNU GPLv2+
+#
+# This file is part of Parabola.
+#
+# Parabola 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.
+#
+# Parabola 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 Parabola. If not, see <http://www.gnu.org/licenses/>.
+
+. "$(librelib conf)"
+. "$(librelib messages)"
+. "$(librelib chroot/makechrootpkg.sh)"
+
+set -o pipefail
+shopt -s nullglob
+umask 0022
+
+# Global variables:
+readonly _indent="$(librelib chroot/indent)"
+readonly INCHROOT=$([[ -f /.arch-chroot ]] && echo true || echo false)
+NONET=true # can be changed with the -N flag
+# {PKG,SRC,SRCPKG,LOG}DEST set at runtime by makepkg.conf
+# MAKEFLAGS, PACKAGER set at runtime by makepkg.conf
+# LIBREUSER, LIBREHOME are set by conf.sh
+librechroot_flags=()
+
+# Hooks ########################################################################
+
+hook_pre_build=(:)
+hook_post_build=(:)
+hook_check_pkgbuild=(:)
+hook_check_pkg=(:)
+. "$(librelib chroot/hooks-chcleanup.sh)"
+. "$(librelib chroot/hooks-check.sh)"
+. "$(librelib chroot/hooks-distcc.sh)"
+
+# Boring/mundane functions #####################################################
+
+indent() {
+ "$_indent" ' | '
+}
+
+# Usage: exit_copy $copydir $src_owner
+# End immediately, but copy log files out
+exit_copy() {
+ local copydir=$1
+ local src_owner=$2
+ if ! $INCHROOT; then
+ msg "Copying log and package files out of the chroot..."
+ move_products "$copydir" "$src_owner"
+ fi
+}
+
+# Usage; run_hook $hookname $args...
+run_hook() {
+ local hookname=$1; shift
+ local hookvar="hook_${hookname}[@]"
+
+ local fails=()
+ for hook in "${!hookvar}"; do
+ "$hook" "$@" || fails+=("$hook")
+ done |& indent
+
+ if [[ ${#fails[@]} -gt 0 ]]; then
+ error "Failure(s) in %s: %s" "$hookname" "${fails[*]}"
+ return 1
+ else
+ return 0
+ fi
+}
+
+# Usage: add_to_local_repo $copydir $pkgfiles...
+add_to_local_repo() {
+ local copydir=$1; shift
+ mkdir -p "$copydir/repo"
+ local pkgfile
+ for pkgfile in "$@"; do
+ cp "$pkgfile" "$copydir/repo"
+ pushd "$copydir/repo" >/dev/null
+ repo-add repo.db.tar.gz "${pkgfile##*/}"
+ popd >/dev/null
+ done
+}
+
+hook_post_build+=('cleanup')
+cleanup() {
+ local copydir=$1
+ rm -f -- "$copydir"/chroot{prepare,build}
+}
+
+build() (
+ local copydir=$1
+ local repack=$2
+ local makepkg_args=("${@:3}")
+
+ local run_ynet=()
+ local run_nnet=()
+ if $INCHROOT; then
+ run_ynet=(unshare)
+ run_nnet=(unshare -n)
+ else
+ run_ynet=(librechroot "${librechroot_flags[@]}" run)
+ run_nnet=(librechroot "${librechroot_flags[@]}" -N run)
+ fi
+ $NONET || run_nnet=("${run_ynet[@]}")
+
+ prepare_chroot "$copydir" "$LIBREHOME" "$repack" false
+ "${run_ynet[@]}" /chrootprepare "${makepkg_args[@]}" |& indent
+ run_hook pre_build "$copydir"
+ trap "run_hook post_build $(printf '%q' "$copydir")" EXIT
+ "${run_nnet[@]}" /chrootbuild "${makepkg_args[@]}" |& indent
+)
+
+# The main program #############################################################
+
+usage() {
+ print "Usage: %s [options]" "${0##*/}"
+ print 'This program will build your package.'
+ echo
+ prose 'If run from outside of a chroot, command will make the following
+ configuration changes in the chroot:'
+ bullet 'whatever changes `librechroot` makes.'
+ bullet 'set `{PKG,SRC,SRCPKG,LOG}DEST` in `/etc/makepkg.conf`'
+ bullet 'set `PACKAGER` in `/etc/makepkg.conf` to reflect the value
+ outside of the chroot.'
+ bullet '(maybe) delete `/build/.makepkg.conf`'
+ bullet '(maybe) delete `/build/.ssh/config`'
+ prose 'If run from inside of a chroot, this command will:'
+ bullet '(maybe) delete `~/.makepkg.conf`'
+ bullet '(maybe) delete `~/.ssh/config`'
+ prose 'The above "maybe"s happen as part of the workarounds to make
+ distcc work in a network-less environment. They will happen if
+ both `socat` and `distcc` are installed in the chroot.'
+ echo
+ prose 'The `-n` and `-l` options behave identically to librechroot, see
+ the documentation there.'
+ echo
+ print 'Options:'
+ print ' %s options:' librechroot
+ flag "-n <$(_ CHROOT)>" 'Name of the chroot to use'
+ flag "-l <$(_ COPY)>" 'Name of, or absolute path to, the chroot copy to use'
+ flag "-w <$(_ 'PATH[:PATH]')>" 'Bind mount a file or directory, read/write'
+ flag "-r <$(_ 'PATH[:PATH]')>" 'Bind mount a file or directory, read-only'
+ print ' %s options:' libremakepkg
+ flag '-N' "Don't disable networking during build() and
+ package(). PLEASE don't use this unless you
+ have a special reason, its use is a violation
+ of Parabola policy."
+ flag '-R' 'Repackage contents of the package without rebuilding'
+ flag '-h' 'Show this message'
+}
+
+# Convenience method for use in option parsing
+err_chflag() {
+ local flag=$1
+ error 'The -%s flag does not make sense inside of a chroot' "$flag"
+ return 1
+}
+
+main() {
+ # Initial variable values ##############################################
+ local copy=$([[ $LIBREUSER == root ]] && echo copy || echo "$LIBREUSER")
+ local makepkg_args=(-s --noconfirm -L)
+ local repack=false
+ local chroot=''
+
+ # Parse command line options ###########################################
+ while getopts 'n:l:w:r:NRh' flag ; do
+ case "${flag}" in
+ n) if $INCHROOT; then err_chflag "$flag"; else
+ chroot=$OPTARG; fi;;
+ l) if $INCHROOT; then err_chflag "$flag"; else
+ copy=$OPTARG; fi;;
+ w|r) if $INCHROOT; then err_chflag "$flag"; else
+ librechroot_flags+=(-$flag "$OPTARG"); fi;;
+ N) NONET=false;;
+ R) repack=true; makepkg_args+=(-R);;
+ h) usage; return 0;;
+ *) usage >&2; return 1;;
+ esac
+ done
+ shift $(($OPTIND - 1))
+ if [[ $# != 0 ]]; then
+ error 'Extra arguments: %s' "$*"
+ return 1
+ fi
+
+ # Resolve the chroot path ##############################################
+ local copydir
+ if $INCHROOT; then
+ copydir='/'
+ else
+ load_files chroot
+ check_vars chroot CHROOTDIR CHROOT
+ [[ -z ${chroot} ]] || CHROOT=$chroot
+ if [[ ${copy:0:1} = / ]]; then
+ copydir=$copy
+ else
+ copydir="${CHROOTDIR}/${CHROOT}/${copy}"
+ fi
+ unset CHROOTDIR CHROOTEXTRAPKG
+ fi
+ unset chroot
+
+ # Load makepkg configuration ###########################################
+ # Note that all of these are globals
+ PKGDEST="$(get_var makepkg PKGDEST "$PWD")"
+ SRCDEST="$(get_var makepkg SRCDEST "$PWD")"
+ SRCPKGDEST="$(get_var makepkg SRCPKGDEST "$PWD")"
+ LOGDEST="$(get_var makepkg LOGDEST "$PWD")"
+ MAKEFLAGS="$(get_var makepkg MAKEFLAGS '')"
+ PACKAGER="$(get_var makepkg PACKAGER '')"
+
+ # Quick sanity check ###################################################
+
+ if (( EUID )); then
+ error "This program must be run as root"
+ exit 1
+ fi
+
+ if [[ ! -f PKGBUILD ]]; then
+ # This is the message used by makepkg
+ error "PKGBUILD does not exist."
+ exit 1
+ fi
+
+ # Make sure that the various *DEST directories exist
+ mkdir -p -- "$PKGDEST" "$SRCDEST" "$SRCPKGDEST" "$LOGDEST"
+
+ # OK, we are starting now ##############################################
+
+ if $INCHROOT; then
+ lock 9 "/build/.lock" \
+ "Waiting for existing lock on build directory to be released"
+ else
+ librechroot_flags+=(
+ -r "$PWD:/startdir_host"
+ -r "$SRCDEST:/srcdest_host"
+ -n "$CHROOT"
+ -l "$copy"
+ )
+
+ # Obtain a lock on the chroot
+ lock 9 "$copydir.lock" \
+ "Waiting for existing lock on chroot copy to be released: [%s]" "$copy"
+ # Create the chroot if it does not exist
+ msg 'Initializing the chroot...'
+ librechroot "${librechroot_flags[@]}" make |& indent
+ fi
+
+ # Set target CARCH
+ # note that we waited until after locking/creating the chroot to do this
+ export CARCH="$(MAKEPKG_CONF=$copydir/etc/makepkg.conf get_var makepkg CARCH)"
+
+ # Pre-build
+ msg 'Starting pre-build activities...'
+ run_hook check_pkgbuild
+ msg 'Downloading sources...'
+ download_sources "$copydir" "$LIBREUSER" |& indent
+
+ # Build
+ msg 'Starting to build the package...'
+ trap "exit_copy '$copydir' '$LIBREUSER'" EXIT
+ build "$copydir" "$repack" "${makepkg_args[@]}"
+
+ # Post-build
+ msg 'Starting post-build activities...'
+ run_hook check_pkg
+ add_to_local_repo "$copydir" "$copydir"/pkgdest/*.pkg.tar* |& indent
+}
+
+main "$@"
diff --git a/src/chroot-tools/makechrootpkg.sh.patch b/src/chroot-tools/makechrootpkg.sh.patch
new file mode 100644
index 0000000..d5c6fca
--- /dev/null
+++ b/src/chroot-tools/makechrootpkg.sh.patch
@@ -0,0 +1,342 @@
+--- makechrootpkg.sh.in 2016-02-08 17:17:11.848386338 -0500
++++ makechrootpkg.sh.ugly 2016-04-14 22:01:05.923288181 -0400
+@@ -1,4 +1,6 @@
+ #!/bin/bash
++# License: GNU GPLv2
++#
+ # 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; version 2 of the License.
+@@ -12,6 +14,7 @@
+
+ shopt -s nullglob
+
++init_variables() {
+ default_makepkg_args=(-s --noconfirm -L --holdver)
+ makepkg_args=("${default_makepkg_args[@]}")
+ repack=false
+@@ -29,9 +32,10 @@
+ bindmounts_rw=()
+
+ copy=$USER
+-[[ -n $SUDO_USER ]] && copy=$SUDO_USER
++[[ -n ${SUDO_USER:-} ]] && copy=$SUDO_USER
+ [[ -z "$copy" || $copy = root ]] && copy=copy
+ src_owner=${SUDO_USER:-$USER}
++}
+
+ usage() {
+ echo "Usage: ${0##*/} [options] -r <chrootdir> [--] [makepkg args]"
+@@ -68,28 +72,51 @@
+ }
+
+ # {{{ functions
++# Usage: load_vars $makepkg_conf
++# Globals:
++# - SRCDEST
++# - SRCPKGDEST
++# - PKGDEST
++# - LOGDEST
++# - MAKEFLAGS
++# - PACKAGER
+ load_vars() {
+ local makepkg_conf="$1" var
+
+ [[ -f $makepkg_conf ]] || return 1
+
+ for var in {SRC,SRCPKG,PKG,LOG}DEST MAKEFLAGS PACKAGER; do
+- [[ -z ${!var} ]] && eval $(grep "^${var}=" "$makepkg_conf")
++ [[ -z ${!var:-} ]] && eval $(grep "^${var}=" "$makepkg_conf")
+ done
+
+ return 0
+ }
+
+-create_chroot() {
+- # Lock the chroot we want to use. We'll keep this lock until we exit.
+- lock 9 "$copydir.lock" "Locking chroot copy [%s]" "$copy"
++# Usage: sync_chroot $CHROOTDIR/$CHROOT <$CHROOTCOPY|$copydir>
++sync_chroot() {
++ local chrootdir=$1
++ local copy=$2
++ local copydir=''
++ if [[ ${copy:0:1} = / ]]; then
++ copydir=$copy
++ else
++ copydir="$chrootdir/$copy"
++ fi
++
++ if [[ "$chrootdir/root" -ef "$copydir" ]]; then
++ error 'Cannot sync copy with itself: %s' "$copydir"
++ return 1
++ fi
++
++ # Detect chrootdir filesystem type
++ local chroottype=$(stat -f -c %T "$chrootdir")
+
+- if [[ ! -d $copydir ]] || $clean_first; then
+ # Get a read lock on the root chroot to make
+ # sure we don't clone a half-updated chroot
+- slock 8 "$chrootdir/root.lock" "Locking clean chroot"
++ slock 8 "$chrootdir/root.lock" \
++ "Locking clean chroot [%s]" "$chrootdir/root"
+
+- stat_busy "Creating clean working copy [%s]" "$copy"
++ stat_busy "Synchronizing chroot copy [%s] -> [%s]" "$chrootdir/root" "$copydir"
+ if [[ "$chroottype" == btrfs ]] && ! mountpoint -q "$copydir"; then
+ if [[ -d $copydir ]]; then
+ btrfs subvolume delete "$copydir" >/dev/null ||
+@@ -105,14 +132,18 @@
+
+ # Drop the read lock again
+ lock_close 8
+- fi
+
+ # Update mtime
+ touch "$copydir"
+ }
+
+-clean_temporary() {
+- stat_busy "Removing temporary copy [%s]" "$copy"
++# Usage: delete_chroot $copydir
++delete_chroot() {
++ local copydir=$1
++ # Detect chrootdir filesystem type
++ local chroottype=$(stat -f -c %T "$copydir")
++
++ stat_busy "Removing chroot copy [%s]" "$copydir"
+ if [[ "$chroottype" == btrfs ]] && ! mountpoint -q "$copydir"; then
+ btrfs subvolume delete "$copydir" >/dev/null ||
+ die "Unable to delete subvolume %s" "$copydir"
+@@ -127,9 +158,14 @@
+ stat_done
+ }
+
++# Usage: install_packages $copydir $pkgs...
+ install_packages() {
++ local copydir=$1
++ local install_pkgs=("${@:2}")
++ declare -i ret=0
+ local pkgname
+
++ local install_pkg
+ for install_pkg in "${install_pkgs[@]}"; do
+ pkgname="${install_pkg##*/}"
+ cp "$install_pkg" "$copydir/$pkgname"
+@@ -142,11 +178,19 @@
+ rm "$copydir/$pkgname"
+ done
+
+- # If there is no PKGBUILD we are done
+- [[ -f PKGBUILD ]] || exit $ret
++ return $ret
+ }
+
++# Usage: prepare_chroot $copydir $HOME $repack $run_namcap
++# Globals:
++# - MAKEFLAGS
++# - PACKAGER
+ prepare_chroot() {
++ local copydir=$1
++ local USER_HOME=$2
++ local repack=$3
++ local run_namcap=$4
++
+ $repack || rm -rf "$copydir/build"
+
+ mkdir -p "$copydir/build"
+@@ -193,12 +237,12 @@
+ printf 'builduser:x:%d:100:builduser:/:/usr/bin/nologin\n' "$builduser_uid" >>"$copydir/etc/passwd"
+ chown -R "$builduser_uid" "$copydir"/{build,pkgdest,srcpkgdest,logdest,srcdest,startdir}
+
+- if [[ -n $MAKEFLAGS ]]; then
++ if [[ -n ${MAKEFLAGS:-} ]]; then
+ sed -i '/^MAKEFLAGS=/d' "$copydir/etc/makepkg.conf"
+ echo "MAKEFLAGS='${MAKEFLAGS}'" >> "$copydir/etc/makepkg.conf"
+ fi
+
+- if [[ -n $PACKAGER ]]; then
++ if [[ -n ${PACKAGER:-} ]]; then
+ sed -i '/^PACKAGER=/d' "$copydir/etc/makepkg.conf"
+ echo "PACKAGER='${PACKAGER}'" >> "$copydir/etc/makepkg.conf"
+ fi
+@@ -211,10 +255,25 @@
+ chmod 440 "$copydir/etc/sudoers.d/builduser-pacman"
+ fi
+
++ if ! grep -q '^\[repo\]' "$copydir/etc/pacman.conf"; then
++ local line=$(grep -n '^\[' "$copydir/etc/pacman.conf" |grep -Fv ':[options]'|sed 's/:.*//;1q')
++ local ins='[repo]
++SigLevel = Optional TrustAll
++Server = file:///repo
++'
++ sed -i "${line}i${ins//$'\n'/\\n}" "$copydir/etc/pacman.conf"
++ fi
++
+ # This is a little gross, but this way the script is recreated every time in the
+ # working copy
+ {
+ printf '#!/bin/bash\n'
++ declare -f _chrootprepare
++ printf '_chrootprepare "$@"\n'
++ } > "$copydir/chrootprepare"
++ chmod +x "$copydir/chrootprepare"
++ {
++ printf '#!/bin/bash\n'
+ declare -f _chrootbuild
+ printf '_chrootbuild "$@" || exit\n'
+
+@@ -231,13 +290,19 @@
+ chmod +x "$copydir/chrootbuild"
+ }
+
++# Usage: download_sources $copydir $src_owner
++# Globals:
++# - SRCDEST
+ download_sources() {
++ local copydir=$1
++ local src_owner=$2
++
+ local builddir="$(mktemp -d)"
+ chmod 1777 "$builddir"
+
+ # Ensure sources are downloaded
+- if [[ -n $SUDO_USER ]]; then
+- sudo -u $SUDO_USER env SRCDEST="$SRCDEST" BUILDDIR="$builddir" \
++ if [[ $USER != $src_owner ]]; then
++ sudo -u $src_owner env SRCDEST="$SRCDEST" BUILDDIR="$builddir" \
+ makepkg --config="$copydir/etc/makepkg.conf" --verifysource -o
+ else
+ ( export SRCDEST BUILDDIR="$builddir"
+@@ -247,10 +312,10 @@
+ (( $? != 0 )) && die "Could not download sources."
+
+ # Clean up garbage from verifysource
+- rm -rf $builddir
++ rm -rf "$builddir"
+ }
+
+-_chrootbuild() {
++_chrootprepare() {
+ # This function isn't run in makechrootpkg,
+ # so no global variables
+
+@@ -259,6 +324,7 @@
+ shopt -s nullglob
+
+ # XXX: Workaround makepkg disliking read-only dirs
++ rm -rf -- /srcdest/* /startdir/*
+ ln -sft /srcdest /srcdest_host/*
+ ln -sft /startdir /startdir_host/*
+
+@@ -288,15 +354,42 @@
+ exit 1
+ fi
+
+- sudo -u builduser makepkg "$@"
++ # Sync deps now, as networking may be disabled during _chrootbuild
++ cp /repo/repo.db /var/lib/pacman/sync/repo.db
++ sudo -u builduser makepkg "$@" --nobuild
++}
++
++_chrootbuild() {
++ # This function isn't run in makechrootpkg,
++ # so no global variables
++
++ . /etc/profile
++ export HOME=/build
++ shopt -s nullglob
++
++ cd /startdir
++
++ sudo -u builduser makepkg "$@" --noextract --noprepare
+ }
+
++# Usage: move_products $copydir $owner
++# Globals:
++# - PKGDEST
++# - LOGDEST
+ move_products() {
++ local copydir=$1
++ local src_owner=$2
++
++ local pkgfile
+ for pkgfile in "$copydir"/pkgdest/*; do
+ chown "$src_owner" "$pkgfile"
+ mv "$pkgfile" "$PKGDEST"
++ if [[ $PKGDEST != $PWD ]]; then
++ ln -sf "$PKGDEST/${pkgfile##*/}" .
++ fi
+ done
+
++ local l
+ for l in "$copydir"/logdest/*; do
+ [[ $l == */logpipe.* ]] && continue
+ chown "$src_owner" "$l"
+@@ -310,6 +403,9 @@
+ }
+ # }}}
+
++main() {
++init_variables
++
+ orig_argv=("$@")
+
+ while getopts 'hcur:I:l:nTD:d:' arg; do
+@@ -375,30 +471,45 @@
+ [[ -d $SRCPKGDEST ]] || SRCPKGDEST=$PWD
+ [[ -d $LOGDEST ]] || LOGDEST=$PWD
+
+-create_chroot
++# Lock the chroot we want to use. We'll keep this lock until we exit.
++lock 9 "$copydir.lock" "Locking chroot copy [%s]" "$copy"
++
++if [[ ! -d $copydir ]] || $clean_first; then
++ sync_chroot "$chrootdir" "$copy"
++fi
+
+ $update_first && arch-nspawn "$copydir" \
+ "${bindmounts_ro[@]}" "${bindmounts_rw[@]}" \
+ pacman -Syu --noconfirm
+
+-[[ -n ${install_pkgs[*]} ]] && install_packages
++if [[ -n ${install_pkgs[*]:-} ]]; then
++ install_packages "$copydir" "${install_pkgs[@]}"
++ ret=$?
++ # If there is no PKGBUILD we have done
++ [[ -f PKGBUILD ]] || exit $ret
++fi
+
+-download_sources
++download_sources "$copydir" "$src_owner"
+
+-prepare_chroot
++prepare_chroot "$copydir" "$USER_HOME" "$repack"
+
+ if arch-nspawn "$copydir" \
+ --bind-ro="$PWD:/startdir_host" \
+ --bind-ro="$SRCDEST:/srcdest_host" \
+ "${bindmounts_ro[@]}" "${bindmounts_rw[@]}" \
++ /chrootprepare "${makepkg_args[@]}" &&
++ arch-nspawn "$copydir" \
++ --bind-ro="$PWD:/startdir_host" \
++ --bind-ro="$SRCDEST:/srcdest_host" \
++ "${bindmounts_ro[@]}" "${bindmounts_rw[@]}" \
+ /chrootbuild "${makepkg_args[@]}"
+ then
+- move_products
++ move_products "$copydir" "$src_owner"
+ else
+ (( ret += 1 ))
+ fi
+
+-$temp_chroot && clean_temporary
++$temp_chroot && delete_chroot "$copydir"
+
+ if (( ret != 0 )); then
+ if $temp_chroot; then
+@@ -409,3 +520,4 @@
+ else
+ true
+ fi
++}
diff --git a/src/chroot-tools/mkarchroot.patch b/src/chroot-tools/mkarchroot.patch
new file mode 100644
index 0000000..3882ec6
--- /dev/null
+++ b/src/chroot-tools/mkarchroot.patch
@@ -0,0 +1,67 @@
+--- mkarchroot.in 2016-04-15 17:38:00.221067734 -0400
++++ mkarchroot.ugly 2016-05-09 22:36:18.284175885 -0400
+@@ -1,4 +1,6 @@
+ #!/bin/bash
++# License: GNU GPLv2
++#
+ # 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; version 2 of the License.
+@@ -14,23 +16,29 @@
+
+ working_dir=''
+
++files=()
++
+ usage() {
+ echo "Usage: ${0##*/} [options] working-dir package-list..."
+ echo ' options:'
+ echo ' -C <file> Location of a pacman config file'
+ echo ' -M <file> Location of a makepkg config file'
+ echo ' -c <dir> Set pacman cache'
++ echo ' -f <file> Copy file from the host to the chroot'
++ echo ' -s Do not run setarch'
+ echo ' -h This message'
+ exit 1
+ }
+
+ orig_argv=("$@")
+
+-while getopts 'hC:M:c:' arg; do
++while getopts 'hC:M:c:f:s' arg; do
+ case "$arg" in
+ C) pac_conf="$OPTARG" ;;
+ M) makepkg_conf="$OPTARG" ;;
+ c) cache_dir="$OPTARG" ;;
++ f) files+=("$OPTARG") ;;
++ s) nosetarch=1 ;;
+ h|?) usage ;;
+ *) error "invalid argument '%s'" "$arg"; usage ;;
+ esac
+@@ -68,6 +76,16 @@
+ chmod 0755 "$working_dir"
+ fi
+
++for file in "${files[@]}"; do
++ mkdir -p "$(dirname "$working_dir$file")"
++ cp "$file" "$working_dir$file"
++done
++
++_env=()
++while read -r varname; do
++ _env+=("$varname=${!varname}")
++done < <(declare -x | sed -r 's/^declare -x ([^=]*)=.*/\1/' | grep -i '_proxy$')
++env -i "${_env[@]}" \
+ pacstrap -GMcd ${pac_conf:+-C "$pac_conf"} "$working_dir" \
+ "${cache_dirs[@]/#/--cachedir=}" "$@" || die 'Failed to install all packages'
+
+@@ -75,7 +93,8 @@
+ echo 'LANG=C' > "$working_dir/etc/locale.conf"
+ echo "$CHROOT_VERSION" > "$working_dir/.arch-chroot"
+
+-exec arch-nspawn \
++exec "$(librelib chroot/arch-nspawn)" \
++ ${nosetarch:+-s} \
+ ${pac_conf:+-C "$pac_conf"} \
+ ${makepkg_conf:+-M "$makepkg_conf"} \
+ ${cache_dir:+-c "$cache_dir"} \