summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorP. J. McDermott <pj@pehjota.net>2015-12-07 12:07:15 (EST)
committer P. J. McDermott <pj@pehjota.net>2015-12-07 12:07:15 (EST)
commit34a77f7822126263fed5c72aedaa65b58fee94ac (patch)
treea3404e611672f64126298e2f0f6d3398b014851e /src
parent84f3cccfb57fc8270b2ae14f0adf2c2ff24cc69b (diff)
parent41f638c961d5c7832e2b18a9f2e45a09b54dc53e (diff)
Merge branch 'feature/use-shld'
Diffstat (limited to 'src')
-rw-r--r--src/archplat.sh144
-rw-r--r--src/block.sh86
-rw-r--r--src/cmd.sh122
-rw-r--r--src/cmd/build.sh152
-rw-r--r--src/cmd/help.sh47
-rw-r--r--src/cmd/install.sh81
-rw-r--r--src/cmd/installer-pc.sh101
-rw-r--r--src/cmd/local.mk8
-rw-r--r--src/cmd/opkg.sh112
-rw-r--r--src/cmd/shell.sh62
-rw-r--r--src/cmd/version.sh33
-rw-r--r--src/control.sh147
-rw-r--r--src/deps.sh156
-rw-r--r--src/dir.sh56
-rw-r--r--src/fd.sh92
-rw-r--r--src/feed.sh137
-rw-r--r--src/getopt.sh52
-rw-r--r--src/install.sh266
-rw-r--r--src/local.mk29
-rw-r--r--src/locale.sh84
-rw-r--r--src/main.sh (renamed from src/prokit.sh)38
-rw-r--r--src/mutex.sh54
-rw-r--r--src/opkg.sh29
-rw-r--r--src/output.sh56
-rw-r--r--src/package.sh62
-rw-r--r--src/package/2.sh77
-rw-r--r--src/package/local.mk2
-rw-r--r--src/pkg.sh51
-rw-r--r--src/profile.sh165
-rw-r--r--src/profile/local.mk2
-rw-r--r--src/profile/proteanos.sh258
-rw-r--r--src/rand.sh42
-rw-r--r--src/session.sh210
-rw-r--r--src/substvars.sh103
-rw-r--r--src/vardata.sh52
35 files changed, 3131 insertions, 37 deletions
diff --git a/src/archplat.sh b/src/archplat.sh
new file mode 100644
index 0000000..b8324a0
--- /dev/null
+++ b/src/archplat.sh
@@ -0,0 +1,144 @@
+# Functions for matching architectures and platforms
+#
+# Copyright (C) 2012 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/>.
+
+match_arch()
+{
+ local arch="${1}"
+ local arch_field="${2}"
+ local a=
+ local a_ere=
+
+ for a in ${arch_field}; do
+
+ # "all" == "all"
+ if [ "${arch}" = 'all' ]; then
+ if [ "${a}" = 'all' ]; then
+ return 0
+ else
+ continue
+ fi
+ fi
+
+ # "foo-bar-baz" == "any"
+ if [ "${a}" = 'any' ]; then
+ return 0
+ fi
+
+ # Generate an ERE to match hyphenated architectures.
+ a_ere="$(printf '%s\n' "${a}" | sed \
+ 's/^any-/[^-]+-/;s/-any-/-[^-]+-/g;s/-any$/-[^-]+/g')"
+
+ # "foo-bar-baz" == "foo-any-any"
+ if echo "${arch}" | grep -E "${a_ere}" >/dev/null 2>&1; then
+ return 0
+ fi
+
+ done
+
+ # Nothing matched.
+ return 1
+}
+
+match_plat()
+{
+ local plat="${1}"
+ local plat_field="${2}"
+ local p=
+
+ for p in ${plat_field}; do
+ if [ "x${plat}" = 'xall' ]; then
+ if [ "x${p}" = 'xall' ]; then
+ return 0
+ else
+ continue
+ fi
+ fi
+ if [ "x${p}" = 'xany' ]; then
+ return 0
+ fi
+ if [ "x${p}" = "x${plat}" ]; then
+ return 0
+ fi
+ done
+
+ return 1
+}
+
+arch_is_concerned()
+{
+ local host_arch="${1}"
+ local arches="${2}"
+ local arch=
+ local not_arch=
+ local seen_arch=
+
+ if [ "x${arches}" = 'x' ]; then
+ return 0
+ else
+ seen_arch=1
+ for arch in ${arches}; do
+ not_arch="${arch#!}"
+ if [ "x${not_arch}" != "x${arch}" ]; then
+ if match_arch "${host_arch}" "${not_arch}"
+ then
+ seen_arch=1
+ break
+ elif [ "x${host_arch}" != 'xall' ]; then
+ seen_arch=0
+ fi
+ elif match_arch "${host_arch}" "${arch}"; then
+ seen_arch=0
+ break
+ fi
+ done
+ return ${seen_arch}
+ fi
+}
+
+plat_is_concerned()
+{
+ local host_plat="${1}"
+ local plats="${2}"
+ local plat=
+ local not_plat=
+ local seen_plat=
+
+ if [ "x${plats}" = 'x' ]; then
+ return 0
+ else
+ seen_plat=1
+ for plat in ${plats}; do
+ not_plat="${plat#!}"
+ if [ "x${not_plat}" != "x${plat}" ]; then
+ if match_plat "${host_plat}" "${not_plat}"
+ then
+ seen_plat=1
+ break
+ elif [ "x${host_plat}" != 'xall' ]; then
+ seen_plat=0
+ fi
+ elif match_plat "${host_plat}" "${plat}"; then
+ seen_plat=0
+ break
+ fi
+ done
+ return ${seen_plat}
+ fi
+}
diff --git a/src/block.sh b/src/block.sh
new file mode 100644
index 0000000..f056305
--- /dev/null
+++ b/src/block.sh
@@ -0,0 +1,86 @@
+# Functions for mounting and unmounting block devices
+#
+# Copyright (C) 2015 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/>.
+
+is_block()
+{
+ local dev="${1}"
+
+ [ -b "${dev}" ]
+ return ${?}
+}
+
+check_block()
+{
+ local dev="${1}"
+
+ if ! [ -b "${dev}" ]; then
+ error 2 "$(get_msg 'block_device_invalid')" "${dev}"
+ fi
+}
+
+block_mount()
+{
+ local dev="${1}"
+ local dir=
+
+ check_block "${dev}"
+
+ rand
+ dir="$(get_vardata_dir 'mount')/block-$(printf '%010d' ${rand_x})"
+ if ! mkdir "${dir}"; then
+ error 2 "$(get_msg 'block_mkdir_fail')" "${dir}"
+ fi
+
+ if ! mount "${dev}" "${dir}"; then
+ rmdir "${dir}"
+ error 2 "$(get_msg 'block_mount_fail')" "${dev}"
+ fi
+
+ printf '%s' "${dir}"
+ return 0
+}
+
+block_umount()
+{
+ local dir="${1}"
+ local i=
+ local timed_out=
+
+ i=0
+ timed_out=false
+ while ! umount "${dir}"; do
+ i=$(($i + 1))
+ if [ ${i} -eq 10 ]; then
+ timed_out=true
+ break
+ fi
+ sleep 1
+ done
+
+ if ${timed_out}; then
+ error 2 "$(get_msg 'block_umount_fail')" "${dir}"
+ fi
+
+ if ! rmdir "${dir}"; then
+ error 2 "$(get_msg 'block_rmdir_fail')" "${dir}"
+ fi
+
+ return 0
+}
diff --git a/src/cmd.sh b/src/cmd.sh
new file mode 100644
index 0000000..d4e7824
--- /dev/null
+++ b/src/cmd.sh
@@ -0,0 +1,122 @@
+# Command-related functions
+#
+# Copyright (C) 2013 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/>.
+
+cmds=' '
+running_cmd=
+running_cmd_clean=
+
+register_cmd()
+{
+ local cmd="${1}"
+ shift 1
+
+ cmds="${cmds}${cmd} "
+ return 0
+}
+
+print_opt_summaries()
+{
+ local padding=
+ local opt=
+ local opt_out=
+ local summary=
+
+ padding="$(printf '%24s' '')"
+ for opt in $(printf '%s' "${OPTSTRING}" | sed 's/\([a-zA-Z0-9]\)/ \1/g')
+ do
+ if [ ${#opt} -eq 1 ]; then
+ # No argument expected.
+ opt_out="-${opt}"
+ else
+ # Argument expected.
+ opt="${opt%?}"
+ opt_out="-${opt} $(get_msg "opt_${opt}_arg")"
+ fi
+ if [ ${#opt_out} -gt 20 ]; then
+ printf ' %s\n%24s' "${opt_out}" ''
+ else
+ printf ' %-20s ' "${opt_out}"
+ fi
+ summary="$(get_msg "opt_${opt}_summary")"
+ printf '%s\n' "${summary}" | fold -s -w 56 | \
+ sed "2,\$s/^/${padding}/;"
+ done
+}
+
+print_cmd_summaries()
+{
+ local padding=
+ local cmd=
+ local cmd_clean=
+ local summary=
+
+ padding="$(printf '%24s' '')"
+ for cmd in ${cmds}; do
+ if [ ${#cmd} -gt 20 ]; then
+ printf ' %s\n%24s' "${cmd}" ''
+ else
+ printf ' %-20s ' "${cmd}"
+ fi
+ cmd_clean="$(printf '%s' "${cmd}" | \
+ tr '[A-Z]' '[a-z]' | tr -C '[a-z0-9_]' '_')"
+ summary="$(get_msg "cmd_${cmd_clean}_summary")"
+ printf '%s\n' "${summary}" | fold -s -w 56 | \
+ sed "2,\$s/^/${padding}/;"
+ done
+}
+
+print_cmd_usage()
+{
+ local cmd="${1}"
+ local cmd_clean=
+ local usage=
+
+ cmd_clean="$(printf '%s' "${cmd}" | \
+ tr '[A-Z]' '[a-z]' | tr -C '[a-z0-9_]' '_')"
+ usage="$(get_msg "cmd_${cmd_clean}_usage")"
+
+ printf "$(get_msg 'cmd_usage')\n" "${0}" "${cmd}" "${usage}"
+}
+
+is_cmd()
+{
+ local cmd="${1}"
+
+ [ "x$(printf '%s\n' ${cmds} | grep "^${cmd}$")" = "x${cmd}" ]
+}
+
+run_cmd()
+{
+ local cmd="${1}"
+ local cmd_clean=
+ shift
+
+ cmd_clean="$(printf '%s' "${cmd}" | \
+ tr '[A-Z]' '[a-z]' | tr -C '[a-z0-9_]' '_')"
+ if is_cmd "${cmd}"; then
+ running_cmd="${cmd}"
+ running_cmd_clean="${cmd_clean}"
+ "cmd_${cmd_clean}_main" "${@}"
+ running_cmd=''
+ running_cmd_clean=''
+ else
+ error 1 "$(get_msg 'cmd_not_found')" "${cmd}"
+ fi
+}
diff --git a/src/cmd/build.sh b/src/cmd/build.sh
new file mode 100644
index 0000000..8d12691
--- /dev/null
+++ b/src/cmd/build.sh
@@ -0,0 +1,152 @@
+# "build" command
+#
+# 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/>.
+
+cmd_build_root=
+cmd_build_pkg_dir=
+cmd_build_build_deps=
+
+cmd_build_main()
+{
+ local root=
+ local dev=
+ local opkbuild_optstring=
+ local arch=
+ local plat=
+ local first_arg=
+ local arg=
+
+ if [ ${#} -lt 2 ]; then
+ print_cmd_usage 'build' >&2
+ exit 1
+ fi
+
+ root="${1}"
+ shift 1
+
+ dev=''
+ if is_block "${root}"; then
+ dev="${root}"
+ root="$(block_mount "${dev}")"
+ fi
+
+ cmd_build_root="${root}"
+
+ opkbuild_optstring="$(cat "${root}/usr/share/opkbuild/optstring")"
+ arch="$(cat "${root}/etc/proteanos_arch")"
+ plat="$(cat "${root}/etc/proteanos_plat")"
+ while getopts "${opkbuild_optstring}" opt 2>/dev/null; do
+ case "${opt}" in
+ a) arch="${OPTARG}";;
+ p) plat="${OPTARG}";;
+ esac
+ done
+
+ first_arg=true
+ cmd_build_pkg_dir=''
+ for arg in "${@}"; do
+ if ${first_arg}; then
+ set --
+ first_arg=false
+ else
+ set -- "${@}" "${cmd_build_pkg_dir}"
+ fi
+ cmd_build_pkg_dir="${arg}"
+ done
+
+ . "${root}/etc/os-release"
+ profile_set "${ID}"
+
+ if ! [ -d "${cmd_build_pkg_dir}" ]; then
+ error 2 "$(get_msg 'cmd_build_not_a_dir')" \
+ "${cmd_build_pkg_dir}"
+ fi
+ package_init "${cmd_build_pkg_dir}"
+ package_set_substvars "${arch}" "${plat}"
+
+ 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
+
+ session_exec opkbuild "${@}"
+
+ cmd_build_fini
+
+ session_end
+
+ if [ "x${dev}" != 'x' ]; then
+ block_umount "${root}"
+ fi
+}
+
+cmd_build_make_deps_pkg()
+{
+ local pkg_dir=
+
+ 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: ${cmd_build_build_deps}
+ Description: Build dependencies metapackage generated by prokit
+ EOF
+ (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 "${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
+}
+
+cmd_build_register()
+{
+ register_cmd 'build'
+}
+__init cmd_build_register
diff --git a/src/cmd/help.sh b/src/cmd/help.sh
new file mode 100644
index 0000000..c699c73
--- /dev/null
+++ b/src/cmd/help.sh
@@ -0,0 +1,47 @@
+# "help" command
+#
+# Copyright (C) 2013 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/>.
+
+cmd_help_main()
+{
+ local cmd=
+
+ if [ ${#} -eq 1 ]; then
+ cmd="${1}"
+ if is_cmd "${cmd}"; then
+ print_cmd_usage "${cmd}"
+ return 0
+ fi
+ fi
+
+ printf "$(get_msg 'cmd_help_head')\n\n" "${0}"
+
+ printf "$(get_msg 'cmd_help_opts_head')\n"
+ print_opt_summaries
+ printf '\n'
+
+ printf "$(get_msg 'cmd_help_summary_head')\n"
+ print_cmd_summaries
+}
+
+cmd_help_register()
+{
+ register_cmd 'help'
+}
+__init cmd_help_register
diff --git a/src/cmd/install.sh b/src/cmd/install.sh
new file mode 100644
index 0000000..e0bdc4e
--- /dev/null
+++ b/src/cmd/install.sh
@@ -0,0 +1,81 @@
+# "install" command
+#
+# Copyright (C) 2013, 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/>.
+
+cmd_install_optstring='a:p:m:F'
+
+cmd_install_main()
+{
+ local suite=
+ local chroot=
+ local profile=
+ local foreign=
+ local dev=
+
+ if ! get_options "${@}"; then
+ print_cmd_usage 'install' >&2
+ exit 1
+ fi
+ shift $(($OPTIND - 1))
+
+ if [ ${#} -lt 2 ]; then
+ print_cmd_usage 'install' >&2
+ exit 1
+ fi
+
+ # TODO: Consider making suite optional, getting a default suite from the
+ # profile.
+ suite="${1}"
+ chroot="${2}"
+
+ if [ "x${suite%%:*}" != "x${suite}" ]; then
+ profile="${suite%%:*}"
+ suite="${suite#*:}"
+ else
+ profile='proteanos'
+ fi
+ profile_set "${profile}"
+ suite="$(profile_normalize_suite "${suite}")"
+
+ if [ "x${cmd_install_opt_F+set}" = 'xset' ]; then
+ foreign=true
+ else
+ foreign=false
+ fi
+
+ dev=''
+ if is_block "${chroot}"; then
+ dev="${chroot}"
+ chroot="$(block_mount "${dev}")"
+ fi
+
+ install_system "${cmd_install_opt_m-}" "${suite}" \
+ "${cmd_install_opt_a-}" "${cmd_install_opt_p-}" \
+ "${chroot}" "${foreign}"
+
+ if [ "x${dev}" != 'x' ]; then
+ block_umount "${chroot}"
+ fi
+}
+
+cmd_install_register()
+{
+ register_cmd 'install'
+}
+__init cmd_install_register
diff --git a/src/cmd/installer-pc.sh b/src/cmd/installer-pc.sh
new file mode 100644
index 0000000..dd01506
--- /dev/null
+++ b/src/cmd/installer-pc.sh
@@ -0,0 +1,101 @@
+# "installer-pc" command
+#
+# 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/>.
+
+cmd_installer_pc_optstring='a:p:m:'
+
+cmd_installer_pc_main()
+{
+ local suite=
+ local dev=
+ local profile=
+ local root=
+ local b4=
+ local b3=
+ local b2=
+ local b1=
+
+ if ! get_options "${@}"; then
+ print_cmd_usage 'installer-pc' >&2
+ exit 1
+ fi
+ shift $(($OPTIND - 1))
+
+ if [ "x${cmd_installer_pc_opt_a-}" = 'x' ]; then
+ print_cmd_usage 'installer-pc' >&2
+ exit 1
+ fi
+ if [ "x${cmd_installer_pc_opt_p-}" = 'x' ]; then
+ print_cmd_usage 'installer-pc' >&2
+ exit 1
+ fi
+ if [ ${#} -ne 2 ]; then
+ print_cmd_usage 'installer-pc' >&2
+ exit 1
+ fi
+
+ suite="${1}"
+ dev="${2}"
+ check_block "${dev}"
+
+ if [ "x${suite%%:*}" != "x${suite}" ]; then
+ profile="${suite%%:*}"
+ suite="${suite#*:}"
+ else
+ profile='proteanos'
+ fi
+ profile_set "${profile}"
+ suite="$(profile_normalize_suite "${suite}")"
+
+ cmd_installer_pc_make_partition_and_fs "${dev}"
+ root="$(block_mount "${dev}1")"
+
+ install_system "${cmd_installer_pc_opt_m-}" "${suite}" \
+ "${cmd_installer_pc_opt_a-}" "${cmd_installer_pc_opt_p-}" \
+ "${root}" false
+
+ if [ -f "${root}/sbin/lilo" ]; then
+ read b4 b3 b2 b1 <<-EOF
+ $(od -An -tx1 -v -j 440 -N 4 "${dev}")
+ EOF
+ # This ln command won't be needed once lilo 24.1-1 is uploaded.
+ ln "${root}/boot/vmlinuz" "${root}/boot/vmlinuz.old"
+ session_begin "${root}" . : false
+ session_exec install-lilo \
+ "${dev}" "PARTUUID=${b1}${b2}${b3}${b4}-01"
+ session_end
+ fi
+
+ block_umount "${root}"
+}
+
+cmd_installer_pc_make_partition_and_fs()
+{
+ local dev="${1}"
+
+ dd if=/dev/zero of="${dev}" bs=512 count=1
+ printf 'n\np\n1\n\n\nt\n83\na\n1\nw\n' | fdisk "${dev}"
+ mke2fs -t ext4 "${dev}1"
+}
+
+cmd_installer_pc_register()
+{
+ register_cmd 'installer-pc'
+}
+__init cmd_installer_pc_register
diff --git a/src/cmd/local.mk b/src/cmd/local.mk
new file mode 100644
index 0000000..a6dd3bd
--- /dev/null
+++ b/src/cmd/local.mk
@@ -0,0 +1,8 @@
+prokit_SOURCES += \
+ src/cmd/help.sh \
+ src/cmd/version.sh \
+ src/cmd/install.sh \
+ src/cmd/shell.sh \
+ src/cmd/opkg.sh \
+ src/cmd/build.sh \
+ src/cmd/installer-pc.sh
diff --git a/src/cmd/opkg.sh b/src/cmd/opkg.sh
new file mode 100644
index 0000000..e1fc8ce
--- /dev/null
+++ b/src/cmd/opkg.sh
@@ -0,0 +1,112 @@
+# "opkg" command
+#
+# 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/>.
+
+cmd_opkg_opks=
+
+cmd_opkg_main()
+{
+ local root=
+ local dev=
+ local first_arg=
+ local install_cmd=
+ local arg=
+ local new_fname=
+
+ if [ ${#} -lt 1 ]; then
+ print_cmd_usage 'opkg' >&2
+ exit 1
+ fi
+
+ root="${1}"
+ shift 1
+
+ dev=''
+ if is_block "${root}"; then
+ dev="${root}"
+ root="$(block_mount "${dev}")"
+ fi
+
+ first_arg=true
+ install_cmd=false
+ cmd_opkg_opks=''
+ for arg in "${@}"; do
+ if ${first_arg}; then
+ set --
+ first_arg=false
+ fi
+ if ${install_cmd}; then
+ case "${arg}" in
+ *.opk|*.ipk|*.deb)
+ if ! [ -e "${arg}" ]; then
+ set -- "${@}" "${arg}"
+ continue
+ fi
+ rand
+ new_fname="/tmp/prokit.${rand_x}.opk"
+ cmd_opkg_opks="$(printf '%s %s\n' \
+ "${cmd_opkg_opks}" \
+ "${root}${new_fname}")"
+ cp "${arg}" "${root}${new_fname}"
+ set -- "${@}" "${new_fname}"
+ ;;
+ *)
+ set -- "${@}" "${arg}"
+ ;;
+ esac
+ else
+ set -- "${@}" "${arg}"
+ if [ "x${arg}" = 'xinstall' ]; then
+ install_cmd=true
+ fi
+ fi
+ done
+
+ . "${root}/etc/os-release"
+ profile_set "${ID}"
+
+ session_begin "${root}" . cmd_opkg_fini false
+
+ session_exec opkg "${@}"
+
+ cmd_opkg_fini
+ session_end
+
+ if [ "x${dev}" != 'x' ]; then
+ block_umount "${root}"
+ fi
+}
+
+cmd_opkg_fini()
+{
+ case "${cmd_opkg_opks}" in
+ *[!\ ]*)
+ 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
+}
+
+cmd_opkg_register()
+{
+ register_cmd 'opkg'
+}
+__init cmd_opkg_register
diff --git a/src/cmd/shell.sh b/src/cmd/shell.sh
new file mode 100644
index 0000000..468da0f
--- /dev/null
+++ b/src/cmd/shell.sh
@@ -0,0 +1,62 @@
+# "shell" command
+#
+# 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/>.
+
+cmd_shell_main()
+{
+ local root=
+ local dev=
+
+ if [ ${#} -lt 1 ]; then
+ print_cmd_usage 'shell' >&2
+ exit 1
+ fi
+
+ root="${1}"
+ shift 1
+
+ dev=''
+ if is_block "${root}"; then
+ dev="${root}"
+ root="$(block_mount "${dev}")"
+ fi
+
+ . "${root}/etc/os-release"
+ profile_set "${ID}"
+
+ session_begin "${root}" . : false
+ if [ ${#} -eq 0 ]; then
+ session_exec /bin/sh
+ printf '\n'
+ info "$(get_msg 'cmd_shell_exiting')"
+ else
+ session_exec "${@}"
+ fi
+ session_end
+
+ if [ "x${dev}" != 'x' ]; then
+ block_umount "${root}"
+ fi
+}
+
+cmd_shell_register()
+{
+ register_cmd 'shell'
+}
+__init cmd_shell_register
diff --git a/src/cmd/version.sh b/src/cmd/version.sh
new file mode 100644
index 0000000..0105f20
--- /dev/null
+++ b/src/cmd/version.sh
@@ -0,0 +1,33 @@
+# "version" command
+#
+# Copyright (C) 2013 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/>.
+
+cmd_version_main()
+{
+ printf '%s (%s) %s\n' "${0##*/}" "${PACKAGE_NAME}" "${PACKAGE_VERSION}"
+
+ printf "$(get_msg 'cmd_version_copyright')\n" \
+ '2012, 2013, 2014' 'Patrick "P. J." McDermott'
+}
+
+cmd_version_register()
+{
+ register_cmd 'version'
+}
+__init cmd_version_register
diff --git a/src/control.sh b/src/control.sh
new file mode 100644
index 0000000..b25d00b
--- /dev/null
+++ b/src/control.sh
@@ -0,0 +1,147 @@
+# Functions for parsing control files.
+#
+# Copyright (C) 2012, 2013 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/>.
+
+control_file=
+control_line_nr=
+
+parse_control()
+{
+ local field_cb="${2}"
+ local paragraph_cb="${3}"
+ local req_fields="${4}"
+ local in_paragraph=
+ local line=
+ local para_req_fields=
+ local got_fields=
+ local name=
+ local value=
+
+ control_file="${1}"
+ control_line_nr=0
+
+ in_paragraph='false'
+
+ while IFS='' read -r line; do
+ control_line_nr=$(($control_line_nr + 1))
+ if [ "x$(echo ${line})" = 'x' ]; then
+ # Paragraph end.
+ if ${in_paragraph}; then
+ # The first line is blank to consolidate
+ # initialization code (see heredocument below).
+ in_paragraph='false'
+ if [ "x${name}" != 'x' ]; then
+ if ! "${field_cb}" "${name}" "${value}"
+ then
+ return 0
+ fi
+ fi
+ if ! "${paragraph_cb}"; then
+ return 0
+ fi
+ if [ "x${para_req_fields}" != 'x' ]; then
+ para_req_fields="$(printf "%s$(get_msg \
+ 'list_item_separator')" \
+ ${para_req_fields})"
+ parse_control_error \
+ 'control_missing_fields' \
+ "${para_req_fields}"
+ fi
+ fi
+ para_req_fields="$(printf '%s\n' ${req_fields})"
+ got_fields="${LF}"
+ name=''
+ value=''
+ elif [ "x${line#\#}" != "x${line}" ]; then
+ # Comment.
+ in_paragraph='true'
+ elif [ "x${line# }" = "x${line}" ]; then
+ # "Name: Value" line.
+ in_paragraph='true'
+ if [ "x${name}" != 'x' ]; then
+ if ! "${field_cb}" "${name}" "${value}"; then
+ return 0
+ fi
+ fi
+ name="${line%%:*}"
+ value="${line#*:}"
+ value="${value# }"
+ if [ "x${name}" = 'x' -o "x${name}" = "x${line}" ]; then
+ parse_control_error 'control_bad_nv'
+ continue
+ fi
+ if [ "x${para_req_fields}" != 'x' ]; then
+ # Remove field from list of required fields.
+ para_req_fields="$(printf '%s' "${para_req_fields}" | \
+ grep -Fv "${name}")"
+ fi
+ if [ "x${got_fields%${LF}${name}${LF}*}" != \
+ "x${got_fields}" ]; then
+ # Duplicate field.
+ parse_control_error 'control_duplicate_field' \
+ "${name}"
+ else
+ got_fields="${got_fields}${name}${LF}"
+ fi
+ else
+ # Continuation line.
+ in_paragraph='true'
+ if [ "x${name}" = 'x' ]; then
+ # Expecting a "Name: Value" line.
+ parse_control_error 'control_found_continuation'
+ continue
+ fi
+ value="${value}${LF}${line# }"
+ fi
+ done <<-EOF
+
+ $(cat "${control_file}")
+
+ EOF
+ # The first blank line above triggers the paragraph end code to
+ # consolidate initialization code.
+ # The second blank line above is needed because the command substitution
+ # removes any trailing newlines.
+
+ if [ "x${name}" != 'x' ]; then
+ if ! "${field_cb}" "${name}" "${value}"; then
+ return 0
+ fi
+ fi
+
+ return 0
+}
+
+parse_control_error()
+{
+ local msgid="${1}"
+ local file_info=
+ shift 1
+
+ if [ ${control_line_nr} -eq 0 ]; then
+ file_info="$(printf '%20s:' "${control_file}")"
+ else
+ file_info="$(printf '%20s(l%d):' "${control_file}" \
+ "${control_line_nr}")"
+ fi
+
+ warn "${file_info} $(get_msg "${msgid}")" "${@}"
+
+ return 0
+}
diff --git a/src/deps.sh b/src/deps.sh
new file mode 100644
index 0000000..862caff
--- /dev/null
+++ b/src/deps.sh
@@ -0,0 +1,156 @@
+# Functions for parsing dependency field values
+#
+# Copyright (C) 2012, 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/>.
+
+parse_dep()
+{
+ local dep="${1}"
+ local host_arch="${2}"
+ local host_plat="${3}"
+ local pkgarchqual=
+ local pkg=
+ local archqual=
+ local relver=
+ local rel=
+ local ver=
+ local arches=
+ local plats=
+
+ dep="$(printf '%s\n' "${dep}" | sed -n '
+ H; # Store each input line in hold space.
+ ${ # On the last line:
+ g; # Restore everything to pattern space.
+ s/[\t\n]/ /g; # Replace tabs & newlines with spaces.
+ s/ *\([(\[<]\) */ \1/g; # One space before "(", "[", and "<".
+ s/ *\([)\]>]\) */\1 /g; # One space after "(", "[", and "<".
+ s/ *\(<[<=]\) */\1 /g; # One space after "<<" and "<=".
+ s/ *\(>[>=]\) */\1 /g; # One space after ">>" and ">=".
+ s/ *\(=\) */\1 /g; # One space after "=".
+ s/^ *//; # Remove leading spaces.
+ s/ *$//; # Remove trailing spaces.
+ s/ */ /g; # Remove duplicate spaces.
+ p; # Print the pattern space.
+ }; # End.
+ ')"
+
+ # dep is now of the form:
+ # pkg[:archqual] [(rel ver)] [\[arches\]] [<plats>]
+
+ pkgarchqual="${dep%% *}"
+ dep=" ${dep#* }"
+ pkg="${pkgarchqual%:*}"
+ if [ "x${pkg}" != "x${pkgarchqual}" ]; then
+ archqual="${pkgarchqual##*:}"
+ fi
+
+ if [ "x${dep# (*)}" != "x${dep}" ]; then
+ relver="${dep# (}"
+ relver="${relver%%)*}"
+ dep="${dep# (*)}"
+ rel="${relver% *}"
+ ver="${relver##* }"
+ fi
+
+ if [ "x${dep# \[*\]}" != "x${dep}" ]; then
+ arches="${dep# \[}"
+ arches="${arches%%\]*}"
+ dep="${dep# \[*\]}"
+ fi
+
+ if [ "x${dep# <*>}" != "x${dep}" ]; then
+ plats="${dep# <}"
+ plats="${plats%%>*}"
+ dep="${dep# <*>}"
+ fi
+
+ if [ "x${host_arch}" != 'x' ] && ! arch_is_concerned \
+ "${host_arch}" "${arches}"; then
+ return 0
+ fi
+ if [ "x${host_plat}" != 'x' ] && ! plat_is_concerned \
+ "${host_plat}" "${plats}"; then
+ return 0
+ fi
+
+ printf '%s' "${pkg}"
+ [ "x${archqual}" != 'x' ] && printf ':%s' "${archqual}"
+ [ "x${ver}" != 'x' ] && printf ' (%s %s)' "${rel}" "${ver}"
+ [ "x${host_arch}" = 'x' ] && [ "x${arches}" != 'x' ] && \
+ printf ' [%s]' "${arches}"
+ [ "x${host_plat}" = 'x' ] && [ "x${plats}" != 'x' ] && \
+ printf ' <%s>' "${plats}"
+ printf '\n'
+
+ return 0
+}
+
+reduce_deps()
+{
+ local deps="${1}"
+ local union="${2}"
+ local host_arch="${3}"
+ local host_plat="${4}"
+ local dep_and=
+ local dep_or=
+ local dep_list=
+ local dep_or_list=
+ local dep=
+
+ dep_list=''
+ IFS=','
+ for dep_and in ${deps}; do
+ unset IFS
+ if ! ${union}; then
+ dep_or_list=''
+ IFS='|'
+ for dep_or in ${dep_and}; do
+ unset IFS
+ dep="$(parse_dep "${dep_or}" \
+ "${host_arch}" "${host_plat}")"
+ if [ "x${dep}" != 'x' ]; then
+ if [ "x${dep_or_list}" != 'x' ]; then
+ dep_or_list="${dep_or_list} | "
+ fi
+ dep_or_list="${dep_or_list}${dep}"
+ fi
+ done
+ unset IFS
+ if [ "x${dep_or_list}" != 'x' ]; then
+ if [ "x${dep_list}" != 'x' ]; then
+ dep_list="${dep_list}, "
+ fi
+ dep_list="${dep_list}${dep_or_list}"
+ fi
+ else
+ dep="$(parse_dep "${dep_and}" \
+ "${host_arch}" "${host_plat}")"
+ if [ "x${dep}" != 'x' ]; then
+ if [ "x${dep_list}" != 'x' ]; then
+ dep_list="${dep_list}, "
+ fi
+ dep_list="${dep_list}${dep}"
+ fi
+ fi
+ done
+ unset IFS
+
+ printf '%s\n' "${dep_list}"
+
+ return 0
+}
diff --git a/src/dir.sh b/src/dir.sh
new file mode 100644
index 0000000..338d8fe
--- /dev/null
+++ b/src/dir.sh
@@ -0,0 +1,56 @@
+# Miscellaneous directory-related functions
+#
+# Copyright (C) 2013 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/>.
+
+dir_is_empty()
+{
+ local dir="${1}"
+ local ret=
+ local dirent=
+ local exclude=
+ local exclusion=
+ shift 1
+
+ ret=0
+
+ # Patterns to match all dirents except "." and "..":
+ # * dirents whose names don't start with "."
+ # .[!.] dirents whose names start with ".", are two characters long,
+ # and aren't ".."
+ # .??* dirents whose names start with "." and are three or more
+ # characters long
+ for dirent in "${dir}/"* "${dir}/".[!.] "${dir}/".[!.] "${dir}/".??*; do
+ if ! [ -e "${dirent}" ]; then
+ continue
+ fi
+ exclude=false
+ for exclusion in "${@}"; do
+ if [ "x${dirent##*/}" = "x${exclusion}" ]; then
+ exclude=true
+ break
+ fi
+ done
+ if ! ${exclude}; then
+ ret=1
+ break
+ fi
+ done
+
+ return ${ret}
+}
diff --git a/src/fd.sh b/src/fd.sh
new file mode 100644
index 0000000..0d5a50e
--- /dev/null
+++ b/src/fd.sh
@@ -0,0 +1,92 @@
+# Functions for opening and closing file descriptors
+#
+# Copyright (C) 2013 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/>.
+
+_FD_MIN=3
+# Shells and the file descriptors they reserve for the user:
+# * Debian Almquist Shell
+# - The shell doesn't understand any file descriptor greater than 9.
+# - File descriptors starting at 10 are used for I/O redirection.
+# * BusyBox ash
+# - File descriptors starting at 10 are used for job control and I/O
+# redirection.
+# * GNU Bash:
+# - GNU Readline uses file descriptor 255 in interactive shells.
+# - File descriptors starting at 10 are used for I/O redirection.
+# * MirOS Korn Shell
+# - File descriptors starting at either 10 or 24 are used for the shell's
+# tty_fd and shl_dbg_fd.
+# * Solaris ksh
+# - The shell doesn't understand any file descriptor greater than 9.
+_FD_MAX=9
+
+FD=
+
+fopen()
+{
+ local path="${1}"
+ local mode="${2}"
+ local i=
+ local fd=
+
+ case "${mode}" in
+ 'r') mode='<' ;;
+ 'w') mode='>' ;;
+ 'a') mode='>>';;
+ 'rw') mode='<>';;
+ *) return 125;;
+ esac
+
+ # Find first available file descriptor.
+ i=${_FD_MIN}
+ while [ ${i} -le ${_FD_MAX} ]; do
+ if [ "x$(eval echo "\${_fd_${i}+set}")" != 'xset' ]; then
+ fd=${i}
+ break
+ fi
+ i=$(($i + 1))
+ done
+ if [ "x${fd:+set}" != 'xset' ]; then
+ error 2 "$(get_msg 'emfile')"
+ return
+ fi
+
+ if eval "exec ${fd}${mode}'${path}'"; then
+ eval "_fd_${fd}='${mode}${path}'"
+ FD="${fd}"
+ return 0
+ else
+ error 2 "$(get_msg 'cant_fopen')"
+ fi
+}
+
+fclose()
+{
+ local fd="${1}"
+
+ # Make sure the file descriptor is open.
+ if [ "x$(eval echo "\${_fd_${fd}+set}")" != 'xset' ]; then
+ error 2 "$(get_msg 'ebadf')"
+ fi
+
+ eval "exec ${fd}>&-"
+ unset "_fd_${fd}"
+
+ return 0
+}
diff --git a/src/feed.sh b/src/feed.sh
new file mode 100644
index 0000000..4288c2a
--- /dev/null
+++ b/src/feed.sh
@@ -0,0 +1,137 @@
+# Functions for handling feed index files
+#
+# Copyright (C) 2013, 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/>.
+
+feed_dep_fields=
+feed_pkg_cb=
+feed_deps_cb=
+feed_fname_cb=
+feed_md5sum_cb=
+feed_sha256sum_cb=
+feed_pkgs=
+feed_pkg_include=
+feed_pkg=
+feed_deps=
+
+feed_download()
+{
+ local feed_index="${1}"
+ local use_gzip="${2}"
+ local gzip=
+
+ if ${use_gzip}; then
+ gzip=gunzip
+ else
+ gzip=cat
+ fi
+
+ wget -q -O - "${feed_index}" | ${gzip}
+
+ return 0
+}
+
+feed_find_pkgs()
+{
+ local feed_idx="${1}"
+ local dep_fields="${2}"
+ local pkg_cb="${3}"
+ local deps_cb="${4}"
+ local fname_cb="${5}"
+ local md5sum_cb="${6}"
+ local sha256sum_cb="${7}"
+
+ feed_dep_fields=" $(printf '%s ' ${dep_fields} | tr 'A-Z' 'a-z')"
+ feed_pkg_cb="${pkg_cb}"
+ feed_deps_cb="${deps_cb}"
+ feed_fname_cb="${fname_cb}"
+ feed_md5sum_cb="${md5sum_cb}"
+ feed_sha256sum_cb="${sha256sum_cb}"
+
+ feed_pkgs=''
+ feed_pkg_include='false'
+ feed_pkg=''
+ feed_deps=''
+
+ parse_control "${feed_idx}" feed_field_cb feed_para_cb 'Package'
+
+ printf '%s\n' "${feed_pkgs# }"
+
+ return 0
+}
+
+feed_field_cb()
+{
+ local name="${1}"
+ local value="${2}"
+
+ name="$(printf '%s\n' "${name}" | tr 'A-Z' 'a-z')"
+
+ if [ "x${name}" = 'xpackage' ]; then
+ feed_pkg="${value}"
+ elif [ "x${name}" = 'xfilename' ]; then
+ ${feed_fname_cb} "${feed_pkg}" "${value}"
+ elif [ "x${name}" = 'xmd5sum' ]; then
+ ${feed_md5sum_cb} "${feed_pkg}" "${value}"
+ elif [ "x${name}" = 'xsha256sum' ]; then
+ ${feed_sha256sum_cb} "${feed_pkg}" "${value}"
+ elif [ "x${feed_dep_fields#* ${name} }" != "x${feed_dep_fields}" ]; then
+ feed_deps="${feed_deps}${value}, "
+ fi
+
+ if ${feed_pkg_cb} ${name} ${value}; then
+ feed_pkg_include='true'
+ fi
+
+ return 0
+}
+
+feed_para_cb()
+{
+ local new_deps=
+ local dep=
+
+ if ${feed_pkg_include}; then
+ feed_pkgs="${feed_pkgs} ${feed_pkg}"
+ fi
+
+ new_deps=''
+ IFS=','
+ for dep in ${feed_deps%, }; do
+ unset IFS
+ # Trim off versions and disjunctions.
+ dep="${dep%%(*}"
+ dep="${dep%%|*}"
+ # Trim whitespace.
+ dep="$(printf '%s\n' "${dep}" | sed 's/^ *//; s/ *$//')"
+ # In practice, this would suffice for control files generated by
+ # opkbuild:
+ # dep="${dep#[ ${HT}${LF}]}"
+ # dep="${dep%[ ${HT}${LF}]}"
+ new_deps="${new_deps} ${dep}"
+ done
+ unset IFS
+
+ ${feed_deps_cb} "${feed_pkg}" "${new_deps# }"
+
+ feed_pkg_include='false'
+ feed_pkg=''
+ feed_deps=''
+
+ return 0
+}
diff --git a/src/getopt.sh b/src/getopt.sh
new file mode 100644
index 0000000..07769e7
--- /dev/null
+++ b/src/getopt.sh
@@ -0,0 +1,52 @@
+# Functions for parsing command-line options
+#
+# Copyright (C) 2013 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/>.
+
+get_options()
+{
+ local optstring=
+ local prefix=
+ local opt=
+
+ if [ "x${running_cmd_clean}" = 'x' ]; then
+ optstring="${OPTSTRING}"
+ prefix='opt_'
+ else
+ eval "optstring=\"\${cmd_${running_cmd_clean}_optstring}\""
+ prefix="cmd_${running_cmd_clean}_opt_"
+ fi
+
+ unset OPTARG
+ while getopts "${optstring}" opt; do
+ if [ "x${opt}" = 'x?' ]; then
+ return 1
+ fi
+ case "${optstring}" in
+ *"${opt}:"*)
+ eval "${prefix}${opt}=\"\${OPTARG}\""
+ ;;
+ *)
+ eval "${prefix}${opt}=true"
+ ;;
+ esac
+ unset OPTARG
+ done
+
+ return 0
+}
diff --git a/src/install.sh b/src/install.sh
new file mode 100644
index 0000000..e1e93df
--- /dev/null
+++ b/src/install.sh
@@ -0,0 +1,266 @@
+# Functions for installing systems
+#
+# Copyright (C) 2013, 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/>.
+
+install_deps=
+install_fnames=
+install_md5sums=
+install_sha256sums=
+install_feed_url=
+
+install_system()
+{
+ local mirror="${1}"
+ local suite="${2}"
+ local arch="${3}"
+ local plat="${4}"
+ local chroot="${5}"
+ local foreign="${6}"
+
+ if [ "x${arch}" = 'x' ]; then
+ arch="$(profile_detect_arch)"
+ fi
+ if [ "x${plat}" = 'x' ]; then
+ plat="$(profile_default_plat)"
+ fi
+ if [ "x${mirror}" = 'x' ]; then
+ mirror="$(profile_select_mirror)"
+ fi
+
+ if ! profile_validate_archplat "${mirror}" "${arch}" "${plat}" \
+ "${suite}"; then
+ error 2 "$(get_msg 'install_bad_archplat')" \
+ "${arch}" "${plat}"
+ fi
+
+ info "$(get_msg 'install_selected_arch')" "${arch}"
+ info "$(get_msg 'install_selected_plat')" "${plat}"
+ info "$(get_msg 'install_selected_mirror')" "${mirror}"
+
+ info "$(get_msg 'install_setting_up_chroot')"
+ if [ -d "${chroot}" ] && ! dir_is_empty "${chroot}" 'lost+found'; then
+ error 2 "$(get_msg 'install_chroot_dir_exists')" "${chroot}"
+ fi
+ if ! mkdir -p "${chroot}/.prokit" "${chroot}/prokit"; then
+ error 2 "$(get_msg 'install_mkdir_chroot_fail')" "${chroot}"
+ fi
+ >"${chroot}/prokit/installing"
+
+ info "$(get_msg 'install_find_pkgs')"
+ install_find_pkgs "${mirror}" "${suite}" "${arch}" "${plat}" \
+ "${chroot}"
+ install_get_pkgs "${chroot}"
+
+ info "$(get_msg 'install_configuring')"
+ if ! ${foreign}; then
+ profile_configure_system_native "${chroot}" "${arch}" "${plat}"
+ else
+ profile_configure_system_foreign "${chroot}" "${arch}" "${plat}"
+ fi
+
+ rm "${chroot}/prokit/installing"
+}
+
+install_find_pkgs()
+{
+ local mirror="${1}"
+ local suite="${2}"
+ local arch="${3}"
+ local plat="${4}"
+ local chroot="${5}"
+ local opkg_conf_fd=
+ local pkgs_fd=
+ local opt=
+ local feed_idx=
+ local feed=
+ local gzip=
+ local pkgs=
+
+ mkdir -p "${chroot}/etc/opkg" "${chroot}/var/lib/opkg/lists"
+ fopen "${chroot}/etc/opkg/opkg.conf" 'w'
+ opkg_conf_fd=${FD}
+
+ install_deps=
+ install_fnames=
+ install_md5sums=
+ install_sha256sums=
+
+ fopen "${chroot}/.prokit/packages" 'w'
+ pkgs_fd=${FD}
+
+ while read -r opt feed_idx feed; do
+ case "${opt}" in
+ 'src') gzip=false;;
+ 'src/gz') gzip=true;;
+ *) continue;;
+ esac
+ printf '%s %s %s\n' "${opt}" "${feed_idx}" \
+ "${feed%/Packages*}" >&${opkg_conf_fd}
+ feed_download "${feed}" ${gzip} \
+ >"${chroot}/var/lib/opkg/lists/${feed_idx}"
+ install_feed_url="${feed%/*}"
+ feed_find_pkgs "${chroot}/var/lib/opkg/lists/${feed_idx}" \
+ "$(profile_dep_fields)" profile_include_pkg \
+ install_deps_cb install_fname_cb \
+ install_md5sum_cb install_sha256sum_cb \
+ >&${pkgs_fd}
+ done <<-EOF
+ $(profile_feeds "${mirror}" "${arch}" "${plat}" "${suite}")
+ EOF
+
+ printf '\ndest root /\n' >&${opkg_conf_fd}
+ printf 'arch %s 1\n' 'all' "${arch}" 'src' >&${opkg_conf_fd}
+ fclose ${opkg_conf_fd}
+ fclose ${pkgs_fd}
+
+ pkgs="$(resolve_deps "$(cat "${chroot}/.prokit/packages")" \
+ "${install_deps}" | xargs printf '%s\n' | sort -u)"
+ printf '%s\n' "${pkgs}" >"${chroot}/.prokit/packages"
+}
+
+install_deps_cb()
+{
+ local pkg="${1}"
+ local deps="${2}"
+
+ install_deps="${install_deps}${pkg}: ${deps}${LF}"
+}
+
+install_fname_cb()
+{
+ local pkg="${1}"
+ local fname="${2}"
+
+ install_fnames="$(printf '%s\n%s %s/%s' "${install_fnames}" \
+ "${pkg}" "${install_feed_url}" "${fname}")"
+}
+
+install_md5sum_cb()
+{
+ local pkg="${1}"
+ local md5sum="${2}"
+
+ install_md5sums="$(printf '%s\n%s %s' "${install_md5sums}" \
+ "${pkg}" "${md5sum}")"
+}
+
+install_sha256sum_cb()
+{
+ local pkg="${1}"
+ local sha256sum="${2}"
+
+ install_sha256sums="$(printf '%s\n%s %s' \
+ "${install_sha256sums}" "${pkg}" "${sha256sum}")"
+}
+
+install_get_pkgs()
+{
+ local chroot="${1}"
+ local status_fd=
+ local pkg=
+ local fname=
+ local md5sum=
+ local sha256sum=
+ local file=
+ local control=
+ local field=
+ local printed=
+
+ mkdir -p "${chroot}/var/cache/opkg/archives" "${chroot}/tmp/opkg" \
+ "${chroot}/var/lib/opkg/info"
+
+ fopen "${chroot}/var/lib/opkg/status" 'w'
+ status_fd=${FD}
+
+ for pkg in $(cat "${chroot}/.prokit/packages"); do
+ info "$(get_msg 'install_downloading_pkg')" "${pkg}"
+ fname="$(printf '%s\n' "${install_fnames}" | \
+ sed -n "s/^${pkg} //p")"
+ md5sum="$(printf '%s\n' "${install_md5sums}" | \
+ sed -n "s/^${pkg} //p")"
+ sha256sum="$(printf '%s\n' "${install_sha256sums}" | \
+ sed -n "s/^${pkg} //p")"
+ wget -q -O "${chroot}/var/cache/opkg/archives/${fname##*/}" \
+ "${fname}" || \
+ error 2 "$(get_msg 'install_downloading_pkg_fail')"
+ fname="var/cache/opkg/archives/${fname##*/}"
+ if [ "x${md5sum}" != 'x' ]; then
+ printf '%s %s\n' \
+ "${md5sum}" "${chroot}/${fname}" | \
+ md5sum -c >/dev/null 2>&1 || \
+ error 2 "$(get_msg 'install_checksum_fail')"
+ fi
+ if [ "x${sha256sum}" != 'x' ]; then
+ printf '%s %s\n' \
+ "${sha256sum}" "${chroot}/${fname}" | \
+ sha256sum -c >/dev/null 2>&1 || \
+ error 2 "$(get_msg 'install_checksum_fail')"
+ fi
+
+ info "$(get_msg 'install_unpacking_pkg')" "${pkg}"
+ mkdir "${chroot}/tmp/opkg/${pkg}"
+ (
+ cd "${chroot}"
+ tar -xzOf "${fname}" data.tar.gz \
+ >"tmp/opkg/${pkg}/data.tar.gz"
+ tar -xzf "tmp/opkg/${pkg}/data.tar.gz"
+ cd "tmp/opkg/${pkg}"
+ tar -xzOf "../../../${fname}" control.tar.gz | \
+ tar -xz
+ )
+ tar -tzf "${chroot}/tmp/opkg/${pkg}/data.tar.gz" | \
+ sed 's/^\.//' >"${chroot}/var/lib/opkg/info/${pkg}.list"
+ rm -f "${chroot}/tmp/opkg/${pkg}/data.tar.gz"
+ for file in "${chroot}/tmp/opkg/${pkg}/"*; do
+ mv "${file}" \
+ "${chroot}/var/lib/opkg/info/${pkg}.${file##*/}"
+ done
+ rmdir "${chroot}/tmp/opkg/${pkg}"
+
+ # Write status file.
+ control="${chroot}/var/lib/opkg/info/${pkg}.control"
+ for field in Package Version Depends Recommends Suggests \
+ Provides Replaces Conflicts; do
+ grep "^${field}: " "${control}" >&${status_fd}
+ done
+ printf 'Status: install ok unpacked\n' >&${status_fd}
+ for field in Essential Architecture; do
+ grep "^${field}: " "${control}" >&${status_fd}
+ done
+ if [ -r "${chroot}/var/lib/opkg/info/${pkg}.conffiles" ]; then
+ printed=false
+ while read -r file; do
+ ${printed} || printf 'Conffiles:\n' \
+ >&${status_fd}
+ printf ' %s %s\n' "${file}" "$(md5sum \
+ "${chroot}/${file}" | cut -d' ' -f1)" \
+ >&${status_fd}
+ printed=true
+ done <"${chroot}/var/lib/opkg/info/${pkg}.conffiles"
+ fi
+ printf 'Installed-Time: %s\n\n' "$(date '+%s')" >&${status_fd}
+
+ rm -f "${chroot}/${fname}"
+ done
+
+ rm "${chroot}/.prokit/packages"
+ rmdir "${chroot}/.prokit"
+
+ fclose ${status_fd}
+}
diff --git a/src/local.mk b/src/local.mk
index d989dbf..dae4309 100644
--- a/src/local.mk
+++ b/src/local.mk
@@ -1,2 +1,27 @@
-bin_sources = \
- src/prokit.sh
+prokit_SOURCES += \
+ src/main.sh \
+ src/output.sh \
+ src/locale.sh \
+ src/getopt.sh \
+ src/fd.sh \
+ src/dir.sh \
+ src/vardata.sh \
+ src/archplat.sh \
+ src/deps.sh \
+ src/substvars.sh \
+ src/control.sh \
+ src/feed.sh \
+ src/pkg.sh \
+ src/mutex.sh \
+ src/session.sh \
+ src/block.sh \
+ src/opkg.sh \
+ src/rand.sh \
+ src/cmd.sh \
+ src/profile.sh \
+ src/package.sh \
+ src/install.sh
+
+include $(top_srcdir)/src/cmd/local.mk
+include $(top_srcdir)/src/profile/local.mk
+include $(top_srcdir)/src/package/local.mk
diff --git a/src/locale.sh b/src/locale.sh
new file mode 100644
index 0000000..76d2d9b
--- /dev/null
+++ b/src/locale.sh
@@ -0,0 +1,84 @@
+# Locale functions
+#
+# Copyright (C) 2012, 2013 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/>.
+
+DEFAULT_LOCALE='en_US'
+
+load_locale()
+{
+ local localedir=
+
+ # Make sure LC_MESSAGES is set.
+ if [ "x${LC_MESSAGES+set}" != 'xset' ]; then
+ if [ "x${LC_ALL+set}" = 'xset' ]; then
+ LC_MESSAGES="${LC_ALL}"
+ elif [ "x${LANG+set}" = 'xset' ]; then
+ LC_MESSAGES="${LANG}"
+ else
+ LC_MESSAGES="${DEFAULT_LOCALE}"
+ fi
+ fi
+
+ if ${in_place}; then
+ localedir="${builddir}/locale"
+ else
+ localedir="${LOCALEDIR}"
+ fi
+
+ # Try to load the locale.
+ if ! _try_load_locale "${localedir}" \
+ "${LC_MESSAGES%.*}"; then
+ if ! _try_load_locale "${localedir}" \
+ "${LC_MESSAGES%_*}"; then
+ if ! _try_load_locale "${localedir}" \
+ "${DEFAULT_LOCALE}"; then
+ warn 'Cannot load locale'
+ return 1
+ fi
+ fi
+ fi
+
+ return 0
+}
+
+get_msg()
+{
+ local msgid="${1}"
+
+ eval "printf '%s' \"\${msg_${TEXTDOMAIN}_${msgid}}\""
+
+ return 0
+}
+
+_try_load_locale()
+{
+ local localedir="${1}"
+ local locale="${2}"
+ local ms=
+
+ for ms in "${localedir}/${locale}/LC_MESSAGES/${TEXTDOMAIN}.ms" \
+ "${localedir}/${locale}.ms"; do
+ if [ -f "${ms}" ]; then
+ . "${ms}"
+ return 0
+ fi
+ done
+
+ return 1
+}
diff --git a/src/prokit.sh b/src/main.sh
index 5cfdb53..d0ca8f7 100644
--- a/src/prokit.sh
+++ b/src/main.sh
@@ -1,5 +1,3 @@
-#!@SH@
-#
# Main program file
#
# Copyright (C) 2013, 2014 Patrick "P. J." McDermott
@@ -26,52 +24,24 @@ set -u
HT=' '
LF='
'
-PACKAGE_NAME='@PACKAGE_NAME@'
-PACKAGE_VERSION='@PACKAGE_VERSION@'
-PKGDATADIR='@pkgdatadir@'
OPTSTRING='hV'
if [ -f "${0%/*}/../.builddirstamp" ]; then
in_place=true
builddir="${0%/*}/.."
+elif [ -f "${0%/*}/.builddirstamp" ]; then
+ in_place=true
+ builddir="${0%/*}"
else
in_place=false
builddir=''
fi
-# use() must be defined inline so it can be used to load other modules.
-use()
-{
- local module="${1}"
- local dir=
-
- if ${in_place}; then
- dir="${builddir}/lib"
- else
- dir="${PKGDATADIR}"
- fi
-
- if [ -f "${dir}/${module}.sm" ]; then
- . "${dir}/${module}.sm"
- else
- printf '%s: Error: Failed to load module "%s": %s\n' \
- "${0##*/}" "${module}" 'no such file or directory' >&2
- exit 2
- fi
-}
-
-use locale
-use cmd
-use vardata
-use getopt
-use rand
-
main()
{
local cmd=
load_locale
- load_cmds
init_vardata
if ! get_options "${@}"; then
@@ -121,5 +91,3 @@ check_uid()
return 0
}
-
-main "${@}"
diff --git a/src/mutex.sh b/src/mutex.sh
new file mode 100644
index 0000000..cd4a365
--- /dev/null
+++ b/src/mutex.sh
@@ -0,0 +1,54 @@
+# 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/>.
+
+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/src/opkg.sh b/src/opkg.sh
new file mode 100644
index 0000000..9e5c3c4
--- /dev/null
+++ b/src/opkg.sh
@@ -0,0 +1,29 @@
+# Functions for running the package manager
+#
+# 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/>.
+
+opkg_install_all()
+{
+ local root="${1}"
+
+ session_begin "${root}" . : true
+ session_exec /bin/sh -c \
+ 'opkg install $(opkg list-installed | cut -d " " -f 1)'
+ session_end
+}
diff --git a/src/output.sh b/src/output.sh
new file mode 100644
index 0000000..62d2095
--- /dev/null
+++ b/src/output.sh
@@ -0,0 +1,56 @@
+# Functions for printing messages
+#
+# Copyright (C) 2013 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/>.
+
+error()
+{
+ local status=${1}
+ local fmt="${2}"
+ shift 2
+
+ printf '%s: Error: ' "${0##*/}" >&2
+ printf "${fmt}\n" "${@}" >&2
+
+ # 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()
+{
+ local fmt="${1}"
+ shift 1
+
+ printf '%s: Warning: ' "${0##*/}" >&2
+ printf "${fmt}\n" "${@}" >&2
+
+ return 0
+}
+
+info()
+{
+ local fmt="${1}"
+ shift 1
+
+ printf '%s: ' "${0##*/}" >&2
+ printf "${fmt}\n" "${@}" >&2
+
+ return 0
+}
diff --git a/src/package.sh b/src/package.sh
new file mode 100644
index 0000000..3175202
--- /dev/null
+++ b/src/package.sh
@@ -0,0 +1,62 @@
+# Interface for handling source package metadata
+#
+# 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/>.
+
+package_dir=
+package_format=
+
+package_init()
+{
+ local dir="${1}"
+
+ package_dir="${dir}"
+
+ package_format=''
+ if [ -r "${package_dir}/format" ]; then
+ case "$(cat "${package_dir}/format")" in
+ 2.*)
+ package_format=2
+ ;;
+ esac
+ fi
+
+ if [ "x${package_format}" = 'x' ]; then
+ error 2 "$(get_msg 'package_format_unknown')"
+ fi
+
+ use "package/${package_format}"
+
+ return 0
+}
+
+package_get_build_deps()
+{
+ local arch="${1}"
+ local plat="${2}"
+
+ "package_${package_format}_get_build_deps" "${arch}" "${plat}"
+}
+
+package_set_substvars()
+{
+ local arch="${1}"
+ local plat="${2}"
+
+ "package_${package_format}_set_substvars" "${arch}" "${plat}"
+}
diff --git a/src/package/2.sh b/src/package/2.sh
new file mode 100644
index 0000000..fc4c77d
--- /dev/null
+++ b/src/package/2.sh
@@ -0,0 +1,77 @@
+# Functions for handling metadata in SPF 2.0 packages
+#
+# 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/>.
+
+package_2_build_deps=
+
+package_2_get_build_deps()
+{
+ local arch="${1}"
+ local plat="${2}"
+
+ package_2_build_deps=''
+ parse_control "${package_dir}/control" \
+ package_2_build_deps_field_cb false ''
+
+ substvars "$(reduce_deps "${package_2_build_deps}" false \
+ "${arch}" "${plat}")"
+
+ return 0
+}
+
+package_2_build_deps_field_cb()
+{
+ local name="${1}"
+ local value="${2}"
+
+ name="$(printf '%s\n' "${name}" | tr 'A-Z' 'a-z')"
+
+ if [ "x${name}" = 'xbuild-depends' ]; then
+ package_2_build_deps="${value}"
+ return 1
+ fi
+
+ return 0
+}
+
+package_2_set_substvars()
+{
+ local arch="${1}"
+ local plat="${2}"
+
+ if [ -f "${package_dir}/substvars" ]; then
+ parse_control "${package_dir}/substvars" \
+ package_2_substvar_cb false ''
+ fi
+
+ set_substvar 'Host-Arch' "${arch}"
+ set_substvar 'Host-Plat' "${plat}"
+
+ return 0
+}
+
+package_2_substvar_cb()
+{
+ local name="${1}"
+ local value="${2}"
+
+ set_substvar "${name}" "${value}"
+
+ return 0
+}
diff --git a/src/package/local.mk b/src/package/local.mk
new file mode 100644
index 0000000..9ec2158
--- /dev/null
+++ b/src/package/local.mk
@@ -0,0 +1,2 @@
+prokit_SOURCES += \
+ src/package/2.sh
diff --git a/src/pkg.sh b/src/pkg.sh
new file mode 100644
index 0000000..ee4aa25
--- /dev/null
+++ b/src/pkg.sh
@@ -0,0 +1,51 @@
+# Functions for handling packages and their dependencies
+#
+# Copyright (C) 2013, 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/>.
+
+resolve_deps()
+{
+ local new_pkgs="${1}"
+ local deps="${2}"
+ local all_deps=
+ local new_deps=
+ local pkg=
+
+ all_deps=''
+ new_pkgs="${new_pkgs} "
+ while [ "x${new_pkgs}" != 'x' ]; do
+ all_deps="${all_deps}${new_pkgs}"
+ new_deps=''
+ for pkg in ${new_pkgs}; do
+ case "${pkg}" in
+ *[!a-z0-9+.-]*) continue;;
+ esac
+ new_deps="${new_deps} $(printf '%s' "${deps}" | \
+ sed -n "s/^${pkg}: *//p")"
+ done
+ new_deps="$(printf '%s\n' ${new_deps} | sort -u)"
+ new_pkgs=''
+ for pkg in ${new_deps}; do
+ if [ "x${all_deps# ${pkg} }" = "x${all_deps}" ]; then
+ new_pkgs="${new_pkgs}${pkg} "
+ fi
+ done
+ done
+
+ printf '%s\n' "${all_deps% }"
+}
diff --git a/src/profile.sh b/src/profile.sh
new file mode 100644
index 0000000..f64a0d8
--- /dev/null
+++ b/src/profile.sh
@@ -0,0 +1,165 @@
+# Profile-related functions
+#
+# 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/>.
+
+profiles=' '
+profile=
+
+register_profile()
+{
+ local profile="${1}"
+ shift 1
+
+ profiles="${profiles}${profile} "
+ return 0
+}
+
+is_profile()
+{
+ local prof="${1}"
+
+ [ "x${profiles# ${prof} }" != "x${profiles}" ]
+}
+
+profile_set()
+{
+ local prof="${1}"
+
+ if is_profile "${prof}"; then
+ profile="${prof}"
+ use "profile/${profile}"
+ else
+ error 1 "$(get_msg 'profile_not_found')" "${prof}"
+ fi
+
+ return 0
+}
+
+profile_normalize_suite()
+{
+ local suite="${1}"
+
+ "prof_${profile}_normalize_suite" "${suite}"
+}
+
+profile_detect_arch()
+{
+ "prof_${profile}_detect_arch"
+}
+
+profile_default_plat()
+{
+ "prof_${profile}_default_plat"
+}
+
+profile_select_mirror()
+{
+ "prof_${profile}_select_mirror"
+}
+
+profile_validate_archplat()
+{
+ local mirror="${1}"
+ local arch="${2}"
+ local plat="${3}"
+ local suite="${4}"
+
+ "prof_${profile}_validate_archplat" "${mirror}" "${arch}" "${plat}" \
+ "${suite}"
+}
+
+profile_feeds()
+{
+ local mirror="${1}"
+ local arch="${2}"
+ local plat="${3}"
+ local suite="${4}"
+
+ "prof_${profile}_feeds" "${mirror}" "${arch}" "${plat}" "${suite}"
+}
+
+profile_dep_fields()
+{
+ "prof_${profile}_dep_fields"
+}
+
+profile_include_pkg()
+{
+ local name="${1}"
+ local value="${2}"
+
+ "prof_${profile}_include_pkg" "${name}" "${value}"
+}
+
+profile_get_fstab()
+{
+ local arch="${1}"
+ local plat="${2}"
+
+ "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}"
+ local arch="${2}"
+ local plat="${3}"
+
+ "prof_${profile}_configure_system_native" "${root}" "${arch}" "${plat}"
+}
+
+profile_configure_system_foreign()
+{
+ local root="${1}"
+ local arch="${2}"
+ local plat="${3}"
+
+ "prof_${profile}_configure_system_foreign" "${root}" "${arch}" "${plat}"
+}
diff --git a/src/profile/local.mk b/src/profile/local.mk
new file mode 100644
index 0000000..c303390
--- /dev/null
+++ b/src/profile/local.mk
@@ -0,0 +1,2 @@
+prokit_SOURCES += \
+ src/profile/proteanos.sh
diff --git a/src/profile/proteanos.sh b/src/profile/proteanos.sh
new file mode 100644
index 0000000..0e146ee
--- /dev/null
+++ b/src/profile/proteanos.sh
@@ -0,0 +1,258 @@
+# ProteanOS architecture detection and feeds lists.
+#
+# Copyright (C) 2013, 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/>.
+
+prof_proteanos_fstab_linux="\
+proc /proc proc defaults
+sys /sys sysfs defaults
+/dev /dev none bind
+devpts /dev/pts devpts noexec,nosuid,gid=5,mode=0620
+"
+prof_proteanos_install_service=\
+'#!/bin/sh /etc/rc.common
+
+start()
+{
+ log "Configuring packages"
+ printf "disabled\n" >/etc/rc.policy
+ opkg install $(opkg list-installed | cut -d " " -f 1)
+ printf "enabled\n" >/etc/rc.policy
+ rm "${SCRIPT}"
+}
+'
+
+prof_proteanos_normalize_suite()
+{
+ local suite="${1}"
+
+ case "${suite}" in
+ */*) printf '%s\n' "${suite}";;
+ *) printf 'dev/%s\n' "${suite}";;
+ esac
+
+ return 0
+}
+
+prof_proteanos_detect_arch()
+{
+ local uname_m=
+ local uname_s=
+ local arch=
+
+ uname_m="$( (uname -m) 2>/dev/null)" || uname_m='unknown'
+ uname_s="$( (uname -s) 2>/dev/null)" || uname_s='unknown'
+
+ case "${uname_m}:${uname_s}" in
+ 'x86_64:Linux')
+ arch='amd64-linux-glibc'
+ ;;
+ 'i686:Linux')
+ arch='i686-linux-glibc'
+ ;;
+ *)
+ arch=''
+ esac
+
+ printf '%s\n' "${arch}"
+}
+
+prof_proteanos_default_plat()
+{
+ printf 'dev\n'
+}
+
+prof_proteanos_select_mirror()
+{
+ local mirrors=
+ local mirrors_count=
+ local mirror_num=
+
+ mirrors="$(wget -q -O - \
+ 'http://files.proteanos.com/pub/proteanos-mirrors-http')"
+ mirrors_count=$(printf '%s\n' "${mirrors}" | wc -l)
+ rand
+ mirror_num=$(($rand_x % $mirrors_count + 1))
+
+ printf '%s\n' "${mirrors}" | sed -n "${mirror_num}p"
+}
+
+prof_proteanos_validate_archplat()
+{
+ local mirror="${1}"
+ local arch="${2}"
+ local plat="${3}"
+ local suite="${4}"
+
+ while read -r archplat; do
+ case "${archplat}" in
+ "${arch}/${plat}")
+ return 0
+ ;;
+ esac
+ done <<-EOF
+ $(wget -q -O - "${mirror}/feeds/${suite}/Manifest" | sed \
+ 's|/[^/]*$||; /^all\//d; /\/all$/d;')
+ EOF
+
+ return 1
+}
+
+prof_proteanos_feeds()
+{
+ local mirror="${1}"
+ local arch="${2}"
+ local plat="${3}"
+ local suite="${4}"
+ local a=
+ local p=
+ local s=
+
+ for a in "${arch}" 'all'; do
+ for p in "${plat}" 'all'; do
+ for s in 'base'; do
+ printf 'src/gz proteanos_%s_%s_%s_%s_%s ' \
+ "${suite%/*}" "${suite#*/}" \
+ "${a}" "${p}" "${s}"
+ printf '%s/feeds/%s/%s/%s/%s/Packages.gz\n' \
+ "${mirror}" "${suite}" \
+ "${a}" "${p}" "${s}"
+ done
+ done
+ done
+
+ for s in 'base'; do
+ printf 'src/gz proteanos_%s_%s_%s_%s_%s ' \
+ "${suite%/*}" "${suite#*/}" \
+ 'src' 'all' "${s}"
+ printf '%s/feeds/%s/%s/%s/%s/Packages.gz\n' \
+ "${mirror}" "${suite}" \
+ 'src' 'all' "${s}"
+ done
+}
+
+prof_proteanos_dep_fields()
+{
+ printf 'Depends Pre-Depends'
+}
+
+prof_proteanos_include_pkg()
+{
+ local name="${1}"
+ local value="${2}"
+
+ if [ "x${name}" = 'xessential' ]; then
+ if [ "x${value}" = 'xyes' ]; then
+ return 0
+ fi
+ fi
+
+ return 1
+}
+
+prof_proteanos_get_fstab()
+{
+ local arch="${1}"
+ local plat="${2}"
+
+ case "${arch}" in
+ *-linux-*)
+ printf '%s\n' "${prof_proteanos_fstab_linux}"
+ ;;
+ 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}"
+ local arch="${2}"
+ local plat="${3}"
+
+ printf 'disabled\n' >"${root}/etc/rc.policy"
+ opkg_install_all "${root}"
+ if [ "x${plat}" = 'xdev' ]; then
+ [ -r /etc/resolv.conf ] && cp /etc/resolv.conf "${root}/etc"
+ [ -r /etc/hostname ] && cp /etc/hostname "${root}/etc"
+ else
+ printf 'enabled\n' >"${root}/etc/rc.policy"
+ printf 'proteanos\n' >"${root}/etc/hostname"
+ fi
+ [ -e "${root}/etc/passwd" ] || printf \
+ 'root::0:0:root:/root:/bin/sh\n' >"${root}/etc/passwd"
+ [ -e "${root}/etc/group" ] || printf \
+ 'root:x:0:\n' >"${root}/etc/group"
+}
+
+prof_proteanos_configure_system_foreign()
+{
+ local root="${1}"
+ local arch="${2}"
+ local plat="${3}"
+
+ printf '%s' "${prof_proteanos_install_service}" \
+ >"${root}/etc/rc.d/S10install"
+ chmod 0755 "${root}/etc/rc.d/S10install"
+ printf 'proteanos\n' >"${root}/etc/hostname"
+ [ -e "${root}/etc/passwd" ] || printf \
+ 'root::0:0:root:/root:/bin/sh\n' >"${root}/etc/passwd"
+ [ -e "${root}/etc/group" ] || printf \
+ 'root:x:0:\n' >"${root}/etc/group"
+}
+
+prof_proteanos_register()
+{
+ register_profile 'proteanos'
+}
+__init prof_proteanos_register
diff --git a/src/rand.sh b/src/rand.sh
new file mode 100644
index 0000000..872d3fb
--- /dev/null
+++ b/src/rand.sh
@@ -0,0 +1,42 @@
+# Linear congruential pseudorandom number generator
+#
+# 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/>.
+
+rand_x=1
+
+srand()
+{
+ local x="${1}"
+
+ case "${x}" in
+ *[!0-9]*)
+ warn "$(get_msg 'rand_bad_x')"
+ return 1
+ ;;
+ esac
+
+ rand_x=${x}
+}
+
+rand()
+{
+ # Increment, multiplier, and modulus values are those used in glibc.
+ rand_x=$((1103515245 * $rand_x + 12345))
+ rand_x=$(($rand_x % 4294967296))
+}
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
+}
diff --git a/src/substvars.sh b/src/substvars.sh
new file mode 100644
index 0000000..c2bc3f8
--- /dev/null
+++ b/src/substvars.sh
@@ -0,0 +1,103 @@
+# Functions for setting and substituting variables
+#
+# Copyright (C) 2012, 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/>.
+
+substvars_max_depth=50
+
+set_substvar()
+{
+ local name="${1}"
+ local value="${2}"
+
+ # Convert variable name to lower case and validate.
+ name="$(printf '%s\n' "${name}" | tr 'A-Z-' 'a-z_')"
+ case "${name:- }" in
+ *[!a-z0-9_]*)
+ warn "$(get_msg 'substvar_invalid')" "${name}"
+ return 1
+ ;;
+ esac
+
+ # Trim leading and trailing newline characters from value.
+ value="$(printf '%s\n' "${value}" | sed -n '
+ H; # Store each input line in the hold space.
+ ${ # At the last line of input:
+ g; # Restore the hold space into the pattern space.
+ s/^[\n]*//; # Remove leading newline characters.
+ s/[\n]*$//; # Remove trailing newline characters.
+ p; # Print the results.
+ };
+ ')"
+
+ # Escape single quotes in value.
+ value="$(printf '%s\n' "${value}" | sed "s/'/'\\\\''/g")"
+
+ eval "substvar_${name}='${value}'"
+
+ return 0
+}
+
+substvars()
+{
+ local string="${1}"
+ local depth=
+ local lhs=
+ local name=
+ local rhs=
+ local old_rhs=
+ local value=
+
+ # Logic inspired by that of dpkg's Dpkg::Substvars::substvars()
+ # subroutine.
+
+ depth=0
+
+ while :; do
+ lhs="${string%%\$\{*}"
+ if [ ${#lhs} -eq ${#string} ]; then
+ # No "${" was found.
+ break
+ fi
+ string="${string#*\$\{}"
+ name="${string%%\}*}"
+ rhs="${string#*\}}"
+
+ if [ ${#rhs} -lt ${#old_rhs} ]; then
+ # Reset the nesting counter if we've advanced the right
+ # side of the matched space.
+ depth=0
+ fi
+ if [ ${depth} -ge ${substvars_max_depth} ]; then
+ # Warn of possible recursion.
+ warn "$(get_msg 'substvar_deep_nesting')"
+ return 1
+ fi
+ old_rhs="${rhs}"
+
+ # Perform the substitution.
+ name="$(printf '%s\n' "${name}" | tr 'A-Z-' 'a-z_')"
+ value="$(eval "printf '%s\n' \"\${substvar_${name}}\"")"
+ string="${lhs}${value}${rhs}"
+ depth=$(($depth + 1))
+ done
+
+ printf '%s\n' "${string}"
+
+ return 0
+}
diff --git a/src/vardata.sh b/src/vardata.sh
new file mode 100644
index 0000000..0c68d67
--- /dev/null
+++ b/src/vardata.sh
@@ -0,0 +1,52 @@
+# Functions for getting variable/runtime data
+#
+# Copyright (C) 2015 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/>.
+
+VARDATA_DIRS='mount'
+
+vardatadir=
+
+init_vardata()
+{
+ local dir=
+
+ if ${in_place}; then
+ vardatadir="${builddir}/var"
+ for dir in ${VARDATA_DIRS}; do
+ mkdir -p "${vardatadir}/${dir}"
+ done
+ else
+ vardatadir="${PKGLOCALSTATEDIR}"
+ fi
+
+ return 0
+}
+
+get_vardata_dir()
+{
+ local dir="${1}"
+
+ case " ${VARDATA_DIRS} " in
+ *" ${dir} "*)
+ printf '%s/%s' "${vardatadir}" "${dir}"
+ ;;
+ esac
+
+ return 0
+}