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
34 mount mount chroot filesystems according to chroot fstab
36 umount umount chroot filesystems that were mounted with 'mount'
42 cmd="$(basename "${0}")"
43 jarch="${J_ARCH:-$(uname -m)}"
44 jbase="${J_BASE:-$(realpath "$(dirname "${0}")/../")}"
45 jname="${J_NAME:-$(basename "${1}")}" #"
48 # cgroup to use; if not found, cgroup support will not be used
50 # cgroup parent to use (${cgroup}/${parent}/${jname})
53 # Remove chroot name from argument stack, if passed in
54 [ "${J_NAME}" ] || shift
56 # Propagate certain environment variables; sterilize the rest of the environment
63 # Debian-specific init: prepare Debian chroot with debootstrap
66 suite="$(echo "${2:-squeeze}" | tr 'A-Z' 'a-z')"
69 [ "$(which debootstrap 2>&-)" ] || pebkac "j_init_debian: debootstrap not found"
70 [ "${jdir}" ] || pebkac "j_init_debian: jdir must be specified"
71 [ ! -d "${jdir}" ] || pebkac "j_init_debian: jdir must not exist ('${jdir}')"
72 [ -d "$(dirname "${jdir}")" ] || pebkac "j_init_debian: parent of jdir must exist ('${jdir}')"
76 x86|i386) arch=i386 ;;
77 amd64|x86_64|x64) arch=amd64 ;;
78 *) pebkac "Unsupported arch '${jarch}'" ;;
81 cmd="debootstrap --arch=${arch} --include=curl,file,less,locales,sudo,build-essential,libreadline-dev,zlib1g-dev '${suite}' '${jdir}'"
82 echo "Executing ${cmd}"
85 # Make sure locales are generated on first start
86 mkdir -p "${jdir}/etc/rcJ.d"
87 cat > "${jdir}/etc/rcJ.d/S00localegen" <<"EOF"
89 /bin/sed -i '/en_US/s/^# //' /etc/locale.gen
93 chmod 755 "${jdir}/etc/rcJ.d/S00localegen"
96 # Gentoo-specific init: prepare Gentoo chroot with stage3+portage tarballs
99 base="http://distfiles.gentoo.org/releases/${arch}/autobuilds"
100 pointer="${base}/latest-stage3.txt"
103 # Validate signatures
108 # Create a new chroot, somehow
110 # Make sure this does NOT exist
112 dist="$(echo "${2:-debian}" | tr 'A-Z' 'a-z')"
113 jdir="${jbase}/${jname}"
114 [ -d "${jdir}" ] && pebkac "j_init: ${jname} already exists"
117 debian) j_init_debian "${jdir}" "${@}" ;;
118 gentoo) j_init_gentoo "${jdir}" "${@}" ;;
119 *) pebkac "Unsupported distro '${dist}'" ;;
123 # Figure out and set chroot parameters; needed for all functions that follow
125 ( jname="${1:-jname}"
127 # Make sure jname is not empty
130 printf "jerror='%s'\n" "jname empty"
134 # Given a chroot name, find and set up the chroot dir
135 jdir="${jbase}/${jname}"
137 if [ ! -d "${jdir}" -o ! -d "${jroot}" ]
139 printf "jerror='%s'\n" "not a directory"
143 # Does this have a valid fstab file?
144 [ -f "${jdir}/fstab" ] && jfstab="${jdir}/fstab"
146 # Where is the shell?
148 for shell in /bin/bash /usr/bin/bash /usr/local/bin/bash /bin/sh
150 if [ -f "${jroot}/${shell}" ]
156 if [ -z "${jshell}" ]
158 printf "jerror='%s'\n" "unable to locate usable shell; is this a jail?"
162 printf "jerror='' jname='%s' jdir='%s' jroot='%s' jfstab='%s' jshell='%s'\n" "${jname}" "${jdir}" "${jroot}" "${jfstab}" "${jshell}"
166 # Find the 'freezer' cgroup, to determine whether the chroot can be further isolated
167 j_locate_cgroup_mount() {
168 cgroup="${1:-freezer}"
169 cat /proc/mounts | while read src dir type opts fsck dump extra
171 [ "${type}" = "cgroup" ] || continue
178 [ "${1}" = "${cgroup}" ] && echo "${dir}" && break 2
184 # Cache and expose cgroup mountpoint for given chroot; make sure it exists
186 [ "${jcg}" ] && { echo "${jcg}"; return; }
187 jcg="$(j_locate_cgroup_mount "${cgroup}")/${jcgparent}/${jname}"
193 [ "$(j_cgroup)" ] || return
196 echo "${1}" > "$(j_cgroup)/tasks"
201 # Run a command within a cgroup, if available
207 echo $$ > "$(j_cgroup)/tasks"
215 # Copy ipcc into the jail and add helpful symlinks, so it can communicate
216 # simple commands to the outside
218 ipcc="$(dirname "${0}")/ipcc"
219 cp -f "${ipcc}" "${jroot}/bin"
221 [ -e ee ] || ln -s ipcc ee
222 [ -e ff ] || ln -s ipcc ff
223 [ -e gitk ] || ln -s ipcc gitk
224 [ -e gitka ] || ln -s ipcc gitka
228 # Launch ipcd on jail
230 ipcd_pid="${jdir}/ipcd.pid"
231 # Kill any running IPCd; remove stale pid file and socket
232 if [ -f "${ipcd_pid}" ]
234 pid="$(cat "${ipcd_pid}")"
237 if ps ouser=,command= "${pid}" | grep "^${ORIG_USER}" | grep -q "ipcd"
239 omg "IPCd: already running! Killing"
242 omg "IPCd: removing stale pid file"
243 rm -f "${ipcd_pid}" "${jroot}/tmp/jipc"
246 su "${ORIG_USER}" "${jbase}/j/ipcd" "${jdir}" &
248 echo "${pid}" > "${ipcd_pid}"
254 ipcd_pid="${jdir}/ipcd.pid"
255 [ -f "${ipcd_pid}" ] && pid="$(cat "${ipcd_pid}")"
256 [ "${pid}" ] && kill -TERM ${pid}
262 eval $(j_params "${1}")
263 [ "${jerror}" ] && return 1 || return 0
266 # List available chroots
268 ( cd "${jbase}"; ls -1 ) | while read jname
270 j_is "${jname}" && echo "${jname}"
274 # Chroot is 'up' if /dev/pts and /proc are mounted
276 jname="${1:-${jname}}"
277 eval "$(j_params "${jname}")"
278 [ "${jerror}" ] && wtf "${jerror}"
279 grep -q "^devpts ${jroot}/dev/pts devpts" /proc/mounts || return 1
280 grep -q "^proc ${jroot}/proc proc" /proc/mounts || return 1
284 # Poll chroot status (j_up)
286 [ -z "${1}" ] && set - $(j_ls)
289 j_up "${1}" && meh "$(printf '\033[1;32mup\033[0m')" || meh "$(printf '\033[1;31mdown\033[0m')"
294 # Copy a mount from the host (for things like /dev, /proc, /sys, et alia)
295 j_fstab_copy_mount() {
296 grep -Eq "${1}[[:space:]]" "${tmp_fstab}" || grep -E \
297 "[[:space:]]${1}[[:space:]]" /proc/mounts | head -n 1 >> "${tmp_fstab}"
300 # Prepare fstab for use:
302 # * Make sure certain required mounts exist (/dev/pts, /proc)
303 # * Sort by mountpoint, to ensure mounts are performed in the correct order
304 j_fstab_synthesize() {
305 local tmp_fstab="$(mktemp "${jdir}/.fstab.XXXXXXXX")"
306 [ -f "${jfstab}" ] && sed -e 's/#.*$//; /^\W*$/d' "${jfstab}" > "${tmp_fstab}"
307 j_fstab_copy_mount "/dev/pts"
308 j_fstab_copy_mount "/proc"
309 sort -k2 "${tmp_fstab}"
313 # Process fstab file and mount the filesystems therein; if no fstab file (or if
314 # excludes certain critical filesystems) then augment it. This will also maintain
315 # an mtab file so they can be unmounted later (and so df works inside chroot).
317 # All mountpoints listed in this file will be prepended with ${jroot} prior to
318 # their being mounted or umounted! Make sure the fstab mountpoints are relative
319 # to the chroot root!
321 echo "rootfs / rootfs rw 0 0" > "${jdir}/mtab"
322 j_fstab_synthesize | while read src dir type opts fsck dump
324 mkdir -p "${dir}" || break
325 mount -t "${type}" -o "${opts}" "${src}" "${jroot}/${dir}" || break
326 printf "%s %s %s %s 0 0\n" "${src}" "/${dir##/}" "${type}" "${opts}" >> "${jdir}/mtab"
330 # Process fstab and mtab files and unmount the filesystems therein (opposite j_fstab_mount)
332 [ -s "${jdir}/mtab" ] || j_fstab_synthesize > "${jdir}/mtab"
333 tac "${jdir}/mtab" | while read src dir unused
336 grep -Eq "[[:space:]]${jroot}${dir%%/}[[:space:]]" /proc/mounts || continue
337 umount -f "${jroot}/${dir}" || umount -l "${jroot}/${dir}" || break
344 jname="${1:-${jname}}"
345 j_up "${jname}" && return 0
346 eval "$(j_params "${jname}")"
347 meh "starting ${jname} ..."
349 # Mount filesystems and copy in /etc/mtab
351 cp "${jdir}/mtab" "${jroot}/etc/mtab"
353 # Copy in ipcc and launch ipcd
357 # Start all services in /etc/rcJ.d
358 j_root_eval "${jname}" '[ -d /etc/rcJ.d ] && ( ls -1 /etc/rcJ.d/* 2>&- | grep /S | sort | sed -e "s/$/ start/" | sh )'
361 # Execute command in chroot as root
363 jname="${1:-${jname}}"; shift
364 j_up "${jname}" || wtf "chroot not running"
365 eval "$(j_params "${jname}")"
367 env -i ${jenv} /usr/bin/chroot "${jroot}" /bin/sh -c "${*}"
370 # Execute command in chroot
372 jname="${1:-${jname}}"; shift
373 j_up "${jname}" || wtf "chroot not running"
374 eval "$(j_params "${jname}")"
376 env -i ${jenv} /usr/bin/chroot "${jroot}" /bin/su "${juser:-${USER}}" -c "${*}"
379 # Invoke a shell within the chroot
381 jname="${1:-${jname}}"
382 eval "$(j_params "${jname}")"
383 j_eval "${jname}" "cd; exec ${jshell} -l"
388 jname="${1:-${jname}}"
389 eval "$(j_params "${jname}")"
390 j_up "${jname}" || return 0
391 meh "stopping ${jname} ..."
393 # Stop all services in /etc/rcJ.d
394 j_root_eval "${jname}" '[ -d /etc/rcJ.d ] && ( ls -1 /etc/rcJ.d/* 2>&- | grep /S | sort -r | sed -e "s/$/ stop/" | sh )'
399 # Unmount filesystems
403 # Send a signal to all processes inside a chroot, if possible
406 jname="${2:-${jname}}"
408 [ "$(j_cgroup)" ] || { echo "No cgroup support; cannot signal"; return 1; }
409 pids="$(cat "$(j_cgroup)/tasks")"
412 echo Sending ${sig} to ${pids} ...
413 echo "FROZEN" > "$(j_cgroup)/freezer.state"
415 echo "THAWED" > "$(j_cgroup)/freezer.state"
419 # Kill all processes inside chroot (TERM, then KILL)
421 jname="${1:-${jname}}"
422 eval "$(j_params "${jname}")"
423 meh "killing ${jname} ..."
425 j_signal "TERM" "${jname}"
427 j_signal "KILL" "${jname}"
430 # Hook to allow mounting chroot mounts from command line
432 jname="${1:-${jname}}"
433 eval "$(j_params "${jname}")"
437 # Hook to allow unmounting chroot mounts from command line
439 jname="${1:-${jname}}"
440 eval "$(j_params "${jname}")"
445 init|create) j_init "${jname}" "${@}" ;;
447 status) j_status "${jname}" "${@}" ;;
448 start) j_start "${jname}" ;;
449 shell|enter) j_shell "${jname}" ;;
450 eval) j_eval "${jname}" "${*}" ;;
451 stop) j_stop "${jname}" ;;
452 kill) j_kill "${jname}" ;;
453 mount) j_mount "${jname}" ;;
454 umount) j_umount "${jname}" ;;