diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/archplat.sh | 144 | ||||
-rw-r--r-- | src/block.sh | 86 | ||||
-rw-r--r-- | src/cmd.sh | 122 | ||||
-rw-r--r-- | src/cmd/build.sh | 152 | ||||
-rw-r--r-- | src/cmd/help.sh | 47 | ||||
-rw-r--r-- | src/cmd/install.sh | 81 | ||||
-rw-r--r-- | src/cmd/installer-pc.sh | 101 | ||||
-rw-r--r-- | src/cmd/local.mk | 8 | ||||
-rw-r--r-- | src/cmd/opkg.sh | 112 | ||||
-rw-r--r-- | src/cmd/shell.sh | 62 | ||||
-rw-r--r-- | src/cmd/version.sh | 33 | ||||
-rw-r--r-- | src/control.sh | 147 | ||||
-rw-r--r-- | src/deps.sh | 156 | ||||
-rw-r--r-- | src/dir.sh | 56 | ||||
-rw-r--r-- | src/fd.sh | 92 | ||||
-rw-r--r-- | src/feed.sh | 137 | ||||
-rw-r--r-- | src/getopt.sh | 52 | ||||
-rw-r--r-- | src/install.sh | 266 | ||||
-rw-r--r-- | src/local.mk | 29 | ||||
-rw-r--r-- | src/locale.sh | 84 | ||||
-rw-r--r-- | src/main.sh (renamed from src/prokit.sh) | 38 | ||||
-rw-r--r-- | src/mutex.sh | 54 | ||||
-rw-r--r-- | src/opkg.sh | 29 | ||||
-rw-r--r-- | src/output.sh | 56 | ||||
-rw-r--r-- | src/package.sh | 62 | ||||
-rw-r--r-- | src/package/2.sh | 77 | ||||
-rw-r--r-- | src/package/local.mk | 2 | ||||
-rw-r--r-- | src/pkg.sh | 51 | ||||
-rw-r--r-- | src/profile.sh | 165 | ||||
-rw-r--r-- | src/profile/local.mk | 2 | ||||
-rw-r--r-- | src/profile/proteanos.sh | 258 | ||||
-rw-r--r-- | src/rand.sh | 42 | ||||
-rw-r--r-- | src/session.sh | 210 | ||||
-rw-r--r-- | src/substvars.sh | 103 | ||||
-rw-r--r-- | src/vardata.sh | 52 |
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 +} |