From 86eaf5e03289e45a95514b4f6011157972016e9d Mon Sep 17 00:00:00 2001 From: fuzzyray Date: Thu, 30 Apr 2009 21:52:45 +0000 Subject: Tagging the gentoolkit-0.2.4 release svn path=/tags/gentoolkit-0.2.4/; revision=564 --- src/revdep-rebuild/99revdep-rebuild | 21 + src/revdep-rebuild/AUTHORS | 2 + src/revdep-rebuild/ChangeLog | 9 + src/revdep-rebuild/Makefile | 23 + src/revdep-rebuild/README | 4 + src/revdep-rebuild/TODO | 7 + src/revdep-rebuild/find_pkgs.py | 22 + src/revdep-rebuild/revdep-rebuild | 1094 +++++++++++++++++++++++++++++++++ src/revdep-rebuild/revdep-rebuild-old | 720 ++++++++++++++++++++++ src/revdep-rebuild/revdep-rebuild-sh | 332 ++++++++++ src/revdep-rebuild/revdep-rebuild.1 | 101 +++ 11 files changed, 2335 insertions(+) create mode 100644 src/revdep-rebuild/99revdep-rebuild create mode 100644 src/revdep-rebuild/AUTHORS create mode 100644 src/revdep-rebuild/ChangeLog create mode 100644 src/revdep-rebuild/Makefile create mode 100644 src/revdep-rebuild/README create mode 100644 src/revdep-rebuild/TODO create mode 100755 src/revdep-rebuild/find_pkgs.py create mode 100755 src/revdep-rebuild/revdep-rebuild create mode 100755 src/revdep-rebuild/revdep-rebuild-old create mode 100755 src/revdep-rebuild/revdep-rebuild-sh create mode 100644 src/revdep-rebuild/revdep-rebuild.1 (limited to 'src/revdep-rebuild') diff --git a/src/revdep-rebuild/99revdep-rebuild b/src/revdep-rebuild/99revdep-rebuild new file mode 100644 index 0000000..bdaecc7 --- /dev/null +++ b/src/revdep-rebuild/99revdep-rebuild @@ -0,0 +1,21 @@ +# Default revdep-rebuild configuration file +# +# revdep-rebuild no longer uses hardcoded paths. To change the default +# behavior the following variables can be changed: +# +# LD_LIBRARY_MASK - Mask of specially evaluated libraries +# +# SEARCH_DIRS - List of directories to search for executables and libraries +# Use this for directories that are not included in PATH or ld.so.conf. +# An application should normally not have to set this variable +# +# SEARCH_DIRS_MASK - List of directories to not search +# Use this for directories that should not be searched by revdep-rebuild +# This is normally used by binary packages such as openoffice-bin +# +# Note: This file is sourced using bash by the revdep-rebuild script + +LD_LIBRARY_MASK="libodbcinst.so libodbc.so libjava.so libjvm.so" +SEARCH_DIRS="/bin /sbin /usr/bin /usr/sbin /lib* /usr/lib*" +SEARCH_DIRS_MASK="/lib*/modules" + diff --git a/src/revdep-rebuild/AUTHORS b/src/revdep-rebuild/AUTHORS new file mode 100644 index 0000000..b3d9b32 --- /dev/null +++ b/src/revdep-rebuild/AUTHORS @@ -0,0 +1,2 @@ +Stanislav Brabec (original author) +Paul Varner diff --git a/src/revdep-rebuild/ChangeLog b/src/revdep-rebuild/ChangeLog new file mode 100644 index 0000000..9060781 --- /dev/null +++ b/src/revdep-rebuild/ChangeLog @@ -0,0 +1,9 @@ +2005-06-05 Paul Varner + + * ChangeLog moved to main gentoolkit ChangeLog + +2004-01-07 Karl Trygve Kalleberg + + * Added Makefile + * Copied revdep-rebuild script from app-portage/gentoolkit + diff --git a/src/revdep-rebuild/Makefile b/src/revdep-rebuild/Makefile new file mode 100644 index 0000000..d509681 --- /dev/null +++ b/src/revdep-rebuild/Makefile @@ -0,0 +1,23 @@ +# Copyright 2004 Karl Trygve Kalleberg +# Copyright 2004 Gentoo Technologies, Inc. +# Distributed under the terms of the GNU General Public License v2 +# +# $Header$ + +include ../../makedefs.mak + +all: + echo "AIGBURTH (AYG-berth n.) Any piece of readily identifiable anatomy found among cooked meat." + +dist: + mkdir -p ../../$(distdir)/src/revdep-rebuild + cp Makefile AUTHORS README TODO ChangeLog revdep-rebuild revdep-rebuild.1 99revdep-rebuild ../../$(distdir)/src/revdep-rebuild/ + +install: + + install -m 0755 revdep-rebuild $(bindir)/ + install -d $(docdir)/revdep-rebuild + install -m 0644 AUTHORS README TODO $(docdir)/revdep-rebuild/ + install -m 0644 revdep-rebuild.1 $(mandir)/ + install -d $(sysconfdir)/revdep-rebuild + install -m 0644 99revdep-rebuild $(sysconfdir)/revdep-rebuild/ diff --git a/src/revdep-rebuild/README b/src/revdep-rebuild/README new file mode 100644 index 0000000..3a51d9f --- /dev/null +++ b/src/revdep-rebuild/README @@ -0,0 +1,4 @@ +This tool scans libraries and binaries for broken shared lib dependencies +and fixes them by re-emerging those broken binaries and shared libraries. + +- Alastair Tse diff --git a/src/revdep-rebuild/TODO b/src/revdep-rebuild/TODO new file mode 100644 index 0000000..d9f6350 --- /dev/null +++ b/src/revdep-rebuild/TODO @@ -0,0 +1,7 @@ +- revdep cache in /var/cache + - list all .so files this package depends on + - use timestamps of files to know when to rebuild + - if ts of cache is older than any of the package's contained + files, we must rebuild +- update to use equery/gentoolkit +- rewrite in python and/or eclectic module diff --git a/src/revdep-rebuild/find_pkgs.py b/src/revdep-rebuild/find_pkgs.py new file mode 100755 index 0000000..7013813 --- /dev/null +++ b/src/revdep-rebuild/find_pkgs.py @@ -0,0 +1,22 @@ +#!/usr/bin/python +# Copyright 1999-2005 Gentoo Foundation +# $Header$ + +# Temporary script to find package versions and slot for revdep-rebuild + +import sys + +sys.path.insert(0, "/usr/lib/gentoolkit/pym") +import gentoolkit + +for pkgname in sys.argv[1:]: + matches = gentoolkit.find_packages(pkgname) + for pkg in matches: + (cat, name, ver, rev) = gentoolkit.split_package_name(pkg.get_cpv()) + slot = pkg.get_env_var("SLOT") + if rev == "r0": + fullversion = ver + else: + fullversion = ver + "-" + rev + + print name + " " + fullversion + " (" + slot + ")" diff --git a/src/revdep-rebuild/revdep-rebuild b/src/revdep-rebuild/revdep-rebuild new file mode 100755 index 0000000..3ffe904 --- /dev/null +++ b/src/revdep-rebuild/revdep-rebuild @@ -0,0 +1,1094 @@ +#!/bin/bash +# Copyright 1999-2008 Gentoo Foundation + +# revdep-rebuild: Reverse dependency rebuilder. +# Original Author: Stanislav Brabec +# Rewrite Author: Michael A. Smith +# Current Maintainer: Paul Varner + +# TODO: +# - Use more /etc/init.d/functions.sh +# - Try to reduce the number of global vars + +## +# Global Variables: + +# Must-be-blank: +unset GREP_OPTIONS + +# Readonly variables: +declare -r APP_NAME="${0##*/}" # The name of this application +declare -r OIFS="$IFS" # Save the IFS +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 +declare -ra FILES=( + "$ENV_FILE" + "$FILES_FILE" + "$LDPATH_FILE" + "$BROKEN_FILE" + "$ERRORS_FILE" + "$RAW_FILE" + "$OWNERS_FILE" + "$PKGS_FILE" + "$EBUILDS_FILE" + "$ORDER_FILE" + "$STATUS_FILE" +) + +# "Boolean" variables: Considered "true" if it has any value at all +# "True" indicates we should... +declare FULL_LD_PATH # ...search across the COMPLETE_LD_LIBRARY_PATH +declare KEEP_TEMP # ...not delete tempfiles from the current run +declare ORDER_PKGS # ...sort the atoms in deep dependency order +declare PACKAGE_NAMES # ...emerge by slot, not by versionated atom +declare RM_OLD_TEMPFILES # ...remove tempfiles from prior runs +declare SEARCH_BROKEN # ...search for broken libraries and binaries +declare VERBOSE # ...give verbose output + +# Globals that impact portage directly: +declare EMERGE_DEFAULT_OPTS # String of options portage assumes to be set +declare EMERGE_OPTIONS # Array of options to pass to portage +declare PORTAGE_NICENESS # Renice to this value +declare PORTAGE_ROOT # The root path for portage + +# Customizable incremental variables: +# These variables can be prepended to either by setting the variable in +# your environment prior to execution, or by placing an entry in +# /etc/make.conf. +# +# An entry of "-*" means to clear the variable from that point forward. +# Example: env SEARCH_DIRS="/usr/bin -*" revdep-rebuild will set SEARCH_DIRS +# to contain only /usr/bin +declare LD_LIBRARY_MASK # Mask of specially evaluated libraries +declare SEARCH_DIRS # List of dirs to search for executables and libraries +declare SEARCH_DIRS_MASK # List of dirs not to search + +# Other globals: +declare OLDPROG # Previous pass through the progress meter +declare EXACT_PKG # Versionated atom to emerge +declare HEAD_TEXT # Feedback string about the search +declare NOCOLOR # Set to "true" not to output term colors +declare OK_TEXT # Feedback about a search which found no errors +declare RC_NOCOLOR # Hack to insure we respect NOCOLOR +declare REBUILD_LIST # Array of atoms to emerge +declare SKIP_LIST # Array of atoms that cannot be emerged (masked?) +declare SONAME # Soname/soname path pattern given on commandline +declare SONAME_SEARCH # Value of SONAME modified to match ldd's output +declare WORKING_TEXT # Feedback about the search +declare WORKING_DIR # Working directory where cache files are kept + +main() { + # preliminary setup + get_opts "$@" + setup_portage + setup_search_paths_and_masks + get_search_env + echo + + # Search for broken binaries + get_files + get_ldpath + main_checks + + # Associate broken binaries with packages to rebuild + if [[ $PACKAGE_NAMES ]]; then + get_packages + clean_packages + assign_packages_to_ebuilds + else + get_exact_ebuilds + fi + + # Rebuild packages owning broken binaries + get_build_order + rebuild + + # All done + cleanup +} +## +# Refuse to delete anything before we cd to our tmpdir +# (See mkdir_and_cd_to_tmpdir() +rm() { + eerror "I was instructed to rm '$@'" + die 1 "Refusing to delete anything before changing to temporary directory." +} +## +# GNU find has -executable, but if our users' finds do not have that flag +# we emulate it with this function. Also emulates -writable and -readable. +# Usage: find PATH ARGS -- use find like normal, except use -executable instead +# of various versions of -perm /+ blah blah and hacks +find() { + hash find || { die 1 'find not found!'; } + # We can be pretty sure find itself should be executable. + local testsubject="$(type -P find)" + if [[ $(command find "$testsubject" -executable 2> /dev/null) ]]; then + unset -f find # We can just use the command find + elif [[ $(command find "$testsubject" -perm /u+x 2> /dev/null) ]]; then + find() { + a=(${@//-executable/-perm \/u+x}) + a=(${a[@]//-writable/-perm \/u+w}) + a=(${a[@]//-readable/-perm \/r+w}) + command find "${a[@]}" + } + elif [[ $(command find "$testsubject" -perm +u+x 2> /dev/null) ]]; then + find() { + a=(${@//-executable/-perm +u+x}) + a=(${a[@]//-writable/-perm +u+w}) + a=(${a[@]//-readable/-perm +r+w}) + command find "${a[@]}" + } + else # Last resort + find() { + a=(${@//-executable/-exec test -x '{}' \; -print}) + a=(${a[@]//-writable/-exec test -w '{}' \; -print}) + a=(${a[@]//-readable/-exec test -r '{}' \; -print}) + command find "${a[@]}" + } + fi + find "$@" +} + +print_usage() { +cat << EOF +Usage: $APP_NAME [OPTIONS] [--] [EMERGE_OPTIONS] + +Broken reverse dependency rebuilder. + + -C, --nocolor Turn off colored output + -d, --debug Print way too much information (uses bash's set -xv) + -e, --exact Emerge based on exact package version + -h, --help Print this usage + -i, --ignore Ignore temporary files from previous runs + -k, --keep-temp Do not delete temporary files on exit + -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) + -l, --no-ld-path Do not set LD_LIBRARY_PATH + -o, --no-order Do not check the build order + (Saves time, but may cause breakage.) + -p, --pretend Do a trial run without actually emerging anything + (also passed to emerge command) + -P, --no-progress Turn off the progress meter + -q, --quiet Be less verbose (also passed to emerge command) + -v, --verbose Be more verbose (also passed to emerge command) + +Calls emerge, options after -- are ignored by $APP_NAME +and passed directly to emerge. + +Report bugs to +EOF +} +## +# 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 +} +## +# Usage: countdown n +# n: number of seconds to count +countdown() { + local i + for ((i=1; i<$1; i++)); do + echo -ne '\a.' + ((i<$1)) && sleep 1 + done + echo -e '\a.' +} +## +# Replace whitespace with linebreaks, normalize repeated '/' chars, and sort -u +# (If any libs have whitespace in their filenames, someone needs punishment.) +clean_var() { + awk 'BEGIN {RS="[[:space:]]"} + /-\*/ {exit} + /[^[:space:]]/ {gsub(/\/\/+/, "/"); print}' | sort -u +} +## +# Exit and optionally output to sterr +die() { + local status=$1 + shift + eerror "$@" + exit $status +} +## +# What to do when dynamic linking is consistent +clean_exit() { + if [[ ! $KEEP_TEMP ]]; then + rm -f "${FILES[@]}" + if [[ "$WORKING_DIR" != "/var/cache/${APP_NAME}" ]]; then + # Remove the working directory + builtin cd; rmdir "$WORKING_DIR" + fi + fi + echo + einfo "$OK_TEXT... All done. " + exit 0 +} +## +# Get the name of the package that owns a file or list of files given as args. +get_file_owner() { + local IFS=$'\n' + # ${*/%/ } adds a space to the end of each object name to prevent false + # matches, for example /usr/bin/dia matching /usr/bin/dialog (bug #196460). + find -L /var/db/pkg -name CONTENTS -print0 | + xargs -0 grep -Fl "${*/%/ }" | + sed 's:/var/db/pkg/\(.*\)/CONTENTS:\1:' +} +## +# Normalize some EMERGE_OPTIONS +normalize_emerge_opts() { + # Normalize some EMERGE_OPTIONS + EMERGE_OPTIONS=(${EMERGE_OPTIONS[@]/%-p/--pretend}) + EMERGE_OPTIONS=(${EMERGE_OPTIONS[@]/%-f/--fetchonly}) + EMERGE_OPTIONS=(${EMERGE_OPTIONS[@]/%-f/--verbose}) +} +## +# Use the color preference from portage +setup_color() { + # This should still work if NOCOLOR is set by the -C flag or in the user's + # environment. + export NOCOLOR=$(portageq envvar NOCOLOR) + [[ $NOCOLOR = yes || $NOCOLOR = true ]] && export RC_NOCOLOR=yes # HACK! (grr) + . /etc/init.d/functions.sh +} +## +# Die if an argument is missing. +die_if_missing_arg() { + [[ ! $2 || $2 = -* ]] && die 1 "Missing expected argument to $1" +} +## +# Die because an option is not recognized. +die_invalid_option() { + # Can't use eerror and einfo because this gets called before function.sh + # is sourced + echo + echo "Encountered unrecognized option $1." >&2 + echo + echo "$APP_NAME no longer automatically passes unrecognized options to portage." + echo "Separate emerge-only options from revdep-rebuild options with the -- flag." + echo + echo "For example, $APP_NAME -v -- --ask" + echo + echo "See the man page or $APP_NAME -h for more detail." + echo + exit 1 +} +## +# Warn about deprecated options. +warn_deprecated_opt() { + # Can't use eerror and einfo because this gets called before function.sh + # is sourced + echo + echo "Encountered deprecated option $1." >&2 + [[ $2 ]] && echo "Please use $2 instead." >&2 +} +## +# Get whole-word commandline options preceded by two dashes. +get_longopts() { + case $1 in + --nocolor) export NOCOLOR="yes";; + --no-color) warn_deprecated_opt "$1" "--nocolor" + export NOCOLOR="yes";; + --debug) set -xv;; + --exact) unset PACKAGE_NAMES;; + --help) print_usage + exit 0;; + --ignore) RM_OLD_TEMPFILES=1;; + --keep-temp) KEEP_TEMP=1;; + --library=*) # TODO: check for invalid values + SONAME="${1#*=}" + unset SEARCH_BROKEN;; + --soname=*|--soname-regexp=*) # TODO: check for invalid values + warn_deprecated_opt "${1%=*}" "--library" + SONAME="${1#*=}" + unset SEARCH_BROKEN;; + --library) # TODO: check for invalid values + die_if_missing_arg $1 $2 + shift + SONAME="$1" + unset SEARCH_BROKEN;; + --soname|--soname-regexp) # TODO: check for invalid values + warn_deprecated_opt "$1" "--library" + die_if_missing_arg $1 $2 + shift + SONAME="$1" + unset SEARCH_BROKEN;; + --no-ld-path) unset FULL_LD_PATH;; + --no-order) unset ORDER_PKGS;; + --no-progress) progress() { :; };; + --pretend) EMERGE_OPTIONS+=("--pretend");; + --quiet) echo_v() { :; } + progress() { :; } + quiet=1 + EMERGE_OPTIONS+=($1);; + --verbose) VERBOSE=1 + EMERGE_OPTIONS+=("--verbose");; + --extra-verbose) warn_deprecated_opt "$1" "--verbose" + VERBOSE=1 + EMERGE_OPTIONS+=("--verbose");; + --package-names) # No longer used, since it is the + # default. We accept it for + # backwards compatibility. + warn_deprecated_opt "$1" + PACKAGE_NAMES=1;; + *) die_invalid_option $1;; + esac +} + +## +# Get single-letter commandline options preceded by a single dash. +get_shortopts() { + local OPT OPTSTRING OPTARG OPTIND + while getopts ":CdehikL:loPpqu:vX" OPT; do + case "$OPT" in + C) # TODO: Match syntax with the rest of gentoolkit + export NOCOLOR="yes";; + d) set -xv;; + e) unset PACKAGE_NAMES;; + h) print_usage + exit 0;; + i) RM_OLD_TEMPFILES=1;; + k) KEEP_TEMP=1;; + L) # TODO: Check for invalid values + SONAME="${OPTARG#*=}" + unset SEARCH_BROKEN;; + l) unset FULL_LD_PATH;; + o) unset ORDER_PKGS;; + P) progress() { :; };; + p) EMERGE_OPTIONS+=("--pretend");; + q) echo_v() { :; } + progress() { :; } + quiet=1 + EMERGE_OPTIONS+=("--quiet");; + v) VERBOSE=1 + EMERGE_OPTIONS+=("--verbose");; + X) # No longer used, since it is the default. + # We accept it for backwards compatibility. + warn_deprecated_opt "-X" + PACKAGE_NAMES=1;; + *) die_invalid_option "-$OPTARG";; + esac + done +} +## +# Get command-line options. +get_opts() { + local avoid_utils + local -a args + echo_v() { ewarn "$@"; } + unset VERBOSE KEEP_TEMP EMERGE_OPTIONS RM_OLD_TEMPFILES + ORDER_PKGS=1 + PACKAGE_NAMES=1 + SONAME="not found" + SEARCH_BROKEN=1 + FULL_LD_PATH=1 + while [[ $1 ]]; do + case $1 in + --) shift + EMERGE_OPTIONS+=("$@") + break;; + -*) while true; do + args+=("$1") + shift + [[ ${1:--} = -* ]] && break + done + if [[ ${args[0]} = --* ]]; then + get_longopts "${args[@]}" + else + get_shortopts "${args[@]}" + fi;; + *) die_invalid_option "$1";; + esac + unset args + done + + setup_color + normalize_emerge_opts + + # If the user is not super, add --pretend to EMERGE_OPTIONS + if [[ ${EMERGE_OPTIONS[@]} != *--pretend* && $UID -ne 0 ]]; then + ewarn "You are not superuser. Adding --pretend to emerge options." + EMERGE_OPTIONS+=(--pretend) + fi +} +## +# Is there a --pretend or --fetchonly flag in the EMERGE_OPTIONS array? +is_real_merge() { + [[ ${EMERGE_OPTIONS[@]} != *--pretend* && + ${EMERGE_OPTIONS[@]} != *--fetchonly* ]] +} +## +# Clean up temporary files and exit +cleanup_and_die() { + rm -f "$@" + die 1 " ...terminated. Removing incomplete $@." +} +## +# Clean trap +clean_trap() { + trap "cleanup_and_die $*" SIGHUP SIGINT SIGQUIT SIGABRT SIGTERM + rm -f "$@" +} +## +# Returns 0 if the first arg is found in the remaining args, 1 otherwise +# (Returns 2 if given fewer than 2 arguments) +has() { + (( $# > 1 )) || return 2 + local IFS=$'\a' target="$1" + shift + [[ $'\a'"$*"$'\a' = *$'\a'$target$'\a'* ]] +} +## +# Dies when it can't change directories +cd() { + if builtin cd -P "$@"; then + if [[ $1 != $PWD ]]; then + # Some symlink malfeasance is going on + die 1 "Working directory expected to be $1, but it is $PWD" + fi + else + die 1 "Unable to change working directory to '$@'" + fi +} +## +# Tries not to delete any files or directories it shouldn't +setup_rm() { + ## + # Anything in the FILES array in tmpdir is fair game for removal + rm() { + local i IFS=$'\a' + [[ $APP_NAME ]] || die 1 '$APP_NAME is not defined! (This is a bug.)' + case $@ in + */*|*-r*|*-R*) die 1 "Oops, I'm not allowed to delete that. ($@)";; + esac + for i; do + # Don't delete files that are not listed in the array + # Allow no slashes or recursive deletes at all. + case $i in + */*|-*r*|-*R*) :;; # Not OK + -*) continue;; # OK + esac + has "$i" "${FILES[@]}" && continue + die 1 "Oops, I'm not allowed to delete that. ($@)" + done + command rm "$@" + } + # delete this setup function so it's harmless to re-run + setup_rm() { :; } +} +## +# Make our temporary files directory +# $1 - directory name +# $2 - user name +verify_tmpdir() { + umask 007 || die $? "Unable to set umask 007" + if [[ ! $1 ]]; then + die 1 'Temporary file path is unset! (This is a bug.)' + elif [[ -d $1 ]]; then + # HACK: I hate using find this way + if [[ $(find "$1" -type d ! \( -user $2 -perm -0700 \) ) ]]; then + eerror "Incorrect permissions on $1" + eerror "or at least one file in $1." + die 1 "Please make sure it's not a symlink and then remove it." + fi + cd "$1" + else + die 1 "Unable to find a satisfactory location for temporary files ($1)" + fi + [[ $VERBOSE ]] && einfo "Temporary cache files are located in $PWD" + setup_rm +} +get_search_env() { + local new_env + local old_env + local uid=$(python -c 'import os; import pwd; print pwd.getpwuid(os.getuid())[0]') + # Find a place to put temporary files + if [[ "$uid" == "root" ]]; then + local tmp_target="/var/cache/${APP_NAME}" + else + local tmp_target="$(mktemp -d -t revdep-rebuild.XXXXXXXXXX)" + fi + + # From here on all work is done inside the temporary directory + verify_tmpdir "$tmp_target" "$uid" + WORKING_DIR="$tmp_target" + + if [[ $SEARCH_BROKEN ]]; then + SONAME_SEARCH="$SONAME" + HEAD_TEXT="broken by a package update" + OK_TEXT="Dynamic linking on your system is consistent" + WORKING_TEXT="consistency" + else + # first case is needed to test against /path/to/foo.so + if [[ $SONAME = /* ]]; then + # Set to "$SONAME" + SONAME_SEARCH=" $SONAME " + # Escape the "/" characters + SONAME_SEARCH="${SONAME_SEARCH//\//\\/}" + else + # Set to "$SONAME" + SONAME_SEARCH=$'\t'"$SONAME " + fi + HEAD_TEXT="using $SONAME" + OK_TEXT="There are no dynamic links to $SONAME" + unset WORKING_TEXT + fi + + # If any of our temporary files are older than 1 day, remove them all + if [[ ! $KEEP_TEMP ]]; then + while read; do + RM_OLD_TEMPFILES=1 + break + done < <(find -L . -maxdepth 1 -type f -name '*.rr' -mmin +1440 -print 2>/dev/null) + fi + + # Compare old and new environments + # Don't use our previous files if environment doesn't match + new_env=$( + # We do not care if these emerge options change + EMERGE_OPTIONS=(${EMERGE_OPTIONS[@]//--pretend/}) + EMERGE_OPTIONS=(${EMERGE_OPTIONS[@]//--fetchonly/}) + EMERGE_OPTIONS=(${EMERGE_OPTIONS[@]//--verbose/}) + cat <<- EOF + SEARCH_DIRS="$SEARCH_DIRS" + SEARCH_DIRS_MASK="$SEARCH_DIRS_MASK" + LD_LIBRARY_MASK="$LD_LIBRARY_MASK" + PORTAGE_ROOT="$PORTAGE_ROOT" + EMERGE_OPTIONS="${EMERGE_OPTIONS[@]}" + ORDER_PKGS="$ORDER_PKGS" + FULL_LD_PATH="$FULL_LD_PATH" + EOF + ) + if [[ -r "$ENV_FILE" && -s "$ENV_FILE" ]]; then + old_env=$(<"$ENV_FILE") + if [[ $old_env != $new_env ]]; then + ewarn 'Environment mismatch from previous run, deleting temporary files...' + RM_OLD_TEMPFILES=1 + fi + else + # No env file found, silently delete any other tempfiles that may exist + RM_OLD_TEMPFILES=1 + fi + + # If we should remove old tempfiles, do so + if [[ $RM_OLD_TEMPFILES ]]; then + rm -f "${FILES[@]}" + else + for file in "${FILES[@]}"; do + if [ -e "$file" ]; then + chown ${uid}:portage "$file" + chmod 700 "$file" + fi + done + fi + + # Save the environment in a file for next time + echo "$new_env" > "$ENV_FILE" + + [[ $VERBOSE ]] && echo $'\n'"$APP_NAME environment:"$'\n'"$new_env" + + echo + einfo "Checking reverse dependencies" + einfo "Packages containing binaries and libraries $HEAD_TEXT" + einfo "will be emerged." +} + +get_files() { + einfo "Collecting system binaries and libraries" + if [[ -r "$FILES_FILE" && -s "$FILES_FILE" ]]; then + einfo "Found existing $FILES_FILE" + else + # Be safe and remove any extraneous temporary files + # Don't remove 0_env.rr - The first file in the array + rm -f "${FILES[@]:1}" + + clean_trap "$FILES_FILE" + + if [[ $SEARCH_DIRS_MASK ]]; then + findMask=($SEARCH_DIRS_MASK) + findMask="${findMask[@]/#/-o -path }" + findMask="( ${findMask#-o } ) -prune -o" + fi + # TODO: Check this + find ${SEARCH_DIRS[@]} $findMask -type f \( -executable -o \ + -name '*.so' -o -name '*.so.*' -o -name '*.la' \) -print 2> /dev/null | + sort -u > "$FILES_FILE" || + die $? "find failed to list binary files (This is a bug.)" + einfo "Generated new $FILES_FILE" + fi +} +get_ldpath() { + local COMPLETE_LD_LIBRARY_PATH + [[ $SEARCH_BROKEN && $FULL_LD_PATH ]] || return + einfo 'Collecting complete LD_LIBRARY_PATH' + if [[ -r "$LDPATH_FILE" && -s "$LDPATH_FILE" ]]; then + einfo "Found existing $LDPATH_FILE." + else + clean_trap "$LDPATH_FILE" + # Ensure that the "trusted" lib directories are at the start of the path + COMPLETE_LD_LIBRARY_PATH=( + /lib* + /usr/lib* + $(sed '/^#/d;s/#.*$//' < /etc/ld.so.conf) + $(sed 's:/[^/]*$::' < "$FILES_FILE" | sort -ru) + ) + IFS=':' + COMPLETE_LD_LIBRARY_PATH="${COMPLETE_LD_LIBRARY_PATH[*]}" + IFS="$OIFS" + echo "$COMPLETE_LD_LIBRARY_PATH" > "$LDPATH_FILE" + einfo "Generated new $LDPATH_FILE" + fi +} +main_checks() { + local target_file + local -a files + local i=0 + local ldd_output + local ldd_status + local numFiles + local COMPLETE_LD_LIBRARY_PATH + if [[ $SEARCH_BROKEN && $FULL_LD_PATH ]]; then + [[ -r "$LDPATH_FILE" && -s "$LDPATH_FILE" ]] || + die 1 "Unable to find $LDPATH_FILE" + COMPLETE_LD_LIBRARY_PATH=$(<"$LDPATH_FILE") + fi + einfo "Checking dynamic linking $WORKING_TEXT" + if [[ -r "$BROKEN_FILE" && -s "$BROKEN_FILE" ]]; then + einfo "Found existing $BROKEN_FILE." + else + clean_trap "$BROKEN_FILE" "$ERRORS_FILE" + files=($(<"$FILES_FILE")) + numFiles="${#files[@]}" + for target_file in "${files[@]}"; do + if [[ $target_file != *.la ]]; then + # Note: double checking seems to be faster than single with complete path + # (special add ons are rare). + ldd_output=$(ldd "$target_file" 2>> "$ERRORS_FILE" | sort -u) + ldd_status=$? # TODO: Check this for problems with sort + # HACK: if LD_LIBRARY_MASK is null or undefined grep -vF doesn't work + if grep -vF "${LD_LIBRARY_MASK:=$'\a'}" <<< "$ldd_output" | + grep -q "$SONAME_SEARCH"; then + if [[ $SEARCH_BROKEN && $FULL_LD_PATH ]]; then + if LD_LIBRARY_PATH="$COMPLETE_LD_LIBRARY_PATH" ldd "$target_file" 2>/dev/null | + grep -vF "$LD_LIBRARY_MASK" | grep -q "$SONAME_SEARCH"; then + # FIXME: I hate duplicating code + # Only build missing direct dependencies + MISSING_LIBS=$( + expr='s/[[:space:]]*\([^[:space:]]*\) => not found/\1/p' + sed -n "$expr" <<< "$ldd_output" + ) + REQUIRED_LIBS=$( + expr='s/^[[:space:]]*NEEDED[[:space:]]*\([^[:space:]]*\).*/\1/p'; + objdump -x "$target_file" | grep NEEDED | sed "$expr" | sort -u + ) + MISSING_LIBS=$(grep -F "$REQUIRED_LIBS" <<< "$MISSING_LIBS") + if [[ $MISSING_LIBS ]]; then + echo "obj $target_file" >> "$BROKEN_FILE" + echo_v " broken $target_file (requires $MISSING_LIBS)" + fi + fi + else + # FIXME: I hate duplicating code + # Only rebuild for direct dependencies + MISSING_LIBS=$( + expr="/$SONAME_SEARCH/s/^[[:space:]]*\([^[:space:]]*\).*$/\1/p" + sort -u <<< "$ldd_output" | sed -n "$expr" + ) + REQUIRED_LIBS=$( + expr='s/^[[:space:]]*NEEDED[[:space:]]*\([^[:space:]]*\).*/\1/p'; + objdump -x "$target_file" | grep NEEDED | sed "$expr" | sort -u + ) + MISSING_LIBS=$(grep -F "$REQUIRED_LIBS" <<< "$MISSING_LIBS") + if [[ $MISSING_LIBS ]]; then + echo "obj $target_file" >> "$BROKEN_FILE" + if [[ $SEARCH_BROKEN ]]; then + echo_v " broken $target_file (requires $MISSING_LIBS)" + else + echo_v " found $target_file" + fi + fi + fi + fi + elif [[ $SEARCH_BROKEN ]]; then + # Look for broken .la files + for depend in $( + awk -F"[=']" '/^dependency_libs/{ + gsub("^-[^[:space:]]*", "", $3); + gsub("[[:space:]]-[^[:space:]]*", "", $3); + print $3 + }' "$target_file" + ); do + if [[ $depend = /* && ! -e $depend ]]; then + echo "obj $target_file" >> "$BROKEN_FILE" + echo_v " broken $target_file (requires $depend)" + fi + done + fi + [[ $VERBOSE ]] && + progress $((++i)) $numFiles $target_file || + progress $((++i)) $numFiles + done + if [[ $SEARCH_BROKEN ]]; then + # Look for missing version + while read target_file; do + echo "obj $target_file" >> "$BROKEN_FILE" + echo_v " broken $target_file (no version information available)" + done < <( + # Regexify LD_LIBRARY_MASK. Exclude it from the search. + LD_LIBRARY_MASK="${LD_LIBRARY_MASK//$'\n'/|}" + awk -v ldmask="(${LD_LIBRARY_MASK//./\\\.})" ' + /no version information available/ && $0 !~ ldmask { + gsub(/[()]/, "", $NF) + if (seen[$NF]++) next + print $NF + }' "$ERRORS_FILE" + ) + fi + [[ -r "$BROKEN_FILE" && -s "$BROKEN_FILE" ]] || clean_exit + sort -u "$BROKEN_FILE" -o "$BROKEN_FILE" + einfo "Generated new $BROKEN_FILE" + fi +} +get_packages() { + local target_file + local EXACT_PKG + local PKG + local obj + einfo 'Assigning files to packages' + if [[ -r "$RAW_FILE" && -s "$RAW_FILE" ]]; then + einfo "Found existing $RAW_FILE" + else + clean_trap "$RAW_FILE" "$OWNERS_FILE" + while read obj target_file; do + EXACT_PKG=$(get_file_owner $target_file) + if [[ $EXACT_PKG ]]; then + # Strip version information + PKG="${EXACT_PKG%%-r[[:digit:]]*}" + PKG="${PKG%-*}" + echo "$EXACT_PKG" >> "$RAW_FILE" + echo "$target_file -> $EXACT_PKG" >> "$OWNERS_FILE" + echo_v " $target_file -> $PKG" + else + ewarn " !!! $target_file not owned by any package is broken !!!" + echo "$target_file -> (none)" >> "$OWNERS_FILE" + echo_v " $target_file -> (none)" + fi + done < "$BROKEN_FILE" + einfo "Generated new $RAW_FILE and $OWNERS_FILE" + fi + # if we find '(none)' on every line, exit out + if ! grep -qvF '(none)' "$OWNERS_FILE"; then + ewarn "Found some broken files, but none of them were associated with known packages" + ewarn "Unable to proceed with automatic repairs." + ewarn "The broken files are listed in $OWNERS_FILE" + if [[ $VERBOSE ]]; then + ewarn "The broken files are:" + while read filename junk; do + ewarn " $filename" + done < "$OWNERS_FILE" + fi + exit 0 # FIXME: Should we exit 1 here? + fi +} +clean_packages() { + einfo 'Cleaning list of packages to rebuild' + if [[ -r "$PKGS_FILE" && -s "$PKGS_FILE" ]]; then + einfo "Found existing $PKGS_FILE" + else + sort -u "$RAW_FILE" > "$PKGS_FILE" + einfo "Generated new $PKGS_FILE" + fi +} +assign_packages_to_ebuilds() { + local EXACT_PKG + local PKG + local SLOT + einfo 'Assigning packages to ebuilds' + if [[ -r "$EBUILDS_FILE" && -s "$EBUILDS_FILE" ]]; then + einfo "Found existing $EBUILDS_FILE" + elif [[ -r "$PKGS_FILE" && -s "$PKGS_FILE" ]]; then + clean_trap "$EBUILDS_FILE" + while read EXACT_PKG; do + # Get the slot + PKG="${EXACT_PKG%%-r[[:digit:]]*}" + PKG="${PKG%-*}" + SLOT=$( "$EBUILDS_FILE" + einfo "Generated new $EBUILDS_FILE" + else + einfo 'Nothing to rebuild.' + die 1 '(The program should have already quit, so this is a minor bug.)' + fi +} +get_exact_ebuilds() { + einfo 'Assigning files to ebuilds' + if [[ -r $EBUILDS_FILE && -s $EBUILDS_FILE ]]; then + einfo "Found existing $EBUILDS_FILE" + elif [[ -r $BROKEN_FILE && -s $BROKEN_FILE ]]; then + rebuildList=" $(<"$BROKEN_FILE") " + rebuildList=(${rebuildList//[[:space:]]obj[[:space:]]/ }) + get_file_owner "${rebuildList[@]}" | sed 's/^/=/' > "$EBUILDS_FILE" + einfo "Generated new $EBUILDS_FILE" + else + einfo 'Nothing to rebuild.' + die 1 '(The program should have already quit, so this is a minor bug.)' + fi +} +list_skipped_packages() { + ewarn + ewarn 'Portage could not find any version of the following packages it could build:' + ewarn "${SKIP_LIST[@]}" + ewarn + ewarn '(Perhaps they are masked, blocked, or removed from portage.)' + ewarn 'Try to emerge them manually.' + ewarn +} +get_build_order() { + local -r OLD_EMERGE_DEFAULT_OPTS="$EMERGE_DEFAULT_OPTS" + local RAW_REBUILD_LIST + local REBUILD_GREP + local i + if [[ ! $ORDER_PKGS ]]; then + einfo 'Skipping package ordering' + return + fi + einfo 'Evaluating package order' + if [[ -r "$ORDER_FILE" && -s "$ORDER_FILE" ]]; then + einfo "Found existing $ORDER_FILE" + else + clean_trap "$ORDER_FILE" + RAW_REBUILD_LIST=$(<"$EBUILDS_FILE") + if [[ $RAW_REBUILD_LIST ]]; then + export EMERGE_DEFAULT_OPTS="--nospinner --pretend --oneshot --quiet" + RAW_REBUILD_LIST=($RAW_REBUILD_LIST) # convert into array + # If PACKAGE_NAMES is defined we're using slots, not versions + if [[ $PACKAGE_NAMES ]]; then + # Eliminate atoms that can't be built + for i in "${!RAW_REBUILD_LIST[@]}"; do + if [[ "${RAW_REBUILD_LIST[i]}" = *[A-Za-z]* ]]; then + portageq best_visible "$PORTAGE_ROOT" "${RAW_REBUILD_LIST[i]}" >/dev/null && continue + SKIP_LIST+=("${RAW_REBUILD_LIST[i]}") + fi + unset RAW_REBUILD_LIST[i] + done + # If RAW_REBUILD_LIST is empty, then we have nothing to build. + if (( ${#RAW_REBUILD_LIST[@]} == 0 )); then + if (( ${#SKIP_LIST[@]} == 0 )); then + ewarn "The list of packages to skip is empty, but there are no" + ewarn "packages listed to rebuild either. (This is a bug.)" + else + list_skipped_packages + fi + die 1 'Warning: Portage cannot rebuild any of the necessary packages.' + fi + fi + RAW_REBUILD_LIST="${RAW_REBUILD_LIST[@]}" + REBUILD_GREP=$(emerge --nodeps $RAW_REBUILD_LIST | sed 's/\[[^]]*\]//g') + if (( ${PIPESTATUS[0]} == 0 )); then + emerge --deep $RAW_REBUILD_LIST | + sed 's/\[[^]]*\]//g' | + grep -F "$REBUILD_GREP" > "$ORDER_FILE" + fi + + # Here we use the PIPESTATUS from the second emerge, the --deep one. + if (( ${PIPESTATUS[0]} != 0 )); then + eerror + eerror 'Warning: Failed to resolve package order.' + eerror 'Will merge in arbitrary order' + eerror + cat <<- EOF + Possible reasons: + - An ebuild is no longer in the portage tree. + - An ebuild is masked, use /etc/portage/packages.keyword + and/or /etc/portage/package.unmask to unmask it + EOF + countdown 5 + rm -f "$ORDER_FILE" + fi + export EMERGE_DEFAULT_OPTS="$OLD_EMERGE_DEFAULT_OPTS" + else + einfo 'Nothing to rebuild.' + die 1 '(The program should have already quit, so this is a minor bug.)' + fi + fi + [[ -r "$ORDER_FILE" && -s "$ORDER_FILE" ]] && einfo "Generated new $ORDER_FILE" +} + +show_unowned_files() { + if grep -qF '(none)' "$OWNERS_FILE"; then + ewarn "Found some broken files that weren't associated with known packages" + ewarn "The broken files are:" + while read filename junk; do + [[ $junk = *none* ]] && ewarn " $filename" + done < "$OWNERS_FILE" | awk '!s[$0]++' # (omit dupes) + fi +} +## +# Setup portage and the search paths +setup_portage() { + local PORTAGE_NICENESS=$(portageq envvar PORTAGE_NICENESS) + PORTAGE_ROOT=$(portageq envvar ROOT) + + # Obey PORTAGE_NICENESS + if [[ $PORTAGE_NICENESS ]]; then + renice $PORTAGE_NICENESS $$ > /dev/null + # Since we have already set our nice value for our processes, + # reset PORTAGE_NICENESS to zero to avoid having emerge renice again. + export PORTAGE_NICENESS="0" + fi + + PORTAGE_ROOT="${PORTAGE_ROOT:-/}" +} + +## +# 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 for $APP_NAME" + + # 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 /etc/revdep-rebuild ]]; then + for configfile in /etc/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 "/etc/profile.env" && -s "/etc/profile.env" ]]; then + SEARCH_DIRS+=" "$(. /etc/profile.env; /usr/bin/tr ':' ' ' <<< "$ROOTPATH $PATH") + fi + + # Get the directories from /etc/ld.so.conf + if [[ -r /etc/ld.so.conf && -s /etc/ld.so.conf ]]; then + SEARCH_DIRS+=" "$(sed '/^#/d;s/#.*$//' /etc/ld.so.conf) + fi + + # Set the final variables + SEARCH_DIRS=$(clean_var <<< "$SEARCH_DIRS") + SEARCH_DIRS_MASK=$(clean_var <<< "$SEARCH_DIRS_MASK") + 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+=" ${sdir}" + done + SEARCH_DIRS=$(clean_var <<< "${filter_SEARCH_DIRS}") + [[ $SEARCH_DIRS ]] || die 1 "No search defined -- this is a bug." +} +## +# Rebuild packages owning broken binaries +rebuild() { + if [[ -r $LIST.5_order && -s $LIST.5_order ]]; then + REBUILD_LIST=( $(<"$LIST.5_order") ) + REBUILD_LIST="${REBUILD_LIST[@]/#/=}" + else + REBUILD_LIST=$(sort -u "$EBUILDS_FILE") + fi + + trap - SIGHUP SIGINT SIGQUIT SIGABRT SIGTERM + + einfo 'All prepared. Starting rebuild' + echo "emerge --oneshot ${EMERGE_OPTIONS[@]} $REBUILD_LIST" + + is_real_merge && countdown 10 + + # Link file descriptor #6 with stdin so --ask will work + exec 6<&0 + + # Run in background to correctly handle Ctrl-C + { + EMERGE_DEFAULT_OPTS="--oneshot ${EMERGE_OPTIONS[@]}" emerge $REBUILD_LIST <&6 + echo $? > "$STATUS_FILE" + } & + wait + + # Now restore stdin from fd #6, where it had been saved, and close fd #6 ( 6<&- ) to free it for other processes to use. + exec 0<&6 6<&- +} +## +# Finish up +cleanup() { + if (( $(<"$STATUS_FILE") != 0 )); then + ewarn + ewarn "$APP_NAME failed to emerge all packages." + ewarn 'you have the following choices:' + einfo "- If emerge failed during the build, fix the problems and re-run $APP_NAME." + einfo '- Use /etc/portage/package.keywords to unmask a newer version of the package.' + einfo " (and remove $ORDER_FILE to be evaluated again)" + einfo '- Modify the above emerge command and run it manually.' + einfo '- Compile or unmerge unsatisfied packages manually,' + einfo ' remove temporary files, and try again.' + einfo ' (you can edit package/ebuild list first)' + einfo + einfo 'To remove temporary files, please run:' + einfo "rm ${WORKING_DIR}/*.rr" + show_unowned_files + exit $EMERGE_STATUS + elif is_real_merge; then + trap_cmd() { + eerror "terminated. Please remove the temporary files manually:" + eerror "rm ${WORKING_DIR}/*.rr" + exit 1 + } + [[ "${SKIP_LIST[@]}" != "" ]] && list_skipped_packages + trap trap_cmd SIGHUP SIGINT SIGQUIT SIGABRT SIGTERM + einfo 'Build finished correctly. Removing temporary files...' + einfo + einfo 'You can re-run revdep-rebuild to verify that all libraries and binaries' + einfo 'are fixed. If some inconsistency remains, it can be orphaned file, deep' + einfo 'dependency, binary package or specially evaluated library.' + if [[ -r "$OWNERS_FILE" && -s "$OWNERS_FILE" ]]; then + show_unowned_files + fi + [[ $KEEP_TEMP ]] || rm "${FILES[@]}" + else + einfo 'Now you can remove -p (or --pretend) from arguments and re-run revdep-rebuild.' + fi +} + +main "$@" diff --git a/src/revdep-rebuild/revdep-rebuild-old b/src/revdep-rebuild/revdep-rebuild-old new file mode 100755 index 0000000..52d6d19 --- /dev/null +++ b/src/revdep-rebuild/revdep-rebuild-old @@ -0,0 +1,720 @@ +#!/bin/bash +# Copyright 1999-2007 Gentoo Foundation +# $Header$ + +# revdep-rebuild: Reverse dependency rebuilder. +# Original Author: Stanislav Brabec +# Current Maintainer: Paul Varner + +# Known problems: +# +# In exact ebuild mode revdep-rebuild can fail to properly order packages, +# which are not up to date. +# http://bugs.gentoo.org/show_bug.cgi?id=23018 +# +# Rebuilding using --package-names mode should be default, but emerge has no +# feature to update to latest version of defined SLOT. +# http://bugs.gentoo.org/show_bug.cgi?id=4698 + +# Customizable variables: +# +# LD_LIBRARY_MASK - Mask of specially evaluated libraries +# SEARCH_DIRS - List of directories to search for executables and libraries +# SEARCH_DIRS_MASK - List of directories to not search +# +# These variables can be prepended to either by setting the variable in +# your environment prior to execution, or by placing an entry in +# /etc/make.conf. +# +# An entry of "-*" means to clear the variable from that point forward. +# Example: env SEARCH_DIRS="/usr/bin -*" revdep-rebuild will set SEARCH_DIRS +# to contain only /usr/bin + +if [ "$1" = "-h" -o "$1" = "--help" ] +then + echo "Usage: $0 [OPTIONS] [--] [EMERGE_OPTIONS]" + echo + echo "Broken reverse dependency rebuilder." + echo + echo " -X, --package-names Emerge based on package names, not exact versions" + echo " --library NAME Emerge existing packages that use the library with NAME" + echo " --library=NAME NAME can be a full path to the library or a basic" + echo " regular expression (man grep)" + echo " -np, --no-ld-path Do not set LD_LIBRARY_PATH" + echo " -nc, --nocolor Turn off colored output" + echo " -i, --ignore Ignore temporary files from previous runs" + echo " -q, --quiet Be less verbose (also passed to emerge command)" + echo " -vv, --extra-verbose Be extra verbose" + echo + echo "Calls emerge, all other options are used for it (e. g. -p, --pretend)." + echo + echo "Report bugs to " + exit 0 +fi + +echo "Configuring search environment for revdep-rebuild" + +# Obey PORTAGE_NICENESS +PORTAGE_NICENESS=$(portageq envvar PORTAGE_NICENESS) +if [ ! -z "$PORTAGE_NICENESS" ] +then + renice $PORTAGE_NICENESS $$ > /dev/null + # Since we have already set our nice value for our processes, + # reset PORTAGE_NICENESS to zero to avoid having emerge renice again. + export PORTAGE_NICENESS="0" +fi + +PORTAGE_ROOT=$(portageq envvar ROOT) +[ -z "$PORTAGE_ROOT" ] && PORTAGE_ROOT="/" + +# 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 +PRELIMINARY_SEARCH_DIRS="$SEARCH_DIRS $(unset SEARCH_DIRS; portageq envvar SEARCH_DIRS)" +PRELIMINARY_SEARCH_DIRS_MASK="$SEARCH_DIRS_MASK $(unset SEARCH_DIRS_MASK; portageq envvar SEARCH_DIRS_MASK)" +PRELIMINARY_LD_LIBRARY_MASK="$LD_LIBRARY_MASK $(unset LD_LIBRARY_MASK; portageq envvar LD_LIBRARY_MASK)" + +# Add the defaults +if [ -d /etc/revdep-rebuild ] +then + for file in $(ls /etc/revdep-rebuild) + do + PRELIMINARY_SEARCH_DIRS="$PRELIMINARY_SEARCH_DIRS $(. /etc/revdep-rebuild/${file}; echo $SEARCH_DIRS)" + PRELIMINARY_SEARCH_DIRS_MASK="$PRELIMINARY_SEARCH_DIRS_MASK $(. /etc/revdep-rebuild/${file}; echo $SEARCH_DIRS_MASK)" + PRELIMINARY_LD_LIBRARY_MASK="$PRELIMINARY_LD_LIBRARY_MASK $(. /etc/revdep-rebuild/${file}; echo $LD_LIBRARY_MASK)" + done +else + PRELIMINARY_SEARCH_DIRS="$PRELIMINARY_SEARCH_DIRS /bin /sbin /usr/bin /usr/sbin /lib* /usr/lib*" + PRELIMINARY_SEARCH_DIRS_MASK="$PRELIMINARY_SEARCH_DIRS_MASK /opt/OpenOffice /usr/lib/openoffice" + PRELIMINARY_LD_LIBRARY_MASK="$PRELIMINARY_LD_LIBRARY_MASK libodbcinst.so libodbc.so libjava.so libjvm.so" +fi + +# Get the ROOTPATH and PATH from /etc/profile.env +if [ -e "/etc/profile.env" ] +then + PRELIMINARY_SEARCH_DIRS="$PRELIMINARY_SEARCH_DIRS $((. /etc/profile.env; echo ${ROOTPATH}:${PATH}) | tr ':' ' ')" +fi + +# Get the directories from /etc/ld.so.conf +if [ -e /etc/ld.so.conf ] +then + PRELIMINARY_SEARCH_DIRS="$PRELIMINARY_SEARCH_DIRS $(grep -v "^#" /etc/ld.so.conf | tr '\n' ' ')" +fi + +# Set the final variables +# Note: Using $(echo $variable) removes extraneous spaces from variable assignment +unset SEARCH_DIRS +for i in $(echo $PRELIMINARY_SEARCH_DIRS) +do + [ "$i" = "-*" ] && break + # Append a / at the end so that links and directories are treated the same by find + # Remove any existing trailing slashes to prevent double-slashes + SEARCH_DIRS="$(echo $SEARCH_DIRS ${i/%\//}/)" +done +# Remove any double-slashes from the path +SEARCH_DIRS="$(echo $SEARCH_DIRS | sed 's:/\+:/:g')" + +unset SEARCH_DIRS_MASK +for i in $(echo $PRELIMINARY_SEARCH_DIRS_MASK) +do + [ "$i" = "-*" ] && break + SEARCH_DIRS_MASK="$(echo $SEARCH_DIRS_MASK $i)" +done + +unset LD_LIBRARY_MASK +for i in $(echo $PRELIMINARY_LD_LIBRARY_MASK) +do + [ "$i" = "-*" ] && break + LD_LIBRARY_MASK="$(echo $LD_LIBRARY_MASK $i)" +done + +# Use the color preference from portage +NOCOLOR=$(portageq envvar NOCOLOR) + +# Base of temporary files names. +touch ${HOME}/.revdep-rebuild_0.test 2>/dev/null +if [ $? -eq 0 ] +then + LIST="${HOME}/.revdep-rebuild" + rm ~/.revdep-rebuild_0.test +else + # Try to use /var/tmp since $HOME is not available + touch /var/tmp/.revdep-rebuild_0.test 2>/dev/null + if [ $? -eq 0 ] + then + LIST="/var/tmp/.revdep-rebuild" + rm /var/tmp/.revdep-rebuild_0.test + else + echo + echo "!!! Unable to write temporary files to either $HOME or /var/tmp !!!" + echo + exit 1 + fi +fi + +shopt -s nullglob +shopt -s expand_aliases +unalias -a + +# Color Definitions +NO="\x1b[0m" +BR="\x1b[0;01m" +CY="\x1b[36;01m" +GR="\x1b[32;01m" +RD="\x1b[31;01m" +YL="\x1b[33;01m" +BL="\x1b[34;01m" + +# Check if portage-utils are installed +portageq has_version $PORTAGE_ROOT portage-utils +if [ "$?" -eq 0 ] +then + PORTAGE_UTILS=true +else + PORTAGE_UTILS=false +fi + +alias echo_v=echo + +PACKAGE_NAMES=false +SONAME="not found" +SONAME_GREP=grep +SEARCH_BROKEN=true +EXTRA_VERBOSE=false +KEEP_TEMP=false +FULL_LD_PATH=true + +EMERGE_OPTIONS="" +PRELIMINARY_CALLED_OPTIONS="" +while [ ! -z "$1" ] ; do + case "$1" in + -X | --package-names ) + PACKAGE_NAMES=true + PRELIMINARY_CALLED_OPTIONS="${PRELIMINARY_CALLED_OPTIONS} --package_names" + shift + ;; + -q | --quiet ) + alias echo_v=: + EMERGE_OPTIONS="${EMERGE_OPTIONS} $1" + shift + ;; + --library=* | --soname=* | --soname-regexp=* ) + SONAME="${1#*=}" + SEARCH_BROKEN=false + PRELIMINARY_CALLED_OPTIONS="${PRELIMINARY_CALLED_OPTIONS} --library=${SONAME}" + shift + ;; + --library | --soname | --soname-regexp ) + SONAME="$2" + SEARCH_BROKEN=false + PRELIMINARY_CALLED_OPTIONS="${PRELIMINARY_CALLED_OPTIONS} --library=${SONAME}" + shift 2 + ;; + -nc | --no-color | --nocolor ) + NOCOLOR=true + shift + ;; + -np | --no-ld-path ) + FULL_LD_PATH=false + PRELIMINARY_CALLED_OPTIONS="${PRELIMINARY_CALLED_OPTIONS} --no-ld-path" + shift + ;; + -i | --ignore ) + rm -f ${LIST}* + shift + ;; + --keep-temp ) + KEEPTEMP=true + shift + ;; + -vv | --extra-verbose ) + EXTRA_VERBOSE=true + shift + ;; + -- ) + shift + ;; + * ) + EMERGE_OPTIONS="${EMERGE_OPTIONS} $1" + shift + ;; + esac +done + +EMERGE_OPTIONS=$(echo $EMERGE_OPTIONS | sed 's/^ //') + +if [ -z "$PRELIMINARY_CALLED_OPTIONS" ] +then + CALLED_OPTIONS="" +else + for i in $(echo $PRELIMINARY_CALLED_OPTIONS | tr ' ' '\n'| sort) + do + CALLED_OPTIONS="$(echo $CALLED_OPTIONS $i)" + done +fi + +if [ "$NOCOLOR" = "yes" -o "$NOCOLOR" = "true" ] +then + NOCOLOR=true +else + NOCOLOR=false +fi + +# Make the NOCOLOR variable visible to emerge +export NOCOLOR + +if $NOCOLOR +then + NO="" + BR="" + CY="" + GR="" + RD="" + YL="" + BL="" +fi + +function set_trap () { + trap "rm_temp $1" SIGHUP SIGINT SIGQUIT SIGABRT SIGTERM +} + +function rm_temp () { + echo " terminated." + echo "Removing incomplete $1." + rm $1 + echo + exit 1 +} + +if $SEARCH_BROKEN ; then + SONAME_SEARCH="$SONAME" + LLIST=$LIST + HEAD_TEXT="broken by a package update" + OK_TEXT="Dynamic linking on your system is consistent" + WORKING_TEXT=" consistency" +else + # first case is needed to test against /path/to/foo.so + if [ ${SONAME:0:1} == '/' ] ; then + # Set to "$SONAME" + SONAME_SEARCH=" $SONAME " + else + # Set to "$SONAME" + SONAME_SEARCH=" $SONAME " + fi + LLIST=${LIST}_$(echo "$SONAME_SEARCH$SONAME" | md5sum | head -c 8) + HEAD_TEXT="using $SONAME" + OK_TEXT="There are no dynamic links to $SONAME" + WORKING_TEXT="" +fi + +# If any of our temporary files are older than 1 day, remove them all +[ "$(find "${LIST%/*}/." ! -name . -prune -name "${LIST##*/}*" -type f -mmin +1440)" != "" ] && rm -f ${LIST}* + +# Don't use our previous files if environment doesn't match +if [ -f $LIST.0_env ] +then + PREVIOUS_SEARCH_DIRS=$(. ${LIST}.0_env; echo "$SEARCH_DIRS") + PREVIOUS_SEARCH_DIRS_MASK=$(. ${LIST}.0_env; echo "$SEARCH_DIRS_MASK") + PREVIOUS_LD_LIBRARY_MASK=$(. ${LIST}.0_env; echo "$LD_LIBRARY_MASK") + PREVIOUS_PORTAGE_ROOT=$(. ${LIST}.0_env; echo "$PORTAGE_ROOT") + PREVIOUS_OPTIONS=$(. ${LIST}.0_env; echo "$CALLED_OPTIONS") + if [ "$PREVIOUS_SEARCH_DIRS" != "$SEARCH_DIRS" ] || \ + [ "$PREVIOUS_SEARCH_DIRS_MASK" != "$SEARCH_DIRS_MASK" ] || \ + [ "$PREVIOUS_LD_LIBRARY_MASK" != "$LD_LIBRARY_MASK" ] || \ + [ "$PREVIOUS_PORTAGE_ROOT" != "$PORTAGE_ROOT" ] || \ + [ "$PREVIOUS_OPTIONS" != "$CALLED_OPTIONS" ] + then + echo + echo "Environment mismatch from previous run, deleting temporary files..." + rm -f ${LIST}* + fi +fi + +# Clean up no longer needed environment variables +unset PREVIOUS_SEARCH_DIRS PREVIOUS_SEARCH_DIRS_MASK PREVIOUS_LD_LIBRARY_MASK PREVIOUS_PORTAGE_ROOT PREVIOUS_OPTIONS +unset PRELIMINARY_SEARCH_DIRS PRELIMINARY_SEARCH_DIRS_MASK PRELIMINARY_LD_LIBRARY_MASK PRELIMINARY_CALLED_OPTIONS + +# Log our environment +echo "SEARCH_DIRS=\"$SEARCH_DIRS\"" > $LIST.0_env +echo "SEARCH_DIRS_MASK=\"$SEARCH_DIRS_MASK\"" >> $LIST.0_env +echo "LD_LIBRARY_MASK=\"$LD_LIBRARY_MASK\"" >> $LIST.0_env +echo "PORTAGE_ROOT=\"$PORTAGE_ROOT\"" >> $LIST.0_env +echo "CALLED_OPTIONS=\"$CALLED_OPTIONS\"" >> $LIST.0_env +echo "EMERGE_OPTIONS=\"$EMERGE_OPTIONS\"" >> $LIST.0_env + +if $EXTRA_VERBOSE +then + echo + echo "revdep-rebuild environment:" + cat $LIST.0_env +fi + +echo +echo "Checking reverse dependencies..." +echo +echo "Packages containing binaries and libraries $HEAD_TEXT" +echo "will be emerged." + +echo +echo -n -e "${GR}Collecting system binaries and libraries...${NO}" + +if [ -f $LIST.1_files ] +then + echo " using existing $LIST.1_files." +else + # Be safe and remove any extraneous temporary files + rm -f ${LIST}.[1-9]_* + + set_trap "$LIST.1_*" + + # Hack for the different versions of find. + # Be extra paranoid and pipe results through sed to remove multiple slashes + find_results=$(find /usr/bin/revdep-rebuild -type f -perm /u+x 2>/dev/null) + if [ -z $find_results ] + then + find_results=$(find /usr/bin/revdep-rebuild -type f -perm +u+x 2>/dev/null) + if [ -z $find_results ] + then + echo -e "\n" + echo -e "${RD}Unable to determine how to use find to locate executable files${NO}" + echo -e "${RD}Open a bug at http://bugs.gentoo.org${NO}" + echo + exit 1 + else + # using -perm +u+x for find command + find $SEARCH_DIRS -type f \( -perm +u+x -o -name '*.so' -o -name '*.so.*' -o -name '*.la' \) 2>/dev/null | sort | uniq | sed 's:/\+:/:g' >$LIST.0_files + fi + else + # using -perm /u+x for find command + find $SEARCH_DIRS -type f \( -perm /u+x -o -name '*.so' -o -name '*.so.*' -o -name '*.la' \) 2>/dev/null | sort | uniq | sed 's:/\+:/:g' >$LIST.0_files + fi + + # Remove files that match SEARCH_DIR_MASK + for dir in $SEARCH_DIRS_MASK + do + grep -v "^$dir" $LIST.0_files > $LIST.1_files + mv $LIST.1_files $LIST.0_files + done + + mv $LIST.0_files $LIST.1_files + echo -e " done.\n ($LIST.1_files)" +fi + +if $SEARCH_BROKEN && $FULL_LD_PATH ; then + echo + echo -n -e "${GR}Collecting complete LD_LIBRARY_PATH...${NO}" + if [ -f $LIST.2_ldpath ] ; then + echo " using existing $LIST.2_ldpath." + else + set_trap "$LIST.2_ldpath" + # Ensure that the "trusted" lib directories are at the start of the path + ( + echo /lib* /usr/lib* | sed 's/ /:/g' + sed '/^#/d;s/#.*$//' $LIST.2_ldpath + echo -e " done.\n ($LIST.2_ldpath)" + fi + COMPLETE_LD_LIBRARY_PATH="$(cat $LIST.2_ldpath)" +fi + +echo +echo -n -e "${GR}Checking dynamic linking$WORKING_TEXT...${NO}" +if [ -f $LLIST.3_rebuild ] ; then + echo " using existing $LLIST.3_rebuild." +else + echo_v + set_trap "$LLIST.3_rebuild" + LD_MASK="\\( $(echo "$LD_LIBRARY_MASK" | sed 's/\./\\./g;s/ / \\| /g') \\)" + echo -n >$LLIST.3_rebuild + echo -n >$LLIST.3_ldd_errors + cat $LIST.1_files | egrep -v '*\.la$' | while read FILE ; do + # Note: double checking seems to be faster than single + # with complete path (special add ons are rare). + if ldd "$FILE" 2>>$LLIST.3_ldd_errors | grep -v "$LD_MASK" | $SONAME_GREP -q "$SONAME_SEARCH" ; then + if $SEARCH_BROKEN && $FULL_LD_PATH ; then + if LD_LIBRARY_PATH="$COMPLETE_LD_LIBRARY_PATH" ldd "$FILE" 2>/dev/null | grep -v "$LD_MASK" | $SONAME_GREP -q "$SONAME_SEARCH" ; then + # FIX: I hate duplicating code + # Only build missing direct dependencies + ALL_MISSING_LIBS=$(ldd "$FILE" 2>/dev/null | sort -u | sed -n 's/ \(.*\) => not found/\1/p' | tr '\n' ' ' | sed 's/ $//' ) + REQUIRED_LIBS=$(objdump -x $FILE | grep NEEDED | awk '{print $2}' | tr '\n' ' ' | sed 's/ $//') + MISSING_LIBS="" + for lib in $ALL_MISSING_LIBS + do + if echo $REQUIRED_LIBS | grep -q $lib + then + MISSING_LIBS="$MISSING_LIBS $lib" + fi + done + if [ "$MISSING_LIBS" != "" ] + then + echo "obj $FILE" >>$LLIST.3_rebuild + echo_v " broken $FILE (requires ${MISSING_LIBS})" + fi + fi + else + # FIX: I hate duplicating code + # Only rebuild for direct dependencies + ALL_MISSING_LIBS=$(ldd "$FILE" 2>/dev/null | sort -u | $SONAME_GREP "$SONAME_SEARCH" | awk '{print $1}' | tr '\n' ' ' | sed 's/ $//' ) + REQUIRED_LIBS=$(objdump -x $FILE | grep NEEDED | awk '{print $2}' | tr '\n' ' ' | sed 's/ $//') + MISSING_LIBS="" + for lib in $ALL_MISSING_LIBS + do + if echo $REQUIRED_LIBS | grep -q $lib + then + MISSING_LIBS="$MISSING_LIBS $lib" + fi + done + if [ "$MISSING_LIBS" != "" ] + then + echo "obj $FILE" >>$LLIST.3_rebuild + if $SEARCH_BROKEN ; then + echo_v " broken $FILE (requires ${MISSING_LIBS})" + else + echo_v " found $FILE" + fi + fi + fi + fi + done + if $SEARCH_BROKEN ; then + # Look for missing version + for FILE in $(grep "no version information available" $LLIST.3_ldd_errors | awk '{print $NF}' | sed 's/[()]//g' | sort -u) ; do + echo "obj $FILE" >>$LLIST.3_rebuild + echo_v " broken $FILE (no version information available)" + done + # Look for broken .la files + cat $LIST.1_files | egrep '*\.la$' | while read FILE ; do + for depend in $(grep '^dependency_libs' $FILE | awk -F'=' '{print $2}' | sed "s/'//g") ; do + [ ${depend:0:1} != '/' ] && continue + if [ ! -e $depend ] ; then + echo "obj $FILE" >>$LLIST.3_rebuild + echo_v " broken $FILE (requires ${depend})" + fi + done + done + fi + echo -e " done.\n ($LLIST.3_rebuild)" +fi + +if $PACKAGE_NAMES ; then + EXACT_EBUILDS=false + + echo + echo -n -e "${GR}Assigning files to packages...${NO}" + if [ -f $LLIST.4_packages_raw ] ; then + echo " using existing $LLIST.4_packages_raw." + else + set_trap "$LLIST.4_packages*" + echo -n >$LLIST.4_packages_raw + echo -n >$LLIST.4_package_owners + cat $LLIST.3_rebuild | while read obj FILE ; do + if $PORTAGE_UTILS ; then + EXACT_PKG="$(qfile -qvC ${FILE} )" + else + EXACT_PKG=$(find /var/db/pkg -name CONTENTS | xargs fgrep -l "obj $FILE " | sed -e 's:/var/db/pkg/\(.*\)/CONTENTS:\1:g') + fi + # Ugly sed hack to strip version information + PKG="$(echo $EXACT_PKG | sed 's/-r[0-9].*$//;s/\(^.*\/*\)-.*$/\1/')" + if [ -z "$PKG" ] ; then + echo -n -e "\n ${RD}*** $FILE not owned by any package is broken! ***${NO}" + echo "$FILE -> (none)" >> $LLIST.4_package_owners + echo_v -n -e "\n $FILE -> (none)" + else + echo "$EXACT_PKG" >> $LLIST.4_packages_raw + echo "$FILE -> $EXACT_PKG" >> $LLIST.4_package_owners + echo_v -n -e "\n $FILE -> $PKG" + fi + done + echo_v + echo -e " done.\n ($LLIST.4_packages_raw, $LLIST.4_package_owners)" + fi + + echo + echo -n -e "${GR}Cleaning list of packages to rebuild...${NO}" + if [ -f $LLIST.4_packages ] ; then + echo " using existing $LLIST.4_packages." + else + sort -u $LLIST.4_packages_raw >$LLIST.4_packages + echo -e " done.\n ($LLIST.4_packages)" + fi + + echo + echo -n -e "${GR}Assigning packages to ebuilds...${NO}" + if [ -f $LLIST.4_ebuilds ] ; then + echo " using existing $LLIST.4_ebuilds." + else + if [ -s "$LLIST.4_packages" ] + then + set_trap "$LLIST.4_ebuilds" + cat $LLIST.4_packages | while read EXACT_PKG + do + PKG="$(echo $EXACT_PKG | sed 's/-r[0-9].*$//;s/\(^.*\/*\)-.*$/\1/')" + SLOT=$(cat /var/db/pkg/${EXACT_PKG}/SLOT) + best_visible=$(portageq best_visible $PORTAGE_ROOT ${PKG}:${SLOT}) + [ "x" != "x$best_visible" ] && echo $best_visible + done > $LLIST.4_ebuilds + echo -e " done.\n ($LLIST.4_ebuilds)" + else + echo " Nothing to rebuild" + echo -n > $LLIST.4_ebuilds + fi + fi +else + EXACT_EBUILDS=true + + echo + echo -n -e "${GR}Assigning files to ebuilds...${NO}" + if [ -f $LLIST.4_ebuilds ] ; then + echo " using existing $LLIST.4_ebuilds." + else + if [ -s "$LLIST.3_rebuild" ] ; then + set_trap "$LLIST.4_ebuilds" + find /var/db/pkg -name CONTENTS | xargs fgrep -l -f $LLIST.3_rebuild | + sed 's:/var/db/pkg/\(.*\)/CONTENTS:\1:' > $LLIST.4_ebuilds + echo -e " done.\n ($LLIST.4_ebuilds)" + else + echo " Nothing to rebuild" + echo -n > $LLIST.4_ebuilds + fi + fi + +fi + +echo +echo -n -e "${GR}Evaluating package order...${NO}" +if [ -f $LLIST.5_order ] ; then + echo " using existing $LLIST.5_order." +else + set_trap "$LLIST.5_order" + RAW_REBUILD_LIST="$(cat $LLIST.4_ebuilds | sed s/^/=/ | tr '\n' ' ')" + if [ ! -z "$RAW_REBUILD_LIST" ] ; then + REBUILD_GREP="^\\($( (EMERGE_DEFAULT_OPTS="" emerge --nospinner --pretend --oneshot --nodeps --quiet $RAW_REBUILD_LIST ; echo $? >$LLIST.5a_status ) | sed -n 's/\./\\&/g;s/ //g;s/$/\\/;s/\[[^]]*\]//gp' | tr '\n' '|' | sed 's/|$//'))\$" + if [ $(cat $LLIST.5a_status) -gt 0 ] ; then + echo "" + echo -e "${RD}Warning: Failed to resolve package order." + echo -e "Will merge in \"random\" order!${NO}" + echo "Possible reasons:" + echo "- An ebuild is no longer in the portage tree." + echo "- An ebuild is masked, use /etc/portage/packages.keyword" + echo " and/or /etc/portage/package.unmask to unmask it" + for i in . . . . . ; do + echo -n -e '\a.' + sleep 1 + done + ln -f $LLIST.4_ebuilds $LLIST.5_order + else + (EMERGE_DEFAULT_OPTS="" emerge --nospinner --pretend --oneshot --deep --quiet $RAW_REBUILD_LIST ; echo $? >$LLIST.5b_status ) | sed -n 's/ *$//;s/^\[.*\] //p' | awk '{print $1}' | grep "$REBUILD_GREP" >$LLIST.5_order + if [ $(cat $LLIST.5b_status) -gt 0 ] ; then + echo "" + echo -e "${RD}Warning: Failed to resolve package order." + echo -e "Will merge in \"random\" order!${NO}" + echo "Possible reasons:" + echo "- An ebuild is no longer in the portage tree." + echo "- An ebuild is masked, use /etc/portage/packages.keyword" + echo " and/or /etc/portage/package.unmask to unmask it" + for i in . . . . . ; do + echo -n -e '\a.' + sleep 1 + done + rm -f $LLIST.5_order + ln -f $LLIST.4_ebuilds $LLIST.5_order + fi + fi + else + echo -n "" >$LLIST.5_order + fi + echo -e " done.\n ($LLIST.5_order)" +fi + +# Clean up no longer needed environment variables +unset COMPLETE_LD_LIBRARY_PATH SEARCH_DIRS SEARCH_DIRS_MASK LD_LIBRARY_MASK PORTAGE_ROOT CALLED_OPTIONS + +REBUILD_LIST="$(cat $LLIST.5_order | sed s/^/=/ | tr '\n' ' ')" + +trap - SIGHUP SIGINT SIGQUIT SIGABRT SIGTERM + +if [ -z "$REBUILD_LIST" ] ; then + echo -e "\n${GR}$OK_TEXT... All done.${NO} " + if [ ! $KEEPTEMP ] + then + rm $LIST.[0-2]_* + rm $LLIST.[3-9]_* + fi + exit 0 +fi + +IS_REAL_MERGE=true +echo " $EMERGE_OPTIONS " | grep -q '\( -p \| --pretend \| -f \| --fetchonly \)' && IS_REAL_MERGE=false + +echo +echo -e "${GR}All prepared. Starting rebuild...${NO}" + +echo "emerge --oneshot $EMERGE_OPTIONS $REBUILD_LIST" + +if $IS_REAL_MERGE ; then + for i in . . . . . . . . . . ; do + echo -n -e '\a.' + sleep 1 + done + echo +fi + +# Link file descriptor #6 with stdin +exec 6<&0 + +# Run in background to correctly handle Ctrl-C +( + EMERGE_DEFAULT_OPTS="" emerge --oneshot $EMERGE_OPTIONS $REBUILD_LIST <&6 + echo $? >$LLIST.6_status +) & +wait + +# Now restore stdin from fd #6, where it had been saved, and close fd #6 ( 6<&- ) to free it for other processes to use. +exec 0<&6 6<&- + +#if $EXACT_EBUILDS ; then +# mv -i /usr/portage/profiles/package.mask.hidden /usr/portage/profiles/package.mask +# trap - SIGHUP SIGINT SIGQUIT SIGABRT SIGTERM +#fi + +if [ "$(cat $LLIST.6_status)" -gt 0 ] ; then + echo + echo -e "${RD}revdep-rebuild failed to emerge all packages${NO}" + echo -e "${RD}you have the following choices:${NO}" + echo + echo "- if emerge failed during the build, fix the problems and re-run revdep-rebuild" + echo " or" + echo "- use -X or --package-names as first argument (trys to rebuild package, not exact" + echo " ebuild)" + echo " or" + echo "- set ACCEPT_KEYWORDS=\"~\" and/or /etc/portage/package.unmask" + echo " (and remove $LLIST.5_order to be evaluated again)" + echo " or" + echo "- modify the above emerge command and run it manually" + echo " or" + echo "- compile or unmerge unsatisfied packages manually, remove temporary files and" + echo " try again (you can edit package/ebuild list first)" + echo + echo -e "${GR}To remove temporary files, please run:${NO}" + echo "rm $LIST*.?_*" + exit $(cat $LLIST.6_status) +else + if $IS_REAL_MERGE ; then + trap "echo -e \" terminated. Please remove them manually:\nrm $LIST*.?_*\" ; exit 1" \ + SIGHUP SIGINT SIGQUIT SIGABRT SIGTERM + echo -n -e "${GR}Build finished correctly. Removing temporary files...${NO} " + echo + rm $LIST.[0-2]_* + rm $LLIST.[3-9]_* + echo "You can re-run revdep-rebuild to verify that all libraries and binaries" + echo "are fixed. If some inconsistency remains, it can be orphaned file, deep" + echo "dependency, binary package or specially evaluated library." + else + echo -e "${GR}Now you can remove -p (or --pretend) from arguments and re-run revdep-rebuild.${NO}" + fi +fi +exit 0 diff --git a/src/revdep-rebuild/revdep-rebuild-sh b/src/revdep-rebuild/revdep-rebuild-sh new file mode 100755 index 0000000..c7acdc6 --- /dev/null +++ b/src/revdep-rebuild/revdep-rebuild-sh @@ -0,0 +1,332 @@ +#!/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}" diff --git a/src/revdep-rebuild/revdep-rebuild.1 b/src/revdep-rebuild/revdep-rebuild.1 new file mode 100644 index 0000000..267f7f1 --- /dev/null +++ b/src/revdep-rebuild/revdep-rebuild.1 @@ -0,0 +1,101 @@ +.TH "revdep\-rebuild" "1" "" "gentoolkit" "" +.SH "NAME" +revdep\-rebuild \- Gentoo: Reverse Dependency Rebuilder +.SH "SYNOPSIS" +.B revdep\-rebuild +[OPTIONS] [\-\-] [EMERGE OPTIONS] +.SH "DESCRIPTION" +revdep\-rebuild scans libraries and binaries for missing shared library dependencies and attempts to fix them by re\-emerging those broken binaries and shared libraries. It is useful when an upgraded package breaks other software packages that are dependent upon the upgraded package. +.SH "OPTIONS" +.TP +.B \-C | \-\-nocolor +Turn off colored output. (This option is also passed to portage.) +.TP +.B \-e | \-\-exact +Emerge the most recent version of found packages, without regard to SLOT. +.TP +.B \-h | \-\-help +Print usage. +.TP +.B \-i | \-\-ignore +Delete temporary files from previous runs. +.TP +.B \-k | \-\-keep\-temp +Force revdep\-rebuild not to delete temporary files after it successfully rebuilds packages. This option will NOT prevent revdep\-rebuild from deleting inconsistent or out\-of\-date temporary files. +.TP +.B \-\-library NAME | -L NAME +Search for reverse dependencies for a particular library or group of libraries, rather than every library on the system. Emerge packages that use the named library. NAME can be a full path to a library or basic regular expression. (See regex(7).) +.TP +.B \-l | \-\-no\-ld\-path +Do not set LD_LIBRARY_PATH. \fBNote:\fR Using this option will cause revdep-rebuild to report some false positives. +.TP +.B \-o | \-\-no-order +Do not check the build order against the deep dependency list. This will make revdep-rebuild faster, but it can cause emerge failures. Please try revdep\-rebuild without \-o before reporting any bugs. +.TP +.B \-p | \-\-pretend +Do a dry-run. Do not delete temporary files. (\-k \-p is redundant, but harmless.) \-\-pretend is assumed when not running revdep\-rebuild as root. +.TP +.B \-P | \-\-no\-progress +Turn off the progress meter +.TP +.B \-q | \-\-quiet +Print less output and disable the progress meter. (This option is also passed to portage.) +.TP +.B \-u UTIL | \-\-no-util UTIL +Do not use features provided by UTIL. +UTIL can be one of portage-utils or pkgcore, or it can be a \fBquoted\fR space-delimited list. +.TP +.B \-v | \-\-verbose +More output. (Prints the revdep\-rebuild search environment.) +.TP +.B All other options (including unrecognized ones) are passed to the emerge command. Single\-letter options may not be combined, so for example, \-pv is not valid. Please use \-p \-v. +.SH "CONFIGURATION" +revdep\-rebuild no longer uses hardcoded paths. To change the default behavior the following variables can be changed by the user. + +LD_LIBRARY_MASK \- Mask of specially evaluated libraries +.LP +SEARCH_DIRS \- List of directories to search for executables and libraries +.LP +SEARCH_DIRS_MASK \- List of directories to not search + +You can prepend to these variables by setting the variable in your environment prior to execution, by placing an entry in /etc/make.conf, or by placing a file in /etc/revdep\-rebuild containing the appropriate variables. + +The variables are read and set in the following order: + +environment settings \- one time changes by user +.br +/etc/make.conf \- persistent changes by user +.br +/etc/revdep\-rebuild/* \- persistent changes by ebuild authors + +While a user can edit and modify the files in the /etc/revdep\-rebuild directory, please be aware that the /etc/revdep\-rebuild directory is not under configuration protection and files can be removed and/or overwritten by an ebuild. To change this add /etc/revdep\-rebuild to the CONFIG_PROTECT variable in /etc/make.conf. + +An entry of "\-*" means to clear the variable from that point forward. +Example: SEARCH_DIRS="/usr/bin \-*" will set SEARCH_DIRS to contain only /usr/bin + +revdep\-rebuild honors the NOCOLOR and PORTAGE_NICENESS variables from /etc/make.conf +.SH "EXAMPLES" +It is recommended that when running revdep\-rebuild that the following command be used initially: +.br +\fBrevdep\-rebuild \-\-ignore \-\-pretend\fR + +To search the entire system, while excluding /mnt and /home: +.br +\fBenv SEARCH_DIRS="/ \-*" SEARCH_DIRS_MASK="/mnt /home" revdep\-rebuild\fR + +To rebuild packages that depend on libkdecore.so.4 from KDE 3.3: +.br +\fBrevdep\-rebuild \-\-library /usr/kde/3.3/lib/libkdecore.so.4\fR + +To rebuild packages that depend upon libImlib.so and libImlib2.so: +.br +\fBrevdep\-rebuild \-\-library libImlib[2]*.so.*\fR + +.SH "EXIT STATUS" +revdep\-rebuild returns a zero exit status if it \fBand emerge\fR succeeds, and a nonzero exit status otherwise. +.SH "BUGS" +.LP +Report bugs to . Please do not report emerge failures caused by \-o or \-e. Please include your .revdep\-rebuild* files, your emerge \-\-info, and patches. ;) + +.SH "SEE ALSO" +emerge(1), portage(5), regex(7) -- cgit v1.2.3-65-gdbad