40771ce9fa
Previously, pkg_libchk, pkg_upgrade, and uma failed to run. Bump PORTREVISION PR: 193003 Submitted by: Carlos Jacobo Puga Medina <cpm@fbsd.es> Reviewed by: marino
484 lines
12 KiB
Bash
484 lines
12 KiB
Bash
#!/bin/sh -f
|
|
#
|
|
# Copyright (c) 2007-2009
|
|
# Dominic Fandrey <kamikaze@bsdforen.de>
|
|
#
|
|
# Redistribution and use in source and binary forms, with or without
|
|
# modification, are permitted provided that the following conditions
|
|
# are met:
|
|
# 1. Redistributions of source code must retain the above copyright
|
|
# notice, this list of conditions and the following disclaimer.
|
|
#
|
|
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
#
|
|
|
|
readonly name=pkg_libchk
|
|
readonly version=1.6.1
|
|
readonly osname=`uname -s`
|
|
readonly pkgng=`make -f /usr/share/mk/bsd.port.mk -V WITH_PKGNG`
|
|
|
|
# Use a line break as delimiter.
|
|
IFS='
|
|
'
|
|
|
|
# Filename prefix for shared data
|
|
sharedprefix="%%TMP%%/$$"
|
|
shared="locks"
|
|
|
|
#
|
|
# This function remembers a lock to allow later deletion with the
|
|
# lockUnregisterAll() function.
|
|
#
|
|
# @param $1
|
|
# The name of the lock.
|
|
lockRegister() {
|
|
local lock
|
|
lock="$sharedprefix-$shared"
|
|
lockf -k "$lock" sh -c "
|
|
if ! grep -qE '^$1\$' '$lock'; then
|
|
echo '$1' >> '$lock'
|
|
fi
|
|
"
|
|
}
|
|
|
|
#
|
|
# Unregisters all locks.
|
|
#
|
|
lockUnregisterAll() {
|
|
wait
|
|
for register in $(cat "$sharedprefix-$shared"); {
|
|
lockf "$sharedprefix-$register" wait
|
|
}
|
|
lockf "$sharedprefix-$shared" wait
|
|
}
|
|
|
|
#
|
|
# This function creates a semaphore.
|
|
#
|
|
# @param $1
|
|
# The name of the semaphore.
|
|
# @param $2
|
|
# The size of the semaphore.
|
|
#
|
|
semaphoreCreate() {
|
|
local lock
|
|
lockRegister "semaphore-$1"
|
|
lock="$sharedprefix-semaphore-$1"
|
|
lockf -k "$lock" echo "$2" > "$lock"
|
|
eval "semaphore_$1_size=$2"
|
|
}
|
|
|
|
#
|
|
# This function waits until the semaphore is free und registers its use.
|
|
# Everything that uses this also has to call the semaphoreFree() function.
|
|
#
|
|
# @param $1
|
|
# The name of the semaphore.
|
|
#
|
|
semaphoreUse() {
|
|
local lock semaphores
|
|
lock="$sharedprefix-semaphore-$1"
|
|
while ! lockf -k "$lock" sh -c "
|
|
state=\$(cat '$lock')
|
|
if [ \"\$state\" -gt 0 ]; then
|
|
echo \"\$((\$state - 1))\" > '$lock'
|
|
exit 0
|
|
fi
|
|
exit 1
|
|
"; do
|
|
sleep 0.1
|
|
done
|
|
}
|
|
|
|
#
|
|
# This function frees a semaphore.
|
|
#
|
|
# @param $1
|
|
# The name of the semaphore.
|
|
#
|
|
semaphoreFree() {
|
|
local lock
|
|
lock="$sharedprefix-semaphore-$1"
|
|
lockf -k "$lock" sh -c "
|
|
state=\"\$((\"\$(cat '$lock')\" + 1))\"
|
|
echo \"\$state\" > '$lock'
|
|
"
|
|
}
|
|
|
|
#
|
|
# This function sets a new status and prints it.
|
|
#
|
|
# @param $1
|
|
# The status message.
|
|
# @param $clean
|
|
# If set status handling is disabled.
|
|
#
|
|
statusSet() {
|
|
# In clean mode status handling is disabled.
|
|
test -z "$clean" || return 0
|
|
local lock
|
|
lock="$sharedprefix-status"
|
|
lockf -k "$lock" sh -c "
|
|
status=\"\$(cat '$lock')\"
|
|
echo '$1' > '$lock'
|
|
printf \"\\r%-\${#status}s\\r\" '$1' > /dev/tty
|
|
"
|
|
}
|
|
|
|
#
|
|
# This function prints a message and the current status behind it.
|
|
#
|
|
# @param $1
|
|
# The message to print.
|
|
# @param $clean
|
|
# If set the status will not be printed.
|
|
#
|
|
statusPrint() {
|
|
if [ -z "$clean" ]; then
|
|
local lock
|
|
lock="$sharedprefix-status"
|
|
lockf -k "$lock" sh -c "
|
|
status=\"\$(cat '$lock')\"
|
|
printf \"%-\${#status}s\\r\" '' > /dev/tty
|
|
echo '$1'
|
|
printf '%s\\r' \"\$status\" > /dev/tty
|
|
"
|
|
else
|
|
echo "$1"
|
|
fi
|
|
}
|
|
|
|
#
|
|
# Waits for a semaphore to be completely free and counts down the remaining
|
|
# number of locks.
|
|
#
|
|
# @param $1
|
|
# The semaphore to watch.
|
|
# @param $2
|
|
# The status message to print, insert %d in the place where the number
|
|
# of remaining locks belong.
|
|
#
|
|
semaphoreCountDown() {
|
|
local free size
|
|
while read -t1 free < "$sharedprefix-semaphore-$1"; do
|
|
size=$(eval "echo \$semaphore_$1_size")
|
|
statusSet "$(printf "$2" $(( $size - $free )))"
|
|
test "$free" -eq "$size" && break
|
|
sleep 0.1
|
|
done
|
|
wait
|
|
}
|
|
|
|
# Clean up upon exit.
|
|
trap '
|
|
semaphoreCountDown jobs "Terminated by signal, waiting for %d jobs to die."
|
|
echo > /dev/tty
|
|
lockUnregisterAll
|
|
exit 255
|
|
' int term
|
|
|
|
#
|
|
# This function checks whether a given binary or library directly depends
|
|
# on a missing library.
|
|
# It goes a long way to prevent all kinds of false positives.
|
|
# It always returns 2 (false) for Linux and other non-native libraries
|
|
# and binaries.
|
|
# It also checks whether the missing dependency is really a direct dependency
|
|
# (indirect dependencies have to be fixed somewhere else).
|
|
#
|
|
# @param $1
|
|
# The library or binary to check.
|
|
# @return
|
|
# Returns 0 (true) if a library is missing.
|
|
# Returns 1 if everything is all right.
|
|
# Returns 2 if the check cannot be performed (not a native library).
|
|
#
|
|
dependencyMissing() {
|
|
local missing file direct libfound
|
|
|
|
# We cannot handle non-native binaries,
|
|
# so assume everything is in order.
|
|
if ! readelf -e "$1" 2>&1 | \
|
|
grep -E "^[[:space:]]*OS/ABI:[[:space:]]*UNIX - $osname\$" \
|
|
> /dev/null
|
|
then
|
|
return 2
|
|
# Nothing is missing.
|
|
elif ! missing="$(ldd "$1" 2>&1 | grep -E "$match_expr")"; then
|
|
return 1
|
|
fi
|
|
|
|
# The return status. The value 1 assumes that this is a false positive.
|
|
status=1
|
|
|
|
# Only report misses for direct dependencies.
|
|
direct="$(
|
|
readelf -d "$1" 2> /dev/null | \
|
|
grep 'Shared library:' | \
|
|
sed -E -e 's|^[^[]*\[||1' -e 's|\]$||1'
|
|
)"
|
|
|
|
# Compare every missing depency with the list of direct dependencies
|
|
# and report that the dependency is missing if the missing file is
|
|
# a direct dependency.
|
|
for file in $missing; {
|
|
# Strip the missing file of additional information.
|
|
file="$(echo "$file" | sed -E \
|
|
-e 's| => .*$||1' \
|
|
-e 's|^[[:space:]]*||1' \
|
|
-e 's|^.*dependency ||1' \
|
|
-e 's| not found$||1'
|
|
)"
|
|
|
|
# If in mean mode we do not check for false positives.
|
|
if [ -n "$mean" ]; then
|
|
test -n "$raw" && return 0
|
|
statusPrint "$package_name: $1 misses $file"
|
|
continue
|
|
fi
|
|
|
|
# Handle the case where a library is not found, but exists
|
|
# somewhere in the package. This is for packages that do not
|
|
# rely on the OS to find libraries.
|
|
libfound=
|
|
for library in $(echo "$libraries" | grep -E "/$file\$"); {
|
|
# The library exists after all.
|
|
test -e "$library" && libfound=1 && break
|
|
}
|
|
if test "$libfound"; then
|
|
test -n "$verbose" && statusPrint "$package_name: \
|
|
located: $1 misses $file found at $library."
|
|
continue
|
|
fi
|
|
|
|
# Compare the file with the list of direct dependencies.
|
|
# If it's not in than it's only an indirect dependency and
|
|
# cannot be fixed by rebuilding this port.
|
|
if echo "$direct" | grep -E "^$file\$" > /dev/null; then
|
|
test -n "$raw" && return 0
|
|
statusPrint "$package_name: $1 misses $file"
|
|
status=0
|
|
elif [ -n "$verbose" ]; then
|
|
statusPrint "$package_name: inderect: $1 \
|
|
misses $file is an inderect dependency."
|
|
fi
|
|
}
|
|
|
|
return $status
|
|
}
|
|
|
|
#
|
|
# Checks the parameters for options.
|
|
#
|
|
# @param $packages
|
|
# The parameters to pkg_info -E that will result in the
|
|
# names of the packages to work on.
|
|
# @param $recursive
|
|
# Contains the appropriate parameter to get the
|
|
# dependencies of the given packages from pkg_info.
|
|
# @param $Recursive
|
|
# Contains the appropriate parameter to get the
|
|
# packages depending on the given packages from pkg_info.
|
|
# @param $raw
|
|
# Is set to trigger raw printing.
|
|
# @param $clean
|
|
# Is set to trigger printing without status messages.
|
|
# @param $verbose
|
|
# Is set to be verbose about false positives.
|
|
# @param $mean
|
|
# Is set to switch into mean mode. That means no
|
|
# checking of false positives.
|
|
# @param $compat
|
|
# Delete to avoid detecting compat libraries as misses.
|
|
# @param $origin
|
|
# Is set to turn the print origin mode on.
|
|
# @semaphore jobs
|
|
# Is set to limit the amount of parallel jobs.
|
|
#
|
|
readParams() {
|
|
local option
|
|
|
|
for option {
|
|
case "$option" in
|
|
"-a" | "--all")
|
|
packages="-a"
|
|
;;
|
|
"-c" | "--clean")
|
|
clean=1
|
|
;;
|
|
"-h" | "--help")
|
|
printHelp
|
|
;;
|
|
-j* | --jobs*)
|
|
local jobs
|
|
jobs="${option#-j}"
|
|
jobs="${jobs#--jobs}"
|
|
if [ "$jobs" -ne "$jobs" ] 2> /dev/null; then
|
|
echo "The -j option must be followed" \
|
|
"by a number."
|
|
exit 3
|
|
elif [ "$jobs" -lt 1 ]; then
|
|
echo "The -j option must specify at" \
|
|
"least 1 job."
|
|
exit 3
|
|
else
|
|
semaphoreCreate jobs "$jobs"
|
|
fi
|
|
;;
|
|
"-m" | "--mean")
|
|
mean=1
|
|
;;
|
|
"-n" | "--no-compat")
|
|
compat=
|
|
;;
|
|
"-o" | "--origin")
|
|
origin=1
|
|
;;
|
|
"-q" | "--raw")
|
|
raw=1
|
|
if [ -n "$verbose" ]; then
|
|
echo "The parameters -v and -q may" \
|
|
"not be used at the same time."
|
|
exit 2
|
|
fi
|
|
;;
|
|
"-r" | "--recursive")
|
|
recursive="-r"
|
|
;;
|
|
"-R" | "--upward-recursive")
|
|
Recursive="-R"
|
|
;;
|
|
"-v" | "--verbose")
|
|
verbose=1
|
|
if [ -n "$raw" ]; then
|
|
echo "The parameters -q and -v may" \
|
|
"not be used at the same time."
|
|
exit 2
|
|
fi
|
|
;;
|
|
-? | --*)
|
|
echo "Unknown parameter \"$option\"."
|
|
exit 1
|
|
;;
|
|
-*)
|
|
readParams "${option%${option#-?}}"
|
|
readParams "-${option#-?}"
|
|
;;
|
|
*)
|
|
packages="$packages${packages:+$IFS}$option"
|
|
;;
|
|
esac
|
|
}
|
|
}
|
|
|
|
#
|
|
# Display a short help message.
|
|
#
|
|
printHelp() {
|
|
echo "$name v$version
|
|
usage: $name [-a] [-c] [-h] [-jN] [-m] [-n] [-o] [-q] [-r] [-R] [-v] [packages]"
|
|
exit 0
|
|
}
|
|
|
|
# Create the expression to match to find files linking against compat libraries.
|
|
# This can be emptied by readParams to deactivate that feature.
|
|
prefix="$(make -f /usr/share/mk/bsd.port.mk -VPREFIX 2> /dev/null || \
|
|
echo '%%PREFIX%%')"
|
|
compat="=> $prefix/lib/compat|"
|
|
|
|
# Create the semaphore with CPU cores * 2 jobs.
|
|
semaphoreCreate jobs "$(($(sysctl -n hw.ncpu 2> /dev/null || echo 1) * 2))"
|
|
# Register the status lock.
|
|
lockRegister status
|
|
|
|
# Read the parameters.
|
|
readParams "$@"
|
|
|
|
statusSet 'Preparing ...'
|
|
|
|
# Get the packages to work on.
|
|
test -z "$packages" && packages="-a"
|
|
if [ -n "$pkgng" ]; then
|
|
packages="$(pkg info -q $packages)"
|
|
test -z "$recursive" -a -z "$Recursive" || packages="$packages
|
|
$(pkg info -q $recursive $Recursive "$packages" 2> /dev/null | \
|
|
sed -E 's|^@pkgdep[[:space:]]*||1')"
|
|
else
|
|
packages="$(pkg_info -E $packages)"
|
|
test -z "$recursive" -a -z "$Recursive" || packages="$packages
|
|
$(pkg_info -q $recursive $Recursive "$packages" 2> /dev/null | \
|
|
sed -E 's|^@pkgdep[[:space:]]*||1')"
|
|
fi
|
|
|
|
# Create the regexp to match ldd output
|
|
match_expr="$compat=> not found|dependency .+ not found"
|
|
|
|
# The packages to check.
|
|
package_amount="$(echo "$packages" | wc -l | sed 's|[[:space:]]||g')"
|
|
package_num=0
|
|
|
|
# Check each selected package.
|
|
for package in $packages; {
|
|
package_num="$(($package_num + 1))"
|
|
if [ -n "$pkgng" ]; then
|
|
test $origin \
|
|
&& package_name="$(pkg info -qo "$package")" \
|
|
|| package_name="$package"
|
|
else
|
|
test $origin \
|
|
&& package_name="$(pkg_info -qo "$package")" \
|
|
|| package_name="$package"
|
|
fi
|
|
|
|
# Print what we're doing.
|
|
statusSet "Starting job $package_num of $package_amount: $package_name"
|
|
|
|
semaphoreUse jobs
|
|
(
|
|
# Remember freeing the semaphore.
|
|
trap 'semaphoreFree jobs' EXIT
|
|
|
|
files=""
|
|
if [ -n "$pkgng" ]; then
|
|
files="$(pkg info -lq "$package")"
|
|
else
|
|
files="$(pkg_info -qL "$package")"
|
|
fi
|
|
# Get the programs libraries in case it doesn't use the
|
|
# operating system to find its libraries.
|
|
libraries="$(echo "$files" | grep -E '\.so[\.0-9]*$')"
|
|
|
|
outdated=0
|
|
broken=
|
|
|
|
# Check each file of each package.
|
|
for file in $files; {
|
|
if [ ! -L "$file" -a \( \
|
|
-x "$file" -o \
|
|
-n "$(echo "$file" | grep -E '\.so[\.0-9]*$')" \
|
|
\) ]; then
|
|
if dependencyMissing "$file"; then
|
|
if [ -n "$raw" ]; then
|
|
statusPrint "$package_name"
|
|
break 1
|
|
fi
|
|
fi
|
|
fi
|
|
}
|
|
) &
|
|
}
|
|
|
|
semaphoreCountDown jobs "Waiting for %d remaining jobs to finish."
|
|
statusSet
|
|
lockUnregisterAll
|
|
|
|
exit 0
|