summaryrefslogtreecommitdiffstats
path: root/src/session.sh
diff options
context:
space:
mode:
Diffstat (limited to 'src/session.sh')
-rw-r--r--src/session.sh210
1 files changed, 210 insertions, 0 deletions
diff --git a/src/session.sh b/src/session.sh
new file mode 100644
index 0000000..6453fae
--- /dev/null
+++ b/src/session.sh
@@ -0,0 +1,210 @@
+# 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
+# <http://www.gnu.org/licenses/>.
+
+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
+}