All pastes #2058572 Raw Edit

Untitled

public text v1 · immutable
#2058572 ·published 2011-05-13 21:08 UTC
rendered paste body
#!/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