11b56bb4cSMike Smith#!/bin/sh 21b56bb4cSMike Smith# tcl magic \ 31b56bb4cSMike Smithexec tclsh $0 $* 41b56bb4cSMike Smith################################################################################ 5fa3da92aSMike Smith# Copyright (C) 1997 6fa3da92aSMike Smith# Michael Smith. All rights reserved. 7fa3da92aSMike Smith# 8fa3da92aSMike Smith# Redistribution and use in source and binary forms, with or without 9fa3da92aSMike Smith# modification, are permitted provided that the following conditions 10fa3da92aSMike Smith# are met: 11fa3da92aSMike Smith# 1. Redistributions of source code must retain the above copyright 12fa3da92aSMike Smith# notice, this list of conditions and the following disclaimer. 13fa3da92aSMike Smith# 2. Redistributions in binary form must reproduce the above copyright 14fa3da92aSMike Smith# notice, this list of conditions and the following disclaimer in the 15fa3da92aSMike Smith# documentation and/or other materials provided with the distribution. 16fa3da92aSMike Smith# 3. Neither the name of the author nor the names of any co-contributors 17fa3da92aSMike Smith# may be used to endorse or promote products derived from this software 18fa3da92aSMike Smith# without specific prior written permission. 19fa3da92aSMike Smith# 20fa3da92aSMike Smith# THIS SOFTWARE IS PROVIDED BY Michael Smith AND CONTRIBUTORS ``AS IS'' AND 21fa3da92aSMike Smith# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22fa3da92aSMike Smith# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23fa3da92aSMike Smith# ARE DISCLAIMED. IN NO EVENT SHALL Michael Smith OR CONTRIBUTORS BE LIABLE 24fa3da92aSMike Smith# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25fa3da92aSMike Smith# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26fa3da92aSMike Smith# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27fa3da92aSMike Smith# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28fa3da92aSMike Smith# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29fa3da92aSMike Smith# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30fa3da92aSMike Smith# SUCH DAMAGE. 31fa3da92aSMike Smith################################################################################ 321b56bb4cSMike Smith# 331b56bb4cSMike Smith# LibraryReport; produce a list of shared libraries on the system, and a list of 341b56bb4cSMike Smith# all executables that use them. 351b56bb4cSMike Smith# 361b56bb4cSMike Smith################################################################################ 371b56bb4cSMike Smith# 381b56bb4cSMike Smith# Stage 1 looks for shared libraries; the output of 'ldconfig -r' is examined 391b56bb4cSMike Smith# for hints as to where to look for libraries (but not trusted as a complete 401b56bb4cSMike Smith# list). 411b56bb4cSMike Smith# 421b56bb4cSMike Smith# These libraries each get an entry in the global 'Libs()' array. 431b56bb4cSMike Smith# 441b56bb4cSMike Smith# Stage 2 walks the entire system directory heirachy looking for executable 451b56bb4cSMike Smith# files, applies 'ldd' to them and attempts to determine which libraries are 461b56bb4cSMike Smith# used. The path of the executable is then added to the 'Libs()' array 471b56bb4cSMike Smith# for each library used. 481b56bb4cSMike Smith# 491b56bb4cSMike Smith# Stage 3 reports on the day's findings. 501b56bb4cSMike Smith# 511b56bb4cSMike Smith################################################################################ 521b56bb4cSMike Smith# 531b56bb4cSMike Smith# 541b56bb4cSMike Smith 551b56bb4cSMike Smith######################################################################################### 561b56bb4cSMike Smith# findLibs 571b56bb4cSMike Smith# 581b56bb4cSMike Smith# Ask ldconfig where it thinks libraries are to be found. Go look for them, and 591b56bb4cSMike Smith# add an element to 'Libs' for everything that looks like a library. 601b56bb4cSMike Smith# 611b56bb4cSMike Smithproc findLibs {} { 621b56bb4cSMike Smith 631b56bb4cSMike Smith global Libs stats verbose; 641b56bb4cSMike Smith 651b56bb4cSMike Smith # Older ldconfigs return a junk value when asked for a report 661b56bb4cSMike Smith if {[catch {set liblist [exec ldconfig -r]} err]} { # get ldconfig output 671b56bb4cSMike Smith puts stderr "ldconfig returned nonzero, persevering."; 681b56bb4cSMike Smith set liblist $err; # there's junk in this 691b56bb4cSMike Smith } 701b56bb4cSMike Smith 711b56bb4cSMike Smith # remove hintsfile name, convert to list 721b56bb4cSMike Smith set liblist [lrange [split $liblist "\n"] 1 end]; 731b56bb4cSMike Smith 741b56bb4cSMike Smith set libdirs ""; # no directories yet 751b56bb4cSMike Smith foreach line $liblist { 761b56bb4cSMike Smith # parse ldconfig output 771b56bb4cSMike Smith if {[scan $line "%s => %s" junk libname] == 2} { 781b56bb4cSMike Smith # find directory name 791b56bb4cSMike Smith set libdir [file dirname $libname]; 801b56bb4cSMike Smith # have we got this one already? 811b56bb4cSMike Smith if {[lsearch -exact $libdirs $libdir] == -1} { 821b56bb4cSMike Smith lappend libdirs $libdir; 831b56bb4cSMike Smith } 841b56bb4cSMike Smith } else { 851b56bb4cSMike Smith puts stderr "Unparseable ldconfig output line :"; 861b56bb4cSMike Smith puts stderr $line; 871b56bb4cSMike Smith } 881b56bb4cSMike Smith } 891b56bb4cSMike Smith 901b56bb4cSMike Smith # libdirs is now a list of directories that we might find libraries in 911b56bb4cSMike Smith foreach dir $libdirs { 921b56bb4cSMike Smith # get the names of anything that looks like a library 931b56bb4cSMike Smith set libnames [glob -nocomplain "$dir/lib*.so.*"] 941b56bb4cSMike Smith foreach lib $libnames { 95fa3da92aSMike Smith set type [file type $lib]; # what is it? 96fa3da92aSMike Smith switch $type { 97fa3da92aSMike Smith file { # looks like a library 98fa3da92aSMike Smith # may have already been referenced by a symlink 99fa3da92aSMike Smith if {![info exists Libs($lib)]} { 1001b56bb4cSMike Smith set Libs($lib) ""; # add it to our list 1011b56bb4cSMike Smith if {$verbose} {puts "+ $lib";} 1021b56bb4cSMike Smith } 1031b56bb4cSMike Smith } 104fa3da92aSMike Smith link { # symlink; probably to another library 105fa3da92aSMike Smith # If the readlink fails, the symlink is stale 106fa3da92aSMike Smith if {[catch {set ldest [file readlink $lib]}]} { 107fa3da92aSMike Smith puts stderr "Symbolic link points to nothing : $lib"; 108fa3da92aSMike Smith } else { 109fa3da92aSMike Smith # may have already been referenced by another symlink 110fa3da92aSMike Smith if {![info exists Libs($lib)]} { 111fa3da92aSMike Smith set Libs($lib) ""; # add it to our list 112fa3da92aSMike Smith if {$verbose} {puts "+ $lib";} 113fa3da92aSMike Smith } 114fa3da92aSMike Smith # list the symlink as a consumer of this library 115fa3da92aSMike Smith lappend Libs($ldest) "($lib)"; 116fa3da92aSMike Smith if {$verbose} {puts "-> $ldest";} 117fa3da92aSMike Smith } 118fa3da92aSMike Smith } 119fa3da92aSMike Smith } 120fa3da92aSMike Smith } 121fa3da92aSMike Smith } 1221b56bb4cSMike Smith set stats(libs) [llength [array names Libs]]; 1231b56bb4cSMike Smith} 1241b56bb4cSMike Smith 1251b56bb4cSMike Smith################################################################################ 1261b56bb4cSMike Smith# findLibUsers 1271b56bb4cSMike Smith# 1281b56bb4cSMike Smith# Look in the directory (dir) for executables. If we find any, call 1291b56bb4cSMike Smith# examineExecutable to see if it uses any shared libraries. Call ourselves 1301b56bb4cSMike Smith# on any directories we find. 1311b56bb4cSMike Smith# 1321b56bb4cSMike Smith# Note that the use of "*" as a glob pattern means we miss directories and 1331b56bb4cSMike Smith# executables starting with '.'. This is a Feature. 1341b56bb4cSMike Smith# 1351b56bb4cSMike Smithproc findLibUsers {dir} { 1361b56bb4cSMike Smith 1371b56bb4cSMike Smith global stats verbose; 1381b56bb4cSMike Smith 1391b56bb4cSMike Smith if {[catch { 1401b56bb4cSMike Smith set ents [glob -nocomplain "$dir/*"]; 1411b56bb4cSMike Smith } msg]} { 1421b56bb4cSMike Smith if {$msg == ""} { 1431b56bb4cSMike Smith set msg "permission denied"; 1441b56bb4cSMike Smith } 1451b56bb4cSMike Smith puts stderr "Can't search under '$dir' : $msg"; 1461b56bb4cSMike Smith return ; 1471b56bb4cSMike Smith } 1481b56bb4cSMike Smith 1491b56bb4cSMike Smith if {$verbose} {puts "===>> $dir";} 1501b56bb4cSMike Smith incr stats(dirs); 1511b56bb4cSMike Smith 1521b56bb4cSMike Smith # files? 1531b56bb4cSMike Smith foreach f $ents { 1541b56bb4cSMike Smith # executable? 1551b56bb4cSMike Smith if {[file executable $f]} { 1561b56bb4cSMike Smith # really a file? 1571b56bb4cSMike Smith if {[file isfile $f]} { 1581b56bb4cSMike Smith incr stats(files); 1591b56bb4cSMike Smith examineExecutable $f; 1601b56bb4cSMike Smith } 1611b56bb4cSMike Smith } 1621b56bb4cSMike Smith } 1631b56bb4cSMike Smith # subdirs? 1641b56bb4cSMike Smith foreach f $ents { 1651b56bb4cSMike Smith # maybe a directory with more files? 1661b56bb4cSMike Smith # don't use 'file isdirectory' because that follows symlinks 1671b56bb4cSMike Smith if {[catch {set type [file type $f]}]} { 1681b56bb4cSMike Smith continue ; # may not be able to stat 1691b56bb4cSMike Smith } 1701b56bb4cSMike Smith if {$type == "directory"} { 1711b56bb4cSMike Smith findLibUsers $f; 1721b56bb4cSMike Smith } 1731b56bb4cSMike Smith } 1741b56bb4cSMike Smith} 1751b56bb4cSMike Smith 1761b56bb4cSMike Smith################################################################################ 1771b56bb4cSMike Smith# examineExecutable 1781b56bb4cSMike Smith# 1791b56bb4cSMike Smith# Look at (fname) and see if ldd thinks it references any shared libraries. 1801b56bb4cSMike Smith# If it does, update Libs with the information. 1811b56bb4cSMike Smith# 1821b56bb4cSMike Smithproc examineExecutable {fname} { 1831b56bb4cSMike Smith 1841b56bb4cSMike Smith global Libs stats verbose; 1851b56bb4cSMike Smith 1861b56bb4cSMike Smith # ask Mr. Ldd. 1871b56bb4cSMike Smith if {[catch {set result [exec ldd $fname]} msg]} { 1881b56bb4cSMike Smith return ; # not dynamic 1891b56bb4cSMike Smith } 1901b56bb4cSMike Smith 1911b56bb4cSMike Smith if {$verbose} {puts -nonewline "$fname : ";} 1921b56bb4cSMike Smith incr stats(execs); 1931b56bb4cSMike Smith 1941b56bb4cSMike Smith # For a non-shared executable, we get a single-line error message. 1951b56bb4cSMike Smith # For a shared executable, we get a heading line, so in either case 1961b56bb4cSMike Smith # we can discard the first line and any subsequent lines are libraries 1971b56bb4cSMike Smith # that are required. 1981b56bb4cSMike Smith set llist [lrange [split $result "\n"] 1 end]; 1991b56bb4cSMike Smith set uses ""; 2001b56bb4cSMike Smith 2011b56bb4cSMike Smith foreach line $llist { 2021b56bb4cSMike Smith if {[scan $line "%s => %s %s" junk1 lib junk2] == 3} { 203fa3da92aSMike Smith if {$lib == "not"} { # "not found" error 204fa3da92aSMike Smith set mlname [string range $junk1 2 end]; 205fa3da92aSMike Smith puts stderr "$fname : library '$mlname' not known."; 206fa3da92aSMike Smith } else { 2071b56bb4cSMike Smith lappend Libs($lib) $fname; 2081b56bb4cSMike Smith lappend uses $lib; 209fa3da92aSMike Smith } 2101b56bb4cSMike Smith } else { 211fa3da92aSMike Smith puts stderr "Unparseable ldd output line :"; 2121b56bb4cSMike Smith puts stderr $line; 2131b56bb4cSMike Smith } 2141b56bb4cSMike Smith } 2151b56bb4cSMike Smith if {$verbose} {puts "$uses";} 2161b56bb4cSMike Smith} 2171b56bb4cSMike Smith 2181b56bb4cSMike Smith################################################################################ 2191b56bb4cSMike Smith# emitLibDetails 2201b56bb4cSMike Smith# 2211b56bb4cSMike Smith# Emit a listing of libraries and the executables that use them. 2221b56bb4cSMike Smith# 2231b56bb4cSMike Smithproc emitLibDetails {} { 2241b56bb4cSMike Smith 2251b56bb4cSMike Smith global Libs; 2261b56bb4cSMike Smith 2271b56bb4cSMike Smith # divide into used/unused 2281b56bb4cSMike Smith set used ""; 2291b56bb4cSMike Smith set unused ""; 2301b56bb4cSMike Smith foreach lib [array names Libs] { 2311b56bb4cSMike Smith if {$Libs($lib) == ""} { 2321b56bb4cSMike Smith lappend unused $lib; 2331b56bb4cSMike Smith } else { 2341b56bb4cSMike Smith lappend used $lib; 2351b56bb4cSMike Smith } 2361b56bb4cSMike Smith } 2371b56bb4cSMike Smith 2381b56bb4cSMike Smith # emit used list 2391b56bb4cSMike Smith puts "== Current Shared Libraries =================================================="; 2401b56bb4cSMike Smith foreach lib [lsort $used] { 2411b56bb4cSMike Smith # sort executable names 2421b56bb4cSMike Smith set users [lsort $Libs($lib)]; 2431b56bb4cSMike Smith puts [format "%-30s %s" $lib $users]; 2441b56bb4cSMike Smith } 2451b56bb4cSMike Smith # emit unused 2461b56bb4cSMike Smith puts "== Stale Shared Libraries ===================================================="; 2471b56bb4cSMike Smith foreach lib [lsort $unused] { 2481b56bb4cSMike Smith # sort executable names 2491b56bb4cSMike Smith set users [lsort $Libs($lib)]; 2501b56bb4cSMike Smith puts [format "%-30s %s" $lib $users]; 2511b56bb4cSMike Smith } 2521b56bb4cSMike Smith} 2531b56bb4cSMike Smith 2541b56bb4cSMike Smith################################################################################ 2551b56bb4cSMike Smith# Run the whole shebang 2561b56bb4cSMike Smith# 2571b56bb4cSMike Smithproc main {} { 2581b56bb4cSMike Smith 2591b56bb4cSMike Smith global stats verbose argv; 2601b56bb4cSMike Smith 2611b56bb4cSMike Smith set verbose 0; 2621b56bb4cSMike Smith foreach arg $argv { 2631b56bb4cSMike Smith switch -- $arg { 2641b56bb4cSMike Smith -v { 2651b56bb4cSMike Smith set verbose 1; 2661b56bb4cSMike Smith } 2671b56bb4cSMike Smith default { 268fa3da92aSMike Smith puts stderr "Unknown option '$arg'."; 2691b56bb4cSMike Smith exit ; 2701b56bb4cSMike Smith } 2711b56bb4cSMike Smith } 2721b56bb4cSMike Smith } 2731b56bb4cSMike Smith 2741b56bb4cSMike Smith set stats(libs) 0; 2751b56bb4cSMike Smith set stats(dirs) 0; 2761b56bb4cSMike Smith set stats(files) 0; 2771b56bb4cSMike Smith set stats(execs) 0 2781b56bb4cSMike Smith 2791b56bb4cSMike Smith findLibs; 2801b56bb4cSMike Smith findLibUsers "/"; 2811b56bb4cSMike Smith emitLibDetails; 2821b56bb4cSMike Smith 283fa3da92aSMike Smith puts [format "Searched %d directories, %d executables (%d dynamic) for %d libraries." \ 2841b56bb4cSMike Smith $stats(dirs) $stats(files) $stats(execs) $stats(libs)]; 2851b56bb4cSMike Smith} 2861b56bb4cSMike Smith 2871b56bb4cSMike Smith################################################################################ 2881b56bb4cSMike Smithmain; 289