Use realpath in get_file_owner to be able to get the package containing e.g. /lib...
[gentoolkit.git] / bin / revdep-rebuild
1 #!/bin/bash
2 # Copyright 1999-2008 Gentoo Foundation
3
4 # revdep-rebuild: Reverse dependency rebuilder.
5 # Original Author: Stanislav Brabec
6 # Rewrite Author: Michael A. Smith
7 # Current Maintainer: Paul Varner <fuzzyray@gentoo.org>
8
9 # TODO:
10 # - Use more /etc/init.d/functions.sh
11 # - Try to reduce the number of global vars
12
13 ##
14 # Global Variables:
15
16 # Must-be-blank:
17 unset GREP_OPTIONS
18
19 # Readonly variables:
20 declare -r APP_NAME="${0##*/}" # The name of this application
21 declare -r OIFS="$IFS"         # Save the IFS
22 declare -r     ENV_FILE=0_env.rr     # Contains environment variables
23 declare -r   FILES_FILE=1_files.rr   # Contains a list of files to search
24 declare -r  LDPATH_FILE=2_ldpath.rr  # Contains the LDPATH
25 declare -r  BROKEN_FILE=3_broken.rr  # Contains the list of broken files
26 declare -r  ERRORS_FILE=3_errors.rr  # Contains the ldd error output
27 declare -r     RAW_FILE=4_raw.rr     # Contains the raw list of packages
28 declare -r  OWNERS_FILE=4_owners.rr  # Contains the file owners
29 declare -r    PKGS_FILE=4_pkgs.rr    # Contains the unsorted bare package names
30 declare -r EBUILDS_FILE=4_ebuilds.rr # Contains the unsorted atoms
31                                      # (Appropriately slotted or versioned)
32 declare -r   ORDER_FILE=5_order.rr   # Contains the sorted atoms
33 declare -r  STATUS_FILE=6_status.rr  # Contains the ldd error output
34 declare -ra FILES=(
35         "$ENV_FILE"
36         "$FILES_FILE"
37         "$LDPATH_FILE"
38         "$BROKEN_FILE"
39         "$ERRORS_FILE"
40         "$RAW_FILE"
41         "$OWNERS_FILE"
42         "$PKGS_FILE"
43         "$EBUILDS_FILE"
44         "$ORDER_FILE"
45         "$STATUS_FILE"
46 )
47
48 # "Boolean" variables: Considered "true" if it has any value at all
49 # "True" indicates we should...
50 declare FULL_LD_PATH           # ...search across the COMPLETE_LD_LIBRARY_PATH
51 declare KEEP_TEMP              # ...not delete tempfiles from the current run
52 declare ORDER_PKGS             # ...sort the atoms in deep dependency order
53 declare PACKAGE_NAMES          # ...emerge by slot, not by versionated atom
54 declare RM_OLD_TEMPFILES       # ...remove tempfiles from prior runs
55 declare SEARCH_BROKEN          # ...search for broken libraries and binaries
56 declare VERBOSE                # ...give verbose output
57
58 # Globals that impact portage directly:
59 declare EMERGE_DEFAULT_OPTS    # String of options portage assumes to be set
60 declare EMERGE_OPTIONS         # Array of options to pass to portage
61 declare PORTAGE_NICENESS       # Renice to this value
62 declare PORTAGE_ROOT           # The root path for portage
63
64 # Customizable incremental variables:
65 # These variables can be prepended to either by setting the variable in
66 # your environment prior to execution, or by placing an entry in
67 # /etc/make.conf.
68 #
69 # An entry of "-*" means to clear the variable from that point forward.
70 # Example: env SEARCH_DIRS="/usr/bin -*" revdep-rebuild will set SEARCH_DIRS
71 # to contain only /usr/bin
72 declare LD_LIBRARY_MASK  # Mask of specially evaluated libraries
73 declare SEARCH_DIRS      # List of dirs to search for executables and libraries
74 declare SEARCH_DIRS_MASK # List of dirs not to search
75
76 # Other globals:
77 declare OLDPROG                # Previous pass through the progress meter
78 declare EXACT_PKG              # Versionated atom to emerge
79 declare HEAD_TEXT              # Feedback string about the search
80 declare NOCOLOR                # Set to "true" not to output term colors
81 declare OK_TEXT                # Feedback about a search which found no errors
82 declare RC_NOCOLOR             # Hack to insure we respect NOCOLOR
83 declare REBUILD_LIST           # Array of atoms to emerge
84 declare SKIP_LIST              # Array of atoms that cannot be emerged (masked?)
85 declare SONAME                 # Soname/soname path pattern given on commandline
86 declare SONAME_SEARCH          # Value of SONAME modified to match ldd's output
87 declare WORKING_TEXT           # Feedback about the search
88 declare WORKING_DIR            # Working directory where cache files are kept
89
90 main() {
91         # preliminary setup
92         get_opts "$@"
93         setup_portage
94         setup_search_paths_and_masks
95         get_search_env
96         echo
97
98         # Search for broken binaries
99         get_files
100         get_ldpath
101         main_checks
102
103         # Associate broken binaries with packages to rebuild
104         if [[ $PACKAGE_NAMES ]]; then
105                 get_packages
106                 clean_packages
107                 assign_packages_to_ebuilds
108         else
109                 get_exact_ebuilds
110         fi
111
112         # Rebuild packages owning broken binaries
113         get_build_order
114         rebuild
115
116         # All done
117         cleanup
118 }
119 ##
120 # Refuse to delete anything before we cd to our tmpdir
121 # (See mkdir_and_cd_to_tmpdir()
122 rm() {
123         eerror "I was instructed to rm '$@'"
124         die 1 "Refusing to delete anything before changing to temporary directory."
125 }
126 : <<'EW'
127 ##
128 # GNU find has -executable, but if our users' finds do not have that flag
129 # we emulate it with this function. Also emulates -writable and -readable.
130 # Usage: find PATH ARGS -- use find like normal, except use -executable instead
131 # of various versions of -perm /+ blah blah and hacks
132 find() {
133         hash find || { die 1 'find not found!'; }
134         # We can be pretty sure find itself should be executable.
135         local testsubject="$(type -P find)"
136         if [[ $(command find "$testsubject" -executable 2> /dev/null) ]]; then
137                 unset -f find # We can just use the command find
138         elif [[ $(command find "$testsubject" -perm /u+x 2> /dev/null) ]]; then
139                 find() {
140                         a=(${@//-executable/-perm \/u+x})
141                         a=(${a[@]//-writable/-perm \/u+w})
142                         a=(${a[@]//-readable/-perm \/r+w})
143                         command find "${a[@]}"
144                 }
145         elif [[ $(command find "$testsubject" -perm +u+x 2> /dev/null) ]]; then
146                 find() {
147                         a=(${@//-executable/-perm +u+x})
148                         a=(${a[@]//-writable/-perm +u+w})
149                         a=(${a[@]//-readable/-perm +r+w})
150                         command find "${a[@]}"
151                 }
152         else # Last resort
153                 find() {
154                         a=(${@//-executable/-exec test -x '{}' \; -print})
155                         a=(${a[@]//-writable/-exec test -w '{}' \; -print})
156                         a=(${a[@]//-readable/-exec test -r '{}' \; -print})
157                         command find "${a[@]}"
158                 }
159         fi
160         find "$@"
161 }
162 EW
163
164 print_usage() {
165 cat << EOF
166 Usage: $APP_NAME [OPTIONS] [--] [EMERGE_OPTIONS]
167
168 Broken reverse dependency rebuilder.
169
170   -C, --nocolor        Turn off colored output
171   -d, --debug          Print way too much information (uses bash's set -xv)
172   -e, --exact          Emerge based on exact package version
173   -h, --help           Print this usage
174   -i, --ignore         Ignore temporary files from previous runs
175   -k, --keep-temp      Do not delete temporary files on exit
176   -L, --library NAME   Emerge existing packages that use the library with NAME
177       --library=NAME   NAME can be a full path to the library or a basic
178                        regular expression (man grep)
179   -l, --no-ld-path     Do not set LD_LIBRARY_PATH
180   -o, --no-order       Do not check the build order
181                        (Saves time, but may cause breakage.)
182   -p, --pretend        Do a trial run without actually emerging anything
183                        (also passed to emerge command)
184   -P, --no-progress    Turn off the progress meter
185   -q, --quiet          Be less verbose (also passed to emerge command)
186   -v, --verbose        Be more verbose (also passed to emerge command)
187
188 Calls emerge, options after -- are ignored by $APP_NAME
189 and passed directly to emerge.
190
191 Report bugs to <http://bugs.gentoo.org>
192 EOF
193 }
194 ##
195 # Usage: progress i n
196 #        i: current item
197 #        n: total number of items to process
198 progress() {
199         if [[ -t 1 ]]; then
200                 progress() {
201                         local curProg=$(( $1 * 100 / $2 ))
202                         (( curProg == OLDPROG )) && return # no change, output nothing
203                         OLDPROG="$curProg" # must be a global variable
204                         (( $1 == $2 )) && local lb=$'\n'
205                         echo -ne '\r                         \r'"[ $curProg% ] $lb"
206                 }
207                 progress $@
208         else # STDOUT is not a tty. Disable progress meter.
209                 progress() { :; }
210         fi
211 }
212 ##
213 # Usage: countdown n
214 #        n: number of seconds to count
215 countdown() {
216         local i
217         for ((i=1; i<$1; i++)); do
218                 echo -ne '\a.'
219                 ((i<$1)) && sleep 1
220         done
221         echo -e '\a.'
222 }
223 ##
224 # Replace whitespace with linebreaks, normalize repeated '/' chars, and sort -u
225 # (If any libs have whitespace in their filenames, someone needs punishment.)
226 clean_var() {
227         gawk 'BEGIN         {RS="[[:space:]]"}
228              /-\*/         {exit}
229              /[^[:space:]]/ {gsub(/\/\/+/, "/"); print}' | sort -u
230 }
231 ##
232 # Exit and optionally output to sterr
233 die() {
234         local status=$1
235         shift
236         eerror "$@"
237         exit $status
238 }
239 ##
240 # What to do when dynamic linking is consistent
241 clean_exit() {
242         if [[ ! $KEEP_TEMP ]]; then
243                 rm -f "${FILES[@]}"
244                 if [[ "$WORKING_DIR" != "/var/cache/${APP_NAME}" ]]; then
245                         # Remove the working directory
246                         builtin cd; rmdir "$WORKING_DIR"
247                 fi
248         fi
249         echo
250         einfo "$OK_TEXT... All done. "
251         exit 0
252 }
253 ##
254 # Get the name of the package that owns a file or list of files given as args.
255 # NOTE: depends on app-misc/realpath!
256 get_file_owner() {
257         local IFS=$'\n'
258         # Add a space to the end of each object name to prevent false
259         # matches, for example /usr/bin/dia matching /usr/bin/dialog (bug #196460).
260         # The same for "${rpath} ".
261
262         rpath=$(realpath "${*}" 2>/dev/null)
263         # To ensure we always have something in rpath...
264         [[ -z $rpath ]] && rpath=${*}
265
266         find -L /var/db/pkg -name CONTENTS -print0 |
267                 xargs -0 grep -Fl -e "${*} " -e "${rpath} " |
268                 sed 's:/var/db/pkg/\(.*\)/CONTENTS:\1:'
269 }
270 ##
271 # Normalize some EMERGE_OPTIONS
272 normalize_emerge_opts() {
273         # Normalize some EMERGE_OPTIONS
274         EMERGE_OPTIONS=(${EMERGE_OPTIONS[@]/%-p/--pretend})
275         EMERGE_OPTIONS=(${EMERGE_OPTIONS[@]/%-f/--fetchonly})
276         EMERGE_OPTIONS=(${EMERGE_OPTIONS[@]/%-v/--verbose})
277 }
278 ##
279 # Use the color preference from portage
280 setup_color() {
281         # This should still work if NOCOLOR is set by the -C flag or in the user's
282         # environment.
283         export NOCOLOR=$(portageq envvar NOCOLOR)
284         [[ $NOCOLOR = yes || $NOCOLOR = true ]] && export RC_NOCOLOR=yes # HACK! (grr)
285         . /etc/init.d/functions.sh
286 }
287 ##
288 # Die if an argument is missing.
289 die_if_missing_arg() {
290         [[ ! $2 || $2 = -* ]] && die 1 "Missing expected argument to $1"
291 }
292 ##
293 # Die because an option is not recognized.
294 die_invalid_option() {
295         # Can't use eerror and einfo because this gets called before function.sh
296         # is sourced
297         echo
298         echo  "Encountered unrecognized option $1." >&2
299         echo
300         echo  "$APP_NAME no longer automatically passes unrecognized options to portage."
301         echo  "Separate emerge-only options from revdep-rebuild options with the -- flag."
302         echo
303         echo  "For example, $APP_NAME -v -- --ask"
304         echo
305         echo  "See the man page or $APP_NAME -h for more detail."
306         echo
307         exit 1
308 }
309 ##
310 # Warn about deprecated options.
311 warn_deprecated_opt() {
312         # Can't use eerror and einfo because this gets called before function.sh
313         # is sourced
314         echo
315         echo "Encountered deprecated option $1." >&2
316         [[ $2 ]] && echo "Please use $2 instead." >&2
317 }
318 ##
319 # Get whole-word commandline options preceded by two dashes.
320 get_longopts() {
321         case $1 in
322                                                --nocolor) export NOCOLOR="yes";;
323                                               --no-color) warn_deprecated_opt "$1" "--nocolor"
324                                                           export NOCOLOR="yes";;
325                                                  --debug) set -xv;;
326                                                  --exact) unset PACKAGE_NAMES;;
327                                                   --help) print_usage
328                                                           exit 0;;
329                                                 --ignore) RM_OLD_TEMPFILES=1;;
330                                              --keep-temp) KEEP_TEMP=1;;
331                                              --library=*) # TODO: check for invalid values
332                                                           SONAME="${1#*=}"
333                                                           unset SEARCH_BROKEN;;
334                             --soname=*|--soname-regexp=*) # TODO: check for invalid values
335                                                           warn_deprecated_opt "${1%=*}" "--library"
336                                                           SONAME="${1#*=}"
337                                                           unset SEARCH_BROKEN;;
338                                                --library) # TODO: check for invalid values
339                                                           die_if_missing_arg $1 $2
340                                                           shift
341                                                           SONAME="$1"
342                                                           unset SEARCH_BROKEN;;
343                                 --soname|--soname-regexp) # TODO: check for invalid values
344                                                           warn_deprecated_opt "$1" "--library"
345                                                           die_if_missing_arg $1 $2
346                                                           shift
347                                                           SONAME="$1"
348                                                           unset SEARCH_BROKEN;;
349                                             --no-ld-path) unset FULL_LD_PATH;;
350                                               --no-order) unset ORDER_PKGS;;
351                                            --no-progress) progress() { :; };;
352                                                --pretend) EMERGE_OPTIONS+=("--pretend");;
353                                                  --quiet) echo_v() { :; }
354                                                           progress() { :; }
355                                                           quiet=1
356                                                           EMERGE_OPTIONS+=($1);;
357                                                --verbose) VERBOSE=1
358                                                           EMERGE_OPTIONS+=("--verbose");;
359                                          --extra-verbose) warn_deprecated_opt "$1" "--verbose"
360                                                           VERBOSE=1
361                                                           EMERGE_OPTIONS+=("--verbose");;
362                                          --package-names) # No longer used, since it is the
363                                                           # default. We accept it for
364                                                           # backwards compatibility.
365                                                           warn_deprecated_opt "$1"
366                                                           PACKAGE_NAMES=1;;
367                                                        *) die_invalid_option $1;;
368         esac
369 }
370
371 ##
372 # Get single-letter commandline options preceded by a single dash.
373 get_shortopts() {
374         local OPT OPTSTRING OPTARG OPTIND
375         while getopts ":CdehikL:loPpqu:vX" OPT; do
376                 case "$OPT" in
377                         C) # TODO: Match syntax with the rest of gentoolkit
378                            export NOCOLOR="yes";;
379                         d) set -xv;;
380                         e) unset PACKAGE_NAMES;;
381                         h) print_usage
382                            exit 0;;
383                         i) RM_OLD_TEMPFILES=1;;
384                         k) KEEP_TEMP=1;;
385                         L) # TODO: Check for invalid values
386                            SONAME="${OPTARG#*=}"
387                            unset SEARCH_BROKEN;;
388                         l) unset FULL_LD_PATH;;
389                         o) unset ORDER_PKGS;;
390                         P) progress() { :; };;
391                         p) EMERGE_OPTIONS+=("--pretend");;
392                         q) echo_v() { :; }
393                            progress() { :; }
394                            quiet=1
395                            EMERGE_OPTIONS+=("--quiet");;
396                         v) VERBOSE=1
397                            EMERGE_OPTIONS+=("--verbose");;
398                         X) # No longer used, since it is the default.
399                            # We accept it for backwards compatibility.
400                            warn_deprecated_opt "-X"
401                            PACKAGE_NAMES=1;;
402                         *) die_invalid_option "-$OPTARG";;
403                 esac
404         done
405 }
406 ##
407 # Get command-line options.
408 get_opts() {
409         local avoid_utils
410         local -a args
411         echo_v() { ewarn "$@"; }
412         unset VERBOSE KEEP_TEMP EMERGE_OPTIONS RM_OLD_TEMPFILES
413         ORDER_PKGS=1
414         PACKAGE_NAMES=1
415         SONAME="not found"
416         SEARCH_BROKEN=1
417         FULL_LD_PATH=1
418         while [[ $1 ]]; do
419                 case $1 in
420                         --) shift
421                             EMERGE_OPTIONS+=("$@")
422                             break;;
423                         -*) while true; do
424                               args+=("$1")
425                               shift
426                               [[ ${1:--} = -* ]] && break
427                             done
428                             if [[ ${args[0]} = --* ]]; then
429                               get_longopts  "${args[@]}"
430                             else
431                               get_shortopts "${args[@]}"
432                             fi;;
433                          *) die_invalid_option "$1";;
434                 esac
435                 unset args
436         done
437
438         setup_color
439         normalize_emerge_opts
440
441         # If the user is not super, add --pretend to EMERGE_OPTIONS
442         if [[ ${EMERGE_OPTIONS[@]} != *--pretend* && $UID -ne 0 ]]; then
443                 ewarn "You are not superuser. Adding --pretend to emerge options."
444                 EMERGE_OPTIONS+=(--pretend)
445         fi
446 }
447 ##
448 # Is there a --pretend or --fetchonly flag in the EMERGE_OPTIONS array?
449 is_real_merge() {
450         [[ ${EMERGE_OPTIONS[@]} != *--pretend* &&
451            ${EMERGE_OPTIONS[@]} != *--fetchonly* ]]
452 }
453 ##
454 # Clean up temporary files and exit
455 cleanup_and_die() {
456         rm -f "$@"
457         die 1 "  ...terminated. Removing incomplete $@."
458 }
459 ##
460 # Clean trap
461 clean_trap() {
462         trap "cleanup_and_die $*" SIGHUP SIGINT SIGQUIT SIGABRT SIGTERM
463         rm -f "$@"
464 }
465 ##
466 # Returns 0 if the first arg is found in the remaining args, 1 otherwise
467 # (Returns 2 if given fewer than 2 arguments)
468 has() {
469         (( $# > 1 )) || return 2
470         local IFS=$'\a' target="$1"
471         shift
472         [[ $'\a'"$*"$'\a' = *$'\a'$target$'\a'* ]]
473 }
474 ##
475 # Dies when it can't change directories
476 cd() {
477         if builtin cd -P "$@"; then
478                 if [[ $1 != $PWD ]]; then
479                         # Some symlink malfeasance is going on
480                         die 1 "Working directory expected to be $1, but it is $PWD"
481                 fi
482         else
483                 die 1 "Unable to change working directory to '$@'"
484         fi
485 }
486 ##
487 # Tries not to delete any files or directories it shouldn't
488 setup_rm() {
489         ##
490         # Anything in the FILES array in tmpdir is fair game for removal
491         rm() {
492                 local i IFS=$'\a'
493                 [[ $APP_NAME ]] || die 1 '$APP_NAME is not defined! (This is a bug.)'
494                 case $@ in
495                         */*|*-r*|*-R*) die 1 "Oops, I'm not allowed to delete that. ($@)";;
496                 esac
497                 for i; do
498                         # Don't delete files that are not listed in the array
499                         # Allow no slashes or recursive deletes at all.
500                         case $i in
501                                 */*|-*r*|-*R*) :;;        # Not OK
502                                            -*) continue;; # OK
503                         esac
504                         has "$i" "${FILES[@]}" && continue
505                         die 1 "Oops, I'm not allowed to delete that. ($@)"
506                 done
507                 command rm "$@"
508         }
509         # delete this setup function so it's harmless to re-run
510         setup_rm() { :; }
511 }
512 ##
513 # Make our temporary files directory
514 # $1 - directory name
515 # $2 - user name
516 verify_tmpdir() {
517         if [[ ! $1 ]]; then
518                 die 1 'Temporary file path is unset! (This is a bug.)'
519         elif [[ -d $1 ]]; then
520                 cd "$1"
521         else
522                 die 1 "Unable to find a satisfactory location for temporary files ($1)"
523         fi
524         [[ $VERBOSE ]] && einfo "Temporary cache files are located in $PWD"
525         setup_rm
526 }
527 get_search_env() {
528         local new_env
529         local old_env
530         local uid=$(python -c 'import os; import pwd; print pwd.getpwuid(os.getuid())[0]')
531         # Find a place to put temporary files
532         if [[ "$uid" == "root" ]]; then
533                 local tmp_target="/var/cache/${APP_NAME}"
534         else
535                 local tmp_target="$(mktemp -d -t revdep-rebuild.XXXXXXXXXX)"
536         fi
537
538         # From here on all work is done inside the temporary directory
539         verify_tmpdir "$tmp_target"
540         WORKING_DIR="$tmp_target"
541
542         if [[ $SEARCH_BROKEN ]]; then
543                 SONAME_SEARCH="$SONAME"
544                 HEAD_TEXT="broken by a package update"
545                 OK_TEXT="Dynamic linking on your system is consistent"
546                 WORKING_TEXT="consistency"
547         else
548                 # first case is needed to test against /path/to/foo.so
549                 if [[ $SONAME = /* ]]; then
550                         # Set to "<space>$SONAME<space>"
551                         SONAME_SEARCH=" $SONAME "
552                         # Escape the "/" characters
553                         SONAME_SEARCH="${SONAME_SEARCH//\//\\/}"
554                 else
555                         # Set to "<tab>$SONAME<space>"
556                         SONAME_SEARCH=$'\t'"$SONAME "
557                 fi
558                 HEAD_TEXT="using $SONAME"
559                 OK_TEXT="There are no dynamic links to $SONAME"
560                 unset WORKING_TEXT
561         fi
562
563         # If any of our temporary files are older than 1 day, remove them all
564         if [[ ! $KEEP_TEMP ]]; then
565                 while read; do
566                         RM_OLD_TEMPFILES=1
567                         break
568                 done < <(find -L . -maxdepth 1 -type f -name '*.rr' -mmin +1440 -print 2>/dev/null)
569         fi
570
571         # Compare old and new environments
572         # Don't use our previous files if environment doesn't match
573         new_env=$(
574                 # We do not care if these emerge options change
575                 EMERGE_OPTIONS=(${EMERGE_OPTIONS[@]//--pretend/})
576                 EMERGE_OPTIONS=(${EMERGE_OPTIONS[@]//--fetchonly/})
577                 EMERGE_OPTIONS=(${EMERGE_OPTIONS[@]//--verbose/})
578                 cat <<- EOF
579                         SEARCH_DIRS="$SEARCH_DIRS"
580                         SEARCH_DIRS_MASK="$SEARCH_DIRS_MASK"
581                         LD_LIBRARY_MASK="$LD_LIBRARY_MASK"
582                         PORTAGE_ROOT="$PORTAGE_ROOT"
583                         EMERGE_OPTIONS="${EMERGE_OPTIONS[@]}"
584                         ORDER_PKGS="$ORDER_PKGS"
585                         FULL_LD_PATH="$FULL_LD_PATH"
586                 EOF
587         )
588         if [[ -r "$ENV_FILE" && -s "$ENV_FILE" ]]; then
589                 old_env=$(<"$ENV_FILE")
590                 if [[ $old_env != $new_env ]]; then
591                         ewarn 'Environment mismatch from previous run, deleting temporary files...'
592                         RM_OLD_TEMPFILES=1
593                 fi
594         else
595                 # No env file found, silently delete any other tempfiles that may exist
596                 RM_OLD_TEMPFILES=1
597         fi
598
599         # If we should remove old tempfiles, do so
600         if [[ $RM_OLD_TEMPFILES ]]; then
601                 rm -f "${FILES[@]}"
602         else
603                 for file in "${FILES[@]}"; do
604                         if [ -e "$file" ]; then
605                                 chown ${uid}:portage "$file"
606                                 chmod 700 "$file"
607                         fi
608                 done
609         fi
610
611         # Save the environment in a file for next time
612         echo "$new_env" > "$ENV_FILE"
613
614         [[ $VERBOSE ]] && echo $'\n'"$APP_NAME environment:"$'\n'"$new_env"
615
616         echo
617         einfo "Checking reverse dependencies"
618         einfo "Packages containing binaries and libraries $HEAD_TEXT"
619         einfo "will be emerged."
620 }
621
622 get_files() {
623         einfo "Collecting system binaries and libraries"
624         if [[ -r "$FILES_FILE" && -s "$FILES_FILE" ]]; then
625                 einfo "Found existing $FILES_FILE"
626         else
627                 # Be safe and remove any extraneous temporary files
628                 # Don't remove 0_env.rr - The first file in the array
629                 rm -f "${FILES[@]:1}"
630
631                 clean_trap "$FILES_FILE"
632
633                 if [[ $SEARCH_DIRS_MASK ]]; then
634                         findMask=($SEARCH_DIRS_MASK)
635                         findMask="${findMask[@]/#/-o -path }"
636                         findMask="( ${findMask#-o } ) -prune -o"
637                 fi
638                 # TODO: Check this -- afaict SEARCH_DIRS isn't an array, so this should just be $SEARCH_DIRS?
639                 find ${SEARCH_DIRS[@]} $findMask -type f \( -perm -u+x -o -perm -g+x -o -perm -o+x -o \
640                         -name '*.so' -o -name '*.so.*' -o -name '*.la' \) -print 2> /dev/null |
641                         sort -u > "$FILES_FILE" ||
642                         die $? "find failed to list binary files (This is a bug.)"
643                 einfo "Generated new $FILES_FILE"
644         fi
645 }
646 get_ldpath() {
647         local COMPLETE_LD_LIBRARY_PATH
648         [[ $SEARCH_BROKEN && $FULL_LD_PATH ]] || return
649         einfo 'Collecting complete LD_LIBRARY_PATH'
650         if [[ -r "$LDPATH_FILE" && -s "$LDPATH_FILE" ]]; then
651                 einfo "Found existing $LDPATH_FILE."
652         else
653                 clean_trap "$LDPATH_FILE"
654                 # Ensure that the "trusted" lib directories are at the start of the path
655                 COMPLETE_LD_LIBRARY_PATH=(
656                         /lib*
657                         /usr/lib*
658                         $(sed '/^#/d;s/#.*$//' < /etc/ld.so.conf)
659                         $(sed 's:/[^/]*$::' < "$FILES_FILE" | sort -ru)
660                 )
661                 IFS=':'
662                 COMPLETE_LD_LIBRARY_PATH="${COMPLETE_LD_LIBRARY_PATH[*]}"
663                 IFS="$OIFS"
664                 echo "$COMPLETE_LD_LIBRARY_PATH" > "$LDPATH_FILE"
665                 einfo "Generated new $LDPATH_FILE"
666         fi
667 }
668 main_checks() {
669         local target_file
670         local -a files
671         local i=0
672         local ldd_output
673         local ldd_status
674         local numFiles
675         local COMPLETE_LD_LIBRARY_PATH
676         if [[ $SEARCH_BROKEN && $FULL_LD_PATH ]]; then
677                 [[ -r "$LDPATH_FILE" && -s "$LDPATH_FILE" ]] ||
678                         die 1 "Unable to find $LDPATH_FILE"
679                 COMPLETE_LD_LIBRARY_PATH=$(<"$LDPATH_FILE")
680         fi
681         einfo "Checking dynamic linking $WORKING_TEXT"
682         if [[ -r "$BROKEN_FILE" && -s "$BROKEN_FILE" ]]; then
683                 einfo "Found existing $BROKEN_FILE."
684         else
685                 clean_trap "$BROKEN_FILE" "$ERRORS_FILE"
686                 files=($(<"$FILES_FILE"))
687                 numFiles="${#files[@]}"
688                 for target_file in "${files[@]}"; do
689                         if [[ $target_file != *.la ]]; then
690                                 # Note: double checking seems to be faster than single with complete path
691                                 # (special add ons are rare).
692                                 ldd_output=$(ldd "$target_file" 2>> "$ERRORS_FILE" | sort -u)
693                                 ldd_status=$? # TODO: Check this for problems with sort
694                                 # HACK: if LD_LIBRARY_MASK is null or undefined grep -vF doesn't work
695                                 if grep -vF "${LD_LIBRARY_MASK:=$'\a'}" <<< "$ldd_output" |
696                                         grep -q "$SONAME_SEARCH"; then
697                                         if [[ $SEARCH_BROKEN && $FULL_LD_PATH ]]; then
698                                                 if LD_LIBRARY_PATH="$COMPLETE_LD_LIBRARY_PATH" ldd "$target_file" 2>/dev/null |
699                                                         grep -vF "$LD_LIBRARY_MASK" | grep -q "$SONAME_SEARCH"; then
700                                                         # FIXME: I hate duplicating code
701                                                         # Only build missing direct dependencies
702                                                         MISSING_LIBS=$(
703                                                                 expr='s/[[:space:]]*\([^[:space:]]*\) => not found/\1/p'
704                                                                 sed -n "$expr" <<< "$ldd_output"
705                                                         )
706                                                         REQUIRED_LIBS=$(
707                                                                 expr='s/^[[:space:]]*NEEDED[[:space:]]*\([^[:space:]]*\).*/\1/p';
708                                                                 objdump -x "$target_file" | grep NEEDED | sed "$expr" | sort -u
709                                                         )
710                                                         MISSING_LIBS=$(grep -F "$REQUIRED_LIBS" <<< "$MISSING_LIBS")
711                                                         if [[ $MISSING_LIBS ]]; then
712                                                                 echo "obj $target_file" >> "$BROKEN_FILE"
713                                                                 echo_v "  broken $target_file (requires $MISSING_LIBS)"
714                                                         fi
715                                                 fi
716                                         else
717                                                 # FIXME: I hate duplicating code
718                                                 # Only rebuild for direct dependencies
719                                                 MISSING_LIBS=$(
720                                                         expr="/$SONAME_SEARCH/s/^[[:space:]]*\([^[:space:]]*\).*$/\1/p"
721                                                         sort -u <<< "$ldd_output" | sed -n "$expr"
722                                                 )
723                                                 REQUIRED_LIBS=$(
724                                                         expr='s/^[[:space:]]*NEEDED[[:space:]]*\([^[:space:]]*\).*/\1/p';
725                                                         objdump -x "$target_file" | grep NEEDED | sed "$expr" | sort -u
726                                                 )
727                                                 MISSING_LIBS=$(grep -F "$REQUIRED_LIBS" <<< "$MISSING_LIBS")
728                                                 if [[ $MISSING_LIBS ]]; then
729                                                         echo "obj $target_file" >> "$BROKEN_FILE"
730                                                         if [[ $SEARCH_BROKEN ]]; then
731                                                                 echo_v "  broken $target_file (requires $MISSING_LIBS)"
732                                                         else
733                                                                 echo_v "  found $target_file"
734                                                         fi
735                                                 fi
736                                         fi
737                                 fi
738                         elif [[ $SEARCH_BROKEN ]]; then
739                                 # Look for broken .la files
740                                 la_SEARCH_DIRS="$SEARCH_DIRS"
741                                 la_search_dir=""
742                                 la_broken=""
743                                 la_lib=""
744                                 for depend in $(
745                                         gawk -F"[=']" '/^dependency_libs/{
746                                                 print $3
747                                         }' "$target_file"
748                                 ); do
749                                         if [[ $depend = /* && ! -e $depend ]]; then
750                                                 echo "obj $target_file" >> "$BROKEN_FILE"
751                                                 echo_v "  broken $target_file (requires $depend)"
752                                         elif [[ $depend = -[LR]/* ]]; then
753                                                 if ! [[ $'\n'${la_SEARCH_DIRS}$'\n' == *$'\n'${depend#-?}$'\n'* ]]; then
754                                                         la_SEARCH_DIRS+=$'\n'"${depend#-?}"
755                                                 fi
756                                         elif [[ $depend = "-l"* ]]; then
757                                                 la_lib="lib${depend#-l}"
758                                                 la_broken="yes"
759                                                 IFS=$'\n'
760                                                 for la_search_dir in $la_SEARCH_DIRS; do
761                                                         if [[ -e ${la_search_dir}/${la_lib}.so || -e ${la_search_dir}/${la_lib}.a ]]; then
762                                                                 la_broken="no"
763                                                         fi
764                                                 done
765                                                 IFS="$OIFS"
766                                                 if [[ $la_broken = yes ]]; then
767                                                         echo "obj $target_file" >> "$BROKEN_FILE"
768                                                         echo_v "  broken $target_file (requires $depend)"
769                                                 fi
770                                         fi
771                                 done
772                                 unset la_SEARCH_DIRS la_search_dir la_broken la_lib
773                         fi
774                         [[ $VERBOSE ]] &&
775                                 progress $((++i)) $numFiles $target_file ||
776                                 progress $((++i)) $numFiles
777                 done
778                 if [[ $SEARCH_BROKEN ]]; then
779                         # Look for missing version
780                         while read target_file; do
781                                 echo "obj $target_file" >> "$BROKEN_FILE"
782                                 echo_v "  broken $target_file (no version information available)"
783                         done < <(
784                                 # Regexify LD_LIBRARY_MASK. Exclude it from the search.
785                                 LD_LIBRARY_MASK="${LD_LIBRARY_MASK//$'\n'/|}"
786                                 gawk -v ldmask="(${LD_LIBRARY_MASK//./\\\.})" '
787                                         /no version information available/ && $0 !~ ldmask {
788                                                 gsub(/[()]/, "", $NF)
789                                                 if (seen[$NF]++)  next
790                                                 print $NF
791                                         }' "$ERRORS_FILE"
792                         )
793                 fi
794                 [[ -r "$BROKEN_FILE" && -s "$BROKEN_FILE" ]] || clean_exit
795                 sort -u "$BROKEN_FILE" -o "$BROKEN_FILE"
796                 einfo "Generated new $BROKEN_FILE"
797         fi
798 }
799 get_packages() {
800         local target_file
801         local EXACT_PKG
802         local PKG
803         local obj
804         einfo 'Assigning files to packages'
805         if [[ -r "$RAW_FILE" && -s "$RAW_FILE" ]]; then
806                 einfo "Found existing $RAW_FILE"
807         else
808                 clean_trap "$RAW_FILE" "$OWNERS_FILE"
809                 while read obj target_file; do
810                         EXACT_PKG=$(get_file_owner $target_file)
811                         if [[ $EXACT_PKG ]]; then
812                                 # Strip version information
813                                 PKG="${EXACT_PKG%%-r[[:digit:]]*}"
814                                 PKG="${PKG%-*}"
815                                 echo "$EXACT_PKG" >> "$RAW_FILE"
816                                 echo "$target_file -> $EXACT_PKG" >> "$OWNERS_FILE"
817                                 echo_v "  $target_file -> $PKG"
818                         else
819                                 ewarn " !!! $target_file not owned by any package is broken !!!"
820                                 echo "$target_file -> (none)" >> "$OWNERS_FILE"
821                                 echo_v "  $target_file -> (none)"
822                         fi
823                 done < "$BROKEN_FILE"
824                 einfo "Generated new $RAW_FILE and $OWNERS_FILE"
825         fi
826         # if we find '(none)' on every line, exit out
827         if ! grep -qvF '(none)' "$OWNERS_FILE"; then
828                 ewarn "Found some broken files, but none of them were associated with known packages"
829                 ewarn "Unable to proceed with automatic repairs."
830                 ewarn "The broken files are listed in $OWNERS_FILE"
831                 if [[ $VERBOSE ]]; then
832                         ewarn "The broken files are:"
833                         while read filename junk; do
834                                 ewarn "  $filename"
835                         done < "$OWNERS_FILE"
836                 fi
837                 exit 0 # FIXME: Should we exit 1 here?
838         fi
839 }
840 clean_packages() {
841         einfo 'Cleaning list of packages to rebuild'
842         if [[ -r "$PKGS_FILE" && -s "$PKGS_FILE" ]]; then
843                 einfo "Found existing $PKGS_FILE"
844         else
845                 sort -u "$RAW_FILE" > "$PKGS_FILE"
846                 einfo "Generated new $PKGS_FILE"
847         fi
848 }
849 assign_packages_to_ebuilds() {
850         local EXACT_PKG
851         local PKG
852         local SLOT
853         einfo 'Assigning packages to ebuilds'
854         if [[ -r "$EBUILDS_FILE" && -s "$EBUILDS_FILE" ]]; then
855                 einfo "Found existing $EBUILDS_FILE"
856         elif [[ -r "$PKGS_FILE" && -s "$PKGS_FILE" ]]; then
857                         clean_trap "$EBUILDS_FILE"
858                         while read EXACT_PKG; do
859                                 # Get the slot
860                                 PKG="${EXACT_PKG%%-r[[:digit:]]*}"
861                                 PKG="${PKG%-*}"
862                                 SLOT=$(</var/db/pkg/$EXACT_PKG/SLOT)
863                                 echo "$PKG:$SLOT"
864                         done < "$PKGS_FILE" > "$EBUILDS_FILE"
865                         einfo "Generated new $EBUILDS_FILE"
866         else
867                 einfo 'Nothing to rebuild.'
868                 die 1 '(The program should have already quit, so this is a minor bug.)'
869         fi
870 }
871 get_exact_ebuilds() {
872         einfo 'Assigning files to ebuilds'
873         if [[ -r $EBUILDS_FILE && -s $EBUILDS_FILE ]]; then
874                 einfo "Found existing $EBUILDS_FILE"
875         elif [[ -r $BROKEN_FILE && -s $BROKEN_FILE ]]; then
876                 rebuildList=" $(<"$BROKEN_FILE") "
877                 rebuildList=(${rebuildList//[[:space:]]obj[[:space:]]/ })
878                 get_file_owner "${rebuildList[@]}" | sed 's/^/=/' > "$EBUILDS_FILE"
879                 einfo "Generated new $EBUILDS_FILE"
880         else
881                 einfo 'Nothing to rebuild.'
882                 die 1 '(The program should have already quit, so this is a minor bug.)'
883         fi
884 }
885 list_skipped_packages() {
886         ewarn
887         ewarn 'Portage could not find any version of the following packages it could build:'
888         ewarn "${SKIP_LIST[@]}"
889         ewarn
890         ewarn '(Perhaps they are masked, blocked, or removed from portage.)'
891         ewarn 'Try to emerge them manually.'
892         ewarn
893 }
894 get_build_order() {
895         local -r OLD_EMERGE_DEFAULT_OPTS="$EMERGE_DEFAULT_OPTS"
896         local RAW_REBUILD_LIST
897         local REBUILD_GREP
898         local i
899         if [[ ! $ORDER_PKGS ]]; then
900                 einfo 'Skipping package ordering'
901                 return
902         fi
903         einfo 'Evaluating package order'
904         if [[ -r "$ORDER_FILE" && -s "$ORDER_FILE" ]]; then
905                 einfo "Found existing $ORDER_FILE"
906         else
907                 clean_trap "$ORDER_FILE"
908                 RAW_REBUILD_LIST=$(<"$EBUILDS_FILE")
909                 if [[ $RAW_REBUILD_LIST ]]; then
910                         export EMERGE_DEFAULT_OPTS="--nospinner --pretend --oneshot --quiet"
911                         RAW_REBUILD_LIST=($RAW_REBUILD_LIST) # convert into array
912                         # If PACKAGE_NAMES is defined we're using slots, not versions
913                         if [[ $PACKAGE_NAMES ]]; then
914                                 # Eliminate atoms that can't be built
915                                 for i in "${!RAW_REBUILD_LIST[@]}"; do
916                                         if [[ "${RAW_REBUILD_LIST[i]}" = *[A-Za-z]* ]]; then
917                                                 portageq best_visible "$PORTAGE_ROOT" "${RAW_REBUILD_LIST[i]}" >/dev/null && continue
918                                                 SKIP_LIST+=("${RAW_REBUILD_LIST[i]}")
919                                         fi
920                                         unset RAW_REBUILD_LIST[i]
921                                 done
922                                 # If RAW_REBUILD_LIST is empty, then we have nothing to build.
923                                 if (( ${#RAW_REBUILD_LIST[@]} == 0 )); then
924                                         if (( ${#SKIP_LIST[@]} == 0 )); then
925                                                 ewarn "The list of packages to skip is empty, but there are no"
926                                                 ewarn "packages listed to rebuild either. (This is a bug.)"
927                                         else
928                                                 list_skipped_packages
929                                         fi
930                                         die 1 'Warning: Portage cannot rebuild any of the necessary packages.'
931                                 fi
932                         fi
933                         RAW_REBUILD_LIST="${RAW_REBUILD_LIST[@]}"
934                         REBUILD_GREP=$(emerge --nodeps $RAW_REBUILD_LIST | sed 's/\[[^]]*\]//g')
935                         if (( ${PIPESTATUS[0]} == 0 )); then
936                                 emerge --deep $RAW_REBUILD_LIST |
937                                         sed 's/\[[^]]*\]//g' |
938                                         grep -F "$REBUILD_GREP" > "$ORDER_FILE"
939                         fi
940
941                         # Here we use the PIPESTATUS from the second emerge, the --deep one.
942                         if (( ${PIPESTATUS[0]} != 0 )); then
943                                         eerror
944                                         eerror 'Warning: Failed to resolve package order.'
945                                         eerror 'Will merge in arbitrary order'
946                                         eerror
947                                         cat <<- EOF
948                                                 Possible reasons:
949                                                 - An ebuild is no longer in the portage tree.
950                                                 - An ebuild is masked, use /etc/portage/packages.keyword
951                                                         and/or /etc/portage/package.unmask to unmask it
952                                         EOF
953                                         countdown 5
954                                         rm -f "$ORDER_FILE"
955                         fi
956                         export EMERGE_DEFAULT_OPTS="$OLD_EMERGE_DEFAULT_OPTS"
957                 else
958                         einfo 'Nothing to rebuild.'
959                         die 1 '(The program should have already quit, so this is a minor bug.)'
960                 fi
961         fi
962         [[ -r "$ORDER_FILE" && -s "$ORDER_FILE" ]] && einfo "Generated new $ORDER_FILE"
963 }
964
965 show_unowned_files() {
966         if grep -qF '(none)' "$OWNERS_FILE"; then
967                 ewarn "Found some broken files that weren't associated with known packages"
968                 ewarn "The broken files are:"
969                 while read filename junk; do
970                         [[ $junk = *none* ]] && ewarn "  $filename"
971                 done < "$OWNERS_FILE" | gawk '!s[$0]++' # (omit dupes)
972         fi
973 }
974 ##
975 # Setup portage and the search paths
976 setup_portage() {
977         local PORTAGE_NICENESS=$(portageq envvar PORTAGE_NICENESS)
978         PORTAGE_ROOT=$(portageq envvar ROOT)
979
980         # Obey PORTAGE_NICENESS
981         if [[ $PORTAGE_NICENESS ]]; then
982                 renice $PORTAGE_NICENESS $$ > /dev/null
983                 # Since we have already set our nice value for our processes,
984                 # reset PORTAGE_NICENESS to zero to avoid having emerge renice again.
985                 export PORTAGE_NICENESS="0"
986         fi
987
988         PORTAGE_ROOT="${PORTAGE_ROOT:-/}"
989 }
990
991 ##
992 # Setup the paths to search (and filter the ones to avoid)
993 setup_search_paths_and_masks() {
994         local configfile sdir mdir skip_me filter_SEARCH_DIRS
995
996         einfo "Configuring search environment for $APP_NAME"
997
998         # Update the incremental variables using /etc/profile.env, /etc/ld.so.conf,
999         # portage, and the environment
1000
1001         # Read the incremental variables from environment and portage
1002         # Until such time as portage supports these variables as incrementals
1003         # The value will be what is in /etc/make.conf
1004         SEARCH_DIRS+=" "$(unset SEARCH_DIRS; portageq envvar SEARCH_DIRS)
1005         SEARCH_DIRS_MASK+=" "$(unset SEARCH_DIRS_MASK; portageq envvar SEARCH_DIRS_MASK)
1006         LD_LIBRARY_MASK+=" "$(unset LD_LIBRARY_MASK; portageq envvar LD_LIBRARY_MASK)
1007
1008         # Add the defaults
1009         if [[ -d /etc/revdep-rebuild ]]; then
1010                 for configfile in /etc/revdep-rebuild/*; do
1011                         SEARCH_DIRS+=" "$(. $configfile; echo $SEARCH_DIRS)
1012                         SEARCH_DIRS_MASK+=" "$(. $configfile; echo $SEARCH_DIRS_MASK)
1013                         LD_LIBRARY_MASK+=" "$(. $configfile; echo $LD_LIBRARY_MASK)
1014                 done
1015         else
1016                 SEARCH_DIRS+=" /bin /sbin /usr/bin /usr/sbin /lib* /usr/lib*"
1017                 SEARCH_DIRS_MASK+=" /opt/OpenOffice /usr/lib/openoffice"
1018                 LD_LIBRARY_MASK+=" libodbcinst.so libodbc.so libjava.so libjvm.so"
1019         fi
1020
1021         # Get the ROOTPATH and PATH from /etc/profile.env
1022         if [[ -r "/etc/profile.env" && -s "/etc/profile.env" ]]; then
1023                 SEARCH_DIRS+=" "$(. /etc/profile.env; /usr/bin/tr ':' ' ' <<< "$ROOTPATH $PATH")
1024         fi
1025
1026         # Get the directories from /etc/ld.so.conf
1027         if [[ -r /etc/ld.so.conf && -s /etc/ld.so.conf ]]; then
1028                 SEARCH_DIRS+=" "$(sed '/^#/d;s/#.*$//' /etc/ld.so.conf)
1029         fi
1030
1031         # Set the final variables
1032         SEARCH_DIRS=$(clean_var <<< "$SEARCH_DIRS")
1033         SEARCH_DIRS_MASK=$(clean_var <<< "$SEARCH_DIRS_MASK")
1034         LD_LIBRARY_MASK=$(clean_var <<< "$LD_LIBRARY_MASK")
1035         # Filter masked paths from SEARCH_DIRS
1036         for sdir in ${SEARCH_DIRS} ; do
1037                 skip_me=
1038                 for mdir in ${SEARCH_DIRS_MASK}; do
1039                         [[ ${sdir} == ${mdir}/* ]] && skip_me=1 && break
1040                 done
1041                 [[ -n ${skip_me} ]] || filter_SEARCH_DIRS+=" ${sdir}"
1042         done
1043         SEARCH_DIRS=$(clean_var <<< "${filter_SEARCH_DIRS}")
1044         [[ $SEARCH_DIRS ]] || die 1 "No search defined -- this is a bug."
1045 }
1046 ##
1047 # Rebuild packages owning broken binaries
1048 rebuild() {
1049         if [[ -r $LIST.5_order && -s $LIST.5_order ]]; then
1050                 REBUILD_LIST=( $(<"$LIST.5_order") )
1051                 REBUILD_LIST="${REBUILD_LIST[@]/#/=}"
1052         else
1053                 REBUILD_LIST=$(sort -u "$EBUILDS_FILE")
1054         fi
1055
1056         trap - SIGHUP SIGINT SIGQUIT SIGABRT SIGTERM
1057
1058         einfo 'All prepared. Starting rebuild'
1059         echo "emerge --oneshot ${EMERGE_OPTIONS[@]} $REBUILD_LIST"
1060
1061         is_real_merge && countdown 10
1062
1063         # Link file descriptor #6 with stdin so --ask will work
1064         exec 6<&0
1065
1066         # Run in background to correctly handle Ctrl-C
1067         {
1068                 EMERGE_DEFAULT_OPTS="--oneshot ${EMERGE_OPTIONS[@]}" emerge $REBUILD_LIST <&6
1069                 echo $? > "$STATUS_FILE"
1070         } &
1071         wait
1072
1073         # Now restore stdin from fd #6, where it had been saved, and close fd #6 ( 6<&- ) to free it for other processes to use.
1074         exec 0<&6 6<&-
1075 }
1076 ##
1077 # Finish up
1078 cleanup() {
1079         if (( $(<"$STATUS_FILE") != 0 )); then
1080                 ewarn
1081                 ewarn "$APP_NAME failed to emerge all packages."
1082                 ewarn 'you have the following choices:'
1083                 einfo "- If emerge failed during the build, fix the problems and re-run $APP_NAME."
1084                 einfo '- Use /etc/portage/package.keywords to unmask a newer version of the package.'
1085                 einfo "  (and remove $ORDER_FILE to be evaluated again)"
1086                 einfo '- Modify the above emerge command and run it manually.'
1087                 einfo '- Compile or unmerge unsatisfied packages manually,'
1088                 einfo '  remove temporary files, and try again.'
1089                 einfo '  (you can edit package/ebuild list first)'
1090                 einfo
1091                 einfo 'To remove temporary files, please run:'
1092                 einfo "rm ${WORKING_DIR}/*.rr"
1093                 show_unowned_files
1094                 exit $EMERGE_STATUS
1095         elif is_real_merge; then
1096                 trap_cmd() {
1097                         eerror "terminated. Please remove the temporary files manually:"
1098                         eerror "rm ${WORKING_DIR}/*.rr"
1099                         exit 1
1100                 }
1101                 [[ "${SKIP_LIST[@]}" != "" ]] && list_skipped_packages
1102                 trap trap_cmd SIGHUP SIGINT SIGQUIT SIGABRT SIGTERM
1103                 einfo 'Build finished correctly. Removing temporary files...'
1104                 einfo
1105                 einfo 'You can re-run revdep-rebuild to verify that all libraries and binaries'
1106                 einfo 'are fixed. Possible reasons for remaining inconsistencies include:'
1107                 einfo '  orphaned files'
1108                 einfo '  deep dependencies'
1109                 einfo "  packages installed outside of portage's control"
1110                 einfo '  specially-evaluated libraries'
1111                 if [[ -r "$OWNERS_FILE" && -s "$OWNERS_FILE" ]]; then
1112                         show_unowned_files
1113                 fi
1114                 [[ $KEEP_TEMP ]] || rm "${FILES[@]}"
1115         else
1116                 einfo 'Now you can remove -p (or --pretend) from arguments and re-run revdep-rebuild.'
1117         fi
1118 }
1119
1120 main "$@"