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="$(realpath "$(dirname "${0}")")"
47 jarch="${J_ARCH:-$(uname -m)}"
48 jbase="${J_BASE:-$(realpath "${j}/../")}"
50 jname="${J_NAME:-$(basename "${1}")}" #"
53 # cgroup to use; if not found, cgroup support will not be used
55 # cgroup parent to use (${cgroup}/${parent}/${jname})
58 # Remove chroot name from argument stack, if passed in
59 [ "${J_NAME}" ] || shift
61 # Propagate certain environment variables; sterilize the rest of the environment
68 # Create a new chroot, using a seed script in seeds/
70 # Make sure this does NOT exist
72 [ "${jname}" ] || pebkac "j_init: no name provided"
73 seed="$(echo "${1}" | tr 'A-Z' 'a-z')"; shift
74 [ -f "${jbase}/j/seeds/${seed}.sh" ] || pebkac "Unknown seed '${seed}'"
75 jdir="${jbase}/${jname}"
76 [ -d "${jdir}" ] && pebkac "j_init: ${jname} already exists"
79 . "${jbase}/j/seeds/${seed}.sh" "${@}"
82 # Figure out and set chroot parameters; needed for all functions that follow
86 # Make sure jname is not empty
89 printf "jerror='%s'\n" "jname empty"
93 # Given a chroot name, find and set up the chroot dir
94 jdir="${jbase}/${jname}"
96 if [ ! -d "${jdir}" -o ! -d "${jroot}" ]
98 printf "jerror='%s'\n" "not a directory"
102 # Does this have a valid fstab file?
103 [ -f "${jdir}/fstab" ] && jfstab="${jdir}/fstab"
105 # Where is the shell?
107 for shell in /bin/bash /usr/bin/bash /usr/local/bin/bash /bin/sh
109 if [ -f "${jroot}/${shell}" ]
115 if [ -z "${jshell}" ]
117 printf "jerror='%s'\n" "unable to locate usable shell; is this a jail?"
121 printf "jerror='' jname='%s' jdir='%s' jroot='%s' jfstab='%s' jshell='%s'\n" "${jname}" "${jdir}" "${jroot}" "${jfstab}" "${jshell}"
125 # Find the 'freezer' cgroup, to determine whether the chroot can be further isolated
126 j_locate_cgroup_mount() {
127 cgroup="${1:-freezer}"
128 cat /proc/mounts | while read src dir type opts fsck dump extra
130 [ "${type}" = "cgroup" ] || continue
137 [ "${1}" = "${cgroup}" ] && echo "${dir}" && break 2
143 # Cache and expose cgroup mountpoint for given chroot; make sure it exists
145 [ "${jcg}" ] && { echo "${jcg}"; return; }
146 jcg="$(j_locate_cgroup_mount "${cgroup}")/${jcgparent}/${jname}"
152 [ "$(j_cgroup)" ] || return
155 echo "${1}" > "$(j_cgroup)/tasks"
160 # Run a command within a cgroup, if available
166 echo $$ > "$(j_cgroup)/tasks"
174 # Copy ipcc into the jail and add helpful symlinks, so it can communicate
175 # simple commands to the outside
177 ipcc="$(dirname "${0}")/ipcc"
178 cp -f "${ipcc}" "${jroot}/bin"
180 [ -e ee ] || ln -s ipcc ee
181 [ -e ff ] || ln -s ipcc ff
182 [ -e gitk ] || ln -s ipcc gitk
183 [ -e gitka ] || ln -s ipcc gitka
187 # Launch ipcd on jail
189 ipcd_pid="${jdir}/ipcd.pid"
190 # Kill any running IPCd; remove stale pid file and socket
191 if [ -f "${ipcd_pid}" ]
193 pid="$(cat "${ipcd_pid}")"
196 if ps ouser=,command= "${pid}" | grep "^${ORIG_USER}" | grep -q "ipcd"
198 omg "IPCd: already running! Killing"
201 omg "IPCd: removing stale pid file"
202 rm -f "${ipcd_pid}" "${jroot}/tmp/jipc"
205 su "${ORIG_USER}" "${jbase}/j/ipcd" "${jdir}" &
207 echo "${pid}" > "${ipcd_pid}"
213 ipcd_pid="${jdir}/ipcd.pid"
214 [ -f "${ipcd_pid}" ] && pid="$(cat "${ipcd_pid}")"
215 [ "${pid}" ] && kill -TERM ${pid}
221 eval $(j_params "${1}")
222 [ "${jerror}" ] && return 1 || return 0
225 # List available chroots
227 ( cd "${jbase}"; ls -1 ) | while read jname
229 j_is "${jname}" && echo "${jname}"
233 # Chroot is 'up' if /dev/pts and /proc are mounted
235 jname="${1:-${jname}}"
236 eval "$(j_params "${jname}")"
237 [ "${jerror}" ] && wtf "${jerror}"
238 grep -q "^devpts ${jroot}/dev/pts devpts" /proc/mounts || return 1
239 grep -q "^proc ${jroot}/proc proc" /proc/mounts || return 1
243 # Poll chroot status (j_up)
245 [ -z "${1}" ] && set - $(j_ls)
248 j_up "${1}" && meh "$(printf '\033[1;32mup\033[0m')" || meh "$(printf '\033[1;31mdown\033[0m')"
253 # Copy a mount from the host (for things like /dev, /proc, /sys, et alia)
254 j_fstab_copy_mount() {
255 grep -Eq "${1}[[:space:]]" "${tmp_fstab}" || grep -E \
256 "[[:space:]]${1}[[:space:]]" /proc/mounts | head -n 1 >> "${tmp_fstab}"
259 # Prepare fstab for use:
261 # * Make sure certain required mounts exist (/dev/pts, /proc)
262 # * Sort by mountpoint, to ensure mounts are performed in the correct order
263 j_fstab_synthesize() {
264 local tmp_fstab="$(mktemp "${jdir}/.fstab.XXXXXXXX")"
265 [ -f "${jfstab}" ] && sed -e 's/#.*$//; /^\W*$/d' "${jfstab}" > "${tmp_fstab}"
266 j_fstab_copy_mount "/dev/pts"
267 j_fstab_copy_mount "/proc"
268 sort -k2 "${tmp_fstab}"
272 # Process fstab file and mount the filesystems therein; if no fstab file (or if
273 # excludes certain critical filesystems) then augment it. This will also maintain
274 # an mtab file so they can be unmounted later (and so df works inside chroot).
276 # All mountpoints listed in this file will be prepended with ${jroot} prior to
277 # their being mounted or umounted! Make sure the fstab mountpoints are relative
278 # to the chroot root!
280 echo "rootfs / rootfs rw 0 0" > "${jdir}/mtab"
281 j_fstab_synthesize | while read src dir type opts fsck dump
283 mkdir -p "${dir}" || break
284 mount -t "${type}" -o "${opts}" "${src}" "${jroot}/${dir}" || break
285 printf "%s %s %s %s 0 0\n" "${src}" "/${dir##/}" "${type}" "${opts}" >> "${jdir}/mtab"
289 # Process fstab and mtab files and unmount the filesystems therein (opposite j_fstab_mount)
291 [ -s "${jdir}/mtab" ] || j_fstab_synthesize > "${jdir}/mtab"
292 tac "${jdir}/mtab" | while read src dir unused
295 grep -Eq "[[:space:]]${jroot}${dir%%/}[[:space:]]" /proc/mounts || continue
296 umount -f "${jroot}/${dir}" || umount -l "${jroot}/${dir}" || break
303 jname="${1:-${jname}}"
304 j_up "${jname}" && return 0
305 eval "$(j_params "${jname}")"
306 meh "starting ${jname} ..."
308 # Mount filesystems and copy in /etc/mtab
310 cp "${jdir}/mtab" "${jroot}/etc/mtab"
312 # Copy in ipcc and launch ipcd
316 # Start all services in /etc/rcJ.d
317 [ -d "${jroot}/etc/rcJ.d" ] || return 0
318 j_root_eval "${jname}" '( ls -1 /etc/rcJ.d/* 2>&- | grep /S | sort | sed -e "s/$/ start/" | sh )'
321 # Execute command in chroot as root
323 jname="${1:-${jname}}"; shift
324 j_up "${jname}" || wtf "chroot not running"
325 eval "$(j_params "${jname}")"
327 env -i ${jenv} /usr/bin/chroot "${jroot}" /bin/sh -c "${*}"
330 # Execute command in chroot
332 jname="${1:-${jname}}"; shift
333 j_up "${jname}" || wtf "chroot not running"
334 eval "$(j_params "${jname}")"
336 env -i ${jenv} /usr/bin/chroot "${jroot}" /bin/su "${juser:-${USER}}" -c "${*}"
339 # Invoke a shell within the chroot
341 jname="${1:-${jname}}"
342 eval "$(j_params "${jname}")"
343 j_eval "${jname}" "cd; exec ${jshell} -l"
348 jname="${1:-${jname}}"
349 eval "$(j_params "${jname}")"
350 j_up "${jname}" || return 0
351 meh "stopping ${jname} ..."
353 # Stop all services in /etc/rcJ.d
354 j_root_eval "${jname}" '[ -d /etc/rcJ.d ] && ( ls -1 /etc/rcJ.d/* 2>&- | grep /S | sort -r | sed -e "s/$/ stop/" | sh )'
359 # Unmount filesystems
363 # Send a signal to all processes inside a chroot, if possible
366 jname="${2:-${jname}}"
368 [ "$(j_cgroup)" ] || { echo "No cgroup support; cannot signal"; return 1; }
369 pids="$(cat "$(j_cgroup)/tasks")"
372 echo Sending ${sig} to ${pids} ...
373 echo "FROZEN" > "$(j_cgroup)/freezer.state"
375 echo "THAWED" > "$(j_cgroup)/freezer.state"
379 # Kill all processes inside chroot (TERM, then KILL)
381 jname="${1:-${jname}}"
382 eval "$(j_params "${jname}")"
383 meh "killing ${jname} ..."
385 j_signal "TERM" "${jname}"
387 j_signal "KILL" "${jname}"
390 # Hook to allow mounting chroot mounts from command line
392 jname="${1:-${jname}}"
393 eval "$(j_params "${jname}")"
397 # Hook to allow unmounting chroot mounts from command line
399 jname="${1:-${jname}}"
400 eval "$(j_params "${jname}")"
405 init|create) j_init "${jname}" "${@}" ;;
407 status) j_status "${jname}" "${@}" ;;
408 start) j_start "${jname}" ;;
409 shell|enter) j_shell "${jname}" ;;
410 eval) j_eval "${jname}" "${*}" ;;
411 stop) j_stop "${jname}" ;;
412 kill) j_kill "${jname}" ;;
413 mount) j_mount "${jname}" ;;
414 umount) j_umount "${jname}" ;;