SPS · log · files · refs · tags · releases · people · languages · GitHub · file
bin/sget
43501 bytes at 2909919 · GitHub
#!/bin/sh
# High-level package and dependency coordinator.
sps_prog=sget
sps_bin_dir=$(CDPATH= cd -P "$(dirname "$0")" 2>/dev/null && pwd) || exit 1
if [ -z "${SPS_LIBDIR+x}" ]; then
if [ -r "$sps_bin_dir/../lib/common.sh" ]; then
SPS_LIBDIR=$sps_bin_dir/../lib
else
SPS_LIBDIR=$sps_bin_dir/../lib/sps
fi
fi
if ! . "$SPS_LIBDIR/common.sh"; then
printf '%s\n' "sget: cannot load $SPS_LIBDIR/common.sh" >&2
exit 1
fi
sps_load_config || exit $?
sps_index=$SPS_CACHE/indexes/packages.index
sps_tab=$(printf '\tX')
sps_tab=${sps_tab%X}
sget_begin_transaction()
{
if [ -e "$SPS_DB" ] || [ -L "$SPS_DB" ]; then
[ -d "$SPS_DB" ] && [ ! -L "$SPS_DB" ] ||
sps_die "$SPS_EX_DATABASE" "package database is not a real directory: $SPS_DB"
else
mkdir -p "$SPS_DB" ||
sps_die "$SPS_EX_PERMISSION" "cannot create package database '$SPS_DB'"
[ -d "$SPS_DB" ] && [ ! -L "$SPS_DB" ] ||
sps_die "$SPS_EX_DATABASE" "package database is not a real directory: $SPS_DB"
fi
sps_lock_acquire "$SPS_DB" .transaction ||
sps_die "$SPS_EX_DATABASE" "another high-level SPS transaction is active: '$SPS_DB/.transaction'"
}
sget_usage()
{
cat <<'EOF_USAGE'
usage: sget [-q|--quiet] [-v|--verbose] search PATTERN [--raw]
sget [-q|--quiet] [-v|--verbose] info PACKAGE [--raw]
sget [-q|--quiet] [-v|--verbose] depends PACKAGE [--raw] [--build]
sget [-q|--quiet] [-v|--verbose] dependees PACKAGE [--build]
sget [-q|--quiet] [-v|--verbose] why PACKAGE DEPENDENCY [--build]
sget [-q|--quiet] [-v|--verbose] install [--plan] [--nodeps] [--reinstall] PACKAGE...
sget [-q|--quiet] [-v|--verbose] upgrade [--plan] [--downgrade]
sget [-q|--quiet] [-v|--verbose] remove [--plan] [--nodeps] PACKAGE...
sget [-q|--quiet] [-v|--verbose] config-status [--raw] [PACKAGE]
sget [-q|--quiet] [-v|--verbose] config-status --diff PATH
EOF_USAGE
}
sget_note()
{
[ "${sget_quiet:-0}" -eq 1 ] && return 0
printf '%s: %s\n' "$sps_prog" "$*" >&2
}
sget_need_index()
{
[ -r "$sps_index" ] ||
sps_die "$SPS_EX_NOTFOUND" "repository index not found; run 'src update'"
}
sget_query()
{
sget_action=$1
shift
sps_need_arg "$sget_action" "${1-}"
sget_value=$1
shift
sget_raw=0
case ${1-} in
--raw) sget_raw=1; shift ;;
esac
[ "$#" -eq 0 ] || { sget_usage >&2; exit "$SPS_EX_USAGE"; }
sget_need_index
case $sget_action in
search)
sps_tab_safe "$sget_value" ||
sps_die "$SPS_EX_USAGE" "search pattern contains a tab or newline"
[ "$sget_raw" -eq 0 ] || sget_action=search_raw
;;
info)
sps_validate_package_name "$sget_value" ||
sps_die "$SPS_EX_USAGE" "invalid package name '$sget_value'"
[ "$sget_raw" -eq 0 ] && sget_action=show || sget_action=exact
;;
esac
awk -v action="$sget_action" -v query="$sget_value" \
-v program=sget -f "$SPS_LIBDIR/index.awk" "$sps_index"
}
sget_depends()
{
sps_need_arg depends "${1-}"
sget_package=$1
shift
sps_validate_package_name "$sget_package" ||
sps_die "$SPS_EX_USAGE" "invalid package name '$sget_package'"
sget_dep_action=tree
sget_include_build=0
while [ "$#" -gt 0 ]; do
case $1 in
--raw) sget_dep_action=order ;;
--build) sget_include_build=1 ;;
*) sget_usage >&2; exit "$SPS_EX_USAGE" ;;
esac
shift
done
sget_need_index
awk -v action="$sget_dep_action" -v roots="$sget_package" \
-v include_build="$sget_include_build" -v program=sget \
-f "$SPS_LIBDIR/version.awk" -f "$SPS_LIBDIR/deps.awk" \
"$sps_index"
}
sget_dependees()
{
sps_need_arg dependees "${1-}"
sget_package=$1
shift
sps_validate_package_name "$sget_package" ||
sps_die "$SPS_EX_USAGE" "invalid package name '$sget_package'"
sget_include_build=0
case ${1-} in --build) sget_include_build=1; shift ;; esac
[ "$#" -eq 0 ] || { sget_usage >&2; exit "$SPS_EX_USAGE"; }
sget_need_index
awk -v action=dependees -v query="$sget_package" \
-v include_build="$sget_include_build" -v program=sget \
-f "$SPS_LIBDIR/relations.awk" "$sps_index"
}
sget_why()
{
[ "$#" -ge 2 ] || { sget_usage >&2; exit "$SPS_EX_USAGE"; }
sget_root=$1
sget_dependency=$2
shift 2
sps_validate_package_name "$sget_root" ||
sps_die "$SPS_EX_USAGE" "invalid package name '$sget_root'"
sps_validate_package_name "$sget_dependency" ||
sps_die "$SPS_EX_USAGE" "invalid package name '$sget_dependency'"
sget_include_build=0
case ${1-} in --build) sget_include_build=1; shift ;; esac
[ "$#" -eq 0 ] || { sget_usage >&2; exit "$SPS_EX_USAGE"; }
sget_need_index
awk -v action=why -v root="$sget_root" -v query="$sget_dependency" \
-v include_build="$sget_include_build" -v program=sget \
-f "$SPS_LIBDIR/relations.awk" "$sps_index"
}
sget_read_installed()
{
sget_installed_name=$1
sget_installed_record=$SPS_DB/installed/$sget_installed_name
if [ -L "$sget_installed_record" ] ||
{ [ -f "$sget_installed_record" ] && [ ! -d "$sget_installed_record" ]; }; then
sps_die "$SPS_EX_DATABASE" "installed record for '$sget_installed_name' is unsafe"
fi
[ -d "$sget_installed_record" ] || return 1
[ -f "$sget_installed_record/meta" ] && [ ! -L "$sget_installed_record/meta" ] ||
sps_die "$SPS_EX_DATABASE" "installed metadata for '$sget_installed_name' is missing or unsafe"
sget_installed_id=$(awk -v sps_action=meta-id -v sps_program=sget \
-f "$SPS_LIBDIR/package.awk" "$sget_installed_record/meta") ||
sps_die "$SPS_EX_DATABASE" "installed metadata for '$sget_installed_name' is malformed"
sget_saved_ifs=$IFS
IFS=$sps_tab
set -- $sget_installed_id
IFS=$sget_saved_ifs
[ "$#" -eq 4 ] ||
sps_die "$SPS_EX_DATABASE" "installed identity for '$sget_installed_name' is malformed"
[ "$1" = "$sget_installed_name" ] ||
sps_die "$SPS_EX_DATABASE" "installed record '$sget_installed_name' identifies package '$1'"
sget_installed_version=$2
sget_installed_release=$3
sget_installed_arch=$4
return 0
}
# Compare the selected version/release with what is installed. Prints -1, 0, or 1.
sget_compare_selected()
{
sget_cmp=$(awk -v version_action=compare -v version_a="$1" -v version_b="$3" \
-f "$SPS_LIBDIR/version.awk" /dev/null) || return 1
if [ "$sget_cmp" -eq 0 ]; then
sget_cmp=$(awk -v version_action=compare -v version_a="$2" -v version_b="$4" \
-f "$SPS_LIBDIR/version.awk" /dev/null) || return 1
fi
printf '%s\n' "$sget_cmp"
}
sget_installed_names()
{
sget_list=
[ -d "$SPS_DB/installed" ] || {
printf ''
return 0
}
for sget_d in "$SPS_DB/installed"/*; do
[ -d "$sget_d" ] || continue
sget_n=${sget_d##*/}
[ -n "$sget_list" ] && sget_list=$sget_list,$sget_n || sget_list=$sget_n
done
printf '%s' "$sget_list"
}
sget_cleanup_install()
{
if [ -n "${sget_work:-}" ]; then
rm -rf "$sget_work"
sget_work=
fi
sps_lock_release
}
sget_verify_artifact_identity()
{
sget_verify_archive=$1
sget_verify_name=$2
sget_verify_version=$3
sget_verify_release=$4
sget_verify_arch=$5
sget_verify_definition=${6-}
case $sget_verify_archive in
*.tar.gz|*.tgz)
sps_require_cmd gzip
sget_verify_meta=$(gzip -dc "$sget_verify_archive" 2>/dev/null |
tar -xOf - ./.SPS/meta 2>/dev/null) || return 1
;;
*.tar.xz|*.txz)
sps_require_cmd xz
sget_verify_meta=$(xz -dc "$sget_verify_archive" 2>/dev/null |
tar -xOf - ./.SPS/meta 2>/dev/null) || return 1
;;
*.tar.zst|*.tzst)
sps_require_cmd zstd
sget_verify_meta=$(zstd -q -dc "$sget_verify_archive" 2>/dev/null |
tar -xOf - ./.SPS/meta 2>/dev/null) || return 1
;;
*)
sget_verify_meta=$(tar -xOf "$sget_verify_archive" ./.SPS/meta 2>/dev/null) || return 1
;;
esac
sget_verify_id=$(printf '%s\n' "$sget_verify_meta" |
awk -v sps_action=meta-id -v sps_program=sget \
-f "$SPS_LIBDIR/package.awk") || return 1
sget_verify_saved_ifs=$IFS
IFS=$sps_tab
set -- $sget_verify_id
IFS=$sget_verify_saved_ifs
[ "$#" -eq 4 ] || return 1
[ "$1" = "$sget_verify_name" ] &&
[ "$2" = "$sget_verify_version" ] &&
[ "$3" = "$sget_verify_release" ] &&
[ "$4" = "$sget_verify_arch" ] || return 1
[ -n "$sget_verify_definition" ] || return 0
sget_verify_cached_definition=$(printf '%s\n' "$sget_verify_meta" |
awk -v sps_action=meta-get -v sps_key_name=definition_sha256 \
-v sps_program=sget -f "$SPS_LIBDIR/package.awk" 2>/dev/null) ||
return 2
[ "$sget_verify_cached_definition" = "$sget_verify_definition" ] ||
return 2
}
# Set by the upgrade path.
sget_upgrade_mode=${sget_upgrade_mode:-0}
# Build-only tools that a live disc already seeds. Skip installing the
# package when it is not a runtime requirement and the host command exists.
sget_host_build_tool()
{
case $1 in
cmake) command -v cmake >/dev/null 2>&1 ;;
ninja) command -v ninja >/dev/null 2>&1 ;;
meson) command -v meson >/dev/null 2>&1 ;;
patch) command -v patch >/dev/null 2>&1 ;;
pkgconf) command -v pkg-config >/dev/null 2>&1 ||
command -v pkgconf >/dev/null 2>&1 ;;
gperf) command -v gperf >/dev/null 2>&1 ;;
python) command -v python3 >/dev/null 2>&1 ;;
go) command -v go >/dev/null 2>&1 ;;
rust) command -v rustc >/dev/null 2>&1 &&
command -v cargo >/dev/null 2>&1 ;;
unzip) command -v unzip >/dev/null 2>&1 ;;
lzip) command -v lzip >/dev/null 2>&1 ;;
*) return 1 ;;
esac
}
sget_selected_index_field()
{
awk -F "$sps_tab" -v pkg="$1" -v col="$2" '
$1 == pkg {
pri = $9 + 0
if (!seen || pri > best) {
best = pri
val = $col
seen = 1
}
}
END { if (seen) print val }
' "$sps_index"
}
sget_cache_usable()
{
sget_cu_name=$1
sget_cu_version=$2
sget_cu_release=$3
sget_cu_arch=$4
sget_cu_hash=$5
sget_cu_base=$SPS_CACHE/packages/$sget_cu_name-$sget_cu_version-$sget_cu_release-$sget_cu_arch.pkg.tar
for sget_cu_candidate in "$sget_cu_base.zst" "$sget_cu_base.xz" \
"$sget_cu_base.gz" "$sget_cu_base"
do
[ -f "$sget_cu_candidate" ] && [ ! -L "$sget_cu_candidate" ] || continue
sget_verify_artifact_identity "$sget_cu_candidate" "$sget_cu_name" \
"$sget_cu_version" "$sget_cu_release" "$sget_cu_arch" \
"$sget_cu_hash" >/dev/null 2>&1 || continue
return 0
done
return 1
}
# True when mkpkg will run for this selected record.
sget_will_compile()
{
sget_wc_name=$1
sget_wc_version=$2
sget_wc_release=$3
sget_wc_arch=$4
sget_wc_hash=$5
sget_wc_cache=$sget_work/will-compile/$sget_wc_name
if [ -f "$sget_wc_cache" ]; then
read -r sget_wc_cached <"$sget_wc_cache"
return "$sget_wc_cached"
fi
if sget_read_installed "$sget_wc_name"; then
if ! sget_wc_cmp=$(sget_compare_selected "$sget_wc_version" \
"$sget_wc_release" "$sget_installed_version" \
"$sget_installed_release")
then
printf '%s\n' 0 >"$sget_wc_cache"
return 0
fi
if [ "$sget_wc_cmp" -eq 0 ] &&
[ "$sget_installed_arch" = "$sget_wc_arch" ] &&
[ "$sget_reinstall" -eq 0 ]; then
printf '%s\n' 1 >"$sget_wc_cache"
return 1
fi
fi
if sget_cache_usable "$sget_wc_name" "$sget_wc_version" \
"$sget_wc_release" "$sget_wc_arch" "$sget_wc_hash"
then
printf '%s\n' 1 >"$sget_wc_cache"
return 1
fi
printf '%s\n' 0 >"$sget_wc_cache"
return 0
}
# Runtime closure first. Add builddeps only for packages that will be
# compiled, and skip well-known tools already on PATH.
sget_plan_with_builddeps()
{
sget_all_roots=$sget_roots
sget_round=0
mkdir -p "$sget_work/will-compile"
while :
do
sget_round=$((sget_round + 1))
[ "$sget_round" -lt 32 ] ||
sps_die "$SPS_EX_DEPENDENCY" "build-dependency expansion did not settle"
if ! awk -v action=plan -v roots="$sget_all_roots" \
-v include_build=0 -v program=sget \
-v installed="$(sget_installed_names)" \
-f "$SPS_LIBDIR/version.awk" -f "$SPS_LIBDIR/deps.awk" \
"$sps_index" >"$sget_work/resolution"
then
sps_die "$SPS_EX_DEPENDENCY" "cannot resolve requested packages"
fi
: >"$sget_work/new-build-roots"
while IFS="$sps_tab" read -r sget_pl_name sget_pl_recipe sget_pl_hash \
sget_pl_version sget_pl_release sget_pl_arch sget_pl_bdeps || \
[ -n "$sget_pl_name" ]
do
[ -n "$sget_pl_name" ] || continue
sget_will_compile "$sget_pl_name" "$sget_pl_version" \
"$sget_pl_release" "$sget_pl_arch" "$sget_pl_hash" || continue
[ -n "$sget_pl_bdeps" ] || continue
sget_pl_saved_ifs=$IFS
IFS=,
set -- $sget_pl_bdeps
IFS=$sget_pl_saved_ifs
for sget_pl_bdep
do
sget_pl_bdep=$(printf '%s' "$sget_pl_bdep" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
[ -n "$sget_pl_bdep" ] || continue
case $sget_pl_bdep in
*'>='*) sget_pl_bdep=${sget_pl_bdep%%>=*} ;;
*'<='*) sget_pl_bdep=${sget_pl_bdep%%<=*} ;;
*'='*) sget_pl_bdep=${sget_pl_bdep%%=*} ;;
*'>'*) sget_pl_bdep=${sget_pl_bdep%%>*} ;;
*'<'*) sget_pl_bdep=${sget_pl_bdep%%<*} ;;
esac
[ -n "$sget_pl_bdep" ] || continue
case ,$sget_all_roots, in
*,"$sget_pl_bdep",*) continue ;;
esac
if sget_host_build_tool "$sget_pl_bdep"; then
sget_note "using host $sget_pl_bdep to build $sget_pl_name"
continue
fi
printf '%s\n' "$sget_pl_bdep" >>"$sget_work/new-build-roots"
done
done <"$sget_work/resolution"
[ -s "$sget_work/new-build-roots" ] || return 0
sget_new_roots=
while IFS= read -r sget_pl_bdep || [ -n "$sget_pl_bdep" ]
do
[ -n "$sget_pl_bdep" ] || continue
case ,$sget_all_roots, in
*,"$sget_pl_bdep",*) continue ;;
esac
case ,$sget_new_roots, in
*,"$sget_pl_bdep",*) continue ;;
esac
[ -n "$sget_new_roots" ] &&
sget_new_roots=$sget_new_roots,$sget_pl_bdep ||
sget_new_roots=$sget_pl_bdep
done <"$sget_work/new-build-roots"
[ -n "$sget_new_roots" ] || return 0
sget_all_roots=$sget_new_roots,$sget_all_roots
done
}
sget_install()
{
sget_plan=0
sget_nodeps=0
sget_reinstall=0
sget_allow_downgrade=0
while [ "$#" -gt 0 ]; do
case $1 in
--plan) sget_plan=1; shift ;;
--nodeps) sget_nodeps=1; shift ;;
--reinstall) sget_reinstall=1; shift ;;
--downgrade) sget_allow_downgrade=1; shift ;;
-q|--quiet) sget_quiet=1; shift ;;
-v|--verbose) sget_verbose=1; shift ;;
--) shift; break ;;
-*) sps_die "$SPS_EX_USAGE" "unknown option '$1'" ;;
*) break ;;
esac
done
[ "$#" -gt 0 ] || { sget_usage >&2; exit "$SPS_EX_USAGE"; }
sget_need_index
sget_roots=
for sget_package
do
sps_validate_package_name "$sget_package" ||
sps_die "$SPS_EX_USAGE" "invalid package name '$sget_package'"
case ,$sget_roots, in
*,"$sget_package",*)
sps_die "$SPS_EX_USAGE" "package '$sget_package' was requested more than once" ;;
esac
[ -n "$sget_roots" ] && sget_roots=$sget_roots,$sget_package || sget_roots=$sget_package
done
sget_work=$(sps_mktemp_dir "${TMPDIR:-/tmp}" sget)
trap sget_cleanup_install 0
trap 'exit 129' 1
trap 'exit 130' 2
trap 'exit 131' 3
trap 'exit 143' 15
: >"$sget_work/resolution"
: >"$sget_work/todo"
: >"$sget_work/mark-explicit"
sps_mkdir "$SPS_CACHE/packages"
if [ "$sget_nodeps" -eq 1 ]; then
for sget_package
do
awk -v action=install_record -v query="$sget_package" \
-v program=sget -f "$SPS_LIBDIR/index.awk" "$sps_index" \
>>"$sget_work/resolution" || exit $?
done
else
sget_note "resolving $sget_roots"
sget_plan_with_builddeps
fi
sget_count=0
while IFS="$sps_tab" read -r sget_package sget_recipe sget_recipe_hash \
sget_version sget_release sget_arch sget_unused_bdeps || \
[ -n "$sget_package" ]
do
[ -n "$sget_package" ] || continue
case $sget_arch in
any|noarch|"$SPS_ARCH") ;;
*) sps_die "$SPS_EX_PACKAGE" "'$sget_package' targets '$sget_arch', not '$SPS_ARCH'" ;;
esac
sget_action=install
if sget_read_installed "$sget_package"; then
sget_cmp=$(sget_compare_selected "$sget_version" "$sget_release" \
"$sget_installed_version" "$sget_installed_release") ||
sps_die "$SPS_EX_PACKAGE" "cannot compare package versions"
if [ "$sget_cmp" -eq 0 ] && [ "$sget_installed_arch" = "$sget_arch" ]; then
case ,$sget_roots, in
*,"$sget_package",*)
if [ ! -r "$SPS_DB/world" ] ||
! grep -F -x -e "$sget_package" "$SPS_DB/world" >/dev/null 2>&1; then
printf '%s\n' "$sget_package" >>"$sget_work/mark-explicit"
fi
;;
esac
if [ "$sget_reinstall" -eq 1 ] && [ "$sget_upgrade_mode" -eq 0 ]; then
sget_action=reinstall
else
sget_note "$sget_package satisfied by $sget_installed_version-$sget_installed_release"
continue
fi
elif [ "$sget_cmp" -gt 0 ]; then
sget_action=upgrade
else
sget_action=downgrade
if [ "$sget_upgrade_mode" -eq 1 ] && [ "$sget_allow_downgrade" -eq 0 ]; then
printf "%s: keeping newer installed %s %s-%s (repository has %s-%s)\n" \
"$sps_prog" "$sget_package" "$sget_installed_version" \
"$sget_installed_release" "$sget_version" "$sget_release" >&2
continue
fi
fi
fi
sget_old_version=
sget_old_release=
case $sget_action in
upgrade|downgrade|reinstall)
sget_old_version=$sget_installed_version
sget_old_release=$sget_installed_release
;;
esac
printf '%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n' \
"$sget_action" "$sget_package" "$sget_recipe" "$sget_recipe_hash" \
"$sget_version" "$sget_release" "$sget_arch" \
"$sget_old_version" "$sget_old_release" >>"$sget_work/todo"
sget_count=$((sget_count + 1))
done <"$sget_work/resolution"
sget_mark_count=$(awk 'END { print NR + 0 }' "$sget_work/mark-explicit")
sget_note "$sget_count package action(s), $sget_mark_count reason change(s)"
if [ "$sget_plan" -eq 1 ]; then
if [ "$sget_upgrade_mode" -eq 1 ]; then
printf 'Upgrade plan\n\n'
else
printf 'Install plan\n\n'
fi
if [ "$sget_count" -eq 0 ] && [ "$sget_mark_count" -eq 0 ]; then
printf '%s\n' 'No changes are needed.'
else
while IFS="$sps_tab" read -r sget_action sget_package sget_recipe \
sget_recipe_hash sget_version sget_release sget_arch \
sget_old_version sget_old_release || [ -n "$sget_action" ]
do
[ -n "$sget_action" ] || continue
if [ "$sget_action" = upgrade ] || [ "$sget_action" = downgrade ]; then
printf ' %-10s %s %s-%s -> %s-%s\n' \
"$sget_action" "$sget_package" \
"$sget_old_version" "$sget_old_release" \
"$sget_version" "$sget_release"
else
printf ' %-10s %s %s-%s\n' \
"$sget_action" "$sget_package" "$sget_version" "$sget_release"
fi
done <"$sget_work/todo"
while IFS= read -r sget_package || [ -n "$sget_package" ]; do
[ -n "$sget_package" ] && printf ' %-10s %s\n' explicit "$sget_package"
done <"$sget_work/mark-explicit"
if [ "$sget_mark_count" -gt 0 ]; then
printf '\n%d packages, %d reason changes\n' "$sget_count" "$sget_mark_count"
else
printf '\n%d packages\n' "$sget_count"
fi
fi
sget_cleanup_install
trap - 0 1 2 3 15
return 0
fi
if [ "$sget_count" -gt 0 ]; then
sps_require_cmd mkpkg
sps_require_cmd pkin
sget_seen=0
while IFS="$sps_tab" read -r sget_action sget_package sget_recipe \
sget_recipe_hash sget_version sget_release sget_arch \
sget_old_version sget_old_release || [ -n "$sget_action" ]
do
[ -n "$sget_action" ] || continue
[ -f "$sget_recipe" ] ||
sps_die "$SPS_EX_NOTFOUND" "recipe for '$sget_package' is missing: $sget_recipe"
sget_current_hash=$(sps_definition_sha256 "$sget_recipe") ||
sps_die "$SPS_EX_CHECKSUM" "cannot hash package definition for '$sget_package'"
[ "$sget_current_hash" = "$sget_recipe_hash" ] ||
sps_die "$SPS_EX_CHECKSUM" "package definition for '$sget_package' changed since indexing; run 'src update'"
sget_recipe_dir=${sget_recipe%/*}
sget_recipe_name=${sget_recipe##*/}
sget_note "selected $sget_recipe $sget_version-$sget_release"
if [ "$sget_seen" -eq 1 ]; then
printf '\n'
fi
sget_seen=1
case $sget_action in
upgrade|downgrade)
printf '%s %s %s-%s -> %s-%s\n' "$sget_action" "$sget_package" \
"$sget_old_version" "$sget_old_release" \
"$sget_version" "$sget_release"
;;
*)
printf '%s %s %s-%s\n' "$sget_action" "$sget_package" \
"$sget_version" "$sget_release"
;;
esac
sget_note "checking binary cache for $sget_package"
sget_cache_base=$SPS_CACHE/packages/$sget_package-$sget_version-$sget_release-$sget_arch.pkg.tar
sget_artifact=
for sget_candidate in "$sget_cache_base.zst" "$sget_cache_base.xz" \
"$sget_cache_base.gz" "$sget_cache_base"; do
if [ -f "$sget_candidate" ] && [ ! -L "$sget_candidate" ]; then
sget_artifact=$sget_candidate
break
fi
done
if [ -n "$sget_artifact" ]; then
sget_cache_status=0
sget_verify_artifact_identity "$sget_artifact" "$sget_package" \
"$sget_version" "$sget_release" "$sget_arch" \
"$sget_recipe_hash" || sget_cache_status=$?
case $sget_cache_status in
0)
sget_note "binary cache hit"
printf '%s: using cached %s\n' \
"$sps_prog" "${sget_artifact##*/}"
;;
2)
printf '%s: discarding stale cached %s\n' \
"$sps_prog" "${sget_artifact##*/}"
rm -f "$sget_artifact" ||
sps_die "$SPS_EX_PERMISSION" \
"cannot replace stale cache artifact: $sget_artifact"
sget_artifact=
;;
*)
sps_die "$SPS_EX_PACKAGE" \
"cached artifact identity is invalid for '$sget_package': $sget_artifact"
;;
esac
fi
if [ -z "$sget_artifact" ]; then
sget_note "binary cache miss; building $sget_package"
sget_note "building $sget_recipe_dir"
sget_artifact_file=$sget_work/artifact-$sget_package
sget_build_log=$sget_work/build-$sget_package.log
if [ "${sget_quiet:-0}" -eq 1 ]; then
if (CDPATH= cd "$sget_recipe_dir" &&
mkpkg --artifact-file "$sget_artifact_file" \
--output "$SPS_CACHE/packages" "./$sget_recipe_name" \
>"$sget_build_log" 2>&1)
then
:
else
[ ! -s "$sget_build_log" ] || cat "$sget_build_log" >&2
sps_die "$SPS_EX_BUILD" \
"failed to build $sget_package $sget_version-$sget_release"
fi
else
(CDPATH= cd "$sget_recipe_dir" &&
mkpkg --artifact-file "$sget_artifact_file" \
--output "$SPS_CACHE/packages" "./$sget_recipe_name") ||
sps_die "$SPS_EX_BUILD" \
"failed to build $sget_package $sget_version-$sget_release"
fi
sget_artifact=$(sed -n '1p' "$sget_artifact_file" 2>/dev/null) || sget_artifact=
[ -n "$sget_artifact" ] ||
sps_die "$SPS_EX_BUILD" "mkpkg did not report an artifact for '$sget_package'"
[ -f "$sget_artifact" ] ||
sps_die "$SPS_EX_BUILD" "mkpkg artifact for '$sget_package' is missing: $sget_artifact"
sget_verify_artifact_identity "$sget_artifact" "$sget_package" \
"$sget_version" "$sget_release" "$sget_arch" \
"$sget_recipe_hash" ||
sps_die "$SPS_EX_PACKAGE" "mkpkg produced the wrong package identity for '$sget_package'"
fi
case ,$sget_roots, in *,"$sget_package",*) sget_reason=explicit ;; *) sget_reason=dependency ;; esac
sget_pkin_flags=
if [ "${sget_verbose:-0}" -eq 1 ]; then
sget_pkin_flags=-v
elif [ "${sget_quiet:-0}" -eq 1 ]; then
sget_pkin_flags=-q
fi
# shellcheck disable=SC2086
SPS_INSTALL_REASON=$sget_reason pkin $sget_pkin_flags "$sget_artifact" ||
sps_die $? "installation failed for '$sget_package'"
done <"$sget_work/todo"
fi
if [ "$sget_mark_count" -gt 0 ]; then
sps_require_cmd pkmark
# Package names cannot contain spaces, so splitting this list is safe.
set -- $(cat "$sget_work/mark-explicit")
[ "$#" -eq 0 ] || pkmark explicit "$@" || exit $?
fi
if [ "$sget_count" -eq 0 ] && [ "$sget_mark_count" -eq 0 ]; then
[ "$sget_upgrade_mode" -eq 1 ] && printf '%s\n' 'System packages are up to date.' ||
printf '%s\n' 'All requested packages are already installed.'
fi
if [ "${sget_plan:-0}" -eq 0 ] && sps_config_review_pending; then
sget_note "run 'sget config-status' to review protected configuration changes from this update, or 'pkconf' to reconcile them"
fi
sget_cleanup_install
trap - 0 1 2 3 15
}
sget_upgrade()
{
sget_upgrade_plan=0
sget_upgrade_downgrade=0
while [ "$#" -gt 0 ]; do
case $1 in
--plan) sget_upgrade_plan=1 ;;
--downgrade) sget_upgrade_downgrade=1 ;;
-q|--quiet) sget_quiet=1 ;;
-v|--verbose) sget_verbose=1 ;;
*) sget_usage >&2; exit "$SPS_EX_USAGE" ;;
esac
shift
done
[ -f "$SPS_DB/world" ] && [ ! -L "$SPS_DB/world" ] ||
sps_die "$SPS_EX_DATABASE" "world database is missing or unsafe"
awk -v sps_action=world-validate -v sps_program=sget \
-f "$SPS_LIBDIR/db.awk" "$SPS_DB/world" ||
sps_die "$SPS_EX_DATABASE" "world database is malformed"
set -- $(cat "$SPS_DB/world")
if [ "$#" -eq 0 ]; then
printf '%s\n' 'No explicit packages are installed.'
return 0
fi
sget_upgrade_mode=1
if [ "$sget_upgrade_plan" -eq 1 ]; then set -- --plan "$@"; fi
if [ "$sget_upgrade_downgrade" -eq 1 ]; then set -- --downgrade "$@"; fi
sget_install "$@"
}
sget_remove()
{
sget_remove_plan=0
sget_remove_nodeps=0
while [ "$#" -gt 0 ]; do
case $1 in
--plan) sget_remove_plan=1; shift ;;
--nodeps) sget_remove_nodeps=1; shift ;;
-q|--quiet) sget_quiet=1; shift ;;
-v|--verbose) sget_verbose=1; shift ;;
--) shift; break ;;
-*) sps_die "$SPS_EX_USAGE" "unknown option '$1'" ;;
*) break ;;
esac
done
[ "$#" -gt 0 ] || { sget_usage >&2; exit "$SPS_EX_USAGE"; }
[ -d "$SPS_DB/installed" ] && [ ! -L "$SPS_DB/installed" ] ||
sps_die "$SPS_EX_DATABASE" "installed database is missing or unsafe"
sget_remove_work=$(sps_mktemp_dir "${TMPDIR:-/tmp}" remove-plan)
trap 'status=$?; rm -rf "$sget_remove_work"; sps_lock_release; exit "$status"' 0
trap 'exit 130' 1 2 3 15
: >"$sget_remove_work/requested"
for sget_package
do
sps_validate_package_name "$sget_package" ||
sps_die "$SPS_EX_USAGE" "invalid package name '$sget_package'"
[ -d "$SPS_DB/installed/$sget_package" ] && [ ! -L "$SPS_DB/installed/$sget_package" ] ||
sps_die "$SPS_EX_NOTFOUND" "package '$sget_package' is not installed"
if grep -F -x -e "$sget_package" "$sget_remove_work/requested" >/dev/null 2>&1; then
sps_die "$SPS_EX_USAGE" "package '$sget_package' was requested more than once"
fi
printf '%s\n' "$sget_package" >>"$sget_remove_work/requested"
done
: >"$sget_remove_work/edges"
set -- "$SPS_DB"/installed/*/meta
if [ -f "$1" ]; then
awk -v out="$sget_remove_work/edges" '
FNR == 1 {
pkg=FILENAME; sub(/\/meta$/, "", pkg); sub(/^.*\//, "", pkg)
}
{
p=match($0, /[[:space:]]/); if (!p) next
key=substr($0,1,p-1); if (key != "depend") next
value=substr($0,p); sub(/^[[:space:]]+/,"",value)
gsub(/,/," ",value); n=split(value,a,/[[:space:]]+/)
for (i=1;i<=n;i++) {
d=a[i]; sub(/(>=|<=|=|>|<).*/,"",d)
if (d != "") print pkg "\t" d > out
}
}' "$@"
fi
if [ "$sget_remove_nodeps" -eq 0 ]; then
if ! awk -F '\t' '
NR==FNR { req[$1]=1; next }
!($1 in req) && ($2 in req) {
print $1 " requires " $2 > "/dev/stderr"; bad=1
}
END { exit bad }
' "$sget_remove_work/requested" "$sget_remove_work/edges"; then
sps_die "$SPS_EX_DEPENDENCY" "removal would break installed dependencies (use --nodeps to override)"
fi
fi
# Remove requested dependents before their dependencies. Cycles fall back to
# lexical order.
awk -F '\t' '
NR==FNR { req[$1]=1; names[++n]=$1; next }
($1 in req) && ($2 in req) { dep[$1 SUBSEP $2]=1 }
END {
left=n
while (left>0) {
pick=""
for (i=1;i<=n;i++) if (!(names[i] in done)) {
p=names[i]; incoming=0
for (j=1;j<=n;j++) if (!(names[j] in done) && dep[names[j] SUBSEP p]) incoming=1
if (!incoming && (pick=="" || p<pick)) pick=p
}
if (pick=="") for (i=1;i<=n;i++) if (!(names[i] in done) && (pick=="" || names[i]<pick)) pick=names[i]
print pick; done[pick]=1; left--
}
}
' "$sget_remove_work/requested" "$sget_remove_work/edges" >"$sget_remove_work/order"
if [ "$sget_remove_plan" -eq 1 ]; then
printf 'Remove plan\n\n'
while IFS= read -r sget_package || [ -n "$sget_package" ]; do
[ -n "$sget_package" ] && printf ' remove %s\n' "$sget_package"
done <"$sget_remove_work/order"
printf '\n%s packages\n' "$(awk 'END { print NR + 0 }' "$sget_remove_work/order")"
rm -rf "$sget_remove_work"
sps_lock_release
trap - 0 1 2 3 15
return 0
fi
sps_require_cmd pkdel
while IFS= read -r sget_package || [ -n "$sget_package" ]; do
[ -n "$sget_package" ] || continue
pkdel "$sget_package" || exit $?
done <"$sget_remove_work/order"
rm -rf "$sget_remove_work"
if [ "$sget_remove_plan" -eq 0 ] && sps_config_review_pending; then
sget_note "run 'sget config-status' to review protected configuration changes, or 'pkconf' to reconcile them"
fi
sps_lock_release
trap - 0 1 2 3 15
}
# Keep a whole sget install/upgrade/remove from interleaving with another one.
# Low-level tools still use their shorter database lock. Queries and plans do not.
# Read-only. Report protected configuration files that differ from what their
# installed package recorded, and which of those have a packaged default that
# an earlier upgrade wrote beside them (".sps-new*"). The admin reviews and
# merges those by hand; nothing here writes to the filesystem.
sget_config_status()
{
sget_cs_diff=0
sget_cs_raw=0
sget_cs_scope=
sget_cs_nargs=0
while [ "$#" -gt 0 ]; do
case $1 in
--diff) sget_cs_diff=1; shift ;;
--raw) sget_cs_raw=1; shift ;;
--)
shift
while [ "$#" -gt 0 ]; do
sget_cs_scope=$1; sget_cs_nargs=$((sget_cs_nargs + 1)); shift
done
break
;;
-*)
sget_usage >&2
exit "$SPS_EX_USAGE"
;;
*)
sget_cs_scope=$1; sget_cs_nargs=$((sget_cs_nargs + 1)); shift
;;
esac
done
if [ "$sget_cs_diff" -eq 1 ]; then
[ "$sget_cs_nargs" -eq 1 ] ||
sps_die "$SPS_EX_USAGE" "config --diff takes exactly one path"
sps_require_cmd diff ||
sps_die "$SPS_EX_NOTFOUND" "diff is required for config --diff"
else
[ "$sget_cs_nargs" -le 1 ] ||
sps_die "$SPS_EX_USAGE" "config takes at most one package name"
if [ -n "$sget_cs_scope" ]; then
sps_validate_package_name "$sget_cs_scope" ||
sps_die "$SPS_EX_USAGE" "invalid package name '$sget_cs_scope'"
fi
fi
[ -d "$SPS_DB/installed" ] && [ ! -L "$SPS_DB/installed" ] ||
sps_die "$SPS_EX_DATABASE" "installed database is missing or is not a real directory"
[ -f "$SPS_DB/owners" ] && [ ! -L "$SPS_DB/owners" ] ||
sps_die "$SPS_EX_DATABASE" "ownership database is missing or is not a real file"
sget_cs_work=$(sps_mktemp_dir "${TMPDIR:-/tmp}" config) ||
sps_die "$SPS_EX_PERMISSION" "cannot create config review workspace"
sget_cs_cleanup()
{
rm -rf "$sget_cs_work"
}
trap 'sget_cs_cleanup' 0
# --diff accepts the plain path, the ".sps-new" sibling, or the
# ".sps-new.VERSION-RELEASE" spelling. Normalize to the package-relative
# base path before matching hash records.
sget_cs_want=
if [ "$sget_cs_diff" -eq 1 ]; then
case "$SPS_ROOT" in
/) case $sget_cs_scope in /*) sget_cs_want=${sget_cs_scope#/} ;; *) sget_cs_want=$sget_cs_scope ;; esac ;;
*)
case $sget_cs_scope in
"$SPS_ROOT"/*) sget_cs_want=${sget_cs_scope#"$SPS_ROOT"/} ;;
/*) sget_cs_want=${sget_cs_scope#/} ;;
*) sget_cs_want=$sget_cs_scope ;;
esac
;;
esac
sget_cs_want=${sget_cs_want%/}
case $sget_cs_want in
*.sps-new) sget_cs_want=${sget_cs_want%.sps-new} ;;
*.sps-new.*) sget_cs_want=${sget_cs_want%.sps-new.*} ;;
esac
[ -n "$sget_cs_want" ] ||
sps_die "$SPS_EX_USAGE" "invalid config path '$sget_cs_scope'"
if ! printf '%s\n' "$sget_cs_want" | awk -v sps_action=validate-paths \
-v sps_program=sget -f "$SPS_LIBDIR/package.awk"; then
sps_die "$SPS_EX_USAGE" "invalid config path '$sget_cs_scope'"
fi
sget_cs_owner=$(awk -v sps_action=owners-query -v sps_path="$sget_cs_want" \
-v sps_program=sget -f "$SPS_LIBDIR/db.awk" "$SPS_DB/owners" 2>/dev/null) || :
[ -n "$sget_cs_owner" ] ||
sps_die "$SPS_EX_NOTFOUND" "no installed package owns /$sget_cs_want"
fi
# Walk installed hash records for preserved paths. Config files that live
# under SPS_PRESERVE and changed locally are the ones an upgrade refused to
# overwrite, so those are the interesting cases for review.
: >"$sget_cs_work/rows"
for sget_cs_record in "$SPS_DB/installed"/*; do
[ -d "$sget_cs_record" ] && [ ! -L "$sget_cs_record" ] || continue
sget_cs_name=${sget_cs_record##*/}
case $sget_cs_name in .|..) continue ;; esac
[ -f "$sget_cs_record/hashes" ] && [ ! -L "$sget_cs_record/hashes" ] || continue
[ -f "$sget_cs_record/meta" ] && [ ! -L "$sget_cs_record/meta" ] || continue
if [ "$sget_cs_diff" -eq 0 ] && [ -n "$sget_cs_scope" ] &&
[ "$sget_cs_name" != "$sget_cs_scope" ]; then
continue
fi
while IFS="$sps_tab" read -r sget_cs_algo sget_cs_expected sget_cs_path \
|| [ -n "$sget_cs_path" ]; do
[ -n "$sget_cs_path" ] || continue
if [ "$sget_cs_diff" -eq 1 ]; then
[ "$sget_cs_path" = "$sget_cs_want" ] || continue
fi
sps_is_preserved_path "$sget_cs_path" || continue
sget_cs_target=$(sps_root_path "/$sget_cs_path")
sget_cs_state=clean
if [ -f "$sget_cs_target" ] && [ ! -L "$sget_cs_target" ]; then
sget_cs_live=$(sps_sha256 "$sget_cs_target") || sget_cs_live=
set -- $sget_cs_live
sget_cs_live=$1
if [ -n "$sget_cs_live" ] && [ "$sget_cs_live" != "$sget_cs_expected" ]; then
sget_cs_state=modified
fi
elif [ "$sget_cs_diff" -eq 1 ]; then
sget_cs_state=missing
else
continue
fi
sget_cs_defaults=$(sps_config_new_defaults "$sget_cs_path")
sget_cs_has_default=0
for sget_cs_def in $sget_cs_defaults; do
[ -n "$sget_cs_def" ] && sget_cs_has_default=1
done
printf '%s\t%s\t%s\t%s\n' \
"$sget_cs_name" "$sget_cs_path" "$sget_cs_state" \
"$sget_cs_has_default" >>"$sget_cs_work/rows"
done <"$sget_cs_record/hashes"
done
LC_ALL=C sort "$sget_cs_work/rows" | LC_ALL=C uniq >"$sget_cs_work/rows.sorted"
mv -f "$sget_cs_work/rows.sorted" "$sget_cs_work/rows"
if [ "$sget_cs_diff" -eq 1 ]; then
sget_cs_found=0
sget_cs_state=clean
while IFS="$sps_tab" read -r sget_cs_name sget_cs_path sget_cs_state \
sget_cs_has_default; do
[ -n "$sget_cs_name" ] || continue
sget_cs_found=1
done <"$sget_cs_work/rows"
[ "$sget_cs_found" -eq 1 ] ||
sps_die "$SPS_EX_NOTFOUND" "/$sget_cs_want is not a managed protected config file"
sget_cs_live_path=$(sps_root_path "/$sget_cs_want")
sget_cs_defaults=$(sps_config_new_defaults "$sget_cs_want")
sget_cs_first=0
if [ "$sget_cs_state" = missing ]; then
sget_cs_first=1
printf '%s\n' "# /$sget_cs_want (owned by $sget_cs_owner)"
printf '%s\n' 'The local file is missing.'
for sget_cs_default in $sget_cs_defaults; do
[ -f "$sget_cs_default" ] && [ ! -L "$sget_cs_default" ] || continue
diff -u /dev/null "$sget_cs_default"
done
[ -n "$sget_cs_defaults" ] ||
printf '%s\n' 'No packaged default (.sps-new) is present for this file.'
exit 0
fi
for sget_cs_default in $sget_cs_defaults; do
[ -f "$sget_cs_default" ] && [ ! -L "$sget_cs_default" ] || continue
if [ "$sget_cs_first" -eq 0 ]; then
printf '%s\n' "# /$sget_cs_want (owned by $sget_cs_owner)"
printf '%s\n' 'Local file versus packaged default (written as .sps-new during an upgrade):'
sget_cs_first=1
fi
diff -u "$sget_cs_live_path" "$sget_cs_default"
done
if [ "$sget_cs_first" -eq 0 ]; then
printf '%s\n' "# /$sget_cs_want (owned by $sget_cs_owner)"
printf '%s\n' 'No packaged default (.sps-new) is present for this file.'
fi
exit 0
fi
sget_cs_any=0
sget_cs_pending=0
if [ -s "$sget_cs_work/rows" ]; then
while IFS="$sps_tab" read -r sget_cs_name sget_cs_path sget_cs_state \
sget_cs_has_default; do
[ -n "$sget_cs_name" ] || continue
case $sget_cs_state in
modified)
sget_cs_pending=$((sget_cs_pending + 1))
if [ "$sget_cs_has_default" -eq 1 ]; then
sget_cs_label='new packaged default available'
else
sget_cs_label='modified locally'
fi
;;
*)
[ "$sget_cs_has_default" -eq 1 ] || continue
sget_cs_label='matches package; .sps-new left over'
;;
esac
sget_cs_any=1
if [ "$sget_cs_raw" -eq 1 ]; then
printf '%s\t%s\t%s\t%s\n' \
"$sget_cs_name" "$sget_cs_path" "$sget_cs_state" \
"$sget_cs_has_default"
else
printf '%-18s /%-32s %s\n' \
"$sget_cs_name" "$sget_cs_path" "$sget_cs_label"
fi
done <"$sget_cs_work/rows"
fi
if [ "$sget_cs_any" -eq 0 ] && [ "$sget_cs_raw" -eq 0 ]; then
printf '%s\n' 'no protected configuration changes found'
fi
printf '%s\n' "summary: $sget_cs_pending config file(s) differ from their packaged default" >&2
exit 0
}
sget_quiet=0
sget_verbose=0
while [ "$#" -gt 0 ]; do
case $1 in
-q|--quiet) sget_quiet=1; shift ;;
-v|--verbose) sget_verbose=1; shift ;;
-h|--help) sget_usage; exit 0 ;;
--) shift; break ;;
-*)
sget_usage >&2
exit "$SPS_EX_USAGE"
;;
*) break ;;
esac
done
sget_top_command=${1-}
case $sget_top_command in
install|upgrade|remove)
sget_top_plan=0
shift_index=0
for sget_top_arg in "$@"; do
[ "$shift_index" -eq 0 ] && { shift_index=1; continue; }
[ "$sget_top_arg" = --plan ] && sget_top_plan=1
done
if [ "$sget_top_plan" -eq 0 ]; then
sget_begin_transaction
trap 'status=$?; sps_lock_release; exit "$status"' 0
trap 'exit 130' 1 2 3 15
fi
;;
esac
case ${1-} in
search|info)
sget_command=$1; shift; sget_query "$sget_command" "$@" ;;
depends)
shift; sget_depends "$@" ;;
dependees)
shift; sget_dependees "$@" ;;
why)
shift; sget_why "$@" ;;
install)
shift; sget_upgrade_mode=0; sget_install "$@" ;;
upgrade)
shift; sget_upgrade "$@" ;;
remove)
shift; sget_remove "$@" ;;
config-status)
shift; sget_config_status "$@" ;;
-h|--help)
sget_usage ;;
*)
sget_usage >&2; exit "$SPS_EX_USAGE" ;;
esac