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 # Kill any running IPCd; remove stale pid file and socket
225 if [ -f "${ipcd_pid}" ]
227 pid="$(cat "${ipcd_pid}")"
230 if ps ouser=,command= "${pid}" | grep "^${ORIG_USER}" | grep -q "ipcd"
232 omg "IPCd: already running! Killing"
235 omg "IPCd: removing stale pid file"
236 rm -f "${ipcd_pid}" "${jroot}/tmp/jipc"
239 su "${ORIG_USER}" "${jbase}/j/ipcd" "${jdir}" &
241 echo "${pid}" > "${ipcd_pid}"
247 ipcd_pid="${jdir}/ipcd.pid"
248 [ -f "${ipcd_pid}" ] && pid="$(cat "${ipcd_pid}")"
249 [ "${pid}" ] && kill -TERM ${pid}
255 eval $(j_params "${1}")
256 [ "${jerror}" ] && return 1 || return 0
259 # List available chroots
261 ( cd "${jbase}"; ls -1 ) | while read jname
263 j_is "${jname}" && echo "${jname}"
267 # Chroot is 'up' if /dev/pts and /proc are mounted
269 jname="${1:-${jname}}"
270 eval "$(j_params "${jname}")"
271 [ "${jerror}" ] && wtf "${jerror}"
272 grep -q "^devpts ${jroot}/dev/pts devpts" /proc/mounts || return 1
273 grep -q "^proc ${jroot}/proc proc" /proc/mounts || return 1
277 # Poll chroot status (j_up)
279 [ -z "${1}" ] && set - $(j_ls)
282 j_up "${1}" && meh "$(printf '\033[1;32mup\033[0m')" || meh "$(printf '\033[1;31mdown\033[0m')"
287 # Mount /dev/pts and /proc in the chroot
289 jname="${1:-${jname}}"
290 j_up "${jname}" && return 0
291 eval "$(j_params "${jname}")"
292 meh "starting ${jname} ..."
293 mount -t devpts devpts "${jroot}/dev/pts"
294 mount -t proc proc "${jroot}/proc"
296 # Copy in ipcc and launch ipcd
300 # Start all services in /etc/rcJ.d
301 j_root_eval "${jname}" '[ -d /etc/rcJ.d ] && ( ls -1 /etc/rcJ.d/* 2>&- | grep /S | sort | sed -e "s/$/ start/" | sh )'
304 # Execute command in chroot as root
306 jname="${1:-${jname}}"; shift
307 j_up "${jname}" || wtf "chroot not running"
308 eval "$(j_params "${jname}")"
310 env -i ${jenv} /usr/bin/chroot "${jroot}" /bin/sh -c "${*}"
313 # Execute command in chroot
315 jname="${1:-${jname}}"; shift
316 j_up "${jname}" || wtf "chroot not running"
317 eval "$(j_params "${jname}")"
319 env -i ${jenv} /usr/bin/chroot "${jroot}" /bin/su "${juser:-${USER}}" -c "${*}"
323 jname="${1:-${jname}}"
324 eval "$(j_params "${jname}")"
325 j_eval "${jname}" "cd; exec ${jshell} -l"
328 # Unmount /dev/pts and /proc in the chroot
330 jname="${1:-${jname}}"
331 eval "$(j_params "${jname}")"
332 j_up "${jname}" || return 0
333 meh "stopping ${jname} ..."
335 # Stop all services in /etc/rcJ.d
336 j_root_eval "${jname}" '[ -d /etc/rcJ.d ] && ( ls -1 /etc/rcJ.d/* 2>&- | grep /S | sort -r | sed -e "s/$/ stop/" | sh )'
341 umount "${jroot}/proc"
342 umount "${jroot}/dev/pts"
345 # Send a signal to all processes inside a chroot, if possible
348 jname="${2:-${jname}}"
350 [ "$(j_cgroup)" ] || { echo "No cgroup support; cannot signal"; return 1; }
351 pids="$(cat "$(j_cgroup)/tasks")"
354 echo Sending ${sig} to ${pids} ...
355 echo "FROZEN" > "$(j_cgroup)/freezer.state"
357 echo "THAWED" > "$(j_cgroup)/freezer.state"
362 jname="${1:-${jname}}"
363 eval "$(j_params "${jname}")"
364 meh "killing ${jname} ..."
366 j_signal "TERM" "${jname}"
368 j_signal "KILL" "${jname}"
372 init|create) j_init "${jname}" "${@}" ;;
374 status) j_status "${jname}" "${@}" ;;
375 start) j_start "${jname}" ;;
376 shell|enter) j_shell "${jname}" ;;
377 eval) j_eval "${jname}" "${*}" ;;
378 stop) j_stop "${jname}" ;;
379 kill) j_kill "${jname}" ;;