#!/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 # 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