diff options
Diffstat (limited to 'src/chroot-tools/librechroot')
-rwxr-xr-x | src/chroot-tools/librechroot | 132 |
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" |