#!/bin/bash -e bootstrap() { [[ $(id -u) -eq 0 ]] || exec sudo env -uUNSHARE HOME="$HOME" "$0" "$@" 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 unset UNSHARE } is_mounted() { local dst=$1 grep -sq "${dst}" /proc/mounts } maybe_mount() { local src=/$1 dst=${chroot}/${2:-$1} [[ -d ${src} ]] || return 0 if ! mkdir -p "${dst}" ; then [[ -w ${chroot} ]] && exit 1 || return 0 fi is_mounted "${dst}" || mount ${mount_args} --bind "${src}" "${dst}" } get_type() { case $(file "$1") in *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 } init_chroot() { [[ -w . ]] || return 0 if [[ ! -L etc/mtab ]] ; then ln -sfT /proc/mounts etc/mtab fi 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 Add path to mount list -d Use 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 "$@"