#!/bin/bash # # portage-check-files # # - Ensure that all nominally-installed packages on a portage-based # system (e.g. Gentoo) are actually installed. # Any missing files will be flagged, and the option to remerge given. # # Features: Option of using epm or qpkg (or now even equery) # # Initial Version 20031213 - Stuart Shelton [SRCS] # # Revision History # ---------------- # 20031213 SRCS Initial Version (chkpkg) # 20040405 SRCS Rewrote detection routine to use "read" to fix the spaces problem # 20040407 SRCS Removed use of pipe from above, due to sync problems # 20040718 SRCS Renamed to portage-check-files, added logging, added equery support # # TODO: # equery can check MD5 sums, qpkg can list them - we should probably offer to check them # Command-line parsing is getting a little silly here: Switch to getopt # # set -o xtrace [ -n "$( echo ${*} | grep -E -- "^--debug$|^--debug | --debug$| --debug |-.*d" )" ] && set -o xtrace NAME="$( echo ${0} | sed "s#^.*/##g" )" COMMAND=epm VERBOSE=0 SEARCH=0 NOFILTER=0 NOLOG=0 FIFO=${TMP:-/tmp}/$NAME.fifo LOG=${TMP:-/tmp}/$NAME.log function fatalError() { [ -e $FIFO ] && rm $FIFO echo "$@" exit 1 } function dohelp() { echo -e "${NAME}:\n\tCheck that all installed packages for missing files\n" echo -e "\tOptions:" echo -e "\t--help\t\t\t\t-h\t\t\tDisplay this help text" echo -e "\t--debug\t\t\t\t-d\t\t\tPrint debugging information" echo -e "\t--verbose\t\t\t-v\t\t\tPrint large amounts of output" echo -e "\t--search\t\t\t-s\t\t\tSearch for moved files" echo -e "\t--nofilter\t\t\t-f\t\t\tDon't filter out common removals" echo -e "\t--nolog\t\t\t\t-l\t\t\tDon't log missing files to $LOG" echo -e "\t--command[=]\t-c[=]\tUse specified command to discover packages" echo -e "\n\t... where = " echo -e "\t \"epm\" is now the default" } # Parse Arguments... [ -n "$( echo ${*} | grep -Ei -- "^--help$|^--help | --help$| --help |-.*h" )" ] && { dohelp ; exit 0 ; } [ -n "$( echo ${*} | grep -Ei -- "^--debug$|^--debug | --debug$| --debug |-.*d" )" ] && set -o xtrace [ -n "$( echo ${*} | grep -Ei -- "^--verbose$|^--verbose | --verbose$| --verbose |-.*v" )" ] && VERBOSE=1 [ -n "$( echo ${*} | grep -Ei -- "^--search$|^--search | --search$| --search |-.*s" )" ] && SEARCH=1 [ -n "$( echo ${*} | grep -Ei -- "^--nofilter$|^--nofilter | --nofilter$| --nofilter |-.*f" )" ] && NOFILTER=1 [ -n "$( echo ${*} | grep -Ei -- "^--nolog$|^--nolog | --nolog$| --nolog |-.*l" )" ] && NOLOG=1 [ -n "$( echo ${*} | grep -Ei -- "^--command[= ]epm$|^--command[= ]epm | --command[= ]epm$| --command[= ]epm |-.*c[= ]epm$|-.*c[= ]epm " )" ] && COMMAND=epm [ -n "$( echo ${*} | grep -Ei -- "^--command[= ]qpkg$|^--command[= ]qpkg | --command[= ]qpkg$| --command[= ]qpkg |-.*c[= ]qpkg$|-.*c[= ]qpkg " )" ] && COMMAND=qpkg [ -n "$( echo ${*} | grep -Ei -- "^--command[= ]equery$|^--command[= ]equery | --command[= ]equery$| --command[= ]equery |-.*c[= ]equery$|-.*c[= ]equery " )" ] && COMMAND=equery LISTCMD="exit 0" FILECMD="exit 0" EQUALS="" case $COMMAND in equery) LISTCMD="equery -q -C list -i \* | sed 's#^.*/##g' | grep -v \"^$\" | cut -d\" \" -f1" FILECMD="equery -q -C files" EQUALS="=" ;; qpkg) LISTCMD="qpkg -nc -v -I | sed 's#^.*/##g'" FILECMD="qpkg -nc -l" EQUALS="" ;; epm) LISTCMD="epm -qa" FILECMD="epm -ql" EQUALS="" ;; *) echo "Error: Please specify the command to use with --command" exit 1 ;; esac if [ -z "$( type -p $COMMAND )" ]; then echo "Error: Cannot locate $COMMAND" exit 1 fi (( 1 == VERBOSE )) && echo "Using $( type -p $COMMAND ) to evaluate ebuilds..." [ -e $FIFO ] && rm -f $FIFO touch $FIFO [ -f $FIFO ] || fatalError "Cannot create or read from file $FIFO" [ -e $LOG ] && rm -f $LOG touch $LOG [ -f $LOG ] || fatalError "Cannot create or read from file $LOG" echo "Building package list..." >&2 # Bulid list of installed packages... PACKAGES=$( eval $LISTCMD ) echo "Checking ebuild contents..." >&2 (( 1 == VERBOSE )) && echo -e "Package List:\n$PACKAGES\n" for PACKAGE in $PACKAGES; do (( 1 == VERBOSE )) && echo "Processing $PACKAGE:" # Now get a list of all of the files installed by this package... # NB: Do this in a sub-shell: equery sends EOF :( ( exec $FILECMD $EQUALS$PACKAGE &>$FIFO ; ) [ "$COMMAND" = "qpkg" ] && echo $FIFO | cut -d" " -f1-3 | grep -Ev -- "^$|^CONTENTS:$|^.*/$PACKAGE-" >$FIFO while read FILE; do # Ignore the 2nd half of a symlink # (It'd probably make more sense to look at the 2nd half, but this could be relative) [ -n "$( echo $FILE | grep -- "->" )" ] && FILE="$( echo $FILE | cut -d" " -f 1 )" ls -1d "$FILE" >/dev/null 2>&1 if (( 0 == ${?} )); then (( 1 == VERBOSE )) && echo "Found $FILE" else FILEMISSING=1 if (( 0 == NOFILTER )); then # The ..'s below are not accidentally unescaped path elements - they should match language files... if [ -n "$( echo $FILE | \ grep -E -- "/\.keep$|man/../man.$|man/.._../man.$|man/../man./[^/]*\..\.gz|man/.._../man./[^/]*\..\.gz|^/usr/src/|^/lib/modules" \ )" ]; then (( 1 == VERBOSE )) && echo "Filtering missing file $FILE" FILEMISSING=0 fi fi if (( 1 == FILEMISSING )); then echo "Error: $PACKAGE missing $FILE" (( 0 == NOLOG )) && echo -n "$PACKAGE:$FILE" >> $LOG if (( 1 == SEARCH )); then FILESRCH="$( echo $FILE | sed "s#^.*/##g" )" echo "Finding alternatives:" locate $FILESRCH RESULT=$? if (( 0 == NOLOG )); then case $RESULT in 0) echo -en "\n" >> $LOG ;; 1) echo " *" >> $LOG ;; *) echo " !" >> $LOG ;; esac fi echo unset FILESRCH RESULT fi fi unset FILEMISSING fi done < $FIFO done [ -e $FIFO ] && rm -f $FIFO exit 0