# 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 }