--- makechrootpkg.sh.in 2014-01-05 18:51:41.463720929 -0500 +++ makechrootpkg.sh.ugly 2014-02-05 19:01:17.583560384 -0500 @@ -12,6 +12,7 @@ shopt -s nullglob +init_variables() { _makepkg_args=(-s --noconfirm -L --holdver) makepkg_args=("${_makepkg_args[@]}") repack=false @@ -29,9 +30,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 [--] [makepkg args]" @@ -67,6 +69,7 @@ exit 1 } +parse_options_init() { while getopts 'hcur:I:l:nTD:d:' arg; do case "$arg" in h) usage ;; @@ -93,9 +96,6 @@ [[ ! -d $chrootdir ]] && die "No chroot dir defined, or invalid path '%s'" "$passeddir" [[ ! -d $chrootdir/root ]] && die "Missing chroot dir root directory. Try using: mkarchroot %s/root base-devel" "$chrootdir" -# Detect chrootdir filesystem type -chroottype=$(stat -f -c %T "$chrootdir") - if [[ ${copy:0:1} = / ]]; then copydir=$copy else @@ -115,30 +115,48 @@ esac done -if [[ -n $SUDO_USER ]]; then +if [[ -n ${SUDO_USER:-} ]]; then USER_HOME=$(eval echo ~$SUDO_USER) else USER_HOME=$HOME fi +} # {{{ 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 + + # 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" @@ -159,11 +177,16 @@ # Drop the read lock again lock_close 8 - fi } -clean_temporary() { - stat_busy "Removing temporary copy [%s]" "$copy" +# Usage: delete_chroot $copydir [$copy] +delete_chroot() { + local copydir=$1 + local copy=${2:-$copydir} + # Detect chrootdir filesystem type + local chroottype=$(stat -f -c %T "$copydir") + + stat_busy "Removing chroot copy [%s]" "$copy" if [[ "$chroottype" == btrfs ]] && ! mountpoint -q "$copydir"; then btrfs subvolume delete "$copydir" >/dev/null || die "Unable to delete subvolume %s" "$copydir" @@ -178,9 +201,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" @@ -193,11 +221,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" @@ -236,12 +272,12 @@ chown -R nobody "$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 @@ -254,20 +290,38 @@ chmod 440 "$copydir/etc/sudoers.d/nobody-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%s\n_chrootprepare "$@"' "$(declare -f _chrootprepare)" \ + > "$copydir/chrootprepare" + chmod +x "$copydir/chrootprepare" printf $'#!/bin/bash\n%s\n_chrootbuild %q "$@"' "$(declare -f _chrootbuild)" \ "$run_namcap" >"$copydir/chrootbuild" 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" @@ -277,10 +331,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 local run_namcap="$1"; shift @@ -291,6 +345,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/* @@ -314,8 +369,25 @@ cp /startdir_host/PKGBUILD* . chown nobody PKGBUILD* + # Sync deps now, as networking may be disabled during _chrootbuild + sudo -u nobody makepkg "${makepkg_args[@]}" -eo +} + +_chrootbuild() { + # This function isn't run in makechrootpkg, + # so no global variables + local run_namcap="$1"; shift + local makepkg_args=("$@") + + . /etc/profile + export HOME=/build + shopt -s nullglob + + cd /startdir + # Safety check if [[ ! -w PKGBUILD ]]; then + # XXX: internationalize this message echo "Can't write to PKGBUILD!" exit 1 fi @@ -333,12 +405,24 @@ exit 0 } +# 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" @@ -352,6 +436,10 @@ } # }}} +main() { +init_variables +parse_options_init + umask 0022 load_vars /etc/makepkg.conf @@ -363,30 +451,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 -prepare_chroot +prepare_chroot "$copydir" "$USER_HOME" "$repack" -download_sources +download_sources "$copydir" "$src_owner" if arch-nspawn "$copydir" \ --bind-ro="$PWD:/startdir_host" \ --bind-ro="$SRCDEST:/srcdest_host" \ "${bindmounts_ro[@]}" "${bindmounts_rw[@]}" \ + /chrootprepare && + 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" "$copy" if (( ret != 0 )); then if $temp_chroot; then @@ -397,3 +500,4 @@ else true fi +}