#!/bin/bash KEEP_TEMP=1 # Readonly variables: declare -r ENV_FILE=0_env.rr # Contains environment variables declare -r FILES_FILE=1_files.rr # Contains a list of files to search declare -r LDPATH_FILE=2_ldpath.rr # Contains the LDPATH declare -r BROKEN_FILE=3_broken.rr # Contains the list of broken files declare -r ERRORS_FILE=3_errors.rr # Contains the ldd error output declare -r RAW_FILE=4_raw.rr # Contains the raw list of packages declare -r OWNERS_FILE=4_owners.rr # Contains the file owners declare -r PKGS_FILE=4_pkgs.rr # Contains the unsorted bare package names declare -r EBUILDS_FILE=4_ebuilds.rr # Contains the unsorted atoms # (Appropriately slotted or versioned) declare -r ORDER_FILE=5_order.rr # Contains the sorted atoms declare -r STATUS_FILE=6_status.rr # Contains the ldd error output # Example: env SEARCH_DIRS="/usr/bin -*" revdep-rebuild will set SEARCH_DIRS # to contain only /usr/bin declare TMP_DIR # Dir containing the generated files. declare CONFIG_DIR # Dir containing config files (usually ${ROOT}/etc). declare SEARCH_DIRS # List of dirs to search for executables and libraries declare SEARCH_DIRS_MASK # List of dirs not to search declare LD_LIBRARY_SEARCH_DIRS # List of dirs to search for dependency libraries declare LD_LIBRARY_MASK # Mask of specially evaluated libraries # Cleanup a variable (duplicate spaces etc. clean_var() { gawk 'BEGIN {RS="[[:space:]]"} /-\*/ {exit} /[^[:space:]]/ {gsub(/\/\/+/, "/"); print}' | sort -u } # Usage: progress i n # i: current item # n: total number of items to process progress() { if [[ -t 1 ]]; then progress() { local curProg=$(( $1 * 100 / $2 )) (( curProg == OLDPROG )) && return # no change, output nothing OLDPROG="$curProg" # must be a global variable (( $1 == $2 )) && local lb=$'\n' echo -ne '\r \r'"[ $curProg% ] $lb" } progress $@ else # STDOUT is not a tty. Disable progress meter. progress() { :; } fi } # Have a nice die command die() { local status=$1 shift eerror "$@" exit $status } # Setup the local environment setup_env() { # Get all the usefull functions like die etc source /etc/init.d/functions.sh # Check for a valid CHOST [[ -n "${CHOST}" ]] || die 1 "No CHOST! variable set!" # Set the ROOT directory ROOT="/usr/${CHOST}" CONFIG_DIR="${ROOT}/etc" # Create a temporary directory and change to it. TMP_DIR=$(mktemp -d --tmpdir="/tmp" "cross-revdep-XXXXXXXX") || die $? "Creating tempdir failed!" cd "${TMP_DIR}" || die $? "Changing to tempdir failed!" [[ -n "${DEBUG}" ]] && einfo "Changing to temporary dir ${TMP_DIR}" } # Setup the paths to search (and filter the ones to avoid) setup_search_paths_and_masks() { local configfile sdir mdir skip_me filter_SEARCH_DIRS einfo "Configuring search environment" # Update the incremental variables using /etc/profile.env, /etc/ld.so.conf, # portage, and the environment # Read the incremental variables from environment and portage # Until such time as portage supports these variables as incrementals # The value will be what is in /etc/make.conf SEARCH_DIRS+=" "$(unset SEARCH_DIRS; portageq envvar SEARCH_DIRS) SEARCH_DIRS_MASK+=" "$(unset SEARCH_DIRS_MASK; portageq envvar SEARCH_DIRS_MASK) LD_LIBRARY_MASK+=" "$(unset LD_LIBRARY_MASK; portageq envvar LD_LIBRARY_MASK) # Add the defaults if [[ -d "${CONFIG_DIR}/revdep-rebuild" ]]; then for configfile in "${CONFIG_DIR}/revdep-rebuild/"*; do SEARCH_DIRS+=" "$(. $configfile; echo $SEARCH_DIRS) SEARCH_DIRS_MASK+=" "$(. $configfile; echo $SEARCH_DIRS_MASK) LD_LIBRARY_MASK+=" "$(. $configfile; echo $LD_LIBRARY_MASK) done else SEARCH_DIRS+=" /bin /sbin /usr/bin /usr/sbin /lib* /usr/lib*" SEARCH_DIRS_MASK+=" /opt/OpenOffice /usr/lib/openoffice" LD_LIBRARY_MASK+=" libodbcinst.so libodbc.so libjava.so libjvm.so" fi # Get the ROOTPATH and PATH from /etc/profile.env if [[ -r "${CONFIG_DIR}/profile.env" && -s "${CONFIG_DIR}/profile.env" ]]; then SEARCH_DIRS+=" "$(. "${CONFIG_DIR}/profile.env"; /usr/bin/tr ':' ' ' <<< "$ROOTPATH $PATH") fi # Get the directories from /etc/ld.so.conf if [[ -r "${CONFIG_DIR}/ld.so.conf" && -s "${CONFIG_DIR}/ld.so.conf" ]]; then SEARCH_DIRS+=" "$(sed '/^#/d;s/#.*$//' "${CONFIG_DIR}/ld.so.conf") fi # Set the final variables SEARCH_DIRS=$(clean_var <<< "$SEARCH_DIRS") SEARCH_DIRS_MASK=$(clean_var <<< "$SEARCH_DIRS_MASK") LD_LIBRARY_SEARCH_DIRS=$(clean_var <<< "$LD_LIBRARY_SEARCH_DIRS") LD_LIBRARY_MASK=$(clean_var <<< "$LD_LIBRARY_MASK") # Filter masked paths from SEARCH_DIRS for sdir in ${SEARCH_DIRS} ; do skip_me= for mdir in ${SEARCH_DIRS_MASK}; do [[ ${sdir} == ${mdir}/* ]] && skip_me=1 && break done [[ -n ${skip_me} ]] || filter_SEARCH_DIRS+=" ${ROOT}/${sdir}" done SEARCH_DIRS=$(clean_var <<< "${filter_SEARCH_DIRS}") [[ $SEARCH_DIRS ]] || die 1 "No search defined -- this is a bug." echo "CONFIG_DIR=${CONFIG_DIR}" > "${ENV_FILE}" echo "SEARCH_DIRS=(${SEARCH_DIRS[@]})" >> "${ENV_FILE}" echo "LD_LIBRARY_MASK=(${LD_LIBRARY_MASK[@]})" >> "${ENV_FILE}" einfo "Generated new ${ENV_FILE}" } clean_exit() { if [[ ! $KEEP_TEMP ]]; then cd - > /dev/null rm -rf "${TMP_DIR}" fi einfo "" einfo "Dynamic linking on your system is consistent... All done. " exit 0 } # Finds all ELF files among the executables and libraries # in the search directory list. get_files() { einfo "Collecting system binaries and libraries" find ${SEARCH_DIRS[@]} \ -type f \ \( -perm /111 -o -name '*.so' -o -name '*.so.*' -o -name '*.la' \) \ -fprint "${FILES_FILE}" \ > /dev/null 2>&1 einfo "Generated new ${FILES_FILE}" } get_ldpath() { einfo 'Collecting complete LD_LIBRARY_PATH' # Ensure that the "trusted" lib directories are at the start of the path LD_LIBRARY_SEARCH_DIRS+="${ROOT}/lib ${ROOT}/usr/lib" LD_LIBRARY_SEARCH_DIRS+=" "$(sed '/^#/d;s/#.*$//' < "${CONFIG_DIR}/ld.so.conf") LD_LIBRARY_SEARCH_DIRS=$(clean_var <<< "$LD_LIBRARY_SEARCH_DIRS") echo "$LD_LIBRARY_SEARCH_DIRS" > "$LDPATH_FILE" einfo "Generated new $LDPATH_FILE" } # Check if a single file is ok check_file() { local file="$1" [[ -n "${DEBUG}" ]] && einfo "Checking $file" # Get the required dependencies out of the ELF file and # try to find these dependencies in the LD_LIBRARY_SEARCH_DIRS! DEPENDENCIES=$(${CHOST}-readelf -d "${file}" | \ grep "(NEEDED)" | \ sed -e 's/^.*\[//' -e 's/\].*$//') status="ok" for dep in $DEPENDENCIES ; do # Search the dependency and stop at first occurance ret=$(find -L ${LD_LIBRARY_SEARCH_DIRS[@]} \ -type f \ -name ${dep} \ -print \ -quit \ 2>/dev/null) if [ -z "$ret" ]; then status="broken" break fi done # We found a broken files. Spit this file out and add it to broken. if [ "$status" != "ok" ] ; then eindent ewarn "broken ${file} (requires $dep)" eoutdent echo "$file" >> "${BROKEN_FILE}" fi } check_linking() { local i local total einfo "Checking dynamic linking" rm -f ${BROKEN_FILE} || die $? "Unable to remove $BROKEN_FILE" # Get the total number of files total=$(wc -l "${FILES_FILE}" | cut -d' ' -f1) # Check all found elf files i=0 cat "${FILES_FILE}" | while read file; do # Only process the file if it is an ELF file. file "${file}" | grep -qF "${file}: ELF " [[ "$?" == "0" ]] && check_file "${file}" # Increment progressbar let "i++" progress ${i} ${total} done # If no broken file exists, everything is fine. [[ -f "${BROKEN_FILE}" ]] || clean_exit einfo "Generated new $BROKEN_FILE" } get_file_owners() { export ROOT einfo 'Assigning files to packages' rm -f ${RAW_FILE} || die $? "Unable to remove $RAW_FILE" rm -f ${OWNERS_FILE} || die $? "Unable to remove $OWNERS_FILE" # Get the owners of the broken files eindent cat "${BROKEN_FILE}" | while read file; do pkg=$(qfile --nocolor --exact --root-prefix "${file}" | cut -d' ' -f1) [[ "$?" != "0" ]] && pkg="" [[ -n "${pkg}" ]] && echo "=${pkg}" >> "${RAW_FILE}" [[ -n "${pkg}" ]] || ewarn "${file} not owned by any package" [[ -n "${pkg}" ]] || pkg="(none)" echo "${file} -> ${pkg}" >> "${OWNERS_FILE}" done eoutdent einfo "Generated new $RAW_FILE and $OWNERS_FILE" } clean_packages() { einfo 'Cleaning list of packages to rebuild' sort -u "$RAW_FILE" > "$EBUILDS_FILE" einfo "Generated new $EBUILDS_FILE" } rebuild() { einfo 'All prepared. Starting rebuild' REBUILD_LIST=$(tr '\n' ' ' < "$EBUILDS_FILE") echo "emerge-${CHOST} --keep-going ${EMERGE_OPTIONS[@]} ${REBUILD_LIST}" exec emerge-${CHOST} --keep-going ${EMERGE_OPTIONS[@]} ${REBUILD_LIST} } # Setup the script environment setup_env # Setup the search directories setup_search_paths_and_masks # Find all ELF files among the executables and libraries # in the search directory list. get_files # Get the path where we search for the dependency libraries get_ldpath # Check the files check_linking # Get the owners of the broken files and clean the package list get_file_owners clean_packages # Finally rebuild the packages rebuild