#!/bin/bash
# NOTE: As you see above, the shebang points to /bin/dash. If you have no Dash shell
# installed, change it to read #!/bin/bash or whatever Bourne-type shell you like.
# Author: Sasha Alexandr (GrapefruiTgirl) March 2010
# DISCLAIMER/LICENSE: GNU GPL v3 or greater.
# This script is provided with ABSOLUTELY NO WARRANTY in the hope
# that you will find it useful and/or entertaining.
# While the script does not change your system in any way
# the author takes no responsibility for how you use it, or how you
# might use the results produced by it. Use at own risk.
# You may modify or redistribute this program as you like.
version () {
VERSION='Version 1.07'
echo "$VERSION"
}
######################
# BUGFIXES & ChangeLog:
# BUGFIX: grep problem with cxxlibs libs on Slack13/32
#
# Version 1.01 - March 09 2010:
#-- Output the NVIDIA driver blurb only if nvidia.ko is present on the machine.
#-- Check if we're root; if not, quit due to inadequate read/access permissions.
#-- Break some long lines using \'s. Clear screen at start.
# Version 1.02 - March 10 2010:
#-- Adding checking for binaries missing dependencies.
#-- Making two modes of operation: default & 'ridiculous'
# you can pass the 'ridiculous' option to the script, and 'Round 3' will search
# a lot more directory paths for missing dependencies.
#-- Also fixed visual progress display - it displays dots now and will
# not print stuff to the screen that confuses people (including myself).
#-- Formatted the output.log in a slightly more greppable fashion (I hope you like that!)
#-- Don't bother clearing screen at startup.
#-- Put everything into functions.
# Version 1.03 - skipped.
# Version 1.04 - March 12 2010:
#-- Added checking for orphan binaries (now Round 3)
#-- Added --version switch and --help switch
#-- changed mode switches to --basic and --full (instead of ridiculous or not)
#-- tuned up the search-path identifying code (no more using $PATH for bin dirs)
#-- by popular demand, the orphans.log file is now shown automatically upon completion.
#-- also by popular demand, the shebang now points to /bin/sh
# Version 1.05 - March 13 2010:
#-- remove /boot/initrd*/bin from $BINDIRS
#-- modify sanity-checking of libs/binaries identified in rounds 2+3
# because the filename filtering is not perfect. Avoid non-[ELF|lib|ar] files.
#-- remove .pc files from the search (what was I thinking?! Silly ASCII files.)
#-- double-check that libs found in round2 are not in the /incoming/ folder in the
# */packages/log file -- glibc-solibs are like this, causing false positives.
#-- lessen the use of `basename` -- use only where necessary (slight speed improvement)
#-- make the progress display more meaningful (but still slightly vague)
# Version 1.05.1 - later the same day..
#-- tiny correction of counting of the paths. very tiny. does not affect functionality.
# Version 1.06 - March 14 2010
#-- more bug fixes -- some paths not being checked.
#-- remove /boot/initrd* folders from search paths.
# Version 1.07 - March 17 2010
#-- add new mode & switch: --verify
# This function checks that every single file contained in every allegedly installed
# package, is actually present on the system where it is supposed to be.
# The function ignores doinst.sh scripts and slack-desc files, and a few other things.
# Read the comments verify_package_contents() for full ignore list.
######################
check_permissions () {
# Check permissions (are we root?)
if [ "$(id -u)" != '0' ]; then
echo "Please su to root or use sudo to run this script. Exiting now."
exit 1
fi
}
setup_basic () {
# Simulation has no relevance at this time. Your system will not be altered.
# This script currently just produces an output file for you to peruse.
# You decide what to do with the results.
# If $SIMULATION='true' nothing will be changed or deleted:
SIMULATION='true'
# Kinda irrelevant/overkill, but where is ldconfig:
LDCONFIG=$(which ldconfig)
# put the results in this file (in the CWD):
# when using --verify, log is called 'orphans.verify.log'; else just 'orphans.log'
if [ "$1" = 'verify' ]; then
[ "$(pwd)" = '/' ] && RESULT=orphans.verify.log || RESULT=$(pwd)/orphans.verify.log
else
[ "$(pwd)" = '/' ] && RESULT=orphans.log || RESULT=$(pwd)/orphans.log
fi
# create fresh output file:
echo
echo "## orphans.sh $(version) for Slackware." | tee "$RESULT"
echo "## Executed: $(date)" | tee -a "$RESULT"
echo "## Mode of execution: $MODE" | tee -a "$RESULT"
echo "## Results will be outputted to: $RESULT"
echo "## ##########################################" >> "$RESULT"
echo >> "$RESULT"
}
setup_ridiculous () {
printf "## Setting up, please wait.."
case $1 in ridiculous)
# if $1 == 'ridiculous' then add these paths to the search
# NOTE: may produce a lot of results!!
LIB64DIRS=$( find / -type d -name 'lib64' 2>/dev/null | sed \
-e '/^\/boot\/initrd-tree\/.*$/d' \
-e '/^\/mnt\|tmp\|media\/.*$/d' \
-e '/^\/usr\/src\/.*$/d' \
-e '/^\/usr\/source\/.*$/d' )
printf "."
LIB32DIRS=$( find / -type d -name 'lib' 2>/dev/null | sed \
-e '/^\/boot\/initrd-tree\/.*$/d' \
-e '/^\/mnt\|tmp\|media\/.*$/d' \
-e '/^\/usr\/src\/.*$/d' \
-e '/^\/usr\/source\/.*$/d' \
-e '/^\/usr\/libexec\/.*$/d' \
-e '/^\/usr\/doc\/.*$/d' \
-e '/^\/lib\/modules\/.*$/d' \
-e '/^\/var\/cache\/.*$/d' \
-e '/\/lib64\/.*$/d' )
printf "."
LIBEXECDIRS=$( find / -type d -name 'libexec' 2>/dev/null )
;;
esac
echo "."
# if $1 != 'ridiculous' then only check these bin paths
# and/or ld.so.conf known paths:
BINDIRS=$( find / -type d -name 'bin' -or -name 'sbin' 2>/dev/null | sed \
-e '/^\/mnt\|tmp\|media\/.*$/d' \
-e '/^\/usr\/src\/.*$/d' \
-e '/^\/boot\/initrd-tree\/.*$/d' \
-e '/^\/usr\/source\/.*$/d' )
echo "## Done!"
}
count_packages () {
# FYI, this many packages are apparently installed:
NUMPKGS=$( ls /var/log/packages | wc -l )
echo "## Count installed packages: $NUMPKGS" | tee -a "$RESULT"
}
broken_links () {
# round 1-- See if there are links pointing to non-existant libraries.
# This only checks stuff that the linker knows about.
echo >> "$RESULT"
echo "## Round 1/4 = Quick check for broken symlinks that might indicate missing libs.." | tee -a "$RESULT"
$LDCONFIG 1>/dev/null 2>&1
for LIB in $(ldconfig -v 2>&1 >/dev/null | grep 'Cannot stat' | awk '{print $4}' | tr -d ':'); do
# this part is fast: no visual progress display.
echo "1: Broken link to: $LIB" >> "$RESULT"
printf "1:> Possibly comes from package:" >> "$RESULT"
PKG="$(grep "$LIB" /var/log/packages/*)"
[ ! "$PKG" ] && echo " Unknown." >> "$RESULT" || \
printf "\n$(echo "$PKG" | cut -d: -f1 | awk -F/ '{print $5}').t?z\n" >> "$RESULT"
done
echo "## Done!" | tee -a "$RESULT"
}
orphan_stuff () {
# rounds 2+3 -- search lib dirs to see if every library
# on the machine belongs to an installed package:
echo >> "$RESULT"
echo "## Round 2/4 -- Checking for orphan libraries belonging to no installed package.." | tee -a "$RESULT"
# if there's an nvidia driver, put this into the output file:
if [ "$(find /lib/modules/$(uname -r) -name nvidia.ko)" ]; then
cat << END >> "$RESULT"
###########################################################
# WARNING! NVIDIA driver is present for the running kernel.
# If you've installed it using the nvidia driver installer
# instead of pkgtool, you will see "orphaned" files
# which do belong to the driver! Don't go crazy on them.
# They tend to be located in several locations, but here are
# some examples of NVIDIA driver filenames you might see:
# (NOTE: version numbers will vary)
# Orphaned library: /usr/lib64/libGLcore.so.190.53
# Orphaned library: /usr/lib64/libGL.so.190.53
# Orphaned library: /usr/lib64/libXvMCNVIDIA.so.190.53
# Orphaned library: /usr/lib64/libcuda.so.190.53
# Orphaned library: /usr/lib64/libglx.so.190.53
# Orphaned library: /usr/lib64/libnvidia-cfg.so.190.53
# Orphaned library: /usr/lib64/libnvidia-tls.so.190.53
# Orphaned library: /usr/lib64/libnvidia-wfb.so.190.53
# Orphaned library: /usr/lib64/libvdpau.so.190.53
# Orphaned library: /usr/lib64/libvdpau_nvidia.so.190.53
# Orphaned library: /usr/lib64/libvdpau_trace.so.190.53
# Orphaned library: /usr/lib64/nvidia_drv.so
# Orphaned library: /usr/lib64/tls/libnvidia-tls.so.190.53
###########################################################
END
fi
DIRCOUNT=$((1)); LIBINCR=$((0))
[ "$1" = 'ridiculous' ] && \
ALLDIRS="$LIB64DIRS
$LIB32DIRS
$LIBEXECDIRS" || ALLDIRS=$(cat /etc/ld.so.conf)
NUMDIRS=$(echo "$ALLDIRS" | wc -l)
echo "$ALLDIRS" | sort | uniq | \
while read DIR; do
if [ "$DIR" ] && [ -d "$DIR" ]; then
echo "# Evaluating library files in: $DIR" >> $RESULT
LIBCOUNT=$(find "$DIR" -type f | egrep '*\.a$|*\.la$|*\.so|*\.so\.*$' | uniq | wc -l)
for LIB in $(find "$DIR" -type f | egrep '*\.a$|*\.la$|*\.so|*\.so\.*$' | sort | uniq); do
LIBINCR=$((LIBINCR+1))
# visual progress display:
DIRPROGRESS="$DIRCOUNT/$NUMDIRS"; LIBPROGRESS="$((LIBINCR*100/LIBCOUNT))"
printf %b "\r## Path: $DIRPROGRESS Done: $LIBPROGRESS% "
if [ -x "$LIB" ] && [ $(file "$LIB" | grep -c 'ELF\|current ar archive\|libtool library file') -ne 0 ]; then
if [ ! "$(grep "$(echo "$LIB" | sed 's/^\///')" /var/log/packages/*)" ] && \
[ ! "$(grep "$(echo "$DIR" | sed 's/^\///')/incoming/$(basename "$LIB")" /var/log/packages/*)" ]; then
echo "2:> Orphan library file: $LIB" >> "$RESULT"
fi
fi
done
fi
DIRCOUNT=$((DIRCOUNT+1)); LIBINCR=$((0))
done
echo "## Done!" >> "$RESULT"
printf %b "\r## Paths: $NUMDIRS/$NUMDIRS Done! \n"
# round 3/4 -- same as above, but evaluates BINARIES not LIBRARIES.
echo >> "$RESULT"
echo "## Round 3/4 -- Checking for orphan binaries belonging to no installed package.." | tee -a "$RESULT"
DIRCOUNT=$((1)); BININCR=$((0))
ALLDIRS="$BINDIRS
$LIB64DIRS
$LIB32DIRS
$LIBEXECDIRS"
NUMDIRS=$(echo "$ALLDIRS" | wc -l)
echo "$ALLDIRS" | while read DIR; do
if [ "$DIR" ] && [ -d "$DIR" ]; then
echo "# Evaluating binaries in: $DIR" >> $RESULT
BINCOUNT=$(find "$DIR" -type f | egrep -v '*\.a$|*\.la$|*\.so|*\.so\.*$|\[' | uniq | wc -l)
for BINARY in $(find "$DIR" -type f | egrep -v '*\.a$|*\.la$|*\.so|*\.so\.*$|\[' | sort | uniq); do
BININCR=$((BININCR+1))
# visual progress display:
DIRPROGRESS="$DIRCOUNT/$NUMDIRS"; BINPROGRESS="$((BININCR*100/BINCOUNT))"
printf %b "\r## Path: $DIRPROGRESS Done: $BINPROGRESS% "
if [ -x "$BINARY" ] && [ $(file "$BINARY" | grep -c 'ELF') -ne 0 ]; then
[ ! "$(grep "$(echo "$BINARY" | sed 's/^\///')" /var/log/packages/*)" ] && \
echo "3:> Orphan binary file: $BINARY" >> "$RESULT"
fi
done
fi
DIRCOUNT=$((DIRCOUNT+1)); BININCR=$((0))
done
echo "## Done!" >> "$RESULT"
printf %b "\r## Paths: $NUMDIRS/$NUMDIRS Done! \n"
}
dependency () {
# round 4/4
# Checks for missing dependencies of installed binaries and other libs on the system.
# The --full mode checks every lib & bin dir it can find on the system
# instead of just the /bin dirs. Might be overkill - you decide.
echo >> "$RESULT"
echo "## Round 4/4 -- Checking for missing dependencies of installed libs/binaries.." | tee -a "$RESULT"
DIRCOUNT=$((1)); ITEMINCR=$((0))
# NUMDIRS & ALLDIRS are still set from above.
echo "$ALLDIRS" | while read DIR; do
if [ "$DIR" ] && [ -d "$DIR" ]; then
echo "# Evaluating objects in: $DIR" >> $RESULT
ITEMCOUNT=$(find "$DIR" -type f | sort | uniq | wc -l)
for FILE in $(find "$DIR" -type f | sort | uniq); do
ITEMINCR=$((ITEMINCR+1))
# visual progress display:
DIRPROGRESS="$DIRCOUNT/$NUMDIRS"; ITEMPROGRESS="$((ITEMINCR*100/ITEMCOUNT))"
printf %b "\r## Path: $DIRPROGRESS Done: $ITEMPROGRESS% "
if [ -x "$FILE" ] && [ $(file "$FILE" | grep -c 'ELF') -ne 0 ]; then
if [ $(ldd "$FILE" 2>/dev/null | grep -c 'not found') -ne 0 ]; then
echo "4: Missing dependencies for: $FILE" >> "$RESULT"
echo "$(for i in $(ldd "$FILE" | grep 'not found' | sort | uniq | awk '{print $1}'); do
echo "4:> Missing: $i"; done)" >> "$RESULT"
fi
fi
done
DIRCOUNT=$((DIRCOUNT+1)); ITEMINCR=$((0))
fi
done
echo "## Done!" >> "$RESULT"
printf %b "\r## Paths: $NUMDIRS/$NUMDIRS Done! \n"
}
verify_package_contents () {
# checks that all files listed in the package install-log are really somewhere.
# devs- and sysvinit- packages are ignored
# .new files are ignored
# /install/* files are ignored
# /var/cache/* is skipped; nothing important there for these purposes.
# *src2pkg.auto build scripts are skipped
# /incoming/ folder is compensated for (glibc et al.)
echo >> "$RESULT"
PKGPROGRESS=$((1))
find /var/log/packages -type f | sort | uniq | less | \
sed -e '/^\/var\/log\/packages\/devs-.*$/d' \
-e '/^\/var\/log\/packages\/sysvinit-.*$/d' | \
while read PACKAGELOG; do
# visual progress display:
printf %b "\r## Examining package: $PKGPROGRESS / $NUMPKGS"
cat "$PACKAGELOG" | \
sed -e '/^\.\/$/d;/^install\/.*/d' \
-e '/^PACKAGE\|UNCOMPRESSED\|COMPRESSED\|.*:\|FILE LIST:/d' \
-e '/.*\.new$/d' \
-e '/^var\/cache\/.*$/d' \
-e '/.*\.src2pkg\.auto$/d' \
-e 's:/incoming/:/:' | \
while read ITEM; do
[ ! -e "/$ITEM" ] && echo "v: $(basename $PACKAGELOG) -- missing /$ITEM" >> "$RESULT" # && break
done
PKGPROGRESS=$((PKGPROGRESS+1))
done
printf "\r## Examined packages: $NUMPKGS / $NUMPKGS\n"
echo "## Done!" | tee -a "$RESULT"
cat "$RESULT" | grep v: | awk '{print $2}' | uniq | sed '/^$/d' > "$RESULT.packages"
echo
echo "## A plain list of affected packages is saved as $RESULT.packages"
echo "## You can use that to automate reinstallation of these packages if you wish."
}
help () {
cat << END
# orphans.sh
# A tool for Slackers. Helps identify broken dependencies. Finds libraries
# and binaries on the machine that belong to no known installed package.
# Identifies installed packages whose contents are missing from the system.
# Author: Sasha Alexandr <crawlingwithsnakes at hush dot com>
# Created: March 2010
#
# USAGE:
$0 -h|--help ( shows this help info you are reading right now )
$0 -v|--version ( show which version of this script you have )
$0 -b|--basic ( the less exhaustive scan for messed up stuff )
$0 -f|--full ( the more exhaustive scan for messed up stuff )
$0 --verify ( check that contents of all installed packages
are really installed )
# --basic and --full work similarly, but --full searches in more locations.
# --verify is independent of the other two modes.
# That's all for now folks!
END
}
# script entry point:
# use --help for usage instructions.
case "$1" in
version|-v|--v) version && exit 0 ;;
help|usage|-h|--h|-usage|--usage|'?') help && exit 0 ;;
--basic|-basic|basic|-b|--b|--full|-full|full|-f|--f)
[ "$(echo -- "$1" | grep 'b')" ] && MODE=basic || MODE=ridiculous
check_permissions
setup_basic ogopogo
setup_ridiculous $MODE
count_packages
# round 1
broken_links
# round 2 & 3
orphan_stuff $MODE
# round 4
dependency
;;
--verify)
MODE='verify'
check_permissions
setup_basic $MODE
count_packages
verify_package_contents
;;
*) help && exit 1 ;;
esac
# Next lines automatically puts the results file to the screen via 'less':
echo
echo "shell# cat $RESULT | less"
sleep 2 && cat "$RESULT" | less
#EO