2 # Copyright 2011 CyberLeo, All Rights Reserved
3 # http://wiki.cyberleo.net/wiki/CyberLeo/COPYRIGHT
5 : ${ORIG_USER=$(id -un)}
7 # Need root beyond here
8 [ "$(id -u)" -eq 0 ] || exec sudo env "J_ARCH=${J_ARCH}" "J_BASE=${J_BASE}" "J_NAME=${J_NAME}" "J_USER=${J_USER:-${USER}}" "ORIG_USER=${ORIG_USER}" "DISPLAY=${DISPLAY}" "${0}" "${@}"
10 meh() { printf " \033[1;32m*\033[0m %s%s\n" "${jname:+${jname}: }" "${*}"; }
11 omg() { printf " \033[1;33m*\033[0m %s%s\n" "${jname:+${jname}: }" "${*}"; }
12 wtf() { printf " \033[1;31m*\033[0m %s%s\n" "${jname:+${jname}: }" "${*}"; exit 1; }
14 [ "${*}" ] && printf "%s\n\n" "${*}"
17 <command> <name> [arguments]
19 ls list available chroots
21 status show jail status
23 start prepare an existing chroot for use
25 stop undo what 'start' did
27 kill Forcefully kill all processes; only applicable with cgroups
30 shell spawn a shell or command within the chroot
32 eval evaluate a shell command line within the chroot
38 cmd="$(basename "${0}")"
39 jarch="${J_ARCH:-$(uname -m)}"
40 jbase="${J_BASE:-$(realpath "$(dirname "${0}")/../")}"
41 jname="${J_NAME:-$(basename "${1}")}" #"
44 # cgroup to use; if not found, cgroup support will not be used
46 # cgroup parent to use (${cgroup}/${parent}/${jname})
49 # Remove chroot name from argument stack, if passed in
50 [ "${J_NAME}" ] || shift
52 # Propagate certain environment variables; sterilize the rest of the environment
59 # Debian-specific init: prepare Debian chroot with debootstrap
62 suite="$(echo "${2:-squeeze}" | tr 'A-Z' 'a-z')"
65 [ "$(which debootstrap 2>&-)" ] || pebkac "j_init_debian: debootstrap not found"
66 [ "${jdir}" ] || pebkac "j_init_debian: jdir must be specified"
67 [ ! -d "${jdir}" ] || pebkac "j_init_debian: jdir must not exist ('${jdir}')"
68 [ -d "$(dirname "${jdir}")" ] || pebkac "j_init_debian: parent of jdir must exist ('${jdir}')"
72 x86|i386) arch=i386 ;;
73 amd64|x86_64|x64) arch=amd64 ;;
74 *) pebkac "Unsupported arch '${jarch}'" ;;
77 cmd="debootstrap --arch=${arch} --include=curl,file,less,locales,sudo,build-essential,libreadline-dev,zlib1g-dev '${suite}' '${jdir}'"
78 echo "Executing ${cmd}"
81 # Make sure locales are generated on first start
82 mkdir -p "${jdir}/etc/rcJ.d"
83 cat > "${jdir}/etc/rcJ.d/S00localegen" <<"EOF"
85 /bin/sed -i '/en_US/s/^# //' /etc/locale.gen
89 chmod 755 "${jdir}/etc/rcJ.d/S00localegen"
92 # Gentoo-specific init: prepare Gentoo chroot with stage3+portage tarballs
95 base="http://distfiles.gentoo.org/releases/${arch}/autobuilds"
96 pointer="${base}/latest-stage3.txt"
104 # Create a new chroot, somehow
106 # Make sure this does NOT exist
108 dist="$(echo "${2:-debian}" | tr 'A-Z' 'a-z')"
109 jdir="${jbase}/${jname}"
110 [ -d "${jdir}" ] && pebkac "j_init: ${jname} already exists"
113 debian) j_init_debian "${jdir}" "${@}" ;;
114 gentoo) j_init_gentoo "${jdir}" "${@}" ;;
115 *) pebkac "Unsupported distro '${dist}'" ;;
119 # Figure out and set chroot parameters; needed for all functions that follow
121 ( jname="${1:-jname}"
123 # Make sure jname is not empty
126 printf "jerror='%s'\n" "jname empty"
130 # Given a chroot name, find and set up the chroot dir
131 jdir="${jbase}/${jname}"
133 if [ ! -d "${jdir}" -o ! -d "${jroot}" ]
135 printf "jerror='%s'\n" "not a directory"
139 # Where is the shell?
141 for shell in /bin/bash /usr/bin/bash /usr/local/bin/bash /bin/sh
143 if [ -f "${jroot}/${shell}" ]
149 if [ -z "${jshell}" ]
151 printf "jerror='%s'\n" "unable to locate usable shell; is this a jail?"
155 printf "jerror='' jname='%s' jdir='%s' jroot='%s' jshell='%s'\n" "${jname}" "${jdir}" "${jroot}" "${jshell}"
159 # Find the 'freezer' cgroup, to determine whether the chroot can be further isolated
160 j_locate_cgroup_mount() {
161 cgroup="${1:-freezer}"
162 cat /proc/mounts | while read src dir type opts fsck dump extra
164 [ "${type}" = "cgroup" ] || continue
171 [ "${1}" = "${cgroup}" ] && echo "${dir}" && break 2
177 # Cache and expose cgroup mountpoint for given chroot; make sure it exists
179 [ "${jcg}" ] && { echo "${jcg}"; return; }
180 jcg="$(j_locate_cgroup_mount "${cgroup}")/${jcgparent}/${jname}"
186 [ "$(j_cgroup)" ] || return
189 echo "${1}" > "$(j_cgroup)/tasks"
194 # Run a command within a cgroup, if available
200 echo $$ > "$(j_cgroup)/tasks"
208 # Copy ipcc into the jail and add helpful symlinks, so it can communicate
209 # simple commands to the outside
211 ipcc="$(dirname "${0}")/ipcc"
212 cp -f "${ipcc}" "${jroot}/bin"
214 [ -e ee ] || ln -s ipcc ee
215 [ -e ff ] || ln -s ipcc ff
216 [ -e gitk ] || ln -s ipcc gitk
217 [ -e gitka ] || ln -s ipcc gitka
221 # Launch ipcd on jail
223 ipcd_pid="${jdir}/ipcd.pid"
224 su "${ORIG_USER}" "${jbase}/j/ipcd" "${jdir}" &
226 echo "${pid}" > "${ipcd_pid}"
232 ipcd_pid="${jdir}/ipcd.pid"
233 [ -f "${ipcd_pid}" ] && pid="$(cat "${ipcd_pid}")"
234 [ "${pid}" ] && kill -TERM ${pid}
239 eval $(j_params "${1}")
240 [ "${jerror}" ] && return 1 || return 0
243 # List available chroots
245 ( cd "${jbase}"; ls -1 ) | while read jname
247 j_is "${jname}" && echo "${jname}"
251 # Chroot is 'up' if /dev/pts and /proc are mounted
253 jname="${1:-${jname}}"
254 eval "$(j_params "${jname}")"
255 [ "${jerror}" ] && wtf "${jerror}"
256 grep -q "^devpts ${jroot}/dev/pts devpts" /proc/mounts || return 1
257 grep -q "^proc ${jroot}/proc proc" /proc/mounts || return 1
261 # Poll chroot status (j_up)
263 [ -z "${1}" ] && set - $(j_ls)
266 j_up "${1}" && meh "$(printf '\033[1;32mup\033[0m')" || meh "$(printf '\033[1;31mdown\033[0m')"
271 # Mount /dev/pts and /proc in the chroot
273 jname="${1:-${jname}}"
274 j_up "${jname}" && return 0
275 eval "$(j_params "${jname}")"
276 meh "starting ${jname} ..."
277 mount -t devpts devpts "${jroot}/dev/pts"
278 mount -t proc proc "${jroot}/proc"
280 # Copy in ipcc and launch ipcd
284 # Start all services in /etc/rcJ.d
285 j_root_eval "${jname}" '[ -d /etc/rcJ.d ] && ( ls -1 /etc/rcJ.d/* 2>&- | grep /S | sort | sed -e "s/$/ start/" | sh )'
288 # Execute command in chroot as root
290 jname="${1:-${jname}}"; shift
291 j_up "${jname}" || wtf "chroot not running"
292 eval "$(j_params "${jname}")"
294 env -i ${jenv} /usr/bin/chroot "${jroot}" /bin/sh -c "${*}"
297 # Execute command in chroot
299 jname="${1:-${jname}}"; shift
300 j_up "${jname}" || wtf "chroot not running"
301 eval "$(j_params "${jname}")"
303 env -i ${jenv} /usr/bin/chroot "${jroot}" /bin/su "${juser:-${USER}}" -c "${*}"
307 jname="${1:-${jname}}"
308 eval "$(j_params "${jname}")"
309 j_eval "${jname}" "cd; exec ${jshell} -l"
312 # Unmount /dev/pts and /proc in the chroot
314 jname="${1:-${jname}}"
315 eval "$(j_params "${jname}")"
316 j_up "${jname}" || return 0
317 meh "stopping ${jname} ..."
319 # Stop all services in /etc/rcJ.d
320 j_root_eval "${jname}" '[ -d /etc/rcJ.d ] && ( ls -1 /etc/rcJ.d/* 2>&- | grep /S | sort -r | sed -e "s/$/ stop/" | sh )'
325 umount "${jroot}/proc"
326 umount "${jroot}/dev/pts"
329 # Send a signal to all processes inside a chroot, if possible
332 jname="${2:-${jname}}"
334 [ "$(j_cgroup)" ] || { echo "No cgroup support; cannot signal"; return 1; }
335 pids="$(cat "$(j_cgroup)/tasks")"
338 echo Sending ${sig} to ${pids} ...
339 echo "FROZEN" > "$(j_cgroup)/freezer.state"
341 echo "THAWED" > "$(j_cgroup)/freezer.state"
346 jname="${1:-${jname}}"
347 eval "$(j_params "${jname}")"
348 meh "killing ${jname} ..."
350 j_signal "TERM" "${jname}"
352 j_signal "KILL" "${jname}"
356 init|create) j_init "${jname}" "${@}" ;;
358 status) j_status "${jname}" "${@}" ;;
359 start) j_start "${jname}" ;;
360 shell|enter) j_shell "${jname}" ;;
361 eval) j_eval "${jname}" "${*}" ;;
362 stop) j_stop "${jname}" ;;
363 kill) j_kill "${jname}" ;;