Speedup portageq queries. Include FuzzyRay's patch to respect EMERGE_DEFAULT_OPTS.
[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         portage_settings
93         get_opts "$@"
94         setup_portage
95         setup_search_paths_and_masks
96         get_search_env
97         echo
98
99         # Search for broken binaries
100         get_files
101         get_ldpath
102         main_checks
103
104         # Associate broken binaries with packages to rebuild
105         if [[ $PACKAGE_NAMES ]]; then
106                 get_packages
107                 clean_packages
108                 assign_packages_to_ebuilds
109         else
110                 get_exact_ebuilds
111         fi
112
113         # Rebuild packages owning broken binaries
114         get_build_order
115         rebuild
116
117         # All done
118         cleanup
119 }
120 ##
121 # Refuse to delete anything before we cd to our tmpdir
122 # (See mkdir_and_cd_to_tmpdir()
123 rm() {
124         eerror "I was instructed to rm '$@'"
125         die 1 "Refusing to delete anything before changing to temporary directory."
126 }
127 : <<'EW'
128 ##
129 # GNU find has -executable, but if our users' finds do not have that flag
130 # we emulate it with this function. Also emulates -writable and -readable.
131 # Usage: find PATH ARGS -- use find like normal, except use -executable instead
132 # of various versions of -perm /+ blah blah and hacks
133 find() {
134         hash find || { die 1 'find not found!'; }
135         # We can be pretty sure find itself should be executable.
136         local testsubject="$(type -P find)"
137         if [[ $(command find "$testsubject" -executable 2> /dev/null) ]]; then
138                 unset -f find # We can just use the command find
139         elif [[ $(command find "$testsubject" -perm /u+x 2> /dev/null) ]]; then
140                 find() {
141                         a=(${@//-executable/-perm \/u+x})
142                         a=(${a[@]//-writable/-perm \/u+w})
143                         a=(${a[@]//-readable/-perm \/r+w})
144                         command find "${a[@]}"
145                 }
146         elif [[ $(command find "$testsubject" -perm +u+x 2> /dev/null) ]]; then
147                 find() {
148                         a=(${@//-executable/-perm +u+x})
149                         a=(${a[@]//-writable/-perm +u+w})
150                         a=(${a[@]//-readable/-perm +r+w})
151                         command find "${a[@]}"
152                 }
153         else # Last resort
154                 find() {
155                         a=(${@//-executable/-exec test -x '{}' \; -print})
156                         a=(${a[@]//-writable/-exec test -w '{}' \; -print})
157                         a=(${a[@]//-readable/-exec test -r '{}' \; -print})
158                         command find "${a[@]}"
159                 }
160         fi
161         find "$@"
162 }
163 EW
164
165 print_usage() {
166 cat << EOF
167 Usage: $APP_NAME [OPTIONS] [--] [EMERGE_OPTIONS]
168
169 Broken reverse dependency rebuilder.
170
171   -C, --nocolor        Turn off colored output
172   -d, --debug          Print way too much information (uses bash's set -xv)
173   -e, --exact          Emerge based on exact package version
174   -h, --help           Print this usage
175   -i, --ignore         Ignore temporary files from previous runs
176   -k, --keep-temp      Do not delete temporary files on exit
177   -L, --library NAME   Emerge existing packages that use the library with NAME
178       --library=NAME   NAME can be a full path to the library or a basic
179                        regular expression (man grep)
180   -l, --no-ld-path     Do not set LD_LIBRARY_PATH
181   -o, --no-order       Do not check the build order
182                        (Saves time, but may cause breakage.)
183   -p, --pretend        Do a trial run without actually emerging anything
184                        (also passed to emerge command)
185   -P, --no-progress    Turn off the progress meter
186   -q, --quiet          Be less verbose (also passed to emerge command)
187   -v, --verbose        Be more verbose (also passed to emerge command)
188
189 Calls emerge, options after -- are ignored by $APP_NAME
190 and passed directly to emerge.
191
192 Report bugs to <http://bugs.gentoo.org>
193 EOF
194 }
195 ##
196 # Usage: progress i n
197 #        i: current item
198 #        n: total number of items to process
199 progress() {
200         if [[ -t 1 ]]; then
201                 progress() {
202                         local curProg=$(( $1 * 100 / $2 ))
203                         (( curProg == OLDPROG )) && return # no change, output nothing
204                         OLDPROG="$curProg" # must be a global variable
205                         (( $1 == $2 )) && local lb=$'\n'
206                         echo -ne '\r                         \r'"[ $curProg% ] $lb"
207                 }
208                 progress $@
209         else # STDOUT is not a tty. Disable progress meter.
210                 progress() { :; }
211         fi
212 }
213 ##
214 # Usage: countdown n
215 #        n: number of seconds to count
216 countdown() {
217         local i
218         for ((i=1; i<$1; i++)); do
219                 echo -ne '\a.'
220                 ((i<$1)) && sleep 1
221         done
222         echo -e '\a.'
223 }
224 ##
225 # Replace whitespace with linebreaks, normalize repeated '/' chars, and sort -u
226 # (If any libs have whitespace in their filenames, someone needs punishment.)
227 clean_var() {
228         gawk 'BEGIN         {RS="[[:space:]]"}
229              /-\*/         {exit}
230              /[^[:space:]]/ {gsub(/\/\/+/, "/"); print}' | sort -u
231 }
232 ##
233 # Exit and optionally output to sterr
234 die() {
235         local status=$1
236         shift
237         eerror "$@"
238         exit $status
239 }
240 ##
241 # What to do when dynamic linking is consistent
242 clean_exit() {
243         if [[ ! $KEEP_TEMP ]]; then
244                 rm -f "${FILES[@]}"
245                 if [[ "$WORKING_DIR" != "/var/cache/${APP_NAME}" ]]; then
246                         # Remove the working directory
247                         builtin cd; rmdir "$WORKING_DIR"
248                 fi
249         fi
250         echo
251         einfo "$OK_TEXT... All done. "
252         exit 0
253 }
254 ##
255 # Get the name of the package that owns a file or list of files given as args.
256 # NOTE: depends on app-misc/realpath!
257 get_file_owner() {
258         local IFS=$'\n'
259         # Add a space to the end of each object name to prevent false
260         # matches, for example /usr/bin/dia matching /usr/bin/dialog (bug #196460).
261         # The same for "${rpath} ".
262
263         rpath=$(realpath "${*}" 2>/dev/null)
264         # To ensure we always have something in rpath...
265         [[ -z $rpath ]] && rpath=${*}
266
267         find -L /var/db/pkg -name CONTENTS -print0 |
268                 xargs -0 grep -Fl -e "${*} " -e "${rpath} " |
269                 sed 's:/var/db/pkg/\(.*\)/CONTENTS:\1:'
270 }
271 ##
272 # Normalize some EMERGE_OPTIONS
273 normalize_emerge_opts() {
274         # Normalize some EMERGE_OPTIONS
275         EMERGE_OPTIONS=(${EMERGE_OPTIONS[@]/%-p/--pretend})
276         EMERGE_OPTIONS=(${EMERGE_OPTIONS[@]/%-f/--fetchonly})
277         EMERGE_OPTIONS=(${EMERGE_OPTIONS[@]/%-v/--verbose})
278 }
279 ##
280 # Use the color preference from portage
281 setup_color() {
282         # This should still work if NOCOLOR is set by the -C flag or in the user's
283         # environment.
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 # Get multiple portage variables at once to speedup revdep-rebuild.
976 portage_settings() {
977         local results=()
978
979         local query_vars=(
980                 ROOT
981                 PORTAGE_NICENESS
982                 EMERGE_DEFAULT_OPTS
983                 NOCOLOR
984                 SEARCH_DIRS
985                 SEARCH_DIRS_MASK
986                 LD_LIBRARY_MASK
987         )
988
989         # one value per line
990         IFS=$'\n'
991         results=( $(unset SEARCH_DIRS; unset SEARCH_DIRS_MASK; unset LD_LIBRARY_MASK; portageq envvar ${query_vars[*]}) )
992         IFS=$OIFS
993
994         PORTAGE_ROOT=${results[0]}
995         PORTAGE_NICENESS=${results[1]}
996         EMERGE_DEFAULT_OPTS=${results[2]}
997         export NOCOLOR=${results[3]}
998         SEARCH_DIRS+=" "${results[4]}
999         SEARCH_DIRS_MASK+=" "${results[5]}
1000         LD_LIBRARY_MASK+=" "${results[6]}
1001 }
1002
1003 ##
1004 # Setup portage and the search paths
1005 setup_portage() {
1006         # Obey PORTAGE_NICENESS
1007         if [[ $PORTAGE_NICENESS ]]; then
1008                 renice $PORTAGE_NICENESS $$ > /dev/null
1009                 # Since we have already set our nice value for our processes,
1010                 # reset PORTAGE_NICENESS to zero to avoid having emerge renice again.
1011                 export PORTAGE_NICENESS="0"
1012         fi
1013
1014         PORTAGE_ROOT="${PORTAGE_ROOT:-/}"
1015 }
1016
1017 ##
1018 # Setup the paths to search (and filter the ones to avoid)
1019 setup_search_paths_and_masks() {
1020         local configfile sdir mdir skip_me filter_SEARCH_DIRS
1021
1022         einfo "Configuring search environment for $APP_NAME"
1023
1024         # Update the incremental variables using /etc/profile.env, /etc/ld.so.conf,
1025         # portage, and the environment
1026
1027         # Read the incremental variables from environment and portage
1028         # Until such time as portage supports these variables as incrementals
1029         # The value will be what is in /etc/make.conf
1030 #       SEARCH_DIRS+=" "$(unset SEARCH_DIRS; portageq envvar SEARCH_DIRS)
1031 #       SEARCH_DIRS_MASK+=" "$(unset SEARCH_DIRS_MASK; portageq envvar SEARCH_DIRS_MASK)
1032 #       LD_LIBRARY_MASK+=" "$(unset LD_LIBRARY_MASK; portageq envvar LD_LIBRARY_MASK)
1033
1034         # Add the defaults
1035         if [[ -d /etc/revdep-rebuild ]]; then
1036                 for configfile in /etc/revdep-rebuild/*; do
1037                         SEARCH_DIRS+=" "$(. $configfile; echo $SEARCH_DIRS)
1038                         SEARCH_DIRS_MASK+=" "$(. $configfile; echo $SEARCH_DIRS_MASK)
1039                         LD_LIBRARY_MASK+=" "$(. $configfile; echo $LD_LIBRARY_MASK)
1040                 done
1041         else
1042                 SEARCH_DIRS+=" /bin /sbin /usr/bin /usr/sbin /lib* /usr/lib*"
1043                 SEARCH_DIRS_MASK+=" /opt/OpenOffice /usr/lib/openoffice"
1044                 LD_LIBRARY_MASK+=" libodbcinst.so libodbc.so libjava.so libjvm.so"
1045         fi
1046
1047         # Get the ROOTPATH and PATH from /etc/profile.env
1048         if [[ -r "/etc/profile.env" && -s "/etc/profile.env" ]]; then
1049                 SEARCH_DIRS+=" "$(. /etc/profile.env; /usr/bin/tr ':' ' ' <<< "$ROOTPATH $PATH")
1050         fi
1051
1052         # Get the directories from /etc/ld.so.conf
1053         if [[ -r /etc/ld.so.conf && -s /etc/ld.so.conf ]]; then
1054                 SEARCH_DIRS+=" "$(sed '/^#/d;s/#.*$//' /etc/ld.so.conf)
1055         fi
1056
1057         # Set the final variables
1058         SEARCH_DIRS=$(clean_var <<< "$SEARCH_DIRS")
1059         SEARCH_DIRS_MASK=$(clean_var <<< "$SEARCH_DIRS_MASK")
1060         LD_LIBRARY_MASK=$(clean_var <<< "$LD_LIBRARY_MASK")
1061         # Filter masked paths from SEARCH_DIRS
1062         for sdir in ${SEARCH_DIRS} ; do
1063                 skip_me=
1064                 for mdir in ${SEARCH_DIRS_MASK}; do
1065                         [[ ${sdir} == ${mdir}/* ]] && skip_me=1 && break
1066                 done
1067                 [[ -n ${skip_me} ]] || filter_SEARCH_DIRS+=" ${sdir}"
1068         done
1069         SEARCH_DIRS=$(clean_var <<< "${filter_SEARCH_DIRS}")
1070         [[ $SEARCH_DIRS ]] || die 1 "No search defined -- this is a bug."
1071 }
1072 ##
1073 # Rebuild packages owning broken binaries
1074 rebuild() {
1075         if [[ -r $LIST.5_order && -s $LIST.5_order ]]; then
1076                 REBUILD_LIST=( $(<"$LIST.5_order") )
1077                 REBUILD_LIST="${REBUILD_LIST[@]/#/=}"
1078         else
1079                 REBUILD_LIST=$(sort -u "$EBUILDS_FILE")
1080         fi
1081
1082         trap - SIGHUP SIGINT SIGQUIT SIGABRT SIGTERM
1083
1084         einfo 'All prepared. Starting rebuild'
1085         echo "emerge --oneshot ${EMERGE_OPTIONS[@]} ${EMERGE_DEFAULT_OPTS} $REBUILD_LIST"
1086
1087         is_real_merge && countdown 10
1088
1089         # Link file descriptor #6 with stdin so --ask will work
1090         exec 6<&0
1091
1092         # Run in background to correctly handle Ctrl-C
1093         {
1094                 emerge --oneshot ${EMERGE_OPTIONS[@]} ${EMERGE_DEFAULT_OPTS} $REBUILD_LIST <&6
1095                 echo $? > "$STATUS_FILE"
1096         } &
1097         wait
1098
1099         # Now restore stdin from fd #6, where it had been saved, and close fd #6 ( 6<&- ) to free it for other processes to use.
1100         exec 0<&6 6<&-
1101 }
1102 ##
1103 # Finish up
1104 cleanup() {
1105         if (( $(<"$STATUS_FILE") != 0 )); then
1106                 ewarn
1107                 ewarn "$APP_NAME failed to emerge all packages."
1108                 ewarn 'you have the following choices:'
1109                 einfo "- If emerge failed during the build, fix the problems and re-run $APP_NAME."
1110                 einfo '- Use /etc/portage/package.keywords to unmask a newer version of the package.'
1111                 einfo "  (and remove $ORDER_FILE to be evaluated again)"
1112                 einfo '- Modify the above emerge command and run it manually.'
1113                 einfo '- Compile or unmerge unsatisfied packages manually,'
1114                 einfo '  remove temporary files, and try again.'
1115                 einfo '  (you can edit package/ebuild list first)'
1116                 einfo
1117                 einfo 'To remove temporary files, please run:'
1118                 einfo "rm ${WORKING_DIR}/*.rr"
1119                 show_unowned_files
1120                 exit $EMERGE_STATUS
1121         elif is_real_merge; then
1122                 trap_cmd() {
1123                         eerror "terminated. Please remove the temporary files manually:"
1124                         eerror "rm ${WORKING_DIR}/*.rr"
1125                         exit 1
1126                 }
1127                 [[ "${SKIP_LIST[@]}" != "" ]] && list_skipped_packages
1128                 trap trap_cmd SIGHUP SIGINT SIGQUIT SIGABRT SIGTERM
1129                 einfo 'Build finished correctly. Removing temporary files...'
1130                 einfo
1131                 einfo 'You can re-run revdep-rebuild to verify that all libraries and binaries'
1132                 einfo 'are fixed. Possible reasons for remaining inconsistencies include:'
1133                 einfo '  orphaned files'
1134                 einfo '  deep dependencies'
1135                 einfo "  packages installed outside of portage's control"
1136                 einfo '  specially-evaluated libraries'
1137                 if [[ -r "$OWNERS_FILE" && -s "$OWNERS_FILE" ]]; then
1138                         show_unowned_files
1139                 fi
1140                 [[ $KEEP_TEMP ]] || rm "${FILES[@]}"
1141         else
1142                 einfo 'Now you can remove -p (or --pretend) from arguments and re-run revdep-rebuild.'
1143         fi
1144 }
1145
1146 main "$@"