#!/bin/bash # # pkgmeek - a drop-in replacement for pkgmk # based on upkgmk (c) 2018 therealfun # and pkgmk (c) 2004--2022 Per Liden et al. # Distributed under the same license, # https://crux.nu/gitweb/?p=tools/pkgutils.git;a=blob_plain;f=COPYING;hb=HEAD main() { ######################## main routine ################################ local 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" parse_options "$@" # Exit early if refreshing an existing sha256 manifest was requested [ "$PKGMK_REFRESH_SIG" = "yes" ] && { make_signature refresh && info "signature refreshed."; exit $?; } # # Read the Pkgfile to determine what to do next. But first ensure that # it came from a trusted source (FS#1851) # [ "$PKGMK_UPDATE_SIG" = "yes" ] && [ -e ./Pkgfile ] || validate_pkgfile || exit $E_PKGFILE [ -f .32bit ] && PKGMK_ARCH=32 || PKGMK_ARCH=64 . "Pkgfile"; . "$PKGMK_CONF" # respect the settings for centralized source and package directories ... [ -v pkg_dir ] || pkg_dir="$PKGMK_PACKAGE_DIR"/ [ -w "$pkg_dir" ] || pkg_dir="$(pwd)"/ [ -v src_dir ] || src_dir="$PKGMK_SOURCE_DIR" [ -w "$src_dir" ] || src_dir= # ... and for WORK_DIR in case the user wants to build in RAM [ -v PKGMK_WORK_DIR ] && work="$PKGMK_WORK_DIR" # Let the main routine and subsequent subroutines know what filenames # they should expect to see on disk. package="${name}#${version}-${release}.pkg.tar.${PKGMK_COMPRESSION_MODE}" declare -a _local_ for (( s=0; s<${#source[@]}; s++ )); do case "${source[s]}" in http://*|https://*|ftp://*|__git__*) _local_[s]="${source[s]##*/}" # strip the leading path # and for git sources, extract the project name: [[ "${source[s]}" =~ ^__git__ ]] && { _local_[s]="${_local_[s]%%\#*}"; _local_[s]="${_local_[s]%.git}"; } ;; *) _local_[s]="${source[s]}" ;; esac [ -z "${renames[s]}" ] || [ "${renames[s]}" = "SKIP" ] || \ _local_[s]="${renames[s]}" done # Example: source = ( __git__https://gitlab.com/demo-user/cool-project.git#0.4.9 # https://dev.big-corp.com/src/needed-library.tgz # random.patch ) # _local_ = ( cool-project # needed-library.zgt <-- renamed to prevent automatic unpacking # random.patch ) # The effective user should at least have write permissions on $PWD [ -w "$(dirname "$work")" ] || work="$(pwd)"/work rm -rf "$work"; mkdir -p "$work"/{src,pkg} && cd "$work" # 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" ] \ || { for (( u=0; u<${#_local_[@]}; u++ )); do here="${_local_[u]}"; url="${source[u]}"; # at least one of the following commands should put a file of the # appropriate name in the current directory [ -e "$here" ] || [ "$src_dir" = "" ] || [ ! -e "$src_dir/$here" ] || ln -sf "$src_dir/$here" . ; [ -e "$here" ] || [ ! -e "$PKGMK_ROOT/$here" ] || ln -sf "$PKGMK_ROOT/$here" . ; [ -e "$here" ] || fetch_url "$url" "$here"; # <-- should also be able to resume partial downloads [ -e "$here" ] || { error "failed to download $here. Check connection and try again."; errDL+=1; } done ; } [ $errDL = 0 ] && { [[ "$PKGMK_MTIME_ONLY $PKGMK_UPDATE_FOOTPRINT" =~ yes ]] || \ info "Successfully obtained all needed source files."; } || exit $E_DOWNLOAD # 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 [ "$PKGMK_DOWNLOAD_ONLY" = "no" ] || [ "$PKGMK_UPDATE_FOOTPRINT" = "yes" ] \ || [ "$PKGMK_UPDATE_SIG" = "yes" ] || [ "$PKGMK_EXTRACT_ONLY" = "yes" ] \ || [ "$PKGMK_MTIME_ONLY" = "yes" ] || { cleanup_work; exit 0; } # If '-utd' was requested, check the modification times and then exit. check_pkg_mtime; pkg_utd=$? [ "$PKGMK_MTIME_ONLY" = "no" ] || { cleanup_work; exit $pkg_utd; } # Take into account all the actions that can be done without extracting # the downloaded sources [ "$pkg_utd" = 0 ] || [ "$PKGMK_FORCE" = "yes" ] || [ "$PKGMK_EXTRACT_ONLY" = "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; } # Silence the progress report if the user never intended to proceed with unpacking [ "$pkg_utd" = 1 ] || [[ "$PKGMK_CHECK_SIG $PKGMK_UPDATE_SIG" =~ yes ]] || \ [[ "$PKGMK_IGNORE_SIG $PKGMK_UPDATE_FOOTPRINT" =~ yes ]] \ || echo "Checking signatures before unpacking..." # The option -uf is meant to be used *after* a previous invocation of pkgmeek has # alerted the user to a footprint mismatch. if [ "$PKGMK_UPDATE_FOOTPRINT" = "yes" ]; then [ -f "$pkg_dir$package" ] || \ { error "unable to update footprint. File '$package' not found."; cleanup_work; exit "$E_FOOTPRINT"; } [ "$pkg_utd" = 1 ] || [ "$PKGMK_FORCE" = "yes" ] || \ { error "outdated package. Use '-f' to force the footprint update."; cleanup_work; exit "$E_FOOTPRINT"; } cat_footprint > "$PKGMK_ROOT/.footprint" && { info "footprint created."; exit 0; } \ || { error "Failed to write the footprint."; cleanup_work; exit "$E_DIRPERM"; } fi # 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. [ "$PKGMK_UPDATE_SIG" = "yes" ] && { make_signature new && info "signature created."; cleanup_work; exit $?; } # All the sources should be here by now, let's verify that we can trust them. 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; case $? in 0) info "Sources successfully authenticated." ;; 1) error "Signature file missing or corrupted." echo "$cs_fail_msg" ; cleanup_work; exit $E_SIGNATURE ;; 2) error "Failed to authenticate remote sources using signify." echo "$cs_fail_msg" ; cleanup_work; exit $E_SIGNATURE ;; esac; } [ "$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 [ "$(type -t unpack_source)" = "function" ]; then SRC=$PWD/src unpack_source || errUZ+=1 else for (( u=0; u<${#_local_[@]}; u++ )) ; do here="${_local_[u]}" case "$here" in *.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 ;; *) cp -r -L "$here" src/ ;; esac done fi [ $errUZ = 0 ] && info "Sources successfully unpacked." || \ { error "Failed to unpack all sources."; cleanup_work; exit "$E_UNPACK"; } [ "$PKGMK_EXTRACT_ONLY" = "no" ] || exit 0 # The actual build step! (use fakeroot when building daemon ports as an ordinary user, # otherwise the owner and group might not be correct) (SRC=$(pwd)/src; PKG=$(pwd)/pkg; cd src; set -e -x; build) [ $? = 0 ] && echo "Build successful. Moving on to compression." \ || { error "Unsuccessful build!"; cleanup_work; exit "$E_BUILD"; } # Strip binaries [ -n "$ns_filter" ] || [ ! -f "$PKGMK_ROOT/.nostrip" ] \ || ns_filter="| grep -v -f $PKGMK_ROOT/.nostrip" { while read -r f; do case $(file -b "$f") in *ELF*executable*not\ stripped*) strip --strip-all "$f" ;; *ELF*shared\ object*not\ stripped*) strip --strip-unneeded "$f" ;; current\ ar\ archive) strip --strip-debug "$f" ;; esac done } < <(eval find pkg -type f $ns_filter) # Compress anything under /man that does not appear to be gzipped find pkg -type f -path "*/man/man*/*" \! -iname '*.gz' \ -exec gzip -9 '{}' + # Ensure that symlinks point to the (now-compressed) manpages { while IFS="!" read -r f DIR; do TARGET="$(readlink -n "$DIR/$f")" TARGET="${TARGET##*/}"; TARGET="${TARGET%.gz}.gz"; rm -f "$DIR/$f" [ -e "$DIR/$TARGET" ] && ln -sf "$TARGET" "$DIR/${f%.gz}.gz" done } < <(find pkg -type l -path "*/man/man*/*" -printf '%f!%h\n') # Create the archive [ $UID = 0 ] || BSDTAR+=" --uid 0 --gid 0" if (cd pkg; $BSDTAR -cf "$pkg_dir$package" -- *); then info "Package creation successful." else error "Unable to create the compressed package $package." cleanup_work; exit "$E_BUILD" fi # Check the footprint of the built package, unless '-if' was given [ "$PKGMK_IGNORE_FOOTPRINT" = "yes" ] || check_footprint fi # Continue from here if extract and build were skipped # Clean up the work directory. find . -maxdepth 1 -mindepth 1 -type l -delete; cleanup_work # Proceed to install/upgrade if requested. if [ -n "$PKGMK_INSTALL_COMMAND" ]; then [ $UID = 0 ] && PKGMK_SU="" [ -z "$PKGMK_SU" ] || [ -x "$PKGMK_SU" ] || PKGMK_SU="/usr/bin/doas" [ -z "$PKGMK_SU" ] || [ -x "$PKGMK_SU" ] || { error "Cannot run pkgadd as a non-root user."; exit "$E_INSTALL"; } $PKGMK_SU $PKGMK_INSTALL_COMMAND "$pkg_dir$package" \ && info "pkgadd $package succeeded." || \ { error "pkgadd $package failed."; exit "$E_INSTALL"; } fi # Done! } ####################### error codes ############################## E_PKGFILE=2 # invalid Pkgfile E_DIRPERM=3 # (source/build) directory missing or missing read/write permission E_DOWNLOAD=4 # error during download E_UNPACK=5 # error during unpacking of source file(s) E_FOOTPRINT=6 # footprint mismatch E_BUILD=7 # error while running 'build()' E_INSTALL=8 # error while installing the package via 'pkgadd' E_SIGNATURE=9 # error verifying the signature ######################## settings ################################ readonly PKGMK_VERSION="#VERSION#" readonly PKGMK_COMMAND="$0" readonly PKGMK_ROOT="$PWD" PKGMK_DOWNLOAD_PROG="/usr/bin/curl" PKGMK_GIT_COMMAND="/usr/bin/git" PKGMK_CONF="/etc/pkgmk.conf"; PKGMK_SU="/usr/bin/sudo" PKGMK_SOURCE_DIR="$PWD"; PKGMK_WORK_DIR="$PWD/work" PKGMK_PACKAGE_DIR="$PWD"; PKGMK_COMPRESSION_MODE="gz" PKGMK_INSTALL_COMMAND=""; PKGMK_FORCE="no" PKGMK_DOWNLOAD_ONLY="no"; PKGMK_KEEP_WORK="no" PKGMK_EXTRACT_ONLY="no"; PKGMK_UPDATE_SIG="no" PKGMK_MTIME_ONLY="no"; PKGMK_IGNORE_SIG="no" PKGMK_UPDATE_FOOTPRINT="no"; PKGMK_REFRESH_SIG="no" PKGMK_IGNORE_FOOTPRINT="no"; PKGMK_CHECK_SIG="no" PKGMK_IGNORE_NEW="no"; PKGMK_PRIVATEKEY="" ######################## subroutines ################################ parse_options() { while [ "$1" ]; do case "$1" in -h|--help) print_help; exit 0 ;; -v|--version) echo "$(basename "$PKGMK_COMMAND") (pkgutils) $PKGMK_VERSION"; exit 0 ;; -uf|--update-footprint) PKGMK_UPDATE_FOOTPRINT="yes" ;; -us|--update-signature) PKGMK_UPDATE_SIG="yes" ;; -rs|--refresh-signature) PKGMK_REFRESH_SIG="yes" ;; -cs|--check-signature) PKGMK_CHECK_SIG="yes" ;; -d|--download) ;; # deprecated flags but don't throw an error for them -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; } ;; -do|--download-only) PKGMK_DOWNLOAD_ONLY="yes" ;; -eo|--extract-only) PKGMK_EXTRACT_ONLY="yes" ;; -utd|--up-to-date) PKGMK_MTIME_ONLY="yes" ;; -if|--ignore-footprint) PKGMK_IGNORE_FOOTPRINT="yes" ;; -in|--ignore-new) PKGMK_IGNORE_NEW="yes" ;; -is|--ignore-signature) PKGMK_IGNORE_SIG="yes" ;; -ns|--no-strip) ns_filter="-false" ;; -f|--force) PKGMK_FORCE="yes" ;; -kw|--keep-work) PKGMK_KEEP_WORK="yes" ;; -i|--install) PKGMK_INSTALL_COMMAND="/usr/bin/pkgadd" ;; -u|--upgrade) PKGMK_INSTALL_COMMAND="/usr/bin/pkgadd -u" ;; -pk|--public-key) [ "$2" ] && PKGMK_PUBLICKEY="$2" || { echo "$(basename "$PKGMK_COMMAND"): option $1 requires an argument"; exit 1; } shift ;; -sk|--secret-key) [ "$2" ] && PKGMK_PRIVATEKEY="$2" || { echo "$(basename "$PKGMK_COMMAND"): option $1 requires an argument"; exit 1; } shift ;; -cf|--config-file) [ "$2" ] && PKGMK_CONF="$2" || { echo "$(basename "$PKGMK_COMMAND"): option $1 requires an argument"; exit 1; } shift ;; *) echo "$(basename "$PKGMK_COMMAND"): invalid option $1"; exit 1 ;; esac shift done } print_help() { echo "usage: $(basename "$PKGMK_COMMAND") [options]" echo "options: (highest to lowest precedence)" echo " -h, --help print help and exit" echo " -v, --version print version and exit" echo " -cf, --config-file use alternative configuration file" echo " -sk, --secret-key use to sign the port" echo " -pk, --public-key check the port signature using public-key " 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 " -f, --force override timestamp check, build package or update footprint anyway" 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 " -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 " -is, --ignore-signature build package without checking signature" echo " -eo, --extract-only stop after downloading and extracting source file(s)" echo " -ns, --no-strip do not strip executable binaries or libraries" echo " -kw, --keep-work keep temporary working directory" echo " -if, --ignore-footprint build package without checking footprint" echo " -in, --ignore-new build package, ignoring new files in a footprint mismatch" echo " -i, --install build and install package" echo " -u, --upgrade build and upgrade package" } validate_pkgfile() { # called from within PKGMK_ROOT local errcode kv [ -f Pkgfile ] || { error "no Pkgfile found. $PKGMK_ROOT is not a valid ports directory."; return "$E_PKGFILE"; } check_signature "pre-Pkgfile" | parse_signify_output errcode=$? [ "$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 # 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; \ [ -n "$release" ] || kv+=4; [ "$(type -t build)" = "function" ] || kv+=8; \ echo $kv ) | check_reqvars } check_reqvars () { local checksum nullvars; read -r checksum [ $((checksum & 1)) = 1 ] && nullvars=" 'name'" [ $((checksum & 2)) = 2 ] && nullvars+=" 'version'" [ $((checksum & 4)) = 4 ] && nullvars+=" 'release'" [ $((checksum & 8)) = 8 ] && nullvars+=" 'build()'" [ "$checksum" = 0 ] || { error "Pkgfile does not specify these required variables:"; \ info "$nullvars"; return "$E_PKGFILE"; } } 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." if [ -f "$pkg_dir$package" ]; then utd=1 while (( li < ${#_local_[@]} )) && [ "$utd" = 1 ]; do [ ! -e "${_local_[li]}" ] || \ [ "$pkg_dir$package" -nt "$(realpath "${_local_[$li]}")" ] || utd=0 li=$(( li+1 )) done [ ! -e "$PKGMK_ROOT/Pkgfile" ] || [ "$pkg_dir$package" -nt "$PKGMK_ROOT/Pkgfile" ] || utd=0 fi [ $utd = 0 ] || msg="$package is up to date." [ "$PKGMK_MTIME_ONLY" = "yes" ] && info "$msg"; return $utd } fetch_url() { local u="$1"; local h="$2"; local finished=0; local giturl tag CLONE_ARGS local SAVE_AS REPO OCONTINUE OOUT; local m=0 if [[ "$u" =~ ^__git__ ]]; then # git must be installed in order to obtain such a source [ -x "$PKGMK_GIT_COMMAND" ] || return 1; # giturl="${u#__git__}" # Did the port maintainer specify a branch other than 'main'? tag="${giturl##*\#}" [ "$tag" = "$giturl" ] || { giturl="${giturl%\#*}"; CLONE_ARGS=(-c advice.detachedHead=false --branch "$tag"); } # # Has this project been downloaded before? if [ -d "$src_dir/$h.partial" ]; then ln -s "$src_dir/$h.partial" "$h"; cd "$h" "$PKGMK_GIT_COMMAND" pull "$giturl" "$tag" finished=$?; cd .. else "$PKGMK_GIT_COMMAND" clone "$giturl" "${CLONE_ARGS[@]}" "$h.partial" finished=$?; [ "$src_dir" = "" ] || { mv "$h.partial" "$src_dir"; ln -s "$src_dir/$h.partial" "$h"; } fi return $finished; else case "$PKGMK_DOWNLOAD_PROG" in *wget) OCONTINUE="-c" OOUT="--compression=none --passive-ftp --no-directories --tries=3 --waitretry=3 $PKGMK_WGET_OPTIONS -O" ;; *curl) OCONTINUE="-C -" OOUT="-L -# --fail --ftp-pasv --retry 3 --retry-delay 3 $PKGMK_CURL_OPTIONS -o" ;; *) SAVE_AS="false" ;; esac # # start with the mirrors defined in pkgmk.conf, then go to the url found in the Pkgfile while (( m <= ${#PKGMK_SOURCE_MIRRORS[@]} )) && [ $finished = 0 ] \ && [ "$SAVE_AS" != "false" ]; do [ "${PKGMK_SOURCE_MIRRORS[m]}" = "" ] && um=$u || \ { REPO=${PKGMK_SOURCE_MIRRORS[m]%/}; um=$REPO/${u##*/}; } m=$(( m+1 )) # 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" . ; SAVE_AS="$PKGMK_DOWNLOAD_PROG $um $OCONTINUE $OOUT"; } \ || SAVE_AS="$PKGMK_DOWNLOAD_PROG $um $OOUT" if $SAVE_AS "$h.partial"; then finished=1 [ "$src_dir" = "" ] || [ ! -w "$src_dir"/ ] || \ { mv "$h.partial" "$src_dir/$h"; ln -sf "$src_dir/$h" . ; } else # an interrupted download should not have its efforts destroyed by cleanup_work() [ ! -s "$h.partial" ] || [ "$src_dir" = "" ] || \ [ ! -w "$src_dir"/ ] || mv "$h.partial" "$src_dir" fi done fi } cat_footprint() { pkginfo --footprint "$pkg_dir$package" \ | sed "s|\tlib/modules/$(uname -r)/|\tlib/modules//|g" \ | sort -k 3 } check_footprint() { local CN CM local TRUTH="$PKGMK_ROOT/.footprint"; local diffs=0; local severity=error; [ -f "$pkg_dir$package" ] || \ { error "$package not found. Cannot check footprint."; exit "$E_FOOTPRINT"; } if [ -f "$TRUTH" ]; then diff -w -t -U 0 <(sort "$TRUTH") <(cat_footprint | sort) | \ sed '/^@@/d; /^+++/d; /^---/d; s/^+/NEW /g; s/^-/MISSING /g' > ".footprint.diff" if [ -s ".footprint.diff" ]; then CN=$(grep -c ^NEW ".footprint.diff"); CM=$(grep -c ^MISSING ".footprint.diff") [ "$PKGMK_IGNORE_MISSING" = "yes" ] || diffs=$CM [ "$PKGMK_IGNORE_NEW" = "yes" ] || diffs=$(( diffs+CN )) (( diffs == 0 )) && severity=warning $severity "footprint mismatch found:"; cat ".footprint.diff" >&2 fi rm ".footprint.diff" else warning "footprint not found, creating new."; cat_footprint > "$TRUTH" fi (( diffs == 0 )) || exit $E_FOOTPRINT } parse_signify_output() { # chomps the output of check_signature() local signout signerr [ "$PKGMK_IGNORE_SIG" = "yes" ] && return 0 while read -r signout; do case "$signout" in *"Pkgfile verification failed") signerr=-1; error "Signature missing! Unable to authenticate the Pkgfile." ;; *"verification failed") signerr=1; error "Signature file corrupted or unreadable." ;; *"FAIL") signerr=2; error "Signature mismatch found:" echo "$signout" | awk -F: '/FAIL/ {printf "MISMATCH %s\n", $1}' ;; *"OK") signerr=0 ;; esac done return $signerr } check_signature() { # called from $PKGMK_ROOT in the case "when"="pre-Pkgfile", # otherwise called from within $work. local reqfiles=(Pkgfile); local s=0; local when="$1"; local SIGNIFY_ARGS=(-C -x "$PKGMK_ROOT/.signature") [ -n "$PKGMK_PUBLICKEY" ] || PKGMK_PUBLICKEY="$(get_repo_key public)" if [ -f "$PKGMK_ROOT/.signature" ]; then [ "$when" = "pre-Pkgfile" ] || reqfiles=(.footprint) while [ "$when" = "pre-build" ] && (( s < ${#_local_[@]} )); do [[ "${source[s]}" =~ ^__git__ ]] || reqfiles+=("${_local_[s]}") s=$(( s+1 )) done for FILE in "${reqfiles[@]}"; do [ -e "$FILE" ] || ln -sf "$PKGMK_ROOT/$FILE" . done [ -r "$PKGMK_PUBLICKEY" ] && SIGNIFY_ARGS+=(-p "$PKGMK_PUBLICKEY") /usr/bin/signify "${SIGNIFY_ARGS[@]}" "${reqfiles[@]}" 2>&1 else [ "$when" = "pre-Pkgfile" ] && echo "Pkgfile verification failed" [ "$when" != "pre-Pkgfile" ] && echo "signature verification failed" fi } get_repo_key() { local typ="${1:0:3}"; local REPO; REPO=$(dirname "$PKGMK_ROOT"); REPO=$(basename -s .git "$REPO"); ls "/etc/ports/$REPO.$typ" 2>/dev/null \ || ls "$HOME/.ssh/$REPO.$typ" 2>/dev/null } make_signature() { local ordered si pub [ -w "$PKGMK_ROOT/.signature" ] || { error ".signature not writable."; return $E_DIRPERM; } [ -n "$PKGMK_PRIVATEKEY" ] || PKGMK_PRIVATEKEY="$(get_repo_key secret)" if [ -n "$PKGMK_PRIVATEKEY" ]; then pub="/etc/ports/$(basename -s .sec "$PKGMK_PRIVATEKEY").pub" else error "No suitable secret key found. Specify one explicitly with '-sk'." return $E_SIGNATURE fi # create a new .signature, or refresh an existing manifest? case "$1" in new) [ -e "$PKGMK_ROOT/.footprint" ] || \ warning "Footprint not found, signature will be incomplete." for f in "$PKGMK_ROOT/Pkgfile" "$PKGMK_ROOT/.footprint"; do [ -e "$f" ] && ordered+=( "$f" ) done for ((si=0; si < ${#source[@]}; si++)); do # ignore git directories when writing the signature [[ "${source[si]}" =~ ^__git__ ]] || ordered+=("${_local_[si]}") done sha256sum --tag "${ordered[@]}" \ | sed 's|^SHA256 (.*/\(.*\))\(.* = .*\)|SHA256 (\1)\2|' \ | /usr/bin/signify -S -e -x - -q -s "$PKGMK_PRIVATEKEY" -m - \ | sed "s|${PKGMK_PRIVATEKEY%.sec}.pub|$pub|" \ > "$PKGMK_ROOT/.signature" ;; refresh) [ -e ".signature" ] || \ { error "missing .signature, cannot refresh."; return $E_SIGNATURE; } if tail -n +3 ".signature" | /usr/bin/signify -S -e -x - -q \ -s "$PKGMK_PRIVATEKEY" -m - > .signature.tmp; then mv .signature.tmp .signature else rm .signature.tmp; return $E_SIGNATURE fi ;; esac } interrupted() { error "Interrupted."; cleanup_work; exit 1 } cleanup_work() { [ "$PKGMK_KEEP_WORK" = "yes" ] || { cd "$PKGMK_ROOT"; rm -rf "$work"; } } info() { echo "=======> $1" } warning() { info "WARNING: $1" >&2 } error() { info "ERROR: $1" >&2 } ######################## end of subroutines ########################### ## Now ensure that they cannot be overwritten when sourcing Pkgfile ## readonly -f main info warning error print_help parse_options validate_pkgfile \ check_reqvars check_pkg_mtime fetch_url cat_footprint check_footprint \ make_signature get_repo_key check_signature parse_signify_output cleanup_work trap "interrupted" SIGHUP SIGINT SIGQUIT SIGTERM export LC_ALL=C.UTF-8 main "$@"