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