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