freebsd-ports/sysutils/bsdadminscripts/files/pkg_libchk.in
Rene Ladan 40771ce9fa sysutils/bsdadminscripts: fix scripts at runtime.
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
2014-08-26 18:57:57 +00:00

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