From 32cdf8455fd58848b006c85fb9aa05fb5e2a84fb Mon Sep 17 00:00:00 2001 From: CyberLeo Date: Fri, 11 May 2012 13:05:36 -0500 Subject: [PATCH] j: add cgroups support, to isolate and permit signalling of chroot processes --- j | 95 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++---- kill | 1 + 2 files changed, 91 insertions(+), 5 deletions(-) create mode 120000 kill diff --git a/j b/j index dc5d597..3a569fb 100755 --- a/j +++ b/j @@ -24,6 +24,8 @@ Usage: stop undo what 'start' did + kill Forcefully kill all processes; only applicable with cgroups + enter shell spawn a shell or command within the chroot @@ -39,6 +41,11 @@ jbase="${J_BASE:-$(realpath "$(dirname "${0}")/../")}" jname="${J_NAME:-$(basename "${1}")}" #" juser="${J_USER}" +# cgroup to use; if not found, cgroup support will not be used +jcgroup="freezer" +# cgroup parent to use (${cgroup}/${parent}/${jname}) +jcgparent="j" + # Remove chroot name from argument stack, if passed in [ "${J_NAME}" ] || shift @@ -149,6 +156,55 @@ j_params() { ) } +# Find the 'freezer' cgroup, to determine whether the chroot can be further isolated +j_locate_cgroup_mount() { + cgroup="${1:-freezer}" + cat /proc/mounts | while read src dir type opts fsck dump extra + do + [ "${type}" = "cgroup" ] || continue + oldifs="${IFS}" + IFS=',' + set -- ${opts} + IFS=${oldifs} + while [ "${1}" ] + do + [ "${1}" = "${cgroup}" ] && echo "${dir}" && break 2 + shift + done + done +} + +# Cache and expose cgroup mountpoint for given chroot; make sure it exists +j_cgroup() { + [ "${jcg}" ] && { echo "${jcg}"; return; } + jcg="$(j_locate_cgroup_mount "${cgroup}")/${jcgparent}/${jname}" + mkdir -p "${jcg}" + echo "${jcg}" +} + +j_cg_trap() { + [ "$(j_cgroup)" ] || return + while [ "${1}" ] + do + echo "${1}" > "$(j_cgroup)/tasks" + shift + done +} + +# Run a command within a cgroup, if available +j_cg_eval() { + j_cg="$(j_cgroup)" + if [ "$(j_cgroup)" ] + then + sh -ex < "$(j_cgroup)/tasks" +eval "${*}" +EOF + else + eval "${*}" + fi +} + # Copy ipcc into the jail and add helpful symlinks, so it can communicate # simple commands to the outside j_ipc_setup() { @@ -166,7 +222,9 @@ j_ipc_setup() { j_ipc_start() { ipcd_pid="${jdir}/ipcd.pid" su "${ORIG_USER}" "${jbase}/j/ipcd" "${jdir}" & - echo "${!}" > "${ipcd_pid}" + pid="${!}" + echo "${pid}" > "${ipcd_pid}" + j_cg_trap "${pid}" } # Stop ipcd on jail @@ -229,19 +287,19 @@ j_start() { # Execute command in chroot as root j_root_eval() { - jname="${1:-${jname}}" + jname="${1:-${jname}}"; shift j_up "${jname}" || wtf "chroot not running" eval "$(j_params "${jname}")" - shift + j_cg_trap $$ env -i ${jenv} /usr/bin/chroot "${jroot}" /bin/sh -c "${*}" } # Execute command in chroot j_eval() { - jname="${1:-${jname}}" + jname="${1:-${jname}}"; shift j_up "${jname}" || wtf "chroot not running" eval "$(j_params "${jname}")" - shift + j_cg_trap $$ env -i ${jenv} /usr/bin/chroot "${jroot}" /bin/su "${juser:-${USER}}" -c "${*}" } @@ -268,6 +326,32 @@ j_stop() { umount "${jroot}/dev/pts" } +# Send a signal to all processes inside a chroot, if possible +j_signal() { + sig="${1:-HUP}" + jname="${2:-${jname}}" + + [ "$(j_cgroup)" ] || { echo "No cgroup support; cannot signal"; return 1; } + pids="$(cat "$(j_cgroup)/tasks")" + if [ "${pids}" ] + then + echo Sending ${sig} to ${pids} ... + echo "FROZEN" > "$(j_cgroup)/freezer.state" + kill -${sig} ${pids} + echo "THAWED" > "$(j_cgroup)/freezer.state" + fi +} + +j_kill() { + jname="${1:-${jname}}" + eval "$(j_params "${jname}")" + meh "killing ${jname} ..." + + j_signal "TERM" "${jname}" + sleep 2 + j_signal "KILL" "${jname}" +} + case "${cmd}" in init|create) j_init "${jname}" "${@}" ;; ls|list) j_ls ;; @@ -276,5 +360,6 @@ start) j_start "${jname}" ;; shell|enter) j_shell "${jname}" ;; eval) j_eval "${jname}" "${*}" ;; stop) j_stop "${jname}" ;; +kill) j_kill "${jname}" ;; *) pebkac ;; esac diff --git a/kill b/kill new file mode 120000 index 0000000..0fe2fa5 --- /dev/null +++ b/kill @@ -0,0 +1 @@ +j \ No newline at end of file -- 2.42.0