From d7325b15a0949cef77707be5fd0bf864d6338c57 Mon Sep 17 00:00:00 2001 From: P. J. McDermott Date: Tue, 07 Oct 2014 20:30:51 -0400 Subject: Merge branch 'feature/sessions' --- (limited to 'lib/session.sh') diff --git a/lib/session.sh b/lib/session.sh new file mode 100644 index 0000000..05fecc4 --- /dev/null +++ b/lib/session.sh @@ -0,0 +1,219 @@ +# Functions for managing prokit sessions +# +# Copyright (C) 2014 Patrick "P. J." McDermott +# +# This file is part of the ProteanOS Development Kit. +# +# The ProteanOS Development Kit is free software: you can redistribute +# it and/or modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# The ProteanOS Development Kit is distributed in the hope that it +# will be useful, but WITHOUT ANY WARRANTY; without even the implied +# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with the ProteanOS Development Kit. If not, see +# . + +[ "x${_SESSION_SM+set}" = 'xset' ] && return 0 +_SESSION_SM=1 + +use rand +use mutex +use output +use locale +use profile + +session_id= +session_root= +session_arch= +session_plat= +session_mountdir= +session_atexit= +session_sigs= + +session_begin() +{ + local root="${1}" + local mountdir="${2}" + local atexit="${3}" + local installing="${4}" + + rand + session_id=${rand_x} + session_root="${root}" + session_arch="$(cat "${root}/etc/proteanos_arch")" + session_plat="$(cat "${root}/etc/proteanos_plat")" + session_mountdir="${mountdir}" + session_atexit="${atexit}" + + [ -d "${session_root}/prokit" ] || mkdir "${session_root}/prokit" + if [ -f "${session_root}/prokit/installing" ] && ! ${installing}; then + error 2 "$(get_msg 'install_running')" + fi + + session_set_sigs + + if ! mutex_timedlock "${session_root}/prokit/sessions.lock" 5; then + error 2 "$(get_msg 'cant_lock_sessions')" + fi + + # Check for a sessions pool. + if [ -d "${session_root}/prokit/sessions" ]; then + if ! profile_file_systems_mounted "${session_root}" \ + "${session_arch}" "${session_plat}"; then + # If a sessions pool exists but the file systems aren't + # mounted, clean up the old sessions and mount the file + # systems. + rmdir "${session_root}/prokit/sessions/"* + session_mount + fi + else + # If the sessions pool doesn't exist, create it and mount the + # file systems. + mkdir "${session_root}/prokit/sessions" + session_mount + fi + + # Register the session. + mkdir "${session_root}/prokit/sessions/${session_id}" + mkdir "${session_root}/prokit/sessions/${session_id}/wd" + profile_bind_mount "${session_arch}" "${session_plat}" \ + "${session_mountdir}" \ + "${session_root}/prokit/sessions/${session_id}/wd" + + mutex_unlock "${session_root}/prokit/sessions.lock" +} + +session_end() +{ + trap : ${session_sigs} + + ${session_atexit} + + # Unregister the session. + profile_bind_umount "${session_arch}" "${session_plat}" \ + "${session_mountdir}" \ + "${session_root}/prokit/sessions/${session_id}/wd" + rmdir "${session_root}/prokit/sessions/${session_id}/wd" + rmdir "${session_root}/prokit/sessions/${session_id}" + + if ! mutex_is_unlockable "${session_root}/prokit/sessions.lock"; then + if ! mutex_timedlock "${session_root}/prokit/sessions.lock" 5 + then + error 2 "$(get_msg 'cant_lock_sessions')" + fi + fi + + # Clear the sessions pool. If there are no more sessions, unmount the + # file systems. + if rmdir "${session_root}/prokit/sessions" 2>/dev/null; then + session_umount + fi + + mutex_unlock "${session_root}/prokit/sessions.lock" + + trap - ${session_sigs} +} + +session_id() +{ + printf '%d\n' ${session_id} + return 0 +} + +session_dir() +{ + printf '/prokit/sessions/%d\n' ${session_id} + return 0 +} + +session_exec() +{ + local args= + local session_dir= + + args='' + for arg in "${@}"; do + arg="'$(printf '%s\n' "${arg}" | sed "s/'/'\\\\''/g")'" + args="${args} ${arg}" + done + session_dir="/prokit/sessions/${session_id}/wd" + chroot "${session_root}" /bin/sh -c "cd ${session_dir}; ${args}" +} + +session_mount() +{ + local fs= + local dir= + local fstype= + local options= + + while read fs dir fstype options; do + [ "x${dir}" = 'x' ] && continue + mount -t "${fstype}" -o "${options}" "${fs}" \ + "${session_root}/${dir}" + done <<-EOF + $(profile_get_fstab "${session_arch}" "${session_plat}") + EOF +} + +session_umount() +{ + local fs= + local dir= + local fstype= + local options= + + while read fs dir fstype options; do + [ "x${dir}" = 'x' ] && continue + # umount sometimes complains that the /dev file system is busy. + # Here's a kludge to try to handle that. We better make sure + # bind mounts get unmounted; otherwise, `rm -Rf ${root}` can be + # painful. + while ! umount "${session_root}/${dir}"; do + sleep 1 + done + done <<-EOF + $(profile_get_fstab "${session_arch}" "${session_plat}" | \ + sed -n '1!G;h;$p') + EOF +} + +session_set_sigs() +{ + local i= + local sig= + + # We need the signal *number* in the signal handler. The only portable + # and easy way to get the number of a named signal is to search for it + # as in the following loop hack. + i=0 + session_sigs='' + while [ ${i} -lt 127 ]; do + i=$(($i + 1)) + sig="$(kill -l ${i} 2>/dev/null)" || continue + case "${sig}" in + 'HUP' | 'INT' | 'QUIT' | 'ABRT' | 'ALRM' | 'TERM') + session_sigs="${session_sigs} ${i}" + trap "session_handle_sig ${i}" ${i} + ;; + esac + done +} + +session_handle_sig() +{ + local sig="${1}" + + session_end + + if [ "x${exit_status:+set}" = 'xset' ]; then + exit ${exit_status} + else + exit $((128 + $sig)) + fi +} -- cgit v0.9.1