prt-auf: delegate install/update to pkgmk

avoid having multiple independent parsers of /etc/pkgmk.conf
This commit is contained in:
John McQuah
2025-11-09 18:12:04 +00:00
parent 8f7c8f592b
commit 4145a63e04
2 changed files with 447 additions and 416 deletions

View File

@@ -10,18 +10,43 @@ main() {
######################## main routine ################################ ######################## main routine ################################
local N pkg_dir src_dir work _local_ here url u pkg_utd f DIR TARGET local N pkg_dir src_dir work _local_ here url u pkg_utd f DIR TARGET
local errDL=0; local errUZ=0; local BSDTAR="/usr/bin/bsdtar --format=gnutar" local errDL=0; local errUZ=0; local BSDTAR="/usr/bin/bsdtar --format=gnutar"
declare -A ZFLAG=([xz]="-J" [bz2]="-j" [gz]="-z" [lz4]="--lz4" [zstd]="--zstd") declare -A ZFLAG=([xz]="-J" [bz2]="-j" [gz]="-z" [lz]="--lzip" [lz4]="--lz4" [zst]="--zstd")
declare -A CMD_FLAGS
parse_options "$@" parse_options "$@"
# Exit early if refreshing an existing sha256 manifest was requested # Exit early if refreshing an existing sha256 manifest was requested
[ "$PKGMK_REFRESH_SIG" = "yes" ] && { make_signature refresh; exit $?; } [ "$PKGMK_REFRESH_SIG" = "yes" ] && { make_signature refresh; exit $?; }
#
# Read the Pkgfile to determine what to do next. But first ensure that # Read the Pkgfile to determine what to do next. But first ensure that
# it came from a trusted source (FS#1851) # it came from a trusted source (FS#1851)
#
[ "$PKGMK_UPDATE_SIG" = "yes" ] && [ -e ./Pkgfile ] || validate_pkgfile || exit $E_PKGFILE [ "${CMD_FLAGS[is]}${CMD_FLAGS[us]}" ] && PKGMK_IGNORE_SIG="yes"
validate_pkgfile || exit $E_PKGFILE
[ -f .32bit ] && PKGMK_ARCH=32 || PKGMK_ARCH=64 [ -f .32bit ] && PKGMK_ARCH=32 || PKGMK_ARCH=64
. "Pkgfile"; . "$PKGMK_CONF" . "Pkgfile"; . "$PKGMK_CONF"
if [ -z "$LIBC" ]; then
LIBC=$(pkginfo -o libc.so | awk '/libc.so/ {if ($1 == "glibc"){print "gnu"} else {print $1}}')
fi
# Ensure that command-line flags take precedence
[ "${CMD_FLAGS[i]}" ] && PKGMK_INSTALL="yes"
[ "${CMD_FLAGS[u]}" ] && PKGMK_INSTALL="update"
[ "${CMD_FLAGS[d]}" ] && PKGMK_DOWNLOAD="yes"
[ "${CMD_FLAGS[do]}" ] && PKGMK_DOWNLOAD_ONLY="yes"
[ "${CMD_FLAGS[eo]}" ] && PKGMK_EXTRACT_ONLY="yes"
[ "${CMD_FLAGS[utd]}" ] && PKGMK_MTIME_ONLY="yes"
[ "${CMD_FLAGS[uf]}" ] && PKGMK_UPDATE_FOOTPRINT="yes"
[ "${CMD_FLAGS[if]}" ] && PKGMK_IGNORE_FOOTPRINT="yes"
[ "${CMD_FLAGS[in]}" ] && PKGMK_IGNORE_NEW="yes"
[ "${CMD_FLAGS[f]}" ] && PKGMK_FORCE="yes"
[ "${CMD_FLAGS[kw]}" ] && PKGMK_KEEP_WORK="yes"
[ "${CMD_FLAGS[us]}" ] && PKGMK_UPDATE_SIG="yes"
[ "${CMD_FLAGS[is]}" ] && PKGMK_IGNORE_SIG="yes"
[ "${CMD_FLAGS[cs]}" ] && PKGMK_CHECK_SIG="yes"
[ "${CMD_FLAGS[rs]}" ] && PKGMK_REFRESH_SIG="yes"
[ "${CMD_FLAGS[ns]}" ] && PKGMK_NO_STRIP="yes"
[ "${KEY_FILES[pk]}" ] && PKGMK_PUBLICKEY="${KEY_FILES[pk]}"
[ "${KEY_FILES[sk]}" ] && PKGMK_PRIVATEKEY="${KEY_FILES[sk]}"
# respect the settings for centralized source and package directories ... # respect the settings for centralized source and package directories ...
[ -v pkg_dir ] || pkg_dir="$PKGMK_PACKAGE_DIR"/ [ -v pkg_dir ] || pkg_dir="$PKGMK_PACKAGE_DIR"/
@@ -37,17 +62,17 @@ parse_options "$@"
package="${name}#${version}-${release}.pkg.tar.${PKGMK_COMPRESSION_MODE}" package="${name}#${version}-${release}.pkg.tar.${PKGMK_COMPRESSION_MODE}"
declare -a _local_ declare -a _local_
for (( s=0; s<${#source[@]}; s++ )); do for (( s=0; s<${#source[@]}; s++ )); do
case "${source[s]}" in case "${source[s]}" in
http://*|https://*|ftp://*|__git__*) http://*|https://*|ftp://*|__git__*)
_local_[s]="${source[s]##*/}" # strip the leading path _local_[s]="${source[s]##*/}" # strip the leading path
# and for git sources, extract the project name: # and for git sources, extract the project name:
[[ "${source[s]}" =~ ^__git__ ]] && { _local_[s]="${_local_[s]%%\#*}"; [[ "${source[s]}" =~ ^__git__ ]] && { _local_[s]="${_local_[s]%%\#*}";
_local_[s]="${_local_[s]%.git}"; } ;; _local_[s]="${_local_[s]%.git}"; } ;;
*) *)
_local_[s]="${source[s]}" ;; _local_[s]="${source[s]}" ;;
esac esac
[ -z "${renames[s]}" ] || [ "${renames[s]}" = "SKIP" ] || \ [ -z "${renames[s]}" ] || [ "${renames[s]}" = "SKIP" ] || \
_local_[s]="${renames[s]}" _local_[s]="${renames[s]}"
done done
# Example: source = ( __git__https://gitlab.com/demo-user/cool-project.git#0.4.9 # Example: source = ( __git__https://gitlab.com/demo-user/cool-project.git#0.4.9
# https://dev.big-corp.com/src/needed-library.tgz # https://dev.big-corp.com/src/needed-library.tgz
@@ -62,8 +87,9 @@ done
mkdir -p "$work"/{src,pkg} && cd "$work" mkdir -p "$work"/{src,pkg} && cd "$work"
# Skip the retrieval of sources if the user asked for '-utd' or '-uf'. # Skip the retrieval of sources if the user asked for '-utd' or '-uf'.
[[ "$PKGMK_MTIME_ONLY $PKGMK_UPDATE_FOOTPRINT" =~ yes ]] && [ -e "$PKGMK_PACKAGE_DIR/$package" ] \ [[ "$PKGMK_MTIME_ONLY $PKGMK_UPDATE_FOOTPRINT" =~ yes ]] || \
|| { for (( u=0; u<${#_local_[@]}; u++ )); do [ "$(type -t download_source)" = "function" ] || \
{ for (( u=0; u<${#_local_[@]}; u++ )); do
here="${_local_[u]}"; url="${source[u]}"; here="${_local_[u]}"; url="${source[u]}";
# at least one of the following commands should put a file of the # at least one of the following commands should put a file of the
# appropriate name in the current directory # appropriate name in the current directory
@@ -73,14 +99,18 @@ here="${_local_[u]}"; url="${source[u]}";
[ -e "$here" ] || { error "failed to download $here. Check connection and try again."; errDL+=1; } [ -e "$here" ] || { error "failed to download $here. Check connection and try again."; errDL+=1; }
done ; } done ; }
[ $errDL = 0 ] && { [[ "$PKGMK_MTIME_ONLY $PKGMK_UPDATE_FOOTPRINT" =~ yes ]] || \ # accommodate the ports that redefine download_source()
info "Successfully obtained all needed source files."; } || exit $E_DOWNLOAD if [[ ! "$PKGMK_MTIME_ONLY $PKGMK_UPDATE_FOOTPRINT" =~ yes ]] && [ "$(type -t download_source)" = "function" ]
then
download_source && info "Successfully obtained all needed source files." \
|| { error "failed to download sources."; exit $E_DOWNLOAD; }
fi
# Can stop here if the user asked for '-do', but honor any requests for '-um', '-uf', # Can stop here if the user asked for '-do', but honor any requests for '-um', '-uf',
# '-us', '-eo', or '-utd' by proceeding to those steps # '-us', '-eo', or '-utd' by proceeding to those steps
[ "$PKGMK_DOWNLOAD_ONLY" = "no" ] || [ "$PKGMK_UPDATE_FOOTPRINT" = "yes" ] \ [ "$PKGMK_DOWNLOAD_ONLY" = "no" ] || [ "$PKGMK_UPDATE_FOOTPRINT" = "yes" ] \
|| [ "$PKGMK_UPDATE_SIG" = "yes" ] || [ "$PKGMK_EXTRACT_ONLY" = "yes" ] \ || [ "$PKGMK_UPDATE_SIG" = "yes" ] || [ "$PKGMK_EXTRACT_ONLY" = "yes" ] \
|| [ "$PKGMK_MTIME_ONLY" = "yes" ] || { cleanup_work; exit 0; } || [ "$PKGMK_MTIME_ONLY" = "yes" ] || { cleanup_work; exit 0; }
# If '-utd' was requested, check the modification times and then exit. # If '-utd' was requested, check the modification times and then exit.
check_pkg_mtime; pkg_utd=$? check_pkg_mtime; pkg_utd=$?
@@ -89,25 +119,26 @@ check_pkg_mtime; pkg_utd=$?
# Take into account all the actions that can be done without extracting # Take into account all the actions that can be done without extracting
# the downloaded sources # the downloaded sources
[ "$pkg_utd" = 0 ] || [ "$PKGMK_FORCE" = "yes" ] || [ "$PKGMK_EXTRACT_ONLY" = "yes" ] \ [ "$pkg_utd" = 0 ] || [ "$PKGMK_FORCE" = "yes" ] || [ "$PKGMK_EXTRACT_ONLY" = "yes" ] \
|| [[ "$PKGMK_UPDATE_FOOTPRINT $PKGMK_CHECK_SIG $PKGMK_UPDATE_SIG" =~ yes ]] \ || [[ "$PKGMK_UPDATE_FOOTPRINT $PKGMK_CHECK_SIG $PKGMK_UPDATE_SIG" =~ yes ]] \
|| { info "$package is up to date, use '-f' to force a rebuild."; cleanup_work; exit 0; } || [ "$PKGMK_INSTALL" != "no" ] \
|| { info "$package is up to date, use '-f' to force a rebuild."; cleanup_work; exit 0; }
# Silence the progress report if the user never intended to proceed with unpacking # Silence the progress report if the user never intended to proceed with unpacking
[ "$pkg_utd" = 1 ] || [[ "$PKGMK_CHECK_SIG $PKGMK_UPDATE_SIG" =~ yes ]] || \ [ "$pkg_utd" = 1 ] || [[ "$PKGMK_CHECK_SIG $PKGMK_UPDATE_SIG" =~ yes ]] || \
[[ "$PKGMK_IGNORE_SIG $PKGMK_UPDATE_FOOTPRINT" =~ yes ]] \ [[ "$PKGMK_IGNORE_SIG $PKGMK_UPDATE_FOOTPRINT" =~ yes ]] \
|| echo "Checking signatures before unpacking..." || echo "Checking signatures before unpacking..."
# The option -uf is meant to be used *after* a previous invocation of pkgmeek has # The option -uf is meant to be used *after* a previous invocation of pkgmeek has
# alerted the user to a footprint mismatch. # alerted the user to a footprint mismatch.
if [ "$PKGMK_UPDATE_FOOTPRINT" = "yes" ]; then if [ "$PKGMK_UPDATE_FOOTPRINT" = "yes" ]; then
[ -f "$pkg_dir$package" ] || \ [ -f "$pkg_dir$package" ] || \
{ error "unable to update footprint. File '$package' not found."; { error "unable to update footprint. File '$package' not found.";
cleanup_work; exit "$E_FOOTPRINT"; } cleanup_work; exit "$E_FOOTPRINT"; }
[ "$pkg_utd" = 1 ] || [ "$PKGMK_FORCE" = "yes" ] || \ [ "$pkg_utd" = 1 ] || [ "$PKGMK_FORCE" = "yes" ] || \
{ error "outdated package. Use '-f' to force the footprint update."; { error "outdated package. Use '-f' to force the footprint update.";
cleanup_work; exit "$E_FOOTPRINT"; } cleanup_work; exit "$E_FOOTPRINT"; }
cat_footprint > "$PKGMK_ROOT/.footprint" && { info "footprint created."; exit 0; } \ make_footprint > "$PKGMK_ROOT/.footprint" && { info "footprint created."; exit 0; } \
|| { error "Failed to write the footprint."; cleanup_work; exit "$E_DIRPERM"; } || { error "Failed to write the footprint."; cleanup_work; exit "$E_DIRPERM"; }
fi fi
# Updating signatures (option -us) requires only sources and footprint, not a built package. # Updating signatures (option -us) requires only sources and footprint, not a built package.
# As with -uf, exit after fulfilling the explicit request for a manifest. # As with -uf, exit after fulfilling the explicit request for a manifest.
@@ -117,81 +148,81 @@ fi
readonly cs_fail_msg="Use '--ignore-signature' to override, if you have determined integrity by other means." readonly cs_fail_msg="Use '--ignore-signature' to override, if you have determined integrity by other means."
[ "$PKGMK_IGNORE_SIG" = "yes" ] || { check_signature "pre-build" | parse_signify_output; [ "$PKGMK_IGNORE_SIG" = "yes" ] || { check_signature "pre-build" | parse_signify_output;
case $? in case $? in
0) info "Sources successfully authenticated." ;; 0) info "Sources successfully authenticated." ;;
1) error "Signature file missing or corrupted." 1) error "Signature file missing or corrupted."
echo "$cs_fail_msg" ; cleanup_work; exit $E_SIGNATURE ;; echo "$cs_fail_msg" ; cleanup_work; exit $E_SIGNATURE ;;
2) error "Failed to authenticate remote sources using signify." 2) error "Failed to authenticate remote sources using signify."
echo "$cs_fail_msg" ; cleanup_work; exit $E_SIGNATURE ;; echo "$cs_fail_msg" ; cleanup_work; exit $E_SIGNATURE ;;
esac; } esac; }
[ "$PKGMK_CHECK_SIG" = "no" ] || { cleanup_work; exit 0; } # no need to continue if the user only requested -cs [ "$PKGMK_CHECK_SIG" = "no" ] || { cleanup_work; exit 0; } # no need to continue if the user only requested -cs
if [ "$pkg_utd" = 0 ] || [[ "$PKGMK_FORCE $PKGMK_EXTRACT_ONLY" =~ yes ]]; then if [ "$pkg_utd" = 0 ] || [[ "$PKGMK_FORCE $PKGMK_EXTRACT_ONLY" =~ yes ]]; then
if [ "$(type -t unpack_source)" = "function" ]; then if [ "$(type -t unpack_source)" = "function" ]; then
SRC=$PWD/src unpack_source || errUZ+=1 SRC=$PWD/src unpack_source || errUZ+=1
else else
for (( u=0; u<${#_local_[@]}; u++ )) ; do for (( u=0; u<${#_local_[@]}; u++ )) ; do
here="${_local_[u]}" here="${_local_[u]}"
case "$here" in case "$here" in
*.tar|*.tar.gz|*.tar.Z|*.tgz|*.tar.bz2|*.tbz2|*.tar.xz|*.txz|*.tar.lzma|*.tar.lz|*.7z|*.zip|*.rpm) *.tar|*.tar.gz|*.tar.Z|*.tgz|*.tar.bz2|*.tbz2|*.tar.xz|*.txz|*.tar.lzma|*.tar.lz|*.7z|*.zip|*.rpm)
bsdtar -p -o -C src -xf "$here" || errUZ+=1 ;; bsdtar -p -o -C src -xf "$here" || errUZ+=1 ;;
*) *)
cp -r -L "$here" src/ ;; cp -r -L "$here" src/ ;;
esac esac
done done
fi fi
[ $errUZ = 0 ] && info "Sources successfully unpacked." || \ [ $errUZ = 0 ] && info "Sources successfully unpacked." || \
{ error "Failed to unpack all sources."; cleanup_work; exit "$E_UNPACK"; } { error "Failed to unpack all sources."; cleanup_work; exit "$E_UNPACK"; }
[ "$PKGMK_EXTRACT_ONLY" = "no" ] || exit 0 [ "$PKGMK_EXTRACT_ONLY" = "no" ] || exit 0
# The actual build step! (use fakeroot when building daemon ports as an ordinary user, # The actual build step! (use fakeroot when building daemon ports as an ordinary user,
# otherwise the owner and group might not be correct) # otherwise the owner and group might not be correct)
(SRC=$(pwd)/src; PKG=$(pwd)/pkg; cd src; set -e -x; build) (SRC=$(pwd)/src; PKG=$(pwd)/pkg; cd src; set -e -x; build)
[ $? = 0 ] && echo "Build successful. Moving on to compression." \ [ $? = 0 ] && echo "Build successful. Moving on to compression." \
|| { error "Unsuccessful build!"; cleanup_work; exit "$E_BUILD"; } || { error "Unsuccessful build!"; cleanup_work; exit "$E_BUILD"; }
# Strip binaries # Strip binaries
N=$(nproc) N=$(nproc)
[ -n "$ns_filter" ] || [ ! -f "$PKGMK_ROOT/.nostrip" ] \ [ -n "$ns_filter" ] || [ ! -f "$PKGMK_ROOT/.nostrip" ] \
|| ns_filter="| grep -v -f $PKGMK_ROOT/.nostrip" || ns_filter="| grep -v -f $PKGMK_ROOT/.nostrip"
ns_filter+="| xargs -r -L10 -P$N file --no-buffer --separator '>'" ns_filter+="| xargs -r -L10 -P$N file --no-buffer --separator '>'"
ns_filter+=" -e apptype -e text -e encoding -e cdf -e compress -e tar" ns_filter+=" -e apptype -e text -e encoding -e cdf -e compress -e tar"
< <(eval find pkg -type f -printf "%P\n" $ns_filter) awk 'BEGIN { FS=">[ ]+" } < <(eval find pkg -type f -printf "%P\n" $ns_filter) awk 'BEGIN { FS=">[ ]+" }
$0 ~ /ELF.*executable.*not stripped/ { print "--strip-all \"" $1 "\"" } $0 ~ /ELF.*executable.*not stripped/ { print "--strip-all \"" $1 "\"" }
$0 ~ /ELF.*shared object.*not stripped/ { print "--strip-unneeded \"" $1 "\"" } $0 ~ /ELF.*shared object.*not stripped/ { print "--strip-unneeded \"" $1 "\"" }
$2 == "current ar archive" { print "--strip-debug \"" $1 "\"" }' \ $2 == "current ar archive" { print "--strip-debug \"" $1 "\"" }' \
| xargs -r -L1 -P$N strip | xargs -r -L1 -P$N strip
# Compress anything under /man that does not appear to be gzipped # Compress anything under /man that does not appear to be gzipped
find pkg -type f -path "*/man/man*/*" \! -iname '*.gz' \ find pkg -type f -path "*/man/man*/*" \! -iname '*.gz' \
-exec gzip -9 '{}' + -exec gzip -9 '{}' +
# Ensure that symlinks point to the (now-compressed) manpages # Ensure that symlinks point to the (now-compressed) manpages
{ while IFS="!" read -r f DIR; do { while IFS="!" read -r f DIR; do
TARGET="$(readlink -n "$DIR/$f")" TARGET="$(readlink -n "$DIR/$f")"
TARGET="${TARGET##*/}"; TARGET="${TARGET%.gz}.gz"; TARGET="${TARGET##*/}"; TARGET="${TARGET%.gz}.gz";
rm -f "$DIR/$f" rm -f "$DIR/$f"
[ -e "$DIR/$TARGET" ] && ln -sf "$TARGET" "$DIR/${f%.gz}.gz" [ -e "$DIR/$TARGET" ] && ln -sf "$TARGET" "$DIR/${f%.gz}.gz"
done } < <(find pkg -type l -path "*/man/man*/*" -printf '%f!%h\n') done } < <(find pkg -type l -path "*/man/man*/*" -printf '%f!%h\n')
# Create the archive # Create the archive
BSDTAR+=" ${ZFLAG[$PKGMK_COMPRESSION_MODE]}" BSDTAR+=" ${ZFLAG[$PKGMK_COMPRESSION_MODE]}"
[ $UID = 0 ] || BSDTAR+=" --uid 0 --gid 0" [ $UID = 0 ] || BSDTAR+=" --uid 0 --gid 0"
[ -w "$pkg_dir$package" ] || rm -f "$pkg_dir$package" 2>/dev/null \ [ -w "$pkg_dir$package" ] || rm -f "$pkg_dir$package" 2>/dev/null \
|| { error "$pkg_dir$package not writable."; || { error "$pkg_dir$package not writable.";
cleanup_work; exit "$E_BUILD"; } cleanup_work; exit "$E_BUILD"; }
if (cd pkg; $BSDTAR -cf "$pkg_dir$package" -- *); then if (cd pkg; $BSDTAR -cf "$pkg_dir$package" -- *); then
info "Package creation successful." info "Package creation successful."
else else
error "Unable to create the compressed package $package." error "Unable to create the compressed package $package."
cleanup_work; exit "$E_BUILD" cleanup_work; exit "$E_BUILD"
fi fi
# Check the footprint of the built package, unless '-if' was given # Check the footprint of the built package, unless '-if' was given
[ "$PKGMK_IGNORE_FOOTPRINT" = "yes" ] || check_footprint [ "$PKGMK_IGNORE_FOOTPRINT" = "yes" ] || check_footprint
# Create signature if it doesn't already exist # Create signature if it doesn't already exist
[ -e "$PKGMK_ROOT/.signature" ] || make_signature new [ -e "$PKGMK_ROOT/.signature" ] || make_signature new
fi # Continue from here if extract and build were skipped fi # Continue from here if extract and build were skipped
@@ -199,14 +230,14 @@ fi # Continue from here if extract and build were skipped
find . -maxdepth 1 -mindepth 1 -type l -delete; cleanup_work find . -maxdepth 1 -mindepth 1 -type l -delete; cleanup_work
# Proceed to install/upgrade if requested. # Proceed to install/upgrade if requested.
if [ -n "$PKGMK_INSTALL_COMMAND" ]; then if [ "$PKGMK_INSTALL" != "no" ]; then
[ $UID = 0 ] && PKGMK_SU="" PKGMK_INSTALL_COMMAND="/usr/bin/pkgadd"
[ -z "$PKGMK_SU" ] || [ -x "$PKGMK_SU" ] || PKGMK_SU="/usr/bin/doas" [ "${CMD_FLAGS[pkgadd]}" ] && PKGMK_INSTALL_COMMAND="${CMD_FLAGS[pkgadd]}"
[ -z "$PKGMK_SU" ] || [ -x "$PKGMK_SU" ] || [ "${CMD_FLAGS[instroot]}" ] && PKGMK_INSTALL_COMMAND+=" -r ${CMD_FLAGS[instroot]}"
{ error "Cannot run pkgadd as a non-root user."; exit "$E_INSTALL"; } [ "$PKGMK_INSTALL" = "update" ] && PKGMK_INSTALL_COMMAND+=" -u"
$PKGMK_SU $PKGMK_INSTALL_COMMAND "$pkg_dir$package" \ $PKGMK_INSTALL_COMMAND "$pkg_dir$package" \
&& info "pkgadd $package succeeded." || \ && info "pkgadd $package succeeded." || \
{ error "pkgadd $package failed."; exit "$E_INSTALL"; } { error "pkgadd $package failed."; exit "$E_INSTALL"; }
fi fi
# Done! # Done!
@@ -231,7 +262,7 @@ PKGMK_GIT_COMMAND="/usr/bin/git"
PKGMK_CONF="/etc/pkgmk.conf"; PKGMK_SU="/usr/bin/sudo" PKGMK_CONF="/etc/pkgmk.conf"; PKGMK_SU="/usr/bin/sudo"
PKGMK_SOURCE_DIR="$PWD"; PKGMK_WORK_DIR="$PWD/work" PKGMK_SOURCE_DIR="$PWD"; PKGMK_WORK_DIR="$PWD/work"
PKGMK_PACKAGE_DIR="$PWD"; PKGMK_COMPRESSION_MODE="gz" PKGMK_PACKAGE_DIR="$PWD"; PKGMK_COMPRESSION_MODE="gz"
PKGMK_INSTALL_COMMAND=""; PKGMK_FORCE="no" PKGMK_INSTALL="no"; PKGMK_FORCE="no"
PKGMK_DOWNLOAD_ONLY="no"; PKGMK_KEEP_WORK="no" PKGMK_DOWNLOAD_ONLY="no"; PKGMK_KEEP_WORK="no"
PKGMK_EXTRACT_ONLY="no"; PKGMK_UPDATE_SIG="no" PKGMK_EXTRACT_ONLY="no"; PKGMK_UPDATE_SIG="no"
PKGMK_MTIME_ONLY="no"; PKGMK_IGNORE_SIG="no" PKGMK_MTIME_ONLY="no"; PKGMK_IGNORE_SIG="no"
@@ -241,344 +272,367 @@ PKGMK_IGNORE_NEW="no"; PKGMK_PRIVATEKEY=""
######################## subroutines ################################ ######################## subroutines ################################
parse_options() { parse_options() {
while [ "$1" ]; do local needHelp && declare -a needHelp
case "$1" in while [ "$1" ]; do
-h|--help) print_help; exit 0 ;; case "$1" in
-v|--version) -h|--help) needHelp+=("help") ;;
echo "$(basename "$PKGMK_COMMAND") (pkgutils) $PKGMK_VERSION"; exit 0 ;; -v|--version) needHelp+=("version") ;;
-uf|--update-footprint) PKGMK_UPDATE_FOOTPRINT="yes" ;; -uf|-us|-rs|-cs|-um|-do|-eo|-if|-is|-in|-ns|-f|-kw|-i|-u) CMD_FLAGS["${1#-}"]=1 ;;
-us|--update-signature) PKGMK_UPDATE_SIG="yes" ;; --update-signature) CMD_FLAGS["us"]=1 ;;
-rs|--refresh-signature) PKGMK_REFRESH_SIG="yes" ;; --refresh-signature) CMD_FLAGS["rs"]=1 ;;
-cs|--check-signature) PKGMK_CHECK_SIG="yes" ;; --check-signature) CMD_FLAGS["cs"]=1 ;;
-d|--download) ;; # deprecated flags but don't throw an error for them -d|--download) ;; # deprecated flags but don't throw an error for them
-um|--update-md5sum) warn "updating md5sums is obsolete, ignoring option '$1'." ;; -utd|--up-to-date) CMD_FLAGS["utd"]=1 ;;
-r|--recursive) { warn "option '$1' no longer supported. Use a wrapper script for recursive operation."; exit 1; } ;; --download-only) CMD_FLAGS["do"]=1 ;;
-c|--clean) { error "option '$1' no longer implemented in $(basename "$PKGMK_COMMAND"). Use prtwash instead."; exit 1; } ;; --extract-only) CMD_FLAGS["eo"]=1 ;;
-do|--download-only) PKGMK_DOWNLOAD_ONLY="yes" ;; --ignore-footprint) CMD_FLAGS["if"]=1 ;;
-eo|--extract-only) PKGMK_EXTRACT_ONLY="yes" ;; --ignore-new) CMD_FLAGS["in"]=1 ;;
-utd|--up-to-date) PKGMK_MTIME_ONLY="yes" ;; --ignore-signature) CMD_FLAGS["is"]=1 ;;
-if|--ignore-footprint) PKGMK_IGNORE_FOOTPRINT="yes" ;; --no-strip) ns_filter="-false" ;;
-in|--ignore-new) PKGMK_IGNORE_NEW="yes" ;; --force) CMD_FLAGS["f"]=1 ;;
-is|--ignore-signature) PKGMK_IGNORE_SIG="yes" ;; --keep-work) CMD_FLAGS["kw"]=1 ;;
-ns|--no-strip) ns_filter="-false" ;; --install) CMD_FLAGS["i"]=1 ;;
-f|--force) PKGMK_FORCE="yes" ;; --upgrade) CMD_FLAGS["u"]=1 ;;
-kw|--keep-work) PKGMK_KEEP_WORK="yes" ;; -pk|--public-key)
-i|--install) PKGMK_INSTALL_COMMAND="/usr/bin/pkgadd" ;; [ -r "$2" ] && KEY_FILES["pk"]="$2" || { echo "$(basename "$PKGMK_COMMAND"): option $1 requires a filename as argument";
-u|--upgrade) PKGMK_INSTALL_COMMAND="/usr/bin/pkgadd -u" ;; exit 1; }
-pk|--public-key) shift ;;
[ "$2" ] && PKGMK_PUBLICKEY="$2" || { echo "$(basename "$PKGMK_COMMAND"): option $1 requires an argument"; -sk|--secret-key)
exit 1; } [ -r "$2" ] && KEY_FILES["sk"]="$2" || { echo "$(basename "$PKGMK_COMMAND"): option $1 requires a filename as argument";
shift ;; exit 1; }
-sk|--secret-key) shift ;;
[ "$2" ] && PKGMK_PRIVATEKEY="$2" || { echo "$(basename "$PKGMK_COMMAND"): option $1 requires an argument"; -cf|--config-file)
exit 1; } [ -r "$2" ] && PKGMK_CONF="$2" || { echo "$(basename "$PKGMK_COMMAND"): option $1 requires a filename as argument";
shift ;; exit 1; }
-cf|--config-file) shift ;;
[ "$2" ] && PKGMK_CONF="$2" || { echo "$(basename "$PKGMK_COMMAND"): option $1 requires an argument"; --install-root)
exit 1; } [ -d "$2" ] && CMD_FLAGS["rootfs"]="$2" || { echo "$(basename "$PKGMK_COMMAND"): option $1 requires a mountpoint as argument";
shift ;; exit 1; }
*) shift ;;
echo "$(basename "$PKGMK_COMMAND"): invalid option $1"; exit 1 ;; --addcommand)
esac [ "$2" ] && CMD_FLAGS["pkgadd"]="$2" || { echo "$(basename "$PKGMK_COMMAND"): option $1 requires a path to pkgadd";
shift exit 1; }
done shift ;;
-um|--update-md5sum) warn "updating md5sums is obsolete, ignoring option '$1'." ;;
-r|--recursive) { warn "option '$1' no longer supported. Use a wrapper script for recursive operation."; exit 1; } ;;
-c|--clean) { error "option '$1' no longer implemented in $(basename "$PKGMK_COMMAND"). Use prtwash instead."; exit 1; } ;;
*)
echo "$(basename "$PKGMK_COMMAND"): invalid option $1"; exit 1 ;;
esac
shift
done
if [ ${#needHelp[@]} != 0 ]; then
for ((i=0; i<${#needHelp[@]}; i++)); do
case "${needHelp[i]}" in
help) print_help ;;
version) echo "$(basename "$PKGMK_COMMAND") (pkgutils) $PKGMK_VERSION" ;;
esac
done
exit 0
fi
} }
print_help() { print_help() {
echo "usage: $(basename "$PKGMK_COMMAND") [options]" echo "usage: $(basename "$PKGMK_COMMAND") [options]"
echo "options: (highest to lowest precedence)" echo "options: (highest to lowest precedence)"
echo " -h, --help print help and exit" echo " -h, --help print help and exit"
echo " -v, --version print version and exit" echo " -v, --version print version and exit"
echo " -cf, --config-file <file> use alternative configuration file" echo " -cf, --config-file <file> use alternative configuration file"
echo " -sk, --secret-key <file> use <file> to sign the port" echo " -sk, --secret-key <file> use <file> to sign the port"
echo " -pk, --public-key <file> check the port signature using public-key <file>" echo " -pk, --public-key <file> check the port signature using public-key <file>"
echo " -rs, --refresh-signature create new signature based on existing sha256 checksums" echo " -rs, --refresh-signature create new signature based on existing sha256 checksums"
echo " -utd, --up-to-date report whether the built package is up to date, then exit" echo " -utd, --up-to-date report whether the built package is up to date, then exit"
echo " -f, --force override timestamp check, build package or update footprint anyway" echo " -f, --force override timestamp check, build package or update footprint anyway"
echo " -uf, --update-footprint update footprint using result from last build" echo " -uf, --update-footprint update footprint using result from last build"
echo " -do, --download-only stop after downloading all the necessary source file(s)" echo " -do, --download-only stop after downloading all the necessary source file(s)"
echo " -cs, --check-signature stop after verifying the signatures of all downloaded sources" echo " -cs, --check-signature stop after verifying the signatures of all downloaded sources"
echo " -us, --update-signature update signature of Pkgfile and sources, do not build" echo " -us, --update-signature update signature of Pkgfile and sources, do not build"
echo " -is, --ignore-signature build package without checking signature" echo " -is, --ignore-signature build package without checking signature"
echo " -eo, --extract-only stop after downloading and extracting source file(s)" echo " -eo, --extract-only stop after downloading and extracting source file(s)"
echo " -ns, --no-strip do not strip executable binaries or libraries" echo " -ns, --no-strip do not strip executable binaries or libraries"
echo " -kw, --keep-work keep temporary working directory" echo " -kw, --keep-work keep temporary working directory"
echo " -if, --ignore-footprint build package without checking footprint" echo " -if, --ignore-footprint build package without checking footprint"
echo " -in, --ignore-new build package, ignoring new files in a footprint mismatch" echo " -in, --ignore-new build package, ignoring new files in a footprint mismatch"
echo " -i, --install build and install package" echo " -i, --install build and install package"
echo " -u, --upgrade build and upgrade package" echo " -u, --upgrade build and upgrade package"
echo " --install-root <dir> install package to the CRUX system mounted at <dir>"
echo " --addcommand <path> specify an alternative pkgadd command"
} }
validate_pkgfile() { # called from within PKGMK_ROOT validate_pkgfile() { # called from within PKGMK_ROOT
local errcode kv local errcode kv
[ -f Pkgfile ] || { error "no Pkgfile found. $PKGMK_ROOT is not a valid ports directory."; return "$E_PKGFILE"; } [ -f Pkgfile ] || { error "no Pkgfile found. $PKGMK_ROOT is not a valid ports directory."; return "$E_PKGFILE"; }
check_signature "pre-Pkgfile" | parse_signify_output [ "$PKGMK_UPDATE_SIG" = "yes" ] && return 0
errcode=$? check_signature "pre-Pkgfile" | parse_signify_output
[ "$errcode" = 0 ] || info "Use '-is' if you have independent confirmation of the port's integrity." errcode=$?
[ "$errcode" = 0 ] || [ "$PKGMK_IGNORE_SIG" = "yes" ] || return $E_SIGNATURE [ "$errcode" = 0 ] || info "Use '-is' if you have independent confirmation of the port's integrity."
[ "$errcode" = 0 ] || [ "$PKGMK_IGNORE_SIG" = "yes" ] || return $E_SIGNATURE
# the environment should not be affected by sourcing the Pkgfile both here # the environment should not be affected by sourcing the Pkgfile both here
# and in the later build, but to be safe we use a nested subshell # and in the later build, but to be safe we use a nested subshell
( . Pkgfile; kv=0; [ -n "$name" ] || kv+=1; [ -n "$version" ] || kv+=2; \ ( . Pkgfile; kv=0; [ -n "$name" ] || kv+=1; [ -n "$version" ] || kv+=2; \
[ -n "$release" ] || kv+=4; [ "$(type -t build)" = "function" ] || kv+=8; \ [ -n "$release" ] || kv+=4; [ "$(type -t build)" = "function" ] || kv+=8; \
echo $kv ) | check_reqvars echo $kv ) | check_reqvars
} }
check_reqvars () { check_reqvars () {
local checksum nullvars; local checksum nullvars;
read -r checksum read -r checksum
[ $((checksum & 1)) = 1 ] && nullvars=" 'name'" [ $((checksum & 1)) = 1 ] && nullvars=" 'name'"
[ $((checksum & 2)) = 2 ] && nullvars+=" 'version'" [ $((checksum & 2)) = 2 ] && nullvars+=" 'version'"
[ $((checksum & 4)) = 4 ] && nullvars+=" 'release'" [ $((checksum & 4)) = 4 ] && nullvars+=" 'release'"
[ $((checksum & 8)) = 8 ] && nullvars+=" 'build()'" [ $((checksum & 8)) = 8 ] && nullvars+=" 'build()'"
[ "$checksum" = 0 ] || { error "Pkgfile does not specify these required variables:"; \ [ "$checksum" = 0 ] || { error "Pkgfile does not specify these required variables:"; \
info "$nullvars"; return "$E_PKGFILE"; } info "$nullvars"; return "$E_PKGFILE"; }
} }
check_pkg_mtime() { # can be called even if some sources are missing check_pkg_mtime() { # can be called even if some sources are missing
local li=0; local utd=0; local msg="$package is not up to date." local li=0; local utd=0; local msg="$package is not up to date."
if [ -f "$pkg_dir$package" ]; then if [ -f "$pkg_dir$package" ]; then
utd=1 utd=1
while (( li < ${#_local_[@]} )) && [ "$utd" = 1 ]; do while (( li < ${#_local_[@]} )) && [ "$utd" = 1 ]; do
[ ! -e "${_local_[li]}" ] || \ [ ! -e "${_local_[li]}" ] || \
[ "$pkg_dir$package" -nt "$(realpath "${_local_[$li]}")" ] || utd=0 [ "$pkg_dir$package" -nt "$(realpath "${_local_[$li]}")" ] || utd=0
li=$(( li+1 )) li=$(( li+1 ))
done done
[ ! -e "$PKGMK_ROOT/Pkgfile" ] || [ "$pkg_dir$package" -nt "$PKGMK_ROOT/Pkgfile" ] || utd=0 [ ! -e "$PKGMK_ROOT/Pkgfile" ] || [ "$pkg_dir$package" -nt "$PKGMK_ROOT/Pkgfile" ] || utd=0
fi else
msg="$package is not yet built."
utd=0
fi
[ $utd = 0 ] || msg="$package is up to date." [ $utd = 0 ] || msg="$package is up to date."
[ "$PKGMK_MTIME_ONLY" = "yes" ] && info "$msg"; return $utd [ "$PKGMK_MTIME_ONLY" = "yes" ] && info "$msg"; return $utd
} }
get_basename() { get_basename() {
echo "${1##*/}" echo "${1##*/}"
} }
get_filename() { get_filename() {
local ABSOLUTE="" local ABSOLUTE=""
[ "$1" = "-a" ] && { ABSOLUTE=1; shift; } [ "$1" = "-a" ] && { ABSOLUTE=1; shift; }
if [[ $1 =~ ^(http|https|ftp|file)://.*/(.+) ]]; then if [[ $1 =~ ^(http|https|ftp|file)://.*/(.+) ]]; then
echo "$PKGMK_SOURCE_DIR/${BASH_REMATCH[2]}" echo "$PKGMK_SOURCE_DIR/${BASH_REMATCH[2]}"
else else
[ "$ABSOLUTE" ] && echo "$PKGMK_ROOT/$1" || echo "$1" [ "$ABSOLUTE" ] && echo "$PKGMK_ROOT/$1" || echo "$1"
fi fi
} }
fetch_url() { fetch_url() {
local u="$1"; local h="$2"; local finished=0; local giturl tag CLONE_ARGS local u="$1"; local h="$2"; local finished=0; local giturl tag CLONE_ARGS
local SAVE_AS REPO OCONTINUE OOUT; local m=0 local SAVE_AS REPO OCONTINUE OOUT; local m=0
if [[ "$u" =~ ^__git__ ]]; then if [[ "$u" =~ ^__git__ ]]; then
# git must be installed in order to obtain such a source # git must be installed in order to obtain such a source
[ -x "$PKGMK_GIT_COMMAND" ] || return 1; [ -x "$PKGMK_GIT_COMMAND" ] || return 1;
# #
giturl="${u#__git__}" giturl="${u#__git__}"
# Did the port maintainer specify a branch other than 'main'? # Did the port maintainer specify a branch other than 'main'?
tag="${giturl##*\#}" tag="${giturl##*\#}"
[ "$tag" = "$giturl" ] || { giturl="${giturl%\#*}"; [ "$tag" = "$giturl" ] || { giturl="${giturl%\#*}";
CLONE_ARGS=(-c advice.detachedHead=false --branch "$tag"); } CLONE_ARGS=(-c advice.detachedHead=false --branch "$tag"); }
# #
# Has this project been downloaded before? # Has this project been downloaded before?
if [ -d "$src_dir/$h.partial" ]; then if [ -d "$src_dir/$h.partial" ]; then
ln -s "$src_dir/$h.partial" "$h"; cd "$h" ln -s "$src_dir/$h.partial" "$h"; cd "$h"
"$PKGMK_GIT_COMMAND" pull "$giturl" "$tag" "$PKGMK_GIT_COMMAND" pull "$giturl" "$tag"
finished=$?; cd .. finished=$?; cd ..
else else
"$PKGMK_GIT_COMMAND" clone "$giturl" "${CLONE_ARGS[@]}" "$h.partial" "$PKGMK_GIT_COMMAND" clone "$giturl" "${CLONE_ARGS[@]}" "$h.partial"
finished=$?; [ "$src_dir" = "" ] || { mv "$h.partial" "$src_dir"; finished=$?; [ "$src_dir" = "" ] || { mv "$h.partial" "$src_dir";
ln -s "$src_dir/$h.partial" "$h"; } ln -s "$src_dir/$h.partial" "$h"; }
fi fi
return $finished; return $finished;
else else
case "$PKGMK_DOWNLOAD_PROG" in case "$PKGMK_DOWNLOAD_PROG" in
*wget) OCONTINUE="-c" *wget) OCONTINUE="-c"
OOUT="--compression=none --passive-ftp OOUT="--compression=none --passive-ftp
--no-directories --tries=3 --waitretry=3 --no-directories --tries=3 --waitretry=3
$PKGMK_WGET_OPTIONS -O" ;; $PKGMK_WGET_OPTIONS -O" ;;
*curl) OCONTINUE="-C -" *curl) OCONTINUE="-C -"
OOUT="-L -# --fail --ftp-pasv OOUT="-L -# --fail --ftp-pasv
--retry 3 --retry-delay 3 $PKGMK_CURL_OPTIONS -o" ;;
$PKGMK_CURL_OPTIONS -o" ;; *) SAVE_AS="false" ;;
*) SAVE_AS="false" ;; esac
esac #
# # start with the mirrors defined in pkgmk.conf, then go to the url found in the Pkgfile
# start with the mirrors defined in pkgmk.conf, then go to the url found in the Pkgfile while (( m <= ${#PKGMK_SOURCE_MIRRORS[@]} )) && [ $finished = 0 ] \
while (( m <= ${#PKGMK_SOURCE_MIRRORS[@]} )) && [ $finished = 0 ] \ && [ "$SAVE_AS" != "false" ]; do
&& [ "$SAVE_AS" != "false" ]; do [ "${PKGMK_SOURCE_MIRRORS[m]}" = "" ] && um=$u || \
[ "${PKGMK_SOURCE_MIRRORS[m]}" = "" ] && um=$u || \ { REPO=${PKGMK_SOURCE_MIRRORS[m]%/}; um=$REPO/${u##*/}; }
{ REPO=${PKGMK_SOURCE_MIRRORS[m]%/}; um=$REPO/${u##*/}; } m=$(( m+1 ))
m=$(( m+1 )) # interrupted downloads from a previous run should be put where wget or curl will find them
# interrupted downloads from a previous run should be put where wget or curl will find them [ -f "$src_dir/$h.partial" ] && { ln -s "$src_dir/$h.partial" . ;
[ -f "$src_dir/$h.partial" ] && { ln -s "$src_dir/$h.partial" . ; SAVE_AS="$PKGMK_DOWNLOAD_PROG $um $OCONTINUE $OOUT"; } \
SAVE_AS="$PKGMK_DOWNLOAD_PROG $um $OCONTINUE $OOUT"; } \ || SAVE_AS="$PKGMK_DOWNLOAD_PROG $um $OOUT"
|| SAVE_AS="$PKGMK_DOWNLOAD_PROG $um $OOUT" if $SAVE_AS "$h.partial"; then
if $SAVE_AS "$h.partial"; then finished=1
finished=1 [ "$src_dir" = "" ] || [ ! -w "$src_dir"/ ] || \
[ "$src_dir" = "" ] || [ ! -w "$src_dir"/ ] || \ { mv "$h.partial" "$src_dir/$h"; ln -sf "$src_dir/$h" . ; }
{ mv "$h.partial" "$src_dir/$h"; ln -sf "$src_dir/$h" . ; } else # an interrupted download should not have its efforts destroyed by cleanup_work()
else # an interrupted download should not have its efforts destroyed by cleanup_work() [ ! -s "$h.partial" ] || [ "$src_dir" = "" ] || \
[ ! -s "$h.partial" ] || [ "$src_dir" = "" ] || \ [ ! -w "$src_dir"/ ] || mv "$h.partial" "$src_dir"
[ ! -w "$src_dir"/ ] || mv "$h.partial" "$src_dir" fi
fi done
done fi
fi
} }
cat_footprint() { make_footprint() {
pkginfo --footprint "$pkg_dir$package" \ pkginfo --footprint "$pkg_dir$package" \
| sed "s|\tlib/modules/$(uname -r)/|\tlib/modules/<kernel-version>/|g" \ | sed -e "s|\tlib/modules/$(uname -r)/|\tlib/modules/<kernel-version>/|g" \
| sort -k 3 | sort -k 3
} }
check_footprint() { check_footprint() {
local CN CM local CN CM
local TRUTH="$PKGMK_ROOT/.footprint"; local diffs=0; local severity=error; local TRUTH="$PKGMK_ROOT/.footprint"; local diffs=0; local severity=error;
[ -f "$pkg_dir$package" ] || \ [ -f "$pkg_dir$package" ] || \
{ error "$package not found. Cannot check footprint."; exit "$E_FOOTPRINT"; } { error "$package not found. Cannot check footprint."; exit "$E_FOOTPRINT"; }
if [ -f "$TRUTH" ]; then if [ -f "$TRUTH" ]; then
diff -w -t -U 0 <(sort "$TRUTH") <(cat_footprint | sort) | \ diff -w -t -U 0 <(sed "s;x86_64-linux-\(gnu\|musl\);x86_64-linux-LIBC;" "$TRUTH" | sort) \
sed '/^@@/d; /^+++/d; /^---/d; s/^+/NEW /g; s/^-/MISSING /g' > ".footprint.diff" <(make_footprint | sed "s;x86_64-linux-\(gnu\|musl\);x86_64-linux-LIBC;" | sort) | \
if [ -s ".footprint.diff" ]; then sed '/^@@/d; /^+++/d; /^---/d; s/^+/NEW /g; s/^-/MISSING /g' > ".footprint.diff"
CN=$(grep -c ^NEW ".footprint.diff"); CM=$(grep -c ^MISSING ".footprint.diff") if [ -s ".footprint.diff" ]; then
[ "$PKGMK_IGNORE_MISSING" = "yes" ] || diffs=$CM CN=$(grep -c ^NEW ".footprint.diff"); CM=$(grep -c ^MISSING ".footprint.diff")
[ "$PKGMK_IGNORE_NEW" = "yes" ] || diffs=$(( diffs+CN )) [ "$PKGMK_IGNORE_NEW" = "yes" ] || diffs=$CN
(( diffs == 0 )) && severity=warning [ "$PKGMK_IGNORE_FOOTPRINT" = "yes" ] && diffs=0 || diffs=$(( diffs+CM ))
$severity "footprint mismatch found:"; cat ".footprint.diff" >&2 (( diffs == 0 )) && severity=warning
fi $severity "footprint mismatch found:"; cat ".footprint.diff" >&2
rm ".footprint.diff" fi
else rm ".footprint.diff"
warning "footprint not found, creating new."; cat_footprint > "$TRUTH" else
fi warning "footprint not found, creating new."; make_footprint > "$TRUTH"
(( diffs == 0 )) || { cleanup_work; exit $E_FOOTPRINT; } fi
(( diffs == 0 )) || { cleanup_work; exit $E_FOOTPRINT; }
} }
parse_signify_output() { # chomps the output of check_signature() parse_signify_output() { # chomps the output of check_signature()
local signout signerr local signout signerr
[ "$PKGMK_IGNORE_SIG" = "yes" ] && return 0 [ "$PKGMK_IGNORE_SIG" = "yes" ] && return 0
while read -r signout; do while read -r signout; do
case "$signout" in case "$signout" in
*"Pkgfile verification failed") *"Pkgfile verification failed")
signerr=-1; error "Signature missing! Unable to authenticate the Pkgfile." signerr=-1; error "Signature missing! Unable to authenticate the Pkgfile."
;; ;;
*"verification failed"*) *"verification failed"*)
signerr=1; error "Signature file corrupted or unreadable." signerr=1; error "Signature file corrupted or unreadable."
;; ;;
*"FAIL") *"FAIL")
signerr=2; error "Signature mismatch found:" signerr=2; error "Signature mismatch found:"
echo "$signout" | awk -F: '/FAIL/ {printf "MISMATCH %s\n", $1}' echo "$signout" | awk -F: '/FAIL/ {printf "MISMATCH %s\n", $1}'
;; ;;
*"OK") signerr=0 ;; *"OK") signerr=0 ;;
esac esac
done done
return $signerr return $signerr
} }
check_signature() { # called from $PKGMK_ROOT in the case "when"="pre-Pkgfile", check_signature() { # called from $PKGMK_ROOT in the case "when"="pre-Pkgfile",
# otherwise called from within $work. # otherwise called from within $work.
local reqfiles=(Pkgfile); local s=0; local when="$1"; local reqfiles=(Pkgfile); local s=0; local when="$1";
local SIGNIFY_ARGS=(-C -x "$PKGMK_ROOT/.signature") local SIGNIFY_ARGS=(-C -x "$PKGMK_ROOT/.signature")
[ "$PKGMK_PUBLICKEY" ] || PKGMK_PUBLICKEY="$(get_repo_key public)" [ "$PKGMK_PUBLICKEY" ] || PKGMK_PUBLICKEY="$(get_repo_key public)"
if [ -f "$PKGMK_ROOT/.signature" ]; then if [ -f "$PKGMK_ROOT/.signature" ]; then
[ "$when" = "pre-Pkgfile" ] || reqfiles=(.footprint) [ "$when" = "pre-Pkgfile" ] || reqfiles=(.footprint)
while [ "$when" = "pre-build" ] && (( s < ${#_local_[@]} )); do while [ "$when" = "pre-build" ] && (( s < ${#_local_[@]} )); do
[[ "${source[s]}" =~ ^__git__ ]] || reqfiles+=("${_local_[s]}") [[ "${source[s]}" =~ ^__git__ ]] || reqfiles+=("${_local_[s]}")
s=$(( s+1 )) s=$(( s+1 ))
done done
for FILE in "${reqfiles[@]}"; do for FILE in "${reqfiles[@]}"; do
[ -e "$FILE" ] || ln -sf "$PKGMK_ROOT/$FILE" . [ -e "$FILE" ] || ln -sf "$PKGMK_ROOT/$FILE" .
done done
[ -r "$PKGMK_PUBLICKEY" ] && SIGNIFY_ARGS+=(-p "$PKGMK_PUBLICKEY") [ -r "$PKGMK_PUBLICKEY" ] && SIGNIFY_ARGS+=(-p "$PKGMK_PUBLICKEY")
/usr/bin/signify "${SIGNIFY_ARGS[@]}" "${reqfiles[@]}" 2>&1 /usr/bin/signify "${SIGNIFY_ARGS[@]}" "${reqfiles[@]}" 2>&1
else else
[ "$when" = "pre-Pkgfile" ] && echo "Pkgfile verification failed" [ "$when" = "pre-Pkgfile" ] && echo "Pkgfile verification failed"
[ "$when" != "pre-Pkgfile" ] && echo "signature verification failed" [ "$when" != "pre-Pkgfile" ] && echo "signature verification failed"
fi fi
} }
get_repo_key() { get_repo_key() {
local typ="${1:0:3}"; local REPO; local typ="${1:0:3}"; local REPO;
REPO=$(dirname "$PKGMK_ROOT"); REPO=$(basename -s .git "$REPO"); REPO=$(dirname "$PKGMK_ROOT"); REPO=$(basename -s .git "$REPO");
ls "/etc/ports/$REPO.$typ" 2>/dev/null \ ls "/etc/ports/$REPO.$typ" 2>/dev/null \
|| ls "$HOME/.ssh/$REPO.$typ" 2>/dev/null || ls "$HOME/.ssh/$REPO.$typ" 2>/dev/null
} }
make_signature() { make_signature() {
local ordered si pub local ordered si pub
[ ! -e "$PKGMK_ROOT/.signature" ] || [ -w "$PKGMK_ROOT/.signature" ] \ [ ! -e "$PKGMK_ROOT/.signature" ] || [ -w "$PKGMK_ROOT/.signature" ] \
|| { error ".signature not writable."; return $E_DIRPERM; } || { error ".signature not writable."; return $E_DIRPERM; }
[ -n "$PKGMK_PRIVATEKEY" ] || PKGMK_PRIVATEKEY="$(get_repo_key secret)" [ -n "$PKGMK_PRIVATEKEY" ] || PKGMK_PRIVATEKEY="$(get_repo_key secret)"
if [ -n "$PKGMK_PRIVATEKEY" ]; then if [ -n "$PKGMK_PRIVATEKEY" ]; then
pub="/etc/ports/$(basename -s .sec "$PKGMK_PRIVATEKEY").pub" pub="/etc/ports/$(basename -s .sec "$PKGMK_PRIVATEKEY").pub"
elif [[ "$PKGMK_UPDATE_SIG$PKGMK_REFRESH_SIG" =~ yes ]]; then elif [[ "$PKGMK_UPDATE_SIG$PKGMK_REFRESH_SIG" =~ yes ]]; then
error "No suitable secret key found. Specify one explicitly with '-sk'." error "No suitable secret key found. Specify one explicitly with '-sk'."
return $E_SIGNATURE return $E_SIGNATURE
else else
return 0 return 0
fi fi
# create a new .signature, or refresh an existing manifest? # create a new .signature, or refresh an existing manifest?
case "$1" in case "$1" in
new) new)
[ -e "$PKGMK_ROOT/.footprint" ] || \ [ -e "$PKGMK_ROOT/.footprint" ] || \
warning "Footprint not found, signature will be incomplete." warning "Footprint not found, signature will be incomplete."
for f in "$PKGMK_ROOT/Pkgfile" "$PKGMK_ROOT/.footprint"; do for f in "$PKGMK_ROOT/Pkgfile" "$PKGMK_ROOT/.footprint"; do
[ -e "$f" ] && ordered+=( "$f" ) [ -e "$f" ] && ordered+=( "$f" )
done done
for ((si=0; si < ${#source[@]}; si++)); do for ((si=0; si < ${#source[@]}; si++)); do
# ignore git directories when writing the signature # ignore git directories when writing the signature
[[ "${source[si]}" =~ ^__git__ ]] || ordered+=("${_local_[si]}") [[ "${source[si]}" =~ ^__git__ ]] || ordered+=("${_local_[si]}")
done done
sha256sum --tag "${ordered[@]}" \ sha256sum --tag "${ordered[@]}" \
| sed 's|^SHA256 (.*/\(.*\))\(.* = .*\)|SHA256 (\1)\2|' \ | sed 's|^SHA256 (.*/\(.*\))\(.* = .*\)|SHA256 (\1)\2|' \
| /usr/bin/signify -S -e -x - -q -s "$PKGMK_PRIVATEKEY" -m - \ | /usr/bin/signify -S -e -x - -q -s "$PKGMK_PRIVATEKEY" -m - \
| sed "s|${PKGMK_PRIVATEKEY%.sec}.pub|$pub|" \ | sed "s|${PKGMK_PRIVATEKEY%.sec}.pub|$pub|" \
> "$PKGMK_ROOT/.signature" > "$PKGMK_ROOT/.signature"
;; ;;
refresh) refresh)
[ -e ".signature" ] || \ [ -e ".signature" ] || \
{ error "missing .signature, cannot refresh."; return $E_SIGNATURE; } { error "missing .signature, cannot refresh."; return $E_SIGNATURE; }
if tail -n +3 ".signature" | /usr/bin/signify -S -e -x - -q \ if tail -n +3 ".signature" | /usr/bin/signify -S -e -x - -q \
-s "$PKGMK_PRIVATEKEY" -m - > .signature.tmp; then -s "$PKGMK_PRIVATEKEY" -m - > .signature.tmp; then
mv .signature.tmp .signature mv .signature.tmp .signature
else else
rm .signature.tmp; return $E_SIGNATURE rm .signature.tmp; return $E_SIGNATURE
fi fi
;; ;;
esac esac
[ "$1" = "new" ] && info "signature created." || info "signature updated." [ "$1" = "new" ] && info "signature created." || info "signature updated."
} }
interrupted() { interrupted() {
error "Interrupted."; cleanup_work; exit 1 error "Interrupted."; cleanup_work; exit 1
} }
cleanup_work() { cleanup_work() {
[ "$PKGMK_KEEP_WORK" = "yes" ] || { cd "$PKGMK_ROOT"; rm -rf "$work"; } [ "$PKGMK_KEEP_WORK" = "yes" ] || { cd "$PKGMK_ROOT"; rm -rf "$work"; }
} }
info() { info() {
echo "=======> $1" echo "=======> $1"
} }
warning() { warning() {
info "WARNING: $1" >&2 info "WARNING: $1" >&2
} }
error() { error() {
info "ERROR: $1" >&2 info "ERROR: $1" >&2
} }
######################## end of subroutines ########################### ######################## end of subroutines ###########################
## Now ensure that they cannot be overwritten when sourcing Pkgfile ## ## Now ensure that some of them cannot be overwritten when sourcing Pkgfile ##
readonly -f main info warning error print_help parse_options validate_pkgfile \ readonly -f main info warning error print_help parse_options validate_pkgfile \
check_reqvars check_pkg_mtime fetch_url cat_footprint check_footprint \ check_reqvars check_pkg_mtime fetch_url get_repo_key \
make_signature get_repo_key check_signature parse_signify_output cleanup_work check_signature parse_signify_output cleanup_work
trap "interrupted" SIGHUP SIGINT SIGQUIT SIGTERM trap "interrupted" SIGHUP SIGINT SIGQUIT SIGTERM
export LC_ALL=C.UTF-8 export LC_ALL=C.UTF-8

View File

@@ -21,7 +21,7 @@ my %osearch = ( cache=>0, regex=>0, path=>0, exact=>0, verbose=>0 );
my %odepends = ( inject=>1, soft=>0, tree=>0, recursive=>0, all=>0 ); my %odepends = ( inject=>1, soft=>0, tree=>0, recursive=>0, all=>0 );
my %opkg = ( margs=>"", aargs=>"", rargs=>"", run_scripts=>"no", my %opkg = ( margs=>"", aargs=>"", rargs=>"", run_scripts=>"no",
pre_install=>"no", post_install=>"no", scriptcommand=>"/bin/sh", pre_install=>"no", post_install=>"no", scriptcommand=>"/bin/sh",
removecommand=>"/usr/bin/pkgrm", addcommand=>"/usr/bin/pkgadd", removecommand=>"/usr/bin/pkgrm", addcommand=>"",
makecommand=>"/usr/bin/pkgmk", nolock=>0, test=>"no", group=>"no" ); makecommand=>"/usr/bin/pkgmk", nolock=>0, test=>"no", group=>"no" );
my %olog = ( write => "disabled", mode => "overwrite", rm_on_success => "yes", my %olog = ( write => "disabled", mode => "overwrite", rm_on_success => "yes",
rm_on_uninst => "no", file => "/var/log/pkgbuild/%n.log" ); rm_on_uninst => "no", file => "/var/log/pkgbuild/%n.log" );
@@ -128,7 +128,7 @@ if (($action =~ /^(listinst|listorphans|dependent)/)
print "\n" if ($action eq "quickdiff"); print "\n" if ($action eq "quickdiff");
exit $ind; exit $ind;
} elsif ($action =~ /^(depends|quickdep)$/) { } elsif ($action =~ /^(depends|quickdep)$/) {
print "-- dependency list ([i] = installed)\n" if ($action =~ /^dep/); print "-- dependency list ([i] = installed, [u] = updateable)\n" if ($action =~ /^dep/);
my $strf="%3s %s\n"; my $dep; my $missing=0; my $strf="%3s %s\n"; my $dep; my $missing=0;
foreach $dep (@results) { foreach $dep (@results) {
if ($dep =~ /MISSING/) { if ($dep =~ /MISSING/) {
@@ -137,6 +137,7 @@ if (($action =~ /^(listinst|listorphans|dependent)/)
next if (! $dep); next if (! $dep);
if ($action ne "quickdep") { if ($action ne "quickdep") {
$ind = (grep { $_ eq $dep } keys %V_INST) ? "[i]" : "[ ]"; $ind = (grep { $_ eq $dep } keys %V_INST) ? "[i]" : "[ ]";
($ind eq "[ ]") or (! $V_REPO{$dep}) or ($V_REPO{$dep} eq $V_INST{$dep}) or $ind = "[u]";
$dep .= " $V_REPO{$dep}" if (($osearch{verbose}==1) and ($V_REPO{$dep})); $dep .= " $V_REPO{$dep}" if (($osearch{verbose}==1) and ($V_REPO{$dep}));
$dep .= " $V_REPO{$dep}\n$DESC{$dep}" if (($osearch{verbose}>1) and ($V_REPO{$dep}) and ($DESC{$dep})); $dep .= " $V_REPO{$dep}\n$DESC{$dep}" if (($osearch{verbose}>1) and ($V_REPO{$dep}) and ($DESC{$dep}));
} }
@@ -258,6 +259,9 @@ if (($#query < 0) and
($action =~ /^(install|update|depinst|remove)$/)) { ($action =~ /^(install|update|depinst|remove)$/)) {
print "$1 requires at least one argument.\n"; exit 1; print "$1 requires at least one argument.\n"; exit 1;
} }
if (($osearch{verbose} > 0) and ($action eq "dup")) {
push @query, "%n:\n%p1/%n %v1 > %p2/%n %v2";
}
return $action, @query; return $action, @query;
} }
@@ -613,7 +617,7 @@ sub list_ports {
} }
if ($subset eq "orphans") { if ($subset eq "orphans") {
my %not_orphans = map { $_ => 0 } @searchspace; my %not_orphans = map { $_ => 0 } keys %V_INST;
foreach my $port (@searchspace) { foreach my $port (@searchspace) {
map { $not_orphans{$_} = 1 } split(/[ ,]+/, $DEPENDS{$port}); map { $not_orphans{$_} = 1 } split(/[ ,]+/, $DEPENDS{$port});
if (($odepends{soft} == 1) and ($SOFTDEPS{$port})) { if (($odepends{soft} == 1) and ($SOFTDEPS{$port})) {
@@ -623,7 +627,7 @@ sub list_ports {
foreach my $al (keys %ALIASES) { foreach my $al (keys %ALIASES) {
$not_orphans{$al} = 1 if (($not_orphans{$ALIASES{$al}}) and ($not_orphans{$ALIASES{$al}} == 1)); $not_orphans{$al} = 1 if (($not_orphans{$ALIASES{$al}}) and ($not_orphans{$ALIASES{$al}} == 1));
} }
@found = grep { $not_orphans{$_} == 0 } keys %V_INST; @found = grep { ($not_orphans{$_} == 0) } keys %V_INST;
} elsif (($subset eq "dependent") and ($odepends{recursive}==0)) { } elsif (($subset eq "dependent") and ($odepends{recursive}==0)) {
@found = grep { " $DEPENDS{$_} " =~ / $sseed / } @searchspace; @found = grep { " $DEPENDS{$_} " =~ / $sseed / } @searchspace;
if ($odepends{soft}==1) { if ($odepends{soft}==1) {
@@ -811,7 +815,7 @@ sub up_inst { # returns scalar references to six arrays
my @requested=@_; my @sortedList; my @targets; my %pdirs; my %builtpkg; my @requested=@_; my @sortedList; my @targets; my %pdirs; my %builtpkg;
my %mkcmd; my %addcmd; my $rs_cmd; my %logfile; my %pvars; my $status; my %mkcmd; my %addcmd; my $rs_cmd; my %logfile; my %pvars; my $status;
my %ok; my @missing; my %not_ok; my %ok_pre; my %ok_post; my @ok_readme=(); my %ok; my @missing; my %not_ok; my %ok_pre; my %ok_post; my @ok_readme=();
my $ord=0; my $PKGMK=$opkg{makecommand}; my $PKGADD=$opkg{addcommand}; my $ord=0; my $PKGMK=$opkg{makecommand}; my $PKGADD;
my $SH=$opkg{scriptcommand}; my $SH=$opkg{scriptcommand};
# resolve dependencies unless --nodeps was given, # resolve dependencies unless --nodeps was given,
@@ -835,24 +839,24 @@ sub up_inst { # returns scalar references to six arrays
@targets = grep {(! $LOCKED{$_})} @targets if ($opkg{nolock}==0); @targets = grep {(! $LOCKED{$_})} @targets if ($opkg{nolock}==0);
# first determine the directories from which pkgmk must be called, # first determine the directories from which pkgmk must be called,
# the package that will appear after a successful build,
# and where to save the build log. # and where to save the build log.
my ($COMPRESSION, $PKG_DIR) = parse_pkgmk_conf();
foreach my $t (@targets) { foreach my $t (@targets) {
$opkg{$t} = $opkg{margs}; $pvars{'%n'}=$t; $opkg{$t} = $opkg{margs}; $pvars{'%n'}=$t;
$opkg{$t} =~ s/-f// unless (grep /^$t$/, @requested); $opkg{$t} =~ s/-f// unless (grep /^$t$/, @requested);
$pvars{'%p'} = find_port_by_name($t,1,1,0); $pdirs{$t} = $pvars{'%p'}; $pdirs{$t} = find_port_by_name($t,1,1,0);
$pvars{'%p'} = $pdirs{$t};
$pvars{'%v'} = $1 if ( $V_REPO{$t} =~ m/(.+)-[0-9]+$/ ); $pvars{'%v'} = $1 if ( $V_REPO{$t} =~ m/(.+)-[0-9]+$/ );
$pvars{'%r'} = $1 if ( $V_REPO{$t} =~ m/-([0-9]+)$/ ); $pvars{'%r'} = $1 if ( $V_REPO{$t} =~ m/-([0-9]+)$/ );
$builtpkg{$t} = ($PKG_DIR ne "") ? "$PKG_DIR/$t#$pvars{'%v'}-$pvars{'%r'}.pkg.tar.$COMPRESSION" : "$pvars{'%p'}/$t#$pvars{'%v'}-$pvars{'%r'}.pkg.tar.$COMPRESSION";
$builtpkg{$t} =~ s/uuiName/$t/g;
$builtpkg{$t} =~ s/uuiVer/$pvars{'%v'}/g;
$builtpkg{$t} =~ s/uuiRel/$pvars{'%r'}/g;
$mkcmd{$t} = "$PKGMK -d $opkg{$t}"; $mkcmd{$t} = "$PKGMK -d $opkg{$t}";
$mkcmd{$t} .= ($V_INST{$t}) ? " -u" : " -i";
if (($altroot ne "") and ($opkg{aargs} !~ m/(-r|--root)/)) { if (($altroot ne "") and ($opkg{aargs} !~ m/(-r|--root)/)) {
$opkg{aargs} .= " -r $altroot"; $mkcmd{$t} .= " --install-root \"$altroot\"";
} }
$addcmd{$t} = "$PKGADD -u $opkg{aargs} $builtpkg{$t}"; (! $opkg{addcommand}) or $PKGADD=$opkg{addcommand};
if ($opkg{aargs}) {
($PKGADD) ? $PKGADD .= " $opkg{aargs}" : $PKGADD = "/usr/bin/pkgadd $opkg{aargs}";
}
$mkcmd{$t} .= " --addcommand \"$PKGADD\"" if ($PKGADD);
if ($olog{write} eq "enabled") { if ($olog{write} eq "enabled") {
$logfile{$t} = $olog{file}; $logfile{$t} = $olog{file};
$logfile{$t} =~ s/(%n|%v|%r|%p)/$pvars{$1}/g; $logfile{$t} =~ s/(%n|%v|%r|%p)/$pvars{$1}/g;
@@ -866,10 +870,6 @@ sub up_inst { # returns scalar references to six arrays
# build each package # build each package
BUILDLOG: foreach my $t (@targets) { BUILDLOG: foreach my $t (@targets) {
if ( (-f $builtpkg{$t}) and ($opkg{$t} !~ /-f/) and
((-M $builtpkg{$t}) > (-M "$pdirs{$t}/Pkgfile")) ) {
$mkcmd{$t} = "echo \"skipped build (package already exists)\"";
}
if ($opkg{test} eq "yes") { if ($opkg{test} eq "yes") {
($mkcmd{$t} !~ /skipped/) or next BUILDLOG; ($mkcmd{$t} !~ /skipped/) or next BUILDLOG;
print("$t"); print("$t");
@@ -897,16 +897,9 @@ sub up_inst { # returns scalar references to six arrays
($status) ? $ok{$t} = $ord : $not_ok{$t} = $ord; ($status) ? $ok{$t} = $ord : $not_ok{$t} = $ord;
} }
if ($ok{$t}) { if ($ok{$t}) {
$addcmd{$t} =~ s/ -u / / if (! $V_INST{$t}); push (@ok_readme, $t) if (-f "README");
if (system("$addcmd{$t}")==0) {
push (@ok_readme, $t) if (-f "README");
} else {
$not_ok{$t} = $ord; delete $ok{$t};
}
unlink($logfile{$t}) unlink($logfile{$t})
if ( ($logfile{$t}) and ($olog{rm_on_success} eq "yes") ); if ( ($logfile{$t}) and ($olog{rm_on_success} eq "yes") );
} elsif ( ($not_ok{$t}) and (-f "$builtpkg{$t}") ) {
rename("$builtpkg{$t}","$builtpkg{$t}.CHECKME");
} }
if (($ok{$t}) and ("$opkg{run_scripts} $opkg{post_install}" =~ /yes/) and (-f "$altroot$pdirs{$t}/post-install")) { if (($ok{$t}) and ("$opkg{run_scripts} $opkg{post_install}" =~ /yes/) and (-f "$altroot$pdirs{$t}/post-install")) {
$rs_cmd="$SH post-install"; $rs_cmd="$SH post-install";
@@ -928,22 +921,6 @@ sub up_inst { # returns scalar references to six arrays
return \@ok, \%ok_pre, \%ok_post, \@ok_readme, \@not_ok, \@missing; return \@ok, \%ok_pre, \%ok_post, \@ok_readme, \@not_ok, \@missing;
} }
sub parse_pkgmk_conf {
my @pkgmkVars;
my $heredoc = "bash -c 'name=uuiName; version=uuiVer;";
$heredoc .= "release=uuiRel; source /etc/pkgmk.conf; ";
$heredoc .= "printf \"%s:%s\" \"\$PKGMK_PACKAGE_DIR\" ";
$heredoc .= "\$PKGMK_COMPRESSION_MODE;'";
open (CF,"-|",$heredoc) or return;
while (<CF>) { @pkgmkVars = split /:/;
} close (CF);
my $COMPRESSION = ($pkgmkVars[1]) ? $pkgmkVars[1] : "gz";
my $PKGDIR = ($pkgmkVars[0]) ? $pkgmkVars[0] : "";
return $COMPRESSION, $PKGDIR;
}
sub port_ls { sub port_ls {
my $port=shift; my $pp=find_port_by_name($port,1,1,0); my $port=shift; my $pp=find_port_by_name($port,1,1,0);
return if (! defined $pp); return if (! defined $pp);