diff options
Diffstat (limited to 'src/chroot-tools/librechroot')
-rwxr-xr-x | src/chroot-tools/librechroot | 915 |
1 files changed, 455 insertions, 460 deletions
diff --git a/src/chroot-tools/librechroot b/src/chroot-tools/librechroot index ae01e51..f7921bf 100755 --- a/src/chroot-tools/librechroot +++ b/src/chroot-tools/librechroot @@ -47,66 +47,66 @@ umask 0022 # Usage: detect_chroot_arch $makepkg_conf detect_chroot_arch() { - local makepkg_conf="$1" - local 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) + 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} + 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" + 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 + 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 + # 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[@]}" + 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[@]}" } @@ -122,87 +122,87 @@ sysd_nspawn_flags=() hack_arch_nspawn_flags() { - local copydir="$1" - local makepkg_conf="$copydir/etc/makepkg.conf" - - OPTIND=1 - set -- "${arch_nspawn_flags[@]}" - while getopts 'hC:M:c:f:s' arg; do - case "$arg" in - M) makepkg_conf="$OPTARG" ;; - *) :;; - esac - done - - 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 ;; - esac - if ! setarch $setarch /bin/true 2>/dev/null; then - # We're running a cross-arch chroot - - # Make sure that qemu-static is set up with binfmt_misc - if [[ -z $(grep -l -xF \ - -e "interpreter $interpreter" \ - -r -- /proc/sys/fs/binfmt_misc 2>/dev/null \ - | xargs -r grep -xF 'enabled') ]] - then - error 'Cannot cross-compile for %s on %s' "$CARCH" "$(uname -m)" - plain 'This requires a binfmt_misc entry for %s.' "$interpreter" - prose 'Such a binfmt_misc entry is provided by the %s - package. If you have it installed, but still see - this message, you may need to restart %s.' \ - qemu-user-static-binfmt systemd-binfmt.service - return $EXIT_NOTINSTALLED - fi - - # Let qemu/binfmt_misc do its thing - arch_nspawn_flags+=(-f "$interpreter" -s) - fi + local copydir="$1" + local makepkg_conf="$copydir/etc/makepkg.conf" + + OPTIND=1 + set -- "${arch_nspawn_flags[@]}" + while getopts 'hC:M:c:f:s' arg; do + case "$arg" in + M) makepkg_conf="$OPTARG" ;; + *) :;; + esac + done + + 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 ;; + esac + if ! setarch $setarch /bin/true 2>/dev/null; then + # We're running a cross-arch chroot + + # Make sure that qemu-static is set up with binfmt_misc + if [[ -z $(grep -l -xF \ + -e "interpreter $interpreter" \ + -r -- /proc/sys/fs/binfmt_misc 2>/dev/null \ + | xargs -r grep -xF 'enabled') ]] + then + error 'Cannot cross-compile for %s on %s' "$CARCH" "$(uname -m)" + plain 'This requires a binfmt_misc entry for %s.' "$interpreter" + prose 'Such a binfmt_misc entry is provided by the %s + package. If you have it installed, but still see + this message, you may need to restart %s.' \ + qemu-user-static-binfmt systemd-binfmt.service + return $EXIT_NOTINSTALLED + fi + + # Let qemu/binfmt_misc do its thing + arch_nspawn_flags+=(-f "$interpreter" -s) + fi } # Usage: arch-nspawn $copydir $cmd... arch-nspawn() { - local copydir=$1; shift - local cmd=("$@") - - local arch_nspawn_flags=("${arch_nspawn_flags[@]}") - hack_arch_nspawn_flags "$copydir" - - "$_arch_nspawn" \ - "${arch_nspawn_flags[@]}" \ - "$copydir" \ - "${sysd_nspawn_flags[@]}" \ - -- \ - "${cmd[@]}" + local copydir=$1; shift + local cmd=("$@") + + local arch_nspawn_flags=("${arch_nspawn_flags[@]}") + hack_arch_nspawn_flags "$copydir" + + "$_arch_nspawn" \ + "${arch_nspawn_flags[@]}" \ + "$copydir" \ + "${sysd_nspawn_flags[@]}" \ + -- \ + "${cmd[@]}" } # Usage: mkarchroot $copydir $pkgs... mkarchroot() { - local copydir=$1; shift - local pkgs=("$@") - - local arch_nspawn_flags=("${arch_nspawn_flags[@]}") - hack_arch_nspawn_flags "$copydir" - - local cmd=( - unshare -m "$_mkarchroot" - # mkarchroot flags: (flags are very similar to arch-nspawn) - "${arch_nspawn_flags[@]}" - # chroot directory: - -- "$copydir" - # pacman flags: - # * hack around https://bugs.archlinux.org/task/49347 - --hookdir="$copydir/etc/pacman.d/hooks" - # packages: (and maybe more flags... pacstrap injects - # flags at the end, so we can't turn of flag parsing - # with '--') - "${pkgs[@]}" - ) - "${cmd[@]}" + local copydir=$1; shift + local pkgs=("$@") + + local arch_nspawn_flags=("${arch_nspawn_flags[@]}") + hack_arch_nspawn_flags "$copydir" + + local cmd=( + unshare -m "$_mkarchroot" + # mkarchroot flags: (flags are very similar to arch-nspawn) + "${arch_nspawn_flags[@]}" + # chroot directory: + -- "$copydir" + # pacman flags: + # * hack around https://bugs.archlinux.org/task/49347 + --hookdir="$copydir/etc/pacman.d/hooks" + # packages: (and maybe more flags... pacstrap injects + # flags at the end, so we can't turn of flag parsing + # with '--') + "${pkgs[@]}" + ) + "${cmd[@]}" } @@ -214,360 +214,355 @@ readonly DEF_PACMANCONF_DIR=/usr/share/pacman/defaults/ 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`' # libretools/librechroot - bullet 'overwrite `/etc/pacman.d/mirrorlist`' # devtools/arch-nspawn - bullet 'set `CacheDir` in `/etc/pacman.conf`' # devtools/arch-nspawn - 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 + 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`' # libretools/librechroot + bullet 'overwrite `/etc/pacman.d/mirrorlist`' # devtools/arch-nspawn + bullet 'set `CacheDir` in `/etc/pacman.conf`' # devtools/arch-nspawn + 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 \"${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`' - bullet 'comment out any `Include = /etc/pacman.d/*.conf` lines' - 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[:INSIDE_PATH[:OPTIONS]]')>" 'Bind mount a file or directory, read/write' - flag "-r <$(_ 'PATH[:INSIDE_PATH[:OPTIONS]]')>" '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; and install all packages - that are.' - 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' + 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`' + bullet 'comment out any `Include = /etc/pacman.d/*.conf` lines' + 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[:INSIDE_PATH[:OPTIONS]]')>" 'Bind mount a file or directory, read/write' + flag "-r <$(_ 'PATH[:INSIDE_PATH[:OPTIONS]]')>" '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; and install all packages + that are.' + 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 + 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 - - 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) 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 - - - ## validate CLI options ## - - for opt in n l C M A; do - if (( ${used_opts[$opt]:-0} > 1 )); then - error "Option -%s may only be given once" "$opt" - usage >&2 - return $EXIT_INVALIDARGUMENT - fi - done - if (( ${used_opts[A]:-0} && ( ${used_opts[C]:-0} || ${used_opts[M]:-0} ) )); then - error "Option -A may not be used together with -C or -M" - usage >&2 - return $EXIT_INVALIDARGUMENT - fi - shift $((OPTIND - 1)) - if [[ $# -lt 1 ]]; then - error "Must specify a command" - usage >&2 - return $EXIT_INVALIDARGUMENT - fi - mode=$1 - if ! in_array "$mode" "${commands[@]}"; then - error "Unrecognized command: %s" "$mode" - usage >&2 - return $EXIT_INVALIDARGUMENT - fi - shift - case "$mode" in - help) - usage - return $EXIT_SUCCESS - :;; - 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 $EXIT_INVALIDARGUMENT - fi - :;; - install-file) - if [[ $# -lt 1 ]]; then - error 'Command `%s` requires at least one file' "$mode" - usage >&2 - return $EXIT_INVALIDARGUMENT - 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 $EXIT_INVALIDARGUMENT - fi - fi - :;; - install-name) - if [[ $# -lt 1 ]]; then - error 'Command `%s` requires at least one package name' "$mode" - usage >&2 - return $EXIT_INVALIDARGUMENT - fi - :;; - run) - if [[ $# -lt 1 ]]; then - error 'Command `%s` requires at least one argument' "$mode" - usage >&2 - return $EXIT_INVALIDARGUMENT - 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)" - - readonly LIBREUSER LIBREHOME - readonly CHROOTDIR CHROOT COPY - readonly rootdir copydir - readonly mode - - - ## business ## - - if (( EUID )); then - error "This program must be run as root." - return $EXIT_NOPERMISSION - fi - - umask 0022 - - # 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 $EXIT_FAILURE - fi - - if [[ ! -d $rootdir ]]; then - msg "Creating 'root' copy for chroot [%s]" "$CHROOT" - mkarchroot "$rootdir" base-devel </dev/null - make_empty_repo "$rootdir" - fi - - if [[ ! -d $copydir ]] || [[ $mode == sync ]]; then - msg "Syncing copy [%s] with root copy" "$COPY" - sync_chroot "$CHROOTDIR/$CHROOT/root" "$copydir" "$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 </dev/null - if [[ -n ${tmp_pacmanconf:-} ]]; then - rm -f -- "$tmp_pacmanconf" - fi - 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 - # Create/copy/delete - noop|make|sync) :;; - delete) - if [[ -d $copydir ]]; then - delete_chroot "$copydir" - fi - ;; - - # Dealing with packages - install-file) - install_packages "$copydir" "$@" </dev/null - chroot_add_to_local_repo "$copydir" "$@" - ;; - install-name) - arch-nspawn "$copydir" pacman -Sy --noconfirm -- "$@" </dev/null - ;; - update) - arch-nspawn "$copydir" pacman -Syu --noconfirm </dev/null - ;; - clean-pkgs) - trap "rm -f -- ${copydir@Q}/{chcleanup,chrootexec}" EXIT - install -m755 "$(librelib chroot/chcleanup)" "$copydir/chcleanup" - printf '%s\n' \ - '#!/bin/bash' \ - 'mkdir -p /startdir' \ - 'cd /startdir' \ - '/chcleanup' \ - > "$copydir/chrootexec" - chmod 755 "$copydir/chrootexec" - arch-nspawn "$copydir" /chrootexec </dev/null - ;; - - # Other - run) - arch-nspawn "$copydir" "$@" - ;; - enter) - arch-nspawn "$copydir" bash - ;; - clean-repo) - rm -rf "${copydir}"/repo/* - make_empty_repo "$copydir" - ;; - esac + COPY=$LIBREUSER + [[ $COPY != root ]] || COPY=copy + + 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) 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 + + + ## validate CLI options ## + + for opt in n l C M A; do + if (( ${used_opts[$opt]:-0} > 1 )); then + error "Option -%s may only be given once" "$opt" + usage >&2 + return $EXIT_INVALIDARGUMENT + fi + done + if (( ${used_opts[A]:-0} && ( ${used_opts[C]:-0} || ${used_opts[M]:-0} ) )); then + error "Option -A may not be used together with -C or -M" + usage >&2 + return $EXIT_INVALIDARGUMENT + fi + shift $((OPTIND - 1)) + if [[ $# -lt 1 ]]; then + error "Must specify a command" + usage >&2 + return $EXIT_INVALIDARGUMENT + fi + mode=$1 + if ! in_array "$mode" "${commands[@]}"; then + error "Unrecognized command: %s" "$mode" + usage >&2 + return $EXIT_INVALIDARGUMENT + fi + shift + case "$mode" in + help) + usage + return $EXIT_SUCCESS + :;; + 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 $EXIT_INVALIDARGUMENT + fi + :;; + install-file) + if [[ $# -lt 1 ]]; then + error 'Command `%s` requires at least one file' "$mode" + usage >&2 + return $EXIT_INVALIDARGUMENT + 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 $EXIT_INVALIDARGUMENT + fi + fi + :;; + install-name) + if [[ $# -lt 1 ]]; then + error 'Command `%s` requires at least one package name' "$mode" + usage >&2 + return $EXIT_INVALIDARGUMENT + fi + :;; + run) + if [[ $# -lt 1 ]]; then + error 'Command `%s` requires at least one argument' "$mode" + usage >&2 + return $EXIT_INVALIDARGUMENT + 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}" + fi + + + ## finalize state ## + + [[ $retconf = 0 ]] || exit $retconf + eval "$(calculate_directories)" + + readonly LIBREUSER LIBREHOME + readonly CHROOTDIR CHROOT COPY + readonly rootdir copydir + readonly mode + + + ## business ## + + if (( EUID )); then + error "This program must be run as root." + return $EXIT_NOPERMISSION + fi + + umask 0022 + + # 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 $EXIT_FAILURE + fi + + if [[ ! -d $rootdir ]]; then + msg "Creating 'root' copy for chroot [%s]" "$CHROOT" + mkarchroot "$rootdir" base-devel </dev/null + make_empty_repo "$rootdir" + fi + + if [[ ! -d $copydir ]] || [[ $mode == sync ]]; then + msg "Syncing copy [%s] with root copy" "$COPY" + sync_chroot "$CHROOTDIR/$CHROOT/root" "$copydir" "$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 </dev/null + if [[ -n ${tmp_pacmanconf:-} ]]; then + rm -f -- "$tmp_pacmanconf" + fi + 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 + # Create/copy/delete + noop|make|sync) :;; + delete) + if [[ -d $copydir ]]; then + delete_chroot "$copydir" + fi + ;; + + # Dealing with packages + install-file) + install_packages "$copydir" "$@" </dev/null + chroot_add_to_local_repo "$copydir" "$@" + ;; + install-name) + arch-nspawn "$copydir" pacman -Sy --noconfirm -- "$@" </dev/null + ;; + update) + arch-nspawn "$copydir" pacman -Syu --noconfirm </dev/null + ;; + clean-pkgs) + trap "rm -f -- ${copydir@Q}/{chcleanup,chrootexec}" EXIT + install -m755 "$(librelib chroot/chcleanup)" "$copydir/chcleanup" + printf '%s\n' \ + '#!/bin/bash' \ + 'mkdir -p /startdir' \ + 'cd /startdir' \ + '/chcleanup' \ + > "$copydir/chrootexec" + chmod 755 "$copydir/chrootexec" + arch-nspawn "$copydir" /chrootexec </dev/null + ;; + + # Other + run) + arch-nspawn "$copydir" "$@" + ;; + enter) + arch-nspawn "$copydir" bash + ;; + clean-repo) + rm -rf "${copydir}"/repo/* + make_empty_repo "$copydir" + ;; + esac } |