summaryrefslogtreecommitdiff
path: root/src/chroot-tools/librechroot
diff options
context:
space:
mode:
Diffstat (limited to 'src/chroot-tools/librechroot')
-rwxr-xr-xsrc/chroot-tools/librechroot132
1 files changed, 111 insertions, 21 deletions
diff --git a/src/chroot-tools/librechroot b/src/chroot-tools/librechroot
index 0b3ad43..051148d 100755
--- a/src/chroot-tools/librechroot
+++ b/src/chroot-tools/librechroot
@@ -2,9 +2,9 @@
set -euE
# librechroot
-# Copyright 2010 Nicolás Reynolds
-# Copyright 2011 Joshua Haase
-# Copyright 2012-2013 Luke Shumaker
+# Copyright (C) 2010 Nicolás Reynolds
+# Copyright (C) 2011 Joshua Haase
+# Copyright (C) 2012-2014 Luke Shumaker
#
# This file is part of Parabola.
#
@@ -21,10 +21,11 @@ set -euE
# 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 3 places:
+# 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()
+# - the case statement in main() that checks the number of arguments
+# - the case statement in main() that runs them
. $(librelib conf)
load_files chroot
@@ -88,16 +89,16 @@ usage() {
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 varibles are:'
+ 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
+ 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.
+ 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
@@ -136,8 +137,8 @@ usage() {
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 `/build/PKGBUILD` in
- the chroot copy'
+ 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'
@@ -150,7 +151,7 @@ readonly commands=(
run enter clean-repo help
)
-# set $rootdir and $copydir; blank them on error
+# 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.
@@ -174,12 +175,37 @@ calculate_directories() {
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[@]}"
+}
+
arch_nspawn_flags=()
sysd_nspawn_flags=()
arch-nspawn() {
local copydir=$1; shift
+ # XXX: SYSTEMD-STDOUT HACK
+ if [[ -t 1 ]]; then
+ cmd=("$@")
+ else
+ # This perl script is similar to `sed 's|\n|\r\n|g'`, (or, more
+ # correctly, `sed 's|$|\r|'`) but it does't line-buffer.
+ local perlcmd='
+my $size;
+my $buffer;
+while(1) {
+ $size=sysread(STDIN, $buffer, 40);
+ last if ($size < 1);
+ $buffer =~ s/\n/\r\n/g;
+ syswrite(STDOUT, $buffer);
+}'
+ cmd=(bash --noprofile --norc -c "set -o pipefail; $(printf '%q ' "$@") |& perl -e $(printf '%q' "$perlcmd")")
+ fi
+
set +u # if an array is empty, it counts as unbound
- "$_arch_nspawn" "${arch_nspawn_flags[@]}" "$copydir" "${sysd_nspawn_flags[@]}" -- "$@"
+ "$_arch_nspawn" "${arch_nspawn_flags[@]}" "$copydir" "${sysd_nspawn_flags[@]}" -- "${cmd[@]}"
set -u
}
@@ -197,22 +223,65 @@ main() {
C|M) arch_nspawn_flags+=(-$opt "$OPTARG");;
w) sysd_nspawn_flags+=("--bind=$OPTARG");;
r) sysd_nspawn_flags+=("--bind-ro=$OPTARG");;
- *) usage >/dev/stderr; return 1;;
+ *) usage >&2; return 1;;
esac
done
shift $(($OPTIND - 1))
if [[ $# -lt 1 ]]; then
error "Must specify a command"
- usage >/dev/stderr
+ usage >&2
return 1
fi
mode=$1
if ! in_array "$mode" "${commands[@]}"; then
error "Unrecognized command: %s" "$mode"
- usage >/dev/stderr
+ 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
@@ -236,11 +305,28 @@ main() {
umask 0022
+ # XXX: SYSTEMD-STDIN HACK
+ if ! [[ -t 0 ]]; then
+ error "Input is not a TTY"
+ plain "https://labs.parabola.nu/issues/420"
+ plain "https://bugs.freedesktop.org/show_bug.cgi?id=70290"
+ prose "Due to a bug in systemd-nspawn, redirecting stdin is not
+ supported. We have been able to mitigate the problems
+ with redirecting stdout, but until the bug is fixed,
+ redirecting stdin will only end in pain." >&2
+ return 1
+ fi
+
# Keep this lock 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 ! 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"
set +u # if an array is empty, it counts as unbound
@@ -256,10 +342,12 @@ main() {
mkdir -p "$copydir/etc/libretools.d"
{
- if [[ -n ${CHROOTEXTRAPKG[*]:-} ]]; then
- declare -p CHROOTEXTRAPKG | sed -r 's/declare( -.)* //'
+ if [[ ${#CHROOTEXTRAPKG[*]} -eq 0 ]]; then
+ echo 'CHROOTEXTRAPKG=()'
else
- printf 'CHROOTEXTRAPKG=()\n'
+ printf 'CHROOTEXTRAPKG=('
+ printf '%q ' "${CHROOTEXTRAPKG[@]}"
+ printf ')\n'
fi
} > "$copydir"/etc/libretools.d/chroot.conf
@@ -293,15 +381,17 @@ main() {
arch-nspawn "$copydir" pacman -Sy "$@"
;;
update)
- arch-nspawn "$copydir" pacman -Syu --noconfirm
+ # umount resolv.conf so that it can be upgraded, if nescessary
+ # this disables DNS, so fetch everything first
+ arch-nspawn "$copydir" bash -c 'pacman -Syuw --noconfirm && umount /etc/resolv.conf && pacman -Su --noconfirm'
;;
clean-pkgs)
trap "rm -f '$copydir'/bin/chcleanup '$copydir'/chrootexec" EXIT
install -m755 "$(librelib chroot/chcleanup)" "$copydir/bin/chcleanup"
printf '%s\n' \
'#!/bin/bash' \
- 'mkdir -p /build' \
- 'cd /build' \
+ 'mkdir -p /startdir' \
+ 'cd /startdir' \
'/bin/chcleanup' \
> "$copydir/chrootexec"
chmod 755 "$copydir/chrootexec"