#!/bin/sh # Copyright 2011 CyberLeo, All Rights Reserved # http://wiki.cyberleo.net/wiki/CyberLeo/COPYRIGHT # Need root beyond here [ "$(id -u)" -eq 0 ] || exec sudo env "J_ARCH=${J_ARCH}" "J_BASE=${J_BASE}" "J_NAME=${J_NAME}" "J_USER=${J_USER:-${USER}}" "${0}" "${@}" meh() { printf " \033[1;32m*\033[0m %s%s\n" "${jname:+${jname}: }" "${*}"; } omg() { printf " \033[1;33m*\033[0m %s%s\n" "${jname:+${jname}: }" "${*}"; } wtf() { printf " \033[1;31m*\033[0m %s%s\n" "${jname:+${jname}: }" "${*}"; exit 1; } pebkac() { [ "${*}" ] && printf "%s\n\n" "${*}" cat < [arguments] list ls list available chroots status show jail status start prepare an existing chroot for use stop undo what 'start' did enter shell spawn a shell or command within the chroot eval evaluate a shell command line within the chroot EOF exit 1 } cmd="$(basename "${0}")" jarch="${J_ARCH:-$(uname -m)}" jbase="${J_BASE:-$(realpath "$(dirname "${0}")/../")}" jname="${J_NAME:-$(basename "${1}")}" #" juser="${J_USER}" # Remove chroot name from argument stack, if passed in [ "${J_NAME}" ] || shift # Propagate certain environment variables; sterilize the rest of the environment jenv=" LANG=${LANG} TERM=${TERM} USER=${USER} " # Debian-specific init: prepare Debian chroot with debootstrap j_init_debian() { jdir="${1}" suite="$(echo "${2:-squeeze}" | tr 'A-Z' 'a-z')" # Validation [ "$(which debootstrap 2>&-)" ] || pebkac "j_init_debian: debootstrap not found" [ "${jdir}" ] || pebkac "j_init_debian: jdir must be specified" [ ! -d "${jdir}" ] || pebkac "j_init_debian: jdir must not exist ('${jdir}')" [ -d "$(dirname "${jdir}")" ] || pebkac "j_init_debian: parent of jdir must exist ('${jdir}')" # Figure out arch case "${jarch}" in x86) arch=x86 ;; amd64|x86_64|x64) arch=amd64 ;; *) pebkac "Unsupported arch '${jarch}'" ;; esac cmd="debootstrap --arch=${arch} --include=curl,file,less,locales,sudo,build-essential,libreadline-dev,zlib1g-dev '${suite}' '${jdir}'" echo "Executing ${cmd}" eval "${cmd}" # Make sure locales are generated on first start mkdir -p "${jdir}/etc/rcJ.d" cat > "${jdir}/etc/rcJ.d/S00localegen" <<"EOF" #!/bin/sh /bin/sed -i '/en_US/s/^# //' /etc/locale.gen /usr/sbin/locale-gen /bin/rm -f "${0}" EOF chmod 755 "${jdir}/etc/rcJ.d/S00localegen" } # Gentoo-specific init: prepare Gentoo chroot with stage3+portage tarballs j_init_gentoo() { arch="$(uname -m)" base="http://distfiles.gentoo.org/releases/${arch}/autobuilds" pointer="${base}/latest-stage3.txt" # Fetch stage3 # Fetch portage # Validate signatures # Unpack stage3 # Unpack portage } # Create a new chroot, somehow j_init() { # Make sure this does NOT exist jname="${1:-jname}" dist="$(echo "${2:-debian}" | tr 'A-Z' 'a-z')" jdir="${jbase}/${jname}" [ -d "${jdir}" ] && pebkac "j_init: ${jname} already exists" shift 2 case "${dist}" in debian) j_init_debian "${jdir}" "${@}" ;; gentoo) j_init_gentoo "${jdir}" "${@}" ;; *) pebkac "Unsupported distro '${dist}'" ;; esac } # Figure out and set chroot parameters; needed for all functions that follow j_params() { ( jname="${1:-jname}" # Make sure jname is not empty if [ -z "${jname}" ] then printf "jerror='%s'\n" "jname empty" return 1 fi # Given a chroot name, find and set up the chroot dir jdir="${jbase}/${jname}" if [ ! -d "${jdir}" ] then printf "jerror='%s'\n" "not a directory" return 1 fi # Where is the shell? jshell="" for shell in /bin/bash /usr/bin/bash /usr/local/bin/bash /bin/sh do if [ -f "${jdir}/${shell}" ] then jshell=${shell} break fi done if [ -z "${jshell}" ] then printf "jerror='%s'\n" "unable to locate usable shell; is this a jail?" return 1 fi printf "jerror='' jname='%s' jdir='%s' jshell='%s'\n" "${jname}" "${jdir}" "${jshell}" ) } # Is this a chroot? j_is() { eval $(j_params "${1}") [ "${jerror}" ] && return 1 || return 0 } # List available chroots j_ls() { ( cd "${jbase}"; ls -1 ) | while read jname do j_is "${jname}" && echo "${jname}" done } # Chroot is 'up' if /dev/pts and /proc are mounted j_up() { jname="${1:-${jname}}" eval "$(j_params "${jname}")" [ "${jerror}" ] && wtf "${jerror}" grep -q "^devpts ${jdir}/dev/pts devpts" /proc/mounts || return 1 grep -q "^proc ${jdir}/proc proc" /proc/mounts || return 1 return 0 } # Poll chroot status (j_up) j_status() { [ -z "${1}" ] && set - $(l_ls) while [ "${1}" ] do j_up "${1}" && meh "$(printf '\033[1;32mup\033[0m')" || meh "$(printf '\033[1;31mdown\033[0m')" shift done } # Mount /dev/pts and /proc in the chroot j_start() { jname="${1:-${jname}}" j_up "${jname}" && return 0 eval "$(j_params "${jname}")" meh "starting ${jname} ..." mount -t devpts devpts "${jdir}/dev/pts" mount -t proc proc "${jdir}/proc" # Start all services in /etc/rcJ.d j_root_eval "${jname}" '[ -d /etc/rcJ.d ] && ( ls -1 /etc/rcJ.d/* 2>&- | grep /S | sort | sed -e "s/$/ start/" | sh )' } # Execute command in chroot as root j_root_eval() { jname="${1:-${jname}}" j_up "${jname}" || wtf "chroot not running" eval "$(j_params "${jname}")" shift env -i ${jenv} /usr/bin/chroot "${jdir}" /bin/sh -c "${*}" } # Execute command in chroot j_eval() { jname="${1:-${jname}}" j_up "${jname}" || wtf "chroot not running" eval "$(j_params "${jname}")" shift env -i ${jenv} /usr/bin/chroot "${jdir}" /bin/su "${juser:-${USER}}" -c "${*}" } j_shell() { jname="${1:-${jname}}" eval "$(j_params "${jname}")" j_eval "${jname}" "cd; exec ${jshell} -l" } # Unmount /dev/pts and /proc in the chroot j_stop() { jname="${1:-${jname}}" eval "$(j_params "${jname}")" j_up "${jname}" || return 0 meh "stopping ${jname} ..." # Stop all services in /etc/rcJ.d j_root_eval "${jname}" '[ -d /etc/rcJ.d ] && ( ls -1 /etc/rcJ.d/* 2>&- | grep /S | sort -r | sed -e "s/$/ stop/" | sh )' umount "${jdir}/proc" umount "${jdir}/dev/pts" } case "${cmd}" in init|create) j_init "${jname}" "${@}" ;; ls|list) j_ls ;; status) j_status "${jname}" "${@}" ;; start) j_start "${jname}" ;; shell|enter) j_shell "${jname}" ;; eval) j_eval "${jname}" "${*}" ;; stop) j_stop "${jname}" ;; *) pebkac ;; esac