#!/bin/sh # Copyright 1999-2007 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 appname=${0##*/} # If baselayout is broken, define our own functions [ -r /etc/init.d/functions.sh ] && . /etc/init.d/functions.sh if ! type eend >/dev/null 2>&1 || ! eend 0 >/dev/null 2>&1; then einfo() { echo " * $*"; } eerror() { echo " * $*" >&2; return 1; } eindent() { :; } eoutdent() { :; } fi # No temporary files used, so nothing to clean up :) trap "export RC_EINDENT=; echo; eerror 'Caught interrupt'; exit 1" \ SIGINT SIGQUIT print_usage() { cat << EOF Usage: ${appname} [OPTIONS] [--] [EMERGE_OPTIONS] Broken reverse dependency rebuilder. -h, --help Print this usage -e, --exact Emerge based on exact package version -C, --nocolor Turn off colored output -L, --library NAME Emerge existing packages that use the library with NAME --library=NAME NAME can be a full path to the library or a basic regular expression (man grep) Calls emerge, all other options are used for it (e. g. -p, --pretend). Report bugs to EOF } # Have we linked to this library? elf_linked() { local f=$1 shift while [ -n "$1" ]; do ldd "${f}" 2>/dev/null | grep -q "=> $1 " && return 0 shift done return 1 } # Work out of we really need this library or not elf_needed() { local f=$1 shift while [ -n "$1" ]; do objdump -p "${f}" 2>/dev/null | \ grep -vF "${ld_mask:=$'\a'}" | \ grep -q "^ NEEDED ${1##*/}" && return 0 shift done return 1 } elf_broken() { local lib= for lib in $(ldd "$1" 2>/dev/null | \ sed -n -e 's/[[:space:]]*\(.*\) => not found.*/\1/p'); do if elf_needed "$1" "${lib}"; then echo "(missing ${lib})" return 0 fi done return 1 } # Check that all direct files exist in .la files la_broken() { local x= for x in $(sed -n -e "s/^dependency_libs=\'\(.*\)'\$/\1/p" "$1"); do case "${x}" in /*) if [ ! -e "${x}" ]; then echo "(missing ${x})" return 0 fi ;; esac done return 1 } # Return a $PATH style variable based on ld.so.conf read_so_conf() { local line= while read line; do case "${line}" in "#"*) ;; *) printf ":%s" "${line}";; esac done < /etc/ld.so.conf } # Check to see if we have already scanned a dir or not scanned() { local dir=$1 IFS=: set -- ${scanned} while [ -n "$1" ]; do [ "$1" = "$dir" ] && return 0 shift done scanned="${scanned}${scanned:+:}${dir}" return 1 } # Hit the portage vdb to work out our ebuilds # If everything is 100% then this happens in one very fast pass # Otherwise we have to take the slow approach to inform the user which files # are orphans get_exact_ebuilds() { local regex= ebuilds= x= IFS=: set -- $@ IFS=" " # Hit the vdb in one go - this is fast! regex=$(printf "%s|" "$@") regex=${regex%*|} find /var/db/pkg -name CONTENTS | \ xargs egrep "^obj (${regex}) " | \ sed -e 's,/var/db/pkg/\(.*\/.*\)/CONTENTS:.*,=\1,g' | \ tr '\n' ' ' } # Get our args libs= exact=false order=true while [ -n "$1" ]; do case "$1" in --*=*) arg1=${1%%=*} arg2=${1#*=} shift set -- ${arg1} ${arg2} $@ continue ;; -h|--help) print_usage; exit 0;; -L|--library|--soname|--soname-regexp) if [ -z "$2" ]; then eerror "Missing expected argument to $1" exit 1 fi libs="${libs}${libs:+ }$2" shift ;; -e|--exact) exact=true;; -X|--package-names) ;; #compat --) shift; emerge_opts="$@"; break;; *) eerror "$0: unknown option $1"; exit 1;; esac shift done einfo "Configuring search environment for ${appname}" # OK, this truely sucks. Paths can have spaces in, but our config format # is space separated? sdirs=$(unset SEARCH_DIRS; portageq envvar SEARCH_DIRS) sdirs_mask=$(unset SEARCH_DIRS_MASK; portageq envvar SEARCH_DIRS_MASK) ld_mask=$(unset LD_LIBRARY_MASK; portageq envvar LD_LIBRARY_MASK) if [ -d /etc/revdep-rebuild ]; then for x in /etc/revdep-rebuild/*; do sdirs="${sdirs}${sdirs:+ }$(unset SEARCH_DIRS; . "${x}"; echo "${SEARCH_DIRS}")" sdirs_mask="${sdirs_mask}${sdirs_mask:+ }$(unset SEARCH_DIRS_MASK; . "${x}" ; echo "${SEARCH_DIRS_MASK}")" ld_mask="${ld_mask}${ld_mask:+ }$(unset LD_LIBRARY_MASK; . "${x}"; echo "${LD_LIBRARY_MASK}")" done else sdirs="${sdirs}${sdirs:+ }/bin /sbin /usr/bin /usr/sbin /lib* /usr/lib*" sdirs_mask="${sdirs_mask}${sdirs_mask:+ }/opt/OpenOffice /usr/lib/openoffice" ld_mask="${ld_mask}${ld_mask:+ }libodbcinst.so libodbc.so libjava.so libjvm.so" fi sdirs=$(find ${sdirs} -type d) einfo "Starting scan" eindent # Mark our masked dirs already scanned scanned= for dir in ${sdirs_mask}; do scanned "${dir}" done # Now scan our dirs for dir in ${sdirs}; do scanned "${dir}" && continue einfo "in ${dir}" eindent for x in "${dir}"/*; do [ -d "${x}" ] && continue [ -L "${x}" ] && continue scan=true process=false reason= case "${x}" in *.so|*.so.*) process=true;; *.la) scan=false if [ -z "${libs}" ]; then reason=$(la_broken "${x}") [ $? = 0 ] && process=true fi ;; esac [ -x "${x}" ] && ${scan} && process=true ${process} || continue if ${scan}; then process=false if [ -n "${libs}" ]; then for lib in ${libs}; do if [ "${lib#/}" != "${lib}" ]; then # If lib starts with / then check if the exact # lib is linked elf_linked "${x}" "${lib}" || continue fi if elf_needed "${x}" ${lib}; then process=true break fi done else reason=$(elf_broken "${x}") [ $? = 0 ] && process=true fi fi ${process} || continue einfo "found ${x} ${reason}" files="${files}${files:+:}${x}" done eoutdent done eoutdent if [ -z "${files}" ]; then if [ -z "${libs}" ]; then einfo "Nothing found that needs rebuilding" else einfo "No dynamic binaries found with these libraries" fi exit 0 fi einfo "Assigning files to packages" eindent ebuilds=$(get_exact_ebuilds "${files}") if [ -z "${ebuilds}" ]; then eerror "No packages own these files" exit 1 fi # Work out the best visible package for the slot if ! ${exact}; then root=$(portageq envvar ROOT) root=${root:-/} set -- ${ebuilds} ebuilds= for x in "$@"; do x=${x#=*} pkg=${x%-r[0-9]*} pkg=${pkg%-*} slot=$(cat "/var/db/pkg/${x}/SLOT") ebd=$(portageq best_visible "${root}" "${pkg}:${slot}") if [ -z "${ebd}" ]; then eerror "Cannot find an ebuild visible for ${x}" else ebuilds="${ebuilds}${ebuilds:+ }=${ebd}" fi done fi eoutdent # Work out the build order if ${order}; then einfo "Ordering packages" order="$(EMERGE_DEFAULT_OPTS="" \ emerge --nospinner --pretend --deep --quiet ${ebuilds})" if [ $? = 0 ]; then ebuilds=$(echo "${order}" | \ sed -e 's:^\[.*\] \([^ ]*\)[ ].*$:=\1:' | \ grep -F "$(printf "%s\n" ${ebuilds})" | \ tr '\n' ' ') else eerror "Unable to order packages!" fi fi if [ -z "${ebuilds}" ]; then eerror "Don't know how to find which package owns what file :/" exit 1 fi echo einfo "About to execute" echo "emerge --oneshot ${emerge_opts} ${ebuilds}" echo i=5 printf "in" while [ ${i} -gt 0 ]; do printf " ${i}" sleep 1 i=$((${i} - 1)) done printf "\n\n" EMERGE_DEFAULT_OPTS="" emerge --oneshot ${emerge_opts} ${ebuilds} retval=$? if [ "${retval}" = 0 ]; then einfo "All done" exit 0 fi eerror "There was an error trying to emerge the broken packages" exit "${retval}"