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