#!/bin/bash -e
-[[ -w / ]] || exec sudo env HOME="$HOME" "$0" "$@"
+bootstrap() {
+ [[ $(id -u) -eq 0 ]] || exec sudo env -uUNSHARE HOME="$HOME" "$0" "$@"
-mounts="proc sys dev dev/pts usr/portage usr/portage/distfiles"
-
-chroot=${0%/*}
-case ${chroot} in
- .) chroot=${PWD} ;;
- ./*) chroot=${PWD}/${chroot#./} ;;
-esac
-cd "${chroot}"
-
-do_umount() {
- mounts=$(mount | grep ${chroot} | awk '{print $3}' | tac)
- for m in ${mounts} ; do
- echo "unmounting $m"
- umount $m
- done
- if [[ -z ${mounts} ]] ; then
- echo "nothing mounted in ${chroot}"
+ if [[ -z ${UNSHARE} ]] ; then
+ mount_args=
+ if type -P unshare >&/dev/null ; then
+ test_arg() { unshare "$@" -- true >&/dev/null && uargs+=( "$@" ) || :; }
+ uargs=( -m )
+ test_arg -u
+ test_arg -i
+ test_arg -p -f --mount-proc
+ test_arg --propagation=private
+ UNSHARE=true exec unshare "${uargs[@]}" -- "$0" "$@"
+ fi
+ else
+ mount_args='-n'
fi
- exit 0
+ unset UNSHARE
}
-while [[ -n $1 ]] ; do
- case $1 in
- -u) do_umount ;;
- -*) echo "unknown option $1"; exit 1 ;;
- *) break ;;
- esac
-done
+is_mounted() {
+ local dst=$1
+ grep -sq "${dst}" /proc/mounts
+}
maybe_mount() {
local src=/$1 dst=${chroot}/${2:-$1}
[[ -d ${src} ]] || return 0
- mkdir -p "${dst}"
- grep -sq "${dst}" /proc/mounts || mount --bind "${src}" "${dst}"
+ if ! mkdir -p "${dst}" ; then
+ [[ -w ${chroot} ]] && exit 1 || return 0
+ fi
+ is_mounted "${dst}" || mount ${mount_args} --bind "${src}" "${dst}"
}
-for m in ${mounts} ; do
- maybe_mount ${m}
-done
get_type() {
case $(file "$1") in
- *x86-64*) echo x86_64;;
- *"Intel 80386"*) echo i386;;
- *32-bit*PowerPC*) echo ppc;;
- *64-bit*PowerPC*) echo ppc64;;
+ *x86-64*) echo x86_64;;
+ *"Intel 80386"*) echo i386;;
+ *32-bit*PowerPC*MSB*) echo ppc;;
+ *64-bit*PowerPC*MSB*) echo ppc64;;
+ *32-bit*PowerPC*LSB*) echo ppcle;;
+ *64-bit*PowerPC*LSB*) echo ppc64le;;
+ *32-bit*S/390*) echo s390;;
+ *64-bit*S/390*) echo s390x;;
+ *64-bit*MIPS*) echo mips64;;
+ *32-bit*MIPS*N32*) echo mips64;;
+ *32-bit*MIPS*) echo mips;;
+ *32-bit*PA-RISC*) echo parisc;;
+ *64-bit*PA-RISC*) echo parisc64;;
+ *32-bit*SPARC*) echo sparc;;
+ *64-bit*SPARC*) echo sparc64;;
esac
}
-bin_dst=$(get_type bin/bash)
-if [[ -n ${bin_dst} ]] && type -P setarch &>/dev/null ; then
- setarch="setarch ${bin_dst}"
-fi
-
-if [[ ! -L etc/mtab ]] ; then
- rm -f etc/mtab
- ln -sf /proc/mounts etc/mtab
-fi
-etc="
- hosts
- locale.gen
- localtime
- resolv.conf
-"
-home="
- .inputrc
- .nanorc
-"
-for f in \
- $(printf 'etc/%s ' ${etc}) \
-; do
- if [ -e "/${f}" ] ; then
- cp /${f} ${f}
+
+init_chroot() {
+ [[ -w . ]] || return 0
+
+ if [[ ! -L etc/mtab ]] ; then
+ ln -sfT /proc/mounts etc/mtab
fi
-done
-for f in ${home} ; do
- cp ~/${f} root/${f}
-done
-
-unset LS_COLORS # format changes over time
-[[ $# -eq 0 ]] && set -- env HOME=/root /bin/bash -l
-exec \
- ${setarch} \
- chroot "${chroot}" \
- "$@"
+ local f dst
+ local etc=(
+ hosts
+ locale.gen
+ localtime
+ resolv.conf
+ )
+ local home=(
+ .inputrc
+ .gdbinit
+ .gitconfig
+ .nanorc
+ )
+ for f in \
+ $(printf 'etc/%s ' "${etc[@]}") \
+ ; do
+ if [[ -e /${f} ]] ; then
+ cp "/${f}" "${f}"
+ fi
+ done
+ for f in "${home[@]}" ; do
+ df="root/${f}"
+ f="${HOME}/${f}"
+ if [[ -e ${f} ]] ; then
+ cp "${f}" "${df}"
+ fi
+ done
+
+ if [[ ! -d root/.git ]] ; then
+ f="${HOME}/.profile.d/aliases.sh"
+ if [[ -e ${f} ]] ; then
+ cat "${f}" > root/.bash_profile
+ fi
+ fi
+}
+
+usage() {
+ cat <<-EOF
+ Usage: ${0##*/} [options] [program to run]
+
+ Sets up common mount points and then chroots in and runs a program.
+ If no program is specified, then launch a login shell.
+
+ Options:
+ -u Unmount all paths in the chroot
+ -m <path> Add path to mount list
+ -d <dir> Use <dir> as chroot (defaults to ${0%/*})
+ -h This help screen
+ EOF
+ exit
+}
+
+main() {
+ bootstrap "$@"
+
+ local mounts=(
+ proc sys tmp dev dev/shm run
+ usr/portage usr/portage/distfiles
+ var/db/repos/gentoo var/cache/distfiles
+ usr/local/src
+ )
+
+ local chroot=${0%/*}
+ case ${chroot} in
+ .) chroot=${PWD} ;;
+ ./*) chroot=${PWD}/${chroot#./} ;;
+ esac
+
+ local cmd
+ while [[ -n $1 ]] ; do
+ case $1 in
+ -u) cmd='umount' ;;
+ -m) mounts+=( "$2" ); shift ;;
+ -d) chroot=$(realpath "$2"); shift ;;
+ --help|-h) usage ;;
+ -*) echo "${0##*/}: unknown option $1"; exit 1 ;;
+ *) break ;;
+ esac
+ shift
+ done
+ cd "${chroot}"
+
+ case ${cmd} in
+ umount) exec "${0%/*}/umount-tree" -y "${chroot}" ;;
+ esac
+
+ local m
+ for m in "${mounts[@]}" ; do
+ maybe_mount "${m}"
+ done
+ # Handle special mounts that we don't want to just bind mount.
+ if ! is_mounted "${chroot}/dev/pts" ; then
+ # Option order matters: on older kernels that don't recognize newinstance,
+ # parsing stops as soon as it hits that.
+ mount -t devpts devpts "${chroot}/dev/pts" \
+ -o nosuid,noexec,mode=0620,gid=5,ptmxmode=0666,newinstance
+ fi
+
+ init_chroot
+
+ local setarch
+ if type -P setarch &>/dev/null ; then
+ local bin_dst=$(get_type bin/bash)
+ if [[ -n ${bin_dst} ]] ; then
+ setarch="setarch ${bin_dst}"
+ fi
+ fi
+
+ # Doubtful these settings we want to leak into the chroot.
+ unset ROOT PORTAGE_CONFIGROOT LD_LIBRARY_PATH
+ unset LS_COLORS # format changes over time
+ [[ $# -eq 0 ]] && set -- env HOME=/root /bin/bash -l
+ exec \
+ ${setarch} \
+ chroot "${chroot}" \
+ "$@"
+}
+
+main "$@"