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