From ef67221e1971aca6b0891b2f6ac30da07fe1037d Mon Sep 17 00:00:00 2001 From: bill-auger Date: Fri, 14 Aug 2020 17:37:25 -0400 Subject: refactor librechroot args processing --- src/chroot-tools/librechroot | 230 ++++++++++++++++++++++++------------------- 1 file changed, 130 insertions(+), 100 deletions(-) diff --git a/src/chroot-tools/librechroot b/src/chroot-tools/librechroot index 56a8292..ae01e51 100755 --- a/src/chroot-tools/librechroot +++ b/src/chroot-tools/librechroot @@ -40,6 +40,76 @@ shopt -s nullglob umask 0022 +################################################################################ +# Utility functions # +################################################################################ + +# Usage: detect_chroot_arch $makepkg_conf +detect_chroot_arch() +{ + local makepkg_conf="$1" + local chroot_arch + + chroot_arch=$(grep -a '^CARCH=' "$makepkg_conf" 2> /dev/null | cut -d '=' -f 2 | tr -d '"') + [[ ${chroot_arch} =~ x86_64|i686|armv7h ]] || chroot_arch=$(uname -m) + + echo ${chroot_arch} +} + +# 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 mountopts + mountpoint="$(df -P "$file"|sed '1d;s/.*\s//')" + mountopts=($(LC_ALL=C mount|awk "{ if (\$3==\"$mountpoint\") { gsub(/[(,)]/, \" \", \$6); print \$6 } }")) + ! in_array nosuid "${mountopts[@]}" && ! in_array noexec "${mountopts[@]}" +} + + ################################################################################ # Wrappers for files in ${pkglibexecdir}/chroot/ # ################################################################################ @@ -64,15 +134,9 @@ hack_arch_nspawn_flags() { esac done - # Detect the architecture of the chroot - local CARCH - if [[ -f "$makepkg_conf" ]]; then - eval "$(grep -a '^CARCH=' "$makepkg_conf")" - else - CARCH="$(uname -m)" - fi - + local CARCH=$(detect_chroot_arch "$makepkg_conf") local setarch interpreter + case $CARCH in armv7h|armv7l) setarch=armv7l; interpreter=/usr/bin/qemu-arm-static ;; *) setarch=$CARCH; interpreter=/usr/bin/qemu-$CARCH-static ;; @@ -141,67 +205,14 @@ mkarchroot() { "${cmd[@]}" } -################################################################################ -# 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 mountopts - mountpoint="$(df -P "$file"|sed '1d;s/.*\s//')" - mountopts=($(LC_ALL=C mount|awk "{ if (\$3==\"$mountpoint\") { gsub(/[(,)]/, \" \", \$6); print \$6 } }")) - ! in_array nosuid "${mountopts[@]}" && ! in_array noexec "${mountopts[@]}" -} ################################################################################ # Main program # ################################################################################ +readonly DEF_PACMANCONF_DIR=/usr/share/pacman/defaults/ + + usage() { eval "$(calculate_directories)" print "Usage: %s [OPTIONS] COMMAND [ARGS...]" "${0##*/}" @@ -252,10 +263,10 @@ usage() { 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 'The `-A CARCH` flag is *almost* simply an alias for' + printf ' %s\n' \ + "-C \"${DEF_PACMAN_CONF_DIR}pacman.conf.\$CARCH\" \\" \ + "-M \"${DEF_PACMAN_CONF_DIR}makepkg.conf.\$CARCH\"" prose 'However, before doing that, it actually makes a temporary copy of `pacman.conf`, and modifies it to:' bullet 'set `Architecture` to match the `CARCH` line in `makepkg.conf`' @@ -310,43 +321,36 @@ main() { declare -i retconf=0 load_conf chroot.conf CHROOTDIR CHROOT || retconf=$? + local target_arch=$(uname -m) local mode=enter opt declare -Ai used_opts + local tmp_pacmanconf="$(mktemp --tmpdir ${tmp_pacmanconf_prefix}.XXXXXXXXXX)" + local has_tmp_pacmanconf=0 + local def_pacmanconf + local def_makepkgconf + + trap 'rm -f -- "$tmp_pacmanconf"' EXIT + ## parse CLI options ## 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 $EXIT_INVALIDARGUMENT; - fi - trap 'rm -f -- "$tmppacmanconf"' EXIT - tmppacmanconf="$(mktemp --tmpdir librechroot-pacman.conf.XXXXXXXXXX)" - sed -r \ - -e "s|^#?\\s*Architecture.+|Architecture = ${OPTARG}|g" \ - -e "s|^.*Include\s*=\s*/etc/pacman.d/.*\.conf|#&|" \ - < "/usr/share/pacman/defaults/pacman.conf.$OPTARG" \ - > "$tmppacmanconf" - echo -e '\n\n# Enable the volatile i686 [build-support] repo only as needed.' \ - '\n#[build-support]\n#Server = http://mirror.archlinux32.org/$arch/$repo/' \ - '\n\n# Enable the volatile arm [aur] repo only as needed.' \ - '\n#[aur]\n#Server = https://mirror.archlinuxarm.org/$arch/$repo/' \ - >> "$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 $EXIT_INVALIDARGUMENT;; + n ) CHROOT=$OPTARG ;; + l ) COPY=$OPTARG ;; + N ) sysd_nspawn_flags+=(--private-network) ;; + C|M) target_arch=$(detect_chroot_arch "$OPTARG") + arch_nspawn_flags+=(-$opt "$OPTARG") ;; + A ) target_arch=$OPTARG + def_pacmanconf="${DEF_PACMANCONF_DIR}"pacman.conf.${target_arch} + def_makepkgconf="${DEF_PACMANCONF_DIR}"makepkg.conf.${target_arch} + arch_nspawn_flags+=( -C "${tmp_pacmanconf}" + -M "${def_makepkgconf}" ) + has_tmp_pacmanconf=1 ;; + w ) sysd_nspawn_flags+=("--bind=$OPTARG") ;; + r ) sysd_nspawn_flags+=("--bind-ro=$OPTARG") ;; + * ) usage >&2; return $EXIT_INVALIDARGUMENT ;; esac used_opts[$opt]+=1 done @@ -425,6 +429,31 @@ main() { fi :;; esac + if (( has_tmp_pacmanconf )) && \ + ! [[ -f "${def_pacmanconf}" && -f "${def_makepkgconf}" ]]; then + error 'Unsupported architecture: %s' "${target_arch}" + plain 'See the files in %q for valid architectures.' "${DEF_PACMANCONF_DIR}" + return $EXIT_INVALIDARGUMENT; + fi + + + ## process CLI options ## + + if (( has_tmp_pacmanconf )); then + sed -r \ + -e "s|^#?\\s*Architecture.+|Architecture = ${target_arch}|g" \ + -e "s|^.*Include\s*=\s*/etc/pacman.d/.*\.conf|#&|" \ + < "${def_pacmanconf}" \ + > "$tmp_pacmanconf" + echo -e '\n\n# Enable the volatile i686 [build-support] repo only as needed.' \ + '\n#[build-support]\n#Server = http://mirror.archlinux32.org/$arch/$repo/' \ + '\n\n# Enable the volatile arm [aur] repo only as needed.' \ + '\n#[aur]\n#Server = https://mirror.archlinuxarm.org/$arch/$repo/' \ + >> "$tmp_pacmanconf" + fi + + + ## finalize state ## [[ $retconf = 0 ]] || exit $retconf eval "$(calculate_directories)" @@ -434,7 +463,8 @@ main() { readonly rootdir copydir readonly mode - ######################################################################## + + ## business ## if (( EUID )); then error "This program must be run as root." @@ -484,8 +514,8 @@ main() { # - set 'CacheDir' in \`/etc/pacman.conf'" # - apply -C or -M flags arch-nspawn "$copydir" true