c55acfc48b
Signed-off-by: Davide Madrisan <davide.madrisan@gmail.com>
464 lines
15 KiB
Bash
464 lines
15 KiB
Bash
#!/bin/bash
|
|
# test01_pkgquality -- @package@ test (rpm quality checks)
|
|
# Copyright (C) 2008,2012-2014 Davide Madrisan <davide.madrisan@gmail.com>
|
|
|
|
[ -z "$BASH" ] || [ ${BASH_VERSION:0:1} -lt 2 ] &&
|
|
echo $"this script requires bash version 2 or better" >&2 && exit 1
|
|
|
|
me=("test01_pkgquality" "@version@" "@date@")
|
|
|
|
[ -r @libdir@/libmsgmng.lib ] ||
|
|
{ echo "$me: "$"library not found"": @libdir@/libmsgmng.lib" 1>&2
|
|
exit 1; }
|
|
|
|
. @libdir@/libmsgmng.lib
|
|
|
|
[ -r @libdir@/libtranslate.lib ] ||
|
|
{ echo "$me: "$"library not found"": @libdir@/libtranslate.lib" 1>&2
|
|
exit 1; }
|
|
. @libdir@/libtranslate.lib
|
|
|
|
notify.debug $"loading"": \`test01_pkgquality'..."
|
|
|
|
# check if all the needed tools are available
|
|
for tool in file find getopt grep ls ldd wc; do
|
|
[ "$(type -p $tool)" ] ||
|
|
notify.error $"utility not found"": \`$tool'"
|
|
done
|
|
|
|
function alltests() {
|
|
notify.note " ${NOTE}"$"performing quality checks""${NORM}""..."
|
|
|
|
TEMP=`LC_ALL=C getopt \
|
|
-o i:t: --long infofile:,tmpdir: \
|
|
-n "$FUNCNAME" -- "$@"`
|
|
[ $? = 0 ] || return 1
|
|
eval set -- "$TEMP"
|
|
|
|
while :; do
|
|
case "$1" in
|
|
-i|--infofile)
|
|
rpminfofile="$2"
|
|
shift
|
|
;;
|
|
-t|--tmpdir)
|
|
tmpextractdir="$2"
|
|
shift
|
|
;;
|
|
--) shift; break ;;
|
|
*) notify.error $"\
|
|
(bug)"" -- $FUNCNAME: "$"\`getopt' error" ;;
|
|
esac
|
|
shift
|
|
done
|
|
|
|
[ "$rpminfofile" ] || notify.error $"\
|
|
(bug)"" -- $FUNCNAME: "$"missing mandatory arg"" (--infofile)"
|
|
[ -r "$rpminfofile" ] || notify.error $"\
|
|
(bug)"" -- $FUNCNAME: "$"cannot read"" \`$rpminfofile'"
|
|
|
|
. $rpminfofile
|
|
|
|
[ "$tmpextractdir" ] || notify.error $"\
|
|
(bug)"" -- $FUNCNAME: "$"missing mandatory arg"" (--tmpdir)"
|
|
[ -d "$tmpextractdir" ] || notify.error $"\
|
|
(bug)"" -- $FUNCNAME: "$"no such file or directory"" \`$tmpextractdir'"
|
|
|
|
local total_issues=0
|
|
|
|
# check for broken symlinks
|
|
# - symlinks to files in the buildroot directory for rpm
|
|
# (usable for a symlink attacks)
|
|
# - symlinks not pointing to existing files
|
|
|
|
test.skip $test_number || {
|
|
notify.note "$(test.num2str). ${NOTE}"\
|
|
$"checking for wrong symbolic links""${NORM}..."
|
|
|
|
# local rpmbuildroot=`sed -n "/%description/q;{
|
|
# /^BuildRoot[ ]*:/{s/[^ ]*[ ]*//;p}}" \
|
|
# $spec_dir/$SRPM_SPECFILE | \
|
|
# sed "s,%[{]*name[}]*,$SPEC_NAME,
|
|
# s,%[{]*version[}]*,$SPEC_VERSION,
|
|
# s,%[{]*_tmppath[}]*,$tmppath_dir,;p"`
|
|
|
|
# FIXME: 'tmppath_dir' should be get from configuration files
|
|
tmppath_dir=`rpm --eval %_tmppath 2>/dev/null`
|
|
|
|
[ "$tmppath_dir" ] ||
|
|
notify.error $"(bug)"" -- $FUNCNAME: ""empty string"" (tmppath_dir)"
|
|
|
|
notify.debug "tmppath_dir = $tmppath_dir"
|
|
|
|
let "i = 0"
|
|
for pck in ${rpmpkg_name[@]}; do
|
|
pushd $tmpextractdir/$i >/dev/null
|
|
for f in $(find -mindepth 1 -type l); do
|
|
notify.debug "$f --> `readlink $f`"
|
|
# FIXME: the check fails if 'BuildRoot' doesn't start
|
|
# by '%_{tmppath}'
|
|
# note: the first condition check for wrong links, like
|
|
# /usr/share/man/man1/zcmp.1.gz -> .gz
|
|
# made by the broken `brp-compress' script in rpm 4.0.4
|
|
if [[ "$(readlink $f)" = ".gz" || \
|
|
"$(readlink $f)" =~ $tmppath_dir ]]; then
|
|
notify.warning "${NOTE}${pck##*/}${NORM}"
|
|
notify.note $"\
|
|
wrong symlink"": \`${NOTE}${f/./}${NORM}' --> \`${NOTE}$(readlink $f)${NORM}'"
|
|
let "total_issues += 1"
|
|
fi
|
|
done
|
|
popd >/dev/null
|
|
let "i += 1"
|
|
done; }
|
|
test_number=$(($test_number + 1))
|
|
|
|
# check for `%buildroot' strings
|
|
test.skip $test_number || {
|
|
if [ "$rpm_ignores_buildroot" = 1 ]; then
|
|
if [ "$SPEC_BUILDROOT" ]; then
|
|
notify.note \
|
|
"$(test.num2str). ${NOTE}"\
|
|
$"checking for \`$SPEC_BUILDROOT' (%buildroot) strings"\
|
|
"${NORM}... "$"skipped"
|
|
else
|
|
notify.note \
|
|
"$(test.num2str). ${NOTE}"\
|
|
$"checking for %buildroot strings""${NORM}... "$"N/A"
|
|
fi
|
|
else
|
|
notify.note "$(test.num2str). "\
|
|
$"checking for \`$SPEC_BUILDROOT' (%buildroot) strings"
|
|
|
|
[ "$SPEC_BUILDROOT" ] || notify.error \
|
|
$"(bug)"" -- $FUNCNAME: ""empty string"" (SPEC_BUILDROOT)"
|
|
|
|
let "i = 0"
|
|
for pck in ${rpmpkg_name[@]}; do
|
|
pushd $tmpextractdir/$i >/dev/null
|
|
for f in $(find -type f \
|
|
-exec grep -ls "$SPEC_BUILDROOT" {} \;2>/dev/null); do
|
|
notify.warning "${NOTE}${pck##*/}${NORM}"
|
|
notify.note "\
|
|
${NOTE}$(echo $f | sed "s,$tmpextractdir/$i,," )${NORM}"
|
|
notify.note "\
|
|
"$"mime type: ""${NOTE}$(file --brief --mime --uncompress $f)${NORM}"
|
|
notify.note "$(\
|
|
strings -a $f | grep "^$SPEC_BUILDROOT" | sort -bu | \
|
|
sed "s,$SPEC_BUILDROOT\(.*\), - [%buildroot]\1,")"
|
|
let "total_issues += 1"
|
|
done
|
|
popd >/dev/null
|
|
let "i += 1"
|
|
done
|
|
fi; }
|
|
test_number=$(($test_number + 1))
|
|
|
|
# check for `%_builddir' strings
|
|
BUILDDIR="$(rpm --eval=%_builddir 2>/dev/null)"
|
|
test.skip $test_number || {
|
|
notify.note "$(test.num2str). ${NOTE}"\
|
|
$"checking for \`$BUILDDIR' (%_builddir) strings""${NORM}... "
|
|
|
|
[ "$BUILDDIR" ] ||
|
|
notify.error $"(bug)"" -- $FUNCNAME: ""empty string"" (BUILDDIR)"
|
|
|
|
let "i = 0"
|
|
for pck in ${rpmpkg_name[@]}; do
|
|
pushd $tmpextractdir/$i >/dev/null
|
|
for f in $(find -type f \
|
|
-exec grep -ls "$BUILDDIR" {} \; 2>/dev/null); do
|
|
notify.warning "${NOTE}${pck##*/}${NORM}"
|
|
notify.note "\
|
|
${NOTE}$(echo $f | sed "s,$tmpextractdir/$i,," )${NORM}"
|
|
notify.note "\
|
|
"$"mime type: ""${NOTE}$(file --brief --mime --uncompress $f)${NORM}"
|
|
notify.note "$(\
|
|
strings -a $f | grep "$BUILDDIR" | sort -bu | \
|
|
sed "s,$BUILDDIR,[%_builddir],g;s,.*, - &,")"
|
|
let "total_issues += 1"
|
|
done
|
|
popd >/dev/null
|
|
let "i += 1"
|
|
done; }
|
|
test_number=$(($test_number + 1))
|
|
|
|
# check for suspected plugins (.la, .so) in devel packages
|
|
# note: pure plugins must be in the main package, not in devel
|
|
test.skip $test_number || {
|
|
notify.note "$(test.num2str). ${NOTE}"\
|
|
$"checking for suspicious plugins in devel packages""${NORM}..."
|
|
|
|
let "i = 0"
|
|
for pck in ${rpmpkg_name[@]}; do
|
|
# skip non devel packages
|
|
[[ "${pck##*/}" =~ -devel- ]] || { let "i += 1"; continue; }
|
|
|
|
pushd $tmpextractdir/$i >/dev/null
|
|
# find *.so files that are not symlinks to dynamic libraries
|
|
for f in `\
|
|
find -mindepth 1 -type f -name \*.so -exec file {} \; | \
|
|
grep ' shared object,' | sed -n 's/.\(.*\):.*/\1/p'`; do
|
|
notify.warning "${NOTE}${pck##*/}${NORM}"
|
|
notify.note $"found suspect plugin \`${NOTE}$f${NORM}'"
|
|
let "total_issues += 1"
|
|
done
|
|
popd >/dev/null
|
|
let "i += 1"
|
|
done; }
|
|
test_number=$(($test_number + 1))
|
|
|
|
# check for wrong file attributes in lib and bin dirs
|
|
test.skip $test_number || {
|
|
notify.note "$(test.num2str). ${NOTE}"\
|
|
$"checking for wrong file attributes in bin and lib directories""${NORM}..."
|
|
|
|
warning=0
|
|
let "i = 0"
|
|
for pck in ${rpmpkg_name[@]}; do
|
|
pushd $tmpextractdir/$i >/dev/null
|
|
for f in $( find . -type f \( \
|
|
\( -name '*.so.*' -and -not -name '*.so.owner' \
|
|
-and -not -perm 755 \) -or \
|
|
\( -name '*.so' -and -not -perm 755 \) -or \
|
|
\( \( -path './bin/*' -or \
|
|
-path './sbin/*' -or \
|
|
-path './usr/bin/*' -or \
|
|
-path './usr/sbin/*' \) \
|
|
-not -perm -111 \) 2>/dev/null \) ); do
|
|
let "warning = 1" &&
|
|
notify.warning "${NOTE}${pck##*/}${NORM}"
|
|
notify.note $"found suspect file"": \
|
|
\`${NOTE}${f/./}${NORM}' [$(ls -l "$f" | sed 's, .*,,')]"
|
|
let "total_issues += 1"
|
|
done
|
|
let "i += 1"
|
|
popd >/dev/null
|
|
done
|
|
[[ $warning -eq 0 ]] || notify.note "\
|
|
-----------------------------
|
|
${NOTE}"$"Hint"":${NORM}
|
|
# fixup strange shared library permissions
|
|
chmod 755 %{buildroot}%{_libdir}/*.so*
|
|
|
|
%files
|
|
%defattr(-,root,root)
|
|
...
|
|
%attr(0755,root,root) %{_bindir}/<program>
|
|
-----------------------------"; }
|
|
test_number=$(($test_number + 1))
|
|
|
|
# check for libraries with undefined symbols
|
|
test.skip $test_number || {
|
|
notify.note "$(test.num2str). ${NOTE}"\
|
|
$"checking for libraries with undefined symbols after relocation""${NORM}..."
|
|
|
|
let "i = 0"
|
|
local undefsyms
|
|
for pck in ${rpmpkg_name[@]}; do
|
|
# skip debug packages
|
|
[[ "${pck##*/}" =~ -debug- ]] && { let "i += 1"; continue; }
|
|
|
|
pushd $tmpextractdir/$i >/dev/null
|
|
for f in `find -mindepth 1 -type f \
|
|
\( -name *\.so\.* -not -name *.debug \) -exec file {} \; | \
|
|
grep 'shared object' | sed -n 's/.\([^:]*\):.*/\1/p'`; do
|
|
undefsyms="\
|
|
$(LC_ALL=C ldd -d -r "$f" 2>/dev/null |& grep "undefined symbol")"
|
|
if [ "$undefsyms" ]; then
|
|
notify.warning "${NOTE}${pck##*/}${NORM}"
|
|
echo -n "${CRIT}"
|
|
LC_ALL=C ldd -d -r "$f" 2>&1 | \
|
|
grep "undefined symbol" | sed "s,.*, - &,"
|
|
echo -n "${NORM}"
|
|
let "total_issues += 1"
|
|
fi
|
|
done
|
|
popd >/dev/null
|
|
let "i += 1"
|
|
done; }
|
|
test_number=$(($test_number + 1))
|
|
|
|
# check for binary files in etc (see FHS-2.2)
|
|
test.skip $test_number || {
|
|
notify.note "$(test.num2str). ${NOTE}"\
|
|
$"checking for binary files installed in /etc (see FHS)""${NORM}..."
|
|
|
|
warning=0
|
|
let "i = 0"
|
|
for pck in ${rpmpkg_name[@]}; do
|
|
pushd $tmpextractdir/$i >/dev/null
|
|
for f in $( find ./etc -type f -perm /111 2>/dev/null ); do
|
|
case $f in
|
|
./etc/rc.d/init.d/*) ;;
|
|
*) let "warning = 1" &&
|
|
{ notify.warning "${NOTE}${pck##*/}${NORM}"
|
|
notify.note $"found suspect file"": \
|
|
\`${NOTE}${f/./}${NORM}' [$(ls -l "$f" | sed 's, .*,,')]"
|
|
let "total_issues += 1"; } ;;
|
|
esac
|
|
done
|
|
popd >/dev/null
|
|
let "i += 1"
|
|
done
|
|
[ "$warning" = 0 ] || notify.note "\
|
|
-----------------------------
|
|
${NOTE}"$"Hint"":${NORM}
|
|
%files
|
|
%defattr(-,root,root)
|
|
...
|
|
%attr(0644,root,root) %{_sysconfdir}/<...file>
|
|
-----------------------------"; } #|| exit 1
|
|
test_number=$(($test_number + 1))
|
|
|
|
# check for installation code needed by info pages
|
|
test.skip $test_number || {
|
|
notify.note "$(test.num2str). ${NOTE}"\
|
|
$"checking if the info catalog is updated when necessary""${NORM}..."
|
|
|
|
error=0
|
|
let "i = 0"
|
|
for pck in ${rpmpkg_name[@]}; do
|
|
[[ -e $pck ]] || notify.error $"package not found"": \`${pck##*/}'"
|
|
|
|
[[ "$(rpm -p -ql $pck |
|
|
# FIXME: this check only works for FHS-compliant distros
|
|
grep "^$(rpm --eval %_infodir)")" ]] ||
|
|
{ let "i += 1"; continue; } # no info pages found
|
|
#notify.debug "$FUNCNAME: info page(s) found"
|
|
|
|
[[ "$(rpm -p -q --requires $pck 2>/dev/null | sed -n '
|
|
/^[ \t]*\/sbin\/install-info/p')" ]] || let "error+=1"
|
|
|
|
# note: we just check for at list two occurences of the command
|
|
# '/sbin/install-info' in the package scriptlets
|
|
[[ "$(rpm -p -q --scripts $pck 2>/dev/null | sed -n '
|
|
/^[ \t]*\/sbin\/install-info/p' | wc -l)" -ge 2 ]] || let "error+=1"
|
|
|
|
[ "$error" = "0" ] ||
|
|
{ notify.warning "${NOTE}${pck##*/}${NORM}"
|
|
notify.note $"info pages should be installed/uninstalled""${NORM}
|
|
---------------------------------------
|
|
${NOTE}"$"Hint"":${NORM}
|
|
%package [<subpackage>]
|
|
...
|
|
$(if [[ "$rpm_macro_installinfo_binary" ]]; then
|
|
echo "\
|
|
Requires(post):$rpm_macro_installinfo_binary
|
|
Requires(preun):$rpm_macro_installinfo_binary"
|
|
else
|
|
echo "\
|
|
Requires(post):${path_installinfo:-/sbin/install-info}
|
|
Requires(preun):${path_installinfo:-/sbin/install-info}"
|
|
fi)
|
|
|
|
%post [<subpackage>]
|
|
$([[ "$rpm_macro_installinfo" ]] &&
|
|
echo "$rpm_macro_installinfo %{name}.info" ||
|
|
echo "${path_installinfo:-/sbin/install-info} %{name}.info")
|
|
exit 0
|
|
|
|
%preun [<subpackage>]
|
|
$([[ "$rpm_macro_uninstallinfo" ]] &&
|
|
echo "$rpm_macro_uninstallinfo %{name}.info" ||
|
|
echo "${path_installinfo:-/sbin/install-info} --delete %{name}.info")
|
|
exit 0
|
|
---------------------------------------"
|
|
let "total_issues += $error"; }
|
|
done; }
|
|
test_number=$(($test_number + 1))
|
|
|
|
# check packages for wrong user and/or group ownerships
|
|
test.skip $test_number || {
|
|
notify.note "$(test.num2str). ${NOTE}"\
|
|
$"checking packages for wrong user and/or group ownerships""${NORM}..."
|
|
|
|
error=0
|
|
idun="$(id -un)" idgn="$(id -gn)"
|
|
let "i = 0"
|
|
for pck in ${rpmpkg_name[@]}; do
|
|
[[ -e $pck ]] || notify.error $"\
|
|
package not found"": \`${pck##*/}'"
|
|
( LC_ALL=C rpm -p -qlv $pck | \
|
|
while read line; do
|
|
set -- $line
|
|
# FIXME : find a better check, perhaps using a range
|
|
# of uid reserved for users
|
|
if [[ "$idun" = "$3" || "$idgn" = "$4" ]]; then
|
|
notify.warning "${NOTE}${pck##*/}${NORM}"
|
|
notify.note $"found suspect file"": \
|
|
\`${NOTE}$9${NORM}' [uid:\`${NOTE}$3${NORM}', gid:\`${NOTE}$4${NORM}']"
|
|
let "total_issues += 1"
|
|
fi
|
|
done )
|
|
done; }
|
|
test_number=$(($test_number + 1))
|
|
|
|
# check for desktop files installed in non standard applnk dir
|
|
test.skip $test_number || {
|
|
notify.note "$(test.num2str). ${NOTE}"\
|
|
$"checking packages for desktop files installed in the applnk dir""${NORM}..."
|
|
|
|
warning=0
|
|
rpmdatadir=$(rpm --eval %_datadir 2>/dev/null)
|
|
let "i = 0"
|
|
for pck in ${rpmpkg_name[@]}; do
|
|
pushd $tmpextractdir/$i >/dev/null
|
|
for f in $( find .${rpmdatadir} -type f 2>/dev/null ); do
|
|
case $f in
|
|
.${rpmdatadir}/applnk/*.desktop)
|
|
let "warning = 1" &&
|
|
{ notify.warning "${NOTE}${pck##*/}${NORM}"
|
|
notify.note $"found suspect file"": \
|
|
\`${NOTE}${f/./}${NORM}' [$(ls -l "$f" | sed 's, .*,,')]"
|
|
let "total_issues += 1"; } ;;
|
|
*) ;;
|
|
esac
|
|
done
|
|
popd >/dev/null
|
|
done
|
|
[ "$warning" = 0 ] || notify.note "\
|
|
-----------------------------
|
|
${NOTE}"$"Hint"":${NORM}
|
|
"$"create desktop files for:"" ${rpmdatadir}/applications
|
|
"$"see:"" <http://www.freedesktop.org/>
|
|
-----------------------------"; }
|
|
test_number=$(($test_number + 1))
|
|
|
|
# check if a package that do not contains binaries is tagged noarch
|
|
test.skip $test_number || {
|
|
notify.note "$(test.num2str). ${NOTE}"\
|
|
$"checking for packages with bad BuildArch tag""${NORM}..."
|
|
|
|
warning=0
|
|
let "i = 0"
|
|
for pck in ${rpmpkg_name[@]}; do
|
|
pushd $tmpextractdir/$i >/dev/null
|
|
for f in $(LC_ALL=C find -mindepth 2 -type f \
|
|
-exec file {} \; 2>/dev/null | grep -E "(\
|
|
ELF | OCaml library | ar archive |\
|
|
dynamically linked | statically linked )"); do
|
|
notify.debug "found binary or library file: \`${NOTE}${f/./}${NORM}'"
|
|
let "warning = 1"
|
|
break
|
|
done
|
|
popd >/dev/null
|
|
done
|
|
if [ "$warning" = 0 ]; then
|
|
[ "$SPEC_BUILDARCH" = "noarch" ] ||
|
|
{ notify.warning "${NOTE}${pck##*/}${NORM}"
|
|
notify.note $"this package should be tagged \`noarch'""
|
|
-----------------------------
|
|
${NOTE}"$"Hint"":${NORM}
|
|
BuildArch: noarch
|
|
-----------------------------"
|
|
let "total_issues += 1"; }
|
|
fi; }
|
|
test_number=$(($test_number + 1))
|
|
|
|
notify.note "
|
|
--> "$"${NOTE}Quality checks: ${#rpmpkg_name[@]} \
|
|
package(s) checked: ${NORM}${WARN}$total_issues${NORM}${NOTE} warning(s).${NORM}""
|
|
"
|
|
}
|