summaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorP. J. McDermott <pj@pehjota.net>2014-10-07 20:30:51 (EDT)
committer P. J. McDermott <pj@pehjota.net>2014-10-07 20:30:51 (EDT)
commitd7325b15a0949cef77707be5fd0bf864d6338c57 (patch)
treef10d1191c1a05056bef57b2569bd096fdd973d7d /lib
parentd8c058f9e333ae7d99030c64f9c19c47b3a35085 (diff)
parent6ca3ac8f4d34f2f0348d64178dc0b3e8ccd7aa7f (diff)
Merge branch 'feature/sessions'
Diffstat (limited to 'lib')
-rw-r--r--lib/cmd/build.sh126
-rw-r--r--lib/cmd/install.sh5
-rw-r--r--lib/cmd/opkg.sh28
-rw-r--r--lib/cmd/shell.sh26
-rw-r--r--lib/local.mk2
-rw-r--r--lib/mutex.sh57
-rw-r--r--lib/opkg.sh6
-rw-r--r--lib/output.sh5
-rw-r--r--lib/profile.sh31
-rw-r--r--lib/profile/proteanos.sh37
-rw-r--r--lib/session.sh219
11 files changed, 443 insertions, 99 deletions
diff --git a/lib/cmd/build.sh b/lib/cmd/build.sh
index 140ef4a..32dca26 100644
--- a/lib/cmd/build.sh
+++ b/lib/cmd/build.sh
@@ -19,11 +19,15 @@
# <http://www.gnu.org/licenses/>.
use profile
-use chroot
+use session
use rand
use package
use control
+cmd_build_root=
+cmd_build_pkg_dir=
+cmd_build_build_deps=
+
cmd_build_main()
{
local root=
@@ -31,12 +35,7 @@ cmd_build_main()
local arch=
local plat=
local first_arg=
- local prev_arg=
local arg=
- local dir=
- local uname_s=
- local build_deps=
- local f=
if [ ${#} -lt 2 ]; then
print_cmd_usage 'build' >&2
@@ -44,6 +43,7 @@ cmd_build_main()
fi
root="${1}"
+ cmd_build_root="${root}"
shift 1
opkbuild_optstring="$(cat "${root}/usr/share/opkbuild/optstring")"
@@ -51,98 +51,90 @@ cmd_build_main()
plat="$(cat "${root}/etc/proteanos_plat")"
while getopts "${opkbuild_optstring}" opt 2>/dev/null; do
case "${opt}" in
- a)
- arch="${OPTARG}"
- ;;
- p)
- plat="${OPTARG}"
- ;;
+ a) arch="${OPTARG}";;
+ p) plat="${OPTARG}";;
esac
done
first_arg=true
- prev_arg=
+ cmd_build_pkg_dir=''
for arg in "${@}"; do
if ${first_arg}; then
set --
first_arg=false
else
- set -- "${@}" "${prev_arg}"
+ set -- "${@}" "${cmd_build_pkg_dir}"
fi
- prev_arg="${arg}"
+ cmd_build_pkg_dir="${arg}"
done
. "${root}/etc/os-release"
profile_set "${ID}"
- if ! [ -d "${prev_arg}" ]; then
- error 2 "$(get_msg 'cmd_build_not_a_dir')" "${prev_arg}"
+ if ! [ -d "${cmd_build_pkg_dir}" ]; then
+ error 2 "$(get_msg 'cmd_build_not_a_dir')" \
+ "${cmd_build_pkg_dir}"
fi
- package_init "${prev_arg}"
+ package_init "${cmd_build_pkg_dir}"
package_set_substvars "${arch}" "${plat}"
- rand
- dir="/prokit/build.${rand_x}"
- uname_s="$( (uname -s) 2>/dev/null)" || uname_s='unknown'
- mkdir -p "${root}${dir}/pkg"
- case "${uname_s}" in
- 'Linux')
- mount -o bind "${prev_arg}" "${root}${dir}/pkg"
- ;;
- esac
-
- build_deps="$(package_get_build_deps "${arch}" "${plat}")"
- if [ "x${build_deps}" != 'x' ]; then
- mkdir -p "${root}${dir}/builddeps/control" \
- "${root}${dir}/builddeps/data"
- cmd_build_make_deps_pkg "${root}${dir}" "${rand_x}" \
- "${build_deps}"
+ session_begin "${root}" "${cmd_build_pkg_dir}" cmd_build_fini false
+
+ cmd_build_build_deps="$(package_get_build_deps "${arch}" "${plat}")"
+ if [ "x${cmd_build_build_deps}" != 'x' ]; then
+ cmd_build_make_deps_pkg
+ session_exec opkg install ../builddeps.opk
fi
- chroot_exec "${root}" sh <<-EOF
- cd '${dir}/pkg'
- if [ 'x${build_deps}' != 'x' ]; then
- opkg install ../builddeps.opk
- fi
- opkbuild ${@}
- if [ 'x${build_deps}' != 'x' ]; then
- opkg --autoremove remove prokit-builddeps-${rand_x}
- fi
- EOF
+ session_exec opkbuild "${@}"
- case "${uname_s}" in
- 'Linux')
- umount "${root}${dir}/pkg"
- ;;
- esac
- rmdir "${root}${dir}/pkg"
- rm -f "${root}${dir}/builddeps.opk"
- for f in "${root}${dir}/"*; do
- [ -e "${f}" ] || continue
- mv "${f}" "${prev_arg}/.."
- done
- rmdir "${root}${dir}"
+ cmd_build_fini
+
+ session_end
}
cmd_build_make_deps_pkg()
{
- local build_dir="${1}"
- local build_id="${2}"
- local build_deps="${3}"
+ local pkg_dir=
- printf '2.0\n' >"${build_dir}/builddeps/debian-binary"
- cat >"${build_dir}/builddeps/control/control" <<-EOF
- Package: prokit-builddeps-${build_id}
+ pkg_dir="${cmd_build_root}$(session_dir)/builddeps"
+ mkdir -p "${pkg_dir}/control" "${pkg_dir}/data"
+ printf '2.0\n' >"${pkg_dir}/debian-binary"
+ cat >"${pkg_dir}/control/control" <<-EOF
+ Package: prokit-builddeps-$(session_id)
Source: prokit
Version: 1.0
Architecture: all
Platform: all
- Depends: ${build_deps}
+ Depends: ${cmd_build_build_deps}
Description: Build dependencies metapackage generated by prokit
EOF
- (cd "${build_dir}/builddeps/data"; tar -czf ../data.tar.gz .)
- (cd "${build_dir}/builddeps/control"; tar -czf ../control.tar.gz .)
- (cd "${build_dir}/builddeps"; tar -czf ../builddeps.opk \
+ (cd "${pkg_dir}/data"; tar -czf ../data.tar.gz .)
+ (cd "${pkg_dir}/control"; tar -czf ../control.tar.gz .)
+ (cd "${pkg_dir}"; tar -czf ../builddeps.opk \
'debian-binary' 'data.tar.gz' 'control.tar.gz')
- rm -Rf "${build_dir}/builddeps"
+ rm -Rf "${pkg_dir}"
+}
+
+cmd_build_fini()
+{
+ local session_dir=
+ local f=
+
+ session_dir="$(session_dir)"
+
+ if [ "x${cmd_build_build_deps}" != 'x' ]; then
+ session_exec opkg --autoremove remove \
+ prokit-builddeps-$(session_id)
+ rm "${cmd_build_root}${session_dir}/builddeps.opk"
+ # Hack to avoid this code branch if the function is called again
+ # in response to a signal.
+ cmd_build_build_deps=''
+ fi
+
+ for f in "${cmd_build_root}${session_dir}/"*; do
+ [ -e "${f}" ] || continue
+ [ "x${f##*/}" = 'xwd' ] && continue
+ mv "${f}" "${cmd_build_pkg_dir}/.."
+ done
}
diff --git a/lib/cmd/install.sh b/lib/cmd/install.sh
index 6c1e6ce..529382d 100644
--- a/lib/cmd/install.sh
+++ b/lib/cmd/install.sh
@@ -100,9 +100,10 @@ cmd_install_main()
if [ -d "${chroot}" ]; then
error 2 "$(get_msg 'cmd_install_chroot_dir_exists')" "${chroot}"
fi
- if ! mkdir -p "${chroot}/.prokit"; then
+ if ! mkdir -p "${chroot}/.prokit" "${chroot}/prokit"; then
error 2 "$(get_msg 'cmd_install_mkdir_chroot_fail')" "${chroot}"
fi
+ >"${chroot}/prokit/installing"
info "$(get_msg 'cmd_install_find_pkgs')"
cmd_install_find_pkgs "${mirror}" "${suite}" "${arch}" "${plat}" \
@@ -115,6 +116,8 @@ cmd_install_main()
else
profile_configure_system_foreign "${chroot}"
fi
+
+ rm "${chroot}/prokit/installing"
}
cmd_install_find_pkgs()
diff --git a/lib/cmd/opkg.sh b/lib/cmd/opkg.sh
index 3016773..9f0c0de 100644
--- a/lib/cmd/opkg.sh
+++ b/lib/cmd/opkg.sh
@@ -19,15 +19,16 @@
# <http://www.gnu.org/licenses/>.
use profile
-use chroot
+use session
use rand
+cmd_opkg_opks=
+
cmd_opkg_main()
{
local root=
local first_arg=
local install_cmd=
- local opks=
local arg=
local new_fname=
@@ -41,7 +42,7 @@ cmd_opkg_main()
first_arg=true
install_cmd=false
- opks=''
+ cmd_opkg_opks=''
for arg in "${@}"; do
if ${first_arg}; then
set --
@@ -56,7 +57,9 @@ cmd_opkg_main()
fi
rand
new_fname="/tmp/prokit.${rand_x}.opk"
- opks="${opks} ${root}${new_fname}"
+ cmd_opkg_opks="$(printf '%s %s\n' \
+ "${cmd_opkg_opks}" \
+ "${root}${new_fname}")"
cp "${arg}" "${root}${new_fname}"
set -- "${@}" "${new_fname}"
;;
@@ -75,11 +78,22 @@ cmd_opkg_main()
. "${root}/etc/os-release"
profile_set "${ID}"
- chroot_exec "${root}" opkg "${@}"
+ session_begin "${root}" . cmd_opkg_fini false
+
+ session_exec opkg "${@}"
- case "${opks}" in
+ cmd_opkg_fini
+ session_end
+}
+
+cmd_opkg_fini()
+{
+ case "${cmd_opkg_opks}" in
*[!\ ]*)
- rm -f ${opks}
+ rm -f ${cmd_opkg_opks}
+ # Hack to avoid this code branch if the function is
+ # called again in response to a signal.
+ cmd_opkg_opks=''
;;
esac
}
diff --git a/lib/cmd/shell.sh b/lib/cmd/shell.sh
index 5c85d7e..dfff07f 100644
--- a/lib/cmd/shell.sh
+++ b/lib/cmd/shell.sh
@@ -19,12 +19,11 @@
# <http://www.gnu.org/licenses/>.
use profile
-use chroot
+use session
cmd_shell_main()
{
local root=
- local uname_s=
if [ ${#} -lt 1 ]; then
print_cmd_usage 'shell' >&2
@@ -37,28 +36,13 @@ cmd_shell_main()
. "${root}/etc/os-release"
profile_set "${ID}"
- uname_s="$( (uname -s) 2>/dev/null)" || uname_s='unknown'
- mkdir -p "${root}/prokit/wd/${$}"
- case "${uname_s}" in
- 'Linux')
- mount -o bind . "${root}/prokit/wd/${$}"
- ;;
- esac
-
- chroot_mount "${root}"
+ session_begin "${root}" . : false
if [ ${#} -eq 0 ]; then
- chroot "${root}" /bin/sh -c "cd '/prokit/wd/${$}'; /bin/sh"
+ session_exec /bin/sh
printf '\n'
info "$(get_msg 'cmd_shell_exiting')"
else
- chroot "${root}" /bin/sh -c "cd '/prokit/wd/${$}'; ${*}"
+ session_exec "${@}"
fi
- chroot_umount "${root}"
-
- case "${uname_s}" in
- 'Linux')
- umount "${root}/prokit/wd/${$}"
- ;;
- esac
- rmdir "${root}/prokit/wd/${$}"
+ session_end
}
diff --git a/lib/local.mk b/lib/local.mk
index d593289..66d203f 100644
--- a/lib/local.mk
+++ b/lib/local.mk
@@ -9,6 +9,8 @@ pkgdata_sources = \
lib/control.sh \
lib/feed.sh \
lib/pkg.sh \
+ lib/mutex.sh \
+ lib/session.sh \
lib/chroot.sh \
lib/opkg.sh \
lib/rand.sh \
diff --git a/lib/mutex.sh b/lib/mutex.sh
new file mode 100644
index 0000000..fb96e66
--- /dev/null
+++ b/lib/mutex.sh
@@ -0,0 +1,57 @@
+# Functions for locking and unlocking mutex objects
+#
+# 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/>.
+
+[ "x${_MUTEX_SM+set}" = 'xset' ] && return 0
+_MUTEX_SM=1
+
+mutex_trylock()
+{
+ local mutex="${1}"
+
+ (set -C; printf '%d\n' "${$}" >"${mutex}") 2>/dev/null
+}
+
+mutex_timedlock()
+{
+ local mutex="${1}"
+ local timeout="${2}"
+
+ while ! mutex_trylock "${mutex}"; do
+ [ ${timeout} -eq 0 ] && return 1
+ timeout=$(($timeout - 1))
+ sleep 1
+ done
+
+ return 0
+}
+
+mutex_unlock()
+{
+ local mutex="${1}"
+
+ rm "${mutex}"
+}
+
+mutex_is_unlockable()
+{
+ local mutex="${1}"
+
+ [ "x$(cat "${mutex}" 2>/dev/null)" = "x${$}" ]
+}
diff --git a/lib/opkg.sh b/lib/opkg.sh
index 1966ebc..2b942d3 100644
--- a/lib/opkg.sh
+++ b/lib/opkg.sh
@@ -21,12 +21,14 @@
[ "x${_OPKG_SM+set}" = 'xset' ] && return 0
_OPKG_SM=1
-use chroot
+use session
opkg_install_all()
{
local root="${1}"
- chroot_exec "${root}" /bin/sh -c \
+ session_begin "${root}" . : true
+ session_exec /bin/sh -c \
'opkg install $(opkg list-installed | cut -d " " -f 1)'
+ session_end
}
diff --git a/lib/output.sh b/lib/output.sh
index ffe982d..877c7fc 100644
--- a/lib/output.sh
+++ b/lib/output.sh
@@ -30,7 +30,10 @@ error()
printf '%s: Error: ' "${0##*/}" >&2
printf "${fmt}\n" "${@}" >&2
- exit ${status}
+ # In a subshell, this will have no effect, so the shell's exit status
+ # will be 128+SIGINT. Meh.
+ exit_status=${status}
+ kill -s INT ${$}
}
warn()
diff --git a/lib/profile.sh b/lib/profile.sh
index caa1160..5ec2309 100644
--- a/lib/profile.sh
+++ b/lib/profile.sh
@@ -124,6 +124,37 @@ profile_get_fstab()
"prof_${profile}_get_fstab" "${arch}" "${plat}"
}
+profile_file_systems_mounted()
+{
+ local root="${1}"
+ local arch="${1}"
+ local plat="${2}"
+
+ "prof_${profile}_file_systems_mounted" "${root}" "${arch}" "${plat}"
+}
+
+profile_bind_mount()
+{
+ local arch="${1}"
+ local plat="${2}"
+ local olddir="${3}"
+ local newdir="${4}"
+
+ "prof_${profile}_bind_mount" "${arch}" "${plat}" \
+ "${olddir}" "${newdir}"
+}
+
+profile_bind_umount()
+{
+ local arch="${1}"
+ local plat="${2}"
+ local olddir="${3}"
+ local newdir="${4}"
+
+ "prof_${profile}_bind_umount" "${arch}" "${plat}" \
+ "${olddir}" "${newdir}"
+}
+
profile_configure_system_native()
{
local root="${1}"
diff --git a/lib/profile/proteanos.sh b/lib/profile/proteanos.sh
index 2e0739c..ee3627d 100644
--- a/lib/profile/proteanos.sh
+++ b/lib/profile/proteanos.sh
@@ -175,6 +175,43 @@ prof_proteanos_get_fstab()
esac
}
+prof_proteanos_file_systems_mounted()
+{
+ local root="${1}"
+ local arch="${2}"
+ local plat="${3}"
+
+ [ -e "${root}/dev/null" ]
+}
+
+prof_proteanos_bind_mount()
+{
+ local arch="${1}"
+ local plat="${2}"
+ local olddir="${3}"
+ local newdir="${4}"
+
+ case "${arch}" in
+ *-linux-*)
+ mount -o bind "${olddir}" "${newdir}"
+ ;;
+ esac
+}
+
+prof_proteanos_bind_umount()
+{
+ local arch="${1}"
+ local plat="${2}"
+ local olddir="${3}"
+ local newdir="${4}"
+
+ case "${arch}" in
+ *-linux-*)
+ umount "${newdir}"
+ ;;
+ esac
+}
+
prof_proteanos_configure_system_native()
{
local root="${1}"
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
+# <http://www.gnu.org/licenses/>.
+
+[ "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
+}