]> git.wh0rd.org - home.git/blobdiff - .bin/custom-chroot
crostini-vapier-setup: install edge
[home.git] / .bin / custom-chroot
index dcaeabc6bf03f25157efa62e16aaa57773174bc1..08d76ada03bfa4aedf7429ab16893cb924858bdd 100755 (executable)
 #!/bin/bash -e
 
-[[ -w / ]] || exec sudo env HOME="$HOME" "$0" "$@"
+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
-               UNSHARE=true exec unshare -m -- "$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
-else
-       mount_args='-n'
-fi
-unset UNSHARE
-
-mounts="proc sys tmp 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}"
-       fi
-       exit 0
+       unset UNSHARE
 }
 
-while [[ -n $1 ]] ; do
-       case $1 in
-       -u) do_umount ;;
-       -m) mounts+=" $2"; shift ;;
-       -*) echo "unknown option $1"; exit 1 ;;
-       *)  break ;;
-       esac
-       shift
-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 ${mount_args} --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)
-setarch=
-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
-       .gdbinit
-       .gitconfig
-       .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
+       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
-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}" \
-       "$@"
+}
+
+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 "$@"