updated on Mon Jan 23 00:00:36 UTC 2012
[aur-mirror.git] / findbrokenpkgs / findbrokenpkgs-1.1.sh
blobee596285997e0c39f699f344d3835e871eed765f
1 #! /bin/bash
2 # Distributed under the terms of the GNU General Public License v2
4 # Shamelessly copied mostly from Gentoo's revdep-rebuild utility.
5 # http://sources.gentoo.org/viewcvs.py/gentoo-x86/app-portage/gentoolkit/
6 # $ equery belongs /usr/bin/revdep-rebuild
7 # app-portage/gentoolkit-0.2.3-r1
8 # $ cat /usr/portage/app-portage/gentoolkit/gentoolkit-0.2.3-r1.ebuild | grep LICENSE
9 # LICENSE="GPL-2"
11 # findbrokenpkgs for Arch Linux.
12 # Converted to use pacman instead of emerge by Paul Bredbury <brebs@sent.com>
14 # v1.1 - files now in $HOME/.findbrokenpkgs/ directory - based on changes from
15 # Stefan Husmann, repackaged by Jaroslav Lichtblau
17 # Customizable variables:
19 # LD_LIBRARY_MASK - Mask of specially evaluated libraries
20 # SEARCH_DIRS - List of directories to search for executables and libraries
21 # SEARCH_DIRS_MASK - List of directories to not search
23 # These variables can be prepended to by setting the variable in
24 # your environment prior to execution.
26 # An entry of "-*" means to clear the variable from that point forward.
27 # Example: env SEARCH_DIRS="/usr/bin -*" findbrokenpkgs will set SEARCH_DIRS
28 # to contain only /usr/bin
30 if [ "$1" = "-h" -o "$1" = "-help" -o "$1" = "--help" ] ; then
31 echo "Broken package identifier, version 1.0"
32 echo "Checks dynamic library linking."
33 echo
34 echo "Usage: $0 [OPTIONS]"
35 echo
36 echo " -nc, --no-color Turn off colored output"
37 echo " -nw, --no-warning Disable newbie-friendly warning"
38 echo " -q, --quiet Be less verbose"
39 echo
40 echo "Report bugs to http://bbs.archlinux.org/viewtopic.php?id=13882"
41 exit 0
44 # Update the incremental variables using /etc/profile.env, /etc/ld.so.conf,
45 # and the environment.
47 # Read the incremental variables from environment
48 PRELIMINARY_SEARCH_DIRS="$SEARCH_DIRS"
49 PRELIMINARY_SEARCH_DIRS_MASK="$SEARCH_DIRS_MASK"
50 PRELIMINARY_LD_LIBRARY_MASK="$LD_LIBRARY_MASK"
51 SONAME_SEARCH="not found"
52 SONAME_GREP=grep
54 # Add the defaults
55 if [ -d /etc/findbrokenpkgs ] ; then
56 for file in $(ls /etc/findbrokenpkgs) ; do
57 PRELIMINARY_SEARCH_DIRS="$PRELIMINARY_SEARCH_DIRS $(. /etc/findbrokenpkgs/${file}; echo $SEARCH_DIRS)"
58 PRELIMINARY_SEARCH_DIRS_MASK="$PRELIMINARY_SEARCH_DIRS_MASK $(. /etc/findbrokenpkgs/${file}; echo $SEARCH_DIRS_MASK)"
59 PRELIMINARY_LD_LIBRARY_MASK="$PRELIMINARY_LD_LIBRARY_MASK $(. /etc/findbrokenpkgs/${file}; echo $LD_LIBRARY_MASK)"
60 done
61 else
62 PRELIMINARY_SEARCH_DIRS="$PRELIMINARY_SEARCH_DIRS /bin /sbin /usr/bin /usr/sbin /lib* /usr/lib*"
63 # openoffice is a binary, and we don't want to check that monster
64 PRELIMINARY_SEARCH_DIRS_MASK="$PRELIMINARY_SEARCH_DIRS_MASK /opt/openoffice"
65 # Binary libraries
66 PRELIMINARY_LD_LIBRARY_MASK="$PRELIMINARY_LD_LIBRARY_MASK libodbcinst.so libodbc.so libjava.so libjvm.so"
69 # Get the ROOTPATH and PATH from /etc/profile.env
70 if [ -e "/etc/profile.env" ] ; then
71 PRELIMINARY_SEARCH_DIRS="$PRELIMINARY_SEARCH_DIRS $((. /etc/profile.env; echo ${ROOTPATH}:${PATH}) | tr ':' ' ')"
74 # Get the directories from /etc/ld.so.conf
75 if [ -e /etc/ld.so.conf ] ; then
76 PRELIMINARY_SEARCH_DIRS="$PRELIMINARY_SEARCH_DIRS $(grep -v "^#" /etc/ld.so.conf | tr '\n' ' ')"
79 # Set the final variables
80 # Note: Using $(echo $variable) removes extraneous spaces from variable assignment
81 unset SEARCH_DIRS
82 for i in $(echo $PRELIMINARY_SEARCH_DIRS) ; do
83 [ "$i" = "-*" ] && break
84 # Append a / at the end so that links and directories are treated the same by find
85 # Remove any existing trailing slashes to prevent double-slashes
86 SEARCH_DIRS="$(echo $SEARCH_DIRS ${i/%\//}/)"
87 done
88 # Remove any double-slashes from the path
89 SEARCH_DIRS="$(echo $SEARCH_DIRS | sed 's:/\+:/:g')"
91 unset SEARCH_DIRS_MASK
92 for i in $(echo $PRELIMINARY_SEARCH_DIRS_MASK) ; do
93 [ "$i" = "-*" ] && break
94 SEARCH_DIRS_MASK="$(echo $SEARCH_DIRS_MASK $i)"
95 done
97 unset LD_LIBRARY_MASK
98 for i in $(echo $PRELIMINARY_LD_LIBRARY_MASK) ; do
99 [ "$i" = "-*" ] && break
100 LD_LIBRARY_MASK="$(echo $LD_LIBRARY_MASK $i)"
101 done
103 # Base of temporary files names.
104 [ -d ${HOME}/.findbrokenpkgs ] || mkdir ${HOME}/.findbrokenpkgs
105 touch ${HOME}/.findbrokenpkgs/findbrokenpkgs_0.test 2>/dev/null
106 if [ $? -eq 0 ] ; then
107 LIST="${HOME}/.findbrokenpkgs/findbrokenpkgs"
108 rm ~/.findbrokenpkgs/findbrokenpkgs_0.test
109 else
110 # Try to use /var/tmp since $HOME is not available
111 touch /var/tmp/.findbrokenpkgs/findbrokenpkgs_0.test 2>/dev/null
112 if [ $? -eq 0 ] ; then
113 LIST="/var/tmp/.findbrokenpkgs/findbrokenpkgs"
114 rm /var/tmp/.findbrokenpkgs/findbrokenpkgs_0.test
115 else
116 echo
117 echo "!!! Unable to write temporary files to either $HOME or /var/tmp !!!"
118 echo
119 exit 1
123 shopt -s nullglob
124 shopt -s expand_aliases
125 unalias -a
126 alias echo_v=echo
128 while [ ! -z "$1" ] ; do
129 case "$1" in
130 -q | --quiet )
131 alias echo_v=:
132 shift
134 -nc | --no-color )
135 NOCOLOR=true
136 shift
138 -nw | --no-warning )
139 NOWARNING=true
140 shift
143 echo "Unknown option: $1"
144 exit 1
146 esac
147 done
149 # Color Definitions
150 if [ "$NOCOLOR" = "yes" -o "$NOCOLOR" = "true" ] ; then
151 NO=""
152 BR=""
153 CY=""
154 GR=""
155 RD=""
156 YL=""
157 BL=""
158 else
159 NO="\x1b[0m"
160 BR="\x1b[0;01m"
161 CY="\x1b[36;01m"
162 GR="\x1b[32;01m"
163 RD="\x1b[31;01m"
164 YL="\x1b[33;01m"
165 BL="\x1b[34;01m"
168 function set_trap () {
169 trap "rm_temp $1" SIGHUP SIGINT SIGQUIT SIGABRT SIGTERM
172 function rm_temp () {
173 echo " terminated."
174 echo "Removing incomplete $1."
175 rm $1
176 echo
177 exit 1
180 # Want program results (especially from pacman) in English
181 export LC_ALL=C
183 # Always delete the temporary files from a previous run
184 rm -f ${LIST}.[0-9]_*
186 # Clean up no longer needed environment variables
187 unset PREVIOUS_SEARCH_DIRS PREVIOUS_SEARCH_DIRS_MASK PREVIOUS_LD_LIBRARY_MASK PREVIOUS_PREVIOUS_OPTIONS
188 unset PRELIMINARY_SEARCH_DIRS PRELIMINARY_SEARCH_DIRS_MASK PRELIMINARY_LD_LIBRARY_MASK
190 # Log our environment
191 echo "SEARCH_DIRS=\"$SEARCH_DIRS\"" > $LIST.0_env
192 echo "SEARCH_DIRS_MASK=\"$SEARCH_DIRS_MASK\"" >> $LIST.0_env
193 echo "LD_LIBRARY_MASK=\"$LD_LIBRARY_MASK\"" >> $LIST.0_env
195 echo_v
196 echo_v "Checking reverse dependencies..."
197 echo_v
198 echo_v -n -e "${GR}Collecting system binaries and libraries...${NO}"
200 set_trap "$LIST.1_*"
202 # Be extra paranoid and pipe results through sed to remove multiple slashes
203 # using -perm /u+x for find command
204 find $SEARCH_DIRS -type f \( -perm /u+x -o -name '*.so' -o -name '*.so.*' -o -name '*.la' \) 2>/dev/null | sort | uniq | sed 's:/\+:/:g' >$LIST.0_files
206 # Remove files that match SEARCH_DIR_MASK
207 for dir in $SEARCH_DIRS_MASK ; do
208 grep -v "^$dir" $LIST.0_files > $LIST.1_files
209 mv $LIST.1_files $LIST.0_files
210 done
212 mv $LIST.0_files $LIST.1_files
213 echo_v -e " ${GR}done.${NO}\n ($LIST.1_files)"
215 echo_v
216 echo_v -n -e "${GR}Collecting complete LD_LIBRARY_PATH...${NO}"
217 set_trap "$LIST.2_ldpath"
218 # Ensure that the "trusted" lib directories are at the start of the path
220 echo /lib* /usr/lib* | sed 's/ /:/g'
221 sed '/^#/d;s/#.*$//' </etc/ld.so.conf
222 sed 's:/[^/]*$::' <$LIST.1_files | sort -ru
223 ) | tr '\n' : | tr -d '\r' | sed 's/:$//' >$LIST.2_ldpath
224 echo_v -e " ${GR}done.${NO}\n ($LIST.2_ldpath)"
225 COMPLETE_LD_LIBRARY_PATH="$(cat $LIST.2_ldpath)"
227 echo_v
228 echo_v -e "${GR}Checking dynamic linking consistency...${NO}"
229 set_trap "$LIST.3_rebuild"
230 LD_MASK="\\( $(echo "$LD_LIBRARY_MASK" | sed 's/\./\\./g;s/ / \\| /g') \\)"
231 echo -n > $LIST.3_rebuild
232 cat $LIST.1_files | egrep -v '*\.la$' | while read FILE ; do
233 # Note: double checking seems to be faster than single
234 # with complete path (special add-ons are rare).
235 if ldd "$FILE" 2>/dev/null | grep -v "$LD_MASK" | $SONAME_GREP -q "$SONAME_SEARCH" ; then
236 if LD_LIBRARY_PATH="$COMPLETE_LD_LIBRARY_PATH" ldd "$FILE" 2>/dev/null | grep -v "$LD_MASK" | $SONAME_GREP -q "$SONAME_SEARCH" ; then
237 # Only build missing direct dependencies
238 ALL_MISSING_LIBS=$(ldd "$FILE" 2>/dev/null | sort -u | sed -n 's/ \(.*\) => not found/\1/p' | tr '\n' ' ' | sed 's/ $//' )
239 REQUIRED_LIBS=$(objdump -x $FILE | grep NEEDED | awk '{print $2}' | tr '\n' ' ' | sed 's/ $//')
240 MISSING_LIBS=""
241 for lib in $ALL_MISSING_LIBS ; do
242 if echo $REQUIRED_LIBS | grep -q $lib ; then
243 MISSING_LIBS="$MISSING_LIBS $lib"
245 done
246 if [ "$MISSING_LIBS" != "" ] ; then
247 echo "$FILE" >> $LIST.3_rebuild
248 # MISSING_LIBS already starts with a space
249 echo_v -e " $FILE ${RD}needs missing${NO}${MISSING_LIBS}"
253 done
254 # Not sure if *.la files should even be checked
255 cat $LIST.1_files | egrep '*\.la$' | while read FILE ; do
256 for depend in $(grep '^dependency_libs' $FILE | awk -F'=' '{print $2}' | sed "s/'//g") ; do
257 [ ${depend:0:1} != '/' ] && continue
258 if [ ! -e $depend ] ; then
259 echo "$FILE" >> $LIST.3_rebuild
260 echo_v -e " $FILE ${RD}needs missing${NO} ${depend}"
262 done
263 done
264 echo_v -e " ${GR}done${NO}.\n ($LIST.3_rebuild)"
266 echo_v
267 echo_v -n -e "${GR}Assigning files to packages...${NO}"
268 set_trap "$LIST.4_*"
269 echo -n > $LIST.4_package_owners
270 echo -n > $LIST.4_packages_raw
271 echo -n > $LIST.4_orphans
273 cat $LIST.3_rebuild | while read FILE ; do
274 EXACT_PKG=$(pacman -Qo $FILE | awk '{print $5 " " $6}')
275 PKG=$(echo $EXACT_PKG | awk '{print $1}')
276 if [ -z "$PKG" ] ; then
277 echo_v -n -e "\n ${RD}*** $FILE is orphan & broken! ***${NO}"
278 echo "$FILE -> (none)" >> $LIST.4_package_owners
279 echo "$FILE" >> $LIST.4_orphans
280 echo_v -n -e "\n $FILE ${RD}-> (none)${NO}"
281 else
282 echo "$PKG" >> $LIST.4_packages_raw
283 echo "$FILE -> $EXACT_PKG" >> $LIST.4_package_owners
284 echo_v -n -e "\n $FILE ${CY}->${NO} ${BR}$PKG${NO}"
286 done
287 echo_v
288 echo_v -e " ${GR}done.${NO}\n ($LIST.4_*)"
290 echo_v
291 echo_v -n -e "${GR}Cleaning list of packages to rebuild...${NO}"
292 set_trap "$LIST.5_packages"
293 sort -u $LIST.4_packages_raw >$LIST.5_packages
294 echo_v -e " ${GR}done.${NO}\n ($LIST.5_packages)"
296 REBUILD_LIST="$(cat $LIST.5_packages | tr '\n' ' ')"
297 ORPHAN_LIST="$(cat $LIST.4_orphans)"
299 # Clean up no longer needed environment variables
300 unset COMPLETE_LD_LIBRARY_PATH SEARCH_DIRS SEARCH_DIRS_MASK LD_LIBRARY_MASK
302 trap - SIGHUP SIGINT SIGQUIT SIGABRT SIGTERM
304 if [ -z "$REBUILD_LIST" ] && [ -z "$ORPHAN_LIST" ] ; then
305 echo_v -e "\n${GR}Dynamic linking on your system is consistent.${NO}"
306 # All OK, so delete temporary files
307 rm -f ${LIST}.[0-9]_*
308 else
309 # Show broken files & packages
310 if [ -n "$ORPHAN_LIST" ] ; then
311 echo -e "\n${RD}Orphaned broken files:${NO}"
312 echo "$ORPHAN_LIST"
313 echo_v -e "\n${GR}This list of orphaned broken files is in $LIST.4_orphans${NO}"
315 if [ -n "$REBUILD_LIST" ] ; then
316 echo -e "\n${RD}Recompile these packages:${NO}"
317 echo -e "${BR}$REBUILD_LIST${NO}"
318 if ! [ "$NOWARNING" = "yes" -o "$NOWARNING" = "true" ] ; then
319 echo_v -e "\nSome/all breakages may be ${GR}OK${NO} - this program cannot distinguish between ${RD}required${NO}"
320 echo_v -e "and ${GR}optional${NO} dependencies. See http://bbs.archlinux.org/viewtopic.php?id=13882"
323 # The temporary files are deliberately not deleted, as a source of info
326 exit 0