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/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 | 23 | ||||
-rw-r--r-- | src/locale.sh | 84 | ||||
-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/pkg.sh | 51 | ||||
-rw-r--r-- | src/profile.sh | 165 | ||||
-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 |
22 files changed, 2188 insertions, 1 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/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 980b6ac..6133821 100644 --- a/src/local.mk +++ b/src/local.mk @@ -1,2 +1,23 @@ prokit_SOURCES += \ - src/main.sh + 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 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/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/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/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 +} |