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'
38 init <dir> <seed> [seed-specific options]
39 initialize a chroot using the specified seed in seeds/
45 cmd="$(basename "${0}")"
46 j="$(readlink -f "$(dirname "${0}")")"
47 jarch="${J_ARCH:-$(uname -m)}"
48 jbase="${J_BASE:-$(readlink -f "${j}/../")}"
50 jname="${J_NAME:-$(basename "${1}")}" #"
53 # Find chroot binary, since it can be in many different places on different distros
54 for chroot in /usr/sbin/chroot /usr/bin/chroot /sbin/chroot /bin/chroot
56 [ -f "${chroot}" ] && break
59 [ "${chroot}" ] || wtf "Cannot find chroot in /usr/sbin:/usr/bin:/sbin:/bin"
61 # cgroup to use; if not found, cgroup support will not be used
63 # cgroup parent to use (${cgroup}/${parent}/${jname})
66 # Remove chroot name from argument stack, if passed in
67 [ "${J_NAME}" ] || shift && true
69 # Propagate certain environment variables; sterilize the rest of the environment
76 # Create a new chroot, using a seed script in seeds/
78 # Make sure this does NOT exist
80 [ "${jname}" ] || pebkac "j_init: no name provided"
81 seed="$(echo "${1}" | tr 'A-Z' 'a-z')"; shift
82 [ -f "${jbase}/j/seeds/${seed}.sh" ] || pebkac "Unknown seed '${seed}'"
83 jdir="${jbase}/${jname}"
84 [ -d "${jdir}" ] && pebkac "j_init: ${jname} already exists"
87 . "${jbase}/j/seeds/${seed}.sh" "${@}"
90 # Figure out and set chroot parameters; needed for all functions that follow
94 # Make sure jname is not empty
97 printf "jerror='%s'\n" "jname empty"
101 # Given a chroot name, find and set up the chroot dir
102 jdir="${jbase}/${jname}"
104 if [ ! -d "${jdir}" -o ! -d "${jroot}" ]
106 printf "jerror='%s'\n" "not a directory"
110 # Does this have a valid fstab file?
111 [ -f "${jdir}/fstab" ] && jfstab="${jdir}/fstab"
113 # Where is the shell?
115 for shell in /bin/bash /usr/bin/bash /usr/local/bin/bash /bin/sh
117 if [ -f "${jroot}/${shell}" ]
123 if [ -z "${jshell}" ]
125 printf "jerror='%s'\n" "unable to locate usable shell; is this a jail?"
129 printf "jerror='' jname='%s' jdir='%s' jroot='%s' jfstab='%s' jshell='%s'\n" "${jname}" "${jdir}" "${jroot}" "${jfstab}" "${jshell}"
133 # Find the 'freezer' cgroup, to determine whether the chroot can be further isolated
134 j_locate_cgroup_mount() {
135 cgroup="${1:-freezer}"
136 cat /proc/mounts | while read src dir type opts fsck dump extra
138 [ "${type}" = "cgroup" ] || continue
145 [ "${1}" = "${cgroup}" ] && echo "${dir}" && break 2
151 # Cache and expose cgroup mountpoint for given chroot; make sure it exists
153 [ "${jcg}" ] && { echo "${jcg}"; return; }
154 jcg="$(j_locate_cgroup_mount "${cgroup}")/${jcgparent}/${jname}"
160 [ "$(j_cgroup)" ] || return
163 echo "${1}" > "$(j_cgroup)/tasks"
168 # Run a command within a cgroup, if available
174 echo $$ > "$(j_cgroup)/tasks"
182 # Copy ipcc into the jail and add helpful symlinks, so it can communicate
183 # simple commands to the outside
185 ipcc="$(dirname "${0}")/ipcc"
186 cp -f "${ipcc}" "${jroot}/bin"
188 [ -e ee ] || ln -s ipcc ee
189 [ -e ff ] || ln -s ipcc ff
190 [ -e gitk ] || ln -s ipcc gitk
191 [ -e gitka ] || ln -s ipcc gitka
195 # Launch ipcd on jail
197 ipcd_pid="${jdir}/ipcd.pid"
198 # Kill any running IPCd; remove stale pid file and socket
199 if [ -f "${ipcd_pid}" ]
201 pid="$(cat "${ipcd_pid}")"
204 if ps ouser=,command= "${pid}" | grep "^${ORIG_USER}" | grep -q "ipcd"
206 omg "IPCd: already running! Killing"
209 omg "IPCd: removing stale pid file"
210 rm -f "${ipcd_pid}" "${jroot}/tmp/jipc"
213 su "${ORIG_USER}" "${jbase}/j/ipcd" "${jdir}" &
215 echo "${pid}" > "${ipcd_pid}"
221 ipcd_pid="${jdir}/ipcd.pid"
222 [ -f "${ipcd_pid}" ] && pid="$(cat "${ipcd_pid}")"
223 [ "${pid}" ] && kill -TERM ${pid}
229 eval $(j_params "${1}")
230 [ "${jerror}" ] && return 1 || return 0
233 # List available chroots
235 ( cd "${jbase}"; ls -1 ) | while read jname
237 j_is "${jname}" && echo "${jname}"
241 # Chroot is 'up' if /dev/pts and /proc are mounted
243 jname="${1:-${jname}}"
244 eval "$(j_params "${jname}")"
245 [ "${jerror}" ] && wtf "${jerror}"
246 grep -q "^[^ ]* ${jroot}/dev/pts devpts" /proc/mounts || return 1
247 grep -q "^[^ ]* ${jroot}/proc proc" /proc/mounts || return 1
251 # Poll chroot status (j_up)
253 [ -z "${1}" ] && set - $(j_ls)
256 j_up "${1}" && meh "$(printf '\033[1;32mup\033[0m')" || meh "$(printf '\033[1;31mdown\033[0m')"
261 # Copy a mount from the host (for things like /dev, /proc, /sys, et alia)
262 j_fstab_copy_mount() {
263 grep -Eq "${1}[[:space:]]" "${tmp_fstab}" || grep -E \
264 "[[:space:]]${1}[[:space:]]" /proc/mounts | head -n 1 >> "${tmp_fstab}"
267 # Prepare fstab for use:
269 # * Make sure certain required mounts exist (/dev/pts, /proc)
270 # * Sort by mountpoint, to ensure mounts are performed in the correct order
271 j_fstab_synthesize() {
272 local tmp_fstab="$(mktemp "${jdir}/.fstab.XXXXXXXX")"
273 [ -f "${jfstab}" ] && sed -e 's/#.*$//; /^\W*$/d' "${jfstab}" > "${tmp_fstab}"
274 j_fstab_copy_mount "/dev/pts"
275 j_fstab_copy_mount "/proc"
276 sort -k2 "${tmp_fstab}"
280 # Process fstab file and mount the filesystems therein; if no fstab file (or if
281 # excludes certain critical filesystems) then augment it. This will also maintain
282 # an mtab file so they can be unmounted later (and so df works inside chroot).
284 # All mountpoints listed in this file will be prepended with ${jroot} prior to
285 # their being mounted or umounted! Make sure the fstab mountpoints are relative
286 # to the chroot root!
288 echo "rootfs / rootfs rw 0 0" > "${jdir}/mtab"
289 j_fstab_synthesize | while read src dir type opts fsck dump
291 mkdir -p "${dir}" || break
292 mount -t "${type}" -o "${opts}" "${src}" "${jroot}/${dir}" || break
293 printf "%s %s %s %s 0 0\n" "${src}" "/${dir##/}" "${type}" "${opts}" >> "${jdir}/mtab"
297 # Process fstab and mtab files and unmount the filesystems therein (opposite j_fstab_mount)
299 [ -s "${jdir}/mtab" ] || j_fstab_synthesize > "${jdir}/mtab"
300 tac "${jdir}/mtab" | while read src dir unused
303 grep -Eq "[[:space:]]${jroot}${dir%%/}[[:space:]]" /proc/mounts || continue
304 umount -f "${jroot}/${dir}" || umount -l "${jroot}/${dir}" || break
311 jname="${1:-${jname}}"
312 j_up "${jname}" && return 0
313 eval "$(j_params "${jname}")"
314 meh "starting ${jname} ..."
316 # Mount filesystems and copy in /etc/mtab
318 cp "${jdir}/mtab" "${jroot}/etc/mtab"
320 # Copy in ipcc and launch ipcd
324 # Start all services in /etc/rcJ.d
325 [ -d "${jroot}/etc/rcJ.d" ] || return 0
326 j_root_eval "${jname}" '( ls -1 /etc/rcJ.d/* 2>&- | grep /S | sort | sed -e "s/$/ start/" | sh )'
329 # Execute command in chroot as root
331 jname="${1:-${jname}}"; shift
332 j_up "${jname}" || wtf "chroot not running"
333 eval "$(j_params "${jname}")"
335 env -i ${jenv} "${chroot}" "${jroot}" /bin/sh -c "${*}"
338 # Execute command in chroot
340 jname="${1:-${jname}}"; shift
341 j_up "${jname}" || wtf "chroot not running"
342 eval "$(j_params "${jname}")"
344 env -i ${jenv} "${chroot}" "${jroot}" /bin/su "${juser:-${USER}}" -c "${*}"
347 # Invoke a shell within the chroot
349 jname="${1:-${jname}}"
350 eval "$(j_params "${jname}")"
351 j_eval "${jname}" "cd; exec ${jshell} -l"
356 jname="${1:-${jname}}"
357 eval "$(j_params "${jname}")"
358 j_up "${jname}" || return 0
359 meh "stopping ${jname} ..."
361 # Stop all services in /etc/rcJ.d
362 j_root_eval "${jname}" '[ -d /etc/rcJ.d ] && ( ls -1 /etc/rcJ.d/* 2>&- | grep /S | sort -r | sed -e "s/$/ stop/" | sh )'
367 # Unmount filesystems
371 # Send a signal to all processes inside a chroot, if possible
374 jname="${2:-${jname}}"
376 [ "$(j_cgroup)" ] || { echo "No cgroup support; cannot signal"; return 1; }
377 pids="$(cat "$(j_cgroup)/tasks")"
380 echo Sending ${sig} to ${pids} ...
381 echo "FROZEN" > "$(j_cgroup)/freezer.state"
383 echo "THAWED" > "$(j_cgroup)/freezer.state"
387 # Kill all processes inside chroot (TERM, then KILL)
389 jname="${1:-${jname}}"
390 eval "$(j_params "${jname}")"
391 meh "killing ${jname} ..."
393 j_signal "TERM" "${jname}"
395 j_signal "KILL" "${jname}"
398 # Hook to allow mounting chroot mounts from command line
400 jname="${1:-${jname}}"
401 eval "$(j_params "${jname}")"
405 # Hook to allow unmounting chroot mounts from command line
407 jname="${1:-${jname}}"
408 eval "$(j_params "${jname}")"
413 init|create) j_init "${jname}" "${@}" ;;
415 status) j_status "${jname}" "${@}" ;;
416 start) j_start "${jname}" ;;
417 shell|enter) j_shell "${jname}" ;;
418 eval) j_eval "${jname}" "${*}" ;;
419 stop) j_stop "${jname}" ;;
420 kill) j_kill "${jname}" ;;
421 mount) j_mount "${jname}" ;;
422 umount) j_umount "${jname}" ;;